public DataWord memoryLoad(int address) { allocateMemory(address, DataWord.ZERO.getData().length); DataWord newMem = new DataWord(); System.arraycopy(memory.array(), address, newMem.getData(), 0, newMem.getData().length); return newMem; }
public DataWord getBalance(DataWord address) { if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY; BigInteger balance = result.getRepository().getBalance(address.getLast20Bytes()); DataWord balanceData = new DataWord(balance.toByteArray()); return balanceData; }
public void setPC(DataWord pc) { this.pc = pc.intValue(); if (this.pc == ops.length) { stop(); } if (this.pc > ops.length) { stop(); throw new RuntimeException("pc overflow pc=" + pc); } }
public void suicide(DataWord obtainer) { DataWord balance = getBalance(this.getOwnerAddress()); // 1) pass full endowment to the obtainer if (logger.isInfoEnabled()) logger.info( "Transfer to: [ {} ] heritage: [ {} ]", Hex.toHexString(obtainer.getLast20Bytes()), balance.longValue()); this.result.getRepository().addBalance(obtainer.getLast20Bytes(), balance.value()); this.result .getRepository() .addBalance(this.getOwnerAddress().getLast20Bytes(), balance.value().negate()); // 2) mark the account as for delete result.addDeleteAccount(this.getOwnerAddress()); }
@Ignore // TODO #POC9 @Test // contractB call contractA with data to storage public void test2() { /** * #The code will run ------------------ * * <p>contract A: 77045e71a7a2c50903d88e564cd72fab11e82051 --------------- a = msg.data[0] b = * msg.data[1] * * <p>contract.storage[a] contract.storage[b] * * <p>contract B: 83c5541a6c8d2dbad642f385d8d06ca9b6c731ee ----------- a = msg((tx.gas / 10 * * 8), 0x77045e71a7a2c50903d88e564cd72fab11e82051, 0, [11, 22, 33], 3, 6) */ long expectedVal_1 = 11; long expectedVal_2 = 22; // Set contract into Database String callerAddr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826"; String contractA_addr = "77045e71a7a2c50903d88e564cd72fab11e82051"; String contractB_addr = "83c5541a6c8d2dbad642f385d8d06ca9b6c731ee"; String code_a = "60006020023560005260016020023560205260005160005560205160015500"; String code_b = "6000601f5360e05960e05952600060c05901536060596020015980602001600b9052806040016016905280606001602190526080905260007377045e71a7a2c50903d88e564cd72fab11e820516103e8f1602060000260a00160200151600052"; byte[] caller_addr_bytes = Hex.decode(callerAddr); byte[] contractA_addr_bytes = Hex.decode(contractA_addr); byte[] codeA = Hex.decode(code_a); byte[] contractB_addr_bytes = Hex.decode(contractB_addr); byte[] codeB = Hex.decode(code_b); ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl(); pi.setOwnerAddress(contractB_addr_bytes); Repository repository = pi.getRepository(); repository.createAccount(contractA_addr_bytes); repository.saveCode(contractA_addr_bytes, codeA); repository.createAccount(contractB_addr_bytes); repository.saveCode(contractB_addr_bytes, codeB); repository.createAccount(caller_addr_bytes); repository.addBalance(caller_addr_bytes, new BigInteger("100000000000000000000")); // ****************** // // Play the program // // ****************** // VM vm = new VM(); Program program = new Program(codeB, pi); try { while (!program.isStopped()) vm.step(program); } catch (RuntimeException e) { program.setRuntimeFailure(e); } System.out.println(); System.out.println("============ Results ============"); System.out.println("*** Used gas: " + program.getResult().getGasUsed()); DataWord value_1 = repository.getStorageValue(contractA_addr_bytes, new DataWord(00)); DataWord value_2 = repository.getStorageValue(contractA_addr_bytes, new DataWord(01)); repository.close(); assertEquals(expectedVal_1, value_1.longValue()); assertEquals(expectedVal_2, value_2.longValue()); // TODO: check that the value pushed after exec is 1 }
@Ignore @Test // contractB call itself with code from contractA public void test6() { /** * #The code will run ------------------ * * <p>contract A: 945304eb96065b2a98b57a48a06ae28d285a71b5 --------------- * * <p>PUSH1 0 CALLDATALOAD SLOAD NOT PUSH1 9 JUMPI STOP PUSH1 32 CALLDATALOAD PUSH1 0 * CALLDATALOAD SSTORE * * <p>contract B: 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 ----------- { (MSTORE 0 * 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (MSTORE 32 * 0xaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa) [[ 0 ]] (CALLSTATELESS * 1000000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 64 64 0) } */ // Set contract into Database byte[] caller_addr_bytes = Hex.decode("cd1722f3947def4cf144679da39c4c32bdc35681"); byte[] contractA_addr_bytes = Hex.decode("945304eb96065b2a98b57a48a06ae28d285a71b5"); byte[] contractB_addr_bytes = Hex.decode("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"); byte[] codeA = Hex.decode("60003554156009570060203560003555"); byte[] codeB = Hex.decode( "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa6020526000604060406000601773945304eb96065b2a98b57a48a06ae28d285a71b5620f4240f3600055"); ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl(); pi.setOwnerAddress(contractB_addr_bytes); pi.setGasLimit(10000000000000l); Repository repository = pi.getRepository(); repository.createAccount(contractA_addr_bytes); repository.saveCode(contractA_addr_bytes, codeA); repository.addBalance(contractA_addr_bytes, BigInteger.valueOf(23)); repository.createAccount(contractB_addr_bytes); repository.saveCode(contractB_addr_bytes, codeB); repository.addBalance(contractB_addr_bytes, new BigInteger("1000000000000000000")); repository.createAccount(caller_addr_bytes); repository.addBalance(caller_addr_bytes, new BigInteger("100000000000000000000")); // ****************** // // Play the program // // ****************** // VM vm = new VM(); Program program = new Program(codeB, pi); try { while (!program.isStopped()) vm.step(program); } catch (RuntimeException e) { program.setRuntimeFailure(e); } System.out.println(); System.out.println("============ Results ============"); System.out.println("*** Used gas: " + program.getResult().getGasUsed()); DataWord memValue1 = program.memoryLoad(new DataWord(0)); DataWord memValue2 = program.memoryLoad(new DataWord(32)); DataWord storeValue1 = repository.getStorageValue(contractB_addr_bytes, new DataWord(00)); repository.close(); assertEquals( "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", memValue1.toString()); assertEquals( "aaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa", memValue2.toString()); assertEquals("0x1", storeValue1.shortHex()); // TODO: check that the value pushed after exec is 1 }
@Ignore @Test // contractB call contractA with return expectation public void test3() { /** * #The code will run ------------------ * * <p>contract A: 77045e71a7a2c50903d88e564cd72fab11e82051 --------------- * * <p>a = 11 b = 22 c = 33 d = 44 e = 55 f = 66 * * <p>[asm 192 0 RETURN asm] * * <p>contract B: 83c5541a6c8d2dbad642f385d8d06ca9b6c731ee ----------- a = msg((tx.gas / 10 * * 8), 0x77045e71a7a2c50903d88e564cd72fab11e82051, 0, [11, 22, 33], 3, 6) */ long expectedVal_1 = 11; long expectedVal_2 = 22; long expectedVal_3 = 33; long expectedVal_4 = 44; long expectedVal_5 = 55; long expectedVal_6 = 66; // Set contract into Database byte[] caller_addr_bytes = Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826"); byte[] contractA_addr_bytes = Hex.decode("77045e71a7a2c50903d88e564cd72fab11e82051"); byte[] contractB_addr_bytes = Hex.decode("83c5541a6c8d2dbad642f385d8d06ca9b6c731ee"); byte[] codeA = Hex.decode("600b60005260166020526021604052602c6060526037608052604260a05260c06000f2"); byte[] codeB = Hex.decode( "6000601f5360e05960e05952600060c05901536060596020015980602001600b9052806040016016905280606001602190526080905260007377045e71a7a2c50903d88e564cd72fab11e820516103e8f1602060000260a00160200151600052"); ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl(); pi.setOwnerAddress(contractB_addr_bytes); Repository repository = pi.getRepository(); repository.createAccount(contractA_addr_bytes); repository.saveCode(contractA_addr_bytes, codeA); repository.createAccount(contractB_addr_bytes); repository.saveCode(contractB_addr_bytes, codeB); repository.createAccount(caller_addr_bytes); repository.addBalance(caller_addr_bytes, new BigInteger("100000000000000000000")); // ****************** // // Play the program // // ****************** // VM vm = new VM(); Program program = new Program(codeB, pi); try { while (!program.isStopped()) vm.step(program); } catch (RuntimeException e) { program.setRuntimeFailure(e); } System.out.println(); System.out.println("============ Results ============"); System.out.println("*** Used gas: " + program.getResult().getGasUsed()); DataWord value1 = program.memoryLoad(new DataWord(32)); DataWord value2 = program.memoryLoad(new DataWord(64)); DataWord value3 = program.memoryLoad(new DataWord(96)); DataWord value4 = program.memoryLoad(new DataWord(128)); DataWord value5 = program.memoryLoad(new DataWord(160)); DataWord value6 = program.memoryLoad(new DataWord(192)); repository.close(); assertEquals(expectedVal_1, value1.longValue()); assertEquals(expectedVal_2, value2.longValue()); assertEquals(expectedVal_3, value3.longValue()); assertEquals(expectedVal_4, value4.longValue()); assertEquals(expectedVal_5, value5.longValue()); assertEquals(expectedVal_6, value6.longValue()); // TODO: check that the value pushed after exec is 1 }
public void storageSave(DataWord word1, DataWord word2) { storageSave(word1.getData(), word2.getData()); }
/** * That method implement internal calls and code invocations * * @param gas - gas to pay for the call, remaining gas will be refunded to the caller * @param toAddressDW - address to call * @param endowmentValue - the value that can be transfer along with the code execution * @param inDataOffs - start of memory to be input data to the call * @param inDataSize - size of memory to be input data to the call * @param outDataOffs - start of memory to be output of the call * @param outDataSize - size of memory to be output data to the call */ public void callToAddress( DataWord gas, DataWord toAddressDW, DataWord endowmentValue, DataWord inDataOffs, DataWord inDataSize, DataWord outDataOffs, DataWord outDataSize) { ByteBuffer data = memoryChunk(inDataOffs, inDataSize); // FETCH THE SAVED STORAGE byte[] toAddress = toAddressDW.getLast20Bytes(); // FETCH THE CODE byte[] programCode = this.result.getRepository().getCode(toAddress); if (logger.isInfoEnabled()) logger.info( "calling for existing contract: address: [ {} ], outDataOffs: [ {} ], outDataSize: [ {} ] ", Hex.toHexString(toAddress), outDataOffs.longValue(), outDataSize.longValue()); byte[] senderAddress = this.getOwnerAddress().getLast20Bytes(); // 2.1 PERFORM THE GAS VALUE TX // (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION) if (this.getGas().longValue() - gas.longValue() < 0) { logger.info( "No gas for the internal call, \n" + "fromAddress={}, toAddress={}", Hex.toHexString(senderAddress), Hex.toHexString(toAddress)); this.stackPushZero(); return; } BigInteger endowment = endowmentValue.value(); BigInteger senderBalance = result.getRepository().getBalance(senderAddress); if (senderBalance.compareTo(endowment) < 0) { stackPushZero(); return; } result.getRepository().addBalance(senderAddress, endowment.negate()); if (invokeData.byTestingSuite()) { logger.info("[testing suite] - omit real call"); stackPushOne(); this.getResult() .addCallCreate( data.array(), toAddressDW.getLast20Bytes(), gas.getNoLeadZeroesData(), endowmentValue.getNoLeadZeroesData()); return; } // actual gas subtract this.spendGas(gas.intValue(), "internal call"); RepositoryImpl trackRepositoryImpl = result.getRepository().getTrack(); trackRepositoryImpl.startTracking(); trackRepositoryImpl.addBalance(toAddress, endowmentValue.value()); ProgramInvoke programInvoke = ProgramInvokeFactory.createProgramInvoke( this, toAddressDW, endowmentValue, gas, result.getRepository().getBalance(toAddress), data.array(), trackRepositoryImpl, this.invokeData.getCallDeep() + 1); ProgramResult result = null; if (programCode != null && programCode.length != 0) { VM vm = new VM(); Program program = new Program(programCode, programInvoke); vm.play(program); result = program.getResult(); this.result.addDeleteAccounts(result.getDeleteAccounts()); } if (result != null && result.getException() != null && result.getException() instanceof Program.OutOfGasException) { logger.info("contract run halted by OutOfGas: contract={}", Hex.toHexString(toAddress)); trackRepositoryImpl.rollback(); stackPushZero(); return; } // 3. APPLY RESULTS: result.getHReturn() into out_memory allocated if (result != null) { ByteBuffer buffer = result.getHReturn(); int allocSize = outDataSize.intValue(); if (buffer != null && allocSize > 0) { int retSize = buffer.limit(); int offset = outDataOffs.intValue(); if (retSize > allocSize) { this.memorySave(offset, buffer.array()); } else { this.memorySave(offset, allocSize, buffer.array()); } } } // 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK trackRepositoryImpl.commit(); stackPushOne(); // 5. REFUND THE REMAIN GAS if (result != null) { BigInteger refundGas = gas.value().subtract(BigInteger.valueOf(result.getGasUsed())); if (refundGas.compareTo(BigInteger.ZERO) == 1) { this.refundGas(refundGas.intValue(), "remaining gas from the internal call"); logger.info( "The remaining gas refunded, account: [ {} ], gas: [ {} ] ", Hex.toHexString(senderAddress), refundGas.toString()); } } else { this.refundGas(gas.intValue(), "remaining gas from the internal call"); } }
public void createContract(DataWord value, DataWord memStart, DataWord memSize) { if (invokeData.byTestingSuite()) { logger.info("[testing suite] - omit real create"); return; } // [1] FETCH THE CODE FROM THE MEMORY ByteBuffer programCode = memoryChunk(memStart, memSize); byte[] senderAddress = this.getOwnerAddress().getLast20Bytes(); if (logger.isInfoEnabled()) logger.info( "creating a new contract inside contract run: [{}]", Hex.toHexString(senderAddress)); // actual gas subtract int gas = this.getGas().intValue(); this.spendGas(gas, "internal call"); // [2] CREATE THE CONTRACT ADDRESS byte[] nonce = result.getRepository().getNonce(senderAddress).toByteArray(); byte[] newAddress = HashUtil.calcNewAddr(this.getOwnerAddress().getLast20Bytes(), nonce); result.getRepository().createAccount(newAddress); // [3] UPDATE THE NONCE // (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION) result.getRepository().increaseNonce(senderAddress); // [4] TRANSFER THE BALANCE BigInteger endowment = value.value(); BigInteger senderBalance = result.getRepository().getBalance(senderAddress); if (senderBalance.compareTo(endowment) < 0) { stackPushZero(); return; } result.getRepository().addBalance(senderAddress, endowment.negate()); result.getRepository().addBalance(newAddress, endowment); RepositoryImpl trackRepositoryImpl = result.getRepository().getTrack(); trackRepositoryImpl.startTracking(); // [5] COOK THE INVOKE AND EXECUTE ProgramInvoke programInvoke = ProgramInvokeFactory.createProgramInvoke( this, new DataWord(newAddress), DataWord.ZERO, new DataWord(gas), BigInteger.ZERO, null, trackRepositoryImpl, this.invokeData.getCallDeep() + 1); VM vm = new VM(); Program program = new Program(programCode.array(), programInvoke); vm.play(program); ProgramResult result = program.getResult(); this.result.addDeleteAccounts(result.getDeleteAccounts()); if (result.getException() != null && result.getException() instanceof Program.OutOfGasException) { logger.info( "contract run halted by OutOfGas: new contract init ={}", Hex.toHexString(newAddress)); trackRepositoryImpl.rollback(); stackPushZero(); return; } // 4. CREATE THE CONTRACT OUT OF RETURN byte[] code = result.getHReturn().array(); trackRepositoryImpl.saveCode(newAddress, code); // IN SUCCESS PUSH THE ADDRESS INTO THE STACK stackPush(new DataWord(newAddress)); trackRepositoryImpl.commit(); // 5. REFUND THE REMAIN GAS long refundGas = gas - result.getGasUsed(); if (refundGas > 0) { this.refundGas(refundGas, "remain gas from the internal call"); if (logger.isInfoEnabled()) { logger.info( "The remaining gas is refunded, account: [ {} ], gas: [ {} ] ", Hex.toHexString(this.getOwnerAddress().getLast20Bytes()), refundGas); } } }
public ByteBuffer memoryChunk(DataWord offsetData, DataWord sizeData) { return memoryChunk(offsetData.intValue(), sizeData.intValue()); }
public DataWord memoryLoad(DataWord addr) { return memoryLoad(addr.intValue()); }
public void memorySave(DataWord addrB, DataWord value) { memorySave(addrB.intValue(), value.getData()); }