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 test_compute_root_multiple_hashes(self): expected_hash = Helper.bin_dbl_sha256( binascii.unhexlify(b'aa' * 32 + b'bb' * 32)) hash1 = UInt256(data=binascii.unhexlify(b'aa' * 32)) hash2 = UInt256(data=binascii.unhexlify(b'bb' * 32)) hashes = [hash1, hash2] root = MerkleTree.ComputeRoot(hashes) self.assertEqual(expected_hash, root.ToArray())
def test_trim_node3(self): hash1 = UInt256(data=binascii.unhexlify(b'aa' * 32)) hash2 = UInt256(data=binascii.unhexlify(b'bb' * 32)) hashes = [hash1, hash2] m = MerkleTree(hashes) depth = 1 flags = bytearray.fromhex('0000') m._TrimNode(m.Root, 1, depth, flags) self.assertNotEqual(None, m.Root.LeftChild) self.assertNotEqual(None, m.Root.RightChild)
def test_trim_tree(self): hash1 = UInt256(data=binascii.unhexlify(b'aa' * 32)) hash2 = UInt256(data=binascii.unhexlify(b'bb' * 32)) hash3 = UInt256(data=binascii.unhexlify(b'cc' * 32)) hashes = [hash1, hash2, hash3] m = MerkleTree(hashes) flags = bytearray.fromhex('0000') m.Trim(flags) self.assertEqual(None, m.Root.LeftChild) self.assertEqual(None, m.Root.RightChild)
def test_node_methods(self): hash1 = UInt256(data=binascii.unhexlify(b'aa' * 32)) hash2 = UInt256(data=binascii.unhexlify(b'bb' * 32)) hashes = [hash1, hash2] m = MerkleTree(hashes) self.assertEqual(True, m.Root.IsRoot()) self.assertEqual(False, m.Root.IsLeaf()) self.assertEqual(False, m.Root.LeftChild.IsRoot()) self.assertEqual(True, m.Root.LeftChild.IsLeaf()) # I have no acceptable test vector m.Root.LeftChild.Size()
def LoadCoins(self): coins = {} try: for coin in Coin.select(): reference = CoinReference(prev_hash=UInt256(coin.TxId), prev_index=coin.Index) output = TransactionOutput(UInt256(coin.AssetId), Fixed8(coin.Value), UInt160(coin.ScriptHash)) walletcoin = WalletCoin.CoinFromRef(reference, output, coin.State) coins[reference] = walletcoin except Exception as e: logger.error("could not load coins %s " % e) return coins
def test_trim_node1(self): hash1 = UInt256(data=binascii.unhexlify(b'aa' * 32)) hash2 = UInt256(data=binascii.unhexlify(b'bb' * 32)) hash3 = UInt256(data=binascii.unhexlify(b'cc' * 32)) hashes = [hash1, hash2, hash3] m = MerkleTree(hashes) depth = 2 flags = bytearray.fromhex( '11110000' ) # 1 byte left node , 1 byte right node 00=delete, non-00 is keep m._TrimNode(m.Root, 1, depth, flags) self.assertEqual(None, m.Root.LeftChild) self.assertEqual(None, m.Root.RightChild)
def test_parse(self): string = '0xcedb5c4e24b1f6fc5b239f2d1049c3229ad5ed05293c696b3740dc236c3f41b4' uint256 = UInt256.ParseString(string) self.assertIsInstance(uint256, UInt256) self.assertEqual(uint256.To0xString(), string) string = '9410bd44beb7d6febc9278b028158af2781fcfb40cf2c6067b3525d24eff19f6' uint256 = UInt256.ParseString(string) self.assertIsInstance(uint256, UInt256) self.assertEqual(uint256.ToString(), string) string = '9410bd44beb7d6febc9278b028158af2781fcfb40cf2c6067b3525d24eff19f' with self.assertRaises(ValueError) as context: uint256 = UInt256.ParseString(string) self.assertIn(f"Invalid UInt256 input: {len(string)} chars != 64 chars", context)
def Blockchain_GetHeader(self, engine: ExecutionEngine): data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray() header = None if len(data) <= 5: height = BigInteger.FromBytes(data) if Blockchain.Default() is not None: header = Blockchain.Default().GetHeaderBy(height_or_hash=height) elif height == 0: header = Blockchain.GenesisBlock().Header elif len(data) == 32: hash = UInt256(data=data) if Blockchain.Default() is not None: header = Blockchain.Default().GetHeaderBy(height_or_hash=hash) elif hash == Blockchain.GenesisBlock().Hash: header = Blockchain.GenesisBlock().Header engine.CurrentContext.EvaluationStack.PushT(StackItem.FromInterface(header)) return True
def Blockchain_GetBlock(self, engine: ExecutionEngine): data = engine.CurrentContext.EvaluationStack.Pop() if data: data = data.GetByteArray() else: return False block = None if len(data) <= 5: height = BigInteger.FromBytes(data) if Blockchain.Default() is not None: block = Blockchain.Default().GetBlockByHeight(height) elif height == 0: block = Blockchain.GenesisBlock() elif len(data) == 32: hash = UInt256(data=data).ToBytes() if Blockchain.Default() is not None: block = Blockchain.Default().GetBlockByHash(hash=hash) elif hash == Blockchain.GenesisBlock().Hash: block = Blockchain.GenesisBlock().Header engine.CurrentContext.EvaluationStack.PushT(StackItem.FromInterface(block)) return True
def GenesisBlock() -> Block: """ Create the GenesisBlock. Returns: BLock: """ prev_hash = UInt256(data=bytearray(32)) timestamp = int( datetime(2016, 7, 15, 15, 8, 21, tzinfo=pytz.utc).timestamp()) index = 0 consensus_data = 2083236893 # Pay tribute To Bitcoin next_consensus = Blockchain.GetConsensusAddress( Blockchain.StandbyValidators()) script = Witness(bytearray(0), bytearray(PUSHT)) mt = MinerTransaction() mt.Nonce = 2083236893 output = TransactionOutput( Blockchain.SystemShare().Hash, Blockchain.SystemShare().Amount, Crypto.ToScriptHash( Contract.CreateMultiSigRedeemScript( int(len(Blockchain.StandbyValidators()) / 2) + 1, Blockchain.StandbyValidators()))) it = IssueTransaction([], [output], [], [script]) return Block( prev_hash, timestamp, index, consensus_data, next_consensus, script, [mt, Blockchain.SystemShare(), Blockchain.SystemCoin(), it], True)
def __Build(leaves): """ Build the merkle tree. Args: leaves (list): items are of type MerkleTreeNode. Returns: MerkleTreeNode: the root node. Raises: ValueError: if the length of `leaves` is < 1 """ if len(leaves) < 1: raise ValueError('Leaves must have length') if len(leaves) == 1: return leaves[0] num_parents = int((len(leaves) + 1) / 2) parents = [MerkleTreeNode() for i in range(0, num_parents)] for i in range(0, num_parents): node = parents[i] node.LeftChild = leaves[i * 2] leaves[i * 2].Parent = node if (i * 2 + 1 == len(leaves)): node.RightChild = node.LeftChild else: node.RightChild = leaves[i * 2 + 1] leaves[i * 2 + 1].Parent = node hasharray = bytearray(node.LeftChild.Hash.ToArray() + node.RightChild.Hash.ToArray()) node.Hash = UInt256(data=Crypto.Hash256(hasharray)) return MerkleTree.__Build(parents)
def test_to_hash_array(self): hash1 = UInt256(data=binascii.unhexlify(b'aa' * 32)) hash2 = UInt256(data=binascii.unhexlify(b'bb' * 32)) hash3 = UInt256(data=binascii.unhexlify(b'cc' * 32)) hash4 = UInt256(data=binascii.unhexlify(b'dd' * 32)) hash5 = UInt256(data=binascii.unhexlify(b'ee' * 32)) hashes = [hash1, hash2, hash3, hash4, hash5] m = MerkleTree(hashes) hash_array = m.ToHashArray() # sort the array hash_array = sorted(hash_array) for i, h in enumerate(hashes): self.assertEqual(h.ToBytes(), hash_array[i].ToBytes())
def Blockchain_GetTransactionHeight(self, engine: ExecutionEngine): data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray() height = -1 if Blockchain.Default() is not None: tx, height = Blockchain.Default().GetTransaction(UInt256(data=data)) engine.CurrentContext.EvaluationStack.PushT(height) return True
def Account_GetBalance(self, engine: ExecutionEngine): account = engine.CurrentContext.EvaluationStack.Pop().GetInterface() assetId = UInt256(data=engine.CurrentContext.EvaluationStack.Pop().GetByteArray()) if account is None: return False balance = account.BalanceFor(assetId) engine.CurrentContext.EvaluationStack.PushT(balance.GetData()) return True
def Blockchain_GetTransaction(self, engine: ExecutionEngine): data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray() tx = None if Blockchain.Default() is not None: tx, height = Blockchain.Default().GetTransaction(UInt256(data=data)) engine.CurrentContext.EvaluationStack.PushT(StackItem.FromInterface(tx)) return True
def Size(self): """ Get the total size in bytes of the object. Returns: int: size. """ corrected_hashes = list( map(lambda i: UInt256(data=binascii.unhexlify(i)), self.HashStart)) return GetVarSize(corrected_hashes) + self.hash_stop.Size
def __init__(self, hash_start=[], hash_stop=UInt256()): """ Create an instance. Args: hash_start (list): a list of hash values. Each value is of the bytearray type. Note: should actually be UInt256 objects. hash_stop (UInt256): """ self.HashStart = hash_start self.HashStop = hash_stop
def Blockchain_GetAsset(self, engine: ExecutionEngine): data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray() asset = None if Blockchain.Default() is not None: asset = self.Assets.TryGet(UInt256(data=data)) if asset is None: return False engine.CurrentContext.EvaluationStack.PushT(StackItem.FromInterface(asset)) return True
def AddOutput(self, asset, to_addr, amount): """ Specify an output for the transaction. NOTE: Can be used multiple times to create multiple outputs. Args: asset: (str) the asset name or asset hash to_addr: (str) the destination NEO address (e.g. 'AJQ6FoaSXDFzA6wLnyZ1nFN7SGSN2oNTc3') amount: (int/decimal) the amount of the asset to send """ if asset[0:1] == "0x": asset == asset[2:] if asset.lower() == "neo": assetId = self.neo_asset_id elif asset == self.neo_asset_id: assetId = self.neo_asset_id elif asset.lower() == "gas": assetId = self.gas_asset_id elif asset == self.gas_asset_id: assetId = self.gas_asset_id else: raise AssetError( f'Asset {asset} not found. If trying to send tokens use the `buildTokenTransfer` function.' ) dest_scripthash = Helper.AddrStrToScriptHash( to_addr) # also verifies if the address is valid if float(amount) == 0: raise ValueError('Amount cannot be 0.') f8amount = Fixed8.TryParse(amount, require_positive=True) if f8amount is None: raise ValueError('Invalid amount format.') elif assetId == self.neo_asset_id and (f8amount.value / Fixed8.D) != f8amount.ToInt(): raise ValueError('Incorrect amount precision.') # check if the outputs exceed the available unspents subtotal = [] if self.outputs: for output in self.outputs: if output.AssetId == assetId: subtotal.append(output.Value.value) total = f8amount.value + sum(subtotal) total = float(Fixed8(total).ToString()) for asset in self.BALANCE: if assetId == asset['asset_hash']: if total > asset['amount']: raise AssetError( 'Total outputs exceed the available unspents.') self.outputs.append( TransactionOutput(AssetId=UInt256.ParseString(assetId), Value=f8amount, script_hash=dest_scripthash))
def Size(self): """ Get the total size in bytes of the object. Returns: int: size. """ if len(self.Hashes) > 0: if not isinstance(self.Hashes[0], UInt256): corrected_hashes = list(map(lambda i: UInt256(data=binascii.unhexlify(i)), self.Hashes)) return s.uint8 + GetVarSize(corrected_hashes)
def StaticAssetState(assetId): neo = AssetState() neo.AssetId = UInt256.ParseString( "0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b" ) neo.AssetType = 0x00 gas = AssetState() gas.AssetId = UInt256.ParseString( "0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7" ) gas.AssetType = 0x01 if assetId == neo.AssetId: return neo elif assetId == gas.AssetId: return gas else: return None
def test_interop_get_bad_transaction_height(self): u256 = UInt256.ParseString('8be9660512991d36e016b8ced6fda5d611d26a0f6e2faaaf1f379496edb33956') hash = StackItem.New(u256.Data) self.econtext.EvaluationStack.PushT(hash) self.engine.InvocationStack.PushT(self.econtext) self.state_reader.Blockchain_GetTransactionHeight(self.engine) height = self.econtext.EvaluationStack.Pop().GetBigInteger() self.assertEqual(height, -1)
def test_interop_get_transaction(self): u256 = UInt256.ParseString('8be9660512991d36e016b8ced6fda5d611d26a0f6e2faaaf1f379496edb3395f') hash = StackItem.New(u256.Data) self.econtext.EvaluationStack.PushT(hash) self.engine.InvocationStack.PushT(self.econtext) self.state_reader.Blockchain_GetTransaction(self.engine) tx = self.econtext.EvaluationStack.Pop().GetInterface() self.assertIsInstance(tx, Transaction)
def Hash(self): """ Get the hash of the transaction. Returns: UInt256: """ if not self.__hash: ba = bytearray(binascii.unhexlify(self.GetHashData())) hash = Crypto.Hash256(ba) self.__hash = UInt256(data=hash) return self.__hash
def AddClaim(self, claim_addr, to_addr=None): """ Builds a claim transaction for the specified address. Args: claim_addr: (str) the address from which the claim is being constructed (e.g. 'AJQ6FoaSXDFzA6wLnyZ1nFN7SGSN2oNTc3'). NOTE: Claimed GAS is sent to the claim_addr by default to_addr: (str, optional) specify a different destination NEO address (e.g. 'AJQ6FoaSXDFzA6wLnyZ1nFN7SGSN2oNTc3') """ dest_scripthash = Helper.AddrStrToScriptHash( claim_addr) # also verifies if the address is valid self.SOURCE_SCRIPTHASH = dest_scripthash url = self._network + self._get_claimable + claim_addr res = requests.get(url=url) if not res.status_code == 200: raise NetworkError( 'Neoscan request failed. Please check your internet connection.' ) res = res.json() available = res["unclaimed"] if available == 0: raise AssetError( f"Address {claim_addr} has 0 unclaimed GAS. Please ensure the correct network is selected or specify a difference source address." ) for ref in res['claimable']: self.Claims.append( CoinReference(prev_hash=UInt256.ParseString(ref['txid']), prev_index=ref['n'])) if to_addr: dest_scripthash = Helper.AddrStrToScriptHash( to_addr) # also verifies if the address is valid self.outputs.append( TransactionOutput(AssetId=UInt256.ParseString(self.gas_asset_id), Value=Fixed8.FromDecimal(available), script_hash=dest_scripthash))
def References(self): """ Get all references. Returns: dict: Key (UInt256): input PrevHash Value (TransactionOutput): object. """ if self.__references is None: refs = {} # group by the input prevhash for hash, group in groupby(self.inputs, lambda x: x.PrevHash): url = self._network + self._get_transaction + hash.ToString() tx = requests.get(url=url) if not tx.status_code == 200: raise NetworkError( 'Neoscan request failed. Please check your internet connection.' ) tx = tx.json() if tx is not None: for input in group: t = tx['vouts'][input.PrevIndex] if t['asset'].lower() == 'neo': asset = UInt256.ParseString(self.neo_asset_id) elif t['asset'].lower() == 'gas': asset = UInt256.ParseString(self.gas_asset_id) refs[input] = TransactionOutput( AssetId=asset, Value=Fixed8.FromDecimal(t['value']), script_hash=Helper.AddrStrToScriptHash( t['address_hash'])) self.__references = refs return self.__references
def test_compareto_valid(self): u1 = UInt160(b'12345678901234567890') # Same value should return 0 u2 = UIntBase(20, b'12345678901234567890') self.assertEqual(u1.CompareTo(u2), 0) # Higher digit in 'other' should return -1 u2 = UIntBase(20, b'12345678901234567891') self.assertEqual(u1.CompareTo(u2), -1) # Lower digit in 'other' should return 1 u2 = UIntBase(20, b'12345678901234567980') self.assertEqual(u1.CompareTo(u2), 1) # CompareTo across different UIntBase subclasses data = b'12345678901234567890' self.assertEqual(UInt160(data).CompareTo(UIntBase(len(data), data)), 0) self.assertEqual(UIntBase(len(data), data).CompareTo(UInt160(data)), 0) data = b'12345678901234567890123456789012' self.assertEqual(UInt256(data).CompareTo(UIntBase(len(data), data)), 0) self.assertEqual(UIntBase(len(data), data).CompareTo(UInt256(data)), 0)
def Hash(self): """ Get the hash value of the Blockbase. Returns: UInt256: containing the hash of the data. """ if not self.__hash: hashdata = self.RawData() ba = bytearray(binascii.unhexlify(hashdata)) hash = bin_dbl_sha256(ba) self.__hash = UInt256(data=hash) return self.__hash
def test_trim_node2(self): hash1 = UInt256(data=binascii.unhexlify(b'aa' * 32)) hash2 = UInt256(data=binascii.unhexlify(b'bb' * 32)) hash3 = UInt256(data=binascii.unhexlify(b'cc' * 32)) hash4 = UInt256(data=binascii.unhexlify(b'dd' * 32)) hash5 = UInt256(data=binascii.unhexlify(b'ee' * 32)) hash6 = UInt256(data=binascii.unhexlify(b'11' * 32)) hash7 = UInt256(data=binascii.unhexlify(b'22' * 32)) hash8 = UInt256(data=binascii.unhexlify(b'33' * 32)) hash9 = UInt256(data=binascii.unhexlify(b'44' * 32)) hashes = [ hash1, hash2, hash3, hash4, hash5, hash6, hash7, hash8, hash9 ] m = MerkleTree(hashes) depth = 3 flags = bytearray.fromhex('111100000000') m._TrimNode(m.Root, 1, depth, flags) self.assertEqual(None, m.Root.LeftChild) self.assertEqual(None, m.Root.RightChild)