Пример #1
0
        public override void Execute()
        {
            // Obtain our offsets and sizes for the copy.
            BigInteger memoryOffset = Stack.Pop();
            BigInteger dataOffset   = Stack.Pop();
            BigInteger dataSize     = Stack.Pop();

            // This is considered a copy operation, so we charge for the size of the data.
            GasState.Deduct(GasDefinitions.GetMemoryCopyCost(Version, dataSize));

            // Read our data from the code segment (if our offset is wrong or we hit the end of the array, the rest should be zeroes).
            byte[] data   = new byte[(int)dataSize];
            int    length = data.Length;

            if (dataOffset > EVM.Code.Length)
            {
                dataOffset = 0;
                length     = 0;
            }
            else if (dataOffset + length > EVM.Code.Length)
            {
                length = EVM.Code.Length - (int)dataOffset;
            }

            // Copy to the memory location.
            EVM.Code.Slice((int)dataOffset, length).CopyTo(data);

            // Write the data to our given memory offset.
            Memory.Write((long)memoryOffset, data);
        }
Пример #2
0
        public override void Execute()
        {
            // Obtain our offsets and sizes for the copy.
            BigInteger memoryOffset = Stack.Pop();
            BigInteger dataOffset   = Stack.Pop();
            BigInteger dataSize     = Stack.Pop();

            // This is considered a copy operation, so we charge for the size of the data.
            GasState.Deduct(GasDefinitions.GetMemoryCopyCost(Version, dataSize));

            // If we aren't copying anything, we can stop.
            if (dataOffset + dataSize == 0)
            {
                return;
            }

            // Check we have a return result, and check it's bounds.
            if (ExecutionState.LastCallResult?.ReturnData == null)
            {
                throw new EVMException($"{Opcode.ToString()} tried to copy return data from last call, but no last return data exists.");
            }
            else if (dataOffset + dataSize > (ExecutionState.LastCallResult?.ReturnData.Length ?? 0))
            {
                throw new EVMException($"{Opcode.ToString()} tried to copy return data past the end.");
            }
            else
            {
                // Otherwise we write our data we wish to copy to memory.
                Memory.Write((long)memoryOffset, ExecutionState.LastCallResult.ReturnData.Slice((int)dataOffset, (int)(dataSize)).ToArray());
            }
        }
Пример #3
0
        public override void Execute()
        {
            // Obtain the values for our call value, and call data memory.
            BigInteger value            = Stack.Pop();
            BigInteger inputMemoryStart = Stack.Pop();
            BigInteger inputMemorySize  = Stack.Pop();

            // We'll want to charge for memory expansion first
            Memory.ExpandStream(inputMemoryStart, inputMemorySize);

            // If we're in a static context, we can't self destruct
            if (Message.IsStatic)
            {
                throw new EVMException($"{Opcode.ToString()} instruction cannot execute in a static context!");
            }

            // Verify we have enough balance and call depth hasn't exceeded the maximum.
            if (EVM.State.GetBalance(Message.To) >= value && Message.Depth < EVMDefinitions.MAX_CALL_DEPTH)
            {
                // Obtain our call information.
                byte[]     callData     = Memory.ReadBytes((long)inputMemoryStart, (int)inputMemorySize);
                BigInteger innerCallGas = GasState.Gas;
                if (Version >= EthereumRelease.TangerineWhistle)
                {
                    innerCallGas = GasDefinitions.GetMaxCallGas(innerCallGas);
                }

                // Create our message
                EVMMessage         message       = new EVMMessage(Message.To, Address.ZERO_ADDRESS, value, innerCallGas, callData, Message.Depth + 1, Address.ZERO_ADDRESS, true, Message.IsStatic);
                EVMExecutionResult innerVMResult = MeadowEVM.CreateContract(EVM.State, message);
                if (innerVMResult.Succeeded)
                {
                    // Push our resulting address onto the stack.
                    Stack.Push(BigIntegerConverter.GetBigInteger(innerVMResult.ReturnData.ToArray()));
                    EVM.ExecutionState.LastCallResult = null;
                }
                else
                {
                    // We failed, push our fail value and put the last call data in place.
                    Stack.Push(0);
                    ExecutionState.LastCallResult = innerVMResult;
                }
            }
            else
            {
                // We didn't have a sufficient balance or call depth so we push nothing to the stack. We push 0 (fail)
                Stack.Push(0);

                // Set our last call result as null.
                ExecutionState.LastCallResult = null;
            }
        }
