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,18 +133,79 @@ public class WorldManager {
VM vm = new VM();
Program program = new Program(initCode, programInvoke);
vm.play(program);
ProgramResult result = program.getResult();
applyProgramResult(result, gasDebit, senderState, receiverState, senderAddress, tx.getContractAddress());
} else {
if (receiverState.getCodeHash() != HashUtil.EMPTY_DATA_HASH){
byte[] programCode = chainDB.get(receiverState.getCodeHash());
if (programCode != null && programCode.length != 0){
Block lastBlock =
MainData.instance.getBlockchain().getLastBlock();
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());
if (detailsRLPData.length > 0)
details = new ContractDetails(detailsRLPData);
ProgramInvoke programInvoke =
ProgramInvokeFactory.createProgramInvoke(tx, lastBlock, details);
VM vm = new VM();
Program program = new Program(programCode, programInvoke);
vm.play(program);
ProgramResult result = program.getResult();
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) {
// 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
// todo: find out what exactly should be reverted in that case
return;
}
// Save the code created by init
byte[] bodyCode = null;
if (result.gethReturn() != null){
@ -160,7 +221,7 @@ public class WorldManager {
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);
Hex.toHexString(senderAddress) ,Hex.toHexString(contractAddress), refund);
senderState.addToBalance(refund);
worldState.update(senderAddress, senderState.getEncoded());
}
@ -169,11 +230,11 @@ public class WorldManager {
byte[] codeKey = HashUtil.sha3(bodyCode);
chainDB.put(codeKey, bodyCode);
receiverState.setCodeHash(codeKey);
worldState.update(tx.getContractAddress(), receiverState.getEncoded());
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(tx.getContractAddress()),
Hex.toHexString(contractAddress),
Hex.toHexString(codeKey),
Hex.toHexString(bodyCode));
}
@ -182,49 +243,8 @@ public class WorldManager {
Map<DataWord, DataWord> storage = result.getStorage();
if (storage != null){
ContractDetails contractDetails = new ContractDetails(storage);
detaildDB.put(tx.getContractAddress() , contractDetails.getEncoded());
detaildDB.put(contractAddress , contractDetails.getEncoded());
}
} else {
if (receiverState.getCodeHash() != HashUtil.EMPTY_DATA_HASH){
byte[] programCode = chainDB.get(receiverState.getCodeHash());
if (programCode != null && programCode.length != 0){
Block lastBlock =
MainData.instance.getBlockchain().getLastBlock();
if (logger.isInfoEnabled())
logger.info("calling for existing contract: addres={}" , Hex.toHexString(tx.getReceiveAddress()));
// FETCH THE SAVED STORAGE
ContractDetails details = null;
byte[] detailsRLPData = detaildDB.get(tx.getReceiveAddress());
if (detailsRLPData.length > 0)
details = new ContractDetails(detailsRLPData);
ProgramInvoke programInvoke =
ProgramInvokeFactory.createProgramInvoke(tx, lastBlock, details);
VM vm = new VM();
Program program = new Program(programCode, programInvoke);
vm.play(program);
ProgramResult result = program.getResult();
// TODO: (!!!!!) ALL THE CHECKS FOR THE PROGRAM RESULT
}
}
}
pendingTransactions.put(Hex.toHexString(tx.getHash()), tx);
}
public void applyTransactionList(List<Transaction> txList) {

View File

@ -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);

View File

@ -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;
}