Skip to content
This repository has been archived by the owner on Nov 16, 2022. It is now read-only.

add encode fix length array to pyobi #2546

Merged
merged 9 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG_UNRELEASED.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@

### Oracle Binary Encoding (OBI)

- (feat) [\#2546](https://github.com/bandprotocol/bandchain/pull/2546) Implement encode / decode for fix length array

### Helpers

- (impv) [\#2577](https://github.com/bandprotocol/bandchain/pull/2577) Wallet module completion
Expand Down
1 change: 1 addition & 0 deletions obi/pyobi/pyobi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
PyObiStruct,
PyObiString,
PyObiBytes,
PyObiArray,
)
41 changes: 41 additions & 0 deletions obi/pyobi/pyobi/pyobi.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,47 @@ def decode(self, data):
raise ValueError("Boolean value must be 1 or 0 but got {}".format(u8))


class PyObiArray(PyObiSpec):
def __init__(self, _spec):
[spec, size] = _spec[1:-1].rsplit(";", 1)
self.size = int(size, 10)
self.intl_obi = self.from_spec(spec)

@classmethod
def match_schema(cls, schema):
if not (schema[0] == "[" and schema[-1] == "]"):
return False
try:
[spec, size] = schema[1:-1].rsplit(";", 1)
except:
return False

if not size.isdigit():
return False

for impl in cls.impls:
if impl.match_schema(spec):
return True

return False

def encode(self, value):
if len(value) != self.size:
raise ValueError("array size should be {} but got {}".format(self.size, len(value)))
result = b""
for each in value:
result = result + self.intl_obi.encode(each)
return result

def decode(self, data):
remaining = data[:]
result = []
for _ in range(self.size):
each, remaining = self.intl_obi.decode(remaining)
result.append(each)
return result, remaining


class PyObiVector(PyObiSpec):
def __init__(self, spec):
self.intl_obi = self.from_spec(spec[1:-1])
Expand Down
141 changes: 136 additions & 5 deletions obi/pyobi/tests/test_pyobi.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,108 @@ def test_encode_decode_bytes():
)


def test_encode_decode_array():
# encode
# bool
assert PyObiArray("[bool;0]").encode([]) == bytes.fromhex("")
assert PyObiArray("[bool;1]").encode([False]) == bytes.fromhex("00")
assert PyObiArray("[bool;5]").encode([True, False, True, False, True]) == bytes.fromhex(
"0100010001"
)

# integer
assert PyObiArray("[i32;0]").encode([]) == bytes.fromhex("")
assert PyObiArray("[i32;1]").encode([999]) == bytes.fromhex("000003e7")
assert PyObiArray("[i32;3]").encode([11, 2222, 33333]) == bytes.fromhex(
"0000000b000008ae00008235"
)

# string
assert PyObiArray("[string;0]").encode([]) == bytes.fromhex("")
assert PyObiArray("[string;1]").encode(["mumu"]) == bytes.fromhex("000000046d756d75")
assert PyObiArray("[string;3]").encode(["mumu", "lulu", "mumumumu"]) == bytes.fromhex(
"000000046d756d75000000046c756c75000000086d756d756d756d75"
)

# vector
assert PyObiArray("[[u64];0]").encode([]) == bytes.fromhex("")
assert PyObiArray("[[u64];2]").encode([[1, 2, 3, 4, 5], []]) == bytes.fromhex(
"000000050000000000000001000000000000000200000000000000030000000000000004000000000000000500000000"
)
assert PyObiArray("[[string];3]").encode([["a", "", "b"], [], ["aaa", "bbb"]]) == bytes.fromhex(
"00000003000000016100000000000000016200000000000000020000000361616100000003626262"
)

# bytes
assert PyObiArray("[bytes;0]").encode([]) == bytes.fromhex("")
assert PyObiArray("[bytes;1]").encode([bytes.fromhex("ababab")]) == bytes.fromhex(
"00000003ababab"
)
assert PyObiArray("[bytes;3]").encode(
[bytes.fromhex("ababab"), bytes.fromhex("cd"), bytes.fromhex("efef")]
) == bytes.fromhex("00000003ababab00000001cd00000002efef")

# array
assert PyObiArray("[[bool;2];0]").encode([]) == bytes.fromhex("")
assert PyObiArray("[[bool;2];3]").encode(
[[False, False], [True, True], [False, True]]
) == bytes.fromhex("000001010001")

# decode
# bool
assert PyObiArray("[bool;0]").decode(bytes.fromhex("")) == ([], b"")
assert PyObiArray("[bool;1]").decode(bytes.fromhex("00")) == ([False], b"")
assert PyObiArray("[bool;5]").decode(bytes.fromhex("0100010001")) == (
[True, False, True, False, True,],
b"",
)

# integer
assert PyObiArray("[i32;0]").decode(bytes.fromhex("")) == ([], b"")
assert PyObiArray("[i32;1]").decode(bytes.fromhex("000003e7")) == ([999], b"")
assert PyObiArray("[i32;3]").decode(bytes.fromhex("0000000b000008ae00008235")) == (
[11, 2222, 33333,],
b"",
)

