diff --git a/.gitignore b/.gitignore index 836b3bb8..e139e8a1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ venv aepp_sdk.egg-info dist build +coverage.xml +docker-compose.override.yml +test-results.xml diff --git a/aeternity/__main__.py b/aeternity/__main__.py index 518813cb..f42d977c 100644 --- a/aeternity/__main__.py +++ b/aeternity/__main__.py @@ -4,6 +4,7 @@ import json import sys import getpass +import namedtupled from aeternity import __version__ @@ -130,7 +131,7 @@ def _print_object(data, title=None): if ctx.obj.get(CTX_OUTPUT_JSON, False): if isinstance(data, tuple): - print(json.dumps(data._asdict(), indent=2)) + print(json.dumps(namedtupled.reduce(data), indent=2)) return if isinstance(data, str): print(data) @@ -160,8 +161,8 @@ def _print_object(data, title=None): ] _transaction_options = [ - click.option('--native', 'native', is_flag=True, default=False, - help='Use native transaction generation instead of the internal endpoints (always True for offline transactions)'), + click.option('--debug-tx', 'debug_tx', is_flag=True, default=False, + help='Use debug transaction generation endpoints from the node instead of the native python implementation'), click.option('--ttl', 'ttl', type=int, default=config.DEFAULT_TX_TTL, help=f'Set the transaction ttl (relative number, ex 100)', show_default=True), click.option('--fee', 'fee', type=int, default=config.DEFAULT_FEE, @@ -408,11 +409,11 @@ def tx_broadcast(signed_transaction, force, wait, json_): @click.argument('amount', required=True, type=int) @transaction_options @click.option('--payload', default="", help="Spend transaction payload") -def tx_spend(sender_id, recipient_id, amount, native, ttl, fee, nonce, payload, force, wait, json_): +def tx_spend(sender_id, recipient_id, amount, debug_tx, ttl, fee, nonce, payload, force, wait, json_): try: set_global_options(force, wait, json_) - cli = _node_cli(native=native) - if not native: + cli = _node_cli(native=not debug_tx) + if debug_tx: nonce, ttl = cli._get_nonce_ttl(sender_id, ttl) tx = cli.tx_builder.tx_spend(sender_id, recipient_id, amount, payload, fee, ttl, nonce) # print the results diff --git a/aeternity/aens.py b/aeternity/aens.py index 6e461b7b..cc5da3a4 100644 --- a/aeternity/aens.py +++ b/aeternity/aens.py @@ -2,6 +2,7 @@ from aeternity.openapi import OpenAPIClientException from aeternity.config import DEFAULT_TX_TTL, DEFAULT_FEE, DEFAULT_NAME_TTL, NAME_CLIENT_TTL from aeternity import hashing, utils, oracles +from aeternity.identifiers import ACCOUNT_ID, COMMITMENT, NAME class NameStatus: @@ -49,18 +50,18 @@ def _get_name_id(cls, name): """ if isinstance(name, str): name = name.encode('ascii') - return hashing.encode('nm', name) + return hashing.encode(NAME, name) def _get_commitment_id(self): """ Compute the commitment id """ self.preclaim_salt = hashing.randint() - commitment_id = hashing.hash_encode("cm", hashing.namehash(self.domain) + self.preclaim_salt.to_bytes(32, 'big')) + commitment_id = hashing.hash_encode(COMMITMENT, hashing.namehash(self.domain) + self.preclaim_salt.to_bytes(32, 'big')) return commitment_id def _get_pointers(self, target): - if target.startswith('ak'): + if target.startswith(ACCOUNT_ID): pointers = [{'id': target, 'key': 'account_pubkey'}] else: pointers = [{'id': target, 'key': 'oracle_pubkey'}] @@ -196,7 +197,7 @@ def update(self, account, target, raise ValueError('You must register the oracle before using it as target') target = target.oracle_id # get the name_id and pointers - name_id = hashing.namehash_encode("nm", self.domain) + name_id = hashing.namehash_encode(NAME, self.domain) pointers = self._get_pointers(target) # get the transaction builder txb = self.client.tx_builder @@ -216,7 +217,7 @@ def transfer_ownership(self, account, recipient_pubkey, fee=DEFAULT_FEE, tx_ttl= :return: the transaction """ # get the name_id and pointers - name_id = hashing.namehash_encode("nm", self.domain) + name_id = hashing.namehash_encode(NAME, self.domain) # get the transaction builder txb = self.client.tx_builder # get the account nonce and ttl @@ -237,7 +238,7 @@ def revoke(self, account, fee=DEFAULT_FEE, tx_ttl=DEFAULT_TX_TTL): :return: the transaction """ # get the name_id and pointers - name_id = hashing.namehash_encode("nm", self.domain) + name_id = hashing.namehash_encode(NAME, self.domain) # get the transaction builder txb = self.client.tx_builder # get the account nonce and ttl diff --git a/aeternity/aevm.py b/aeternity/aevm.py deleted file mode 100644 index 6a6b8500..00000000 --- a/aeternity/aevm.py +++ /dev/null @@ -1,163 +0,0 @@ - -# AEVM opcodes -# -# as defined in /node/apps/aebytecode/include/aeb_opcodes.hrl -# -import binascii - -# based on https://github.com/holiman/evmlab/blob/master/evmlab/opcodes.py -# -# gas prices might differ for AE - -# schema: [opcode, ins, outs, gas, arguments] -OPCODES = { - 0x00: ('STOP', 0, 0, 0, 0), - 0x01: ('ADD', 2, 1, 3, 0), - 0x02: ('MUL', 2, 1, 5, 0), - 0x03: ('SUB', 2, 1, 3, 0), - 0x04: ('DIV', 2, 1, 5, 0), - 0x05: ('SDIV', 2, 1, 5, 0), - 0x06: ('MOD', 2, 1, 5, 0), - 0x07: ('SMOD', 2, 1, 5, 0), - 0x08: ('ADDMOD', 3, 1, 8, 0), - 0x09: ('MULMOD', 3, 1, 8, 0), - 0x0a: ('EXP', 2, 1, 10, 0), - 0x0b: ('SIGNEXTEND', 2, 1, 5, 0), - 0x10: ('LT', 2, 1, 3, 0), - 0x11: ('GT', 2, 1, 3, 0), - 0x12: ('SLT', 2, 1, 3, 0), - 0x13: ('SGT', 2, 1, 3, 0), - 0x14: ('EQ', 2, 1, 3, 0), - 0x15: ('ISZERO', 1, 1, 3, 0), - 0x16: ('AND', 2, 1, 3, 0), - 0x17: ('OR', 2, 1, 3, 0), - 0x18: ('XOR', 2, 1, 3, 0), - 0x19: ('NOT', 1, 1, 3, 0), - 0x1a: ('BYTE', 2, 1, 3, 0), - 0x20: ('SHA3', 2, 1, 30, 0), - 0x30: ('ADDRESS', 0, 1, 2, 0), - 0x31: ('BALANCE', 1, 1, 20, 0), - 0x32: ('ORIGIN', 0, 1, 2, 0), - 0x33: ('CALLER', 0, 1, 2, 0), - 0x34: ('CALLVALUE', 0, 1, 2, 0), - 0x35: ('CALLDATALOAD', 1, 1, 3, 0), - 0x36: ('CALLDATASIZE', 0, 1, 2, 0), - 0x37: ('CALLDATACOPY', 3, 0, 3, 0), - 0x38: ('CODESIZE', 0, 1, 2, 0), - 0x39: ('CODECOPY', 3, 0, 3, 0), - 0x3a: ('GASPRICE', 0, 1, 2, 0), - 0x3b: ('EXTCODESIZE', 1, 1, 20, 0), - 0x3c: ('EXTCODECOPY', 4, 0, 20, 0), - 0x3d: ('RETURNDATASIZE', 0, 1, 2, 0), - 0x3e: ('RETURNDATACOPY', 3, 0, 3, 0), - 0x40: ('BLOCKHASH', 1, 1, 20, 0), - 0x41: ('COINBASE', 0, 1, 2, 0), - 0x42: ('TIMESTAMP', 0, 1, 2, 0), - 0x43: ('NUMBER', 0, 1, 2, 0), - 0x44: ('DIFFICULTY', 0, 1, 2, 0), - 0x45: ('GASLIMIT', 0, 1, 2, 0), - 0x50: ('POP', 1, 0, 2, 0), - 0x51: ('MLOAD', 1, 1, 3, 0), - 0x52: ('MSTORE', 2, 0, 3, 0), - 0x53: ('MSTORE8', 2, 0, 3, 0), - 0x54: ('SLOAD', 1, 1, 50, 0), - 0x55: ('SSTORE', 2, 0, 0, 0), - 0x56: ('JUMP', 1, 0, 8, 0), - 0x57: ('JUMPI', 2, 0, 10, 0), - 0x58: ('PC', 0, 1, 2, 0), - 0x59: ('MSIZE', 0, 1, 2, 0), - 0x5a: ('GAS', 0, 1, 2, 0), - 0x5b: ('JUMPDEST', 0, 0, 1, 0), - - 0x60: ('PUSH1', 0, 0, 0, 1), - 0x61: ('PUSH2', 0, 0, 0, 2), - 0x62: ('PUSH3', 0, 0, 0, 3), - 0x63: ('PUSH4', 0, 0, 0, 4), - 0x64: ('PUSH5', 0, 0, 0, 5), - 0x65: ('PUSH6', 0, 0, 0, 6), - 0x66: ('PUSH7', 0, 0, 0, 7), - 0x67: ('PUSH8', 0, 0, 0, 8), - 0x68: ('PUSH9', 0, 0, 0, 9), - 0x69: ('PUSH10', 0, 0, 0, 10), - 0x6a: ('PUSH11', 0, 0, 0, 11), - 0x6b: ('PUSH12', 0, 0, 0, 12), - 0x6c: ('PUSH13', 0, 0, 0, 13), - 0x6d: ('PUSH14', 0, 0, 0, 14), - 0x6e: ('PUSH15', 0, 0, 0, 15), - 0x6f: ('PUSH16', 0, 0, 0, 16), - 0x70: ('PUSH17', 0, 0, 0, 17), - 0x71: ('PUSH18', 0, 0, 0, 18), - 0x72: ('PUSH19', 0, 0, 0, 19), - 0x73: ('PUSH20', 0, 0, 0, 20), - 0x74: ('PUSH21', 0, 0, 0, 21), - 0x75: ('PUSH22', 0, 0, 0, 22), - 0x76: ('PUSH23', 0, 0, 0, 23), - 0x77: ('PUSH24', 0, 0, 0, 24), - 0x78: ('PUSH25', 0, 0, 0, 25), - 0x79: ('PUSH26', 0, 0, 0, 26), - 0x7a: ('PUSH27', 0, 0, 0, 27), - 0x7b: ('PUSH28', 0, 0, 0, 28), - 0x7c: ('PUSH29', 0, 0, 0, 29), - 0x7d: ('PUSH30', 0, 0, 0, 30), - 0x7e: ('PUSH31', 0, 0, 0, 31), - 0x7f: ('PUSH32', 0, 0, 0, 32), - 0x80: ('DUP1', 0, 0, 0, 0), - 0x81: ('DUP2', 0, 0, 0, 0), - 0x82: ('DUP3', 0, 0, 0, 0), - 0x83: ('DUP4', 0, 0, 0, 0), - 0x84: ('DUP5', 0, 0, 0, 0), - 0x85: ('DUP6', 0, 0, 0, 0), - 0x86: ('DUP7', 0, 0, 0, 0), - 0x87: ('DUP8', 0, 0, 0, 0), - 0x88: ('DUP9', 0, 0, 0, 0), - 0x89: ('DUP10', 0, 0, 0, 0), - 0x8a: ('DUP11', 0, 0, 0, 0), - 0x8b: ('DUP12', 0, 0, 0, 0), - 0x8c: ('DUP13', 0, 0, 0, 0), - 0x8d: ('DUP14', 0, 0, 0, 0), - 0x8e: ('DUP15', 0, 0, 0, 0), - 0x8f: ('DUP16', 0, 0, 0, 0), - 0x90: ('SWAP1', 0, 0, 0, 0), - 0x91: ('SWAP2', 0, 0, 0, 0), - 0x92: ('SWAP3', 0, 0, 0, 0), - 0x93: ('SWAP4', 0, 0, 0, 0), - 0x94: ('SWAP5', 0, 0, 0, 0), - 0x95: ('SWAP6', 0, 0, 0, 0), - 0x96: ('SWAP7', 0, 0, 0, 0), - 0x97: ('SWAP8', 0, 0, 0, 0), - 0x98: ('SWAP9', 0, 0, 0, 0), - - 0xa0: ('LOG0', 2, 0, 375, 0), - 0xa1: ('LOG1', 3, 0, 750, 0), - 0xa2: ('LOG2', 4, 0, 1125, 0), - 0xa3: ('LOG3', 5, 0, 1500, 0), - 0xa4: ('LOG4', 6, 0, 1875, 0), - 0xf0: ('CREATE', 3, 1, 32000, 0), - 0xf1: ('CALL', 7, 1, 40, 0), - 0xf2: ('CALLCODE', 7, 1, 40, 0), - 0xf3: ('RETURN', 2, 0, 0, 0), - 0xf4: ('DELEGATECALL', 6, 0, 40, 0), - 0xfa: ('STATICCALL', 6, 1, 40, 0), - 0xfd: ('REVERT', 2, 0, 0, 0), - 0xff: ('SUICIDE', 1, 0, 0, 0), -} - - -def pretty_bytecode(bytecode_as_hex): - if bytecode_as_hex.startswith('0x'): - bytecode_as_hex = bytecode_as_hex[2:] - bytecode_as_bytes = binascii.unhexlify(bytecode_as_hex) - - i = 0 - while i < len(bytecode_as_bytes): - opcode = bytecode_as_bytes[i] - name, ins, outs, gas, consumes = OPCODES.get(opcode, (f'UNKNOWN ({opcode})!', 0, 0, 0)) - arguments = bytecode_as_bytes[i + 1:i + 1 + consumes] - arguments_ascii = arguments.decode('ascii', errors='replace') - arguments_hex = binascii.hexlify(arguments).decode('ascii') - pos = '0x%04x' % i - if arguments: - print(pos, name, arguments_hex, f'"{arguments_ascii}"') - else: - print(pos, name) - i += 1 + consumes diff --git a/aeternity/config.py b/aeternity/config.py index 3c752416..a79f7143 100644 --- a/aeternity/config.py +++ b/aeternity/config.py @@ -18,18 +18,18 @@ DEFAULT_NAME_TTL = 500 # default relative ttl in number of blocks for executing transaction on the chain MAX_TX_TTL = sys.maxsize -DEFAULT_TX_TTL = 500 +DEFAULT_TX_TTL = 0 # default fee for posting transaction DEFAULT_FEE = 20000 # contracts CONTRACT_DEFAULT_GAS = 170000 CONTRACT_DEFAULT_GAS_PRICE = 1 -CONTRACT_DEFAULT_DEPOSIT = 4 +CONTRACT_DEFAULT_DEPOSIT = 0 CONTRACT_DEFAULT_VM_VERSION = 1 -CONTRACT_DEFAULT_AMOUNT = 1 +CONTRACT_DEFAULT_AMOUNT = 0 # oracles # https://github.com/aeternity/protocol/blob/master/oracles/oracles.md#technical-aspects-of-oracle-operations -ORACLE_DEFAULT_QUERY_FEE = 30000 +ORACLE_DEFAULT_QUERY_FEE = 0 ORACLE_DEFAULT_TTL_TYPE_DELTA = 'delta' ORACLE_DEFAULT_TTL_TYPE_BLOCK = 'block' ORACLE_DEFAULT_TTL_VALUE = 500 diff --git a/aeternity/contract.py b/aeternity/contract.py index 3b42bfc1..38b29a0a 100644 --- a/aeternity/contract.py +++ b/aeternity/contract.py @@ -1,6 +1,6 @@ -from aeternity.aevm import pretty_bytecode from aeternity.openapi import OpenAPIClientException from aeternity import utils, config +from aeternity.identifiers import CONTRACT_ID class ContractError(Exception): @@ -46,7 +46,7 @@ def tx_call(self, account, function, arg, tx_ttl=config.DEFAULT_TX_TTL): """Call a sophia contract""" - if not utils.is_valid_hash(self.address, prefix="ct"): + if not utils.is_valid_hash(self.address, prefix=CONTRACT_ID): raise ValueError("Missing contract id") try: @@ -119,7 +119,7 @@ def compile(self, code, options=''): raise ContractError(e) def get_pretty_bytecode(self, code, options=''): - return pretty_bytecode(self.bytecode) + return self.bytecode def call(self, function, arg): '''"Call a sophia function with a given name and argument in the given @@ -164,6 +164,6 @@ def decode_data(self, data, sophia_type): "data": data, "sophia-type": sophia_type, }) - return reply.data.get('value'), reply.data.get('type', 'word') + return reply.data.value, reply.data.type except OpenAPIClientException as e: raise ContractError(e) diff --git a/aeternity/identifiers.py b/aeternity/identifiers.py index a2075695..4df01520 100644 --- a/aeternity/identifiers.py +++ b/aeternity/identifiers.py @@ -2,19 +2,19 @@ # https://github.com/aeternity/protocol/blob/14db60b7db4d51618ebad539aaf6a0b40b72e633/epoch/api/api_encoding.md#encoding-scheme-for-api-identifiers-and-byte-arrays # base58 -ACCOUNT_PUBKEY = "ak" # base58 Account pubkey +ACCOUNT_ID = "ak" # base58 Account pubkey BLOCK_PROOF_OF_FRAUD_HASH = "bf" # base58 Block Proof of Fraud hash BLOCK_STATE_HASH = "bs" # base58 Block State hash BLOCK_TRANSACTION_HASH = "bx" # base58 Block transaction hash CHANNEL = "ch" # base58 Channel COMMITMENT = "cm" # base58 Commitment -CONTRACT_PUBKEY = "ct" # base58 Contract pubkey +CONTRACT_ID = "ct" # base58 Contract pubkey KEY_BLOCK_HASH = "kh" # base58 Key block hash MICRO_BLOCK_HASH = "mh" # base58 Micro block hash NAME = "nm" # base58 Name -ORACLE_PUBKEY = "ok" # base58 Oracle pubkey +ORACLE_ID = "ok" # base58 Oracle pubkey ORACLE_QUERY_ID = "oq" # base58 Oracle query id -PEER_PUBKEY = "pp" # base58 Peer pubkey +PEER_ID = "pp" # base58 Peer pubkey SIGNATURE = "sg" # base58 Signature TRANSACTION_HASH = "th" # base58 Transaction hash @@ -28,19 +28,19 @@ TRANSACTION = "tx" # base64 Transaction IDENTIFIERS_B58 = set([ - ACCOUNT_PUBKEY, + ACCOUNT_ID, BLOCK_PROOF_OF_FRAUD_HASH, BLOCK_STATE_HASH, BLOCK_TRANSACTION_HASH, CHANNEL, COMMITMENT, - CONTRACT_PUBKEY, + CONTRACT_ID, KEY_BLOCK_HASH, MICRO_BLOCK_HASH, NAME, - ORACLE_PUBKEY, + ORACLE_ID, ORACLE_QUERY_ID, - PEER_PUBKEY, + PEER_ID, SIGNATURE, TRANSACTION_HASH, ]) @@ -55,3 +55,63 @@ STATE, TRANSACTION ]) + + +# RLP Identifiers + +# RLP version number +# https://github.com/aeternity/protocol/blob/api-v0.10.1/serializations.md#binary-serialization +VSN = 1 + +# Tag constant for ids (type uint8) +# see https://github.com/aeternity/protocol/blob/master/serializations.md#the-id-type +# <> +ID_TAG_ACCOUNT = 1 +ID_TAG_NAME = 2 +ID_TAG_COMMITMENT = 3 +ID_TAG_ORACLE = 4 +ID_TAG_CONTRACT = 5 +ID_TAG_CHANNEL = 6 + +# Object tags +# see https://github.com/aeternity/protocol/blob/master/serializations.md#binary-serialization + +OBJECT_TAG_ACCOUNT = 10 +OBJECT_TAG_SIGNED_TRANSACTION = 11 +OBJECT_TAG_SPEND_TRANSACTION = 12 +OBJECT_TAG_ORACLE = 20 +OBJECT_TAG_ORACLE_QUERY = 21 +OBJECT_TAG_ORACLE_REGISTER_TRANSACTION = 22 +OBJECT_TAG_ORACLE_QUERY_TRANSACTION = 23 +OBJECT_TAG_ORACLE_RESPONSE_TRANSACTION = 24 +OBJECT_TAG_ORACLE_EXTEND_TRANSACTION = 25 +OBJECT_TAG_NAME_SERVICE_NAME = 30 +OBJECT_TAG_NAME_SERVICE_COMMITMENT = 31 +OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION = 32 +OBJECT_TAG_NAME_SERVICE_PRECLAIM_TRANSACTION = 33 +OBJECT_TAG_NAME_SERVICE_UPDATE_TRANSACTION = 34 +OBJECT_TAG_NAME_SERVICE_REVOKE_TRANSACTION = 35 +OBJECT_TAG_NAME_SERVICE_TRANSFER_TRANSACTION = 36 +OBJECT_TAG_CONTRACT = 40 +OBJECT_TAG_CONTRACT_CALL = 41 +OBJECT_TAG_CONTRACT_CREATE_TRANSACTION = 42 +OBJECT_TAG_CONTRACT_CALL_TRANSACTION = 43 +OBJECT_TAG_CHANNEL_CREATE_TRANSACTION = 50 +OBJECT_TAG_CHANNEL_DEPOSIT_TRANSACTION = 51 +OBJECT_TAG_CHANNEL_WITHDRAW_TRANSACTION = 52 +OBJECT_TAG_CHANNEL_FORCE_PROGRESS_TRANSACTION = 521 +OBJECT_TAG_CHANNEL_CLOSE_MUTUAL_TRANSACTION = 53 +OBJECT_TAG_CHANNEL_CLOSE_SOLO_TRANSACTION = 54 +OBJECT_TAG_CHANNEL_SLASH_TRANSACTION = 55 +OBJECT_TAG_CHANNEL_SETTLE_TRANSACTION = 56 +OBJECT_TAG_CHANNEL_OFF_CHAIN_TRANSACTION = 57 +OBJECT_TAG_CHANNEL_OFF_CHAIN_UPDATE_TRANSFER = 570 +OBJECT_TAG_CHANNEL_OFF_CHAIN_UPDATE_DEPOSIT = 571 +OBJECT_TAG_CHANNEL_OFF_CHAIN_UPDATE_WITHDRAWAL = 572 +OBJECT_TAG_CHANNEL_OFF_CHAIN_UPDATE_CREATE_CONTRACT = 573 +OBJECT_TAG_CHANNEL_OFF_CHAIN_UPDATE_CALL_CONTRACT = 574 +OBJECT_TAG_CHANNEL = 58 +OBJECT_TAG_CHANNEL_SNAPSHOT_TRANSACTION = 59 +OBJECT_TAG_POI = 60 +OBJECT_TAG_MICRO_BODY = 101 +OBJECT_TAG_LIGHT_MICRO_BLOCK = 102 diff --git a/aeternity/node.py b/aeternity/node.py index c14df1ce..4b608baa 100644 --- a/aeternity/node.py +++ b/aeternity/node.py @@ -1,7 +1,6 @@ import logging import time import random -from collections import namedtuple from aeternity.transactions import TxSigner from aeternity.signing import Account @@ -115,10 +114,7 @@ def get_top_block(self): to a Block """ b = self.api.get_top_block() - if hasattr(b, 'key_block'): - return namedtuple('Block', sorted(b.key_block))(**b.key_block) - else: - return namedtuple('Block', sorted(b.micro_block))(**b.micro_block) + return b.key_block if hasattr(b, 'key_block') else b.micro_block def get_block_by_hash(self, hash=None): """ diff --git a/aeternity/openapi.py b/aeternity/openapi.py index e2708a61..4bca1e23 100644 --- a/aeternity/openapi.py +++ b/aeternity/openapi.py @@ -2,6 +2,7 @@ import requests import keyword from collections import namedtuple +import namedtupled import logging from aeternity.exceptions import UnsupportedEpochVersion, ConfigException @@ -202,7 +203,7 @@ def api_method(*args, **kwargs): raw = http_reply.json() return list(raw.values())[0] jr = http_reply.json() - return namedtuple(api_response.schema, jr.keys())(**jr) + return namedtupled.map(jr, _nt_name=api_response.schema) # error raise OpenAPIClientException(f"Error: {api_response.desc}", code=http_reply.status_code) # register the method diff --git a/aeternity/oracles.py b/aeternity/oracles.py index 9161bdeb..990f9e90 100644 --- a/aeternity/oracles.py +++ b/aeternity/oracles.py @@ -1,5 +1,6 @@ import logging from aeternity import config, hashing +from aeternity.identifiers import ORACLE_ID logger = logging.getLogger(__name__) @@ -89,7 +90,7 @@ def register(self, account, query_format, response_format, # register the oracle id # the oracle id is the account that register the oracle # with the prefix substituted by with ok_ - self.id = f"ok_{account.get_address()[3:]}" + self.id = f"{ORACLE_ID}_{account.get_address()[3:]}" # return the transaction return tx, tx_signed, sg, tx_hash diff --git a/aeternity/signing.py b/aeternity/signing.py index 165c0b8f..2f2f6b2a 100644 --- a/aeternity/signing.py +++ b/aeternity/signing.py @@ -9,6 +9,7 @@ from nacl.exceptions import CryptoError from nacl.pwhash import argon2id from nacl import secret, utils as nacl_utils +from aeternity.identifiers import ACCOUNT_ID from aeternity import hashing, utils @@ -21,7 +22,7 @@ def __init__(self, signing_key, verifying_key): self.signing_key = signing_key self.verifying_key = verifying_key pub_key = self.verifying_key.encode(encoder=RawEncoder) - self.address = hashing.encode("ak", pub_key) + self.address = hashing.encode(ACCOUNT_ID, pub_key) def get_address(self): """get the keypair public_key base58 encoded and prefixed (ak_...)""" @@ -107,7 +108,7 @@ def generate(cls): def _raw_key(cls, key_string): """decode a key with different method between signing and addresses""" key_string = str(key_string) - if utils.is_valid_hash(key_string, prefix='ak'): + if utils.is_valid_hash(key_string, prefix=ACCOUNT_ID): return hashing.decode(key_string.strip()) return bytes.fromhex(key_string.strip()) diff --git a/aeternity/transactions.py b/aeternity/transactions.py index 6993e174..1ff51414 100644 --- a/aeternity/transactions.py +++ b/aeternity/transactions.py @@ -1,63 +1,7 @@ from aeternity.hashing import _int, _binary, _id, encode, decode, encode_rlp, hash_encode, contract_id from aeternity.openapi import OpenAPICli from aeternity.config import ORACLE_DEFAULT_TTL_TYPE_DELTA - -# RLP version number -# https://github.com/aeternity/protocol/blob/api-v0.10.1/serializations.md#binary-serialization -VSN = 1 - -# Tag constant for ids (type uint8) -# see https://github.com/aeternity/protocol/blob/master/serializations.md#the-id-type -# <> -ID_TAG_ACCOUNT = 1 -ID_TAG_NAME = 2 -ID_TAG_COMMITMENT = 3 -ID_TAG_ORACLE = 4 -ID_TAG_CONTRACT = 5 -ID_TAG_CHANNEL = 6 - -# Object tags -# see https://github.com/aeternity/protocol/blob/master/serializations.md#binary-serialization - -OBJECT_TAG_ACCOUNT = 10 -OBJECT_TAG_SIGNED_TRANSACTION = 11 -OBJECT_TAG_SPEND_TRANSACTION = 12 -OBJECT_TAG_ORACLE = 20 -OBJECT_TAG_ORACLE_QUERY = 21 -OBJECT_TAG_ORACLE_REGISTER_TRANSACTION = 22 -OBJECT_TAG_ORACLE_QUERY_TRANSACTION = 23 -OBJECT_TAG_ORACLE_RESPONSE_TRANSACTION = 24 -OBJECT_TAG_ORACLE_EXTEND_TRANSACTION = 25 -OBJECT_TAG_NAME_SERVICE_NAME = 30 -OBJECT_TAG_NAME_SERVICE_COMMITMENT = 31 -OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION = 32 -OBJECT_TAG_NAME_SERVICE_PRECLAIM_TRANSACTION = 33 -OBJECT_TAG_NAME_SERVICE_UPDATE_TRANSACTION = 34 -OBJECT_TAG_NAME_SERVICE_REVOKE_TRANSACTION = 35 -OBJECT_TAG_NAME_SERVICE_TRANSFER_TRANSACTION = 36 -OBJECT_TAG_CONTRACT = 40 -OBJECT_TAG_CONTRACT_CALL = 41 -OBJECT_TAG_CONTRACT_CREATE_TRANSACTION = 42 -OBJECT_TAG_CONTRACT_CALL_TRANSACTION = 43 -OBJECT_TAG_CHANNEL_CREATE_TRANSACTION = 50 -OBJECT_TAG_CHANNEL_DEPOSIT_TRANSACTION = 51 -OBJECT_TAG_CHANNEL_WITHDRAW_TRANSACTION = 52 -OBJECT_TAG_CHANNEL_FORCE_PROGRESS_TRANSACTION = 521 -OBJECT_TAG_CHANNEL_CLOSE_MUTUAL_TRANSACTION = 53 -OBJECT_TAG_CHANNEL_CLOSE_SOLO_TRANSACTION = 54 -OBJECT_TAG_CHANNEL_SLASH_TRANSACTION = 55 -OBJECT_TAG_CHANNEL_SETTLE_TRANSACTION = 56 -OBJECT_TAG_CHANNEL_OFF_CHAIN_TRANSACTION = 57 -OBJECT_TAG_CHANNEL_OFF_CHAIN_UPDATE_TRANSFER = 570 -OBJECT_TAG_CHANNEL_OFF_CHAIN_UPDATE_DEPOSIT = 571 -OBJECT_TAG_CHANNEL_OFF_CHAIN_UPDATE_WITHDRAWAL = 572 -OBJECT_TAG_CHANNEL_OFF_CHAIN_UPDATE_CREATE_CONTRACT = 573 -OBJECT_TAG_CHANNEL_OFF_CHAIN_UPDATE_CALL_CONTRACT = 574 -OBJECT_TAG_CHANNEL = 58 -OBJECT_TAG_CHANNEL_SNAPSHOT_TRANSACTION = 59 -OBJECT_TAG_POI = 60 -OBJECT_TAG_MICRO_BODY = 101 -OBJECT_TAG_LIGHT_MICRO_BLOCK = 102 +from aeternity import identifiers as idf class TxSigner: @@ -71,10 +15,10 @@ def __init__(self, account, network_id): def encode_signed_transaction(self, transaction, signature): """prepare a signed transaction message""" - tag = bytes([OBJECT_TAG_SIGNED_TRANSACTION]) - vsn = bytes([VSN]) - encoded_signed_tx = encode_rlp("tx", [tag, vsn, [signature], transaction]) - encoded_signature = encode("sg", signature) + tag = bytes([idf.OBJECT_TAG_SIGNED_TRANSACTION]) + vsn = bytes([idf.VSN]) + encoded_signed_tx = encode_rlp(idf.TRANSACTION, [tag, vsn, [signature], transaction]) + encoded_signature = encode(idf.SIGNATURE, signature) return encoded_signed_tx, encoded_signature def sign_encode_transaction(self, tx): @@ -83,7 +27,7 @@ def sign_encode_transaction(self, tx): :return: encoded_signed_tx, encoded_signature, tx_hash """ # decode the transaction if not in native mode - transaction = decode(tx.tx) if hasattr(tx, "tx") else decode(tx) + transaction = decode(tx.tx) if hasattr(tx, idf.TRANSACTION) else decode(tx) # sign the transaction signature = self.account.sign(_binary(self.network_id) + transaction) # encode the transaction @@ -115,7 +59,7 @@ def compute_tx_hash(signed_tx: str) -> str: :param signed_tx: an encoded signed transaction """ signed = decode(signed_tx) - return hash_encode("th", signed) + return hash_encode(idf.TRANSACTION_HASH, signed) def tx_spend(self, account_id, recipient_id, amount, payload, fee, ttl, nonce)-> str: """ @@ -131,17 +75,17 @@ def tx_spend(self, account_id, recipient_id, amount, payload, fee, ttl, nonce)-> # compute the absolute ttl and the nonce if self.native_transactions: tx = [ - _int(OBJECT_TAG_SPEND_TRANSACTION), - _int(VSN), - _id(ID_TAG_ACCOUNT, account_id), - _id(ID_TAG_ACCOUNT, recipient_id), + _int(idf.OBJECT_TAG_SPEND_TRANSACTION), + _int(idf.VSN), + _id(idf.ID_TAG_ACCOUNT, account_id), + _id(idf.ID_TAG_ACCOUNT, recipient_id), _int(amount), _int(fee), _int(ttl), _int(nonce), _binary(payload) ] - tx = encode_rlp("tx", tx) + tx = encode_rlp(idf.TRANSACTION, tx) return tx # use internal endpoints transaction @@ -169,15 +113,15 @@ def tx_name_preclaim(self, account_id, commitment_id, fee, ttl, nonce)-> str: """ if self.native_transactions: tx = [ - _int(OBJECT_TAG_NAME_SERVICE_PRECLAIM_TRANSACTION), - _int(VSN), - _id(ID_TAG_ACCOUNT, account_id), + _int(idf.OBJECT_TAG_NAME_SERVICE_PRECLAIM_TRANSACTION), + _int(idf.VSN), + _id(idf.ID_TAG_ACCOUNT, account_id), _int(nonce), - _id(ID_TAG_COMMITMENT, commitment_id), + _id(idf.ID_TAG_COMMITMENT, commitment_id), _int(fee), _int(ttl) ] - return encode_rlp("tx", tx) + return encode_rlp(idf.TRANSACTION, tx) # use internal endpoints transaction body = dict( commitment_id=commitment_id, @@ -200,16 +144,16 @@ def tx_name_claim(self, account_id, name, name_salt, fee, ttl, nonce)-> str: """ if self.native_transactions: tx = [ - _int(OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION), - _int(VSN), - _id(ID_TAG_ACCOUNT, account_id), + _int(idf.OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION), + _int(idf.VSN), + _id(idf.ID_TAG_ACCOUNT, account_id), _int(nonce), decode(name), _binary(name_salt), _int(fee), _int(ttl) ] - tx = encode_rlp("tx", tx) + tx = encode_rlp(idf.TRANSACTION, tx) # use internal endpoints transaction body = dict( account_id=account_id, @@ -237,26 +181,26 @@ def tx_name_update(self, account_id, name_id, pointers, name_ttl, client_ttl, fe # TODO: verify supported keys for name updates def pointer_tag(pointer): return { - "account_pubkey": ID_TAG_ACCOUNT, - "oracle_pubkey": ID_TAG_ORACLE, - "contract_pubkey": ID_TAG_CONTRACT, - "channel_pubkey": ID_TAG_CHANNEL + "account_pubkey": idf.ID_TAG_ACCOUNT, + "oracle_pubkey": idf.ID_TAG_ORACLE, + "contract_pubkey": idf.ID_TAG_CONTRACT, + "channel_pubkey": idf.ID_TAG_CHANNEL }.get(pointer.get("key")) - ptrs = [[_binary(p.get("key")), _id(pointer_tag(p), p.get("id"))] for p in pointers] + ptrs = [[_binary(p.get("key")), _id(idf.pointer_tag(p), p.get("id"))] for p in pointers] # build tx tx = [ - _int(OBJECT_TAG_NAME_SERVICE_UPDATE_TRANSACTION), - _int(VSN), - _id(ID_TAG_ACCOUNT, account_id), + _int(idf.OBJECT_TAG_NAME_SERVICE_UPDATE_TRANSACTION), + _int(idf.VSN), + _id(idf.ID_TAG_ACCOUNT, account_id), _int(nonce), - _id(ID_TAG_NAME, name_id), + _id(idf.ID_TAG_NAME, name_id), _int(name_ttl), ptrs, _int(client_ttl), _int(fee), _int(ttl) ] - return encode_rlp("tx", tx) + return encode_rlp(idf.TRANSACTION, tx) # use internal endpoints transaction body = dict( account_id=account_id, @@ -282,16 +226,16 @@ def tx_name_transfer(self, account_id, name_id, recipient_id, fee, ttl, nonce)-> """ if self.native_transactions: tx = [ - _int(OBJECT_TAG_NAME_SERVICE_TRANSFER_TRANSACTION), - _int(VSN), - _id(ID_TAG_ACCOUNT, account_id), + _int(idf.OBJECT_TAG_NAME_SERVICE_TRANSFER_TRANSACTION), + _int(idf.VSN), + _id(idf.ID_TAG_ACCOUNT, account_id), _int(nonce), - _id(ID_TAG_NAME, name_id), - _id(ID_TAG_ACCOUNT, recipient_id), + _id(idf.ID_TAG_NAME, name_id), + _id(idf.ID_TAG_ACCOUNT, recipient_id), _int(fee), _int(ttl), ] - return encode_rlp("tx", tx) + return encode_rlp(idf.TRANSACTION, tx) # use internal endpoints transaction body = dict( account_id=account_id, @@ -315,15 +259,15 @@ def tx_name_revoke(self, account_id, name_id, fee, ttl, nonce)-> str: if self.native_transactions: tx = [ - _int(OBJECT_TAG_NAME_SERVICE_REVOKE_TRANSACTION), - _int(VSN), - _id(ID_TAG_ACCOUNT, account_id), + _int(idf.OBJECT_TAG_NAME_SERVICE_REVOKE_TRANSACTION), + _int(idf.VSN), + _id(idf.ID_TAG_ACCOUNT, account_id), _int(nonce), - _id(ID_TAG_NAME, name_id), + _id(idf.ID_TAG_NAME, name_id), _int(fee), _int(ttl), ] - return encode_rlp("tx", tx) + return encode_rlp(idf.TRANSACTION, tx) # use internal endpoints transaction body = dict( account_id=account_id, @@ -354,7 +298,7 @@ def tx_contract_create(self, account_id, code, call_data, amount, deposit, gas, if self.native_transactions: tx = [ - _id(account_id), + _id(idf.account_id), _int(nonce), _binary(code), _int(vm_version), @@ -366,7 +310,7 @@ def tx_contract_create(self, account_id, code, call_data, amount, deposit, gas, _int(gas_price), _binary(call_data), ] - return encode_rlp("tx", tx), contract_id(account_id, nonce) + return encode_rlp(idf.TRANSACTION, tx), contract_id(idf.account_id, nonce) # use internal endpoints transaction body = dict( owner_id=account_id, @@ -402,9 +346,9 @@ def tx_contract_call(self, account_id, contract_id, call_data, function, arg, am """ if self.native_transactions: tx = [ - _id(account_id), + _id(idf.account_id), _int(nonce), - _id(contract_id), + _id(idf.contract_id), _int(vm_version), _int(fee), _int(ttl), @@ -413,7 +357,7 @@ def tx_contract_call(self, account_id, contract_id, call_data, function, arg, am _int(gas_price), _binary(call_data), ] - return encode_rlp("tx", tx) + return encode_rlp(idf.TRANSACTION, tx) # use internal endpoints transaction body = dict( call_data=call_data, @@ -441,9 +385,9 @@ def tx_oracle_register(self, account_id, if self.native_transactions: tx = [ - _int(OBJECT_TAG_ORACLE_REGISTER_TRANSACTION), - _int(VSN), - _id(ID_TAG_ACCOUNT, account_id), + _int(idf.OBJECT_TAG_ORACLE_REGISTER_TRANSACTION), + _int(idf.VSN), + _id(idf.ID_TAG_ACCOUNT, account_id), _int(nonce), _binary(query_format), _binary(response_format), @@ -454,7 +398,7 @@ def tx_oracle_register(self, account_id, _int(ttl), _int(vm_version), ] - return encode_rlp("tx", tx) + return encode_rlp(idf.TRANSACTION, tx) # use internal endpoints transaction body = dict( account_id=account_id, @@ -482,11 +426,11 @@ def tx_oracle_query(self, oracle_id, sender_id, query, if self.native_transactions: tx = [ - _int(OBJECT_TAG_ORACLE_QUERY_TRANSACTION), - _int(VSN), - _id(ID_TAG_ACCOUNT, sender_id), + _int(idf.OBJECT_TAG_ORACLE_QUERY_TRANSACTION), + _int(idf.VSN), + _id(idf.ID_TAG_ACCOUNT, sender_id), _int(nonce), - _id(ID_TAG_ORACLE, oracle_id), + _id(idf.ID_TAG_ORACLE, oracle_id), _binary(query), _int(query_fee), _int(0 if query_ttl_type == ORACLE_DEFAULT_TTL_TYPE_DELTA else 1), @@ -496,7 +440,7 @@ def tx_oracle_query(self, oracle_id, sender_id, query, _int(fee), _int(ttl), ] - return encode_rlp("tx", tx) + return encode_rlp(idf.TRANSACTION, tx) # use internal endpoints transaction body = dict( sender_id=sender_id, @@ -527,9 +471,9 @@ def tx_oracle_respond(self, oracle_id, query_id, response, if self.native_transactions: tx = [ - _int(OBJECT_TAG_ORACLE_RESPONSE_TRANSACTION), - _int(VSN), - _id(ID_TAG_ORACLE, oracle_id), + _int(idf.OBJECT_TAG_ORACLE_RESPONSE_TRANSACTION), + _int(idf.VSN), + _id(idf.ID_TAG_ORACLE, oracle_id), _int(nonce), _binary(query_id), _binary(response), @@ -538,7 +482,7 @@ def tx_oracle_respond(self, oracle_id, query_id, response, _int(fee), _int(ttl), ] - return encode_rlp("tx", tx) + return encode_rlp(idf.TRANSACTION, tx) # use internal endpoints transaction body = dict( response_ttl=dict( @@ -564,16 +508,16 @@ def tx_oracle_extend(self, oracle_id, if self.native_transactions: tx = [ - _int(OBJECT_TAG_ORACLE_EXTEND_TRANSACTION), - _int(VSN), - _id(ID_TAG_ORACLE, oracle_id), + _int(idf.OBJECT_TAG_ORACLE_EXTEND_TRANSACTION), + _int(idf.VSN), + _id(idf.ID_TAG_ORACLE, oracle_id), _int(nonce), _int(0 if ttl_type == ORACLE_DEFAULT_TTL_TYPE_DELTA else 1), _int(ttl_value), _int(fee), _int(ttl), ] - return encode_rlp("tx", tx) + return encode_rlp(idf.TRANSACTION, tx) # use internal endpoints transaction body = dict( oracle_id=oracle_id, diff --git a/requirements.txt b/requirements.txt index 065a1952..70247b07 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,4 @@ requests==2.20.0 websocket_client==0.48.0 validators==0.12.1 semver==2.8.1 +namedtupled==0.3.3 diff --git a/setup.py b/setup.py index d62ceeb8..e879961c 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ def get_version(): author='Andrea Giacobino', author_email='aepp-dev@aeternity.com', url='https://github.com/aeternity/aepp-sdk-python', - python_requires='>=3.5', + python_requires='>=3.6', license='ISC', scripts=['aecli'], packages=find_packages(), @@ -49,7 +49,8 @@ def get_version(): 'requests == 2.20.0', 'websocket_client == 0.48.0', 'validators == 0.12.1', - 'semver==2.8.1' + 'semver==2.8.1', + 'namedtupled==0.3.3' ], classifiers=[ 'Programming Language :: Python', diff --git a/tests/test_aens.py b/tests/test_aens.py index dc90b103..198353e8 100644 --- a/tests/test_aens.py +++ b/tests/test_aens.py @@ -74,8 +74,8 @@ def test_name_update(): print("claimed name", name) print("pointers", name.pointers) assert len(name.pointers) > 0, 'Pointers should not be empty' - assert name.pointers[0]['id'] == ACCOUNT.get_address() - assert name.pointers[0]['key'] == "account_pubkey" + assert name.pointers[0].id == ACCOUNT.get_address() + assert name.pointers[0].key == "account_pubkey" # TODO: enable the test check for pointers @@ -85,8 +85,8 @@ def test_name_transfer_ownership(): name.full_claim_blocking(ACCOUNT) assert name.status == AEName.Status.CLAIMED name.update_status() - assert name.pointers[0]['id'] == ACCOUNT.get_address() - assert name.pointers[0]['key'] == "account_pubkey" + assert name.pointers[0].id == ACCOUNT.get_address() + assert name.pointers[0].key == "account_pubkey" # now transfer the name to the other account name.transfer_ownership(ACCOUNT, ACCOUNT_1.get_address()) @@ -96,8 +96,8 @@ def test_name_transfer_ownership(): name.update(ACCOUNT_1, ACCOUNT_1.get_address()) name.update_status() assert len(name.pointers) > 0, 'Pointers should not be empty' - assert name.pointers[0]['id'] == ACCOUNT_1.get_address() - assert name.pointers[0]['key'] == "account_pubkey" + assert name.pointers[0].id == ACCOUNT_1.get_address() + assert name.pointers[0].key == "account_pubkey" # def test_transfer_failure_wrong_pubkey(): diff --git a/tests/test_cli.py b/tests/test_cli.py index 6ccedc12..d2aa31f9 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -162,8 +162,9 @@ def test_cli_phases_spend(account_path): # generate a new address recipient_id = Account.generate().get_address() # step one, generate transaction - j = call_aecli('tx', 'spend', ACCOUNT.get_address(), recipient_id, '100') - j.get("Sender account") + nonce = NODE_CLI.get_account_by_pubkey(pubkey=ACCOUNT.get_address()).nonce + 1 + j = call_aecli('tx', 'spend', ACCOUNT.get_address(), recipient_id, '100', '--nonce', f'{nonce}') + # assert ACCOUNT.get_address == j.get("Sender account") assert recipient_id == j.get("Recipient account") # step 2, sign the transaction tx_unsigned = j.get("Encoded")