/** * 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); } } }