Contract execution:

+ apply results encapsulated
+ OutOfGas added to step spend
+ Program GAS call bug fixed
This commit is contained in:
romanman 2014-06-11 11:03:14 +01:00
parent 11e9190957
commit 33af300619
3 changed files with 91 additions and 67 deletions

View File

@ -133,60 +133,8 @@ public class WorldManager {
VM vm = new VM(); VM vm = new VM();
Program program = new Program(initCode, programInvoke); Program program = new Program(initCode, programInvoke);
vm.play(program); vm.play(program);
ProgramResult result = program.getResult(); ProgramResult result = program.getResult();
applyProgramResult(result, gasDebit, senderState, receiverState, senderAddress, tx.getContractAddress());
// 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<DataWord, DataWord> storage = result.getStorage();
if (storage != null){
ContractDetails contractDetails = new ContractDetails(storage);
detaildDB.put(tx.getContractAddress() , contractDetails.getEncoded());
}
} else { } else {
@ -201,6 +149,18 @@ public class WorldManager {
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
logger.info("calling for existing contract: addres={}" , Hex.toHexString(tx.getReceiveAddress())); 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 // FETCH THE SAVED STORAGE
ContractDetails details = null; ContractDetails details = null;
byte[] detailsRLPData = detaildDB.get(tx.getReceiveAddress()); byte[] detailsRLPData = detaildDB.get(tx.getReceiveAddress());
@ -215,16 +175,76 @@ public class WorldManager {
vm.play(program); vm.play(program);
ProgramResult result = program.getResult(); ProgramResult result = program.getResult();
applyProgramResult(result, gasDebit, senderState, receiverState, senderAddress, tx.getReceiveAddress());
// TODO: (!!!!!) ALL THE CHECKS FOR THE PROGRAM RESULT
} }
} }
}
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<DataWord, DataWord> storage = result.getStorage();
if (storage != null){
ContractDetails contractDetails = new ContractDetails(storage);
detaildDB.put(contractAddress , contractDetails.getEncoded());
}
} }
public void applyTransactionList(List<Transaction> txList) { public void applyTransactionList(List<Transaction> txList) {

View File

@ -61,6 +61,9 @@ public class DataWord {
public int intValue(){ public int intValue(){
return new BigInteger(1, data).intValue(); return new BigInteger(1, data).intValue();
} }
public long longValue(){
return new BigInteger(1, data).longValue();
}
public BigInteger sValue(){ public BigInteger sValue(){
return new BigInteger(data); return new BigInteger(data);

View File

@ -41,7 +41,7 @@ public class Program {
spendGas(GasCost.TRANSACTION); spendGas(GasCost.TRANSACTION);
spendGas(GasCost.TXDATA * invokeData.getDataSize().intValue()); 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.invokeData = invokeData;
this.ops = ops; this.ops = ops;
@ -173,7 +173,6 @@ public class Program {
return ByteBuffer.wrap(chunk); return ByteBuffer.wrap(chunk);
} }
private void allocateMemory(int address, byte[] value){ private void allocateMemory(int address, byte[] value){
int memSize = 0; int memSize = 0;
@ -216,8 +215,11 @@ public class Program {
public void spendGas(int gasValue){ 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); result.spendGas(gasValue);
} }
@ -257,8 +259,11 @@ public class Program {
} }
public DataWord getGas(){ public DataWord getGas(){
if (invokeData == null) return new DataWord( new byte[0]); 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)){ if (!Arrays.equals(txData, ops)){
globalOutput.append("\n msg.data: ").append(Hex.toHexString( txData )); globalOutput.append("\n msg.data: ").append(Hex.toHexString( txData ));
} }
globalOutput.append("\n\n Spent Gas: ").append(result.getGasUsed()); globalOutput.append("\n\n Spent Gas: ").append(result.getGasUsed());
if (listener != null){ if (listener != null){
listener.output(globalOutput.toString()); listener.output(globalOutput.toString());
} }
}; };
} }
public void addListener(ProgramListener listener){ public void addListener(ProgramListener listener){
this.listener = listener; this.listener = listener;
} }