# string
assert PyObiArray("[string;0]").decode(bytes.fromhex("")) == ([], b"")
assert PyObiArray("[string;1]").decode(bytes.fromhex("000000046d756d75")) == (["mumu"], b"")
assert PyObiArray("[string;3]").decode(
bytes.fromhex("000000046d756d75000000046c756c75000000086d756d756d756d75")
) == (["mumu", "lulu", "mumumumu"], b"")

# bytes
assert PyObiArray("[bytes;0]").decode(bytes.fromhex("")) == ([], b"")
assert PyObiArray("[bytes;1]").decode(bytes.fromhex("00000003ababab")) == (
[bytes.fromhex("ababab")],
b"",
)
assert PyObiArray("[bytes;3]").decode(
bytes.fromhex("00000003ababab00000001cd00000002efef")
) == ([bytes.fromhex("ababab"), bytes.fromhex("cd"), bytes.fromhex("efef")], b"")

# vector
assert PyObiArray("[[u64];0]").decode(bytes.fromhex("")) == ([], b"")
assert PyObiArray("[[u64];2]").decode(
bytes.fromhex(
"000000050000000000000001000000000000000200000000000000030000000000000004000000000000000500000000"
)
) == ([[1, 2, 3, 4, 5], []], b"")
assert PyObiArray("[[string];3]").decode(
bytes.fromhex(
"00000003000000016100000000000000016200000000000000020000000361616100000003626262"
)
) == ([["a", "", "b"], [], ["aaa", "bbb"]], b"")

# array
assert PyObiArray("[[bool;2];0]").decode(bytes.fromhex("")) == ([], b"")
assert PyObiArray("[[bool;2];3]").decode(bytes.fromhex("000001010001")) == (
[[False, False], [True, True], [False, True],],
b"",
)


def test_encode_decode_vec():
# encode
assert PyObiVector("[i32]").encode([87654321, -12345678]) == bytes.fromhex(
Expand Down Expand Up @@ -320,6 +422,16 @@ def test_encode_decode_muti():
},
c: [string]
}
/
[[u64];10]
/
[
{
a: [string;2],
b: u8
}
;3
]
"""
)

Expand Down Expand Up @@ -347,45 +459,64 @@ def test_encode_decode_muti():
"b": {"b1": [True, False, True, True, True, True, False]},
"c": ["mumu", "lulu", "momo", "toto", "coco", "bobo"],
},
[[], [1], [2, 22], [3, 33, 333], [4], [5], [6, 66], [7, 7777], [8], [9]],
[{"a": ["a1", "a2"], "b": 15}, {"a": ["a3", "a4"], "b": 68}, {"a": ["a5", "a6"], "b": 200}],
]

# sturct 1
# struct 1
encoded = obi.encode(test_structs[0], 0)
assert (
"000000034254430000000000002328010002010000000a00112233445566778899000f423f00"
== encoded.hex()
)
assert test_structs[0] == obi.decode(encoded, 0)

# sturct 2
# struct 2
encoded = obi.encode(test_structs[1], 1)
assert (
"000001bd4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f726520657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c2071756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e69736920757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e2044756973206175746520697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c697420657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e204578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c2073756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e"
== encoded.hex()
)
assert test_structs[1] == obi.decode(encoded, 1)

# sturct 3
# struct 3
encoded = obi.encode(test_structs[2], 2)
assert "18a24ec1659380f421195719845bbff4df8568c7bd615812b39436bbcb8ead4c" == encoded.hex()
assert test_structs[2] == obi.decode(encoded, 2)

# sturct 4
# struct 4
encoded = obi.encode(test_structs[3], 3)
assert (
"0000000500000003000000010000000200000003000000050000002c00000037000000420000004d00000058000000050000270f000027100000271100002712000027130000000100bc614e00000000"
== encoded.hex()
)
assert test_structs[3] == obi.decode(encoded, 3)

# sturct 5
# struct 5
encoded = obi.encode(test_structs[4], 4)
assert (
"0000000300000007fffefd0001020300000003abcdef00000001640000000500000000000000000000000040aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa000000070100010101010000000006000000046d756d75000000046c756c75000000046d6f6d6f00000004746f746f00000004636f636f00000004626f626f"
== encoded.hex()
)
assert test_structs[4] == obi.decode(encoded, 4)

# struct 6
encoded = obi.encode(test_structs[5], 5)
assert (
"0000000000000001000000000000000100000002000000000000000200000000000000160000000300000000000000030000000000000021000000000000014d00000001000000000000000400000001000000000000000500000002000000000000000600000000000000420000000200000000000000070000000000001e61000000010000000000000008000000010000000000000009"
== encoded.hex()
)
assert test_structs[5] == obi.decode(encoded, 5)

# struct 7
encoded = obi.encode(test_structs[6], 6)
print(encoded.hex())
assert (
"0000000261310000000261320f00000002613300000002613444000000026135000000026136c8"
== encoded.hex()
)
assert test_structs[6] == obi.decode(encoded, 6)


def test_encode_decode_not_all_data_is_consumed_fail():
with pytest.raises(ValueError) as e:
Expand Down