From 9917fb1109a5fabb57984823518ee605848eedfb Mon Sep 17 00:00:00 2001 From: jhtitor Date: Sat, 7 Apr 2018 05:27:10 +0000 Subject: [PATCH 1/5] Add "readwire" interface to *some* Graphene Types. This is useful when you want to *read* the serialized wire protocol back into objects-in-memory. Although this is rarely used on the client side of graphene networks, at least one particular use case (BitShares blind transfers) exists. To clarify: this is __bytes__ method in reverse. The `_readwire` interface and method signature are pretty horrid, so some redesign might be required. --- graphenebase/account.py | 12 +++++++++ graphenebase/types.py | 55 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/graphenebase/account.py b/graphenebase/account.py index efe827e3..658da89c 100644 --- a/graphenebase/account.py +++ b/graphenebase/account.py @@ -277,6 +277,18 @@ def __bytes__(self): """ Returns the raw public key (has length 33)""" return bytes(self._pk) + @staticmethod + def _readwire(d, prefix="GPH"): + _pk = hexlify(d[:33]).decode('ascii') + + k = PublicKey.__new__(PublicKey) + k.prefix = prefix + k._pk = Base58(_pk, prefix) + k.address = Address(pubkey=_pk, prefix=prefix) + k.pubkey = k._pk + + return k, d[33:] + class PrivateKey(PublicKey): """ Derives the compressed and uncompressed public keys and diff --git a/graphenebase/types.py b/graphenebase/types.py index ce8efa25..2e4e9b14 100644 --- a/graphenebase/types.py +++ b/graphenebase/types.py @@ -35,6 +35,22 @@ def varintdecode(data): return result +def varintdecode2(data): + """ Varint decoding (with length counting) + """ + nbytes = 0 + shift = 0 + result = 0 + for c in data: + b = c # ord(c) not needed, `data` is bytes + result |= ((b & 0x7f) << shift) + nbytes += 1 + if not (b & 0x80): + break + shift += 7 + return result, nbytes + + def variable_buffer(s): """ Encode variable length buffer """ @@ -90,6 +106,11 @@ def __bytes__(self): def __str__(self): return '%d' % self.data + @staticmethod + def _readwire(d): + val = struct.unpack(" Date: Wed, 18 Apr 2018 13:18:07 +0000 Subject: [PATCH 2/5] Add tests for `_readwire` interface. --- tests/test_bytes.py | 67 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tests/test_bytes.py diff --git a/tests/test_bytes.py b/tests/test_bytes.py new file mode 100644 index 00000000..75ea72cb --- /dev/null +++ b/tests/test_bytes.py @@ -0,0 +1,67 @@ +import pytest +import unittest +from binascii import hexlify, unhexlify +from graphenebase.account import PrivateKey, PublicKey +from graphenebase.types import ( + Uint32, Int64, String, Bytes, + Optional, ObjectId +) + + +wif = "5J4KCbg1G3my9b9hCaQXnHSm6vrwW9xQTJS6ZciW2Kek7cCkCEk" +pub_key = str(PrivateKey(wif).pubkey) + +class Testcases(unittest.TestCase): + + def test_read_pubkey(self): + w = bytes( PublicKey(pub_key) ) + r, b = PublicKey._readwire(w) + self.assertEqual(str(r), pub_key) + + def test_read_uint32(self): + v = 256 + w = bytes( Uint32( v ) ) + r, b = Uint32._readwire(w) + self.assertEqual(r.data, v) + + def test_read_int64(self): + v = -23 + w = bytes( Int64( v ) ) + r, b = Int64._readwire(w) + self.assertEqual(r.data, v) + + def test_read_string(self): + v = "hello world" + w = bytes( String( v ) ) + r, b = String._readwire(w) + self.assertEqual(r.data.decode('utf-8'), v) + + def test_read_bytes(self): + v = "040810172A" + w = bytes( Bytes( v ) ) + r, b = Bytes._readwire(w) + self.assertEqual(r.data, unhexlify(v)) + + def test_read_objectid(self): + v = "1.2.0" + w = bytes( ObjectId( v ) ) + r, b = ObjectId._readwire(w) + self.assertEqual(r.Id, v) + + def test_read_optional_none(self): + v = None + w = bytes( Optional( None ) ) + r, b = Optional._readwire(w, String) + self.assertEqual(r.data, v) + + def test_read_optional_string(self): + v = "hello world" + w = bytes( Optional( String( v ) ) ) + r, b = Optional._readwire(w, String) + r = r.data + self.assertEqual(r.data.decode('utf-8'), v) + + + +if __name__ == '__main__': + unittest.main() From cca3294348bc546aa33c4e796f28644727da6c60 Mon Sep 17 00:00:00 2001 From: jhtitor Date: Thu, 11 Oct 2018 18:25:35 +0000 Subject: [PATCH 3/5] Rename `_readwire` to `fromBytes`. React to latest upstream fixes. --- graphenebase/account.py | 8 ++------ graphenebase/types.py | 16 ++++++++-------- tests/test_bytes.py | 16 ++++++++-------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/graphenebase/account.py b/graphenebase/account.py index ffea12f7..f492255f 100644 --- a/graphenebase/account.py +++ b/graphenebase/account.py @@ -332,14 +332,10 @@ def __bytes__(self): return bytes(self._pk) @staticmethod - def _readwire(d, prefix="GPH"): + def fromBytes(d, prefix="GPH"): _pk = hexlify(d[:33]).decode('ascii') - k = PublicKey.__new__(PublicKey) - k.prefix = prefix - k._pk = Base58(_pk, prefix) - k.address = Address(pubkey=_pk, prefix=prefix) - k.pubkey = k._pk + k = PublicKey(_pk) return k, d[33:] diff --git a/graphenebase/types.py b/graphenebase/types.py index f3c6246a..eb5174ef 100644 --- a/graphenebase/types.py +++ b/graphenebase/types.py @@ -105,7 +105,7 @@ def __str__(self): return '%d' % self.data @staticmethod - def _readwire(d): + def fromBytes(d): val = struct.unpack(" Date: Thu, 11 Oct 2018 19:10:37 +0000 Subject: [PATCH 4/5] Restore Fixed_Bytes type. --- graphenebase/types.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/graphenebase/types.py b/graphenebase/types.py index eb5174ef..bcf85e02 100644 --- a/graphenebase/types.py +++ b/graphenebase/types.py @@ -192,6 +192,32 @@ def fromBytes(d): return Bytes(val), d[vallen:] +class Fixed_Bytes(): + def __init__(self, d, length=None): + if isinstance(d, str): + d = unhexlify(bytes(d, 'utf-8')) + self.data = d + if length: + self.length = length + else: + self.length = len(self.data) + + def __bytes__(self): + # TODO: constrain to self.length + return self.data + + def __str__(self): + return str(hexlify(self.data), 'utf-8') + + def __json__(self): + return str(self) + + @staticmethod + def fromBytes(d, vallen): + val = d[:vallen] + return Fixed_Bytes(val, vallen), d[vallen:] + + class Void(): def __init__(self): pass From 5be5038bc162c2127f927926d4bbd74fe042ff82 Mon Sep 17 00:00:00 2001 From: jhtitor Date: Thu, 11 Oct 2018 19:14:48 +0000 Subject: [PATCH 5/5] Add unit test for Fixed_Bytes fromBytes. --- tests/test_bytes.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_bytes.py b/tests/test_bytes.py index 666b3775..c97ff1be 100644 --- a/tests/test_bytes.py +++ b/tests/test_bytes.py @@ -4,6 +4,7 @@ from graphenebase.account import PrivateKey, PublicKey from graphenebase.types import ( Uint32, Int64, String, Bytes, + Fixed_Bytes, Optional, ObjectId ) @@ -42,6 +43,12 @@ def test_read_bytes(self): r, b = Bytes.fromBytes(w) self.assertEqual(r.data, unhexlify(v)) + def test_read_fixedbytes(self): + v = "0408" + w = bytes( Fixed_Bytes( v, 4 ) ) + r, b = Fixed_Bytes.fromBytes(w, 4) + self.assertEqual(r.data, unhexlify(v)) + def test_read_objectid(self): v = "1.2.0" w = bytes( ObjectId( v ) )