def parse_send_many_params(self, params): if type(params[0]) is not list: raise JsonRpcError(-32602, "Invalid params") if len(params) not in [1, 2, 3]: raise JsonRpcError(-32602, "Invalid params") output = [] for info in params[0]: asset = get_asset_id(self.wallet, info['asset']) if not type(asset) is UInt256: raise JsonRpcError(-32602, "Invalid params") address = info["address"] try: address = self.wallet.ToScriptHash(address) except Exception: raise JsonRpcError(-32602, "Invalid params") amount = Fixed8.TryParse(info["value"], require_positive=True) if not amount or float(info["value"]) == 0: raise JsonRpcError(-32602, "Invalid params") tx_output = TransactionOutput(AssetId=asset, Value=amount, script_hash=address) output.append(tx_output) contract_tx = ContractTransaction(outputs=output) fee = Fixed8.TryParse(params[1]) if len(params) >= 2 else Fixed8.Zero() if fee < Fixed8.Zero(): raise JsonRpcError(-32602, "Invalid params") change_addr_sh = None if len(params) >= 3: change_addr = params[2] try: change_addr_sh = self.wallet.ToScriptHash(change_addr) except Exception: raise JsonRpcError(-32602, "Invalid params") return contract_tx, fee, change_addr_sh
def parse_send_from_params(self, params): if len(params) not in [4, 5, 6]: raise JsonRpcError(-32602, "Invalid params") asset_id = get_asset_id(self.wallet, params[0]) if not type(asset_id) is UInt256: raise JsonRpcError(-32602, "Invalid params") address_from = params[1] try: address_from_sh = self.wallet.ToScriptHash(address_from) except Exception: raise JsonRpcError(-32602, "Invalid params") address_to = params[2] try: address_to_sh = self.wallet.ToScriptHash(address_to) except Exception: raise JsonRpcError(-32602, "Invalid params") amount = Fixed8.TryParse(params[3], require_positive=True) if not amount or float(params[3]) == 0: raise JsonRpcError(-32602, "Invalid params") output = TransactionOutput(AssetId=asset_id, Value=amount, script_hash=address_to_sh) contract_tx = ContractTransaction(outputs=[output]) fee = Fixed8.TryParse(params[4]) if len(params) >= 5 else Fixed8.Zero() if fee < Fixed8.Zero(): raise JsonRpcError(-32602, "Invalid params") change_addr_sh = None if len(params) >= 6: change_addr = params[5] try: change_addr_sh = self.wallet.ToScriptHash(change_addr) except Exception: raise JsonRpcError(-32602, "Invalid params") return contract_tx, address_from_sh, fee, change_addr_sh
def test_various_methods(self): self.assertEqual(Fixed8.FD(), Fixed8(100000000)) self.assertEqual(Fixed8.FD(), Fixed8.One()) sat = Fixed8.Satoshi() self.assertEqual(sat, Fixed8(1)) self.assertEqual(sat.value, 1) negsat = Fixed8.NegativeSatoshi() self.assertEqual(negsat, Fixed8(-1)) self.assertEqual(negsat.value, -1) zero = Fixed8.Zero() self.assertEqual(zero, Fixed8(0)) self.assertEqual(zero.value, 0) decimal = 1.2 f8 = Fixed8.FromDecimal(decimal) self.assertEqual(f8.value, f8.GetData()) f8 = Fixed8.TryParse(123.123) f8 = Fixed8.TryParse(123) self.assertEqual(f8.Size(), 8) zero = Fixed8.TryParse(0) self.assertEqual(zero, Fixed8(0)) self.assertEqual(Fixed8.TryParse("foo"), None) self.assertEqual(Fixed8.TryParse(-1, require_positive=True), None)
def GetTransactionResults(self): """ Get the execution results of the transaction. Returns: None: if the transaction has no references. list: of TransactionResult objects. """ if self.References is None: return None results = [] realresults = [] for ref_output in self.References.values(): results.append( TransactionResult(ref_output.AssetId, ref_output.Value)) for output in self.outputs: results.append( TransactionResult(output.AssetId, output.Value * Fixed8(-1))) for key, group in groupby(results, lambda x: x.AssetId): sum = Fixed8(0) for item in group: sum = sum + item.Amount if sum != Fixed8.Zero(): realresults.append(TransactionResult(key, sum)) return realresults
def Run(snapshot, script, container=None, exit_on_error=False, gas=Fixed8.Zero(), test_mode=True, wb=None): """ Runs a script in a test invoke environment Args: script (bytes): The script to run container (neo.Core.TX.Transaction): [optional] the transaction to use as the script container Returns: ApplicationEngine """ engine = ApplicationEngine(TriggerType.Application, container, snapshot, gas, test_mode) # maybe not the best solution # but one for now if not wb: _script = binascii.unhexlify(script) else: _script = script engine.LoadScript(_script) engine.Execute() for event in engine._Service.events_to_dispatch: events.emit(event.event_type, event) return engine
def do_token_transfer(token, wallet, from_address, to_address, amount, fee=Fixed8.Zero(), tx_attributes=None): if not tx_attributes: tx_attributes = [] p_fee = fee # because we cannot differentiate between a normal and multisig from_addr, and because we want to make # sending NEP5 tokens straight forward even when sending from multisig addresses, we include the script_hash # for verification by default to the transaction attributes. See PR/Issue: https://github.com/CityOfZion/neo-python/pull/491 from_script_hash = binascii.unhexlify( bytes(wallet.ToScriptHash(from_address).ToString2(), 'utf-8')) tx_attributes.append( TransactionAttribute(usage=TransactionAttributeUsage.Script, data=from_script_hash)) tx, fee, results = token.Transfer(wallet, from_address, to_address, amount, tx_attributes=tx_attributes) if tx is not None and results is not None and len(results) > 0: if results[0].GetBigInteger() == 1: print( "\n-----------------------------------------------------------" ) print("Will transfer %s %s from %s to %s" % (string_from_amount( token, amount), token.symbol, from_address, to_address)) print("Transfer fee: %s " % (fee.value / Fixed8.D)) print( "-------------------------------------------------------------\n" ) comb_fee = p_fee + fee if comb_fee != fee: print( f"Priority Fee ({p_fee.value / Fixed8.D}) + Transfer Fee ({fee.value / Fixed8.D}) = {comb_fee.value / Fixed8.D}\n" ) print("Enter your password to send to the network") try: passwd = prompt("[Password]> ", is_password=True) except KeyboardInterrupt: print("Transfer cancelled") return False if not wallet.ValidatePassword(passwd): print("incorrect password") return False return InvokeContract(wallet, tx, comb_fee) print("could not transfer tokens") return False
def InvokeWithTokenVerificationScript(wallet, tx, token, fee=Fixed8.Zero(), invoke_attrs=None): try: wallet_tx = wallet.MakeTransaction(tx=tx, fee=fee, use_standard=True) except ValueError: print("Insufficient funds") return False if wallet_tx: token_contract_state = Blockchain.Default().GetContract(token.ScriptHash.ToString()) print("token contract %s " % token_contract_state) tx.Attributes = [ TransactionAttribute(usage=TransactionAttributeUsage.Script, data=token.ScriptHash.Data) ] if invoke_attrs: tx.Attributes += invoke_attrs reedeem_script = token_contract_state.Code.Script.hex() # there has to be at least 1 param, and the first # one needs to be a signature param param_list = bytearray(b'\x00\x00') verification_contract = Contract.Create(reedeem_script, param_list, wallet.GetDefaultContract().PublicKeyHash) context = ContractParametersContext(wallet_tx) wallet.Sign(context) context.Add(verification_contract, 0, 0) if context.Completed: wallet_tx.scripts = context.GetScripts() nodemgr = NodeManager() relayed = nodemgr.relay(wallet_tx) if relayed: print("Relayed Tx: %s " % wallet_tx.Hash.ToString()) # if it was relayed, we save tx wallet.SaveTransaction(wallet_tx) return wallet_tx else: print("Could not relay tx %s " % wallet_tx.Hash.ToString()) else: print("Incomplete signature") else: print("Insufficient funds") return False
def InvokeContract(wallet, tx, fee=Fixed8.Zero(), from_addr=None, owners=None): if from_addr is not None: from_addr = PromptUtils.lookup_addr_str(wallet, from_addr) try: wallet_tx = wallet.MakeTransaction(tx=tx, fee=fee, use_standard=True, from_addr=from_addr) except ValueError: print("Insufficient funds") return False except TXFeeError as e: print(e) return False if wallet_tx: if owners: for owner in list(owners): wallet_tx.Attributes.append( TransactionAttribute( usage=TransactionAttributeUsage.Script, data=owner)) wallet_tx.Attributes = make_unique_script_attr(tx.Attributes) context = ContractParametersContext(wallet_tx) wallet.Sign(context) if owners: gather_signatures(context, wallet_tx, list(owners)) if context.Completed: wallet_tx.scripts = context.GetScripts() relayed = False # print("SENDING TX: %s " % json.dumps(wallet_tx.ToJson(), indent=4)) relayed = NodeLeader.Instance().Relay(wallet_tx) if relayed: print("Relayed Tx: %s " % wallet_tx.Hash.ToString()) wallet.SaveTransaction(wallet_tx) return wallet_tx else: print("Could not relay tx %s " % wallet_tx.Hash.ToString()) else: print("Incomplete signature") else: print("Insufficient funds") return False
def GetSystemFee_Validator(self): if self.Field == "Registered": for character in self.Value: if character != '0': return Fixed8.FromDecimal(1000) return Fixed8.Zero() raise Exception("Invalid operation")
def execute(self, arguments): wallet = PromptData.Wallet if len(arguments) < 4: print("Please specify the required parameters") return if len(arguments) > 5: # the 5th argument is the optional attributes, print("Too many parameters supplied. Please check your command") return addr = arguments[0] if not isValidPublicAddress(addr): print("Invalid address specified") return try: from_addr = wallet.ToScriptHash(addr) except ValueError as e: print(str(e)) return asset_id = PromptUtils.get_asset_id(wallet, arguments[1]) if not asset_id: print(f"Unknown asset id: {arguments[1]}") return try: index = int(arguments[2]) except ValueError: print(f"Invalid unspent index value: {arguments[2]}") return try: divisions = int(arguments[3]) except ValueError: print(f"Invalid divisions value: {arguments[3]}") return if divisions < 2: print("Divisions cannot be lower than 2") return if len(arguments) == 5: fee = Fixed8.TryParse(arguments[4], require_positive=True) if not fee: print(f"Invalid fee value: {arguments[4]}") return else: fee = Fixed8.Zero() return SplitUnspentCoin(wallet, asset_id, from_addr, index, divisions, fee)
def SystemFee(self): """ Get the system fee. Returns: Fixed8: """ if self.Version >= 1: return Fixed8.Zero() # if all outputs are NEO or gas, return 0 all_neo_gas = True for output in self.outputs: if output.AssetId != GetSystemCoin( ).Hash and output.AssetId != GetSystemShare().Hash: all_neo_gas = False if all_neo_gas: return Fixed8.Zero() return super(IssueTransaction, self).SystemFee()
def TotalFees(self): """ Get the total transaction fees in the block. Returns: Fixed8: """ amount = Fixed8.Zero() for tx in self.Transactions: amount += tx.SystemFee() return amount
def SystemFee(self): """ Get the system fee. Returns: Fixed8: """ if self.AssetType == AssetType.GoverningToken or self.AssetType == AssetType.UtilityToken: return Fixed8.Zero() return super(RegisterTransaction, self).SystemFee()
def CalcChange(self, change_addr=None): """ Calculates the change output(s). NOTE: Assumes all other outputs have been added. Args: change_addr: (str, optional) specify a change address. NOTE: Defaults to the sourceAddress. """ if not change_addr: change_addr = self.SOURCE_SCRIPTHASH if change_addr != self.SOURCE_SCRIPTHASH: change_hash = Helper.AddrStrToScriptHash( change_addr) # also verifies if the address is valid else: change_hash = change_addr if not self.outputs: raise RawTXError( "Please specify outputs prior to creating change output(s).") neo = [] gas = [] for output in self.outputs: if output.AssetId == UInt256.ParseString(self.neo_asset_id): neo.append(output.Value.value) elif output.AssetId == UInt256.ParseString(self.gas_asset_id): gas.append(output.Value.value) if self.SystemFee() > Fixed8.Zero(): gas.append(self.SystemFee().value) if self._network_fee: if self._network_fee > Fixed8.Zero(): gas.append(self._network_fee.value) neo_total = 0 gas_total = 0 for asset in self.BALANCE: if asset['asset_hash'] == self.neo_asset_id: neo_total = asset['amount'] elif asset['asset_hash'] == self.gas_asset_id: gas_total = asset['amount'] neo_diff = Fixed8.FromDecimal(neo_total) - Fixed8(sum(neo)) gas_diff = Fixed8.FromDecimal(gas_total) - Fixed8(sum(gas)) if neo_diff < Fixed8.Zero() or gas_diff < Fixed8.Zero(): raise AssetError('Total outputs exceed the available unspents.') if neo_diff > Fixed8.Zero(): self.outputs.append( TransactionOutput(AssetId=UInt256.ParseString( self.neo_asset_id), Value=neo_diff, script_hash=change_hash)) if gas_diff > Fixed8.Zero() and Fixed8(sum(gas)) > Fixed8.Zero(): self.outputs.append( TransactionOutput(AssetId=UInt256.ParseString( self.gas_asset_id), Value=gas_diff, script_hash=change_hash))
def split_to_vouts(asset, addr, input_val, divisions): divisor = Fixed8(divisions) new_amounts = input_val / divisor outputs = [] total = Fixed8.Zero() if asset == Blockchain.Default().SystemShare().Hash: if new_amounts % Fixed8.FD() > Fixed8.Zero(): new_amounts = new_amounts.Ceil() while total < input_val: if total + new_amounts < input_val: outputs.append(TransactionOutput(asset, new_amounts, addr)) total += new_amounts else: diff = input_val - total outputs.append(TransactionOutput(asset, diff, addr)) total += diff return outputs
def Run(script, container=None, exit_on_error=False, gas=Fixed8.Zero(), test_mode=True): """ Runs a script in a test invoke environment Args: script (bytes): The script to run container (neo.Core.TX.Transaction): [optional] the transaction to use as the script container Returns: ApplicationEngine """ from neo.Core.Blockchain import Blockchain from neo.SmartContract.StateMachine import StateMachine from neo.EventHub import events bc = Blockchain.Default() accounts = DBCollection(bc._db, DBPrefix.ST_Account, AccountState) assets = DBCollection(bc._db, DBPrefix.ST_Asset, AssetState) validators = DBCollection(bc._db, DBPrefix.ST_Validator, ValidatorState) contracts = DBCollection(bc._db, DBPrefix.ST_Contract, ContractState) storages = DBCollection(bc._db, DBPrefix.ST_Storage, StorageItem) script_table = CachedScriptTable(contracts) service = StateMachine(accounts, validators, assets, contracts, storages, None) engine = ApplicationEngine( trigger_type=TriggerType.Application, container=container, table=script_table, service=service, gas=gas, testMode=test_mode, exit_on_error=exit_on_error ) script = binascii.unhexlify(script) engine.LoadScript(script) try: success = engine.Execute() engine.testMode = True service.ExecutionCompleted(engine, success) except Exception as e: engine.testMode = True service.ExecutionCompleted(engine, False, e) for event in service.events_to_dispatch: events.emit(event.event_type, event) return engine
def InvokeContract(wallet, tx, fee=Fixed8.Zero(), from_addr=None, owners=None): if from_addr is not None: from_addr = PromptUtils.lookup_addr_str(wallet, from_addr) try: wallet_tx = wallet.MakeTransaction(tx=tx, fee=fee, use_standard=True, from_addr=from_addr) except ValueError: print("Insufficient funds") return False if wallet_tx: if owners: for owner in list(owners): wallet_tx.Attributes.append(TransactionAttribute(usage=TransactionAttributeUsage.Script, data=owner)) wallet_tx.Attributes = make_unique_script_attr(tx.Attributes) context = ContractParametersContext(wallet_tx) wallet.Sign(context) if owners: gather_signatures(context, wallet_tx, list(owners)) if context.Completed: wallet_tx.scripts = context.GetScripts() passed, reason = validate_simple_policy(wallet_tx) if not passed: print(reason) return False nodemgr = NodeManager() relayed = nodemgr.relay(wallet_tx) if relayed: print("Relayed Tx: %s " % wallet_tx.Hash.ToString()) wallet.SaveTransaction(wallet_tx) return wallet_tx else: print("Could not relay tx %s " % wallet_tx.Hash.ToString()) else: print("Incomplete signature") else: print("Insufficient funds") return False
def token_mint(token, wallet, to_addr, asset_attachments=[], fee=Fixed8.Zero(), invoke_attrs=None): if not invoke_attrs: invoke_attrs = [] p_fee = fee tx, fee, results = token.Mint(wallet, to_addr, asset_attachments, invoke_attrs=invoke_attrs) if tx is not None and results is not None: if len(results) > 0 and results[0] is not None: print( "\n-----------------------------------------------------------" ) print(f"[{token.symbol}] Will mint tokens to address: {to_addr}") print(f"Invocation Fee: {fee.value / Fixed8.D}") print( "-------------------------------------------------------------\n" ) comb_fee = p_fee + fee if comb_fee != fee: print( f"Priority Fee ({p_fee.value / Fixed8.D}) + Invocation Fee ({fee.value / Fixed8.D}) = {comb_fee.value / Fixed8.D}\n" ) print("Enter your password to send to the network") try: passwd = prompt("[Password]> ", is_password=True) except KeyboardInterrupt: print("Token mint cancelled") return False if not wallet.ValidatePassword(passwd): print("incorrect password") return False return InvokeWithTokenVerificationScript(wallet, tx, token, comb_fee, invoke_attrs=invoke_attrs) print("Failed to mint tokens") return False
def token_send(wallet, token_str, from_addr, to_addr, amount, fee=Fixed8.Zero(), user_tx_attributes=None): """ Send `amount` of tokens from `from_addr` to `to_addr` Args: wallet (Wallet): a UserWallet instance token_str (str): symbol name or script_hash from_addr (str): a wallet address to_addr (str): a wallet address amount (float): the number of tokens to send fee (Fixed8): (optional) a fee to give the transaction priority (> 0.001) user_tx_attributes (list): a list of ``TransactionAttribute``s. Raises: ValueError: for invalid arguments Returns: a Transaction object if successful, False otherwise. """ if not user_tx_attributes: user_tx_attributes = [] try: token = _validate_nep5_args(wallet, token_str, from_addr, to_addr, amount) except ValueError: # just making it explicit for the reader raise for attr in user_tx_attributes: if not isinstance(attr, TransactionAttribute): raise ValueError(f"{attr} is not a valid transaction attribute") decimal_amount = amount_from_string(token, amount) return do_token_transfer(token, wallet, from_addr, to_addr, decimal_amount, fee=fee, tx_attributes=user_tx_attributes)
def __init__(self, pub_key=None): """ Create an instance. Args: pub_key (EllipticCurve.ECPoint): Raises: Exception: if `pub_key` is not a valid ECPoint. """ if pub_key is not None and type(pub_key) is not EllipticCurve.ECPoint: raise Exception("Pubkey must be ECPoint Instance") self.PublicKey = pub_key self.Registered = False self.Votes = Fixed8.Zero()
def CalculateBonusInternal(unclaimed): amount_claimed = Fixed8.Zero() decInterval = Blockchain.DECREMENT_INTERVAL genAmount = Blockchain.GENERATION_AMOUNT genLen = len(genAmount) for coinheight, group in groupby(unclaimed, lambda x: x.Heights): amount = 0 ustart = int(coinheight.start / decInterval) if ustart < genLen: istart = coinheight.start % decInterval uend = int(coinheight.end / decInterval) iend = coinheight.end % decInterval if uend >= genLen: iend = 0 if iend == 0: uend -= 1 iend = decInterval while ustart < uend: amount += (decInterval - istart) * genAmount[ustart] ustart += 1 istart = 0 amount += (iend - istart) * genAmount[ustart] endamount = Blockchain.Default().GetSysFeeAmountByHeight( coinheight.end - 1) startamount = 0 if coinheight.start == 0 else Blockchain.Default( ).GetSysFeeAmountByHeight(coinheight.start - 1) amount += endamount - startamount outputSum = 0 for spentcoin in group: outputSum += spentcoin.Value.value outputSum = outputSum / 100000000 outputSumFixed8 = Fixed8(int(outputSum * amount)) amount_claimed += outputSumFixed8 return amount_claimed
def execute(self, arguments): wallet = PromptData.Wallet if len(arguments) < 4: print("Please specify the required parameters") return False if len(arguments) > 6: # the 5th and 6th arguments are optional print("Too many parameters supplied. Please check your command") return False arguments, priority_fee = PromptUtils.get_fee(arguments) arguments, user_tx_attributes = PromptUtils.get_tx_attr_from_args( arguments) token = arguments[0] from_addr = arguments[1] to_addr = arguments[2] try: amount = float(arguments[3]) except ValueError: print(f"{arguments[3]} is not a valid amount") return False fee = Fixed8.Zero() if priority_fee is not None: fee = priority_fee if fee is False: logger.debug("invalid fee") return False try: success = token_send(wallet, token, from_addr, to_addr, amount, fee=fee, user_tx_attributes=user_tx_attributes) except ValueError as e: # occurs if arguments are invalid print(str(e)) success = False return success
def test_1_no_available_claim(self): wallet = self.GetWallet3() unspents = wallet.FindUnspentCoinsByAsset(self.NEO) self.assertEqual(1, len(unspents)) unavailable_bonus = wallet.GetUnavailableBonus() self.assertEqual(Fixed8.FromDecimal(0.0002685), unavailable_bonus) unclaimed_coins = wallet.GetUnclaimedCoins() self.assertEqual(0, len(unclaimed_coins)) available_bonus = wallet.GetAvailableClaimTotal() self.assertEqual(Fixed8.Zero(), available_bonus)
def Validate(self): """ Validate the transaction. """ if self.Size() > self.MAX_TX_SIZE: raise TXAttributeError('Maximum transaction size exceeded.') # calculate and verify the required network fee for the tx if not self._network_fee: self._network_fee = Fixed8.Zero() fee = self._network_fee if self.Size() > settings.MAX_FREE_TX_SIZE: req_fee = Fixed8.FromDecimal( settings.FEE_PER_EXTRA_BYTE * (self.Size() - settings.MAX_FREE_TX_SIZE)) if req_fee < settings.LOW_PRIORITY_THRESHOLD: req_fee = settings.LOW_PRIORITY_THRESHOLD if fee < req_fee: raise TXFeeError( f'The tx size ({self.Size()}) exceeds the max free tx size ({settings.MAX_FREE_TX_SIZE}).\nA network fee of {req_fee.ToString()} GAS is required.' )
def DeserializeExclusiveData(self, reader): """ Deserialize full object. Args: reader (neo.IO.BinaryReader): """ if self.Type == b'\x02': # ClaimTransaction if self.Version != 0: raise FormatError('Invalid format') numrefs = reader.ReadVarInt() claims = [] for i in range(0, numrefs): c = CoinReference() c.Deserialize(reader) claims.append(c) self.Claims = claims if len(self.Claims) == 0: raise FormatError('Invalid format') elif self.Type == b'\xd1': # InvocationTransaction if self.Version > 1: raise FormatError('Invalid format') self.Script = reader.ReadVarBytes() if len(self.Script) == 0: raise FormatError('Invalid Format') if self.Version >= 1: self.Gas = reader.ReadFixed8() if self.Gas < Fixed8.Zero(): raise FormatError("Invalid Format") else: self.Gas = Fixed8(0) else: super(RawTransaction, self).DeserializeExclusiveData(reader=reader)
def DeserializeExclusiveData(self, reader): """ Deserialize full object. Args: reader (neo.IO.BinaryReader): Raises: Exception: If the version read is incorrect. """ if self.Version > 1: raise Exception('Invalid format') self.Script = reader.ReadVarBytes() if len(self.Script) == 0: raise Exception('Invalid Format') if self.Version >= 1: self.Gas = reader.ReadFixed8() if self.Gas < Fixed8.Zero(): raise Exception("Invalid Format") else: self.Gas = Fixed8(0)
def test_deploy_and_invoke(deploy_script, invoke_args, wallet, from_addr=None, min_fee=DEFAULT_MIN_FEE, invocation_test_mode=True, debug_map=None, invoke_attrs=None, owners=None): bc = GetBlockchain() accounts = DBCollection(bc._db, DBPrefix.ST_Account, AccountState) assets = DBCollection(bc._db, DBPrefix.ST_Asset, AssetState) validators = DBCollection(bc._db, DBPrefix.ST_Validator, ValidatorState) contracts = DBCollection(bc._db, DBPrefix.ST_Contract, ContractState) storages = DBCollection(bc._db, DBPrefix.ST_Storage, StorageItem) if settings.USE_DEBUG_STORAGE: debug_storage = DebugStorage.instance() storages = DBCollection(debug_storage.db, DBPrefix.ST_Storage, StorageItem) storages.DebugStorage = True dtx = InvocationTransaction() dtx.Version = 1 dtx.outputs = [] dtx.inputs = [] dtx.scripts = [] dtx.Script = binascii.unhexlify(deploy_script) if from_addr is not None: from_addr = PromptUtils.lookup_addr_str(wallet, from_addr) try: dtx = wallet.MakeTransaction(tx=dtx, from_addr=from_addr) except (ValueError, TXFeeError): pass context = ContractParametersContext(dtx) wallet.Sign(context) dtx.scripts = context.GetScripts() script_table = CachedScriptTable(contracts) service = StateMachine(accounts, validators, assets, contracts, storages, None) contract = wallet.GetDefaultContract() dtx.Attributes = [ TransactionAttribute(usage=TransactionAttributeUsage.Script, data=Crypto.ToScriptHash(contract.Script, unhex=False)) ] dtx.Attributes = make_unique_script_attr(dtx.Attributes) to_dispatch = [] engine = ApplicationEngine(trigger_type=TriggerType.Application, container=dtx, table=script_table, service=service, gas=dtx.Gas, testMode=True) engine.LoadScript(dtx.Script) # first we will execute the test deploy # then right after, we execute the test invoke d_success = engine.Execute() if d_success: items = engine.ResultStack.Items contract_state = None for i in items: if type(i) is ContractState: contract_state = i break elif type(i) is InteropInterface: item = i.GetInterface() if type(item) is ContractState: contract_state = item break shash = contract_state.Code.ScriptHash() invoke_args, neo_to_attach, gas_to_attach = PromptUtils.get_asset_attachments( invoke_args) invoke_args, no_parse_addresses = PromptUtils.get_parse_addresses( invoke_args) invoke_args.reverse() if '--i' in invoke_args: invoke_args = [] for index, iarg in enumerate(contract_state.Code.ParameterList): param, abort = PromptUtils.gather_param(index, iarg) if abort: return None, [], 0, None else: invoke_args.append(param) invoke_args.reverse() sb = ScriptBuilder() for p in invoke_args: item = PromptUtils.parse_param(p, wallet, parse_addr=no_parse_addresses) if type(item) is list: item.reverse() listlength = len(item) for listitem in item: subitem = PromptUtils.parse_param( listitem, wallet, parse_addr=no_parse_addresses) if type(subitem) is list: subitem.reverse() for listitem2 in subitem: subsub = PromptUtils.parse_param( listitem2, wallet, parse_addr=no_parse_addresses) sb.push(subsub) sb.push(len(subitem)) sb.Emit(PACK) else: sb.push(subitem) sb.push(listlength) sb.Emit(PACK) else: sb.push(item) sb.EmitAppCall(shash.Data) out = sb.ToArray() outputs = [] if neo_to_attach: output = TransactionOutput( AssetId=Blockchain.SystemShare().Hash, Value=neo_to_attach, script_hash=contract_state.Code.ScriptHash(), ) outputs.append(output) if gas_to_attach: output = TransactionOutput( AssetId=Blockchain.SystemCoin().Hash, Value=gas_to_attach, script_hash=contract_state.Code.ScriptHash()) outputs.append(output) itx = InvocationTransaction() itx.Version = 1 itx.outputs = outputs itx.inputs = [] itx.scripts = [] itx.Attributes = deepcopy(invoke_attrs) if invoke_attrs else [] itx.Script = binascii.unhexlify(out) if len(outputs) < 1 and not owners: contract = wallet.GetDefaultContract() itx.Attributes.append( TransactionAttribute(usage=TransactionAttributeUsage.Script, data=contract.ScriptHash)) itx.Attributes = make_unique_script_attr(itx.Attributes) try: itx = wallet.MakeTransaction(tx=itx, from_addr=from_addr) except (ValueError, TXFeeError): pass context = ContractParametersContext(itx) wallet.Sign(context) if owners: owners = list(owners) for owner in owners: itx.Attributes.append( TransactionAttribute( usage=TransactionAttributeUsage.Script, data=owner)) itx.Attributes = make_unique_script_attr(itx.Attributes) context = ContractParametersContext(itx, isMultiSig=True) if context.Completed: itx.scripts = context.GetScripts() else: logger.warn( "Not gathering signatures for test build. For a non-test invoke that would occur here." ) # if not gather_signatures(context, itx, owners): # return None, [], 0, None # print("gathered signatures %s " % itx.scripts) engine = ApplicationEngine(trigger_type=TriggerType.Application, container=itx, table=script_table, service=service, gas=itx.Gas, testMode=invocation_test_mode) engine.invocation_args = invoke_args engine.LoadScript(itx.Script) engine.LoadDebugInfoForScriptHash(debug_map, shash.Data) i_success = engine.Execute() service.ExecutionCompleted(engine, i_success) to_dispatch = to_dispatch + service.events_to_dispatch for event in to_dispatch: events.emit(event.event_type, event) if i_success: service.TestCommit() if len(service.notifications) > 0: for n in service.notifications: Blockchain.Default().OnNotify(n) logger.info("Used %s Gas " % engine.GasConsumed().ToString()) consumed = engine.GasConsumed() - Fixed8.FromDecimal(10) consumed = consumed.Ceil() if consumed <= Fixed8.Zero(): consumed = min_fee total_ops = engine.ops_processed # set the amount of gas the tx will need itx.Gas = consumed itx.Attributes = [] result = engine.ResultStack.Items return itx, result, total_ops, engine else: print("error executing invoke contract...") else: print("error executing deploy contract.....") service.ExecutionCompleted(engine, False, 'error') return None, [], 0, None
def test_invoke(script, wallet, outputs, withdrawal_tx=None, from_addr=None, min_fee=DEFAULT_MIN_FEE, invoke_attrs=None, owners=None): # print("invoke script %s " % script) if from_addr is not None: from_addr = PromptUtils.lookup_addr_str(wallet, from_addr) bc = GetBlockchain() accounts = DBCollection(bc._db, DBPrefix.ST_Account, AccountState) assets = DBCollection(bc._db, DBPrefix.ST_Asset, AssetState) validators = DBCollection(bc._db, DBPrefix.ST_Validator, ValidatorState) contracts = DBCollection(bc._db, DBPrefix.ST_Contract, ContractState) storages = DBCollection(bc._db, DBPrefix.ST_Storage, StorageItem) # if we are using a withdrawal tx, don't recreate the invocation tx # also, we don't want to reset the inputs / outputs # since those were already calculated if withdrawal_tx is not None: tx = withdrawal_tx else: tx = InvocationTransaction() tx.outputs = outputs tx.inputs = [] tx.Version = 1 tx.scripts = [] tx.Script = binascii.unhexlify(script) tx.Attributes = [] if invoke_attrs is None else deepcopy(invoke_attrs) script_table = CachedScriptTable(contracts) service = StateMachine(accounts, validators, assets, contracts, storages, None) if len(outputs) < 1: contract = wallet.GetDefaultContract() tx.Attributes.append( TransactionAttribute(usage=TransactionAttributeUsage.Script, data=contract.ScriptHash)) tx.Attributes = make_unique_script_attr(tx.Attributes) # same as above. we don't want to re-make the transaction if it is a withdrawal tx if withdrawal_tx is not None: wallet_tx = tx else: try: wallet_tx = wallet.MakeTransaction(tx=tx, from_addr=from_addr, skip_fee_calc=True) except ValueError: pass context = ContractParametersContext(wallet_tx) wallet.Sign(context) if owners: owners = list(owners) for owner in owners: # print("contract %s %s" % (wallet.GetDefaultContract().ScriptHash, owner)) if wallet.GetDefaultContract().ScriptHash != owner: wallet_tx.Attributes.append( TransactionAttribute( usage=TransactionAttributeUsage.Script, data=owner)) wallet_tx.Attributes = make_unique_script_attr(tx.Attributes) context = ContractParametersContext(wallet_tx, isMultiSig=True) if context.Completed: wallet_tx.scripts = context.GetScripts() else: logger.warning( "Not gathering signatures for test build. For a non-test invoke that would occur here." ) # if not gather_signatures(context, wallet_tx, owners): # return None, [], 0, None engine = ApplicationEngine(trigger_type=TriggerType.Application, container=wallet_tx, table=script_table, service=service, gas=wallet_tx.Gas, testMode=True) engine.LoadScript(wallet_tx.Script) try: success = engine.Execute() service.ExecutionCompleted(engine, success) for event in service.events_to_dispatch: events.emit(event.event_type, event) if success: # this will be removed in favor of neo.EventHub if len(service.notifications) > 0: for n in service.notifications: Blockchain.Default().OnNotify(n) # print("Used %s Gas " % engine.GasConsumed().ToString()) consumed = engine.GasConsumed() - Fixed8.FromDecimal(10) consumed = consumed.Ceil() net_fee = None tx_gas = None if consumed <= Fixed8.Zero(): net_fee = min_fee tx_gas = Fixed8.Zero() else: tx_gas = consumed net_fee = Fixed8.Zero() # set the amount of gas the tx will need wallet_tx.Gas = tx_gas # reset the wallet outputs wallet_tx.outputs = outputs wallet_tx.Attributes = [] if invoke_attrs is None else deepcopy( invoke_attrs) # calculate the required network fee for the tx and compare to fee if wallet_tx.Size() > settings.MAX_FREE_TX_SIZE: req_fee = Fixed8.FromDecimal( settings.FEE_PER_EXTRA_BYTE * (wallet_tx.Size() - settings.MAX_FREE_TX_SIZE)) if req_fee < settings.LOW_PRIORITY_THRESHOLD: req_fee = settings.LOW_PRIORITY_THRESHOLD if net_fee < req_fee: net_fee = req_fee return wallet_tx, net_fee, engine.ResultStack.Items, engine.ops_processed, success # this allows you to to test invocations that fail else: wallet_tx.outputs = outputs wallet_tx.Attributes = [] if invoke_attrs is None else deepcopy( invoke_attrs) return wallet_tx, min_fee, [], engine.ops_processed, success except Exception as e: service.ExecutionCompleted(engine, False, e) return None, None, None, None, False
def construct_send_many(wallet, arguments): if len(arguments) is 0: print("Please specify the required parameter") return outgoing = get_arg(arguments, convert_to_int=True) if outgoing is None: print("Invalid outgoing number") return if outgoing < 1: print("Outgoing number must be >= 1") return arguments, from_address = get_from_addr(arguments) arguments, change_address = get_change_addr(arguments) arguments, priority_fee = get_fee(arguments) arguments, owners = get_owners_from_params(arguments) arguments, user_tx_attributes = get_tx_attr_from_args(arguments) output = [] for i in range(outgoing): try: print('Outgoing Number ', i + 1) to_send = prompt("Asset to send: ") assetId = get_asset_id(wallet, to_send) if assetId is None: print("Asset id not found") return if type(assetId) is NEP5Token: print('sendmany does not support NEP5 tokens') return address_to = prompt("Address to: ") scripthash_to = lookup_addr_str(wallet, address_to) if scripthash_to is None: logger.debug("invalid destination address") return amount = prompt("Amount to send: ") f8amount = get_asset_amount(amount, assetId) if f8amount is False: logger.debug("invalid amount") return if float(amount) == 0: print("Amount cannot be 0") return tx_output = TransactionOutput(AssetId=assetId, Value=f8amount, script_hash=scripthash_to) output.append(tx_output) except KeyboardInterrupt: print('Transaction cancelled') return contract_tx = ContractTransaction(outputs=output) scripthash_from = None if from_address is not None: scripthash_from = lookup_addr_str(wallet, from_address) if scripthash_from is None: logger.debug("invalid source address") return scripthash_change = None if change_address is not None: scripthash_change = lookup_addr_str(wallet, change_address) if scripthash_change is None: logger.debug("invalid change address") return fee = Fixed8.Zero() if priority_fee is not None: fee = priority_fee if fee is False: logger.debug("invalid fee") return print(f"Sending with fee: {fee.ToString()}") return [ contract_tx, scripthash_from, scripthash_change, fee, owners, user_tx_attributes ]
def construct_send_basic(wallet, arguments): if len(arguments) < 3: print("Please specify the required parameters") return arguments, from_address = get_from_addr(arguments) arguments, priority_fee = get_fee(arguments) arguments, user_tx_attributes = get_tx_attr_from_args(arguments) arguments, owners = get_owners_from_params(arguments) to_send = get_arg(arguments) address_to = get_arg(arguments, 1) amount = get_arg(arguments, 2) assetId = get_asset_id(wallet, to_send) if assetId is None: print("Asset id not found") return scripthash_to = lookup_addr_str(wallet, address_to) if scripthash_to is None: logger.debug("invalid destination address") return scripthash_from = None if from_address is not None: scripthash_from = lookup_addr_str(wallet, from_address) if scripthash_from is None: logger.debug("invalid source address") return # if this is a token, we will use a different # transfer mechanism if type(assetId) is NEP5Token: return do_token_transfer(assetId, wallet, from_address, address_to, amount_from_string(assetId, amount), tx_attributes=user_tx_attributes) f8amount = get_asset_amount(amount, assetId) if f8amount is False: logger.debug("invalid amount") return if float(amount) == 0: print("Amount cannot be 0") return fee = Fixed8.Zero() if priority_fee is not None: fee = priority_fee if fee is False: logger.debug("invalid fee") return print(f"Sending with fee: {fee.ToString()}") output = TransactionOutput(AssetId=assetId, Value=f8amount, script_hash=scripthash_to) contract_tx = ContractTransaction(outputs=[output]) return [contract_tx, scripthash_from, fee, owners, user_tx_attributes]