From 7bda908f2d170ffcaa974447bcc4bc06e23ed33e Mon Sep 17 00:00:00 2001 From: Anthony Martin Date: Mon, 24 Sep 2018 13:57:28 -0700 Subject: [PATCH 01/11] handle operation: witness_set_properties #264 --- steem/commit.py | 34 +++++++++++++ steembase/operationids.py | 1 + steembase/operations.py | 53 ++++++++++++++++++++ tests/block_data/witness_set_properties.json | 20 ++++++++ tests/steem/test_broadcast.py | 28 ++++++++++- tests/steem/test_steemd.py | 2 +- tests/steem/test_transactions.py | 28 +++++++++++ 7 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 tests/block_data/witness_set_properties.json diff --git a/steem/commit.py b/steem/commit.py index 976c0c3..3b39a51 100644 --- a/steem/commit.py +++ b/steem/commit.py @@ -1003,6 +1003,40 @@ def witness_update(self, signing_key, url, props, account=None): }) return self.finalizeOp(op, account, "active") + def witness_set_properties(self, signing_key, props, account=None): + """ Update witness + + :param pubkey signing_key: Signing key + :param dict props: Properties + :param str account: (optional) witness account name + + Properties::: + + [ + ["account_creation_fee": x] + ["maximum_block_size": x] + ["sbd_interest_rate": x] + ] + + """ + if not account: + account = configStorage.get("default_account") + if not account: + raise ValueError("You need to provide an account") + + try: + PublicKey(signing_key) + except Exception as e: + raise e + + op = operations.WitnessSetProperties( + **{ + "owner": account, + "props": props, + "extensions": [] + }) + return self.finalizeOp(op, account, "active") + def decode_memo(self, enc_memo): """ Try to decode an encrypted memo """ diff --git a/steembase/operationids.py b/steembase/operationids.py index b2744d0..195a21b 100644 --- a/steembase/operationids.py +++ b/steembase/operationids.py @@ -41,6 +41,7 @@ 'claim_reward_balance', 'delegate_vesting_shares', 'account_create_with_delegation', + 'witness_set_properties', 'fill_convert_request', 'author_reward', 'curation_reward', diff --git a/steembase/operations.py b/steembase/operations.py index d77b287..4be0e1f 100644 --- a/steembase/operations.py +++ b/steembase/operations.py @@ -1,5 +1,6 @@ import importlib import json +from binascii import hexlify, unhexlify import re import struct from collections import OrderedDict @@ -683,6 +684,58 @@ def __init__(self, *args, **kwargs): ('fee', Amount(kwargs["fee"])), ])) +class WitnessSetProperties(GrapheneObject): + """ + Based on https://github.com/holgern/beem/blob/6cc303d1b0fdfb096da78d3ff331aaa79a18ad8f/beembase/operations.py#L278-L318 + """ + def __init__(self, *args, **kwargs): + if isArgsThisClass(self, args): + self.data = args[0].data + else: + if len(args) == 1 and len(kwargs) == 0: + kwargs = args[0] + prefix = kwargs.pop("prefix", default_prefix) + extensions = Array([]) + props = {} + for k in kwargs["props"]: + if "key" == k[0]: + block_signing_key = (PublicKey(k[1], prefix=prefix)) + props["key"] = repr(block_signing_key) + elif "new_signing_key" == k[0]: + new_signing_key = (PublicKey(k[1], prefix=prefix)) + props["new_signing_key"] = repr(new_signing_key) + for k in kwargs["props"]: + if k[0] in ["key", "new_signing_key"]: + continue + if isinstance(k[1], str): + is_hex = re.fullmatch(r'[0-9a-fA-F]+', k[1] or '') is not None + else: + is_hex = False + if isinstance(k[1], int) and k[0] in ["account_subsidy_budget", "account_subsidy_decay", "maximum_block_size", "sbd_interest_rate"]: + props[k[0]] = (hexlify(Uint32(k[1]).__bytes__())).decode() + elif not isinstance(k[1], str) and k[0] in ["account_creation_fee"]: + props[k[0]] = (hexlify(Amount(k[1]).__bytes__())).decode() + elif not is_hex and isinstance(k[1], str) and k[0] in ["account_creation_fee"]: + props[k[0]] = (hexlify(Amount(k[1]).__bytes__())).decode() + elif not isinstance(k[1], str) and k[0] in ["sbd_exchange_rate"]: + props[k[0]] = (hexlify(ExchangeRate(k[1]).__bytes__())).decode() + elif not is_hex and k[0] in ["url"]: + props[k[0]] = (hexlify(String(k[1]).__bytes__())).decode() + else: + props[k[0]] = (k[1]) + props_list = [] + for k in props: + if k == "key": + props_list.append(([String(k), String(props[k])])) + else: + props_list.append(([String(k), String(props[k])])) + map_props = Map(props_list) + + super(WitnessSetProperties, self).__init__(OrderedDict([ + ('owner', String(kwargs["owner"])), + ('props', map_props), + ('extensions', extensions), + ])) class AccountWitnessVote(GrapheneObject): def __init__(self, *args, **kwargs): diff --git a/tests/block_data/witness_set_properties.json b/tests/block_data/witness_set_properties.json new file mode 100644 index 0000000..2e351e4 --- /dev/null +++ b/tests/block_data/witness_set_properties.json @@ -0,0 +1,20 @@ +{ + "ref_block_prefix": 82172829, + "expiration": "2018-09-24T19:22:18", + "operations": [ + [ + "witness_set_properties", { + "owner": "init-1", + "props": [ + ["account_creation_fee", "d0070000000000000354455354530000"], + ["key", "032d2a4af3e23294e0a1d9dbc46e0272d8e1977ce2ae3349527cc90fe1cc9c5db9"] + ] + } + ] + ], + "signatures": [ + "a1489ddbe5046a39a95b012a06696e69742d3102146163636f756e745f6372656174696f6e5f66656510d0070000000000000354455354530000036b657921032d2a4af3e23294e0a1d9dbc46e0272d8e1977ce2ae3349527cc90fe1cc9c5db9000000" + ], + "ref_block_num": 18593, + "extensions": [] +} diff --git a/tests/steem/test_broadcast.py b/tests/steem/test_broadcast.py index cb0444d..3f65082 100644 --- a/tests/steem/test_broadcast.py +++ b/tests/steem/test_broadcast.py @@ -40,8 +40,6 @@ def test_claim_reward(): def test_witness_update(): - # TODO: Remove when witness_update is fixed. - return wif = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3' c = Commit(steemd_instance=Steemd(nodes=[]), keys=[wif]) @@ -65,3 +63,29 @@ def test_witness_update(): raise Exception('expected RPCError') assert 'tx_missing_active_auth' in rpc_error + + +def test_witness_set_properties(): + wif = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3' + c = Commit(steemd_instance=Steemd(nodes=[]), + keys=[wif]) + + signing_key = 'STM1111111111111111111111111111111114T1Anm' + props = [ + ['account_creation_fee', 'd0070000000000000354455354530000'], + ['key', ('032d2a4af3e23294e0a1d9dbc46e0272d' + '8e1977ce2ae3349527cc90fe1cc9c5db9')] + ] + + rpc_error = None + try: + c.witness_set_properties( + signing_key=signing_key, + props=props, + account='test') + except RPCError as e: + rpc_error = str(e) + else: + raise Exception('expected RPCError') + + assert 'tx_missing_other_auth' in rpc_error diff --git a/tests/steem/test_steemd.py b/tests/steem/test_steemd.py index 74a8689..8dfd7a6 100644 --- a/tests/steem/test_steemd.py +++ b/tests/steem/test_steemd.py @@ -7,7 +7,7 @@ def test_get_version(): s = Steemd() response = s.call('get_version', api='login_api') version = response['blockchain_version'] - assert version[0:4] == '0.19' + assert version[0:4] == '0.20' def test_get_dgp(): diff --git a/tests/steem/test_transactions.py b/tests/steem/test_transactions.py index 21cc45b..06c545f 100644 --- a/tests/steem/test_transactions.py +++ b/tests/steem/test_transactions.py @@ -713,6 +713,34 @@ def test_witness_update(self): "b9f2405478badadb4c") self.assertEqual(compare[:-130], tx_wire[:-130]) + def test_witness_set_properties(self): + op = operations.WitnessSetProperties(**{ + "owner": "init-1", + "props": [ + ["account_creation_fee", "d0070000000000000354455354530000"], + ["key", ("032d2a4af3e23294e0a1d9dbc46e0272d" + "8e1977ce2ae3349527cc90fe1cc9c5db9")] + ] + }) + ops = [operations.Operation(op)] + tx = SignedTransaction( + ref_block_num=ref_block_num, + ref_block_prefix=ref_block_prefix, + expiration=expiration, + operations=ops) + tx = tx.sign([wif], chain=self.steem.chain_params) + tx_wire = hexlify(compat_bytes(tx)).decode("ascii") + compare = ("f68585abf4dce7c80457012a06696e69742d3102036b65794230" + "3332643261346166336532333239346530613164396462633436" + "6530323732643865313937376365326165333334393532376363" + "39306665316363396335646239146163636f756e745f63726561" + "74696f6e5f666565206430303730303030303030303030303030" + "3335343435353335343533303030300000011f3ed64264c74203" + "7955e9eb71f7de8a6bd2635251ad569629f849f23d53f2874d0a" + "879f2579bee31f34bbc7c67456e5be08ee8436f0fd5e0e4cc030" + "3030000001") + self.assertEqual(compare[:-130], tx_wire[:-130]) + def test_witness_vote(self): op = operations.AccountWitnessVote(**{ "account": "xeroc", From 252ace165a658a3548abe60cc2acec420a7b3dd2 Mon Sep 17 00:00:00 2001 From: Anthony Martin Date: Mon, 24 Sep 2018 13:57:48 -0700 Subject: [PATCH 02/11] version bump #264 --- docs/conf.py | 2 +- setup.py | 2 +- steem/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 7318dfe..4ec1d80 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -68,7 +68,7 @@ # The short X.Y version. version = '1.0' # The full version, including alpha/beta/rc tags. -release = '1.0.1' +release = '1.0.2-rc1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 615cc6a..b126ea0 100644 --- a/setup.py +++ b/setup.py @@ -131,7 +131,7 @@ def run(self): # Where the magic happens: setup( name=NAME, - version='1.0.1', + version=__import__('steem').__version__, description=DESCRIPTION, keywords=['steem', 'steemit', 'cryptocurrency', 'blockchain'], # long_description=long_description, diff --git a/steem/__init__.py b/steem/__init__.py index 54fa57c..35540d9 100644 --- a/steem/__init__.py +++ b/steem/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- from .steem import Steem -__version__ = '1.0.1' +__version__ = '1.0.2-rc1' From 04f3e231df16d4acca03d0a5f5a11f874e9b6b3d Mon Sep 17 00:00:00 2001 From: Anthony Martin Date: Mon, 24 Sep 2018 14:45:48 -0700 Subject: [PATCH 03/11] to avoid voluptuous error --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b126ea0..d364b68 100644 --- a/setup.py +++ b/setup.py @@ -131,7 +131,7 @@ def run(self): # Where the magic happens: setup( name=NAME, - version=__import__('steem').__version__, + version='1.0.2-rc1', description=DESCRIPTION, keywords=['steem', 'steemit', 'cryptocurrency', 'blockchain'], # long_description=long_description, From e11c74d659a6c13d5c1b568a91706c83de299987 Mon Sep 17 00:00:00 2001 From: Anthony Martin Date: Mon, 24 Sep 2018 19:18:38 -0700 Subject: [PATCH 04/11] for python 3.3 support --- steembase/operations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/steembase/operations.py b/steembase/operations.py index 4be0e1f..5688567 100644 --- a/steembase/operations.py +++ b/steembase/operations.py @@ -708,7 +708,7 @@ def __init__(self, *args, **kwargs): if k[0] in ["key", "new_signing_key"]: continue if isinstance(k[1], str): - is_hex = re.fullmatch(r'[0-9a-fA-F]+', k[1] or '') is not None + is_hex = re.match(r'^[0-9a-fA-F]+$', k[1] or '') is not None else: is_hex = False if isinstance(k[1], int) and k[0] in ["account_subsidy_budget", "account_subsidy_decay", "maximum_block_size", "sbd_interest_rate"]: From 7bd1ea6585888b38d6fa680d6a04643255d7a771 Mon Sep 17 00:00:00 2001 From: roadscape Date: Mon, 24 Sep 2018 21:32:59 -0500 Subject: [PATCH 05/11] bump ci From 2c4af7d2b17336b9bfd657b6fa8e6fc46d349e8d Mon Sep 17 00:00:00 2001 From: Anthony Martin Date: Tue, 25 Sep 2018 08:59:39 -0700 Subject: [PATCH 06/11] fix to enable broadcast #264 --- steembase/operations.py | 14 ++++++++------ steembase/types.py | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/steembase/operations.py b/steembase/operations.py index 5688567..d8a7fad 100644 --- a/steembase/operations.py +++ b/steembase/operations.py @@ -8,8 +8,8 @@ from steem.utils import compat_bytes from .account import PublicKey from .operationids import operations -from .types import (Int16, Uint16, Uint32, Uint64, String, Bytes, Array, - PointInTime, Bool, Optional, Map, Id, JsonObj, +from .types import (Int16, Uint16, Uint32, Uint64, String, HexString, Bytes, + Array, PointInTime, Bool, Optional, Map, Id, JsonObj, StaticVariant) default_prefix = "STM" @@ -725,10 +725,12 @@ def __init__(self, *args, **kwargs): props[k[0]] = (k[1]) props_list = [] for k in props: - if k == "key": - props_list.append(([String(k), String(props[k])])) - else: - props_list.append(([String(k), String(props[k])])) + props_list.append(([String(k), HexString(props[k])])) + props_list = sorted( + props_list, + key=lambda x: str(x[0]), + reverse=False, + ) map_props = Map(props_list) super(WitnessSetProperties, self).__init__(OrderedDict([ diff --git a/steembase/types.py b/steembase/types.py index 8ac3c0e..42585fa 100644 --- a/steembase/types.py +++ b/steembase/types.py @@ -193,6 +193,20 @@ def unicodify(self): return compat_bytes("".join(r), "utf-8") +class HexString(object): + def __init__(self, d): + self.data = d + + def __bytes__(self): + """Returns bytes representation.""" + d = bytes(unhexlify(bytes(self.data, 'ascii'))) + return varint(len(d)) + d + + def __str__(self): + """Returns data as string.""" + return '%s' % str(self.data) + + class Bytes: def __init__(self, d, length=None): self.data = d From 55219debd8d545c12319d42f099b7d0ed4c17fc6 Mon Sep 17 00:00:00 2001 From: Anthony Martin Date: Tue, 25 Sep 2018 09:00:41 -0700 Subject: [PATCH 07/11] serialized transaction now matches my local curl on get_transaction_hex #254 --- tests/steem/test_transactions.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tests/steem/test_transactions.py b/tests/steem/test_transactions.py index 06c545f..f969b73 100644 --- a/tests/steem/test_transactions.py +++ b/tests/steem/test_transactions.py @@ -730,15 +730,13 @@ def test_witness_set_properties(self): operations=ops) tx = tx.sign([wif], chain=self.steem.chain_params) tx_wire = hexlify(compat_bytes(tx)).decode("ascii") - compare = ("f68585abf4dce7c80457012a06696e69742d3102036b65794230" - "3332643261346166336532333239346530613164396462633436" - "6530323732643865313937376365326165333334393532376363" - "39306665316363396335646239146163636f756e745f63726561" - "74696f6e5f666565206430303730303030303030303030303030" - "3335343435353335343533303030300000011f3ed64264c74203" - "7955e9eb71f7de8a6bd2635251ad569629f849f23d53f2874d0a" - "879f2579bee31f34bbc7c67456e5be08ee8436f0fd5e0e4cc030" - "3030000001") + compare = ("f68585abf4dce7c80457012a06696e69742d3102146163636f75" + "6e745f6372656174696f6e5f66656510d0070000000000000354" + "455354530000036b657921032d2a4af3e23294e0a1d9dbc46e02" + "72d8e1977ce2ae3349527cc90fe1cc9c5db90000011f7797b8f7" + "3e03c04d603512f278aeceb5f76de1d0c527052886df806badb0" + "e41f55a8321abc6431c38130cc789b992e6c79ed4d403eb5906d" + "5af6d3b83626a3e7") self.assertEqual(compare[:-130], tx_wire[:-130]) def test_witness_vote(self): From 27a1cbb47c0ad750903dd036d56bab2f22f6595d Mon Sep 17 00:00:00 2001 From: Anthony Martin Date: Tue, 25 Sep 2018 09:47:46 -0700 Subject: [PATCH 08/11] compatibility with python 2.7 #264 --- steembase/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/steembase/types.py b/steembase/types.py index 42585fa..1797e1d 100644 --- a/steembase/types.py +++ b/steembase/types.py @@ -199,7 +199,7 @@ def __init__(self, d): def __bytes__(self): """Returns bytes representation.""" - d = bytes(unhexlify(bytes(self.data, 'ascii'))) + d = bytes(unhexlify(compat_bytes(self.data, 'ascii'))) return varint(len(d)) + d def __str__(self): From 448b545a21ea7d7cbd65c3a12882763c04defb67 Mon Sep 17 00:00:00 2001 From: Anthony Martin Date: Tue, 25 Sep 2018 10:13:25 -0700 Subject: [PATCH 09/11] bump ci From 5f25f066a8306b8e1e93887722547206da90b496 Mon Sep 17 00:00:00 2001 From: Anthony Martin Date: Tue, 25 Sep 2018 10:35:47 -0700 Subject: [PATCH 10/11] bump ci From ed7a41c7dd424fdf92c24d056ab91612d6d10276 Mon Sep 17 00:00:00 2001 From: Anthony Martin Date: Tue, 25 Sep 2018 10:41:13 -0700 Subject: [PATCH 11/11] bump ci