public void Step(SolidityProgram program, bool trace = false) { if (program == null) // http://solidity.readthedocs.io/en/develop/assembly.html { throw new ArgumentNullException(nameof(program)); } var solidityOpCode = SolidityOpCode.Instance(); var currentOpCode = program.GetCurrentOpCode(); var opCode = solidityOpCode.GetCode(currentOpCode); var stack = program.GetStack(); var oldMemSize = program.GetMemorySize(); long gasCost = (long)solidityOpCode.GetTier(opCode.Value); switch (opCode) { case SolidityOpCodes.STOP: gasCost = SolidityGasCost.STOP; break; case SolidityOpCodes.SUICIDE: gasCost = SolidityGasCost.SUICIDE; break; case SolidityOpCodes.SSTORE: var sstoreNewValue = stack.ElementAt(stack.Count() - 2); var sstoreOldValue = program.StorageLoad(stack.Last()); if (sstoreOldValue == null || !sstoreOldValue.IsZero()) { gasCost = SolidityGasCost.SET_SSTORE; } else if (sstoreOldValue != null && sstoreNewValue.IsZero()) { gasCost = SolidityGasCost.CLEAR_SSTORE; } else { gasCost = SolidityGasCost.RESET_SSTORE; } break; case SolidityOpCodes.SLOAD: gasCost = SolidityGasCost.SLOAD; break; case SolidityOpCodes.MSTORE: gasCost += CalcMemGas(oldMemSize, MemNeeded(stack.Last(), new DataWord(32)), 0); break; case SolidityOpCodes.MSTORE8: gasCost += CalcMemGas(oldMemSize, MemNeeded(stack.Last(), new DataWord(1)), 0); break; case SolidityOpCodes.MLOAD: gasCost += CalcMemGas(oldMemSize, MemNeeded(stack.Last(), new DataWord(32)), 0); break; case SolidityOpCodes.RETURN: case SolidityOpCodes.REVERT: gasCost = SolidityGasCost.STOP + CalcMemGas(oldMemSize, MemNeeded(stack.Last(), stack.ElementAt(stack.Count() - 2)), 0); break; case SolidityOpCodes.SHA3: gasCost = SolidityGasCost.SHA3 + CalcMemGas(oldMemSize, MemNeeded(stack.Last(), stack.ElementAt(stack.Count() - 2)), 0); var size = stack.ElementAt(stack.Count() - 2); var chunkUsed = (size.GetLongValueSafe() + 31) / 32; gasCost += chunkUsed * SolidityGasCost.SHA3_WORD; break; case SolidityOpCodes.CALLDATACOPY: case SolidityOpCodes.RETURNDATACOPY: gasCost += CalcMemGas(oldMemSize, MemNeeded(stack.Last(), stack.ElementAt(stack.Count() - 3)), stack.ElementAt(stack.Count() - 3).GetLongValueSafe()); break; case SolidityOpCodes.CODECOPY: gasCost += CalcMemGas(oldMemSize, MemNeeded(stack.Last(), stack.ElementAt(stack.Count() - 3)), stack.ElementAt(stack.Count() - 3).GetLongValueSafe()); break; case SolidityOpCodes.EXTCODESIZE: gasCost = SolidityGasCost.EXT_CODE_SIZE; break; case SolidityOpCodes.EXTCODECOPY: gasCost = SolidityGasCost.EXT_CODE_COPY + CalcMemGas(oldMemSize, MemNeeded(stack.ElementAt(stack.Count() - 2), stack.ElementAt(stack.Count() - 4)), stack.ElementAt(stack.Count() - 4).GetLongValueSafe()); break; case SolidityOpCodes.CALL: // case SolidityOpCodes.CALLCODE: case SolidityOpCodes.DELEGATECALL: case SolidityOpCodes.STATICCALL: gasCost = SolidityGasCost.CALL; var callGasWord = stack.ElementAt(stack.Count() - 1); var callAddressWord = stack.ElementAt(stack.Count() - 2); var value = DataWord.ZERO; /* * DataWord value = op.callHasValue() ? * stack.get(stack.size() - 3) : DataWord.ZERO; */ if (opCode == SolidityOpCodes.CALL) { gasCost += SolidityGasCost.NEW_ACCT_CALL; } if (!value.IsZero()) { gasCost += SolidityGasCost.VT_CALL; } break; case SolidityOpCodes.EXP: DataWord exp = stack.ElementAt(stack.Count() - 2); int bytesOccupied = exp.GetBytesOccupied(); gasCost = SolidityGasCost.EXP_GAS + SolidityGasCost.EXP_BYTE_GAS * bytesOccupied; break; } if (trace) { Trace.WriteLine("Operation " + Enum.GetName(typeof(SolidityOpCodes), opCode) + " " + program.GetPc() + " COST = " + gasCost); Trace.WriteLine("Stack: "); foreach (var s in program.GetStack()) { Trace.WriteLine(s.GetData().ToHexString()); } Trace.WriteLine("Memory: "); Trace.WriteLine(program.GetMemory().ToHexString()); } switch (opCode) { case SolidityOpCodes.PUSH1: case SolidityOpCodes.PUSH2: case SolidityOpCodes.PUSH3: case SolidityOpCodes.PUSH4: case SolidityOpCodes.PUSH5: case SolidityOpCodes.PUSH6: case SolidityOpCodes.PUSH7: case SolidityOpCodes.PUSH8: case SolidityOpCodes.PUSH9: case SolidityOpCodes.PUSH10: case SolidityOpCodes.PUSH11: case SolidityOpCodes.PUSH12: case SolidityOpCodes.PUSH13: case SolidityOpCodes.PUSH14: case SolidityOpCodes.PUSH15: case SolidityOpCodes.PUSH16: case SolidityOpCodes.PUSH17: case SolidityOpCodes.PUSH18: case SolidityOpCodes.PUSH19: case SolidityOpCodes.PUSH20: case SolidityOpCodes.PUSH21: case SolidityOpCodes.PUSH22: case SolidityOpCodes.PUSH23: case SolidityOpCodes.PUSH24: case SolidityOpCodes.PUSH25: case SolidityOpCodes.PUSH26: case SolidityOpCodes.PUSH27: case SolidityOpCodes.PUSH28: case SolidityOpCodes.PUSH29: case SolidityOpCodes.PUSH30: case SolidityOpCodes.PUSH31: case SolidityOpCodes.PUSH32: if (opCode == SolidityOpCodes.PUSH32) { string sss = ""; } program.Step(); var nPush = SizeSolidityCodes[opCode.Value]; var data = program.Sweep(nPush); program.StackPush(data); break; case SolidityOpCodes.CALLDATALOAD: var pop = program.StackPop(); var dataValue = program.GetDataValue(pop); program.StackPush(dataValue); program.Step(); break; case SolidityOpCodes.CALLDATACOPY: var memOffsetData = program.StackPop(); var dataOffsetData = program.StackPop(); var lengthData = program.StackPop(); var msgData = program.GetDataCopy(dataOffsetData, lengthData); program.SaveMemory(memOffsetData.GetIntValueSafe(), msgData.ToArray()); program.Step(); break; case SolidityOpCodes.ADDRESS: var address = program.GetOwnerAddress(); program.StackPush(address); program.Step(); break; case SolidityOpCodes.AND: var word1 = program.StackPop(); var word2 = program.StackPop(); word1.And(word2); program.StackPush(word1); program.Step(); break; case SolidityOpCodes.OR: var w1 = program.StackPop(); var w2 = program.StackPop(); w1.Or(w2); program.StackPush(w1); program.Step(); break; case SolidityOpCodes.XOR: var xorW1 = program.StackPop(); var xorW2 = program.StackPop(); xorW1.XOR(xorW2); program.StackPush(xorW1); program.Step(); break; case SolidityOpCodes.BYTE: var bw1 = program.StackPop(); var bw2 = program.StackPop(); DataWord result = new DataWord(); var vBw1 = bw1.GetValue(); if (!bw1.GetValue().Equals(BG_32)) { byte tmp = bw2.GetData()[bw1.GetInt()]; bw2.And(DataWord.ZERO); bw2.GetData()[31] = tmp; result = bw2; } program.StackPush(result); program.Step(); break; case SolidityOpCodes.ISZERO: var isZeroW1 = program.StackPop(); // var isZeroW1 = stack.Last(); if (isZeroW1.IsZero()) { isZeroW1.GetData()[31] = 1; } else { isZeroW1.And(DataWord.ZERO); } program.StackPush(isZeroW1); program.Step(); break; case SolidityOpCodes.EQ: var eqW1 = program.StackPop(); var eqW2 = program.StackPop(); if (eqW1.XOR(eqW2).IsZero()) { eqW1.And(DataWord.ZERO); eqW1.GetData()[31] = 1; } else { eqW1.And(DataWord.ZERO); } program.StackPush(eqW1); program.Step(); break; case SolidityOpCodes.GT: var gtW1 = program.StackPop(); var gtW2 = program.StackPop(); if (gtW1.GetValue().CompareTo(gtW2.GetValue()) == 1) { gtW1.And(DataWord.ZERO); gtW1.GetData()[31] = 1; } else { gtW1.And(DataWord.ZERO); } program.StackPush(gtW1); program.Step(); break; case SolidityOpCodes.SGT: var sgtW1 = program.StackPop(); var sgtW2 = program.StackPop(); if (sgtW1.GetSValue().CompareTo(sgtW2.GetSValue()) == 1) { sgtW1.And(DataWord.ZERO); sgtW1.GetData()[31] = 1; } else { sgtW1.And(DataWord.ZERO); } program.StackPush(sgtW1); program.Step(); break; case SolidityOpCodes.LT: var ltW1 = program.StackPop(); var ltW2 = program.StackPop(); if (ltW1.GetValue().CompareTo(ltW2.GetValue()) == -1) { ltW1.And(DataWord.ZERO); ltW1.GetData()[31] = 1; } else { ltW1.And(DataWord.ZERO); } program.StackPush(ltW1); program.Step(); break; case SolidityOpCodes.SLT: var sltW1 = program.StackPop(); var sltW2 = program.StackPop(); if (sltW1.GetSValue().CompareTo(sltW2.GetSValue()) == -1) { sltW1.And(DataWord.ZERO); sltW1.GetData()[31] = 1; } else { sltW1.And(DataWord.ZERO); } program.StackPush(sltW1); program.Step(); break; case SolidityOpCodes.MSTORE: var msStoreW1 = program.StackPop(); var msStoreW2 = program.StackPop(); program.SaveMemory(msStoreW1, msStoreW2); program.Step(); break; case SolidityOpCodes.MSTORE8: var addr = program.StackPop(); var value = program.StackPop(); byte[] byteVal = { value.GetData()[31] }; program.SaveMemory(addr.GetIntValueSafe(), byteVal); program.Step(); break; case SolidityOpCodes.CALLVALUE: var callValue = program.GetCallValue(); program.StackPush(callValue); program.Step(); break; case SolidityOpCodes.JUMPI: var pos = program.StackPop(); var cond = program.StackPop(); if (!cond.IsZero() && !pos.IsZero()) { int nextPC = program.VerifyJumpDest(pos); program.SetPc(nextPC); } else { program.Step(); } break; case SolidityOpCodes.JUMP: var jumpW1 = program.StackPop(); var nextPc = program.VerifyJumpDest(jumpW1); program.SetPc(nextPc); break; case SolidityOpCodes.JUMPDEST: program.Step(); break; case SolidityOpCodes.DUP1: case SolidityOpCodes.DUP2: case SolidityOpCodes.DUP3: case SolidityOpCodes.DUP4: case SolidityOpCodes.DUP5: case SolidityOpCodes.DUP6: case SolidityOpCodes.DUP7: case SolidityOpCodes.DUP8: case SolidityOpCodes.DUP9: case SolidityOpCodes.DUP10: case SolidityOpCodes.DUP11: case SolidityOpCodes.DUP12: case SolidityOpCodes.DUP13: case SolidityOpCodes.DUP14: case SolidityOpCodes.DUP15: case SolidityOpCodes.DUP16: var n = SizeSolidityCodes[opCode.Value] - SizeSolidityCodes[SolidityOpCodes.DUP1] + 1; var dup1W1 = stack[stack.Count() - n]; program.StackPush(dup1W1); program.Step(); break; case SolidityOpCodes.CODECOPY: byte[] fullCode = ByteUtil.EMPTY_BYTE_ARRAY; fullCode = program.GetCode().ToArray(); int copyDataW1 = program.StackPop().GetIntValueSafe(); int copyDataW2 = program.StackPop().GetIntValueSafe(); int copyDataW3 = program.StackPop().GetIntValueSafe(); int sizeToBeCopied = (long)copyDataW2 + copyDataW3 > fullCode.Length ? (fullCode.Length < copyDataW2 ? 0 : fullCode.Length - copyDataW2) : copyDataW3; byte[] codeCopy = new byte[copyDataW3]; if (copyDataW2 < fullCode.Length) { Array.Copy(fullCode, copyDataW2, codeCopy, 0, sizeToBeCopied); } program.SaveMemory(copyDataW1, codeCopy); program.Step(); break; case SolidityOpCodes.RETURN: case SolidityOpCodes.REVERT: int retW1 = program.StackPop().GetIntValueSafe(); int retW2 = program.StackPop().GetIntValueSafe(); byte[] hReturn = program.ChunkMemory(retW1, retW2); program.SetHReturn(hReturn); program.Step(); program.Stop(); if (opCode == SolidityOpCodes.REVERT) { program.GetResult().SetRevert(); } break; case SolidityOpCodes.SWAP1: case SolidityOpCodes.SWAP2: case SolidityOpCodes.SWAP3: case SolidityOpCodes.SWAP4: case SolidityOpCodes.SWAP5: case SolidityOpCodes.SWAP6: case SolidityOpCodes.SWAP7: case SolidityOpCodes.SWAP8: case SolidityOpCodes.SWAP9: case SolidityOpCodes.SWAP10: case SolidityOpCodes.SWAP11: case SolidityOpCodes.SWAP12: case SolidityOpCodes.SWAP13: case SolidityOpCodes.SWAP14: case SolidityOpCodes.SWAP15: case SolidityOpCodes.SWAP16: var sn = SizeSolidityCodes[opCode.Value] - SizeSolidityCodes[SolidityOpCodes.SWAP1] + 2; stack.Swap(stack.Count() - 1, stack.Count() - sn); program.Step(); break; case SolidityOpCodes.DIV: var dW1 = program.StackPop(); var dW2 = program.StackPop(); dW1.Div(dW2); program.StackPush(dW1); program.Step(); break; case SolidityOpCodes.POP: program.StackPop(); program.Step(); break; case SolidityOpCodes.MLOAD: var mloadW1 = program.StackPop(); var memoryW2 = program.LoadMemory(mloadW1); program.StackPush(memoryW2); program.Step(); break; case SolidityOpCodes.ADD: var addW1 = program.StackPop(); var addW2 = program.StackPop(); addW1.Add(addW2); program.StackPush(addW1); program.Step(); break; case SolidityOpCodes.SUB: var subW1 = program.StackPop(); var subW2 = program.StackPop(); subW1.Sub(subW2); program.StackPush(subW1); program.Step(); break; case SolidityOpCodes.MUL: var mulW1 = program.StackPop(); var mulW2 = program.StackPop(); mulW1.Mul(mulW2); program.StackPush(mulW1); program.Step(); break; case SolidityOpCodes.CALL: // CONTINUE TO DEVELOP METHOD CALL. program.StackPop(); var codeAddress = program.StackPop(); var cv = program.StackPop(); DataWord inDataOffs = program.StackPop(); DataWord inDataSize = program.StackPop(); DataWord outDataOffs = program.StackPop(); DataWord outDataSize = program.StackPop(); program.MemoryExpand(outDataOffs, outDataSize); var msg = new SolidityMessageCall(opCode.Value, null, codeAddress, cv, inDataOffs, inDataSize, outDataOffs, outDataSize); var contract = SolidityPrecompiledContract.GetContractForAddress(codeAddress); if (contract != null) { program.CallPrecompiledAddress(msg, contract); } program.Step(); break; case SolidityOpCodes.NOT: var notW1 = program.StackPop(); notW1.BNot(); program.StackPush(notW1); program.Step(); break; case SolidityOpCodes.EXP: var expWord1 = program.StackPop(); var expWord2 = program.StackPop(); expWord1.Exp(expWord2); program.StackPush(expWord1); program.Step(); break; case SolidityOpCodes.CALLDATASIZE: var dataSize = program.GetDataSize(); program.StackPush(dataSize); program.Step(); break; case SolidityOpCodes.SSTORE: var sstoreWord1 = program.StackPop(); var sstoreWord2 = program.StackPop(); var tt = sstoreWord1.GetData().ToHexString(); var tt2 = sstoreWord2.GetData().ToHexString(); program.SaveStorage(sstoreWord1, sstoreWord2); program.Step(); break; case SolidityOpCodes.STOP: program.SetHReturn(SolidityMemory.EMPTY_BYTE_ARRAY); program.Stop(); break; case SolidityOpCodes.SLOAD: var sLoadKey = program.StackPop(); var sLoadVal = program.StorageLoad(sLoadKey); program.StackPush(sLoadVal); program.Step(); break; case SolidityOpCodes.SHA3: var sha3MemOffsetData = program.StackPop(); var sha3LengthData = program.StackPop(); byte[] buffer = program.ChunkMemory(sha3MemOffsetData.GetIntValueSafe(), sha3LengthData.GetIntValueSafe()); var hash = HashFactory.Crypto.SHA3.CreateBlake256(); var shaWord = hash.ComputeBytes(buffer).GetBytes(); program.StackPush(shaWord); program.Step(); break; case SolidityOpCodes.MSIZE: int memSize = program.GetMemorySize(); var sizeW = new DataWord(memSize); program.StackPush(sizeW); program.Step(); break; case SolidityOpCodes.LOG0: case SolidityOpCodes.LOG1: case SolidityOpCodes.LOG2: case SolidityOpCodes.LOG3: case SolidityOpCodes.LOG4: var logAdr = program.GetOwnerAddress(); var logMemStart = stack.Pop(); var logMemOffset = stack.Pop(); var nTopics = SizeSolidityCodes[opCode.Value] - SizeSolidityCodes[SolidityOpCodes.LOG0]; var topics = new List <DataWord>(); for (int i = 0; i < nTopics; ++i) { DataWord topic = stack.Pop(); topics.Add(topic); } var logData = program.ChunkMemory(logMemStart.GetIntValueSafe(), logMemOffset.GetIntValueSafe()); program.GetResult().AddLogInfo(new SolidityLogInfo(topics, logData)); program.Step(); break; } }
private void Persist(Block block, WriteBatch writeBatch, bool addIntoCache = false) { if (writeBatch == null) { throw new ArgumentNullException(nameof(writeBatch)); } if (block == null) { throw new ArgumentNullException(nameof(block)); } var defaultCallValue = new DataWord(new byte[] { 0x00 }); foreach (var transaction in block.Transactions) { var smartContractTransaction = transaction as SmartContractTransaction; if (smartContractTransaction == null) { continue; } if (smartContractTransaction.To == null || !smartContractTransaction.To.Any()) // Create the contract. { var solidityVm = new SolidityVm(); var program = new SolidityProgram(smartContractTransaction.Data.ToList(), new SolidityProgramInvoke(new byte[0], new DataWord(smartContractTransaction.From.ToArray()), defaultCallValue, this)); while (!program.IsStopped()) { solidityVm.Step(program); } var contractCode = program.GetResult().GetHReturn(); var txId = transaction.GetTxId(); var smartContractAdr = smartContractTransaction.GetSmartContractAddress(); var hex = contractCode.ToHexString(); writeBatch.Put(string.Format(SMART_CONTRACT_ELT, smartContractAdr.ToHexString()), hex); writeBatch.Put(string.Format(SMART_CONTRACT_TX_ELT, txId.ToHexString()), smartContractAdr.ToHexString()); if (addIntoCache) { _cacheSmartContracts.Add(smartContractAdr.ToHexString(), new SmartContract { Address = smartContractAdr, Code = contractCode }); _cacheTxSmartContracts.Add(txId.ToHexString(), new SmartContract { Address = smartContractAdr, Code = contractCode }); } } else if (smartContractTransaction.To != null) { var sc = GetSmartContract(smartContractTransaction.To); if (sc == null) { return; } var from = smartContractTransaction.From; if (from == null) { from = new byte[0]; } var solidityVm = new SolidityVm(); var program = new SolidityProgram(sc.Code.ToList(), new SolidityProgramInvoke(smartContractTransaction.Data, smartContractTransaction.To, new DataWord(from.ToArray()), defaultCallValue, this)); while (!program.IsStopped()) { solidityVm.Step(program); } var logs = program.GetResult().GetLogs(); if (logs != null && logs.Any()) { AddLogs(smartContractTransaction.To, block.GetHashHeader(), logs, addIntoCache); } } } }