From 3aa4cd0b9935316bd84267fd2ccbea882483f998 Mon Sep 17 00:00:00 2001 From: Will Qiu Date: Sat, 16 Jan 2021 21:48:26 +0800 Subject: [PATCH 1/5] Bump version to 0.3.10 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b7275b2..bc3b15c 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setuptools.setup( name='pyrchain', - version='0.3.9', + version='0.3.10', author='RChain Cooperative', author_email='rchain-makers@rchain.coop', description='Interface to RChain RNode RPC', From 752643a0265d18682e133ecb437a09e8da54bbc7 Mon Sep 17 00:00:00 2001 From: Will Qiu Date: Thu, 18 Nov 2021 16:00:02 +0800 Subject: [PATCH 2/5] move generate_rev_addr_from_eth out --- rchain/crypto.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/rchain/crypto.py b/rchain/crypto.py index 7f42ada..c5f02cc 100644 --- a/rchain/crypto.py +++ b/rchain/crypto.py @@ -44,6 +44,15 @@ def verify_rev_address(rev_address: str) -> bool: return checksum == cal and len(revBytes) == REVADDR_LENGTH +def generate_rev_addr_from_eth(eth_address: str) -> str: + prefix = b'\0\0\0\0' + pub_key_hash = keccak(bytes.fromhex(eth_address)) + payload = prefix + pub_key_hash + payload_chksum = blake2b_32(payload).digest()[:4] + addr_bytes = payload + payload_chksum + return bitcoin.base58.encode(addr_bytes) + + class PublicKey: @classmethod @@ -74,13 +83,8 @@ def to_bytes(self) -> bytes: return b'\x04' + self._pub_key.to_string() def get_rev_address(self) -> str: - prefix = b'\0\0\0\0' eth_address = self.get_eth_address() - pub_key_hash = keccak(bytes.fromhex(eth_address)) - payload = prefix + pub_key_hash - payload_chksum = blake2b_32(payload).digest()[:4] - addr_bytes = payload + payload_chksum - return bitcoin.base58.encode(addr_bytes) + return generate_rev_addr_from_eth(eth_address) def get_eth_address(self) -> str: return keccak(self.to_bytes()[1:]).hex()[-40:] From d09e01239f63e6ef11bece735ef0fb8efefbf371 Mon Sep 17 00:00:00 2001 From: Will Qiu Date: Thu, 18 Nov 2021 16:00:17 +0800 Subject: [PATCH 3/5] add click package --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index bc3b15c..506a38f 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,8 @@ 'eth_hash', 'pycryptodome', 'eth-keyfile', - 'dataclasses' + 'dataclasses', + 'click' ], extras_require={ 'dev': ['grpcio-tools', 'mypy', 'typing-extensions', 'mypy-protobuf', 'isort', 'pytest', 'sphinx']}, From 09b80e6646543c5f46d27007387a6db52d08ad47 Mon Sep 17 00:00:00 2001 From: Will Qiu Date: Thu, 18 Nov 2021 16:00:40 +0800 Subject: [PATCH 4/5] add command line tool --- rchain/__main__.py | 85 ++++++++++++++++++++++++++++++++ rchain/tests/test_cli.py | 104 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 rchain/__main__.py create mode 100644 rchain/tests/test_cli.py diff --git a/rchain/__main__.py b/rchain/__main__.py new file mode 100644 index 0000000..4e083c4 --- /dev/null +++ b/rchain/__main__.py @@ -0,0 +1,85 @@ +import click + +from rchain.client import RClient +from rchain.crypto import PrivateKey, PublicKey, generate_rev_addr_from_eth +from rchain.pb.CasperMessage_pb2 import DeployDataProto +from rchain.util import create_deploy_data + + +@click.command() +@click.option('--input-type', type=click.Choice(['eth', 'public', 'private'], case_sensitive=False), + help='the kind of the input you are going to provide.') +@click.option('--input', help='the concrete content of your input type') +def get_rev_addr(input_type: str, input: str) -> None: + if input_type == 'eth': + click.echo(generate_rev_addr_from_eth(input)) + elif input_type == 'public': + pub = PublicKey.from_hex(input) + click.echo(pub.get_rev_address()) + elif input_type == 'private': + private = PrivateKey.from_hex(input) + click.echo(private.get_public_key().get_rev_address()) + else: + raise NotImplementedError("Not supported type {}".format(input_type)) + + +@click.command() +@click.option('--private-key', help='the private key hex string is used to sign') +@click.option('--term', help='the rholang term') +@click.option('--phlo-price', type=int, help='phlo price') +@click.option('--phlo-limit', type=int, help='phlo limit') +@click.option('--valid-after-block-number', type=int, + help='valid after block number, usually used the latest block number') +@click.option('--timestamp', type=int, help='timestamp, unit millisecond') +@click.option('--sig-algorithm', type=click.Choice(['secp256k1']), + help='signature algorithm. Currently only support secp256k1') +def sign_deploy(private_key: str, term: str, phlo_price: int, phlo_limit: int, valid_after_block_number: int, + timestamp: int, sig_algorithm: str) -> None: + pri = PrivateKey.from_hex(private_key) + signed_deploy = create_deploy_data( + pri, term, phlo_price, phlo_limit, valid_after_block_number, timestamp + ) + click.echo(signed_deploy.sig.hex()) + + +@click.command() +@click.option('--deployer', help='the public key hex string is used to sign') +@click.option('--term', help='the rholang term') +@click.option('--phlo-price', type=int, help='phlo price') +@click.option('--phlo-limit', type=int, help='phlo limit') +@click.option('--valid-after-block-number', type=int, + help='valid after block number, usually used the latest block number') +@click.option('--timestamp', type=int, help='timestamp, unit millisecond') +@click.option('--sig-algorithm', type=click.Choice(['secp256k1']), + help='signature algorithm. Currently only support secp256k1') # not used actually +@click.option('--sig', help='the signature of the deploy') +@click.option('--host', help='validator host the deploy is going to send to') +@click.option('--port', type=int, help='validator grpc port the deploy is going to send to') +def submit_deploy(deployer: str, term: str, phlo_price: int, phlo_limit: int, valid_after_block_number: int, + timestamp: int, sig_algorithm: str, sig: str, host: str, + port: int) -> None: + deploy = DeployDataProto( + deployer=bytes.fromhex(deployer), + term=term, + phloPrice=phlo_price, + phloLimit=phlo_limit, + validAfterBlockNumber=valid_after_block_number, + timestamp=timestamp, + sigAlgorithm='secp256k1', + sig=bytes.fromhex(sig) + ) + with RClient(host, port) as client: + client.send_deploy(deploy) + + +@click.group() +def cli() -> None: + pass + + +cli.add_command(get_rev_addr) +cli.add_command(sign_deploy) +cli.add_command(submit_deploy) + +if __name__ == '__main__': + cli() diff --git a/rchain/tests/test_cli.py b/rchain/tests/test_cli.py new file mode 100644 index 0000000..1e95778 --- /dev/null +++ b/rchain/tests/test_cli.py @@ -0,0 +1,104 @@ +import grpc +import pytest +from click.testing import CliRunner + +from rchain.__main__ import cli +from rchain.crypto import PrivateKey +from rchain.pb.CasperMessage_pb2 import DeployDataProto +from rchain.pb.DeployServiceV1_pb2 import DeployResponse +from rchain.pb.DeployServiceV1_pb2_grpc import DeployServiceServicer +from rchain.util import verify_deploy_data + +from .test_client import deploy_service + +key = PrivateKey.generate() + + +def test_get_rev_from_private(): + runner = CliRunner() + result = runner.invoke(cli, ['get-rev-addr', '--input-type', 'private', '--input', + "1000000000000000000000000000000000000000000000000000000000000000"]) + assert result.exit_code == 0 + assert result.output == '1111cnoFDAa7GubxBMHpPLbbediPegnjSdZwNjxg9oqYvSvSmfqQL\n' + + +def test_get_rev_from_pub(): + runner = CliRunner() + result = runner.invoke(cli, ['get-rev-addr', '--input-type', 'public', '--input', + "0408ea9666139527a8c1dd94ce4f071fd23c8b350c5a4bb33748c4ba111faccae0620efabbc8ee2782e24e7c0cfb95c5d735b783be9cf0f8e955af34a30e62b945"]) + assert result.exit_code == 0 + assert result.output == '1111cnoFDAa7GubxBMHpPLbbediPegnjSdZwNjxg9oqYvSvSmfqQL\n' + + +def test_get_rev_from_eth(): + runner = CliRunner() + result = runner.invoke(cli, ['get-rev-addr', '--input-type', 'eth', '--input', + "7b2419e0ee0bd034f7bf24874c12512acac6e21c"]) + assert result.exit_code == 0 + assert result.output == '1111cnoFDAa7GubxBMHpPLbbediPegnjSdZwNjxg9oqYvSvSmfqQL\n' + + +def test_get_rev_from_err(): + runner = CliRunner() + result = runner.invoke(cli, ['get-rev-addr', '--input-type', 'eth', '--input', + "7b2419e0ee0bd034f7bf24874c12512acac6e1c"]) + assert result.exit_code == 1 + assert result.output == '' + + +@pytest.mark.parametrize("key,terms,phlo_price,phlo_limit,valid_after_block_no,timestamp_millis", [ + (key, "@0!(2)", 1, 10000, 1, 1000), + (key, "@0!(2) | @1!(1)", 1, 10000, 10, 1000), + (key, "@0!(2)", 10, 200000, 10, 3000), +]) +def test_sign_deploy(key: PrivateKey, terms: str, phlo_price: int, phlo_limit: int, valid_after_block_no: int, + timestamp_millis: int): + runner = CliRunner() + result = runner.invoke(cli, ['sign-deploy', '--private-key', key.to_hex(), + '--term', terms, + "--phlo-price", phlo_price, + "--phlo-limit", phlo_limit, + "--valid-after-block-number", valid_after_block_no, + "--timestamp", timestamp_millis, + "--sig-algorithm", "secp256k1" + ]) + assert result.exit_code == 0 + sig = result.output.strip() + + data = DeployDataProto( + deployer=key.get_public_key().to_bytes(), + term=terms, + phloPrice=phlo_price, + phloLimit=phlo_limit, + validAfterBlockNumber=valid_after_block_no, + timestamp=timestamp_millis, + sigAlgorithm='secp256k1', + ) + assert verify_deploy_data(key.get_public_key(), bytes.fromhex(sig), data) + + +@pytest.mark.parametrize("key,terms,phlo_price,phlo_limit,valid_after_block_no,timestamp_millis", [ + (key, "@0!(2)", 1, 10000, 1, 1000), + (key, "@0!(2) | @1!(1)", 1, 10000, 10, 1000), + (key, "@0!(2)", 10, 200000, 10, 3000), +]) +def test_submit_deploy(key: PrivateKey, terms: str, phlo_price: int, phlo_limit: int, valid_after_block_no: int, + timestamp_millis: int): + class DummyDeploySerivce(DeployServiceServicer): + def doDeploy(self, request: DeployDataProto, context: grpc.ServicerContext) -> DeployResponse: + return DeployResponse(result=request.sig.hex()) + + with deploy_service(DummyDeploySerivce()) as (server, port): + runner = CliRunner() + result = runner.invoke(cli, ['submit-deploy', '--deployer', key.get_public_key().to_hex(), + '--term', terms, + "--phlo-price", phlo_price, + "--phlo-limit", phlo_limit, + "--valid-after-block-number", valid_after_block_no, + "--timestamp", timestamp_millis, + "--sig-algorithm", "secp256k1", + "--sig", "1111", + "--host", 'localhost', + "--port", port + ]) + assert result.exit_code == 0 From 7c3a66d9d71768b2b63ca34ef2ee002248b12b0a Mon Sep 17 00:00:00 2001 From: Will Qiu Date: Thu, 18 Nov 2021 16:06:48 +0800 Subject: [PATCH 5/5] add entry point in setup --- setup.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup.py b/setup.py index 506a38f..617c139 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,11 @@ 'dataclasses', 'click' ], + entry_points={ + 'console_scripts': [ + 'pyrchain = rchain.__main__:cli', + ], + }, extras_require={ 'dev': ['grpcio-tools', 'mypy', 'typing-extensions', 'mypy-protobuf', 'isort', 'pytest', 'sphinx']}, zip_safe=False