public (Money, TxOut) Process(ContractTxData contractTxData,
                                      ulong mempoolFee, uint160 sender,
                                      Gas gasConsumed,
                                      bool outOfGas)
        {
            this.logger.LogTrace("(){0}:{1}", nameof(mempoolFee), mempoolFee);

            Money fee = mempoolFee;

            if (outOfGas)
            {
                this.logger.LogTrace("(-)[OUTOFGAS_EXCEPTION]");
                return(fee, null);
            }

            var refund = new Money(contractTxData.GasCostBudget - (gasConsumed * contractTxData.GasPrice));

            this.logger.LogTrace("{0}:{1},{2}:{3},{4}:{5},{6}:{7}", nameof(contractTxData.GasCostBudget), contractTxData.GasCostBudget, nameof(gasConsumed), gasConsumed, nameof(contractTxData.GasPrice), contractTxData.GasPrice, nameof(refund), refund);

            TxOut ret = null;

            if (refund > 0)
            {
                fee -= refund;
                ret  = CreateRefund(sender, refund);
            }

            this.logger.LogTrace("(-)");

            return(fee, ret);
        }
Example #2
0
        public (Money, List <TxOut>) Process(ContractTxData contractTxData,
                                             ulong mempoolFee, uint160 sender,
                                             Gas gasConsumed,
                                             Exception exception)
        {
            this.logger.LogTrace("(){0}:{1}", nameof(mempoolFee), mempoolFee);

            Money fee = mempoolFee;

            var refunds = new List <TxOut>();

            if (exception is OutOfGasException)
            {
                this.logger.LogTrace("(-)[OUTOFGAS_EXCEPTION]");
                return(fee, refunds);
            }

            var refund = new Money(contractTxData.GasCostBudget - (gasConsumed * contractTxData.GasPrice));

            this.logger.LogTrace("{0}:{1},{2}:{3},{4}:{5},{6}:{7}", nameof(contractTxData.GasCostBudget), contractTxData.GasCostBudget, nameof(gasConsumed), gasConsumed, nameof(contractTxData.GasPrice), contractTxData.GasPrice, nameof(refund), refund);

            if (refund > 0)
            {
                fee -= refund;
                refunds.Add(CreateRefund(sender, refund));
            }

            this.logger.LogTrace("(-)");

            return(fee, refunds);
        }
        public byte[] Serialize(ContractTxData contractTxData)
        {
            var bytes = new List <byte>
            {
                contractTxData.OpCodeType
            };

            bytes.AddRange(PrefixLength(BitConverter.GetBytes(contractTxData.VmVersion)));
            bytes.AddRange(PrefixLength(BitConverter.GetBytes(contractTxData.GasPrice)));
            bytes.AddRange(PrefixLength(BitConverter.GetBytes(contractTxData.GasLimit)));

            if (contractTxData.OpCodeType == (byte)ScOpcodeType.OP_CALLCONTRACT)
            {
                bytes.AddRange(PrefixLength(contractTxData.ContractAddress.ToBytes()));
                bytes.AddRange(PrefixLength(Encoding.UTF8.GetBytes(contractTxData.MethodName)));
            }

            if (contractTxData.OpCodeType == (byte)ScOpcodeType.OP_CREATECONTRACT)
            {
                bytes.AddRange(PrefixLength(contractTxData.ContractExecutionCode));
            }

            if (contractTxData.MethodParameters != null && contractTxData.MethodParameters.Any())
            {
                bytes.AddRange(PrefixLength(this.MethodParamSerializer.Serialize(contractTxData.MethodParameters)));
            }
            else
            {
                bytes.AddRange(BitConverter.GetBytes(0));
            }

            return(bytes.ToArray());
        }
