diff --git a/.env b/.env index 30fab3e8..c7097a81 100644 --- a/.env +++ b/.env @@ -1,2 +1,14 @@ # this is used by docker-compose.yml to for the node image tag -TAG=v1.4.0 +TAG=v2.0.0 + + +Coordinate the developmnet of the sdks (features, release schedule, development policies) +Coordinate with the core team to keep all the aepps working on the 3 environments +Maintain the aepps so they keep working +Maintain a 2 way communication between core team and aepps team +Operate 3 environments (mainnet, testnet, unstable) that are used by the aepps and the public +Operate all the aepps-dev in the 3 environments + other stuff (like hackmd) +Design and mediate the feature/user experience for developer tools with the teams +Keep track of the security issues and + + diff --git a/Jenkinsfile b/Jenkinsfile index ecbf10c2..9f1ea5fe 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -12,7 +12,6 @@ pipeline { environment { DOCKER_COMPOSE = "docker-compose -p ${env.BUILD_TAG} -H 127.0.0.1:2376" - SCANNER_HOME = tool 'default-sonarqube-scanner' } stages { @@ -21,12 +20,9 @@ pipeline { withCredentials([usernamePassword(credentialsId: 'genesis-wallet', usernameVariable: 'WALLET_PUB', passwordVariable: 'WALLET_PRIV')]) { + sh "${env.DOCKER_COMPOSE} pull node" sh "${env.DOCKER_COMPOSE} run sdk flake8" sh "${env.DOCKER_COMPOSE} run sdk make test" - // run sonar? - // withSonarQubeEnv('default-sonarqube-server') { - // sh "${env.SCANNER_HOME}/bin/sonar-scanner -X" - // } } } } diff --git a/aeternity/__init__.py b/aeternity/__init__.py index b4a92a8d..d2bb24e7 100644 --- a/aeternity/__init__.py +++ b/aeternity/__init__.py @@ -1,6 +1,6 @@ -__version__ = '1.1.2' +__version__ = '2.0.0a1' __compatibility__ = dict( - from_version=">=1.0.0", - to_version="<2.0.0" + from_version=">=1.4.0", + to_version="<3.0.0" ) diff --git a/aeternity/__main__.py b/aeternity/__main__.py index f42d977c..f8951946 100644 --- a/aeternity/__main__.py +++ b/aeternity/__main__.py @@ -846,7 +846,7 @@ def chain_play(height, limit, force, wait, json_): limit -= 1 if limit <= 0: break - g = cli.get_generation_by_hash(hash=g.key_block.get("prev_key_hash")) + g = cli.get_generation_by_hash(hash=g.key_block.prev_key_hash) except Exception as e: print(e) g = None diff --git a/aeternity/config.py b/aeternity/config.py index a79f7143..8766a4cb 100644 --- a/aeternity/config.py +++ b/aeternity/config.py @@ -1,12 +1,8 @@ import requests -import sys import semver from collections import MutableSequence from . import __compatibility__ -# vm version specification -# https://github.com/aeternity/protocol/blob/master/contracts/contract_vms.md#virtual-machines-on-the-%C3%A6ternity-blockchain -AEVM_NO_VM = 0 # fee calculation GAS_PER_BYTE = 20 BASE_GAS = 15000 @@ -17,13 +13,13 @@ NAME_CLIENT_TTL = 60000 DEFAULT_NAME_TTL = 500 # default relative ttl in number of blocks for executing transaction on the chain -MAX_TX_TTL = sys.maxsize +MAX_TX_TTL = 256 DEFAULT_TX_TTL = 0 # default fee for posting transaction -DEFAULT_FEE = 20000 +DEFAULT_FEE = 0 # contracts -CONTRACT_DEFAULT_GAS = 170000 -CONTRACT_DEFAULT_GAS_PRICE = 1 +CONTRACT_DEFAULT_GAS = 100000 +CONTRACT_DEFAULT_GAS_PRICE = 1000000000 CONTRACT_DEFAULT_DEPOSIT = 0 CONTRACT_DEFAULT_VM_VERSION = 1 CONTRACT_DEFAULT_AMOUNT = 0 @@ -35,8 +31,6 @@ ORACLE_DEFAULT_TTL_VALUE = 500 ORACLE_DEFAULT_QUERY_TTL_VALUE = 10 ORACLE_DEFAULT_RESPONSE_TTL_VALUE = 10 -ORACLE_DEFAULT_VM_VERSION = AEVM_NO_VM - # network id DEFAULT_NETWORK_ID = "ae_mainnet" diff --git a/aeternity/contract.py b/aeternity/contract.py index 38b29a0a..cbe9ac36 100644 --- a/aeternity/contract.py +++ b/aeternity/contract.py @@ -1,6 +1,7 @@ -from aeternity.openapi import OpenAPIClientException +from aeternity.openapi import OpenAPIClientException, UnsupportedEpochVersion from aeternity import utils, config -from aeternity.identifiers import CONTRACT_ID +from aeternity.identifiers import CONTRACT_ID, CONTRACT_ROMA_VM, CONTRACT_ROMA_ABI, CONTRACT_MINERVA_VM, CONTRACT_MINERVA_ABI +import semver class ContractError(Exception): @@ -42,7 +43,8 @@ def tx_call(self, account, function, arg, gas=config.CONTRACT_DEFAULT_GAS, gas_price=config.CONTRACT_DEFAULT_GAS_PRICE, fee=config.DEFAULT_FEE, - vm_version=config.CONTRACT_DEFAULT_VM_VERSION, + vm_version=None, + abi_version=None, tx_ttl=config.DEFAULT_TX_TTL): """Call a sophia contract""" @@ -50,6 +52,10 @@ def tx_call(self, account, function, arg, raise ValueError("Missing contract id") try: + # retrieve the correct vm/abi version + vm, abi = self._get_vm_abi_versions() + vm_version = vm if vm_version is None else vm_version + abi_version = abi if abi_version is None else abi_version # prepare the call data call_data = self.encode_calldata(function, arg) # get the transaction builder @@ -57,7 +63,9 @@ def tx_call(self, account, function, arg, # get the account nonce and ttl nonce, ttl = self.client._get_nonce_ttl(account.get_address(), tx_ttl) # build the transaction - tx = txb.tx_contract_call(account.get_address(), self.address, call_data, function, arg, amount, gas, gas_price, vm_version, fee, ttl, nonce) + tx = txb.tx_contract_call(account.get_address(), self.address, call_data, function, arg, + amount, gas, gas_price, abi_version, + fee, ttl, nonce) # sign the transaction tx_signed, sg, tx_hash = self.client.sign_transaction(account, tx) # post the transaction to the chain @@ -76,21 +84,27 @@ def tx_create(self, gas=config.CONTRACT_DEFAULT_GAS, gas_price=config.CONTRACT_DEFAULT_GAS_PRICE, fee=config.DEFAULT_FEE, - vm_version=config.CONTRACT_DEFAULT_VM_VERSION, + vm_version=None, + abi_version=None, tx_ttl=config.DEFAULT_TX_TTL): """ Create a contract and deploy it to the chain :return: address """ try: + # retrieve the correct vm/abi version + vm, abi = self._get_vm_abi_versions() + vm_version = vm if vm_version is None else vm_version + abi_version = abi if abi_version is None else abi_version + # encode the call data call_data = self.encode_calldata("init", init_state) - # get the transaction builder txb = self.client.tx_builder # get the account nonce and ttl nonce, ttl = self.client._get_nonce_ttl(account.get_address(), tx_ttl) # build the transaction - tx, contract_id = txb.tx_contract_create(account.get_address(), self.bytecode, call_data, amount, deposit, gas, gas_price, vm_version, + tx, contract_id = txb.tx_contract_create(account.get_address(), self.bytecode, call_data, + amount, deposit, gas, gas_price, vm_version, abi_version, fee, ttl, nonce) # sign the transaction tx_signed, sg, tx_hash = self.client.sign_transaction(account, tx) @@ -167,3 +181,13 @@ def decode_data(self, data, sophia_type): return reply.data.value, reply.data.type except OpenAPIClientException as e: raise ContractError(e) + + def _get_vm_abi_versions(self): + """ + Check the version of the node and retrieve the correct values for abi and vm version + """ + if semver.match(self.client.api_version, "<=1.4.0"): + return CONTRACT_ROMA_VM, CONTRACT_ROMA_ABI + if semver.match(self.client.api_version, "<3.0.0"): + return CONTRACT_MINERVA_VM, CONTRACT_MINERVA_ABI + raise UnsupportedEpochVersion(f"Version {self.client.api_version} is not supported") diff --git a/aeternity/hashing.py b/aeternity/hashing.py index aa14dec1..773f4038 100644 --- a/aeternity/hashing.py +++ b/aeternity/hashing.py @@ -89,7 +89,7 @@ def encode_rlp(prefix, data): :param data: the array that has to be encoded in rlp """ if not isinstance(data, list): - raise ValueError("data to be encoded to rlp must be an array") + raise ValueError("data to be encoded to rlp must be a list") payload = rlp.encode(data) return encode(prefix, payload) @@ -132,13 +132,17 @@ def namehash_encode(prefix, name): return encode(prefix, namehash(name)) -def _int(val: int) -> bytes: +def _int(val: int, byte_length: int = None) -> bytes: """ Encode and int to a big endian byte array + :param val: the value to encode + :param byte_length: number of bytes that should be used to encoded the number, by default is the minimum """ if val == 0: - return val.to_bytes(1, byteorder='big') - return val.to_bytes((val.bit_length() + 7) // 8, byteorder='big') + size = 1 if byte_length is None else byte_length + return val.to_bytes(size, byteorder='big') + size = (val.bit_length() + 7) // 8 if byte_length is None else byte_length + return val.to_bytes(size, byteorder='big') def _binary(val): @@ -168,7 +172,7 @@ def contract_id(owner_id, nonce): :param owner_id: the account creating the conctract :param nonce: the nonce of the contract creation transaction """ - return hash_encode("ct", decode(owner_id) + _int(nonce)) + return hash_encode(identifiers.CONTRACT_ID, decode(owner_id) + _int(nonce)) def oracle_id(account_id): @@ -176,7 +180,7 @@ def oracle_id(account_id): Compute the oracle id of a oracle registration :parm account_id: the account registering the oracle """ - return f"ok_{account_id[3:]}" + return f"{identifiers.ORACLE_ID}_{account_id[3:]}" def oracle_query_id(sender_id, nonce, oracle_id): @@ -186,9 +190,7 @@ def oracle_query_id(sender_id, nonce, oracle_id): :param nonce: the nonce of the query transaction :param oracle_id: the oracle id """ - def _int32(val): - return val.to_bytes(32, byteorder='big') - return hash_encode("oq", decode(sender_id) + _int32(nonce) + decode(oracle_id)) + return hash_encode(identifiers.ORACLE_QUERY_ID, decode(sender_id) + _int(nonce, byte_length=32) + decode(oracle_id)) def randint(upper_bound=2**64): diff --git a/aeternity/identifiers.py b/aeternity/identifiers.py index 4df01520..64ad738f 100644 --- a/aeternity/identifiers.py +++ b/aeternity/identifiers.py @@ -115,3 +115,26 @@ OBJECT_TAG_POI = 60 OBJECT_TAG_MICRO_BODY = 101 OBJECT_TAG_LIGHT_MICRO_BLOCK = 102 + + +# VM Identifiers +# vm version specification +# https://github.com/aeternity/protocol/blob/master/contracts/contract_vms.md#virtual-machines-on-the-%C3%A6ternity-blockchain +NO_VM = 0 +VM_SOPHIA = 1 +VM_SOLIDITY = 2 +VM_SOPHIA_IMPROVEMENTS = 3 +# abi +NO_ABI = 0 +ABI_SOPHIA = 1 +ABI_SOLIDITY = 2 +# Contracts identifiers +# For Roma +CONTRACT_ROMA_VM = 0 # this is to maintain retrocompatibility +CONTRACT_ROMA_ABI = 1 +# For Minerva: 196609 # that is int.from_bytes(int(3).to_bytes(2, "big") + int(1).to_bytes(2, "big"), "big") +CONTRACT_MINERVA_VM = 3 +CONTRACT_MINERVA_ABI = 1 + +# Oracles +ORACLE_DEFAULT_VM_VERSION = NO_VM diff --git a/aeternity/openapi.py b/aeternity/openapi.py index 4bca1e23..446fef66 100644 --- a/aeternity/openapi.py +++ b/aeternity/openapi.py @@ -52,16 +52,16 @@ def __init__(self, url, url_internal=None, debug=False, force_compatibility=Fals try: # load the openapi json file from the node self.api_def = requests.get(f"{url}/api").json() - api_version = self.api_def.get("info", {}).get("version", "unknown") + self.api_version = self.api_def.get("info", {}).get("version", "unknown") # retrieve the version of the node we are connecting to - match_min = semver.match(api_version, __compatibility__.get("from_version")) - match_max = semver.match(api_version, __compatibility__.get("to_version")) + match_min = semver.match(self.api_version, __compatibility__.get("from_version")) + match_max = semver.match(self.api_version, __compatibility__.get("to_version")) if (not match_min or not match_max) and not force_compatibility: f, t = __compatibility__.get('from_version'), __compatibility__.get('to_version') raise UnsupportedEpochVersion( - f"unsupported epoch version {api_version}, supported version are {f} and {t}") + f"unsupported node version {self.api_version}, supported version are {f} and {t}") except requests.exceptions.ConnectionError as e: - raise ConfigException(f"Error connecting to the epoch node at {self.api_url}, connection unavailable") + raise ConfigException(f"Error connecting to the node at {self.api_url}, connection unavailable") except Exception as e: raise UnsupportedEpochVersion(f"Unable to connect to the node: {e}") diff --git a/aeternity/oracles.py b/aeternity/oracles.py index 990f9e90..b044a014 100644 --- a/aeternity/oracles.py +++ b/aeternity/oracles.py @@ -1,6 +1,6 @@ import logging from aeternity import config, hashing -from aeternity.identifiers import ORACLE_ID +from aeternity.identifiers import ORACLE_ID, ORACLE_DEFAULT_VM_VERSION logger = logging.getLogger(__name__) @@ -66,7 +66,7 @@ def register(self, account, query_format, response_format, query_fee=config.ORACLE_DEFAULT_QUERY_FEE, ttl_type=config.ORACLE_DEFAULT_TTL_TYPE_DELTA, ttl_value=config.ORACLE_DEFAULT_TTL_VALUE, - vm_version=config.ORACLE_DEFAULT_VM_VERSION, + vm_version=ORACLE_DEFAULT_VM_VERSION, fee=config.DEFAULT_FEE, tx_ttl=config.DEFAULT_TX_TTL): """ diff --git a/aeternity/transactions.py b/aeternity/transactions.py index 1ff51414..d6745e47 100644 --- a/aeternity/transactions.py +++ b/aeternity/transactions.py @@ -1,7 +1,14 @@ -from aeternity.hashing import _int, _binary, _id, encode, decode, encode_rlp, hash_encode, contract_id +from aeternity.hashing import _int, _binary, _id, encode, decode, encode_rlp, decode_rlp, hash_encode, contract_id from aeternity.openapi import OpenAPICli from aeternity.config import ORACLE_DEFAULT_TTL_TYPE_DELTA from aeternity import identifiers as idf +import rlp +import math + +BASE_GAS = 15000 +GAS_PER_BYTE = 20 +GAS_PRICE = 1000000000 +KEY_BLOCK_INTERVAL = 3 class TxSigner: @@ -38,28 +45,392 @@ def sign_encode_transaction(self, tx): return encoded_signed_tx, encoded_signature, tx_hash +def _tx_native(tag: int, vsn: int, op: int=1, **kwargs): + + def std_fee(tx_raw, fee_idx, base_gas_multiplier=1): + # calculates the standard minimum transaction fee + tx_copy = tx_raw # create a copy of the input + tx_copy[fee_idx] = _int(0, 8) + return (BASE_GAS * base_gas_multiplier + len(rlp.encode(tx_copy)) * GAS_PER_BYTE) * GAS_PRICE + + def contract_fee(tx_raw, fee_idx, gas, base_gas_multiplier=1): + # estimate the contract creation fee + tx_copy = tx_raw # create a copy of the input + tx_copy[fee_idx] = _int(0, 8) + return (BASE_GAS * base_gas_multiplier + gas + len(rlp.encode(tx_copy)) * GAS_PER_BYTE) * GAS_PRICE + + def oracle_fee(tx_raw, fee_idx, relative_ttl): + tx_copy = tx_raw # create a copy of the input + tx_copy[fee_idx] = _int(0, 8) + fee = (BASE_GAS + len(rlp.encode(tx_copy)) * GAS_PER_BYTE) + fee += math.ceil(32000 * relative_ttl / math.floor(60 * 24 * 365 / KEY_BLOCK_INTERVAL)) + return fee * GAS_PRICE + + if tag == idf.OBJECT_TAG_SPEND_TRANSACTION: + if op == 1: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("sender_id")), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("recipient_id")), + _int(kwargs.get("amount")), + _int(kwargs.get("fee")), # index 5 + _int(kwargs.get("ttl")), + _int(kwargs.get("nonce")), + _binary(kwargs.get("payload")) + ] + tx_field_fee_index = 5 + min_fee = std_fee(tx_native, tx_field_fee_index) + else: + tx_native = decode_rlp(kwargs.get("tx")) + body = dict( + type="SpendTx", + vsn=int.from_bytes(tx_native[1], "big"), + sender_id=encode(idf.ACCOUNT_ID, tx_native[2]), + recipient_id=encode(idf.ACCOUNT_ID, tx_native[3]), + amount=int.from_bytes(tx_native[4], "big"), + fee=int.from_bytes(tx_native[5], "big"), + ttl=int.from_bytes(tx_native[6], "big"), + nonce=int.from_bytes(tx_native[7], "big"), + payload=tx_native[8].hex(), + ) + return body + + elif tag == idf.OBJECT_TAG_NAME_SERVICE_PRECLAIM_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("account_id")), + _int(kwargs.get("nonce")), + _id(idf.ID_TAG_COMMITMENT, kwargs.get("commitment_id")), + _int(kwargs.get("fee")), + _int(kwargs.get("ttl")) + ] + tx_field_fee_index = 5 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("account_id")), + _int(kwargs.get("nonce")), + decode(kwargs.get("name")), + _binary(kwargs.get("name_salt")), + _int(kwargs.get("fee")), + _int(kwargs.get("ttl")) + ] + tx_field_fee_index = 6 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_NAME_SERVICE_UPDATE_TRANSACTION: + # first asseble the pointers + def pointer_tag(pointer): + return { + "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 kwargs.get("pointers", [])] + # then build the transaction + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("account_id")), + _int(kwargs.get("nonce")), + _id(idf.ID_TAG_NAME, kwargs.get("name_id")), + _int(kwargs.get("name_ttl")), + ptrs, + _int(kwargs.get("client_ttl")), + _int(kwargs.get("fee")), + _int(kwargs.get("ttl")) + ] + tx_field_fee_index = 8 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_NAME_SERVICE_TRANSFER_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("account_id")), + _int(kwargs.get("nonce")), + _id(idf.ID_TAG_NAME, kwargs.get("name_id")), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("recipient_id")), + _int(kwargs.get("fee")), + _int(kwargs.get("ttl")), + ] + tx_field_fee_index = 6 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_NAME_SERVICE_REVOKE_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("account_id")), + _int(kwargs.get("nonce")), + _id(idf.ID_TAG_NAME, kwargs.get("name_id")), + _int(kwargs.get("fee")), + _int(kwargs.get("ttl")), + ] + tx_field_fee_index = 5 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_CONTRACT_CREATE_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("owner_id")), + _int(kwargs.get("nonce")), + _binary(decode(kwargs.get("code"))), + _int(kwargs.get("vm_version")) + _int(kwargs.get("abi_version"), 2), + _int(kwargs.get("fee")), + _int(kwargs.get("ttl")), + _int(kwargs.get("deposit")), + _int(kwargs.get("amount")), + _int(kwargs.get("gas")), + _int(kwargs.get("gas_price")), + _binary(decode(kwargs.get("call_data"))), + ] + tx_field_fee_index = 6 + # TODO: verify the fee caluclation for the contract + min_fee = contract_fee(tx_native, tx_field_fee_index, kwargs.get("gas"), base_gas_multiplier=5) + elif tag == idf.OBJECT_TAG_CONTRACT_CALL_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("caller_id")), + _int(kwargs.get("nonce")), + _id(idf.ID_TAG_CONTRACT, kwargs.get("contract_id")), + _int(kwargs.get("abi_version")), + _int(kwargs.get("fee")), + _int(kwargs.get("ttl")), + _int(kwargs.get("amount")), + _int(kwargs.get("gas")), + _int(kwargs.get("gas_price")), + _binary(decode(kwargs.get("call_data"))), + ] + tx_field_fee_index = 6 + min_fee = contract_fee(tx_native, tx_field_fee_index, kwargs.get("gas"), base_gas_multiplier=30) + elif tag == idf.OBJECT_TAG_CHANNEL_CREATE_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("initiator")), + _int(kwargs.get("initiator_amount")), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("responder")), + _int(kwargs.get("responder_amount")), + _int(kwargs.get("channel_reserve")), + _int(kwargs.get("lock_period")), + _int(kwargs.get("ttl")), + _int(kwargs.get("fee")), + # _[id(delegate_ids)], TODO: handle delegate ids + _binary(kwargs.get("state_hash")), + _int(kwargs.get("nonce")), + ] + tx_field_fee_index = 9 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_CHANNEL_DEPOSIT_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), + _int(kwargs.get("amount")), + _int(kwargs.get("ttl")), + _int(kwargs.get("fee")), + _binary(kwargs.get("state_hash")), + _int(kwargs.get("round")), + _int(kwargs.get("nonce")), + ] + tx_field_fee_index = 6 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_CHANNEL_WITHDRAW_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("to_id")), + _int(kwargs.get("amount")), + _int(kwargs.get("ttl")), + _int(kwargs.get("fee")), + _binary(kwargs.get("state_hash")), + _int(kwargs.get("round")), + _int(kwargs.get("nonce")), + ] + tx_field_fee_index = 6 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_CHANNEL_CLOSE_MUTUAL_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), + _int(kwargs.get("initiator_amount_final")), + _int(kwargs.get("responder_amount_final")), + _int(kwargs.get("ttl")), + _int(kwargs.get("fee")), + _int(kwargs.get("nonce")), + ] + tx_field_fee_index = 7 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_CHANNEL_CLOSE_SOLO_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), + _binary(kwargs.get("payload")), + # _poi(kwargs.get("poi")), TODO: implement support for _poi + _int(kwargs.get("ttl")), + _int(kwargs.get("fee")), + _int(kwargs.get("nonce")), + ] + tx_field_fee_index = 7 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_CHANNEL_SLASH_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), + _binary(kwargs.get("payload")), + # _poi(kwargs.get("poi")), TODO: implement support for _poi + _int(kwargs.get("ttl")), + _int(kwargs.get("fee")), + _int(kwargs.get("nonce")), + ] + tx_field_fee_index = 7 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_CHANNEL_SETTLE_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), + _int(kwargs.get("initiator_amount_final")), + _int(kwargs.get("responder_amount_final")), + _int(kwargs.get("ttl")), + _int(kwargs.get("fee")), + _int(kwargs.get("nonce")), + ] + tx_field_fee_index = 7 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_CHANNEL_SNAPSHOT_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), + _binary(kwargs.get("payload")), + _int(kwargs.get("ttl")), + _int(kwargs.get("fee")), + _int(kwargs.get("nonce")), + ] + tx_field_fee_index = 6 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_CHANNEL_FORCE_PROGRESS_TRANSACTION: + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), + _binary(kwargs.get("payload")), + _int(kwargs.get("round")), + _binary(kwargs.get("update")), + _binary(kwargs.get("state_hash")), + # _trees(kwargs.get("offchain_trees")), TODO: implement support for _trees + _int(kwargs.get("ttl")), + _int(kwargs.get("fee")), + _int(kwargs.get("nonce")), + ] + tx_field_fee_index = 9 + min_fee = std_fee(tx_native, tx_field_fee_index) + elif tag == idf.OBJECT_TAG_ORACLE_REGISTER_TRANSACTION: + oracle_ttl = kwargs.get("oracle_ttl", {}) + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("account_id")), + _int(kwargs.get("nonce")), + _binary(kwargs.get("query_format")), + _binary(kwargs.get("response_format")), + _int(kwargs.get("query_fee")), + _int(0 if oracle_ttl.get("type") == ORACLE_DEFAULT_TTL_TYPE_DELTA else 1), + _int(oracle_ttl.get("value")), + _int(kwargs.get("fee")), + _int(kwargs.get("ttl")), + _int(kwargs.get("vm_version")), + ] + tx_field_fee_index = 9 + min_fee = oracle_fee(tx_native, tx_field_fee_index, oracle_ttl.get("value")) + elif tag == idf.OBJECT_TAG_ORACLE_QUERY_TRANSACTION: + query_ttl = kwargs.get("query_ttl", {}) + response_ttl = kwargs.get("response_ttl", {}) + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ACCOUNT, kwargs.get("sender_id")), + _int(kwargs.get("nonce")), + _id(idf.ID_TAG_ORACLE, kwargs.get("oracle_id")), + _binary(kwargs.get("query")), + _int(kwargs.get("query_fee")), + _int(0 if query_ttl.get("type") == ORACLE_DEFAULT_TTL_TYPE_DELTA else 1), + _int(query_ttl.get("value")), + _int(0 if response_ttl.get("type") == ORACLE_DEFAULT_TTL_TYPE_DELTA else 1), + _int(response_ttl.get("value")), + _int(kwargs.get("fee")), + _int(kwargs.get("ttl")), + ] + tx_field_fee_index = 11 + min_fee = oracle_fee(tx_native, tx_field_fee_index, query_ttl.get("value")) + elif tag == idf.OBJECT_TAG_ORACLE_RESPONSE_TRANSACTION: + response_ttl = kwargs.get("response_ttl", {}) + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ORACLE, kwargs.get("oracle_id")), + _int(kwargs.get("nonce")), + decode(kwargs.get("query_id")), + _binary(kwargs.get("response")), + _int(0 if response_ttl.get("type") == ORACLE_DEFAULT_TTL_TYPE_DELTA else 1), + _int(response_ttl.get("value")), + _int(kwargs.get("fee")), + _int(kwargs.get("ttl")), + ] + tx_field_fee_index = 8 + min_fee = oracle_fee(tx_native, tx_field_fee_index, response_ttl.get("value")) + elif tag == idf.OBJECT_TAG_ORACLE_EXTEND_TRANSACTION: + oracle_ttl = kwargs.get("oracle_ttl", {}) + tx_native = [ + _int(tag), + _int(vsn), + _id(idf.ID_TAG_ORACLE, kwargs.get("oracle_id")), + _int(kwargs.get("nonce")), + _int(0 if oracle_ttl.get("type", {}) == ORACLE_DEFAULT_TTL_TYPE_DELTA else 1), + _int(oracle_ttl.get("value")), + _int(kwargs.get("fee")), + _int(kwargs.get("ttl")), + ] + tx_field_fee_index = 6 + min_fee = oracle_fee(tx_native, tx_field_fee_index, oracle_ttl.get("value")) + # set the correct fee if fee is empty + if kwargs.get("fee") < min_fee: + tx_native[tx_field_fee_index] = min_fee + return encode_rlp(idf.TRANSACTION, tx_native), min_fee + + class TxBuilder: """ TxBuilder is used to build and post transactions to the chain. """ - def __init__(self, native=False, api: OpenAPICli =None): - """ - :param native: if the transactions should be built by the sdk (True) or requested to the debug api (False) - """ - if not native and api is None: - raise ValueError("A initialized api rest client has to be provided to build a transaction using the node internal API ") - self.api = api - self.native_transactions = native + def __init__(self, native=None, api=None): + pass @staticmethod - def compute_tx_hash(signed_tx: str) -> str: + def compute_tx_hash(encoded_tx: str) -> str: """ Generate the hash from a signed and encoded transaction - :param signed_tx: an encoded signed transaction + :param encoded_tx: an encoded signed transaction """ - signed = decode(signed_tx) - return hash_encode(idf.TRANSACTION_HASH, signed) + tx_raw = decode(encoded_tx) + return hash_encode(idf.TRANSACTION_HASH, tx_raw) def tx_spend(self, account_id, recipient_id, amount, payload, fee, ttl, nonce)-> str: """ @@ -72,22 +443,6 @@ def tx_spend(self, account_id, recipient_id, amount, payload, fee, ttl, nonce)-> :param ttl: the absolute ttl of the transaction :param nonce: the nonce of the transaction """ - # compute the absolute ttl and the nonce - if self.native_transactions: - tx = [ - _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(idf.TRANSACTION, tx) - return tx - # use internal endpoints transaction body = { "recipient_id": recipient_id, @@ -98,7 +453,10 @@ def tx_spend(self, account_id, recipient_id, amount, payload, fee, ttl, nonce)-> "ttl": ttl, "nonce": nonce, } - return self.api.post_spend(body=body).tx + tx_native, min_fee = _tx_native(idf.OBJECT_TAG_SPEND_TRANSACTION, idf.VSN, **body) + # compute the absolute ttl and the nonce + return tx_native + # return self.api.post_spend(body=body).tx # NAMING # @@ -111,18 +469,6 @@ def tx_name_preclaim(self, account_id, commitment_id, fee, ttl, nonce)-> str: :param ttl: the ttl for the transaction :param nonce: the nonce of the account for the transaction """ - if self.native_transactions: - tx = [ - _int(idf.OBJECT_TAG_NAME_SERVICE_PRECLAIM_TRANSACTION), - _int(idf.VSN), - _id(idf.ID_TAG_ACCOUNT, account_id), - _int(nonce), - _id(idf.ID_TAG_COMMITMENT, commitment_id), - _int(fee), - _int(ttl) - ] - return encode_rlp(idf.TRANSACTION, tx) - # use internal endpoints transaction body = dict( commitment_id=commitment_id, fee=fee, @@ -130,7 +476,10 @@ def tx_name_preclaim(self, account_id, commitment_id, fee, ttl, nonce)-> str: ttl=ttl, nonce=nonce ) - return self.api.post_name_preclaim(body=body).tx + tx_native, min_fee = _tx_native(idf.OBJECT_TAG_NAME_SERVICE_PRECLAIM_TRANSACTION, idf.VSN, **body) + # compute the absolute ttl and the nonce + return tx_native + # sreturn self.api.post_name_preclaim(body=body).tx def tx_name_claim(self, account_id, name, name_salt, fee, ttl, nonce)-> str: """ @@ -142,19 +491,6 @@ def tx_name_claim(self, account_id, name, name_salt, fee, ttl, nonce)-> str: :param ttl: the ttl for the transaction :param nonce: the nonce of the account for the transaction """ - if self.native_transactions: - tx = [ - _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(idf.TRANSACTION, tx) - # use internal endpoints transaction body = dict( account_id=account_id, name=name, @@ -163,7 +499,10 @@ def tx_name_claim(self, account_id, name, name_salt, fee, ttl, nonce)-> str: ttl=ttl, nonce=nonce ) - return self.api.post_name_claim(body=body).tx + tx_native, min_fee = _tx_native(idf.OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION, idf.VSN, **body) + # compute the absolute ttl and the nonce + return tx_native + # return self.api.post_name_claim(body=body).tx def tx_name_update(self, account_id, name_id, pointers, name_ttl, client_ttl, fee, ttl, nonce)-> str: """ @@ -177,31 +516,6 @@ def tx_name_update(self, account_id, name_id, pointers, name_ttl, client_ttl, fe :param ttl: the ttl of the transaction :param nonce: the nonce of the account for the transaction """ - if self.native_transactions: - # TODO: verify supported keys for name updates - def pointer_tag(pointer): - return { - "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(idf.pointer_tag(p), p.get("id"))] for p in pointers] - # build tx - tx = [ - _int(idf.OBJECT_TAG_NAME_SERVICE_UPDATE_TRANSACTION), - _int(idf.VSN), - _id(idf.ID_TAG_ACCOUNT, account_id), - _int(nonce), - _id(idf.ID_TAG_NAME, name_id), - _int(name_ttl), - ptrs, - _int(client_ttl), - _int(fee), - _int(ttl) - ] - return encode_rlp(idf.TRANSACTION, tx) - # use internal endpoints transaction body = dict( account_id=account_id, name_id=name_id, @@ -212,7 +526,10 @@ def pointer_tag(pointer): fee=fee, nonce=nonce ) - return self.api.post_name_update(body=body).tx + tx_native, min_fee = _tx_native(idf.OBJECT_TAG_NAME_SERVICE_UPDATE_TRANSACTION, idf.VSN, **body) + # compute the absolute ttl and the nonce + return tx_native + # return self.api.post_name_update(body=body).tx def tx_name_transfer(self, account_id, name_id, recipient_id, fee, ttl, nonce)-> str: """ @@ -224,19 +541,6 @@ def tx_name_transfer(self, account_id, name_id, recipient_id, fee, ttl, nonce)-> :param ttl: the ttl of the transaction :param nonce: the nonce of the account for the transaction """ - if self.native_transactions: - tx = [ - _int(idf.OBJECT_TAG_NAME_SERVICE_TRANSFER_TRANSACTION), - _int(idf.VSN), - _id(idf.ID_TAG_ACCOUNT, account_id), - _int(nonce), - _id(idf.ID_TAG_NAME, name_id), - _id(idf.ID_TAG_ACCOUNT, recipient_id), - _int(fee), - _int(ttl), - ] - return encode_rlp(idf.TRANSACTION, tx) - # use internal endpoints transaction body = dict( account_id=account_id, name_id=name_id, @@ -245,7 +549,10 @@ def tx_name_transfer(self, account_id, name_id, recipient_id, fee, ttl, nonce)-> fee=fee, nonce=nonce ) - return self.api.post_name_transfer(body=body).tx + tx_native, min_fee = _tx_native(idf.OBJECT_TAG_NAME_SERVICE_TRANSFER_TRANSACTION, idf.VSN, **body) + # compute the absolute ttl and the nonce + return tx_native + # return self.api.post_name_transfer(body=body).tx def tx_name_revoke(self, account_id, name_id, fee, ttl, nonce)-> str: """ @@ -257,18 +564,6 @@ def tx_name_revoke(self, account_id, name_id, fee, ttl, nonce)-> str: :param nonce: the nonce of the account for the transaction """ - if self.native_transactions: - tx = [ - _int(idf.OBJECT_TAG_NAME_SERVICE_REVOKE_TRANSACTION), - _int(idf.VSN), - _id(idf.ID_TAG_ACCOUNT, account_id), - _int(nonce), - _id(idf.ID_TAG_NAME, name_id), - _int(fee), - _int(ttl), - ] - return encode_rlp(idf.TRANSACTION, tx) - # use internal endpoints transaction body = dict( account_id=account_id, name_id=name_id, @@ -276,14 +571,17 @@ def tx_name_revoke(self, account_id, name_id, fee, ttl, nonce)-> str: fee=fee, nonce=nonce ) - return self.api.post_name_revoke(body=body).tx + tx_native, min_fee = _tx_native(idf.OBJECT_TAG_NAME_SERVICE_REVOKE_TRANSACTION, idf.VSN, **body) + # compute the absolute ttl and the nonce + return tx_native + # return self.api.post_name_revoke(body=body).tx # CONTRACTS - def tx_contract_create(self, account_id, code, call_data, amount, deposit, gas, gas_price, vm_version, fee, ttl, nonce)-> str: + def tx_contract_create(self, owner_id, code, call_data, amount, deposit, gas, gas_price, vm_version, abi_version, fee, ttl, nonce)-> str: """ Create a contract transaction - :param account_id: the account creating the contract + :param owner_id: the account creating the contract :param code: the binary code of the contract :param call_data: the call data for the contract :param amount: TODO: add definition @@ -291,47 +589,35 @@ def tx_contract_create(self, account_id, code, call_data, amount, deposit, gas, :param gas: TODO: add definition :param gas_price: TODO: add definition :param vm_version: TODO: add definition + :param abi_version: TODO: add definition :param fee: the transaction fee :param ttl: the ttl of the transaction :param nonce: the nonce of the account for the transaction """ - - if self.native_transactions: - tx = [ - _id(idf.account_id), - _int(nonce), - _binary(code), - _int(vm_version), - _int(fee), - _int(ttl), - _int(deposit), - _int(amount), - _int(gas), - _int(gas_price), - _binary(call_data), - ] - return encode_rlp(idf.TRANSACTION, tx), contract_id(idf.account_id, nonce) - # use internal endpoints transaction body = dict( - owner_id=account_id, + owner_id=owner_id, amount=amount, deposit=deposit, fee=fee, gas=gas, gas_price=gas_price, vm_version=vm_version, + abi_version=abi_version, call_data=call_data, code=code, ttl=ttl, nonce=nonce ) - tx = self.api.post_contract_create(body=body) - return tx.tx, tx.contract_id + tx_native, min_fee = _tx_native(idf.OBJECT_TAG_CONTRACT_CREATE_TRANSACTION, idf.VSN, **body) + # compute the absolute ttl and the nonce + return tx_native, contract_id(owner_id, nonce) + # tx = self.api.post_contract_create(body=body) + # return tx.tx, tx.contract_id - def tx_contract_call(self, account_id, contract_id, call_data, function, arg, amount, gas, gas_price, vm_version, fee, ttl, nonce)-> str: + def tx_contract_call(self, caller_id, contract_id, call_data, function, arg, amount, gas, gas_price, abi_version, fee, ttl, nonce)-> str: """ Create a contract call - :param account_id: the account creating the contract + :param caller_id: the account creating the contract :param contract_id: the contract to call :param call_data: the call data for the contract :param function: the function to execute @@ -340,38 +626,28 @@ def tx_contract_call(self, account_id, contract_id, call_data, function, arg, am :param gas: TODO: add definition :param gas_price: TODO: add definition :param vm_version: TODO: add definition + :param abi_version: TODO: add definition :param fee: the transaction fee :param ttl: the ttl of the transaction :param nonce: the nonce of the account for the transaction """ - if self.native_transactions: - tx = [ - _id(idf.account_id), - _int(nonce), - _id(idf.contract_id), - _int(vm_version), - _int(fee), - _int(ttl), - _int(amount), - _int(gas), - _int(gas_price), - _binary(call_data), - ] - return encode_rlp(idf.TRANSACTION, tx) - # use internal endpoints transaction + body = dict( call_data=call_data, - caller_id=account_id, + caller_id=caller_id, contract_id=contract_id, amount=amount, fee=fee, gas=gas, gas_price=gas_price, - vm_version=vm_version, + abi_version=abi_version, ttl=ttl, nonce=nonce ) - return self.api.post_contract_call(body=body).tx + tx_native, min_fee = _tx_native(idf.OBJECT_TAG_CONTRACT_CALL_TRANSACTION, idf.VSN, **body) + # compute the absolute ttl and the nonce + return tx_native + # return self.api.post_contract_call(body=body).tx # ORACLES @@ -382,24 +658,6 @@ def tx_oracle_register(self, account_id, """ Create a register oracle transaction """ - - if self.native_transactions: - tx = [ - _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), - _int(query_fee), - _int(0 if ttl_type == ORACLE_DEFAULT_TTL_TYPE_DELTA else 1), - _int(ttl_value), - _int(fee), - _int(ttl), - _int(vm_version), - ] - return encode_rlp(idf.TRANSACTION, tx) - # use internal endpoints transaction body = dict( account_id=account_id, query_format=query_format, @@ -413,8 +671,11 @@ def tx_oracle_register(self, account_id, ttl=ttl, nonce=nonce ) - tx = self.api.post_oracle_register(body=body) - return tx.tx + tx_native, min_fee = _tx_native(idf.OBJECT_TAG_ORACLE_REGISTER_TRANSACTION, idf.VSN, **body) + # compute the absolute ttl and the nonce + return tx_native + # tx = self.api.post_oracle_register(body=body) + # return tx.tx def tx_oracle_query(self, oracle_id, sender_id, query, query_fee, query_ttl_type, query_ttl_value, @@ -424,24 +685,6 @@ def tx_oracle_query(self, oracle_id, sender_id, query, Create a oracle query transaction """ - if self.native_transactions: - tx = [ - _int(idf.OBJECT_TAG_ORACLE_QUERY_TRANSACTION), - _int(idf.VSN), - _id(idf.ID_TAG_ACCOUNT, sender_id), - _int(nonce), - _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), - _int(query_ttl_value), - _int(0 if response_ttl_type == ORACLE_DEFAULT_TTL_TYPE_DELTA else 1), - _int(response_ttl_value), - _int(fee), - _int(ttl), - ] - return encode_rlp(idf.TRANSACTION, tx) - # use internal endpoints transaction body = dict( sender_id=sender_id, oracle_id=oracle_id, @@ -459,8 +702,11 @@ def tx_oracle_query(self, oracle_id, sender_id, query, ttl=ttl, nonce=nonce, ) - tx = self.api.post_oracle_query(body=body) - return tx.tx + tx_native, min_fee = _tx_native(idf.OBJECT_TAG_ORACLE_QUERY_TRANSACTION, idf.VSN, **body) + # compute the absolute ttl and the nonce + return tx_native + # tx = self.api.post_oracle_query(body=body) + # return tx.tx def tx_oracle_respond(self, oracle_id, query_id, response, response_ttl_type, response_ttl_value, @@ -468,22 +714,6 @@ def tx_oracle_respond(self, oracle_id, query_id, response, """ Create a oracle response transaction """ - - if self.native_transactions: - tx = [ - _int(idf.OBJECT_TAG_ORACLE_RESPONSE_TRANSACTION), - _int(idf.VSN), - _id(idf.ID_TAG_ORACLE, oracle_id), - _int(nonce), - _binary(query_id), - _binary(response), - _int(0 if response_ttl_type == ORACLE_DEFAULT_TTL_TYPE_DELTA else 1), - _int(response_ttl_value), - _int(fee), - _int(ttl), - ] - return encode_rlp(idf.TRANSACTION, tx) - # use internal endpoints transaction body = dict( response_ttl=dict( type=response_ttl_type, @@ -496,8 +726,11 @@ def tx_oracle_respond(self, oracle_id, query_id, response, ttl=ttl, nonce=nonce, ) - tx = self.api.post_oracle_respond(body=body) - return tx.tx + tx_native, min_fee = _tx_native(idf.OBJECT_TAG_ORACLE_RESPONSE_TRANSACTION, idf.VSN, **body) + # compute the absolute ttl and the nonce + return tx_native + # tx = self.api.post_oracle_respond(body=body) + # return tx.tx def tx_oracle_extend(self, oracle_id, ttl_type, ttl_value, @@ -505,20 +738,6 @@ def tx_oracle_extend(self, oracle_id, """ Create a oracle extends transaction """ - - if self.native_transactions: - tx = [ - _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(idf.TRANSACTION, tx) - # use internal endpoints transaction body = dict( oracle_id=oracle_id, oracle_ttl=dict( @@ -529,5 +748,18 @@ def tx_oracle_extend(self, oracle_id, ttl=ttl, nonce=nonce, ) - tx = self.api.post_oracle_extend(body=body) - return tx.tx + tx_native, min_fee = _tx_native(idf.OBJECT_TAG_ORACLE_EXTEND_TRANSACTION, idf.VSN, **body) + # compute the absolute ttl and the nonce + return tx_native + # tx = self.api.post_oracle_extend(body=body) + # return tx.tx + + +class TxBuilderDebug: + def __init__(self, api: OpenAPICli): + """ + :param native: if the transactions should be built by the sdk (True) or requested to the debug api (False) + """ + if api is None: + raise ValueError("A initialized api rest client has to be provided to build a transaction using the node internal API ") + self.api = api diff --git a/docker-compose.yml b/docker-compose.yml index 181a6351..50680c2c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,7 @@ services: volumes: - ${PWD}/docker/aeternity.yaml:/home/aeterinty/aeternity.yaml - ${PWD}/docker/keys/node:/home/aeterinty/node/keys + - ${PWD}/docker/accounts.json:/home/aeternity/node/data/aecore/.genesis/accounts_test.json - node_db:/home/aeterinty/node/data/mnesia - node_keys:/home/aeterinty/node/keys diff --git a/docker/accounts.json b/docker/accounts.json new file mode 100644 index 00000000..8cb732d2 --- /dev/null +++ b/docker/accounts.json @@ -0,0 +1,3 @@ +{ + "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi": 1000000000000000000000000000000000000000 +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 70247b07..c5307ed7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -pytest==3.5.0 -pytest-cov==2.6.0 +pytest==4.3.0 +pytest-cov==2.6.1 base58==0.2.5 click==7.0 deprecation==2.0.5 diff --git a/setup.py b/setup.py index e879961c..f0e50c03 100644 --- a/setup.py +++ b/setup.py @@ -54,7 +54,7 @@ def get_version(): ], classifiers=[ 'Programming Language :: Python', - 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Development Status :: 4 - Beta', 'License :: OSI Approved :: ISC License (ISCL)', 'Intended Audience :: Developers', @@ -62,6 +62,6 @@ def get_version(): ], zip_safe=False, tests_require=[ - 'pytest==3.5.0' + 'pytest==4.3.0' ], ) diff --git a/tests/__init__.py b/tests/__init__.py index b3c41a96..e823c2fa 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,14 +1,6 @@ import logging -import os -from aeternity.config import Config -from aeternity.signing import Account -from aeternity import node # for tempdir -import shutil -import tempfile -from contextlib import contextmanager -import random -import string + logging.getLogger("requests").setLevel(logging.DEBUG) logging.getLogger("urllib3").setLevel(logging.DEBUG) @@ -16,48 +8,5 @@ logging.root.setLevel(logging.DEBUG) -PUBLIC_KEY = os.environ.get('WALLET_PUB') -PRIVATE_KEY = os.environ.get('WALLET_PRIV') -NODE_URL = os.environ.get('TEST_URL') -NODE_URL_DEBUG = os.environ.get('TEST_DEBUG_URL') -NETWORK_ID = os.environ.get('TEST_NETWORK_ID') -# set the key folder as environment variables -genesis = Account.from_public_private_key_strings(PUBLIC_KEY, PRIVATE_KEY) # default values for tests -TEST_FEE = 20000 TEST_TTL = 50 - - -Config.set_defaults(Config( - external_url=NODE_URL, - internal_url=NODE_URL_DEBUG, - network_id=NETWORK_ID -)) - -# Instantiate the node client for the tests -NODE_CLI = node.NodeClient(blocking_mode=True, debug=True, native=False) -# create a new account and fill it with some money -ACCOUNT = Account.generate() -NODE_CLI.spend(genesis, ACCOUNT.get_address(), 1000000000) -a = NODE_CLI.get_account_by_pubkey(pubkey=ACCOUNT.get_address()) -print(f"Test account is {ACCOUNT.get_address()} with balance {a.balance}") - -ACCOUNT_1 = Account.generate() # required for oracles -NODE_CLI.spend(genesis, ACCOUNT_1.get_address(), 1000000000) -a = NODE_CLI.get_account_by_pubkey(pubkey=ACCOUNT_1.get_address()) -print(f"Test account (1) is {ACCOUNT_1.get_address()} with balance {a.balance}") - - -@contextmanager -def tempdir(): - # contextmanager to generate and delete a temporary directory - path = tempfile.mkdtemp() - try: - yield path - finally: - shutil.rmtree(path) - - -def random_domain(length=10): - rand_str = ''.join(random.choice(string.ascii_letters) for _ in range(length)) - return f"{rand_str}.test" diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..4e082dec --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,62 @@ +import pytest +import os +import namedtupled +import shutil +import tempfile +import random +import string +from aeternity.signing import Account +from aeternity.config import Config +from aeternity.node import NodeClient + + +PUBLIC_KEY = os.environ.get('WALLET_PUB') +PRIVATE_KEY = os.environ.get('WALLET_PRIV') +NODE_URL = os.environ.get('TEST_URL') +NODE_URL_DEBUG = os.environ.get('TEST_DEBUG_URL') +NETWORK_ID = os.environ.get('TEST_NETWORK_ID') + + +@pytest.fixture +def tempdir(scope="module"): + # contextmanager to generate and delete a temporary directory + path = tempfile.mkdtemp() + try: + yield path + finally: + shutil.rmtree(path) + + +@pytest.fixture +def random_domain(length=10): + rand_str = ''.join(random.choice(string.ascii_letters) for _ in range(length)) + return f"{rand_str}.test" + + +@pytest.fixture +def chain_fixture(scope="module"): + + # create a new account and fill it with some money + ACCOUNT = Account.generate() + ACCOUNT_1 = Account.generate() # used by for oracles + # set the key folder as environment variables + genesis = Account.from_public_private_key_strings(PUBLIC_KEY, PRIVATE_KEY) + + Config.set_defaults(Config( + external_url=NODE_URL, + internal_url=NODE_URL_DEBUG, + network_id=NETWORK_ID + )) + + # Instantiate the node client for the tests + NODE_CLI = NodeClient(blocking_mode=True, debug=True, native=False) + + NODE_CLI.spend(genesis, ACCOUNT.get_address(), 2000000000000000000) + a = NODE_CLI.get_account_by_pubkey(pubkey=ACCOUNT.get_address()) + print(f"Test account is {ACCOUNT.get_address()} with balance {a.balance}") + + NODE_CLI.spend(genesis, ACCOUNT_1.get_address(), 2000000000000000000) + a = NODE_CLI.get_account_by_pubkey(pubkey=ACCOUNT_1.get_address()) + print(f"Test account (1) is {ACCOUNT_1.get_address()} with balance {a.balance}") + + return namedtupled.map({"NODE_CLI": NODE_CLI, "ACCOUNT": ACCOUNT, "ACCOUNT_1": ACCOUNT_1}, _nt_name="TestData") diff --git a/tests/test_aens.py b/tests/test_aens.py index 198353e8..e6fe2f67 100644 --- a/tests/test_aens.py +++ b/tests/test_aens.py @@ -1,118 +1,117 @@ from aeternity.aens import AEName -from tests import NODE_CLI, ACCOUNT, ACCOUNT_1, random_domain from pytest import raises -def test_name_committment(): - domain = random_domain() - name = NODE_CLI.AEName(domain) +def test_name_committment(chain_fixture, random_domain): + domain = random_domain + name = chain_fixture.NODE_CLI.AEName(domain) cl = name._get_commitment_id() cr = name.client.get_commitment_id(name=name.domain, salt=name.preclaim_salt) assert cl == cr.commitment_id -def test_name_validation_fails(): +def test_name_validation_fails(chain_fixture): with raises(ValueError): - NODE_CLI.AEName('test.lol') + chain_fixture.NODE_CLI.AEName('test.lol') -def test_name_validation_succeeds(): - NODE_CLI.AEName('test.test') +def test_name_validation_succeeds(chain_fixture): + chain_fixture.NODE_CLI.AEName('test.test') -def test_name_is_available(): - name = NODE_CLI.AEName(random_domain()) +def test_name_is_available(chain_fixture, random_domain): + name = chain_fixture.NODE_CLI.AEName(random_domain) assert name.is_available() -def test_name_status_availavle(): - name = NODE_CLI.AEName(random_domain()) +def test_name_status_available(chain_fixture, random_domain): + name = chain_fixture.NODE_CLI.AEName(random_domain) assert name.status == AEName.Status.UNKNOWN name.update_status() assert name.status == AEName.Status.AVAILABLE -def test_name_claim_lifecycle(): +def test_name_claim_lifecycle(chain_fixture, random_domain): try: - domain = random_domain() - name = NODE_CLI.AEName(domain) + domain = random_domain + name = chain_fixture.NODE_CLI.AEName(domain) assert name.status == AEName.Status.UNKNOWN name.update_status() assert name.status == AEName.Status.AVAILABLE - name.preclaim(ACCOUNT) + name.preclaim(chain_fixture.ACCOUNT) assert name.status == AEName.Status.PRECLAIMED - name.claim(ACCOUNT) + name.claim(chain_fixture.ACCOUNT) assert name.status == AEName.Status.CLAIMED except Exception as e: print(e) assert e is None -def test_name_status_unavailable(): +def test_name_status_unavailable(chain_fixture, random_domain): # claim a domain - domain = random_domain() + domain = random_domain print(f"domain is {domain}") - occupy_name = NODE_CLI.AEName(domain) - occupy_name.full_claim_blocking(ACCOUNT) + occupy_name = chain_fixture.NODE_CLI.AEName(domain) + occupy_name.full_claim_blocking(chain_fixture.ACCOUNT) # try to get the same name - same_name = NODE_CLI.AEName(domain) + same_name = chain_fixture.NODE_CLI.AEName(domain) assert not same_name.is_available() -def test_name_update(): +def test_name_update(chain_fixture, random_domain): # claim a domain - domain = random_domain() + domain = random_domain print(f"domain is {domain}") - name = NODE_CLI.AEName(domain) + name = chain_fixture.NODE_CLI.AEName(domain) print("Claim name ", domain) - name.full_claim_blocking(ACCOUNT) + name.full_claim_blocking(chain_fixture.ACCOUNT) # domain claimed name.update_status() - assert not NODE_CLI.AEName(domain).is_available(), 'The name should be claimed now' + assert not chain_fixture.NODE_CLI.AEName(domain).is_available(), 'The name should be claimed now' name.update_status() 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].id == chain_fixture.ACCOUNT.get_address() assert name.pointers[0].key == "account_pubkey" # TODO: enable the test check for pointers -def test_name_transfer_ownership(): - name = NODE_CLI.AEName(random_domain()) - name.full_claim_blocking(ACCOUNT) +def test_name_transfer_ownership(chain_fixture, random_domain): + name = chain_fixture.NODE_CLI.AEName(random_domain) + name.full_claim_blocking(chain_fixture.ACCOUNT) assert name.status == AEName.Status.CLAIMED name.update_status() - assert name.pointers[0].id == ACCOUNT.get_address() + assert name.pointers[0].id == chain_fixture.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()) + name.transfer_ownership(chain_fixture.ACCOUNT, chain_fixture.ACCOUNT_1.get_address()) assert name.status == AEName.Status.TRANSFERRED # try changing the target using that new account name.update_status() - name.update(ACCOUNT_1, ACCOUNT_1.get_address()) + name.update(chain_fixture.ACCOUNT_1, chain_fixture.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].id == chain_fixture.ACCOUNT_1.get_address() assert name.pointers[0].key == "account_pubkey" # def test_transfer_failure_wrong_pubkey(): # client = NodeClient() -# name = NODE_CLI.AEName(random_domain()) +# name = chain_fixture.NODE_CLI.AEName(random_domain) # name.full_claim_blocking() # client.wait_for_next_block() # with raises(AENSException): # name.transfer_ownership('ak_deadbeef') -def test_name_revocation(): - domain = random_domain() - name = NODE_CLI.AEName(domain) - name.full_claim_blocking(ACCOUNT) - name.revoke(ACCOUNT) +def test_name_revocation(chain_fixture, random_domain): + domain = random_domain + name = chain_fixture.NODE_CLI.AEName(domain) + name.full_claim_blocking(chain_fixture.ACCOUNT) + name.revoke(chain_fixture.ACCOUNT) assert name.status == AEName.Status.REVOKED - assert NODE_CLI.AEName(domain).is_available() + assert chain_fixture.NODE_CLI.AEName(domain).is_available() diff --git a/tests/test_api.py b/tests/test_api.py index 4e1ec7bd..c8a18931 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,4 +1,4 @@ -from tests import PUBLIC_KEY, ACCOUNT, NODE_CLI +from tests.conftest import PUBLIC_KEY from aeternity import __compatibility__ from aeternity.signing import Account from aeternity import openapi @@ -7,62 +7,62 @@ # from aeternity.exceptions import TransactionNotFoundException -def test_api_get_account(): +def test_api_get_account(chain_fixture): - account = NODE_CLI.get_account_by_pubkey(pubkey=PUBLIC_KEY) + account = chain_fixture.NODE_CLI.get_account_by_pubkey(pubkey=PUBLIC_KEY) assert account.balance > 0 -def test_api_get_version(): - version = NODE_CLI.get_version() +def test_api_get_version(chain_fixture): + version = chain_fixture.NODE_CLI.get_version() assert semver.match(version, __compatibility__.get("from_version")) assert semver.match(version, __compatibility__.get("to_version")) -def test_api_get_status(): - version = NODE_CLI.get_version() +def test_api_get_status(chain_fixture): + version = chain_fixture.NODE_CLI.get_version() assert semver.match(version, __compatibility__.get("from_version")) assert semver.match(version, __compatibility__.get("to_version")) -def test_api_get_top_block(): - block = NODE_CLI.get_top_block() +def test_api_get_top_block(chain_fixture): + block = chain_fixture.NODE_CLI.get_top_block() # assert type(block) == BlockWithTx assert block.height > 0 -def test_api_get_block_by_height(): - height = NODE_CLI.get_current_key_block_height() +def test_api_get_block_by_height(chain_fixture): + height = chain_fixture.NODE_CLI.get_current_key_block_height() - block = NODE_CLI.get_key_block_by_height(height=height) + block = chain_fixture.NODE_CLI.get_key_block_by_height(height=height) # assert type(block) == BlockWithTx assert block.height > 0 -def test_api_get_block_by_hash(): +def test_api_get_block_by_hash(chain_fixture): has_kb, has_mb = False, False while not has_kb or not has_mb: # the latest block could be both micro or key block - latest_block = NODE_CLI.get_top_block() + latest_block = chain_fixture.NODE_CLI.get_top_block() has_mb = latest_block.hash.startswith("mh_") or has_mb # check if is a microblock has_kb = latest_block.hash.startswith("kh_") or has_kb # check if is a keyblock print(has_kb, has_mb, latest_block.hash) # create a transaction so the top block is going to be an micro block if not has_mb: account = Account.generate().get_address() - NODE_CLI.spend(ACCOUNT, account, 100) + chain_fixture.NODE_CLI.spend(chain_fixture.ACCOUNT, account, 100) # wait for the next block # client.wait_for_next_block() - block = NODE_CLI.get_block_by_hash(hash=latest_block.hash) + block = chain_fixture.NODE_CLI.get_block_by_hash(hash=latest_block.hash) # assert block.hash == latest_block.hash assert block.height == latest_block.height -def test_api_get_genesis_block(): - node_status = NODE_CLI.get_status() - genesis_block = NODE_CLI.get_key_block_by_hash(hash=node_status.genesis_key_block_hash) - zero_height_block = NODE_CLI.get_key_block_by_height(height=0) # these should be equivalent +def test_api_get_genesis_block(chain_fixture): + node_status = chain_fixture.NODE_CLI.get_status() + genesis_block = chain_fixture.NODE_CLI.get_key_block_by_hash(hash=node_status.genesis_key_block_hash) + zero_height_block = chain_fixture.NODE_CLI.get_key_block_by_height(height=0) # these should be equivalent # assert type(genesis_block) == BlockWithTx # assert type(zero_height_block) == BlockWithTx assert genesis_block.height == genesis_block.height == 0 @@ -71,30 +71,30 @@ def test_api_get_genesis_block(): assert genesis_block.hash == zero_height_block.hash -def test_api_get_generation_transaction_count_by_hash(): +def test_api_get_generation_transaction_count_by_hash(chain_fixture): # get the latest block - block_hash = NODE_CLI.get_current_key_block_hash() + block_hash = chain_fixture.NODE_CLI.get_current_key_block_hash() print(block_hash) assert block_hash is not None # get the transaction count that should be a number >= 0 - generation = NODE_CLI.get_generation_by_hash(hash=block_hash) + generation = chain_fixture.NODE_CLI.get_generation_by_hash(hash=block_hash) print(generation) assert len(generation.micro_blocks) >= 0 -def test_api_get_transaction_by_hash_not_found(): +def test_api_get_transaction_by_hash_not_found(chain_fixture): tx_hash = 'th_LUKGEWyZSwyND7vcQwZwLgUXi23WJLQb9jKgJTr1it9QFViMC' try: - NODE_CLI.get_transaction_by_hash(hash=tx_hash) + chain_fixture.NODE_CLI.get_transaction_by_hash(hash=tx_hash) assert False except openapi.OpenAPIClientException as e: assert e.code == 404 -def test_api_get_transaction_by_hash_bad_request(): +def test_api_get_transaction_by_hash_bad_request(chain_fixture): tx_hash = 'th_LUKG' try: - NODE_CLI.get_transaction_by_hash(hash=tx_hash) + chain_fixture.NODE_CLI.get_transaction_by_hash(hash=tx_hash) assert False except openapi.OpenAPIClientException as e: assert e.code == 400 diff --git a/tests/test_cli.py b/tests/test_cli.py index d2aa31f9..f7350619 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -4,7 +4,7 @@ import os import aeternity import random -from tests import NODE_URL, NODE_URL_DEBUG, ACCOUNT, NODE_CLI, NETWORK_ID, tempdir, random_domain +from tests.conftest import NODE_URL, NODE_URL_DEBUG, NETWORK_ID from aeternity.signing import Account from aeternity import utils from aeternity.aens import AEName @@ -15,13 +15,11 @@ aecli_exe = os.path.join(current_folder, '..', 'aecli') -@pytest.fixture -def account_path(): - with tempdir() as tmp_path: - # save the private key on file - sender_path = os.path.join(tmp_path, 'sender') - ACCOUNT.save_to_keystore_file(sender_path, 'aeternity_bc') - yield sender_path +def _account_path(tempdir, account): + # save the private key on file + sender_path = os.path.join(tempdir, 'sender') + account.save_to_keystore_file(sender_path, 'aeternity_bc') + return sender_path def call_aecli(*params): @@ -44,11 +42,11 @@ def test_cli_version(): assert v == f"aecli, version {aeternity.__version__}" -def test_cli_balance(): - j = call_aecli('inspect', ACCOUNT.get_address()) +def test_cli_balance(chain_fixture): + j = call_aecli('inspect', chain_fixture.ACCOUNT.get_address()) assert isinstance(j.get("balance"), int) assert isinstance(j.get("nonce"), int) - assert j.get("id") == ACCOUNT.get_address() + assert j.get("id") == chain_fixture.ACCOUNT.get_address() assert j.get("balance") > 0 @@ -57,68 +55,67 @@ def test_cli_top(): assert j.get("hash").startswith('kh_') or j.get("hash").startswith('mh_') # block hash -def test_cli_generate_account(): - with tempdir() as tmp_path: - account_key = os.path.join(tmp_path, 'key') - j = call_aecli('account', 'create', account_key, '--password', 'secret', '--overwrite') - gen_address = j.get("Address") - assert utils.is_valid_hash(gen_address, prefix='ak') - # make sure the folder contains the keys - files = sorted(os.listdir(tmp_path)) - assert len(files) == 1 - assert files[0].startswith("key") - - -def test_cli_generate_account_and_account_info(): - with tempdir() as tmp_path: - account_path = os.path.join(tmp_path, 'key') - j = call_aecli('account', 'create', account_path, '--password', 'secret') - gen_address = j.get("Address") - assert utils.is_valid_hash(gen_address, prefix='ak') - j1 = call_aecli('account', 'address', account_path, '--password', 'secret') - assert utils.is_valid_hash(j1.get('Address'), prefix='ak') - - -def test_cli_read_account_fail(): - with tempdir() as tmp_path: - account_path = os.path.join(tmp_path, 'key') - j = call_aecli('account', 'create', account_path, '--password', 'secret') - try: - j1 = call_aecli('account', 'address', account_path, '--password', 'WRONGPASS') - assert j.get("Address") != j1.get("Address") - except CalledProcessError: - # this is fine because invalid passwords exists the command with retcode 1 - pass +def test_cli_generate_account(tempdir): + account_key = os.path.join(tempdir, 'key') + j = call_aecli('account', 'create', account_key, '--password', 'secret', '--overwrite') + gen_address = j.get("Address") + assert utils.is_valid_hash(gen_address, prefix='ak') + # make sure the folder contains the keys + files = sorted(os.listdir(tempdir)) + assert len(files) == 1 + assert files[0].startswith("key") + + +def test_cli_generate_account_and_account_info(tempdir): + account_path = os.path.join(tempdir, 'key') + j = call_aecli('account', 'create', account_path, '--password', 'secret') + gen_address = j.get("Address") + assert utils.is_valid_hash(gen_address, prefix='ak') + j1 = call_aecli('account', 'address', account_path, '--password', 'secret') + assert utils.is_valid_hash(j1.get('Address'), prefix='ak') + + +def test_cli_read_account_fail(tempdir): + account_path = os.path.join(tempdir, 'key') + j = call_aecli('account', 'create', account_path, '--password', 'secret') + try: + j1 = call_aecli('account', 'address', account_path, '--password', 'WRONGPASS') + assert j.get("Address") != j1.get("Address") + except CalledProcessError: + # this is fine because invalid passwords exists the command with retcode 1 + pass # @pytest.mark.skip('Fails with account not founds only on the master build server') -def test_cli_spend(account_path): +def test_cli_spend(chain_fixture, tempdir): + account_path = _account_path(tempdir, chain_fixture.ACCOUNT) # generate a new address recipient_address = Account.generate().get_address() # call the cli call_aecli('account', 'spend', account_path, recipient_address, "90", '--password', 'aeternity_bc', '--network-id', NETWORK_ID) # test that the recipient account has the requested amount print(f"recipient address is {recipient_address}") - recipient_account = NODE_CLI.get_account_by_pubkey(pubkey=recipient_address) + recipient_account = chain_fixture.NODE_CLI.get_account_by_pubkey(pubkey=recipient_address) print(f"recipient address {recipient_address}, balance {recipient_account.balance}") assert recipient_account.balance == 90 -def test_cli_spend_invalid_amount(account_path): +def test_cli_spend_invalid_amount(chain_fixture, tempdir): with pytest.raises(subprocess.CalledProcessError): + account_path = _account_path(tempdir, chain_fixture.ACCOUNT) receipient_address = Account.generate().get_address() call_aecli('account', 'spend', account_path, receipient_address, '-1', '--password', 'secret') -def test_cli_inspect_key_block_by_height(): - height = NODE_CLI.get_current_key_block_height() +def test_cli_inspect_key_block_by_height(chain_fixture): + height = chain_fixture.NODE_CLI.get_current_key_block_height() j = call_aecli('inspect', str(height)) assert utils.is_valid_hash(j.get("hash"), prefix=["kh", "mh"]) assert j.get("height") == height -def test_cli_inspect_key_block_by_hash(): - height = NODE_CLI.get_current_key_block_height() +def test_cli_inspect_key_block_by_hash(chain_fixture): + height = chain_fixture.NODE_CLI.get_current_key_block_height() jh = call_aecli('inspect', str(height)) # retrieve the block hash jb = call_aecli('inspect', jh.get("hash")) @@ -135,46 +132,48 @@ def test_cli_inspect_name(): assert j.get("Status") == "AVAILABLE" -def test_cli_inspect_transaction_by_hash(): +def test_cli_inspect_transaction_by_hash(chain_fixture): # fill the account from genesys na = Account.generate() amount = random.randint(50, 150) - _, _, _, tx_hash = NODE_CLI.spend(ACCOUNT, na.get_address(), amount) + _, _, _, tx_hash = chain_fixture.NODE_CLI.spend(chain_fixture.ACCOUNT, na.get_address(), amount) # now inspect the transaction j = call_aecli('inspect', tx_hash) assert j.get("hash") == tx_hash assert j.get("block_height") > 0 assert j.get("tx", {}).get("recipient_id") == na.get_address() - assert j.get("tx", {}).get("sender_id") == ACCOUNT.get_address() + assert j.get("tx", {}).get("sender_id") == chain_fixture.ACCOUNT.get_address() assert j.get("tx", {}).get("amount") == amount -def test_cli_name_claim(account_path): +@pytest.mark.skip("Name claim is outdated") +def test_cli_name_claim(account_path, chain_fixture, random_domain): # create a random domain domain = random_domain() print(f"Domain is {domain}") # call the cli call_aecli('name', 'claim', account_path, domain, '--password', 'aeternity_bc', '--network-id', NETWORK_ID) - NODE_CLI.AEName(domain).status == AEName.Status.CLAIMED + chain_fixture.NODE_CLI.AEName(domain).status == AEName.Status.CLAIMED -def test_cli_phases_spend(account_path): +def test_cli_phases_spend(chain_fixture, tempdir): + account_path = _account_path(tempdir, chain_fixture.ACCOUNT) # generate a new address recipient_id = Account.generate().get_address() # step one, generate transaction - 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") + nonce = chain_fixture.NODE_CLI.get_account_by_pubkey(pubkey=chain_fixture.ACCOUNT.get_address()).nonce + 1 + j = call_aecli('tx', 'spend', chain_fixture.ACCOUNT.get_address(), recipient_id, '100', '--nonce', f'{nonce}') + # assert chain_fixture.ACCOUNT.get_address == j.get("Sender account") assert recipient_id == j.get("Recipient account") # step 2, sign the transaction tx_unsigned = j.get("Encoded") s = call_aecli('account', 'sign', account_path, tx_unsigned, '--password', 'aeternity_bc', '--network-id', NETWORK_ID) tx_signed = s.get("Signed") - # recipient_account = NODE_CLI.get_account_by_pubkey(pubkey=recipient_id) + # recipient_account = chain_fixture.NODE_CLI.get_account_by_pubkey(pubkey=recipient_id) # assert recipient_account.balance == 0 # step 3 broadcast call_aecli('tx', 'broadcast', tx_signed, "--wait") # b.get("Transaction hash") # verify - recipient_account = NODE_CLI.get_account_by_pubkey(pubkey=recipient_id) + recipient_account = chain_fixture.NODE_CLI.get_account_by_pubkey(pubkey=recipient_id) assert recipient_account.balance == 100 diff --git a/tests/test_contract.py b/tests/test_contract.py index fbb63db2..d0f5fd4a 100644 --- a/tests/test_contract.py +++ b/tests/test_contract.py @@ -2,7 +2,6 @@ from pytest import raises from aeternity.contract import ContractError, Contract -from tests import ACCOUNT, NODE_CLI from aeternity import hashing, utils aer_identity_contract = ''' @@ -22,23 +21,23 @@ # -def _sophia_contract_tx_create_online(): +def _sophia_contract_tx_create_online(node_cli, account): # runt tests - contract = NODE_CLI.Contract(aer_identity_contract) - contract.tx_create(ACCOUNT, gas=100000, fee=150000) + contract = node_cli.Contract(aer_identity_contract) + contract.tx_create(account, gas=100000) assert contract.address is not None assert len(contract.address) > 0 assert contract.address.startswith('ct') -def _sophia_contract_tx_call_online(): +def _sophia_contract_tx_call_online(node_cli, account): - contract = NODE_CLI.Contract(aer_identity_contract) - tx = contract.tx_create(ACCOUNT, gas=100000, fee=150000) + contract = node_cli.Contract(aer_identity_contract) + tx = contract.tx_create(account, gas=100000) print("contract: ", contract.address) print("tx contract: ", tx) - _, _, _, _, result = contract.tx_call(ACCOUNT, 'main', '42', gas=500000, fee=1000000) + _, _, _, _, result = contract.tx_call(account, 'main', '42', gas=500000) assert result is not None assert result.return_type == 'ok' print("return", result.return_value) @@ -50,77 +49,79 @@ def _sophia_contract_tx_call_online(): assert remote_type == 'word' -def test_sophia_contract_tx_create_native(): +def test_sophia_contract_tx_create_native(chain_fixture): # save settings and go online - original = NODE_CLI.set_native(False) - _sophia_contract_tx_create_online() + original = chain_fixture.NODE_CLI.set_native(True) + _sophia_contract_tx_create_online(chain_fixture.NODE_CLI, chain_fixture.ACCOUNT) # restore settings - NODE_CLI.set_native(original) + chain_fixture.NODE_CLI.set_native(original) -def test_sophia_contract_tx_call_native(): +def test_sophia_contract_tx_call_native(chain_fixture): # save settings and go online - original = NODE_CLI.set_native(False) - _sophia_contract_tx_call_online() + original = chain_fixture.NODE_CLI.set_native(True) + _sophia_contract_tx_call_online(chain_fixture.NODE_CLI, chain_fixture.ACCOUNT) # restore settings - NODE_CLI.set_native(original) + chain_fixture.NODE_CLI.set_native(original) -def test_sophia_contract_tx_create_debug(): +@pytest.mark.skip('Debug transaction disabled') +def test_sophia_contract_tx_create_debug(chain_fixture): # save settings and go online - original = NODE_CLI.set_native(False) - _sophia_contract_tx_create_online() + original = chain_fixture.NODE_CLI.set_native(False) + _sophia_contract_tx_create_online(chain_fixture.NODE_CLI, chain_fixture.ACCOUNT) # restore settings - NODE_CLI.set_native(original) + chain_fixture.NODE_CLI.set_native(original) -def test_sophia_contract_tx_call_debug(): +@pytest.mark.skip('Debug transaction disabled') +def test_sophia_contract_tx_call_debug(chain_fixture): # save settings and go online - original = NODE_CLI.set_native(False) - _sophia_contract_tx_call_online() + original = chain_fixture.NODE_CLI.set_native(False) + _sophia_contract_tx_call_online(chain_fixture.NODE_CLI, chain_fixture.ACCOUNT) # restore settings - NODE_CLI.set_native(original) + chain_fixture.NODE_CLI.set_native(original) # test contracts -def test_sophia_contract_compile(): - contract = NODE_CLI.Contract(aer_identity_contract) +def test_sophia_contract_compile(chain_fixture): + contract = chain_fixture.NODE_CLI.Contract(aer_identity_contract) assert contract is not None utils.is_valid_hash(contract.bytecode, prefix='cb') @pytest.mark.skip("static call are disabled since 1.0.0") -def test_sophia_contract_call(): - contract = NODE_CLI.Contract(aer_identity_contract) +def test_sophia_contract_call(chain_fixture): + contract = chain_fixture.NODE_CLI.Contract(aer_identity_contract) result = contract.call('main', '1') assert result is not None assert result.out -def test_sophia_encode_calldata(): - contract = NODE_CLI.Contract(aer_identity_contract) +def test_sophia_encode_calldata(chain_fixture): + contract = chain_fixture.NODE_CLI.Contract(aer_identity_contract) result = contract.encode_calldata('main', '1') assert result is not None assert utils.is_valid_hash(result, prefix='cb') -def test_sophia_broken_contract_compile(): +def test_sophia_broken_contract_compile(chain_fixture): with raises(ContractError): - contract = NODE_CLI.Contract(broken_contract) + contract = chain_fixture.NODE_CLI.Contract(broken_contract) print(contract.source_code) -def test_sophia_broken_contract_call(): +def test_sophia_broken_contract_call(chain_fixture): with raises(ContractError): - contract = NODE_CLI.Contract(broken_contract) + contract = chain_fixture.NODE_CLI.Contract(broken_contract) result = contract.call('IdentityBroken.main', '1') print(result) -def test_sophia_broken_encode_calldata(): +def test_sophia_broken_encode_calldata(chain_fixture): with raises(ContractError): - contract = NODE_CLI.Contract(broken_contract) + contract = chain_fixture.NODE_CLI.Contract(broken_contract) result = contract.encode_calldata('IdentityBroken.main', '1') print(result) @@ -129,8 +130,8 @@ def test_sophia_broken_encode_calldata(): # -def test_evm_contract_compile(): - contract = NODE_CLI.Contract(aer_identity_contract, abi=Contract.EVM) +def test_evm_contract_compile(chain_fixture): + contract = chain_fixture.NODE_CLI.Contract(aer_identity_contract, abi=Contract.EVM) print(contract) assert contract.bytecode is not None assert utils.is_valid_hash(contract.bytecode, prefix='cb') @@ -139,36 +140,36 @@ def test_evm_contract_compile(): @pytest.mark.skip('This call fails with an out of gas exception') -def test_evm_contract_call(): - contract = NODE_CLI.Contract(aer_identity_contract, abi=Contract.EVM) +def test_evm_contract_call(chain_fixture): + contract = chain_fixture.NODE_CLI.Contract(aer_identity_contract, abi=Contract.EVM) result = contract.call('main', '1') assert result is not None assert result.out -def test_evm_encode_calldata(): - contract = NODE_CLI.Contract(aer_identity_contract, abi=Contract.EVM) +def test_evm_encode_calldata(chain_fixture): + contract = chain_fixture.NODE_CLI.Contract(aer_identity_contract, abi=Contract.EVM) result = contract.encode_calldata('main', '1') assert result is not None assert result == hashing.encode('cb', 'main1') -def test_evm_broken_contract_compile(): +def test_evm_broken_contract_compile(chain_fixture): with raises(ContractError): - contract = NODE_CLI.Contract(broken_contract, abi=Contract.EVM) + contract = chain_fixture.NODE_CLI.Contract(broken_contract, abi=Contract.EVM) print(contract.source_code) -def test_evm_broken_contract_call(): +def test_evm_broken_contract_call(chain_fixture): with raises(ContractError): - contract = NODE_CLI.Contract(broken_contract, abi=Contract.EVM) + contract = chain_fixture.NODE_CLI.Contract(broken_contract, abi=Contract.EVM) result = contract.call('IdentityBroken.main', '1') print(result) -def test_evm_broken_encode_calldata(): +def test_evm_broken_encode_calldata(chain_fixture): with raises(ContractError): - contract = NODE_CLI.Contract(broken_contract, abi=Contract.EVM) + contract = chain_fixture.NODE_CLI.Contract(broken_contract, abi=Contract.EVM) # with raises(AException): result = contract.encode_calldata('IdentityBroken.main', '1') print(result) diff --git a/tests/test_epoch.py b/tests/test_epoch.py deleted file mode 100644 index 5a637839..00000000 --- a/tests/test_epoch.py +++ /dev/null @@ -1,22 +0,0 @@ -from tests import ACCOUNT, NODE_CLI -from aeternity.signing import Account - -# from aeternity.exceptions import TransactionNotFoundException - - -def _test_node_spend(): - account = Account.generate().get_address() - NODE_CLI.spend(ACCOUNT, account, 100) - account = NODE_CLI.get_account_by_pubkey(pubkey=account) - balance = account.balance - assert balance > 0 - - -def test_node_spend_internal(): - NODE_CLI.set_native(False) - _test_node_spend() - - -def test_node_spend_native(): - NODE_CLI.set_native(True) - _test_node_spend() diff --git a/tests/test_node.py b/tests/test_node.py new file mode 100644 index 00000000..dc477f8f --- /dev/null +++ b/tests/test_node.py @@ -0,0 +1,22 @@ +from aeternity.signing import Account +import pytest +# from aeternity.exceptions import TransactionNotFoundException + + +def _test_node_spend(node_cli, sender_account): + account = Account.generate().get_address() + node_cli.spend(sender_account, account, 100) + account = node_cli.get_account_by_pubkey(pubkey=account) + balance = account.balance + assert balance > 0 + + +@pytest.mark.skip('Debug transaction disabled') +def test_node_spend_debug(chain_fixture): + chain_fixture.NODE_CLI.set_native(False) + _test_node_spend(chain_fixture.NODE_CLI, chain_fixture.ACCOUNT) + + +def test_node_spend_native(chain_fixture): + chain_fixture.NODE_CLI.set_native(True) + _test_node_spend(chain_fixture.NODE_CLI, chain_fixture.ACCOUNT) diff --git a/tests/test_openapi.py b/tests/test_openapi.py index 915bae09..39cf11fa 100644 --- a/tests/test_openapi.py +++ b/tests/test_openapi.py @@ -1,6 +1,6 @@ import pytest from aeternity.openapi import OpenAPICli -from tests import NODE_URL, NODE_URL_DEBUG, PUBLIC_KEY +from tests.conftest import NODE_URL, NODE_URL_DEBUG, PUBLIC_KEY client, priv_key, pub_key = None, None, None diff --git a/tests/test_oracle.py b/tests/test_oracle.py index 84ae4b49..50585055 100644 --- a/tests/test_oracle.py +++ b/tests/test_oracle.py @@ -1,7 +1,6 @@ import logging import pytest -from tests import NODE_CLI, ACCOUNT, ACCOUNT_1 from aeternity.oracles import Oracle, OracleQuery from aeternity import hashing @@ -31,8 +30,8 @@ def on_response(self, response, query): self.response_received = True -def _test_oracle_registration(account): - oracle = NODE_CLI.Oracle() +def _test_oracle_registration(node_cli, account): + oracle = node_cli.Oracle() weather_oracle = dict( account=account, query_format="{'city': str}", @@ -40,13 +39,13 @@ def _test_oracle_registration(account): ) tx, tx_signed, signature, tx_hash = oracle.register(**weather_oracle) assert oracle.id == account.get_address().replace("ak_", "ok_") - oracle_api_response = NODE_CLI.get_oracle_by_pubkey(pubkey=oracle.id) + oracle_api_response = node_cli.get_oracle_by_pubkey(pubkey=oracle.id) assert oracle_api_response.id == oracle.id return oracle -def _test_oracle_query(oracle, sender, query): - q = NODE_CLI.OracleQuery(oracle.id) +def _test_oracle_query(node_cli, oracle, sender, query): + q = node_cli.OracleQuery(oracle.id) q.execute(sender, query) return q @@ -62,24 +61,24 @@ def _test_oracle_response(query, expected): assert r.response == hashing.encode("or", expected) -def test_oracle_lifecycle_debug(): +@pytest.mark.skip('Debug transaction disabled') +def test_oracle_lifecycle_debug(chain_fixture): # registration - NODE_CLI.set_native(False) - oracle = _test_oracle_registration(ACCOUNT) + chain_fixture.NODE_CLI.set_native(False) + oracle = _test_oracle_registration(chain_fixture.NODE_CLI, chain_fixture.ACCOUNT) # query - query = _test_oracle_query(oracle, ACCOUNT_1, "{'city': 'Berlin'}") + query = _test_oracle_query(chain_fixture.NODE_CLI, oracle, chain_fixture.ACCOUNT_1, "{'city': 'Berlin'}") # respond - _test_oracle_respond(oracle, query, ACCOUNT, "{'temp_c': 20}") + _test_oracle_respond(oracle, query, chain_fixture.ACCOUNT, "{'temp_c': 20}") _test_oracle_response(query, "{'temp_c': 20}") -@pytest.mark.skip('Invalid query_id (TODO)') -def test_oracle_lifecycle_native(): +def test_oracle_lifecycle_native(chain_fixture): # registration - NODE_CLI.set_native(True) - oracle = _test_oracle_registration(ACCOUNT_1) + chain_fixture.NODE_CLI.set_native(True) + oracle = _test_oracle_registration(chain_fixture.NODE_CLI, chain_fixture.ACCOUNT_1) # query - query = _test_oracle_query(oracle, ACCOUNT, "{'city': 'Sofia'}") + query = _test_oracle_query(chain_fixture.NODE_CLI, oracle, chain_fixture.ACCOUNT, "{'city': 'Sofia'}") # respond - _test_oracle_respond(oracle, query, ACCOUNT_1, "{'temp_c': 2000}") + _test_oracle_respond(oracle, query, chain_fixture.ACCOUNT_1, "{'temp_c': 2000}") _test_oracle_response(query, "{'temp_c': 2000}") diff --git a/tests/test_signing.py b/tests/test_signing.py index 8ea561d8..deaec33a 100644 --- a/tests/test_signing.py +++ b/tests/test_signing.py @@ -1,23 +1,23 @@ from pytest import raises -from tests import ACCOUNT, NODE_CLI, tempdir, TEST_FEE, TEST_TTL +from tests import TEST_TTL from aeternity.signing import Account, is_signature_valid from aeternity.utils import is_valid_hash from aeternity import hashing import os -def test_signing_create_transaction_signature(): +def test_signing_create_transaction_signature(chain_fixture): # generate a new account new_account = Account.generate() receiver_address = new_account.get_address() # create a spend transaction - nonce, ttl = NODE_CLI._get_nonce_ttl(ACCOUNT.get_address(), TEST_TTL) - tx = NODE_CLI.tx_builder.tx_spend(ACCOUNT.get_address(), receiver_address, 321, "test test ", TEST_FEE, ttl, nonce) - tx_signed, signature, tx_hash = NODE_CLI.sign_transaction(ACCOUNT, tx) + nonce, ttl = chain_fixture.NODE_CLI._get_nonce_ttl(chain_fixture.ACCOUNT.get_address(), TEST_TTL) + tx = chain_fixture.NODE_CLI.tx_builder.tx_spend(chain_fixture.ACCOUNT.get_address(), receiver_address, 321, "test test ", 0, ttl, nonce) + tx_signed, signature, tx_hash = chain_fixture.NODE_CLI.sign_transaction(chain_fixture.ACCOUNT, tx) # this call will fail if the hashes of the transaction do not match - NODE_CLI.broadcast_transaction(tx_signed) + chain_fixture.NODE_CLI.broadcast_transaction(tx_signed) # make sure this works for very short block times - spend_tx = NODE_CLI.get_transaction_by_hash(hash=tx_hash) + spend_tx = chain_fixture.NODE_CLI.get_transaction_by_hash(hash=tx_hash) assert spend_tx.signatures[0] == signature @@ -45,33 +45,34 @@ def test_signing_keystore_load(): assert a.get_address() == "ak_2hSFmdK98bhUw4ar7MUdTRzNQuMJfBFQYxdhN9kaiopDGqj3Cr" -def test_signing_keystore_save_load(): - with tempdir() as tmp_path: - filename = ACCOUNT.save_to_keystore(tmp_path, "whatever") - path = os.path.join(tmp_path, filename) - print(f"\nAccount keystore is {path}") - # now load again the same - a = Account.from_keystore(path, "whatever") - assert a.get_address() == ACCOUNT.get_address() - with tempdir() as tmp_path: - filename = "account_ks" - filename = ACCOUNT.save_to_keystore(tmp_path, "whatever", filename=filename) - path = os.path.join(tmp_path, filename) - print(f"\nAccount keystore is {path}") - # now load again the same - a = Account.from_keystore(path, "whatever") - assert a.get_address() == ACCOUNT.get_address() - - -def test_signing_keystore_save_load_wrong_pwd(): - with tempdir() as tmp_path: - filename = ACCOUNT.save_to_keystore(tmp_path, "whatever") - path = os.path.join(tmp_path, filename) - print(f"\nAccount keystore is {path}") - # now load again the same - with raises(ValueError): - a = Account.from_keystore(path, "nononon") - assert a.get_address() == ACCOUNT.get_address() +def test_signing_keystore_save_load(tempdir): + original_account = Account.generate() + filename = original_account.save_to_keystore(tempdir, "whatever") + path = os.path.join(tempdir, filename) + print(f"\nAccount keystore is {path}") + # now load again the same + a = Account.from_keystore(path, "whatever") + assert a.get_address() == original_account.get_address() + ###### + original_account = Account.generate() + filename = "account_ks" + filename = original_account.save_to_keystore(tempdir, "whatever", filename=filename) + path = os.path.join(tempdir, filename) + print(f"\nAccount keystore is {path}") + # now load again the same + a = Account.from_keystore(path, "whatever") + assert a.get_address() == original_account.get_address() + + +def test_signing_keystore_save_load_wrong_pwd(tempdir): + original_account = Account.generate() + filename = original_account.save_to_keystore(tempdir, "whatever") + path = os.path.join(tempdir, filename) + print(f"\nAccount keystore is {path}") + # now load again the same + with raises(ValueError): + a = Account.from_keystore(path, "nononon") + assert a.get_address() == original_account.get_address() def test_signing_is_signature_valid(): diff --git a/tests/test_transactions.py b/tests/test_transactions.py index 0c2fe876..1fa29644 100644 --- a/tests/test_transactions.py +++ b/tests/test_transactions.py @@ -1,9 +1,9 @@ -from tests import NODE_CLI, ACCOUNT + from aeternity.signing import Account from aeternity import transactions -def _execute_test(test_cases): +def _execute_test(test_cases, NODE_CLI): for tt in test_cases: # get a native transaction txbn = transactions.TxBuilder(api=NODE_CLI, native=True) @@ -18,36 +18,80 @@ def _execute_test(test_cases): assert txn != txd -def test_transaction_spend(): +def test_transaction_fee_calculation(): + sender_id = Account.generate().get_address() + recipient_id = Account.generate().get_address() + # account_id, recipient_id, amount, payload, fee, ttl, nonce + tts = [ + { + "native": (sender_id, recipient_id, 1000, "", 1, 0, 1), + "field_fee_idx": 4, + "match": False + }, { + "native": (sender_id, recipient_id, 9845, "another payload", 1, 500, 1241), + "field_fee_idx": 4, + "match": False + }, { + "native": (sender_id, recipient_id, 9845, "another payload", 1, 500, 32131), + "field_fee_idx": 4, + "match": False + }, { + "native": (sender_id, recipient_id, 410000, "this is a very long payload that is not good to have ", 100, 500, 1241), + "field_fee_idx": 4, + "match": False + }, { + "native": (sender_id, recipient_id, 410000, "another payload", 20000000000000, 10000000, 1241), + "field_fee_idx": 4, + "match": True + }, { + "native": (sender_id, recipient_id, 5000000000000000000, "Faucet TX", 20000000000000, 0, 1241), + "field_fee_idx": 4, + "match": True # 16920000000000 + }, + ] + + for tt in tts: + # get a native transaction + txbn = transactions.TxBuilder() + txn = txbn.tx_spend(tt["native"][0], tt["native"][1], tt["native"][2], tt["native"][3], tt["native"][4], tt["native"][5], tt["native"][6]) + print("=================") + print(txn) + # if tt["match"]: + # assert tt["native"][tt.get("field_fee_idx")] >= min_fee + + # get a debug transaction + + +def test_transaction_spend(chain_fixture): recipient_id = Account.generate().get_address() # account_id, recipient_id, amount, payload, fee, ttl, nonce tts = [ { - "native": (ACCOUNT.get_address(), recipient_id, 1000, "payload", 1, 100, 5555), - "debug": (ACCOUNT.get_address(), recipient_id, 1000, "payload", 1, 100, 5555), + "native": (chain_fixture.ACCOUNT.get_address(), recipient_id, 1000, "payload", 1, 100, 5555), + "debug": (chain_fixture.ACCOUNT.get_address(), recipient_id, 1000, "payload", 1, 100, 5555), "match": True }, { - "native": (ACCOUNT.get_address(), recipient_id, 9845, "another payload", 1, 500, 1241), - "debug": (ACCOUNT.get_address(), recipient_id, 9845, "another payload", 1, 500, 1241), + "native": (chain_fixture.ACCOUNT.get_address(), recipient_id, 9845, "another payload", 1, 500, 1241), + "debug": (chain_fixture.ACCOUNT.get_address(), recipient_id, 9845, "another payload", 1, 500, 1241), "match": True }, { - "native": (ACCOUNT.get_address(), recipient_id, 9845, "another payload", 1, 500, 32131), - "debug": (ACCOUNT.get_address(), Account.generate().get_address(), 9845, "another payload", 1, 500, 32131), + "native": (chain_fixture.ACCOUNT.get_address(), recipient_id, 9845, "another payload", 1, 500, 32131), + "debug": (chain_fixture.ACCOUNT.get_address(), Account.generate().get_address(), 9845, "another payload", 1, 500, 32131), "match": False }, { - "native": (ACCOUNT.get_address(), recipient_id, 410000, "another payload", 100, 500, 1241), - "debug": (ACCOUNT.get_address(), recipient_id, 410000, "another payload", 100, 500, 1241), + "native": (chain_fixture.ACCOUNT.get_address(), recipient_id, 410000, "another payload", 100, 500, 1241), + "debug": (chain_fixture.ACCOUNT.get_address(), recipient_id, 410000, "another payload", 100, 500, 1241), "match": True }, ] for tt in tts: # get a native transaction - txbn = transactions.TxBuilder(api=NODE_CLI, native=True) + txbn = transactions.TxBuilder(api=chain_fixture.NODE_CLI, native=True) txn = txbn.tx_spend(tt["native"][0], tt["native"][1], tt["native"][2], tt["native"][3], tt["native"][4], tt["native"][5], tt["native"][6]) # get a debug transaction - txbd = transactions.TxBuilder(api=NODE_CLI, native=False) + txbd = transactions.TxBuilder(api=chain_fixture.NODE_CLI, native=False) txd = txbd.tx_spend(tt["debug"][0], tt["debug"][1], tt["debug"][2], tt["debug"][3], tt["debug"][4], tt["debug"][5], tt["debug"][6]) # theys should be the same if tt["match"]: @@ -56,7 +100,7 @@ def test_transaction_spend(): assert txn != txd -def test_transaction_oracle_register(): +def test_transaction_oracle_register(chain_fixture): # account_id, recipient_id, amount, payload, fee, ttl, nonce tts = [ { @@ -80,4 +124,4 @@ def test_transaction_oracle_register(): "match": False } ] - _execute_test(tts) + _execute_test(tts, chain_fixture.NODE_CLI)