コード例 #1
0
        public void SmartContracts_GasPrice_TestStorageOperationPrices()
        {
            byte[] key   = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            byte[] value = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            Gas    cost  = (Gas)(GasPriceList.StorageGasCost * key.Length + GasPriceList.StorageGasCost * value.Length);

            Assert.Equal(cost, GasPriceList.StorageOperationCost(key, value));
        }
コード例 #2
0
ファイル: GasPriceListTests.cs プロジェクト: georgepinca/src
        public void SmartContracts_GasPrice_TestStorageRetrievalPrices()
        {
            byte[] key   = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            byte[] value = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            var    cost  = (RuntimeObserver.Gas)(GasPriceList.StoragePerByteRetrievedGasCost * key.Length + GasPriceList.StoragePerByteRetrievedGasCost * value.Length);

            Assert.Equal(cost, GasPriceList.StorageRetrieveOperationCost(key, value));
        }
コード例 #3
0
        public void Log<T>(ISmartContractState smartContractState, T toLog) where T : struct 
        {
            var rawLog = new RawLog(smartContractState.Message.ContractAddress.ToUint160(), toLog);
            Log log = rawLog.ToLog(this.serializer);
            this.gasMeter.Spend(GasPriceList.LogOperationCost(log.Topics, log.Data));

            // TODO: This is inefficient, it is deserializing the log more than once.
            this.logger.Log<T>(smartContractState, toLog);
        }
コード例 #4
0
        public void SmartContracts_GasPrice_TestMethodCallPrices()
        {
            var module = typeof(object).Module.FullyQualifiedName;

            var moduleDefinition = ModuleDefinition.ReadModule(module);

            TypeDefinition   type   = moduleDefinition.Types.First(t => t.FullName.Contains("DateTime"));
            MethodDefinition method = type.Methods.First(m => m.FullName.Contains("Parse"));

            Assert.Equal((Gas)GasPriceList.MethodCallGasCost, GasPriceList.MethodCallCost(method));
        }
コード例 #5
0
        public void SmartContracts_GasPrice_TestLogPrices()
        {
            List <byte[]> topics = new List <byte[]>
            {
                new byte[] { 1, 2, 3, 4, 5, 6 }
            };

            byte[] data = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            var    cost = (Gas)(GasPriceList.LogPerTopicByteCost * topics[0].Length + GasPriceList.LogPerByteCost * data.Length);

            Assert.Equal(cost, GasPriceList.LogOperationCost(topics, data));
        }
コード例 #6
0
        public void SmartContracts_GasPrice_TestOperandInstructionPrices()
        {
            var priceList = new Dictionary <Instruction, ulong>
            {
                { Instruction.Create(OpCodes.Ldstr, "test"), 1 }
            };

            foreach (KeyValuePair <Instruction, ulong> kvp in priceList)
            {
                Instruction instruction = kvp.Key;
                ulong       price       = kvp.Value;

                Assert.Equal(price, GasPriceList.InstructionOperationCost(instruction));
            }
        }
コード例 #7
0
        /// <summary>
        /// Total gas cost to execute the instructions in this segment.
        /// </summary>
        public Gas CalculateGasCost()
        {
            Gas gasTally = (Gas)0;

            foreach (Instruction instruction in this.Instructions)
            {
                Gas instructionCost = GasPriceList.InstructionOperationCost(instruction);
                gasTally = (Gas)(gasTally + instructionCost);

                if (instruction.IsMethodCall())
                {
                    var methodToCall = (MethodReference)instruction.Operand;

                    // If it's a method outside this contract then we will add some cost.
                    if (this.methodDefinition.DeclaringType != methodToCall.DeclaringType)
                    {
                        Gas methodCallCost = GasPriceList.MethodCallCost(methodToCall);
                        gasTally = (Gas)(gasTally + methodCallCost);
                    }
                }
            }

            return(gasTally);
        }