Example #4
0
 private void SerializePrefix(byte[] bytes, ContractTxData contractTxData)
 {
     byte[] vmVersion = this.primitiveSerializer.Serialize(contractTxData.VmVersion);
     byte[] gasPrice  = this.primitiveSerializer.Serialize(contractTxData.GasPrice);
     byte[] gasLimit  = this.primitiveSerializer.Serialize(contractTxData.GasLimit.Value);
     bytes[0] = contractTxData.OpCodeType;
     vmVersion.CopyTo(bytes, OpcodeSize);
     gasPrice.CopyTo(bytes, OpcodeSize + VmVersionSize);
     gasLimit.CopyTo(bytes, OpcodeSize + VmVersionSize + GasPriceSize);
 }
Example #5
0
        private Result <ContractTxData> SerializeCreateContract(byte[] smartContractBytes, int vmVersion, ulong gasPrice, Gas gasLimit)
        {
            var remaining = smartContractBytes.Slice(PrefixSize, (uint)(smartContractBytes.Length - PrefixSize));

            IList <byte[]> decodedParams = RLPDecode(remaining);

            var contractExecutionCode = this.primitiveSerializer.Deserialize <byte[]>(decodedParams[0]);
            var methodParameters      = this.DeserializeMethodParameters(decodedParams[1]);

            var callData = new ContractTxData(vmVersion, gasPrice, gasLimit, contractExecutionCode, methodParameters);

            return(Result.Ok(callData));
        }
Example #6
0
        private Result <ContractTxData> SerializeCallContract(byte[] smartContractBytes, int vmVersion, ulong gasPrice, Gas gasLimit)
        {
            var contractAddressBytes = smartContractBytes.Slice(PrefixSize, AddressSize);
            var contractAddress      = new uint160(contractAddressBytes);

            var remaining = smartContractBytes.Slice(CallContractPrefixSize,
                                                     (uint)(smartContractBytes.Length - CallContractPrefixSize));

            IList <byte[]> decodedParams = RLPDecode(remaining);

            var methodName       = this.primitiveSerializer.Deserialize <string>(decodedParams[0]);
            var methodParameters = this.DeserializeMethodParameters(decodedParams[1]);
            var callData         = new ContractTxData(vmVersion, gasPrice, gasLimit, contractAddress, methodName, methodParameters);

            return(Result.Ok(callData));
        }
Example #7
0
        private byte[] SerializeCreateContract(ContractTxData contractTxData)
        {
            var rlpBytes = new List <byte[]>();

            rlpBytes.Add(contractTxData.ContractExecutionCode);

            this.AddMethodParams(rlpBytes, contractTxData.MethodParameters);

            var encoded = RLP.EncodeList(rlpBytes.Select(RLP.EncodeElement).ToArray());

            var bytes = new byte[PrefixSize + encoded.Length];

            this.SerializePrefix(bytes, contractTxData);

            encoded.CopyTo(bytes, PrefixSize);

            return(bytes);
        }
        public Result <ContractTxData> Deserialize(byte[] smartContractBytes)
        {
            try
            {
                var byteCursor = 1;
                var takeLength = 0;

                var type = smartContractBytes[0];

                var vmVersion = Deserialize <int>(smartContractBytes, ref byteCursor, ref takeLength);
                var gasPrice  = (Gas)Deserialize <ulong>(smartContractBytes, ref byteCursor, ref takeLength);
                var gasLimit  = (Gas)Deserialize <ulong>(smartContractBytes, ref byteCursor, ref takeLength);

                if (IsCallContract(type))
                {
                    var contractAddress     = Deserialize <uint160>(smartContractBytes, ref byteCursor, ref takeLength);
                    var methodName          = Deserialize <string>(smartContractBytes, ref byteCursor, ref takeLength);
                    var methodParametersRaw = Deserialize <string>(smartContractBytes, ref byteCursor, ref takeLength);

                    var methodParameters = this.DeserializeMethodParameters(methodParametersRaw);

                    var callData = new ContractTxData(vmVersion, gasPrice, gasLimit, contractAddress, methodName, methodParametersRaw, methodParameters);
                    return(Result.Ok(callData));
                }

                if (IsCreateContract(type))
                {
                    var contractExecutionCode = Deserialize <byte[]>(smartContractBytes, ref byteCursor, ref takeLength);
                    var methodParametersRaw   = Deserialize <string>(smartContractBytes, ref byteCursor, ref takeLength);

                    var methodParameters = this.DeserializeMethodParameters(methodParametersRaw);

                    var callData = new ContractTxData(vmVersion, gasPrice, gasLimit, contractExecutionCode, methodParametersRaw, methodParameters);
                    return(Result.Ok(callData));
                }
            }
            catch (Exception e)
            {
                // TODO: Avoid this catch all exceptions
                return(Result.Fail <ContractTxData>("Error deserializing calldata. " + e.Message));
            }

            return(Result.Fail <ContractTxData>("Error deserializing calldata. Incorrect first byte."));
        }
