Contract execution:
+ apply results encapsulated + OutOfGas added to step spend + Program GAS call bug fixed
This commit is contained in:
parent
11e9190957
commit
33af300619
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue