From a5b7564c5b6c337a9db8272beac7d9fe61e92f6d Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 25 Jun 2019 02:35:55 +0200 Subject: [PATCH] hash-tree-root tests --- .../eth2spec/utils/ssz/test_ssz_impl.py | 192 ++++++++++++++---- 1 file changed, 155 insertions(+), 37 deletions(-) diff --git a/test_libs/pyspec/eth2spec/utils/ssz/test_ssz_impl.py b/test_libs/pyspec/eth2spec/utils/ssz/test_ssz_impl.py index aa7aee64a..82fb4ec68 100644 --- a/test_libs/pyspec/eth2spec/utils/ssz/test_ssz_impl.py +++ b/test_libs/pyspec/eth2spec/utils/ssz/test_ssz_impl.py @@ -1,8 +1,10 @@ +from typing import Iterable from .ssz_impl import serialize, hash_tree_root from .ssz_typing import ( Bit, Bool, Container, List, Vector, Bytes, BytesN, - uint8, uint16, uint32, uint64, byte + uint8, uint16, uint32, uint64, uint256, byte ) +from ..hash_function import hash as bytes_hash import pytest @@ -46,37 +48,117 @@ sig_test_data = [0 for i in range(96)] for k, v in {0: 1, 32: 2, 64: 3, 95: 0xff}.items(): sig_test_data[k] = v + +def chunk(hex: str) -> str: + return (hex + ("00" * 32))[:64] # just pad on the right, to 32 bytes (64 hex chars) + + +def h(a: str, b: str) -> str: + return bytes_hash(bytes.fromhex(a) + bytes.fromhex(b)).hex() + + +# zero hashes, as strings, for +zero_hashes = [chunk("")] +for layer in range(1, 32): + zero_hashes.append(h(zero_hashes[layer - 1], zero_hashes[layer - 1])) + + +def merge(a: str, branch: Iterable[str]) -> str: + """ + Merge (out on left, branch on right) leaf a with branch items, branch is from bottom to top. + """ + out = a + for b in branch: + out = h(out, b) + return out + + test_data = [ - ("bit F", Bit(False), "00"), - ("bit T", Bit(True), "01"), - ("bool F", Bool(False), "00"), - ("bool T", Bool(True), "01"), - ("uint8 00", uint8(0x00), "00"), - ("uint8 01", uint8(0x01), "01"), - ("uint8 ab", uint8(0xab), "ab"), - ("byte 00", byte(0x00), "00"), - ("byte 01", byte(0x01), "01"), - ("byte ab", byte(0xab), "ab"), - ("uint16 0000", uint16(0x0000), "0000"), - ("uint16 abcd", uint16(0xabcd), "cdab"), - ("uint32 00000000", uint32(0x00000000), "00000000"), - ("uint32 01234567", uint32(0x01234567), "67452301"), - ("small (4567, 0123)", SmallTestStruct(A=0x4567, B=0x0123), "67452301"), - ("small [4567, 0123]::2", Vector[uint16, 2](uint16(0x4567), uint16(0x0123)), "67452301"), - ("uint32 01234567", uint32(0x01234567), "67452301"), - ("uint64 0000000000000000", uint64(0x00000000), "0000000000000000"), - ("uint64 0123456789abcdef", uint64(0x0123456789abcdef), "efcdab8967452301"), + ("bit F", Bit(False), "00", chunk("00")), + ("bit T", Bit(True), "01", chunk("01")), + ("bool F", Bool(False), "00", chunk("00")), + ("bool T", Bool(True), "01", chunk("01")), + ("uint8 00", uint8(0x00), "00", chunk("00")), + ("uint8 01", uint8(0x01), "01", chunk("01")), + ("uint8 ab", uint8(0xab), "ab", chunk("ab")), + ("byte 00", byte(0x00), "00", chunk("00")), + ("byte 01", byte(0x01), "01", chunk("01")), + ("byte ab", byte(0xab), "ab", chunk("ab")), + ("uint16 0000", uint16(0x0000), "0000", chunk("0000")), + ("uint16 abcd", uint16(0xabcd), "cdab", chunk("cdab")), + ("uint32 00000000", uint32(0x00000000), "00000000", chunk("00000000")), + ("uint32 01234567", uint32(0x01234567), "67452301", chunk("67452301")), + ("small (4567, 0123)", SmallTestStruct(A=0x4567, B=0x0123), "67452301", h(chunk("6745"), chunk("2301"))), + ("small [4567, 0123]::2", Vector[uint16, 2](uint16(0x4567), uint16(0x0123)), "67452301", chunk("67452301")), + ("uint32 01234567", uint32(0x01234567), "67452301", chunk("67452301")), + ("uint64 0000000000000000", uint64(0x00000000), "0000000000000000", chunk("0000000000000000")), + ("uint64 0123456789abcdef", uint64(0x0123456789abcdef), "efcdab8967452301", chunk("efcdab8967452301")), ("sig", BytesN[96](*sig_test_data), "0100000000000000000000000000000000000000000000000000000000000000" "0200000000000000000000000000000000000000000000000000000000000000" - "03000000000000000000000000000000000000000000000000000000000000ff"), - ("emptyTestStruct", EmptyTestStruct(), ""), - ("singleFieldTestStruct", SingleFieldTestStruct(A=0xab), "ab"), - ("fixedTestStruct", FixedTestStruct(A=0xab, B=0xaabbccdd00112233, C=0x12345678), "ab33221100ddccbbaa78563412"), - ("varTestStruct nil", VarTestStruct(A=0xabcd, C=0xff), "cdab07000000ff"), - ("varTestStruct empty", VarTestStruct(A=0xabcd, B=List[uint16, 1024](), C=0xff), "cdab07000000ff"), + "03000000000000000000000000000000000000000000000000000000000000ff", + h(h(chunk("01"), chunk("02")), + h("03000000000000000000000000000000000000000000000000000000000000ff", chunk("")))), + ("emptyTestStruct", EmptyTestStruct(), "", chunk("")), + ("singleFieldTestStruct", SingleFieldTestStruct(A=0xab), "ab", chunk("ab")), + ("uint16 list", List[uint16, 32](uint16(0xaabb), uint16(0xc0ad), uint16(0xeeff)), "bbaaadc0ffee", + h(h(chunk("bbaaadc0ffee"), chunk("")), chunk("03000000")) # max length: 32 * 2 = 64 bytes = 2 chunks + ), + ("uint32 list", List[uint32, 128](uint32(0xaabb), uint32(0xc0ad), uint32(0xeeff)), "bbaa0000adc00000ffee0000", + # max length: 128 * 4 = 512 bytes = 16 chunks + h(merge(chunk("bbaa0000adc00000ffee0000"), zero_hashes[0:4]), chunk("03000000")) + ), + ("uint256 list", List[uint256, 32](uint256(0xaabb), uint256(0xc0ad), uint256(0xeeff)), + "bbaa000000000000000000000000000000000000000000000000000000000000" + "adc0000000000000000000000000000000000000000000000000000000000000" + "ffee000000000000000000000000000000000000000000000000000000000000", + h(merge(h(h(chunk("bbaa"), chunk("adc0")), h(chunk("ffee"), chunk(""))), zero_hashes[2:5]), chunk("03000000")) + ), + ("uint256 list long", List[uint256, 128](i for i in range(1, 20)), + "".join([i.to_bytes(length=32, byteorder='little').hex() for i in range(1, 20)]), + h(merge( + h( + h( + h( + h(h(chunk("01"), chunk("02")), h(chunk("03"), chunk("04"))), + h(h(chunk("05"), chunk("06")), h(chunk("07"), chunk("08"))), + ), + h( + h(h(chunk("09"), chunk("0a")), h(chunk("0b"), chunk("0c"))), + h(h(chunk("0d"), chunk("0e")), h(chunk("0f"), chunk("10"))), + ) + ), + h( + h( + h(h(chunk("11"), chunk("12")), h(chunk("13"), chunk(""))), + zero_hashes[2] + ), + zero_hashes[3] + ) + ), + zero_hashes[5:7]), chunk("13000000")) # 128 chunks = 7 deep + ), + ("fixedTestStruct", FixedTestStruct(A=0xab, B=0xaabbccdd00112233, C=0x12345678), "ab33221100ddccbbaa78563412", + h(h(chunk("ab"), chunk("33221100ddccbbaa")), h(chunk("78563412"), chunk("")))), + ("varTestStruct nil", VarTestStruct(A=0xabcd, C=0xff), "cdab07000000ff", + h(h(chunk("cdab"), h(zero_hashes[6], chunk("00000000"))), h(chunk("ff"), chunk("")))), + ("varTestStruct empty", VarTestStruct(A=0xabcd, B=List[uint16, 1024](), C=0xff), "cdab07000000ff", + h(h(chunk("cdab"), h(zero_hashes[6], chunk("00000000"))), h(chunk("ff"), chunk("")))), # log2(1024*2/32)= 6 deep ("varTestStruct some", VarTestStruct(A=0xabcd, B=List[uint16, 1024](1, 2, 3), C=0xff), - "cdab07000000ff010002000300"), + "cdab07000000ff010002000300", + h( + h( + chunk("cdab"), + h( + merge( + chunk("010002000300"), + zero_hashes[0:6] + ), + chunk("03000000") # length mix in + ) + ), + h(chunk("ff"), chunk("")) + )), ("complexTestStruct", ComplexTestStruct( A=0xaabb, @@ -90,8 +172,8 @@ test_data = [ FixedTestStruct(A=0xee, B=0x4444444444444444, C=0x00112233), FixedTestStruct(A=0xff, B=0x5555555555555555, C=0x44556677)), G=Vector[VarTestStruct, 2]( - VarTestStruct(A=0xabcd, B=List[uint16, 1024](1, 2, 3), C=0xff), - VarTestStruct(A=0xabcd, B=List[uint16, 1024](1, 2, 3), C=0xff)), + VarTestStruct(A=0xdead, B=List[uint16, 1024](1, 2, 3), C=0x11), + VarTestStruct(A=0xbeef, B=List[uint16, 1024](4, 5, 6), C=0x22)), ), "bbaa" "47000000" # offset of B, []uint16 @@ -107,17 +189,53 @@ test_data = [ "666f6f626172" # foobar "cdab07000000ff010002000300" # contents of E "08000000" "15000000" # [start G]: local offsets of [2]varTestStruct - "cdab07000000ff010002000300" - "cdab07000000ff010002000300", - ) + "adde0700000011010002000300" + "efbe0700000022040005000600", + h( + h( + h( # A and B + chunk("bbaa"), + h(merge(chunk("22114433"), zero_hashes[0:3]), chunk("02000000")) # 2*128/32 = 8 chunks + ), + h( # C and D + chunk("ff"), + h(merge(chunk("666f6f626172"), zero_hashes[0:3]), chunk("06000000")) # 256/32 = 8 chunks + ) + ), + h( + h( # E and F + h(h(chunk("cdab"), h(merge(chunk("010002000300"), zero_hashes[0:6]), chunk("03000000"))), + h(chunk("ff"), chunk(""))), + h( + h( + h(h(chunk("cc"), chunk("4242424242424242")), h(chunk("37133713"), chunk(""))), + h(h(chunk("dd"), chunk("3333333333333333")), h(chunk("cdabcdab"), chunk(""))), + ), + h( + h(h(chunk("ee"), chunk("4444444444444444")), h(chunk("33221100"), chunk(""))), + h(h(chunk("ff"), chunk("5555555555555555")), h(chunk("77665544"), chunk(""))), + ), + ) + ), + h( # G and padding + h( + h(h(chunk("adde"), h(merge(chunk("010002000300"), zero_hashes[0:6]), chunk("03000000"))), + h(chunk("11"), chunk(""))), + h(h(chunk("efbe"), h(merge(chunk("040005000600"), zero_hashes[0:6]), chunk("03000000"))), + h(chunk("22"), chunk(""))), + ), + chunk("") + ) + ) + )) ] -@pytest.mark.parametrize("name, value, serialized", test_data) -def test_serialize(name, value, serialized): +@pytest.mark.parametrize("name, value, serialized, _", test_data) +def test_serialize(name, value, serialized, _): assert serialize(value) == bytes.fromhex(serialized) -@pytest.mark.parametrize("name, value, _", test_data) -def test_hash_tree_root(name, value, _): - hash_tree_root(value) +@pytest.mark.parametrize("name, value, _, root", test_data) +def test_hash_tree_root(name, value, _, root): + assert hash_tree_root(value) == bytes.fromhex(root)