Пример #4
0
        private void Step()
        {
            // Verify we're not at the end of the stream
            if (ExecutionState.PC >= Code.Length)
            {
                // If we reached the end, exit with the remainder of the gas and a success status.
                ExecutionState.Result = new EVMExecutionResult(this, null, true);
                return;
            }

            // Set our position back to the start of the instruction, grab our opcode and verify it.
            InstructionOpcode opcode = (InstructionOpcode)Code.Span[(int)ExecutionState.PC];

            // Obtain our base cost for this opcode. If we fail to, the instruction isn't implemented yet.
            uint?instructionBaseGasCost = GasDefinitions.GetInstructionBaseGasCost(State.Configuration.Version, opcode);

            if (instructionBaseGasCost == null)
            {
                throw new EVMException($"Invalid opcode {opcode.ToString()} read when executing!");
            }

            // If we just jumped, then this next opcode should be a JUMPDEST.
            if (ExecutionState.JumpedLastInstruction && opcode != InstructionOpcode.JUMPDEST)
            {
                throw new EVMException($"Invalid jump to offset {ExecutionState.PC} in code!");
            }

            // Obtain our instruction implementation for this opcode
            var             opcodeDescriptor = opcode.GetDescriptor();
            InstructionBase instruction      = opcodeDescriptor.GetInstructionImplementation(this);

            // Record our code coverage for this execution.
            CoverageMap?.RecordExecution(instruction.Offset, (ExecutionState.PC - instruction.Offset));

            // Record our instruction execution tracing
            if (State.Configuration.DebugConfiguration.IsTracing)
            {
                State.Configuration.DebugConfiguration.ExecutionTrace?.RecordExecution(this, instruction, GasState.Gas, (BigInteger)instructionBaseGasCost);
            }

            // Deduct base gas cost
            GasState.Deduct((BigInteger)instructionBaseGasCost);

            // Debug: Print out instruction execution information.
            //if (opcode == InstructionOpcode.JUMPDEST)
            //    Console.WriteLine($"\r\n---------------------------------------------------------------\r\n");
            //Console.WriteLine($"0x{instruction.Offset.ToString("X4")}: {instruction}");
            //Console.WriteLine($"Stack: {ExecutionState.Stack}");

            // Execute the instruction
            instruction.Execute();
        }
Пример #5
0
        public void TestBaseGasCostUpdates()
        {
            // Make static assertions about updates made during Tangerine Whistle release.
            EthereumRelease[] releases = (EthereumRelease[])Enum.GetValues(typeof(EthereumRelease));
            foreach (EthereumRelease release in releases)
            {
                if (release < EthereumRelease.TangerineWhistle)
                {
                    Assert.Equal <uint>(20, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.BALANCE));
                    Assert.Equal <uint>(20, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.EXTCODESIZE));
                    Assert.Equal <uint>(20, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.EXTCODECOPY));
                    Assert.Equal <uint>(50, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.SLOAD));
                    Assert.Equal <uint>(40, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.CALL));
                    Assert.Equal <uint>(40, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.CALLCODE));

                    // DELEGATE CALL WAS INTRODUCED IN HOMESTEAD WITH 40 BASE GAS, NULL BEFORE.
                    if (release >= EthereumRelease.Homestead)
                    {
                        Assert.Equal <uint>(40, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.DELEGATECALL));
                    }
                    else
                    {
                        Assert.Null(GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.DELEGATECALL));
                    }

                    // STATIC CALL WAS INTRODUCED IN BYZANTIUM WITH 700 BASE GAS, NULL BEFORE
                    if (release >= EthereumRelease.Byzantium)
                    {
                        Assert.Equal <uint>(700, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.STATICCALL));
                    }
                    else
                    {
                        Assert.Null(GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.STATICCALL));
                    }

                    Assert.Equal <uint>(0, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.SELFDESTRUCT));
                }
                else
                {
                    Assert.Equal <uint>(400, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.BALANCE));
                    Assert.Equal <uint>(700, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.EXTCODESIZE));
                    Assert.Equal <uint>(700, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.EXTCODECOPY));
                    Assert.Equal <uint>(200, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.SLOAD));
                    Assert.Equal <uint>(700, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.CALL));
                    Assert.Equal <uint>(700, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.CALLCODE));
                    Assert.Equal <uint>(700, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.DELEGATECALL));
                    Assert.Equal <uint>(5000, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.SELFDESTRUCT));
                }
            }
        }
