public void SmartContract_CanSerialize_OP_CREATECONTRACT_WithoutMethodParameters() { byte[] contractExecutionCode = Encoding.UTF8.GetBytes( @" using System; using Stratis.SmartContracts; [References] public class Test : SmartContract { public void TestMethod() { [CodeToExecute] } }" ); var carrier = SmartContractCarrier.CreateContract(1, contractExecutionCode, 1, (Gas)5000); var serializedCarrier = carrier.Serialize(); var tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(0, 0), new Script(OpcodeType.OP_1))); tx.AddOutput(new TxOut(new Money(5000000000L - 10000), new Script(serializedCarrier))); SmartContractCarrier deserialized = SmartContractCarrier.Deserialize(tx); Assert.Equal(1, deserialized.CallData.VmVersion); Assert.Equal((byte)ScOpcodeType.OP_CREATECONTRACT, deserialized.CallData.OpCodeType); Assert.Equal(contractExecutionCode, deserialized.CallData.ContractExecutionCode); Assert.Equal((Gas)1, deserialized.CallData.GasPrice); Assert.Equal((Gas)5000, deserialized.CallData.GasLimit); Assert.True(tx.Outputs[0].ScriptPubKey.IsSmartContractExec()); }
public void SME_CreateContract_ConstructorFails_Refund() { SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/ContractConstructorInvalid.cs"); Assert.True(compilationResult.Success); byte[] contractCode = compilationResult.Compilation; SmartContractCarrier carrier = SmartContractCarrier.CreateContract(0, contractCode, 1, (Gas)10000); var tx = new Transaction(); tx.AddOutput(0, new Script(carrier.Serialize())); ISmartContractTransactionContext transactionContext = new SmartContractTransactionContext(BlockHeight, CoinbaseAddress, MempoolFee, new uint160(2), tx); var executor = new Executor(this.loggerFactory, this.serializer, this.state, this.refundProcessor, this.transferProcessor, this.vm); ISmartContractExecutionResult result = executor.Execute(transactionContext); Assert.NotNull(result.Exception); // Base cost + constructor cost Assert.Equal(GasPriceList.BaseCost + 13, result.GasConsumed); }
public void SME_CreateContract_MethodParameters_ParameterTypeMismatch() { SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/ContractMethodParameterTypeMismatch.cs"); Assert.True(compilationResult.Success); byte[] contractCode = compilationResult.Compilation; string[] methodParameters = new string[] { string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Bool, true), }; var carrier = SmartContractCarrier.CreateContract(0, contractCode, 1, (Gas)10000, methodParameters); var tx = new Transaction(); tx.AddOutput(0, new Script(carrier.Serialize())); ISmartContractTransactionContext transactionContext = new SmartContractTransactionContext(BlockHeight, CoinbaseAddress, MempoolFee, new uint160(2), tx); var executor = new Executor(this.loggerFactory, this.serializer, this.state, this.refundProcessor, this.transferProcessor, this.vm); ISmartContractExecutionResult result = executor.Execute(transactionContext); Assert.NotNull(result.Exception); Assert.Equal(GasPriceList.BaseCost, result.GasConsumed); }
public void SmartContract_CanSerialize_OP_CREATECONTRACT_WithoutMethodParameters() { byte[] contractExecutionCode = Encoding.UTF8.GetBytes( @" using System; using Stratis.SmartContracts; [References] public class Test : SmartContract { public void TestMethod() { [CodeToExecute] } }" ); var carrier = SmartContractCarrier.CreateContract(1, contractExecutionCode, 1, (Gas)5000); var serializer = CallDataSerializer.Default; var callDataResult = serializer.Deserialize(carrier.Serialize()); var callData = callDataResult.Value; Assert.True(callDataResult.IsSuccess); Assert.Equal(1, callData.VmVersion); Assert.Equal((byte)ScOpcodeType.OP_CREATECONTRACT, callData.OpCodeType); Assert.Equal(contractExecutionCode, callData.ContractExecutionCode); Assert.Equal((Gas)1, callData.GasPrice); Assert.Equal((Gas)5000, callData.GasLimit); }
public string GetAddressFromScriptPubKey(Network network, Script script) { if (script.IsSmartContractCreate() || script.IsSmartContractCall()) { var carrier = SmartContractCarrier.Deserialize(script); return(carrier.ContractAddress?.ToAddress(network)); } return(this.baseAddressReader.GetAddressFromScriptPubKey(network, script)); }
public void SmartContracts_AddToMempool_OnlyValid() { using (NodeBuilder builder = NodeBuilder.Create(this)) { var stratisNodeSync = builder.CreateSmartContractPowNode(); builder.StartAll(); stratisNodeSync.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisNodeSync.FullNode.Network)); stratisNodeSync.GenerateStratisWithMiner(105); // coinbase maturity = 100 TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock); TestHelper.WaitLoop(() => stratisNodeSync.FullNode.GetBlockStoreTip().HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock); var block = stratisNodeSync.FullNode.BlockStoreManager().BlockRepository.GetAsync(stratisNodeSync.FullNode.Chain.GetBlock(4).HashBlock).Result; var prevTrx = block.Transactions.First(); var dest = new BitcoinSecret(new Key(), stratisNodeSync.FullNode.Network); // Gas higher than allowed limit Transaction tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey))); SmartContractCarrier smartContractCarrier = SmartContractCarrier.CallContract(1, new uint160(0), "Test", 1, new Gas(10_000_000)); tx.AddOutput(new TxOut(1, new Script(smartContractCarrier.Serialize()))); tx.Sign(stratisNodeSync.FullNode.Network, stratisNodeSync.MinerSecret, false); stratisNodeSync.Broadcast(tx); // OP_SPEND in user's tx - we can't sign this because the TransactionBuilder recognises the ScriptPubKey is invalid. tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), new Script(new[] { (byte)ScOpcodeType.OP_SPEND }))); smartContractCarrier = SmartContractCarrier.CallContract(1, new uint160(0), "Test", 1, new Gas(100_000)); tx.AddOutput(new TxOut(1, new Script(smartContractCarrier.Serialize()))); stratisNodeSync.Broadcast(tx); // 2 smart contract outputs tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey))); smartContractCarrier = SmartContractCarrier.CallContract(1, new uint160(0), "Test", 1, new Gas(100_000)); tx.AddOutput(new TxOut(1, new Script(smartContractCarrier.Serialize()))); tx.AddOutput(new TxOut(1, new Script(smartContractCarrier.Serialize()))); tx.Sign(stratisNodeSync.FullNode.Network, stratisNodeSync.MinerSecret, false); stratisNodeSync.Broadcast(tx); // After 5 seconds (plenty of time but ideally we would have a more accurate measure) no txs in mempool. All failed validation. Thread.Sleep(5000); Assert.Empty(stratisNodeSync.CreateRPCClient().GetRawMempool()); // Valid tx still works tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey))); tx.AddOutput(new TxOut("25", dest.PubKey.Hash)); tx.AddOutput(new TxOut("24", new Key().PubKey.Hash)); // 1 btc fee tx.Sign(stratisNodeSync.FullNode.Network, stratisNodeSync.MinerSecret, false); stratisNodeSync.Broadcast(tx); TestHelper.WaitLoop(() => stratisNodeSync.CreateRPCClient().GetRawMempool().Length == 1); } }
public void ContractExecutionResult_NoRefundDue_NoFeeAdjustment() { var contractAddress = new uint160(1); var carrier = SmartContractCarrier.CallContract(1, contractAddress, "ThrowException", 1, (Gas)5000); carrier.Sender = new uint160(2); (Money fee, TxOut refund) = this.refundProcessor.Process(carrier.ContractTxData, new Money(10500), carrier.Sender, (Gas)5000, false); Assert.Equal(10500, fee); Assert.Null(refund); }
public void ContractExecutionResult_OutOfGasException_NoRefundDue_NoFeeAdjustment() { var contractAddress = new uint160(1); var carrier = SmartContractCarrier.CallContract(1, contractAddress, "ThrowException", 1, (Gas)5000); carrier.Sender = new uint160(2); (var fee, var refunds) = this.refundProcessor.Process(carrier.ContractTxData, new Money(10500), carrier.Sender, (Gas)5000, new OutOfGasException()); Assert.Equal(10500, fee); Assert.Empty(refunds); }
public void ContractExecutionResult_RefundDue_AdjustFee() { var contractAddress = new uint160(1); var carrier = SmartContractCarrier.CallContract(1, contractAddress, "ThrowException", 1, (Gas)5000); carrier.Sender = new uint160(2); (Money fee, TxOut refund) = this.refundProcessor.Process(carrier.ContractTxData, new Money(10500), carrier.Sender, (Gas)950, false); Assert.Equal(6450, fee); Assert.Equal(carrier.Sender.ToBytes(), refund.ScriptPubKey.GetDestination(this.network).ToBytes()); Assert.Equal(4050, refund.Value); }
public async Task SmartContractFormatRule_MultipleOutputs_SuccessAsync() { TestRulesContext testContext = TestRulesContextFactory.CreateAsync(this.network); SmartContractFormatRule rule = testContext.CreateRule <SmartContractFormatRule>(); var context = new PowRuleContext(new ValidationContext(), testContext.DateTimeProvider.GetTimeOffset()) { UnspentOutputSet = GetMockOutputSet() }; context.ValidationContext.BlockToValidate = testContext.Network.Consensus.ConsensusFactory.CreateBlock(); var gasPriceSatoshis = 20; var gasLimit = 4000000; var gasBudgetSatoshis = gasPriceSatoshis * gasLimit; var relayFeeSatoshis = 10000; var change = 200000; var totalSuppliedSatoshis = gasBudgetSatoshis + relayFeeSatoshis; var carrier = SmartContractCarrier.CallContract(1, 0, "TestMethod", (ulong)gasPriceSatoshis, (Gas)gasLimit); var serialized = carrier.Serialize(); Transaction funding = new Transaction { Outputs = { new TxOut(totalSuppliedSatoshis + change, new Script()) } }; var transactionBuilder = new TransactionBuilder(testContext.Network); transactionBuilder.AddCoins(funding); transactionBuilder.SendFees(totalSuppliedSatoshis); transactionBuilder.Send(new Script(serialized), 0); // Add a change output to the transaction transactionBuilder.SetChange(new Script()); Transaction transaction = transactionBuilder.BuildTransaction(false); context.ValidationContext.BlockToValidate.Transactions = new List <Transaction> { transaction }; await rule.RunAsync(context); }
public void ContractExecutionResult_RefundDue_AdjustFee() { var contractAddress = new uint160(1); var carrier = SmartContractCarrier.CallContract(1, contractAddress, "ThrowException", 1, (Gas)5000); carrier.Sender = new uint160(2); (var fee, var refunds) = this.refundProcessor.Process(carrier, new Money(10500), (Gas)950, null); Assert.Equal(6450, fee); Assert.Single(refunds); Assert.Equal(carrier.Sender.ToBytes(), refunds.First().ScriptPubKey.GetDestination(this.network).ToBytes()); Assert.Equal(4050, refunds.First().Value); }
public void SmartContract_CanSerialize_OP_CALLCONTRACT_WithoutMethodParameters() { var smartContractCarrier = SmartContractCarrier.CallContract(1, 100, "Execute", 1, (Gas)500000); var serializer = CallDataSerializer.Default; var callDataResult = serializer.Deserialize(smartContractCarrier.Serialize()); var callData = callDataResult.Value; Assert.True(callDataResult.IsSuccess); Assert.Equal(smartContractCarrier.ContractTxData.VmVersion, callData.VmVersion); Assert.Equal(smartContractCarrier.ContractTxData.OpCodeType, callData.OpCodeType); Assert.Equal(smartContractCarrier.ContractTxData.ContractAddress, callData.ContractAddress); Assert.Equal(smartContractCarrier.ContractTxData.MethodName, callData.MethodName); Assert.Equal(smartContractCarrier.ContractTxData.GasPrice, callData.GasPrice); Assert.Equal(smartContractCarrier.ContractTxData.GasLimit, callData.GasLimit); }
public void SmartContractExecutor_CallContract_Fails_ReturnFundsToSender() { //Get the contract execution code------------------------ SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/ThrowExceptionContract.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; //------------------------------------------------------- //Call smart contract and add to transaction------------- SmartContractCarrier carrier = SmartContractCarrier.CallContract(1, ToAddress, "ThrowException", 1, (Gas)5000); var transactionCall = new Transaction(); TxOut callTxOut = transactionCall.AddOutput(0, new Script(carrier.Serialize())); callTxOut.Value = 100; //------------------------------------------------------- //Deserialize the contract from the transaction---------- //and get the module definition //------------------------------------------------------- this.state.SetCode(new uint160(1), contractExecutionCode); this.state.SetContractType(new uint160(1), "ThrowExceptionContract"); ISmartContractTransactionContext transactionContext = new SmartContractTransactionContext(BlockHeight, CoinbaseAddress, MempoolFee, SenderAddress, transactionCall); var executor = new Executor(this.loggerFactory, this.serializer, this.state, this.refundProcessor, this.transferProcessor, this.vm); ISmartContractExecutionResult result = executor.Execute(transactionContext); Assert.True(result.Revert); Assert.NotNull(result.InternalTransaction); Assert.Single(result.InternalTransaction.Inputs); Assert.Single(result.InternalTransaction.Outputs); var actualSender = new uint160(result.InternalTransaction.Outputs[0].ScriptPubKey.GetDestination(this.network).ToBytes()); Assert.Equal(SenderAddress, actualSender); Assert.Equal(100, result.InternalTransaction.Outputs[0].Value); }
public void SmartContract_CanSerialize_OP_CALLCONTRACT_WithoutMethodParameters() { var smartContractCarrier = SmartContractCarrier.CallContract(1, 100, "Execute", 1, (Gas)500000); var tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(0, 0), new Script(OpcodeType.OP_1))); tx.AddOutput(new TxOut(new Money(5000000000L - 10000), new Script(smartContractCarrier.Serialize()))); SmartContractCarrier deserialized = SmartContractCarrier.Deserialize(tx); Assert.Equal(smartContractCarrier.CallData.VmVersion, deserialized.CallData.VmVersion); Assert.Equal(smartContractCarrier.CallData.OpCodeType, deserialized.CallData.OpCodeType); Assert.Equal(smartContractCarrier.CallData.ContractAddress, deserialized.CallData.ContractAddress); Assert.Equal(smartContractCarrier.CallData.MethodName, deserialized.CallData.MethodName); Assert.Equal(smartContractCarrier.CallData.GasPrice, deserialized.CallData.GasPrice); Assert.Equal(smartContractCarrier.CallData.GasLimit, deserialized.CallData.GasLimit); }
public void SmartContractFormatRule_FailureAsync() { TestRulesContext testContext = TestRulesContextFactory.CreateAsync(this.network); SmartContractFormatRule rule = testContext.CreateRule <SmartContractFormatRule>(); var context = new PowRuleContext(new ValidationContext(), testContext.DateTimeProvider.GetTimeOffset()); context.ValidationContext.BlockToValidate = testContext.Network.Consensus.ConsensusFactory.CreateBlock(); var gasPriceSatoshis = 20; var gasLimit = 4000000; var gasBudgetSatoshis = gasPriceSatoshis * gasLimit; var relayFeeSatoshis = 10000; var totalSuppliedSatoshis = gasBudgetSatoshis + relayFeeSatoshis; var higherGasLimit = gasLimit + 10000; var carrier = SmartContractCarrier.CallContract(1, 0, "TestMethod", (ulong)gasPriceSatoshis, (Gas)higherGasLimit); var serialized = carrier.Serialize(); Transaction funding = new Transaction { Outputs = { new TxOut(totalSuppliedSatoshis, new Script()) } }; var transactionBuilder = new TransactionBuilder(testContext.Network); transactionBuilder.AddCoins(funding); transactionBuilder.SendFees(relayFeeSatoshis); transactionBuilder.Send(new Script(serialized), gasBudgetSatoshis); Transaction transaction = transactionBuilder.BuildTransaction(false); context.ValidationContext.BlockToValidate.Transactions = new List <Transaction> { transaction }; Task <ConsensusErrorException> error = Assert.ThrowsAsync <ConsensusErrorException>(async() => await rule.RunAsync(context)); }
public void TransferProcessor_NoBalance_NoTransfers() { // Scenario where contract was sent 0, doesn't yet have any UTXO assigned, and no transfers were made. var carrierSkeleton = SmartContractCarrier.CallContract(1, new uint160(1), "Test", 1, (Gas)100_000); var transaction = new Transaction(); transaction.AddOutput(0, new Script(carrierSkeleton.Serialize())); var carrier = SmartContractCarrier.Deserialize(transaction); var stateMock = new Mock <IContractStateRepository>(); stateMock.Setup(x => x.GetCode(It.IsAny <uint160>())).Returns <byte[]>(null); var txContextMock = new Mock <ISmartContractTransactionContext>(); var result = new SmartContractExecutionResult(); this.transferProcessor.Process(carrier, stateMock.Object, txContextMock.Object, new List <TransferInfo>(), false); // Ensure no state changes were made and no transaction has been added Assert.Null(result.InternalTransaction); }
private void CheckTransaction(Transaction transaction, Money suppliedBudget) { if (!transaction.IsSmartContractExecTransaction()) { return; } // TODO: What if deserialization throws an error? We should check this. // Also the deserializer should throw custom exceptions. SmartContractCarrier carrier = SmartContractCarrier.Deserialize(transaction); if (carrier.CallData.GasPrice < GasPriceMinimum) { // Supplied gas price is too low. this.ThrowGasPriceLessThanMinimum(); } if (carrier.CallData.GasPrice > GasPriceMaximum) { // Supplied gas price is too high. this.ThrowGasPriceMoreThanMaximum(); } if (carrier.CallData.GasLimit < GasLimitMinimum) { // Supplied gas limit is too low. this.ThrowGasLessThanBaseFee(); } if (carrier.CallData.GasLimit > GasLimitMaximum) { // Supplied gas limit is too high - at a certain point we deem that a contract is taking up too much time. this.ThrowGasGreaterThanHardLimit(); } // Note carrier.GasCostBudget cannot overflow given values are within constraints above. if (suppliedBudget < new Money(carrier.GasCostBudget)) { // Supplied satoshis are less than the budget we said we had for the contract execution this.ThrowGasGreaterThanFee(); } }
public void TransferProcessor_NoBalance_ReceivedFunds() { // Scenario where contract was sent some funds, doesn't yet have any UTXO assigned, and no transfers were made. var carrierSkeleton = SmartContractCarrier.CallContract(1, new uint160(1), "Test", 1, (Gas)100_000); var transaction = new Transaction(); transaction.AddOutput(100, new Script(carrierSkeleton.Serialize())); var carrier = SmartContractCarrier.Deserialize(transaction); var stateMock = new Mock <IContractStateRepository>(); stateMock.Setup(x => x.GetCode(It.IsAny <uint160>())).Returns <byte[]>(null); var txContextMock = new Mock <ISmartContractTransactionContext>(); var result = new SmartContractExecutionResult(); this.transferProcessor.Process(carrier, stateMock.Object, txContextMock.Object, new List <TransferInfo>(), false); // Ensure unspent was saved, but no condensing transaction was generated. Assert.Null(result.InternalTransaction); stateMock.Verify(x => x.SetUnspent(new uint160(1), It.IsAny <ContractUnspentOutput>())); }
public void SmartContractExecutor_CallContract_DoesNotExist_Refund() { SmartContractCarrier carrier = SmartContractCarrier.CallContract(1, ToAddress, "TestMethod", 1, (Gas)10000); var transaction = new Transaction(); TxOut txOut = transaction.AddOutput(0, new Script(carrier.Serialize())); txOut.Value = 100; ISmartContractTransactionContext transactionContext = new SmartContractTransactionContext(BlockHeight, CoinbaseAddress, MempoolFee, new uint160(2), transaction); var executor = new Executor(this.loggerFactory, this.serializer, this.state, this.refundProcessor, this.transferProcessor, this.vm); ISmartContractExecutionResult result = executor.Execute(transactionContext); Assert.IsType <SmartContractDoesNotExistException>(result.Exception); }
public void VM_CreateContract_WithParameters() { //Get the contract execution code------------------------ SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/Auction.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; //------------------------------------------------------- // //Call smart contract and add to transaction------------- string[] methodParameters = new string[] { string.Format("{0}#{1}", (int)SmartContractCarrierDataType.ULong, 5), }; var carrier = SmartContractCarrier.CreateContract(1, contractExecutionCode, 1, (Gas)500000, methodParameters); var transactionCall = new Transaction(); transactionCall.AddInput(new TxIn()); TxOut callTxOut = transactionCall.AddOutput(0, new Script(carrier.Serialize())); //------------------------------------------------------- //Deserialize the contract from the transaction---------- var deserializedCall = SmartContractCarrier.Deserialize(transactionCall); //------------------------------------------------------- var repository = new ContractStateRepositoryRoot(new NoDeleteSource <byte[], byte[]>(new MemoryDictionarySource())); IContractStateRepository track = repository.StartTracking(); var gasMeter = new GasMeter(deserializedCall.CallData.GasLimit); var persistenceStrategy = new MeteredPersistenceStrategy(repository, gasMeter, new BasicKeyEncodingStrategy()); var persistentState = new PersistentState(persistenceStrategy, TestAddress.ToUint160(this.network), this.network); var internalTxExecutorFactory = new InternalTransactionExecutorFactory(this.keyEncodingStrategy, this.loggerFactory, this.network); var vm = new ReflectionVirtualMachine(internalTxExecutorFactory, this.loggerFactory); var context = new SmartContractExecutionContext( new Block(1, TestAddress), new Message( TestAddress, TestAddress, deserializedCall.Value, deserializedCall.CallData.GasLimit ), TestAddress.ToUint160(this.network), deserializedCall.CallData.GasPrice, deserializedCall.MethodParameters ); var result = vm.Create( contractExecutionCode, context, gasMeter, persistentState, repository); track.Commit(); Assert.Equal(6, BitConverter.ToInt16(track.GetStorageValue(context.Message.ContractAddress.ToUint160(this.network), Encoding.UTF8.GetBytes("EndBlock")), 0)); Assert.Equal(TestAddress.ToUint160(this.network).ToBytes(), track.GetStorageValue(context.Message.ContractAddress.ToUint160(this.network), Encoding.UTF8.GetBytes("Owner"))); }
public void Execute_InterContractCall_InfiniteLoop_AllGasConsumed() { // Create contract 1 //Get the contract execution code------------------------ SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/InfiniteLoop.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; //------------------------------------------------------- // Add contract creation code to transaction------------- var carrier = SmartContractCarrier.CreateContract(1, contractExecutionCode, 1, (Gas)3500); var transaction = new Transaction(); TxOut txOut = transaction.AddOutput(0, new Script(carrier.Serialize())); txOut.Value = 100; //------------------------------------------------------- //Deserialize the contract from the transaction---------- //and get the module definition ISmartContractTransactionContext transactionContext = new SmartContractTransactionContext(BlockHeight, CoinbaseAddress, MempoolFee, SenderAddress, transaction); var executor = new Executor(this.loggerFactory, this.serializer, this.state, this.refundProcessor, this.transferProcessor, this.vm); ISmartContractExecutionResult result = executor.Execute(transactionContext); uint160 address1 = result.NewContractAddress; //------------------------------------------------------- // Create contract 2 //Get the contract execution code------------------------ compilationResult = SmartContractCompiler.CompileFile("SmartContracts/CallInfiniteLoopContract.cs"); Assert.True(compilationResult.Success); contractExecutionCode = compilationResult.Compilation; //------------------------------------------------------- //Call smart contract and add to transaction------------- carrier = SmartContractCarrier.CreateContract(1, contractExecutionCode, 1, (Gas)3500); transaction = new Transaction(); txOut = transaction.AddOutput(0, new Script(carrier.Serialize())); txOut.Value = 100; //------------------------------------------------------- //Deserialize the contract from the transaction---------- //and get the module definition //------------------------------------------------------- transactionContext = new SmartContractTransactionContext(BlockHeight, CoinbaseAddress, MempoolFee, SenderAddress, transaction); result = executor.Execute(transactionContext); uint160 address2 = result.NewContractAddress; // Invoke infinite loop var gasLimit = (Gas)1000000; string[] parameters = { string.Format("{0}#{1}", (int)SmartContractCarrierDataType.String, address1.ToAddress(this.network).Value), }; carrier = SmartContractCarrier.CallContract(1, address2, "CallInfiniteLoop", 1, gasLimit, parameters); transaction = new Transaction(); txOut = transaction.AddOutput(0, new Script(carrier.Serialize())); txOut.Value = 100; transactionContext = new SmartContractTransactionContext(BlockHeight, CoinbaseAddress, MempoolFee, SenderAddress, transaction); var callExecutor = new Executor(this.loggerFactory, this.serializer, this.state, this.refundProcessor, this.transferProcessor, this.vm); // Because our contract contains an infinite loop, we want to kill our test after // some amount of time without achieving a result. 3 seconds is an arbitrarily high enough timeout // for the method body to have finished execution while minimising the amount of time we spend // running tests // If you're running with the debugger on this will obviously be a source of failures result = RunWithTimeout(3, () => callExecutor.Execute(transactionContext)); Assert.IsType <OutOfGasException>(result.Exception); Assert.Equal(gasLimit, result.GasConsumed); }
public void SmartContract_CanSerialize_OP_CALLCONTRACT_WithMethodParameters() { string[] methodParameters = new string[] { string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Bool, true), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Byte, (byte)1), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.ByteArray, BitConverter.ToString(Encoding.UTF8.GetBytes("test"))), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Char, 's'), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.SByte, -45), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Short, 7), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.String, "test"), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.UInt, 36), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.UInt160, new uint160(new byte[20] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 })), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.ULong, 29), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Address, new Address("0x95D34980095380851902ccd9A1Fb4C813C2cb639")), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Address, new Address("mxKorCkWmtrPoekfWiMzERJPhaT13nnkMy")) }; var carrier = SmartContractCarrier.CallContract(1, 100, "Execute", 1, (Gas)500000, methodParameters); var serializer = CallDataSerializer.Default; var callDataResult = serializer.Deserialize(carrier.Serialize()); var callData = callDataResult.Value; Assert.True(callDataResult.IsSuccess); Assert.NotNull(callData.MethodParameters[0]); Assert.Equal(carrier.MethodParameters[0], callData.MethodParameters[0]); Assert.NotNull(callData.MethodParameters[1]); Assert.Equal(carrier.MethodParameters[1], callData.MethodParameters[1]); Assert.NotNull(callData.MethodParameters[2]); Assert.Equal(carrier.MethodParameters[2], callData.MethodParameters[2]); Assert.NotNull(callData.MethodParameters[3]); Assert.Equal(carrier.MethodParameters[3], callData.MethodParameters[3]); Assert.NotNull(callData.MethodParameters[4]); Assert.Equal(carrier.MethodParameters[4], callData.MethodParameters[4]); Assert.NotNull(callData.MethodParameters[5]); Assert.Equal(carrier.MethodParameters[5], callData.MethodParameters[5]); Assert.NotNull(callData.MethodParameters[6]); Assert.Equal(carrier.MethodParameters[6], callData.MethodParameters[6]); Assert.NotNull(callData.MethodParameters[7]); Assert.Equal(carrier.MethodParameters[7], callData.MethodParameters[7]); Assert.NotNull(callData.MethodParameters[8]); Assert.Equal(carrier.MethodParameters[8], callData.MethodParameters[8]); Assert.NotNull(callData.MethodParameters[9]); Assert.Equal(carrier.MethodParameters[9], callData.MethodParameters[9]); Assert.NotNull(callData.MethodParameters[10]); Assert.Equal(carrier.MethodParameters[10], callData.MethodParameters[10]); Assert.NotNull(callData.MethodParameters[11]); Assert.Equal(carrier.MethodParameters[11], callData.MethodParameters[11]); }
public void SmartContract_CanSerialize_OP_CREATECONTRACT_WithMethodParameters() { byte[] contractExecutionCode = Encoding.UTF8.GetBytes( @" using System; using Stratis.SmartContracts; [References] public class Test : SmartContract { public void TestMethod(int orders, bool canOrder) { [CodeToExecute] } }" ); string[] methodParameters = new string[] { string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Short, 12), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Bool, true), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.String, "te|s|t"), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.String, "te#st"), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.String, "#4#te#st#"), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Char, '#'), }; var carrier = SmartContractCarrier.CreateContract(1, contractExecutionCode, 1, (Gas)500000, methodParameters); var serializer = CallDataSerializer.Default; var callDataResult = serializer.Deserialize(carrier.Serialize()); var callData = callDataResult.Value; Assert.True(callDataResult.IsSuccess); Assert.Equal(carrier.ContractTxData.VmVersion, callData.VmVersion); Assert.Equal(carrier.ContractTxData.OpCodeType, callData.OpCodeType); Assert.Equal(carrier.ContractTxData.ContractExecutionCode, callData.ContractExecutionCode); Assert.Equal(6, callData.MethodParameters.Length); Assert.NotNull(callData.MethodParameters[0]); Assert.Equal(12, callData.MethodParameters[0]); Assert.NotNull(carrier.MethodParameters[1]); Assert.True((bool)callData.MethodParameters[1]); Assert.NotNull(callData.MethodParameters[2]); Assert.Equal("te|s|t", callData.MethodParameters[2]); Assert.NotNull(callData.MethodParameters[3]); Assert.Equal("te#st", callData.MethodParameters[3]); Assert.NotNull(callData.MethodParameters[4]); Assert.Equal("#4#te#st#", callData.MethodParameters[4]); Assert.NotNull(callData.MethodParameters[5]); Assert.Equal("#", callData.MethodParameters[5]); Assert.Equal(carrier.ContractTxData.GasPrice, callData.GasPrice); Assert.Equal(carrier.ContractTxData.GasLimit, callData.GasLimit); }
public void SmartContract_CanSerialize_OP_CREATECONTRACT_WithMethodParameters() { byte[] contractExecutionCode = Encoding.UTF8.GetBytes( @" using System; using Stratis.SmartContracts; [References] public class Test : SmartContract { public void TestMethod(int orders, bool canOrder) { [CodeToExecute] } }" ); string[] methodParameters = new string[] { string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Short, 12), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Bool, true), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.String, "te|s|t"), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.String, "te#st"), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.String, "#4#te#st#"), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Char, '#'), }; var carrier = SmartContractCarrier.CreateContract(1, contractExecutionCode, 1, (Gas)500000, methodParameters); var tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(0, 0), new Script(OpcodeType.OP_1))); tx.AddOutput(new TxOut(new Money(5000000000L - 10000), new Script(carrier.Serialize()))); SmartContractCarrier deserialized = SmartContractCarrier.Deserialize(tx); Assert.Equal(carrier.CallData.VmVersion, deserialized.CallData.VmVersion); Assert.Equal(carrier.CallData.OpCodeType, deserialized.CallData.OpCodeType); Assert.Equal(carrier.CallData.ContractExecutionCode, deserialized.CallData.ContractExecutionCode); Assert.Equal(6, deserialized.MethodParameters.Length); Assert.NotNull(carrier.MethodParameters[0]); Assert.Equal(12, deserialized.MethodParameters[0]); Assert.NotNull(carrier.MethodParameters[1]); Assert.True((bool)deserialized.MethodParameters[1]); Assert.NotNull(carrier.MethodParameters[2]); Assert.Equal("te|s|t", deserialized.MethodParameters[2]); Assert.NotNull(carrier.MethodParameters[3]); Assert.Equal("te#st", deserialized.MethodParameters[3]); Assert.NotNull(carrier.MethodParameters[4]); Assert.Equal("#4#te#st#", deserialized.MethodParameters[4]); Assert.NotNull(carrier.MethodParameters[5]); Assert.Equal("#", deserialized.MethodParameters[5]); Assert.Equal(carrier.CallData.GasPrice, deserialized.CallData.GasPrice); Assert.Equal(carrier.CallData.GasLimit, deserialized.CallData.GasLimit); }
public void SmartContract_CanSerialize_OP_CALLCONTRACT_WithMethodParameters() { string[] methodParameters = new string[] { string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Bool, true), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Byte, (byte)1), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.ByteArray, BitConverter.ToString(Encoding.UTF8.GetBytes("test"))), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Char, 's'), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.SByte, -45), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Short, 7), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.String, "test"), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.UInt, 36), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.UInt160, new uint160(new byte[20] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 })), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.ULong, 29), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Address, new Address("0x95D34980095380851902ccd9A1Fb4C813C2cb639")), string.Format("{0}#{1}", (int)SmartContractCarrierDataType.Address, new Address("mxKorCkWmtrPoekfWiMzERJPhaT13nnkMy")) }; var carrier = SmartContractCarrier.CallContract(1, 100, "Execute", 1, (Gas)500000, methodParameters); var tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(0, 0), new Script(OpcodeType.OP_1))); tx.AddOutput(new TxOut(new Money(5000000000L - 10000), new Script(carrier.Serialize()))); SmartContractCarrier deserialized = SmartContractCarrier.Deserialize(tx); Assert.NotNull(deserialized.MethodParameters[0]); Assert.Equal(carrier.MethodParameters[0], deserialized.MethodParameters[0]); Assert.NotNull(deserialized.MethodParameters[1]); Assert.Equal(carrier.MethodParameters[1], deserialized.MethodParameters[1]); Assert.NotNull(deserialized.MethodParameters[2]); Assert.Equal(carrier.MethodParameters[2], deserialized.MethodParameters[2]); Assert.NotNull(deserialized.MethodParameters[3]); Assert.Equal(carrier.MethodParameters[3], deserialized.MethodParameters[3]); Assert.NotNull(deserialized.MethodParameters[4]); Assert.Equal(carrier.MethodParameters[4], deserialized.MethodParameters[4]); Assert.NotNull(deserialized.MethodParameters[5]); Assert.Equal(carrier.MethodParameters[5], deserialized.MethodParameters[5]); Assert.NotNull(deserialized.MethodParameters[6]); Assert.Equal(carrier.MethodParameters[6], deserialized.MethodParameters[6]); Assert.NotNull(deserialized.MethodParameters[7]); Assert.Equal(carrier.MethodParameters[7], deserialized.MethodParameters[7]); Assert.NotNull(deserialized.MethodParameters[8]); Assert.Equal(carrier.MethodParameters[8], deserialized.MethodParameters[8]); Assert.NotNull(deserialized.MethodParameters[9]); Assert.Equal(carrier.MethodParameters[9], deserialized.MethodParameters[9]); Assert.NotNull(deserialized.MethodParameters[10]); Assert.Equal(carrier.MethodParameters[10], deserialized.MethodParameters[10]); Assert.NotNull(deserialized.MethodParameters[11]); Assert.Equal(carrier.MethodParameters[11], deserialized.MethodParameters[11]); }
private BuildCallContractTransactionResponse BuildCallTx(BuildCallContractTransactionRequest request) { this.logger.LogTrace(request.ToString()); AddressBalance addressBalance = this.walletManager.GetAddressBalance(request.Sender); if (addressBalance.AmountConfirmed == 0) { return(BuildCallContractTransactionResponse.Failed($"The 'Sender' address you're trying to spend from doesn't have a confirmed balance. Current unconfirmed balance: {addressBalance.AmountUnconfirmed}. Please check the 'Sender' address.")); } var selectedInputs = new List <OutPoint>(); selectedInputs = this.walletManager.GetSpendableTransactionsInWallet(request.WalletName, MinConfirmationsAllChecks).Where(x => x.Address.Address == request.Sender).Select(x => x.ToOutPoint()).ToList(); ulong gasPrice = ulong.Parse(request.GasPrice); ulong gasLimit = ulong.Parse(request.GasLimit); uint160 addressNumeric = new Address(request.ContractAddress).ToUint160(this.network); SmartContractCarrier carrier; if (request.Parameters != null && request.Parameters.Any()) { carrier = SmartContractCarrier.CallContract(ReflectionVirtualMachine.VmVersion, addressNumeric, request.MethodName, gasPrice, new Gas(gasLimit), request.Parameters); } else { carrier = SmartContractCarrier.CallContract(ReflectionVirtualMachine.VmVersion, addressNumeric, request.MethodName, gasPrice, new Gas(gasLimit)); } HdAddress senderAddress = null; if (!string.IsNullOrWhiteSpace(request.Sender)) { Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccountByCoinType(request.AccountName, this.coinType); senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender); } ulong totalFee = (gasPrice * gasLimit) + Money.Parse(request.FeeAmount); var context = new TransactionBuildContext(this.network) { AccountReference = new WalletAccountReference(request.WalletName, request.AccountName), TransactionFee = totalFee, ChangeAddress = senderAddress, SelectedInputs = selectedInputs, MinConfirmations = MinConfirmationsAllChecks, WalletPassword = request.Password, Recipients = new[] { new Recipient { Amount = request.Amount, ScriptPubKey = new Script(carrier.Serialize()) } }.ToList() }; try { Transaction transaction = this.walletTransactionHandler.BuildTransaction(context); return(BuildCallContractTransactionResponse.Succeeded(request.MethodName, transaction, context.TransactionFee)); } catch (Exception exception) { return(BuildCallContractTransactionResponse.Failed(exception.Message)); } }
public void VM_ExecuteContract_WithoutParameters() { //Get the contract execution code------------------------ SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/StorageTest.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; //------------------------------------------------------- //Call smart contract and add to transaction------------- var carrier = SmartContractCarrier.CallContract(1, new uint160(1), "StoreData", 1, (Gas)500000); var transactionCall = new Transaction(); transactionCall.AddInput(new TxIn()); TxOut callTxOut = transactionCall.AddOutput(0, new Script(carrier.Serialize())); //------------------------------------------------------- //Deserialize the contract from the transaction---------- var deserializedCall = SmartContractCarrier.Deserialize(transactionCall); //------------------------------------------------------- var repository = new ContractStateRepositoryRoot(new NoDeleteSource <byte[], byte[]>(new MemoryDictionarySource())); IContractStateRepository stateRepository = repository.StartTracking(); var gasMeter = new GasMeter(deserializedCall.CallData.GasLimit); var persistenceStrategy = new MeteredPersistenceStrategy(repository, gasMeter, this.keyEncodingStrategy); var persistentState = new PersistentState(persistenceStrategy, deserializedCall.CallData.ContractAddress, this.network); var internalTxExecutorFactory = new InternalTransactionExecutorFactory(this.keyEncodingStrategy, this.loggerFactory, this.network); var vm = new ReflectionVirtualMachine(internalTxExecutorFactory, this.loggerFactory); var sender = deserializedCall.Sender?.ToString() ?? TestAddress.ToString(); var context = new SmartContractExecutionContext( new Block(1, new Address("2")), new Message( new Address(deserializedCall.CallData.ContractAddress.ToString()), new Address(sender), deserializedCall.Value, deserializedCall.CallData.GasLimit ), TestAddress.ToUint160(this.network), deserializedCall.CallData.GasPrice ); var result = vm.ExecuteMethod( contractExecutionCode, "StoreData", context, gasMeter, persistentState, repository); stateRepository.Commit(); Assert.Equal(Encoding.UTF8.GetBytes("TestValue"), stateRepository.GetStorageValue(deserializedCall.CallData.ContractAddress, Encoding.UTF8.GetBytes("TestKey"))); Assert.Equal(Encoding.UTF8.GetBytes("TestValue"), repository.GetStorageValue(deserializedCall.CallData.ContractAddress, Encoding.UTF8.GetBytes("TestKey"))); }
public void SendAndReceiveSmartContractTransactions() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode scSender = builder.CreateSmartContractPowNode(); CoreNode scReceiver = builder.CreateSmartContractPowNode(); builder.StartAll(); scSender.NotInIBD(); scReceiver.NotInIBD(); scSender.FullNode.WalletManager().CreateWallet(Password, WalletName); scReceiver.FullNode.WalletManager().CreateWallet(Password, WalletName); HdAddress addr = scSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, AccountName)); Features.Wallet.Wallet wallet = scSender.FullNode.WalletManager().GetWalletByName(WalletName); Key key = wallet.GetExtendedPrivateKeyForAddress(Password, addr).PrivateKey; scSender.SetDummyMinerSecret(new BitcoinSecret(key, scSender.FullNode.Network)); var maturity = (int)scSender.FullNode.Network.Consensus.CoinbaseMaturity; scSender.GenerateStratisWithMiner(maturity + 5); // Wait for block repo for block sync to work. TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); // The mining should add coins to the wallet. var total = scSender.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * (maturity + 5) * 50, total); // Create a token contract ulong gasPrice = 1; int vmVersion = 1; Gas gasLimit = (Gas)2000; SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/TransferTest.cs"); Assert.True(compilationResult.Success); var contractCarrier = SmartContractCarrier.CreateContract(vmVersion, compilationResult.Compilation, gasPrice, gasLimit); var contractCreateScript = new Script(contractCarrier.Serialize()); var txBuildContext = new TransactionBuildContext(scSender.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, AccountName), MinConfirmations = maturity, FeeType = FeeType.High, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = 0, ScriptPubKey = contractCreateScript } }.ToList() }; Transaction transferContractTransaction = (scSender.FullNode.NodeService <IWalletTransactionHandler>() as SmartContractWalletTransactionHandler).BuildTransaction(txBuildContext); // Broadcast the token transaction to the network scSender.FullNode.NodeService <IBroadcasterManager>().BroadcastTransactionAsync(transferContractTransaction); // Wait for the token transaction to be picked up by the mempool TestHelper.WaitLoop(() => scSender.CreateRPCClient().GetRawMempool().Length > 0); // Mine the token transaction and wait for it sync scSender.GenerateStratisWithMiner(1); TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); // Sync to the receiver node scSender.CreateRPCClient().AddNode(scReceiver.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // Ensure that boths nodes has the contract ContractStateRepositoryRoot senderState = scSender.FullNode.NodeService <ContractStateRepositoryRoot>(); ContractStateRepositoryRoot receiverState = scReceiver.FullNode.NodeService <ContractStateRepositoryRoot>(); IAddressGenerator addressGenerator = scSender.FullNode.NodeService <IAddressGenerator>(); uint160 tokenContractAddress = addressGenerator.GenerateAddress(transferContractTransaction.GetHash(), 0); Assert.NotNull(senderState.GetCode(tokenContractAddress)); Assert.NotNull(receiverState.GetCode(tokenContractAddress)); scSender.FullNode.MempoolManager().Clear(); // Create a transfer token contract compilationResult = SmartContractCompiler.CompileFile("SmartContracts/TransferTest.cs"); Assert.True(compilationResult.Success); contractCarrier = SmartContractCarrier.CreateContract(vmVersion, compilationResult.Compilation, gasPrice, gasLimit); contractCreateScript = new Script(contractCarrier.Serialize()); txBuildContext = new TransactionBuildContext(scSender.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, AccountName), MinConfirmations = maturity, FeeType = FeeType.High, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = 0, ScriptPubKey = contractCreateScript } }.ToList() }; // Broadcast the token transaction to the network transferContractTransaction = (scSender.FullNode.NodeService <IWalletTransactionHandler>() as SmartContractWalletTransactionHandler).BuildTransaction(txBuildContext); scSender.FullNode.NodeService <IBroadcasterManager>().BroadcastTransactionAsync(transferContractTransaction); // Wait for the token transaction to be picked up by the mempool TestHelper.WaitLoop(() => scSender.CreateRPCClient().GetRawMempool().Length > 0); scSender.GenerateStratisWithMiner(1); // Ensure the node is synced TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); // Ensure both nodes are synced with each other TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // Ensure that boths nodes has the contract senderState = scSender.FullNode.NodeService <ContractStateRepositoryRoot>(); receiverState = scReceiver.FullNode.NodeService <ContractStateRepositoryRoot>(); tokenContractAddress = addressGenerator.GenerateAddress(transferContractTransaction.GetHash(), 0); Assert.NotNull(senderState.GetCode(tokenContractAddress)); Assert.NotNull(receiverState.GetCode(tokenContractAddress)); scSender.FullNode.MempoolManager().Clear(); // Create a call contract transaction which will transfer funds contractCarrier = SmartContractCarrier.CallContract(1, tokenContractAddress, "Test", gasPrice, gasLimit); Script contractCallScript = new Script(contractCarrier.Serialize()); txBuildContext = new TransactionBuildContext(scSender.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, AccountName), MinConfirmations = maturity, FeeType = FeeType.High, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = 1000, ScriptPubKey = contractCallScript } }.ToList() }; // Broadcast the token transaction to the network transferContractTransaction = (scSender.FullNode.NodeService <IWalletTransactionHandler>() as SmartContractWalletTransactionHandler).BuildTransaction(txBuildContext); scSender.FullNode.NodeService <IBroadcasterManager>().BroadcastTransactionAsync(transferContractTransaction); TestHelper.WaitLoop(() => scSender.CreateRPCClient().GetRawMempool().Length > 0); // Mine the transaction scSender.GenerateStratisWithMiner(1); // Ensure the nodes are synced TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // The balance should now reflect the transfer Assert.Equal((ulong)900, senderState.GetCurrentBalance(tokenContractAddress)); } }
public void SendAndReceiveSmartContractTransactionsOnPosNetwork() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode scSender = builder.CreateSmartContractPosNode(); CoreNode scReceiver = builder.CreateSmartContractPosNode(); builder.StartAll(); scSender.NotInIBD(); scReceiver.NotInIBD(); scSender.FullNode.WalletManager().CreateWallet(Password, WalletName, Passphrase); scReceiver.FullNode.WalletManager().CreateWallet(Password, WalletName, Passphrase); var maturity = (int)scSender.FullNode.Network.Consensus.CoinbaseMaturity; HdAddress senderAddress = TestHelper.MineBlocks(scSender, WalletName, Password, AccountName, maturity + 5).AddressUsed; // Wait for block repo for block sync to work. TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); // The mining should add coins to the wallet. var total = scSender.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 6 * 50, total); // Create a token contract ulong gasPrice = 1; int vmVersion = 1; Gas gasLimit = (Gas)5000; SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/TransferTestPos.cs"); Assert.True(compilationResult.Success); var contractCarrier = SmartContractCarrier.CreateContract(vmVersion, compilationResult.Compilation, gasPrice, gasLimit); var contractCreateScript = new Script(contractCarrier.Serialize()); var txBuildContext = new TransactionBuildContext(scSender.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, AccountName), ChangeAddress = senderAddress, MinConfirmations = maturity, FeeType = FeeType.High, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = 0, ScriptPubKey = contractCreateScript } }.ToList() }; // Build the transfer contract transaction var transferContractTransaction = BuildTransferContractTransaction(scSender, txBuildContext); // Add the smart contract transaction to the mempool to be mined. scSender.AddToStratisMempool(transferContractTransaction); // Ensure the smart contract transaction is in the mempool. TestHelper.WaitLoop(() => scSender.CreateRPCClient().GetRawMempool().Length > 0); // Mine the token transaction and wait for it sync scSender.GenerateStratisWithMiner(1); TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); // Sync to the receiver node scSender.CreateRPCClient().AddNode(scReceiver.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // Ensure that boths nodes has the contract IContractStateRoot senderState = scSender.FullNode.NodeService <IContractStateRoot>(); IContractStateRoot receiverState = scReceiver.FullNode.NodeService <IContractStateRoot>(); IAddressGenerator addressGenerator = scSender.FullNode.NodeService <IAddressGenerator>(); uint160 tokenContractAddress = addressGenerator.GenerateAddress(transferContractTransaction.GetHash(), 0); Assert.NotNull(senderState.GetCode(tokenContractAddress)); Assert.NotNull(receiverState.GetCode(tokenContractAddress)); scSender.FullNode.MempoolManager().Clear(); // Create a transfer token contract compilationResult = SmartContractCompiler.CompileFile("SmartContracts/TransferTestPos.cs"); Assert.True(compilationResult.Success); contractCarrier = SmartContractCarrier.CreateContract(vmVersion, compilationResult.Compilation, gasPrice, gasLimit); contractCreateScript = new Script(contractCarrier.Serialize()); txBuildContext = new TransactionBuildContext(scSender.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, AccountName), ChangeAddress = senderAddress, MinConfirmations = maturity, FeeType = FeeType.High, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = 0, ScriptPubKey = contractCreateScript } }.ToList() }; // Build the transfer contract transaction transferContractTransaction = BuildTransferContractTransaction(scSender, txBuildContext); // Add the smart contract transaction to the mempool to be mined. scSender.AddToStratisMempool(transferContractTransaction); // Wait for the token transaction to be picked up by the mempool TestHelper.WaitLoop(() => scSender.CreateRPCClient().GetRawMempool().Length > 0); scSender.GenerateStratisWithMiner(1); // Ensure the node is synced TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); // Ensure both nodes are synced with each other TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); tokenContractAddress = addressGenerator.GenerateAddress(transferContractTransaction.GetHash(), 0); // nonce is 0 for user contract creation. Assert.NotNull(senderState.GetCode(tokenContractAddress)); Assert.NotNull(receiverState.GetCode(tokenContractAddress)); scSender.FullNode.MempoolManager().Clear(); // Create a call contract transaction which will transfer funds contractCarrier = SmartContractCarrier.CallContract(1, tokenContractAddress, "Test", gasPrice, gasLimit); Script contractCallScript = new Script(contractCarrier.Serialize()); txBuildContext = new TransactionBuildContext(scSender.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, AccountName), ChangeAddress = senderAddress, MinConfirmations = maturity, FeeType = FeeType.High, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = 1000, ScriptPubKey = contractCallScript } }.ToList() }; // Build the transfer contract transaction var callContractTransaction = BuildTransferContractTransaction(scSender, txBuildContext); // Add the smart contract transaction to the mempool to be mined. scSender.AddToStratisMempool(callContractTransaction); // Wait for the token transaction to be picked up by the mempool TestHelper.WaitLoop(() => scSender.CreateRPCClient().GetRawMempool().Length > 0); scSender.GenerateStratisWithMiner(1); // Ensure the nodes are synced TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // The balance should now reflect the transfer Assert.Equal((ulong)900, senderState.GetCurrentBalance(tokenContractAddress)); } }