Example #9
0
        private byte[] SerializeCallContract(ContractTxData contractTxData)
        {
            var rlpBytes = new List <byte[]>();

            rlpBytes.Add(this.primitiveSerializer.Serialize(contractTxData.MethodName));

            this.AddMethodParams(rlpBytes, contractTxData.MethodParameters);

            var encoded = RLP.EncodeList(rlpBytes.Select(RLP.EncodeElement).ToArray());

            var bytes = new byte[CallContractPrefixSize + encoded.Length];

            this.SerializePrefix(bytes, contractTxData);

            contractTxData.ContractAddress.ToBytes().CopyTo(bytes, PrefixSize);

            encoded.CopyTo(bytes, CallContractPrefixSize);

            return(bytes);
        }
        /// <summary>
        /// Instantiates a <see cref="ScOpcodeType.OP_CREATECONTRACT"/> smart contract carrier.
        /// </summary>
        public static ContractCarrier CreateContract(int vmVersion, byte[] contractExecutionCode, ulong gasPrice,
                                                     Gas gasLimit, string[] methodParameters = null)
        {
            if (contractExecutionCode == null)
            {
                throw new SmartContractCarrierException(nameof(contractExecutionCode) + " is null");
            }

            var    serializer   = new MethodParameterSerializer();
            string methodParams = GetMethodParams(serializer, methodParameters);

            var callData = new ContractTxData(vmVersion, gasPrice, gasLimit, contractExecutionCode, methodParams);

            var carrier = new ContractCarrier(new MethodParameterSerializer());

            carrier.ContractTxData = callData;

            if (!string.IsNullOrWhiteSpace(methodParams))
            {
                carrier.MethodParameters = serializer.ToObjects(methodParams);
            }
            return(carrier);
        }
Example #11
0
        public ISmartContractExecutionResult Execute(ISmartContractTransactionContext transactionContext)
        {
            this.logger.LogTrace("()");

            // Deserialization can't fail because this has already been through SmartContractFormatRule.
            Result <ContractTxData> callDataDeserializationResult = this.serializer.Deserialize(transactionContext.ScriptPubKey.ToBytes());
            ContractTxData          callData = callDataDeserializationResult.Value;

            var gasMeter = new GasMeter(callData.GasLimit);

            gasMeter.Spend((Gas)GasPriceList.BaseCost);

            var context = new TransactionContext(
                transactionContext.TransactionHash,
                transactionContext.BlockHeight,
                transactionContext.CoinbaseAddress,
                transactionContext.Sender,
                transactionContext.TxOutValue);

            var creation = IsCreateContract(callData);

            VmExecutionResult result = creation
                ? this.vm.Create(gasMeter, this.stateSnapshot, callData, context)
                : this.vm.ExecuteMethod(gasMeter, this.stateSnapshot, callData, context);

            var revert = result.ExecutionException != null;

            Transaction internalTransaction = this.transferProcessor.Process(
                this.stateSnapshot,
                creation ? result.NewContractAddress : callData.ContractAddress,
                transactionContext,
                result.InternalTransfers,
                revert);

            (Money fee, List <TxOut> refundTxOuts) = this.refundProcessor.Process(
                callData,
                transactionContext.MempoolFee,
                transactionContext.Sender,
                result.GasConsumed,
                result.ExecutionException);

            var executionResult = new SmartContractExecutionResult
            {
                NewContractAddress  = !revert && creation ? result.NewContractAddress : null,
                Exception           = result.ExecutionException,
                GasConsumed         = result.GasConsumed,
                Return              = result.Result,
                InternalTransaction = internalTransaction,
                Fee     = fee,
                Refunds = refundTxOuts,
                Logs    = result.RawLogs.ToLogs(this.contractPrimitiveSerializer)
            };

            if (revert)
            {
                this.logger.LogTrace("(-)[CONTRACT_EXECUTION_FAILED]");

                this.stateSnapshot.Rollback();
            }
            else
            {
                this.logger.LogTrace("(-)[CONTRACT_EXECUTION_SUCCEEDED]");

                this.stateSnapshot.Commit();
            }

            return(executionResult);
        }