Пример #6
0
        /// <summary>
        /// Expands the memory stream if needed (and charges gas) to accomodate for an operation to occur at the given address with a given size.
        /// </summary>
        /// <param name="address">The address where data is presumed to be read or written.</param>
        /// <param name="size">The size of the data presumed to be read or written.</param>
        public void ExpandStream(BigInteger address, BigInteger size)
        {
            // If our address space doesn't extend to handle data in these bounds, expand memory.
            if (address + size > Length)
            {
                // Memory is allocated such that it is aligned to the size of a WORD.
                BigInteger currentWordCount = EVMDefinitions.GetWordCount(Length);
                BigInteger targetWordCount  = EVMDefinitions.GetWordCount(address + size);

                // Calculate cost of gas for expanding our array.
                BigInteger currentMemoryCost = GasDefinitions.GetMemoryAllocationCost(EVM.Version, currentWordCount);
                BigInteger targetMemoryCost  = GasDefinitions.GetMemoryAllocationCost(EVM.Version, targetWordCount);
                BigInteger costDelta         = targetMemoryCost - currentMemoryCost;

                // Deduct the difference in cost for expanding our memory.
                EVM.GasState.Deduct(costDelta);

                // Set the size of our stream
                _internalBufferStream.SetLength((long)targetWordCount * EVMDefinitions.WORD_SIZE);

                // Update our change count
                ChangeCount++;
            }
        }
