/// <summary> /// Execute the next instruction only. /// </summary> /// <returns>Returns false if execution ended, and true otherwise.</returns> public async Task <bool> executeOnce() { fetchInstruction(); switch (m_op) { // // Call-related instructions // case (Instruction.CREATE2): case (Instruction.CREATE): case (Instruction.DELEGATECALL): case (Instruction.STATICCALL): case (Instruction.CALL): case (Instruction.CALLCODE): // Calling external contracts not supported yet (fixing this is not too hard though. PR welcomed) throw new EVMInstructionNotYetSupported(); case (Instruction.RETURN): // Reverting state is not handled but only indicated by a flag case (Instruction.REVERT): updateMem(memNeed(m_stack[0], m_stack[1])); // A hopefully it ain't trying to return more than 0x0fffffff bytes of data lol m_return = new byte[m_stack[1].ToUInt32()]; m_memory.CopyTo((int)m_stack[0].ToUInt32(), m_return, 0, m_return.Length); m_stack.RemoveAt(0); m_stack.RemoveAt(0); m_flagReverted = m_op == Instruction.REVERT; return(false); case (Instruction.SUICIDE): //Suicide not handled either m_stack.RemoveAt(0); m_flagSuicided = true; return(false); case (Instruction.STOP): return(false); // // instructions potentially expanding memory // case (Instruction.MLOAD): updateMem(m_stack[0] + 32); byte[] bytesMloaded = new byte[32]; m_memory.CopyTo((int)m_stack[0].ToUInt32(), bytesMloaded, 0, 32); m_stack[0] = new uint256(bytesMloaded); m_pc++; break; case (Instruction.MSTORE): updateMem(m_stack[0] + 32); int mstoreStartIndex = (int)m_stack[0].ToUInt32(); for (int i = 0; i < 32; i++) { m_memory[mstoreStartIndex + i] = m_stack[1].bytes[i]; } m_stack.RemoveAt(0); m_stack.RemoveAt(0); m_pc++; break; case (Instruction.MSTORE8): updateMem(m_stack[0] + 1); m_memory[(int)m_stack[0].ToUInt32()] = m_stack[1].bytes[31]; m_stack.RemoveAt(0); m_stack.RemoveAt(0); m_pc++; break; case (Instruction.SHA3): updateMem(memNeed(m_stack[0], m_stack[1])); int sha3Offset = (int)m_stack[0].ToUInt32(); int sha3Size = (int)m_stack[1].ToUInt32(); byte[] sha3Input = new byte[sha3Size]; m_memory.CopyTo(sha3Offset, sha3Input, 0, sha3Size); m_stack.RemoveAt(0); m_stack[0] = new uint256(keccak.ComputeHash(sha3Input)); m_pc++; break; case (Instruction.LOG0): case (Instruction.LOG1): case (Instruction.LOG2): case (Instruction.LOG3): case (Instruction.LOG4): updateMem(memNeed(m_stack[0], m_stack[1])); List <uint256> listOfTopics = new List <uint256>(); int noOfTopics = (int)m_op - (int)Instruction.LOG0; int logOffset = (int)m_stack[0].ToUInt32(); int logSize = (int)m_stack[1].ToUInt32(); byte[] logData = new byte[logSize]; m_memory.CopyTo(logOffset, logData, 0, logSize); for (int i = 0; i < noOfTopics; i++) { listOfTopics.Add(m_stack[i + 2]); } m_log.Add(new EVMLog() { topics = listOfTopics, data = logData }); for (int i = 0; i < noOfTopics + 2; i++) { m_stack.RemoveAt(0); } m_pc++; break; case (Instruction.EXP): m_stack[1] = m_stack[0].exp(m_stack[1]); m_stack.RemoveAt(0); m_pc++; break; // // ordinary instructions // case (Instruction.ADD): m_stack[1] = m_stack[0] + m_stack[1]; m_stack.RemoveAt(0); m_pc++; break; case (Instruction.MUL): m_stack[1] = m_stack[0] * m_stack[1]; m_stack.RemoveAt(0); m_pc++; break; case (Instruction.SUB): m_stack[1] = m_stack[0] - m_stack[1]; m_stack.RemoveAt(0); m_pc++; break; case (Instruction.DIV): if (m_stack[1] != 0) { m_stack[1] = m_stack[0] / m_stack[1]; } m_stack.RemoveAt(0); m_pc++; break; case (Instruction.SDIV): throw new EVMInstructionNotYetSupported(); case (Instruction.MOD): if (m_stack[1] != 0) { m_stack[1] = m_stack[0] % m_stack[1]; } m_stack.RemoveAt(0); m_pc++; break; case (Instruction.SMOD): throw new EVMInstructionNotYetSupported(); case (Instruction.NOT): m_stack[0] = !m_stack[0]; m_pc++; break; case (Instruction.LT): m_stack[1] = m_stack[0] < m_stack[1] ? 1 : 0; m_stack.RemoveAt(0); m_pc++; break; case (Instruction.GT): m_stack[1] = m_stack[0] > m_stack[1] ? 1 : 0; m_stack.RemoveAt(0); m_pc++; break; case (Instruction.SLT): throw new EVMInstructionNotYetSupported(); case (Instruction.SGT): throw new EVMInstructionNotYetSupported(); case (Instruction.EQ): m_stack[1] = m_stack[0] == m_stack[1] ? 1 : 0; m_stack.RemoveAt(0); m_pc++; break; case (Instruction.ISZERO): m_stack[0] = m_stack[0] == 0 ? 1 : 0; m_pc++; break; case (Instruction.AND): m_stack[1] = m_stack[0] & m_stack[1]; m_stack.RemoveAt(0); m_pc++; break; case (Instruction.OR): m_stack[1] = m_stack[0] | m_stack[1]; m_stack.RemoveAt(0); m_pc++; break; case (Instruction.XOR): m_stack[1] = m_stack[0] ^ m_stack[1]; m_stack.RemoveAt(0); m_pc++; break; case (Instruction.BYTE): throw new EVMInstructionNotYetSupported(); case (Instruction.ADDMOD): if (m_stack[2] != 0) { m_stack[2] = (m_stack[0] + m_stack[1]) % m_stack[2]; } m_stack.RemoveAt(0); m_stack.RemoveAt(0); m_pc++; break; case (Instruction.MULMOD): if (m_stack[2] != 0) { m_stack[2] = (m_stack[0] * m_stack[1]) % m_stack[2]; } m_stack.RemoveAt(0); m_stack.RemoveAt(0); m_pc++; break; case (Instruction.SIGNEXTEND): throw new EVMInstructionNotYetSupported(); // EIP 615 NOT supported case (Instruction.JUMPTO): case (Instruction.JUMPIF): case (Instruction.JUMPV): case (Instruction.JUMPSUB): case (Instruction.JUMPSUBV): case (Instruction.RETURNSUB): case (Instruction.BEGINSUB): case (Instruction.BEGINDATA): case (Instruction.GETLOCAL): case (Instruction.PUTLOCAL): throw new EVMEIPNotYetSupported(); // EIP 616 NOT supported case (Instruction.XADD): case (Instruction.XMUL): case (Instruction.XSUB): case (Instruction.XDIV): case (Instruction.XSDIV): case (Instruction.XMOD): case (Instruction.XSMOD): case (Instruction.XLT): case (Instruction.XGT): case (Instruction.XSLT): case (Instruction.XSGT): case (Instruction.XEQ): case (Instruction.XISZERO): case (Instruction.XAND): case (Instruction.XOOR): case (Instruction.XXOR): case (Instruction.XNOT): case (Instruction.XSHL): case (Instruction.XSHR): case (Instruction.XSAR): case (Instruction.XROL): case (Instruction.XROR): case (Instruction.XMLOAD): case (Instruction.XMSTORE): case (Instruction.XSLOAD): case (Instruction.XSSTORE): case (Instruction.XVTOWIDE): case (Instruction.XWIDETOV): case (Instruction.XPUSH): case (Instruction.XPUT): case (Instruction.XGET): case (Instruction.XSWIZZLE): case (Instruction.XSHUFFLE): throw new EVMEIPNotYetSupported(); // 00000000 case (Instruction.ADDRESS): m_stack.Insert(0, transaction.to); m_pc++; break; case (Instruction.ORIGIN): // TODO: REMEMBER TO MODIFY THIS AFTER CALLING IS SUPPORTED // OR IT WILL BECOME A VERY HARD TO FIND BUG m_stack.Insert(0, transaction.sender); m_pc++; break; case (Instruction.BALANCE): throw new EVMInstructionNotYetSupported(); case (Instruction.CALLER): m_stack.Insert(0, transaction.sender); m_pc++; break; case (Instruction.CALLVALUE): m_stack.Insert(0, transaction.value); m_pc++; break; case (Instruction.CALLDATALOAD): byte[] cdloadBytes = new byte[32]; int cdloadOffset = (int)m_stack[0].ToUInt32(); for (int i = 0; i < 32; i++) { cdloadBytes[i] = cdloadOffset + i < transaction.data.Length ? transaction.data[cdloadOffset + i] : (byte)0; } m_stack[0] = new uint256(cdloadBytes); m_pc++; break; case (Instruction.CALLDATASIZE): m_stack.Insert(0, transaction.data.Length); m_pc++; break; case (Instruction.RETURNDATASIZE): case (Instruction.CODESIZE): case (Instruction.EXTCODESIZE): case (Instruction.CALLDATACOPY): case (Instruction.RETURNDATACOPY): throw new EVMInstructionNotYetSupported(); case (Instruction.CODECOPY): updateMem(memNeed(m_stack[0], m_stack[1])); copyDataToMemory(byteCode, m_stack[0], m_stack[1], m_stack[2]); m_stack.RemoveAt(0); m_stack.RemoveAt(0); m_stack.RemoveAt(0); m_pc++; break; case (Instruction.EXTCODECOPY): case (Instruction.GASPRICE): case (Instruction.BLOCKHASH): case (Instruction.COINBASE): case (Instruction.TIMESTAMP): case (Instruction.NUMBER): case (Instruction.DIFFICULTY): case (Instruction.GASLIMIT): throw new EVMInstructionNotYetSupported(); case (Instruction.POP): m_stack.RemoveAt(0); m_pc++; break; case (Instruction.PUSHC): throw new EVMInstructionNotYetSupported(); case (Instruction.PUSH1): m_pc++; m_stack.Insert(0, new uint256(new byte[] { byteCode[m_pc] })); m_pc++; break; case (Instruction.PUSH2): case (Instruction.PUSH3): case (Instruction.PUSH4): case (Instruction.PUSH5): case (Instruction.PUSH6): case (Instruction.PUSH7): case (Instruction.PUSH8): case (Instruction.PUSH9): case (Instruction.PUSH10): case (Instruction.PUSH11): case (Instruction.PUSH12): case (Instruction.PUSH13): case (Instruction.PUSH14): case (Instruction.PUSH15): case (Instruction.PUSH16): case (Instruction.PUSH17): case (Instruction.PUSH18): case (Instruction.PUSH19): case (Instruction.PUSH20): case (Instruction.PUSH21): case (Instruction.PUSH22): case (Instruction.PUSH23): case (Instruction.PUSH24): case (Instruction.PUSH25): case (Instruction.PUSH26): case (Instruction.PUSH27): case (Instruction.PUSH28): case (Instruction.PUSH29): case (Instruction.PUSH30): case (Instruction.PUSH31): case (Instruction.PUSH32): m_pc++; int pushLength = m_op - Instruction.PUSH1 + 1; byte[] pushBytes = new byte[pushLength]; Array.Copy(m_byteCode, m_pc, pushBytes, 0, pushLength); m_stack.Insert(0, new uint256(pushBytes)); m_pc += pushLength; break; case (Instruction.JUMP): if (m_byteCode[(int)m_stack[0].ToUInt32()] != (int)Instruction.JUMPDEST) { throw new EVMNotJumpDestException(); } m_pc = (int)m_stack[0].ToUInt32(); m_stack.RemoveAt(0); break; case (Instruction.JUMPI): if (m_stack[1] != 0) { if (m_byteCode[(int)m_stack[0].ToUInt32()] != (int)Instruction.JUMPDEST) { throw new EVMNotJumpDestException(); } m_pc = (int)m_stack[0].ToUInt32(); } else { m_pc++; } m_stack.RemoveAt(0); m_stack.RemoveAt(0); break; case (Instruction.JUMPC): m_pc = (int)m_stack[0].ToUInt32(); m_stack.RemoveAt(0); break; case (Instruction.JUMPCI): if (m_stack[1] == 0) { m_pc = (int)m_stack[0].ToUInt32(); } else { m_pc++; } m_stack.RemoveAt(0); m_stack.RemoveAt(0); break; case (Instruction.DUP1): case (Instruction.DUP2): case (Instruction.DUP3): case (Instruction.DUP4): case (Instruction.DUP5): case (Instruction.DUP6): case (Instruction.DUP7): case (Instruction.DUP8): case (Instruction.DUP9): case (Instruction.DUP10): case (Instruction.DUP11): case (Instruction.DUP12): case (Instruction.DUP13): case (Instruction.DUP14): case (Instruction.DUP15): case (Instruction.DUP16): int dupN = (int)m_op - (int)Instruction.DUP1; m_stack.Insert(0, m_stack[dupN]); m_pc++; break; case (Instruction.SWAP1): case (Instruction.SWAP2): case (Instruction.SWAP3): case (Instruction.SWAP4): case (Instruction.SWAP5): case (Instruction.SWAP6): case (Instruction.SWAP7): case (Instruction.SWAP8): case (Instruction.SWAP9): case (Instruction.SWAP10): case (Instruction.SWAP11): case (Instruction.SWAP12): case (Instruction.SWAP13): case (Instruction.SWAP14): case (Instruction.SWAP15): case (Instruction.SWAP16): int swapN = (int)m_op - (int)Instruction.SWAP1 + 1; uint256 swapTemp = m_stack[0]; m_stack[0] = m_stack[swapN]; m_stack[swapN] = swapTemp; m_pc++; break; case (Instruction.SLOAD): // If storage already exists (even with value 0x0), take that. // Other wise, find storage data online with DataGateway. // Take the storage data at the PREVIOUS block height if (!m_storage.ContainsKey(m_stack[0])) { m_storage.Add(m_stack[0], await edg.getStorageAt(transaction.to, m_stack[0], (int)(ext.blockheight - 1).ToUInt32())); } m_stack[0] = m_storage[m_stack[0]]; m_pc++; break; case (Instruction.SSTORE): if (!m_storage.ContainsKey(m_stack[0])) { m_storage.Add(m_stack[0], m_stack[1]); } else { m_storage[m_stack[0]] = m_stack[1]; } m_stack.RemoveAt(0); m_stack.RemoveAt(0); m_pc++; break; case (Instruction.PC): m_stack.Insert(0, m_pc); m_pc++; break; case (Instruction.MSIZE): m_stack.Insert(0, m_memory.Count); m_pc++; break; case (Instruction.GAS): throw new EVMInstructionNotYetSupported(); case (Instruction.JUMPDEST): m_pc++; break; case (Instruction.INVALID): default: throw new EVMInstructionNotYetSupported(); } return(true); }