Skip to content

Commit

Permalink
Merge pull request #76 from zsluedem/cmd
Browse files Browse the repository at this point in the history
add pyrchain command line support
  • Loading branch information
zsluedem authored Nov 18, 2021
2 parents 9069174 + 7c3a66d commit d0cba64
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 8 deletions.
85 changes: 85 additions & 0 deletions rchain/__main__.py
Original file line number Diff line number Diff line change
@@ -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()
16 changes: 10 additions & 6 deletions rchain/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:]
Expand Down
104 changes: 104 additions & 0 deletions rchain/tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -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
10 changes: 8 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

setuptools.setup(
name='pyrchain',
version='0.3.9',
version='0.3.10',
author='RChain Cooperative',
author_email='[email protected]',
description='Interface to RChain RNode RPC',
Expand All @@ -22,8 +22,14 @@
'eth_hash',
'pycryptodome',
'eth-keyfile',
'dataclasses'
'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
Expand Down

0 comments on commit d0cba64

Please sign in to comment.