コード例 #8
0
        /// <inheritdoc />
        public void Rewrite(MethodDefinition methodDefinition, ILProcessor il, ObserverRewriterContext context)
        {
            List <Instruction> branches  = methodDefinition.Body.Instructions.Where(x => BranchingOps.Contains(x.OpCode)).ToList();
            List <Instruction> branchTos = branches.Select(x => (Instruction)x.Operand).ToList();

            Gas gasTally = Gas.None;

            Dictionary <Instruction, Gas> gasToSpendForSegment = new Dictionary <Instruction, Gas>();

            // To account  for variable load at start.
            int         position            = 2;
            Instruction currentSegmentStart = methodDefinition.Body.Instructions[position];

            while (position < methodDefinition.Body.Instructions.Count)
            {
                Instruction instruction = methodDefinition.Body.Instructions[position];

                Gas instructionCost = GasPriceList.InstructionOperationCost(instruction);

                // is the end of a segment. Include the current instruction in the count.
                if (branches.Contains(instruction))
                {
                    gasTally = (Gas)(gasTally + instructionCost);
                    gasToSpendForSegment.Add(currentSegmentStart, gasTally);
                    gasTally = Gas.None;
                    position++;
                    if (position == methodDefinition.Body.Instructions.Count)
                    {
                        break;
                    }
                    currentSegmentStart = methodDefinition.Body.Instructions[position];
                }
                // is the start of a new segment. Don't include the current instruction in count.
                else if (branchTos.Contains(instruction) && instruction != currentSegmentStart)
                {
                    gasToSpendForSegment.Add(currentSegmentStart, gasTally);
                    gasTally            = Gas.None;
                    currentSegmentStart = instruction;
                    position++;
                }
                // is a call to another method
                else if (CallingOps.Contains(instruction.OpCode))
                {
                    var methodToCall = (MethodReference)instruction.Operand;

                    // If it's a method inside this contract then the gas will be injected no worries.
                    if (methodToCall.DeclaringType == methodDefinition.DeclaringType)
                    {
                        position++;
                        gasTally = (Gas)(gasTally + instructionCost);
                    }
                    // If it's a method outside this contract then we will need to get some average in future.
                    else
                    {
                        Gas methodCallCost = GasPriceList.MethodCallCost(methodToCall);

                        position++;
                        gasTally = (Gas)(gasTally + instructionCost + methodCallCost);
                    }
                }
                // any other instruction. just increase counter.
                else
                {
                    position++;
                    gasTally = (Gas)(gasTally + instructionCost);
                }
            }

            if (!gasToSpendForSegment.ContainsKey(currentSegmentStart))
            {
                gasToSpendForSegment.Add(currentSegmentStart, gasTally);
            }

            foreach (Instruction instruction in gasToSpendForSegment.Keys)
            {
                Instruction injectAfterInstruction = instruction;

                // If it's a constructor we need to skip the first 3 instructions.
                // These will always be invoking the base constructor
                // ldarg.0
                // ldarg.0
                // call SmartContract::ctor
                if (methodDefinition.IsConstructor)
                {
                    injectAfterInstruction = instruction.Next.Next.Next;
                }

                AddSpendGasMethodBeforeInstruction(methodDefinition, context.Observer, context.ObserverVariable, injectAfterInstruction, gasToSpendForSegment[instruction]);
            }
        }
