From 907a3762d825eb85e9276e8b0756baaf3c748efb Mon Sep 17 00:00:00 2001 From: noescape00 Date: Mon, 28 Jun 2021 21:37:45 +0300 Subject: [PATCH 1/4] added sc related api methods --- .../Controllers/Unity3dController.cs | 162 +++++++++++++++++- .../Stratis.Features.Unity3dApi.csproj | 1 + 2 files changed, 162 insertions(+), 1 deletion(-) diff --git a/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs b/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs index c13c4b078d..9dd86da131 100644 --- a/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs +++ b/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs @@ -4,21 +4,36 @@ using System.Net; using System.Threading; using System.Threading.Tasks; +using CSharpFunctionalExtensions; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using NBitcoin; +using Newtonsoft.Json; using Stratis.Bitcoin.Base; +using Stratis.Bitcoin.Controllers; using Stratis.Bitcoin.Controllers.Models; using Stratis.Bitcoin.Features.BlockStore.AddressIndexing; using Stratis.Bitcoin.Features.BlockStore.Controllers; using Stratis.Bitcoin.Features.BlockStore.Models; using Stratis.Bitcoin.Features.Consensus; using Stratis.Bitcoin.Features.Consensus.CoinViews; +using Stratis.Bitcoin.Features.SmartContracts; +using Stratis.Bitcoin.Features.SmartContracts.Models; +using Stratis.Bitcoin.Features.SmartContracts.ReflectionExecutor; +using Stratis.Bitcoin.Features.SmartContracts.Wallet; using Stratis.Bitcoin.Features.Wallet.Controllers; using Stratis.Bitcoin.Features.Wallet.Models; using Stratis.Bitcoin.Interfaces; using Stratis.Bitcoin.Utilities; using Stratis.Bitcoin.Utilities.JsonErrors; +using Stratis.Bitcoin.Utilities.ModelStateErrors; +using Stratis.SmartContracts.CLR; +using Stratis.SmartContracts.CLR.Caching; +using Stratis.SmartContracts.CLR.Compilation; +using Stratis.SmartContracts.CLR.Local; +using Stratis.SmartContracts.CLR.Serialization; +using Stratis.SmartContracts.Core.Receipts; +using Stratis.SmartContracts.Core.State; namespace Stratis.Features.Unity3dApi.Controllers { @@ -45,8 +60,22 @@ public class Unity3dController : Controller /// Instance logger. private readonly ILogger logger; + private readonly IContractPrimitiveSerializer primitiveSerializer; + + private readonly IStateRepositoryRoot stateRoot; + + private readonly IContractAssemblyCache contractAssemblyCache; + + private readonly IReceiptRepository receiptRepository; + + private readonly ISmartContractTransactionService smartContractTransactionService; + + private readonly ILocalExecutor localExecutor; + public Unity3dController(ILoggerFactory loggerFactory, IAddressIndexer addressIndexer, - IBlockStore blockStore, IChainState chainState, Network network, ICoinView coinView, WalletController walletController, ChainIndexer chainIndexer, IStakeChain stakeChain = null) + IBlockStore blockStore, IChainState chainState, Network network, ICoinView coinView, WalletController walletController, ChainIndexer chainIndexer, IStakeChain stakeChain = null, + IContractPrimitiveSerializer primitiveSerializer = null, IStateRepositoryRoot stateRoot = null, IContractAssemblyCache contractAssemblyCache = null, + IReceiptRepository receiptRepository = null, ISmartContractTransactionService smartContractTransactionService = null, LocalExecutor localExecutor = null) { Guard.NotNull(loggerFactory, nameof(loggerFactory)); this.logger = loggerFactory.CreateLogger(this.GetType().FullName); @@ -58,6 +87,13 @@ public Unity3dController(ILoggerFactory loggerFactory, IAddressIndexer addressIn this.walletController = Guard.NotNull(walletController, nameof(walletController)); this.chainIndexer = Guard.NotNull(chainIndexer, nameof(chainIndexer)); this.stakeChain = stakeChain; + + this.primitiveSerializer = primitiveSerializer; + this.stateRoot = stateRoot; + this.contractAssemblyCache = contractAssemblyCache; + this.receiptRepository = receiptRepository; + this.smartContractTransactionService = smartContractTransactionService; + this.localExecutor = localExecutor; } /// @@ -406,5 +442,129 @@ public TipModel GetTip() return null; } } + + /// + /// Gets a smart contract transaction receipt. Receipts contain information about how a smart contract transaction was executed. + /// This includes the value returned from a smart contract call and how much gas was used. + /// + /// + /// A hash of the smart contract transaction (the transaction ID). + /// + /// The receipt for the smart contract. + /// Returns transaction receipt + /// Transaction not found + [Route("api/[controller]/receipt")] + [HttpGet] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public IActionResult GetReceiptAPI([FromQuery] string txHash) + { + ReceiptResponse receiptResponse; + uint256 txHashNum = new uint256(txHash); + Receipt receipt = this.receiptRepository.Retrieve(txHashNum); + + if (receipt == null) + { + return null; + } + + uint160 address = receipt.NewContractAddress ?? receipt.To; + + if (!receipt.Logs.Any()) + { + receiptResponse = new ReceiptResponse(receipt, new List(), this.network); + } + else + { + var deserializer = new ApiLogDeserializer(this.primitiveSerializer, this.network, this.stateRoot, this.contractAssemblyCache); + + List logResponses = deserializer.MapLogResponses(receipt.Logs); + + receiptResponse = new ReceiptResponse(receipt, logResponses, this.network); + } + + return this.Json(receiptResponse); + } + + + /// + /// Makes a local call to a method on a smart contract that has been successfully deployed. A transaction + /// is not created as the call is never propagated across the network. All persistent data held by the + /// smart contract is copied before the call is made. Only this copy is altered by the call + /// and the actual data is unaffected. Even if an amount of funds are specified to send with the call, + /// no funds are in fact sent. + /// The purpose of this function is to query and test methods. + /// + /// + /// An object containing the necessary parameters to build the transaction. + /// + /// The result of the local call to the smart contract method. + /// Returns call response + /// Invalid request + /// Unable to deserialize method parameters + [Route("api/[controller]/local-call")] + [HttpPost] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public IActionResult LocalCallSmartContractTransaction([FromBody] LocalCallContractRequest request) + { + if (!this.ModelState.IsValid) + return ModelStateErrors.BuildErrorResponse(this.ModelState); + + // Rewrite the method name to a property name + this.RewritePropertyGetterName(request); + + try + { + ContractTxData txData = this.smartContractTransactionService.BuildLocalCallTxData(request); + + var height = request.BlockHeight.HasValue ? request.BlockHeight.Value : (ulong)this.chainIndexer.Height; + + ILocalExecutionResult result = this.localExecutor.Execute( + height, + request.Sender?.ToUint160(this.network) ?? new uint160(), + string.IsNullOrWhiteSpace(request.Amount) ? (Money)request.Amount : 0, + txData); + + return this.Json(result, new JsonSerializerSettings + { + ContractResolver = new ContractParametersContractResolver(this.network) + }); + } + catch (MethodParameterStringSerializerException e) + { + return this.Json(ErrorHelpers.BuildErrorResponse(HttpStatusCode.InternalServerError, e.Message, + "Error deserializing method parameters")); + } + } + + + /// + /// If the call is to a property, rewrites the method name to the getter method's name. + /// + private void RewritePropertyGetterName(LocalCallContractRequest request) + { + // Don't rewrite if there are params + if (request.Parameters != null && request.Parameters.Any()) + return; + + byte[] contractCode = this.stateRoot.GetCode(request.ContractAddress.ToUint160(this.network)); + + string contractType = this.stateRoot.GetContractType(request.ContractAddress.ToUint160(this.network)); + + Result readResult = ContractDecompiler.GetModuleDefinition(contractCode); + + if (readResult.IsSuccess) + { + IContractModuleDefinition contractModule = readResult.Value; + string propertyGetterName = contractModule.GetPropertyGetterMethodName(contractType, request.MethodName); + + if (propertyGetterName != null) + { + request.MethodName = propertyGetterName; + } + } + } } } diff --git a/src/Stratis.Features.Unity3dApi/Stratis.Features.Unity3dApi.csproj b/src/Stratis.Features.Unity3dApi/Stratis.Features.Unity3dApi.csproj index 50438c1f88..17fd6d60ef 100644 --- a/src/Stratis.Features.Unity3dApi/Stratis.Features.Unity3dApi.csproj +++ b/src/Stratis.Features.Unity3dApi/Stratis.Features.Unity3dApi.csproj @@ -8,6 +8,7 @@ + From f4bd3687413ca9ca3e8cc84b678998558f0386f6 Mon Sep 17 00:00:00 2001 From: noescape00 Date: Mon, 28 Jun 2021 21:49:10 +0300 Subject: [PATCH 2/4] fix --- .../Controllers/Unity3dController.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs b/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs index 9dd86da131..700e200606 100644 --- a/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs +++ b/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs @@ -10,7 +10,6 @@ using NBitcoin; using Newtonsoft.Json; using Stratis.Bitcoin.Base; -using Stratis.Bitcoin.Controllers; using Stratis.Bitcoin.Controllers.Models; using Stratis.Bitcoin.Features.BlockStore.AddressIndexing; using Stratis.Bitcoin.Features.BlockStore.Controllers; @@ -75,7 +74,7 @@ public class Unity3dController : Controller public Unity3dController(ILoggerFactory loggerFactory, IAddressIndexer addressIndexer, IBlockStore blockStore, IChainState chainState, Network network, ICoinView coinView, WalletController walletController, ChainIndexer chainIndexer, IStakeChain stakeChain = null, IContractPrimitiveSerializer primitiveSerializer = null, IStateRepositoryRoot stateRoot = null, IContractAssemblyCache contractAssemblyCache = null, - IReceiptRepository receiptRepository = null, ISmartContractTransactionService smartContractTransactionService = null, LocalExecutor localExecutor = null) + IReceiptRepository receiptRepository = null, ISmartContractTransactionService smartContractTransactionService = null, ILocalExecutor localExecutor = null) { Guard.NotNull(loggerFactory, nameof(loggerFactory)); this.logger = loggerFactory.CreateLogger(this.GetType().FullName); From cb86c14e3ba3f89d2c18f1d1ad9fac6e820cdc89 Mon Sep 17 00:00:00 2001 From: noescape00 Date: Tue, 29 Jun 2021 00:10:02 +0300 Subject: [PATCH 3/4] explicit return types --- .../Controllers/Unity3dController.cs | 36 ++++++------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs b/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs index 700e200606..8ed01d1003 100644 --- a/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs +++ b/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs @@ -456,7 +456,7 @@ public TipModel GetTip() [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public IActionResult GetReceiptAPI([FromQuery] string txHash) + public ReceiptResponse GetReceiptAPI([FromQuery] string txHash) { ReceiptResponse receiptResponse; uint256 txHashNum = new uint256(txHash); @@ -482,7 +482,7 @@ public IActionResult GetReceiptAPI([FromQuery] string txHash) receiptResponse = new ReceiptResponse(receipt, logResponses, this.network); } - return this.Json(receiptResponse); + return receiptResponse; } @@ -506,36 +506,22 @@ public IActionResult GetReceiptAPI([FromQuery] string txHash) [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public IActionResult LocalCallSmartContractTransaction([FromBody] LocalCallContractRequest request) + public LocalExecutionResult LocalCallSmartContractTransaction([FromBody] LocalCallContractRequest request) { - if (!this.ModelState.IsValid) - return ModelStateErrors.BuildErrorResponse(this.ModelState); - // Rewrite the method name to a property name this.RewritePropertyGetterName(request); - try - { - ContractTxData txData = this.smartContractTransactionService.BuildLocalCallTxData(request); + ContractTxData txData = this.smartContractTransactionService.BuildLocalCallTxData(request); - var height = request.BlockHeight.HasValue ? request.BlockHeight.Value : (ulong)this.chainIndexer.Height; + var height = request.BlockHeight.HasValue ? request.BlockHeight.Value : (ulong)this.chainIndexer.Height; - ILocalExecutionResult result = this.localExecutor.Execute( - height, - request.Sender?.ToUint160(this.network) ?? new uint160(), - string.IsNullOrWhiteSpace(request.Amount) ? (Money)request.Amount : 0, - txData); + ILocalExecutionResult result = this.localExecutor.Execute( + height, + request.Sender?.ToUint160(this.network) ?? new uint160(), + string.IsNullOrWhiteSpace(request.Amount) ? (Money)request.Amount : 0, + txData); - return this.Json(result, new JsonSerializerSettings - { - ContractResolver = new ContractParametersContractResolver(this.network) - }); - } - catch (MethodParameterStringSerializerException e) - { - return this.Json(ErrorHelpers.BuildErrorResponse(HttpStatusCode.InternalServerError, e.Message, - "Error deserializing method parameters")); - } + return result as LocalExecutionResult; } From c9fcf9bd46a391ef1158d0aa48ca7b590d55a6bc Mon Sep 17 00:00:00 2001 From: noescape00 Date: Tue, 29 Jun 2021 00:23:40 +0300 Subject: [PATCH 4/4] codestyle --- .../Controllers/Unity3dController.cs | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs b/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs index 8ed01d1003..443709ef4f 100644 --- a/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs +++ b/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs @@ -445,10 +445,8 @@ public TipModel GetTip() /// /// Gets a smart contract transaction receipt. Receipts contain information about how a smart contract transaction was executed. /// This includes the value returned from a smart contract call and how much gas was used. - /// - /// - /// A hash of the smart contract transaction (the transaction ID). - /// + /// + /// A hash of the smart contract transaction (the transaction ID). /// The receipt for the smart contract. /// Returns transaction receipt /// Transaction not found @@ -463,9 +461,7 @@ public ReceiptResponse GetReceiptAPI([FromQuery] string txHash) Receipt receipt = this.receiptRepository.Retrieve(txHashNum); if (receipt == null) - { return null; - } uint160 address = receipt.NewContractAddress ?? receipt.To; @@ -476,7 +472,6 @@ public ReceiptResponse GetReceiptAPI([FromQuery] string txHash) else { var deserializer = new ApiLogDeserializer(this.primitiveSerializer, this.network, this.stateRoot, this.contractAssemblyCache); - List logResponses = deserializer.MapLogResponses(receipt.Logs); receiptResponse = new ReceiptResponse(receipt, logResponses, this.network); @@ -484,8 +479,7 @@ public ReceiptResponse GetReceiptAPI([FromQuery] string txHash) return receiptResponse; } - - + /// /// Makes a local call to a method on a smart contract that has been successfully deployed. A transaction /// is not created as the call is never propagated across the network. All persistent data held by the @@ -493,10 +487,8 @@ public ReceiptResponse GetReceiptAPI([FromQuery] string txHash) /// and the actual data is unaffected. Even if an amount of funds are specified to send with the call, /// no funds are in fact sent. /// The purpose of this function is to query and test methods. - /// - /// - /// An object containing the necessary parameters to build the transaction. - /// + /// + /// An object containing the necessary parameters to build the transaction. /// The result of the local call to the smart contract method. /// Returns call response /// Invalid request @@ -513,18 +505,14 @@ public LocalExecutionResult LocalCallSmartContractTransaction([FromBody] LocalCa ContractTxData txData = this.smartContractTransactionService.BuildLocalCallTxData(request); - var height = request.BlockHeight.HasValue ? request.BlockHeight.Value : (ulong)this.chainIndexer.Height; + ulong height = request.BlockHeight.HasValue ? request.BlockHeight.Value : (ulong)this.chainIndexer.Height; - ILocalExecutionResult result = this.localExecutor.Execute( - height, - request.Sender?.ToUint160(this.network) ?? new uint160(), - string.IsNullOrWhiteSpace(request.Amount) ? (Money)request.Amount : 0, - txData); + ILocalExecutionResult result = this.localExecutor.Execute(height, request.Sender?.ToUint160(this.network) ?? new uint160(), + string.IsNullOrWhiteSpace(request.Amount) ? (Money)request.Amount : 0, txData); return result as LocalExecutionResult; } - /// /// If the call is to a property, rewrites the method name to the getter method's name. /// @@ -546,9 +534,7 @@ private void RewritePropertyGetterName(LocalCallContractRequest request) string propertyGetterName = contractModule.GetPropertyGetterMethodName(contractType, request.MethodName); if (propertyGetterName != null) - { request.MethodName = propertyGetterName; - } } } }