Пример #7
0
        public override void Execute()
        {
            // Obtain all of our values for the call (the value variable is only used in some calls, and is zero otherwise)
            BigInteger gas = Stack.Pop(); // The base amount of gas we allocate to the call.
            Address    to  = Stack.Pop(); // The address we're making the call to.

            BigInteger value = 0;

            if (Opcode == InstructionOpcode.CALL || Opcode == InstructionOpcode.CALLCODE)
            {
                value = Stack.Pop();
            }

            // Obtain the values for where our input memory comes from, and where our output memory will go to.
            BigInteger inputMemoryStart  = Stack.Pop();
            BigInteger inputMemorySize   = Stack.Pop();
            BigInteger outputMemoryStart = Stack.Pop();
            BigInteger outputMemorySize  = Stack.Pop();

            // CALL opcode can only make static calls with a zero value.
            if (Opcode == InstructionOpcode.CALL && Message.IsStatic && value != 0)
            {
                throw new EVMException($"Cannot use opcode {Opcode.ToString()} in a static context call with any value but zero.");
            }

            // Gas: Pre-Expand Memory (since it should be charged for expansion, then the call should be made, then written to)
            Memory.ExpandStream(inputMemoryStart, inputMemorySize);
            Memory.ExpandStream(outputMemoryStart, outputMemorySize);

            // Gas: Calculate extra gas costs based off of forks and what kind of call this is.
            BigInteger extraGasCost = 0;

            // Gas: If this is a call and the account doesn't exist
            if (Opcode == InstructionOpcode.CALL && !EVM.State.ContainsAccount(to))
            {
                // If the value is above zero (or if we're pre-spurious dragon) we charge for calling a new account.
                if (value > 0 || Version < EthereumRelease.SpuriousDragon)
                {
                    extraGasCost = GasDefinitions.GAS_CALL_NEW_ACCOUNT;
                }
            }

            // If we are transferring a value, we charge gas
            if (value > 0)
            {
                extraGasCost += GasDefinitions.GAS_CALL_VALUE;
            }

            // Tangerine whistle introduces new inner call gas limits
            // Source: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
            if (Version < EthereumRelease.TangerineWhistle)
            {
                // Prior to tangerine whistle, we need the provided gas + extra gas available
                GasState.Check(gas + extraGasCost);
            }
            else
            {
                // After tangerine whistle, we check that the desired gas amount for the call doesn't exceed our calculated max call gas (or else it's capped).
                GasState.Check(extraGasCost);
                gas = BigInteger.Min(gas, GasDefinitions.GetMaxCallGas(GasState.Gas - extraGasCost));
            }

            // Define how much gas our inner message can take.
            BigInteger innerCallGas = gas;

            if (value > 0)
            {
                innerCallGas += GasDefinitions.GAS_CALL_VALUE_STIPEND;
            }

            // Verify we have enough balance and call depth hasn't exceeded the maximum.
            if (EVM.State.GetBalance(Message.To) >= value && Message.Depth < EVMDefinitions.MAX_CALL_DEPTH)
            {
                // We're going to make an inner call, so we charge our gas and extra gas.
                GasState.Deduct(gas + extraGasCost);

                // Obtain our call data.
                byte[] callData = Memory.ReadBytes((long)inputMemoryStart, (int)inputMemorySize);

                // Create our message
                EVMMessage innerMessage = null;
                switch (Opcode)
                {
                case InstructionOpcode.CALL:
                    innerMessage = new EVMMessage(Message.To, to, value, innerCallGas, callData, Message.Depth + 1, to, true, Message.IsStatic);
                    break;

                case InstructionOpcode.DELEGATECALL:
                    innerMessage = new EVMMessage(Message.Sender, Message.To, Message.Value, innerCallGas, callData, Message.Depth + 1, to, false, Message.IsStatic);
                    break;

                case InstructionOpcode.STATICCALL:
                    innerMessage = new EVMMessage(Message.To, to, value, innerCallGas, callData, Message.Depth + 1, to, true, true);
                    break;

                case InstructionOpcode.CALLCODE:
                    innerMessage = new EVMMessage(Message.To, Message.To, value, innerCallGas, callData, Message.Depth + 1, to, true, Message.IsStatic);
                    break;
                }

                // Execute our message in an inner VM.
                EVMExecutionResult innerVMResult = MeadowEVM.Execute(EVM.State, innerMessage);

                // Refund our remaining gas that the inner VM didn't use.
                GasState.Refund(innerVMResult.RemainingGas);

                // Set our last call results
                ExecutionState.LastCallResult = innerVMResult;

                // Push a status indicating whether execution had succeeded without reverting changes.
                if (!innerVMResult.Succeeded)
                {
                    Stack.Push(0);
                }
                else
                {
                    Stack.Push(1);
                }

                // Determine how much we want to copy out.
                int returnCopyLength = Math.Min(ExecutionState.LastCallResult?.ReturnData.Length ?? 0, (int)outputMemorySize);
                if (returnCopyLength == 0 || ExecutionState.LastCallResult?.ReturnData == null)
                {
                    return;
                }

                // Copy our data out
                Memory.Write((long)outputMemoryStart, ExecutionState.LastCallResult.ReturnData.ToArray());
            }
            else
            {
                // We didn't have a sufficient balance or call depth so we push nothing to the stack. We push 0 (fail)
                Stack.Push(0);

                // Set our last call result as null.
                ExecutionState.LastCallResult = null;

                // Since we couldn't make an inner message call, we charge all the other extra charges, but not the inner message call cost. (Note: inner call gas comes from gas, so we put it here to offset potential extra cost from stipend)
                GasState.Deduct(gas + extraGasCost - innerCallGas);
            }
        }