コード例 #9
0
ファイル: ObserverRewriter.cs プロジェクト: emilm/city-chain
        private void RewriteMethod(MethodDefinition methodDefinition, ObserverReferences observer)
        {
            if (methodDefinition.DeclaringType == observer.InstanceField.DeclaringType)
            {
                return; // don't inject on our injected type.
            }
            if (!methodDefinition.HasBody || methodDefinition.Body.Instructions.Count == 0)
            {
                return; // don't inject on method without a Body
            }
            List <Instruction> branches  = methodDefinition.Body.Instructions.Where(x => BranchingOps.Contains(x.OpCode)).ToList();
            List <Instruction> branchTos = branches.Select(x => (Instruction)x.Operand).ToList();

            Instruction currentSegmentStart = methodDefinition.Body.Instructions.FirstOrDefault();
            Gas         gasTally            = Gas.None;

            Dictionary <Instruction, Gas> gasToSpendForSegment = new Dictionary <Instruction, Gas>();

            // Inject observer instance to method.
            ILProcessor il = methodDefinition.Body.GetILProcessor();
            var         observerVariable = new VariableDefinition(observer.InstanceField.FieldType);

            il.Body.Variables.Add(observerVariable);
            Instruction start = methodDefinition.Body.Instructions[0];

            il.InsertBefore(start, il.Create(OpCodes.Ldsfld, observer.InstanceField));
            il.InsertBefore(start, il.CreateStlocBest(observerVariable));

            // Start at 2 because of the instructions we just added.
            int position = 2;

            while (position < methodDefinition.Body.Instructions.Count)
            {
                Instruction instruction = methodDefinition.Body.Instructions[position];

                Gas instructionCost = GasPriceList.InstructionOperationCost(instruction);

                // is the end of a segment. Include the current instruction in the count.
                if (branches.Contains(instruction))
                {
                    gasTally = (Gas)(gasTally + instructionCost);
                    gasToSpendForSegment.Add(currentSegmentStart, gasTally);
                    gasTally = Gas.None;
                    position++;
                    if (position == methodDefinition.Body.Instructions.Count)
                    {
                        break;
                    }
                    currentSegmentStart = methodDefinition.Body.Instructions[position];
                }
                // is the start of a new segment. Don't include the current instruction in count.
                else if (branchTos.Contains(instruction) && instruction != currentSegmentStart)
                {
                    gasToSpendForSegment.Add(currentSegmentStart, gasTally);
                    gasTally            = Gas.None;
                    currentSegmentStart = instruction;
                    position++;
                }
                // is a call to another method
                else if (CallingOps.Contains(instruction.OpCode))
                {
                    var methodToCall = (MethodReference)instruction.Operand;

                    // If it's a method inside this contract then the gas will be injected no worries.
                    if (methodToCall.DeclaringType == methodDefinition.DeclaringType)
                    {
                        position++;
                        gasTally = (Gas)(gasTally + instructionCost);
                    }
                    // If it's a method outside this contract then we will need to get some average in future.
                    else
                    {
                        Gas methodCallCost = GasPriceList.MethodCallCost(methodToCall);

                        position++;
                        gasTally = (Gas)(gasTally + instructionCost + methodCallCost);
                    }
                }
                // any other instruction. just increase counter.
                else
                {
                    position++;
                    gasTally = (Gas)(gasTally + instructionCost);
                }
            }

            if (!gasToSpendForSegment.ContainsKey(currentSegmentStart))
            {
                gasToSpendForSegment.Add(currentSegmentStart, gasTally);
            }

            foreach (Instruction instruction in gasToSpendForSegment.Keys)
            {
                Instruction injectAfterInstruction = instruction;

                // If it's a constructor we need to skip the first 3 instructions.
                // These will always be invoking the base constructor
                // ldarg.0
                // ldarg.0
                // call SmartContract::ctor
                if (methodDefinition.IsConstructor)
                {
                    injectAfterInstruction = instruction.Next.Next.Next;
                }

                AddSpendGasMethodBeforeInstruction(methodDefinition, observer, observerVariable, injectAfterInstruction, gasToSpendForSegment[instruction]);
            }

            foreach (Instruction instruction in branches)
            {
                var         oldReference   = (Instruction)instruction.Operand;
                Instruction newReference   = oldReference.Previous.Previous.Previous; // 3 were inserted
                Instruction newInstruction = il.Create(instruction.OpCode, newReference);
                il.Replace(instruction, newInstruction);
            }
        }