def get_exec_fee_factor(self, snapshot: storage.Snapshot) -> int: storage_item = snapshot.storages.get(self.key_exec_fee_factor, read_only=True) return int(vm.BigInteger(storage_item.value))
def get_trigger(engine: contracts.ApplicationEngine) -> vm.BigInteger: return vm.BigInteger(engine.trigger.value)
def _initialize(self, engine: contracts.ApplicationEngine) -> None: engine.snapshot.storages.put(self.key_request_id, storage.StorageItem(vm.BigInteger.zero().to_array())) engine.snapshot.storages.put(self.key_price, storage.StorageItem(vm.BigInteger(50000000).to_array()))
def get_price(self, snapshot: storage.Snapshot) -> int: return int(vm.BigInteger(snapshot.storages.get(self.key_price, read_only=True).value))
def deserialize(self, reader: serialization.BinaryReader) -> None: self.value = vm.BigInteger(reader.read_var_bytes())
def _request(self, engine: contracts.ApplicationEngine, url: str, filter: str, callback: str, user_data: vm.StackItem, gas_for_response: int) -> None: if len(url.encode('utf-8')) > self._MAX_URL_LENGTH or \ len(filter.encode('utf-8')) > self._MAX_FILTER_LEN or \ len(callback.encode('utf-8')) > self._MAX_CALLBACK_LEN or \ callback.startswith("_") or \ gas_for_response < 10000000: raise ValueError engine.add_gas(self.get_price(engine.snapshot)) engine.add_gas(gas_for_response) self._gas.mint(engine, self.hash, vm.BigInteger(gas_for_response), False) si_item_id = engine.snapshot.storages.get(self.key_request_id, read_only=False) item_id = vm.BigInteger(si_item_id.value) si_item_id.value = (item_id + 1).to_array() if contracts.ManagementContract().get_contract(engine.snapshot, engine.calling_scripthash) is None: raise ValueError oracle_request = OracleRequest(self._get_original_txid(engine), gas_for_response, url, filter, engine.calling_scripthash, callback, contracts.BinarySerializer.serialize(user_data, self._MAX_USER_DATA_LEN)) engine.snapshot.storages.put(self.key_request + int(item_id).to_bytes(8, 'little', signed=False), storage.StorageItem(oracle_request.to_array()) ) sk_id_list = self.key_id_list + self._get_url_hash(url) si_id_list = engine.snapshot.storages.try_get(sk_id_list, read_only=False) if si_id_list is None: si_id_list = storage.StorageItem(b'\x00') with serialization.BinaryReader(si_id_list.value) as reader: count = reader.read_var_int() id_list = [] for _ in range(count): id_list.append(reader.read_uint64()) id_list.append(item_id) if len(id_list) >= 256: raise ValueError("Oracle has too many pending responses for this url") with serialization.BinaryWriter() as writer: writer.write_var_int(len(id_list)) for id in id_list: writer.write_uint64(id) si_id_list.value = writer.to_array() engine.snapshot.storages.update(sk_id_list, si_id_list) state = vm.ArrayStackItem( engine.reference_counter, [vm.IntegerStackItem(item_id), vm.ByteStringStackItem(engine.calling_scripthash.to_array()), vm.ByteStringStackItem(url.encode()), vm.ByteStringStackItem(filter.encode()), ] ) msgrouter.interop_notify(self.hash, "OracleRequest", state)
def __init__(self): self.value = vm.BigInteger(1)
def test_get_transaction_from_block(self): # this test for the first part is identical to the GetBlock test above engine = test_engine(has_container=True, has_snapshot=True) # test with serialized block hash (UInt256). This fake hash won't return a block engine.push(vm.IntegerStackItem(0)) # index engine.push(vm.ByteStringStackItem(b'\x01' * 32)) engine.invoke_syscall_by_name( "System.Blockchain.GetTransactionFromBlock") self.assertIsInstance(engine.pop(), vm.NullStackItem) # now find an existing block, but with an invalid transaction index ( # first add a block and update the snapshot # normally this would be done while persisting in Blockchain testblock = test_block() engine.snapshot.block_height = testblock.index engine.snapshot.blocks.put(testblock) engine.push(vm.IntegerStackItem(-1)) # index engine.push(vm.ByteStringStackItem( testblock.hash().to_array())) # hash with self.assertRaises(ValueError) as context: engine.invoke_syscall_by_name( "System.Blockchain.GetTransactionFromBlock") self.assertEqual("Transaction index out of range: -1", str(context.exception)) # now let's try again but this time with an invalid index (out of bounds) engine.push(vm.IntegerStackItem(len(testblock.transactions) + 1)) # index engine.push(vm.ByteStringStackItem( testblock.hash().to_array())) # hash with self.assertRaises(ValueError) as context: engine.invoke_syscall_by_name( "System.Blockchain.GetTransactionFromBlock") self.assertEqual("Transaction index out of range: 2", str(context.exception)) # Finally, we try with a valid index (we have only 1 transaction, so 0) engine.push(vm.IntegerStackItem(vm.BigInteger(0))) # index engine.push(vm.ByteStringStackItem( testblock.hash().to_array())) # hash engine.invoke_syscall_by_name( "System.Blockchain.GetTransactionFromBlock") # and test the TX items pushed to the stack item = engine.pop() testblock_tx = testblock.transactions[0] self.assertIsInstance(item, vm.ArrayStackItem) self.assertEqual(len(item), 8) self.assertEqual(item[0].to_array(), testblock_tx.hash().to_array()) self.assertEqual(item[1].to_biginteger(), vm.BigInteger(testblock_tx.version)) self.assertEqual(item[2].to_biginteger(), vm.BigInteger(testblock_tx.nonce)) self.assertEqual(item[3].to_array(), testblock_tx.sender.to_array()) self.assertEqual(item[4].to_biginteger(), vm.BigInteger(testblock_tx.system_fee)) self.assertEqual(item[5].to_biginteger(), vm.BigInteger(testblock_tx.network_fee)) self.assertEqual(item[6].to_biginteger(), vm.BigInteger(testblock_tx.valid_until_block)) self.assertEqual(item[7].to_array(), testblock_tx.script)
def deserialize(self, reader: BinaryReader) -> None: self._balance = vm.BigInteger(reader.read_var_bytes())
def test_negative_mint(self): gas = contracts.GasToken() with self.assertRaises(ValueError) as context: gas.mint(None, None, vm.BigInteger(-1), False) self.assertEqual("Can't mint a negative amount", str(context.exception))
def test_on_persist(self): """ OnPersist will do the following * burn the system and network fees for all transactions * mint the sum of network_fees for all transactions to the address of the consensus node that acted as primary speaker for the block """ engine = test_engine(has_snapshot=True) block = test_block(0) # set or we won't pass the native deploy call engine.snapshot.persisting_block = block gas = contracts.GasToken() # update the TX signer account to point to our validator or the token burn() (part of on persist) # will fail because it can't find an account with balance mock_signer = mock.MagicMock() mock_signer.account = self.validator_account engine.snapshot.persisting_block.transactions[0].signers = [ mock_signer ] # our consensus_data is not setup in a realistic way, so we have to correct for that here # or we fail to get the account of primary consensus node engine.snapshot.persisting_block.header.primary_index = settings.network.validators_count - 1 gas.on_persist(engine) """ Drop the below in a test in UT_NativeContract.cs and change ProtocolSettings.cs to * have a ValidatorsCount of 1 * and the StandbyCommittee should be: 02158c4a4810fa2a6a12f7d33d835680429e1a68ae61161c5b3fbc98c7f1f17765 var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; var point = ECPoint.Parse("02158c4a4810fa2a6a12f7d33d835680429e1a68ae61161c5b3fbc98c7f1f17765", ECCurve.Secp256r1); var account = Contract.CreateMultiSigRedeemScript(1, new ECPoint[] {point}).ToScriptHash(); var tx = TestUtils.GetTransaction(account); tx.SystemFee = 456; tx.NetworkFee = 789; snapshot.PersistingBlock.Transactions = new Transaction[] {tx}; snapshot.PersistingBlock.ConsensusData = new ConsensusData { PrimaryIndex = 0}; ApplicationEngine engine2 = ApplicationEngine.Create(TriggerType.System, tx, snapshot, 0); NativeContract.GAS.OnPersist(engine2); var key = new byte[] {0x14}; var sk = key.Concat(account.ToArray()); var item = engine2.Snapshot.Storages.TryGet(new StorageKey {Id = NativeContract.GAS.Id, Key = sk.ToArray()}); var state = item.GetInteroperable<AccountState>(); Console.WriteLine($"account state {state.Balance}"); var item2 = engine2.Snapshot.Storages.TryGet(new StorageKey {Id = NativeContract.GAS.Id, Key = new byte[]{11}}); Console.WriteLine($"total supply {(BigInteger)item2}"); var primary_account = Contract.CreateSignatureRedeemScript(point).ToScriptHash(); var primary_sk = key.Concat(primary_account.ToArray()); var primary_item = engine2.Snapshot.Storages.TryGet(new StorageKey {Id = NativeContract.GAS.Id, Key = primary_sk.ToArray()}); var primary_state = primary_item.GetInteroperable<AccountState>(); Console.WriteLine($"primary account state {primary_state.Balance}"); """ # * our validator prior to on_persist had a balance of 30_000_000 # * after it should have been reduced by the network + system_fee's paid in the transaction sk_gas_supply = gas.key_account + self.validator_account si_supply = engine.snapshot.storages.try_get(sk_gas_supply) self.assertIsNotNone(si_supply) token_state = gas._state.deserialize_from_bytes(si_supply.value) total_fees = engine.snapshot.persisting_block.transactions[0].network_fee + \ engine.snapshot.persisting_block.transactions[0].system_fee expected = (30_000_000 * gas.factor) - total_fees self.assertEqual(expected, int(token_state.balance)) # * total GAS supply was 30_000_000 + 0.5 for committee reward, should be reduced by the system_fee sk_total_supply = gas.key_total_supply si_total_supply = engine.snapshot.storages.try_get(sk_total_supply) self.assertIsNotNone(si_total_supply) committee_reward = vm.BigInteger(50000000) expected = ( (30_000_000 * gas.factor) + committee_reward ) - engine.snapshot.persisting_block.transactions[0].system_fee self.assertEqual(expected, vm.BigInteger(si_total_supply.value)) # * the persisting block contains exactly 1 transaction # * after on_persist the account our primary validator should have been credited with the transaction's # network_fee primary_validator = to_script_hash( contracts.Contract.create_signature_redeemscript( self.validator_public_key)) sk_gas_supply = gas.key_account + primary_validator si_supply = engine.snapshot.storages.try_get(sk_gas_supply) self.assertIsNotNone(si_supply) token_state = gas._state.deserialize_from_bytes(si_supply.value) expected = engine.snapshot.persisting_block.transactions[ 0].network_fee + committee_reward self.assertEqual(expected, int(token_state.balance))