Example #12
0
 private static bool IsCreateContract(ContractTxData contractTxData)
 {
     return(contractTxData.OpCodeType == (byte)ScOpcodeType.OP_CREATECONTRACT);
 }
Example #13
0
 public byte[] Serialize(ContractTxData contractTxData)
 {
     return(IsCallContract(contractTxData.OpCodeType)
         ? this.SerializeCallContract(contractTxData)
         : this.SerializeCreateContract(contractTxData));
 }
Example #14
0
        public IContractExecutionResult Execute(IContractTransactionContext transactionContext)
        {
            // Deserialization can't fail because this has already been through SmartContractFormatRule.
            Result <ContractTxData> callDataDeserializationResult = this.serializer.Deserialize(transactionContext.Data);
            ContractTxData          callData = callDataDeserializationResult.Value;

            bool creation = callData.IsCreateContract;

            var block = new Block(
                transactionContext.BlockHeight,
                transactionContext.CoinbaseAddress.ToAddress(this.network)
                );

            IState state = this.stateFactory.Create(
                this.stateRoot,
                block,
                transactionContext.TxOutValue,
                transactionContext.TransactionHash);

            StateTransitionResult result;
            IState newState = state.Snapshot();

            if (creation)
            {
                var message = new ExternalCreateMessage(
                    transactionContext.Sender,
                    transactionContext.TxOutValue,
                    callData.GasLimit,
                    callData.ContractExecutionCode,
                    callData.MethodParameters
                    );


                result = this.stateProcessor.Apply(newState, message);
            }
            else
            {
                var message = new ExternalCallMessage(
                    callData.ContractAddress,
                    transactionContext.Sender,
                    transactionContext.TxOutValue,
                    callData.GasLimit,
                    new MethodCall(callData.MethodName, callData.MethodParameters)
                    );

                result = this.stateProcessor.Apply(newState, message);
            }

            if (result.IsSuccess)
            {
                state.TransitionTo(newState);
            }

            bool revert = !result.IsSuccess;

            Transaction internalTransaction = this.transferProcessor.Process(
                this.stateRoot,
                result.Success?.ContractAddress,
                transactionContext,
                state.InternalTransfers,
                revert);

            bool outOfGas = result.IsFailure && result.Error.Kind == StateTransitionErrorKind.OutOfGas;

            (Money fee, TxOut refundTxOut) = this.refundProcessor.Process(
                callData,
                transactionContext.MempoolFee,
                transactionContext.Sender,
                result.GasConsumed,
                outOfGas);

            var executionResult = new SmartContractExecutionResult
            {
                To = !callData.IsCreateContract ? callData.ContractAddress : null,
                NewContractAddress  = !revert && creation ? result.Success?.ContractAddress : null,
                ErrorMessage        = result.Error?.GetErrorMessage(),
                Revert              = revert,
                GasConsumed         = result.GasConsumed,
                Return              = result.Success?.ExecutionResult,
                InternalTransaction = internalTransaction,
                Fee    = fee,
                Refund = refundTxOut,
                Logs   = state.GetLogs(this.contractPrimitiveSerializer)
            };

            return(executionResult);
        }