diff --git a/ethereumj-core/src/main/java/org/ethereum/manager/WorldManager.java b/ethereumj-core/src/main/java/org/ethereum/manager/WorldManager.java index f7e6a0e9..d9215caf 100644 --- a/ethereumj-core/src/main/java/org/ethereum/manager/WorldManager.java +++ b/ethereumj-core/src/main/java/org/ethereum/manager/WorldManager.java @@ -133,60 +133,8 @@ public class WorldManager { VM vm = new VM(); Program program = new Program(initCode, programInvoke); vm.play(program); - ProgramResult result = program.getResult(); - - // TODO: (!!!!!) ALL THE CHECKS FOR THE PROGRAM RESULT - // TODO: (!!!!!) consider introduce one method for applying results - if (result.getException() != null && - result.getException() instanceof Program.OutOfGasException){ - - // The out of gas means nothing applied - } - - - // Save the code created by init - byte[] bodyCode = null; - if (result.gethReturn() != null){ - - bodyCode = result.gethReturn().array(); - } - - BigInteger gasPrice = - BigInteger.valueOf( MainData.instance.getBlockchain().getGasPrice()); - BigInteger refund = - gasDebit.subtract(BigInteger.valueOf( result.getGasUsed()).multiply(gasPrice)); - - if (refund.signum() > 0){ - if(stateLogger.isInfoEnabled()) - stateLogger.info("After contract execution the sender address refunded with gas leftover , \n sender={} \n contract={} \n gas_refund= {}", - Hex.toHexString(tx.getSender()) ,Hex.toHexString(tx.getContractAddress()), refund); - senderState.addToBalance(refund); - worldState.update(senderAddress, senderState.getEncoded()); - } - - if (bodyCode != null){ - byte[] codeKey = HashUtil.sha3(bodyCode); - chainDB.put(codeKey, bodyCode); - receiverState.setCodeHash(codeKey); - worldState.update(tx.getContractAddress(), receiverState.getEncoded()); - - if (stateLogger.isInfoEnabled()) - stateLogger.info("saving code of the contract to the db:\n contract={} sha3(code)={} code={}", - Hex.toHexString(tx.getContractAddress()), - Hex.toHexString(codeKey), - Hex.toHexString(bodyCode)); - } - - // Save the storage changes. - Map storage = result.getStorage(); - if (storage != null){ - ContractDetails contractDetails = new ContractDetails(storage); - detaildDB.put(tx.getContractAddress() , contractDetails.getEncoded()); - } - - - + applyProgramResult(result, gasDebit, senderState, receiverState, senderAddress, tx.getContractAddress()); } else { @@ -201,6 +149,18 @@ public class WorldManager { if (logger.isInfoEnabled()) logger.info("calling for existing contract: addres={}" , Hex.toHexString(tx.getReceiveAddress())); + // first of all debit the gas from the issuer + BigInteger gasDebit = tx.getTotalGasDebit(); + senderState.addToBalance(gasDebit.negate()); + if (senderState.getBalance().signum() == -1){ + // todo: the sender can't afford this contract do Out-Of-Gas + } + + if(stateLogger.isInfoEnabled()) + stateLogger.info("Before contract execution the sender address debit with gas total cost, \n sender={} \n contract={} \n gas_debit= {}", + Hex.toHexString( tx.getSender() ), Hex.toHexString(tx.getReceiveAddress()), gasDebit); + worldState.update(senderAddress, senderState.getEncoded()); + // FETCH THE SAVED STORAGE ContractDetails details = null; byte[] detailsRLPData = detaildDB.get(tx.getReceiveAddress()); @@ -215,16 +175,76 @@ public class WorldManager { vm.play(program); ProgramResult result = program.getResult(); - - // TODO: (!!!!!) ALL THE CHECKS FOR THE PROGRAM RESULT - - + applyProgramResult(result, gasDebit, senderState, receiverState, senderAddress, tx.getReceiveAddress()); } } + } + pendingTransactions.put(Hex.toHexString(tx.getHash()), tx); + } + + /** + * After any contract code finish the run + * the certain result should take place, + * according the given circumstances + * + * @param result + * @param gasDebit + * @param senderState + * @param receiverState + * @param senderAddress + * @param contractAddress + */ + private void applyProgramResult(ProgramResult result, BigInteger gasDebit, + AccountState senderState, AccountState receiverState, + byte[] senderAddress, byte[] contractAddress) { + + if (result.getException() != null && + result.getException() instanceof Program.OutOfGasException){ + + // todo: find out what exactly should be reverted in that case + return; } - pendingTransactions.put(Hex.toHexString(tx.getHash()), tx); + // Save the code created by init + byte[] bodyCode = null; + if (result.gethReturn() != null){ + + bodyCode = result.gethReturn().array(); + } + + BigInteger gasPrice = + BigInteger.valueOf( MainData.instance.getBlockchain().getGasPrice()); + BigInteger refund = + gasDebit.subtract(BigInteger.valueOf( result.getGasUsed()).multiply(gasPrice)); + + if (refund.signum() > 0){ + if(stateLogger.isInfoEnabled()) + stateLogger.info("After contract execution the sender address refunded with gas leftover , \n sender={} \n contract={} \n gas_refund= {}", + Hex.toHexString(senderAddress) ,Hex.toHexString(contractAddress), refund); + senderState.addToBalance(refund); + worldState.update(senderAddress, senderState.getEncoded()); + } + + if (bodyCode != null){ + byte[] codeKey = HashUtil.sha3(bodyCode); + chainDB.put(codeKey, bodyCode); + receiverState.setCodeHash(codeKey); + worldState.update(contractAddress, receiverState.getEncoded()); + + if (stateLogger.isInfoEnabled()) + stateLogger.info("saving code of the contract to the db:\n contract={} sha3(code)={} code={}", + Hex.toHexString(contractAddress), + Hex.toHexString(codeKey), + Hex.toHexString(bodyCode)); + } + + // Save the storage changes. + Map storage = result.getStorage(); + if (storage != null){ + ContractDetails contractDetails = new ContractDetails(storage); + detaildDB.put(contractAddress , contractDetails.getEncoded()); + } } public void applyTransactionList(List txList) { diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/DataWord.java b/ethereumj-core/src/main/java/org/ethereum/vm/DataWord.java index 5f391c62..3c2317f3 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/DataWord.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/DataWord.java @@ -61,6 +61,9 @@ public class DataWord { public int intValue(){ return new BigInteger(1, data).intValue(); } + public long longValue(){ + return new BigInteger(1, data).longValue(); + } public BigInteger sValue(){ return new BigInteger(data); diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java index 75246cd4..278f9f13 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java @@ -41,7 +41,7 @@ public class Program { spendGas(GasCost.TRANSACTION); spendGas(GasCost.TXDATA * invokeData.getDataSize().intValue()); - if (ops == null) throw new RuntimeException("program can not run with ops: null"); + if (ops == null) throw new RuntimeException("program can not run with ops: null"); this.invokeData = invokeData; this.ops = ops; @@ -173,7 +173,6 @@ public class Program { return ByteBuffer.wrap(chunk); } - private void allocateMemory(int address, byte[] value){ int memSize = 0; @@ -216,8 +215,11 @@ public class Program { public void spendGas(int gasValue){ - // todo: check it against avail gas - // todo: out of gas will revert the changes [YP 5, 6 ] + + long afterSpend = invokeData.getGas().longValue() - gasValue - result.getGasUsed(); + if (afterSpend < 0) + throw new OutOfGasException(); + result.spendGas(gasValue); } @@ -257,8 +259,11 @@ public class Program { } public DataWord getGas(){ + if (invokeData == null) return new DataWord( new byte[0]); - return invokeData.getGas(); + + long afterSpend = invokeData.getGas().longValue() - result.getGasUsed(); + return new DataWord(afterSpend); } @@ -401,18 +406,14 @@ public class Program { if (!Arrays.equals(txData, ops)){ globalOutput.append("\n msg.data: ").append(Hex.toHexString( txData )); } - globalOutput.append("\n\n Spent Gas: ").append(result.getGasUsed()); - if (listener != null){ listener.output(globalOutput.toString()); } }; } - - public void addListener(ProgramListener listener){ this.listener = listener; }