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 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 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 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)); } }
private BuildCreateContractTransactionResponse BuildCreateTx(BuildCreateContractTransactionRequest request) { this.logger.LogTrace(request.ToString()); AddressBalance addressBalance = this.walletManager.GetAddressBalance(request.Sender); if (addressBalance.AmountConfirmed == 0) { return(BuildCreateContractTransactionResponse.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); SmartContractCarrier carrier; if (request.Parameters != null && request.Parameters.Any()) { carrier = SmartContractCarrier.CreateContract(ReflectionVirtualMachine.VmVersion, request.ContractCode.HexToByteArray(), gasPrice, new Gas(gasLimit), request.Parameters); } else { carrier = SmartContractCarrier.CreateContract(ReflectionVirtualMachine.VmVersion, request.ContractCode.HexToByteArray(), 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 walletAccountReference = new WalletAccountReference(request.WalletName, request.AccountName); var recipient = new Recipient { Amount = request.Amount ?? "0", ScriptPubKey = new Script(carrier.Serialize()) }; var context = new TransactionBuildContext(this.network) { AccountReference = walletAccountReference, TransactionFee = totalFee, ChangeAddress = senderAddress, SelectedInputs = selectedInputs, MinConfirmations = MinConfirmationsAllChecks, WalletPassword = request.Password, Recipients = new[] { recipient }.ToList() }; try { Transaction transaction = this.walletTransactionHandler.BuildTransaction(context); uint160 contractAddress = this.addressGenerator.GenerateAddress(transaction.GetHash(), 0); return(BuildCreateContractTransactionResponse.Succeeded(transaction, context.TransactionFee, contractAddress.ToAddress(this.network))); } catch (Exception exception) { return(BuildCreateContractTransactionResponse.Failed(exception.Message)); } }
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_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 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 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 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)); } }