Add CALLSTATELESS - POST queue and no fee for zero size memory increase

This commit is contained in:
nicksavers 2014-09-17 14:27:45 +02:00
parent 70479bdd02
commit 57f9db3969
4 changed files with 82 additions and 76 deletions

View File

@ -17,10 +17,8 @@ public class MessageCall {
/** gas to pay for the call, remaining gas will be refunded to the caller */
private DataWord gas;
/** address to call */
private DataWord toAddress;
/** address of the storage to use */
private DataWord storageAddress;
/** address of account which code to call */
private DataWord codeAddress;
/** the value that can be transfer along with the code execution */
private DataWord endowment;
/** start of memory to be input data to the call */
@ -32,23 +30,20 @@ public class MessageCall {
/** size of memory to be output data to the call */
private DataWord outDataSize;
public MessageCall(MsgType type, DataWord gas, DataWord toAddress,
DataWord storageAddress, DataWord endowment,
DataWord inDataOffs, DataWord inDataSize) {
public MessageCall(MsgType type, DataWord gas, DataWord codeAddress,
DataWord endowment, DataWord inDataOffs, DataWord inDataSize) {
this.type = type;
this.gas = gas;
this.toAddress = toAddress;
this.storageAddress = (type == MsgType.STATELESS) ? storageAddress: toAddress;
this.codeAddress = codeAddress;
this.endowment = endowment;
this.inDataOffs = inDataOffs;
this.inDataSize = inDataSize;
}
public MessageCall(MsgType type, DataWord gas, DataWord toAddress,
DataWord storageAddress, DataWord endowment,
DataWord inDataOffs, DataWord inDataSize, DataWord outDataOffs,
DataWord outDataSize) {
this(type, gas, toAddress, storageAddress, endowment, inDataOffs, inDataSize);
public MessageCall(MsgType type, DataWord gas, DataWord codeAddress,
DataWord endowment, DataWord inDataOffs, DataWord inDataSize,
DataWord outDataOffs, DataWord outDataSize) {
this(type, gas, codeAddress, endowment, inDataOffs, inDataSize);
this.outDataOffs = outDataOffs;
this.outDataSize = outDataSize;
}
@ -61,12 +56,8 @@ public class MessageCall {
return gas;
}
public DataWord getToAddress() {
return toAddress;
}
public DataWord getStorageAddress() {
return storageAddress;
public DataWord getCodeAddress() {
return codeAddress;
}
public DataWord getEndowment() {

View File

@ -5,6 +5,7 @@ import org.ethereum.db.ContractDetails;
import org.ethereum.facade.Repository;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.Utils;
import org.ethereum.vm.MessageCall.MsgType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
@ -17,6 +18,7 @@ import java.util.Collections;
import java.util.EmptyStackException;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
/**
@ -31,7 +33,13 @@ public class Program {
private int invokeHash;
private ProgramListener listener;
private static LinkedList<MessageCall> postQueue = new LinkedList<>();
/**
* Instead of immediately executing a POST message call,
* it is added to a post-queue, to be executed after everything else
* (including prior-created posts) within the scope
* of that transaction execution is executed.
*/
private static Queue<MessageCall> messageQueue = new LinkedList<>();
Stack<DataWord> stack = new Stack<>();
ByteBuffer memory = null;
@ -239,8 +247,9 @@ public class Program {
*/
protected void allocateMemory(int offset, int size) {
int memSize = memory != null ? memory.limit(): 0;
double newMemSize = Math.max(memSize, Math.ceil((double)(offset + size) / 32) * 32);
int memSize = memory != null ? memory.limit() : 0;
double newMemSize = Math.max(memSize, size != 0 ?
Math.ceil((double) (offset + size) / 32) * 32 : 0);
ByteBuffer tmpMem = ByteBuffer.allocate((int)newMemSize);
if (memory != null)
tmpMem.put(memory.array(), 0, memory.limit());
@ -344,47 +353,40 @@ public class Program {
}
}
/**
* Instead of immediately executing a message call, it is
* added to a post-queue, to be executed after everything else
* (including prior-created posts) within the scope
* of that transaction execution is executed.
*
* @param msg is the message call object
*/
public void queue(MessageCall msg) {
postQueue.push(msg);
public Queue<MessageCall> getMessageQueue() {
return messageQueue;
}
/**
* That method is for internal code invocations
*
* - Normal calls invoke a specified contract which updates itself
* - Stateless calls invoke another contract, within the context of the caller
*
* @param msg is the message call object
*/
public void callToAddress(MessageCall msg) {
// TODO: update code for CALLSTATELESS
ByteBuffer data = memoryChunk(msg.getInDataOffs(), msg.getInDataSize());
// FETCH THE SAVED STORAGE
byte[] toAddress = msg.getToAddress().getLast20Bytes();
byte[] codeAddress = msg.getCodeAddress().getLast20Bytes();
byte[] senderAddress = this.getOwnerAddress().getLast20Bytes();
byte[] contextAddress = msg.getType() == MsgType.STATELESS ? senderAddress : codeAddress;
// FETCH THE CODE
byte[] programCode = this.result.getRepository().getCode(toAddress);
byte[] programCode = this.result.getRepository().getCode(codeAddress);
if (logger.isInfoEnabled())
logger.info("calling for existing contract: address: [ {} ], outDataOffs: [ {} ], outDataSize: [ {} ] ",
Hex.toHexString(toAddress), msg.getOutDataOffs().longValue(), msg.getOutDataSize().longValue());
byte[] senderAddress = this.getOwnerAddress().getLast20Bytes();
logger.info(msg.getType().name() + " for existing contract: address: [ {} ], outDataOffs: [ {} ], outDataSize: [ {} ] ",
Hex.toHexString(contextAddress), msg.getOutDataOffs().longValue(), msg.getOutDataSize().longValue());
// 2.1 PERFORM THE GAS VALUE TX
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
if (this.getGas().longValue() - msg.getGas().longValue() < 0 ) {
logger.info("No gas for the internal call, \n" +
"fromAddress={}, toAddress={}",
Hex.toHexString(senderAddress), Hex.toHexString(toAddress));
"fromAddress={}, codeAddress={}",
Hex.toHexString(senderAddress), Hex.toHexString(contextAddress));
this.stackPushZero();
return;
}
@ -403,7 +405,7 @@ public class Program {
stackPushOne();
this.getResult().addCallCreate(data.array(),
msg.getToAddress().getLast20Bytes(),
msg.getCodeAddress().getLast20Bytes(),
msg.getGas().getNoLeadZeroesData(),
msg.getEndowment().getNoLeadZeroesData());
@ -415,11 +417,11 @@ public class Program {
Repository trackRepository = result.getRepository().getTrack();
trackRepository.startTracking();
trackRepository.addBalance(toAddress, msg.getEndowment().value());
trackRepository.addBalance(contextAddress, msg.getEndowment().value());
ProgramInvoke programInvoke =
ProgramInvokeFactory.createProgramInvoke(this, msg.getToAddress(),
msg.getEndowment(), msg.getGas(), result.getRepository().getBalance(toAddress),
ProgramInvokeFactory.createProgramInvoke(this, msg.getCodeAddress(),
msg.getEndowment(), msg.getGas(), result.getRepository().getBalance(contextAddress),
data.array(),
trackRepository, this.invokeData.getCallDeep() + 1);
@ -436,7 +438,7 @@ public class Program {
if (result != null &&
result.getException() != null &&
result.getException() instanceof Program.OutOfGasException) {
logger.info("contract run halted by OutOfGas: contract={}" , Hex.toHexString(toAddress));
logger.info("contract run halted by OutOfGas: contract={}" , Hex.toHexString(contextAddress));
trackRepository.rollback();
stackPushZero();

View File

@ -98,14 +98,13 @@ public class VM {
break;
case SSTORE:
program.stackRequire(2);
// for gas calculations [YP 9.2]
DataWord newValue = stack.get(stack.size()-2);
DataWord oldValue = program.storageLoad(stack.peek());
if (oldValue == null && !newValue.isZero()) {
if (oldValue == null && !newValue.isZero())
gasCost = GasCost.SSTORE * 2;
} else if (oldValue != null && newValue.isZero()) {
else if (oldValue != null && newValue.isZero())
gasCost = GasCost.SSTORE * 0;
} else
else
gasCost = GasCost.SSTORE;
break;
case SLOAD:
@ -118,36 +117,36 @@ public class VM {
// These all operate on memory and therefore potentially expand it:
case MSTORE:
program.stackRequire(2);
newMemSize = stack.peek().value().add(BigInteger.valueOf(32));
newMemSize = memNeeded(stack.peek(), new DataWord(32));
break;
case MSTORE8:
program.stackRequire(2);
newMemSize = stack.peek().value().add(BigInteger.ONE);
newMemSize = memNeeded(stack.peek(), new DataWord(1));
break;
case MLOAD:
program.stackRequire(1);
newMemSize = stack.peek().value().add(BigInteger.valueOf(32));
newMemSize = memNeeded(stack.peek(), new DataWord(32));
break;
case RETURN:
program.stackRequire(2);
newMemSize = stack.peek().value().add(stack.get(stack.size()-2).value());
newMemSize = memNeeded(stack.peek(), stack.get(stack.size()-2));
break;
case SHA3:
program.stackRequire(2);
gasCost = GasCost.SHA3;
newMemSize = stack.peek().value().add(stack.get(stack.size()-2).value());
newMemSize = memNeeded(stack.peek(), stack.get(stack.size()-2));
break;
case CALLDATACOPY:
program.stackRequire(3);
newMemSize = stack.peek().value().add(stack.get(stack.size()-3).value());
newMemSize = memNeeded(stack.peek(), stack.get(stack.size()-3));
break;
case CODECOPY:
program.stackRequire(3);
newMemSize = stack.peek().value().add(stack.get(stack.size()-3).value());
newMemSize = memNeeded(stack.peek(), stack.get(stack.size()-3));
break;
case EXTCODECOPY:
program.stackRequire(4);
newMemSize = stack.get(stack.size()-2).value().add(stack.get(stack.size()-4).value());
newMemSize = memNeeded(stack.get(stack.size()-2), stack.get(stack.size()-4));
break;
case CALL: case CALLSTATELESS:
program.stackRequire(7);
@ -157,9 +156,9 @@ public class VM {
throw program.new OutOfGasException();
}
callGas = callGasWord.longValue();
BigInteger x = stack.get(stack.size()-4).value().add(stack.get(stack.size()-5).value()); // in offset+size
BigInteger y = stack.get(stack.size()-6).value().add(stack.get(stack.size()-7).value()); // out offset+size
newMemSize = x.max(y);
BigInteger in = memNeeded(stack.get(stack.size()-4), stack.get(stack.size()-5)); // in offset+size
BigInteger out = memNeeded(stack.get(stack.size()-6), stack.get(stack.size()-7)); // out offset+size
newMemSize = in.max(out);
break;
case POST:
program.stackRequire(5);
@ -168,7 +167,7 @@ public class VM {
case CREATE:
program.stackRequire(3);
gasCost = GasCost.CREATE;
newMemSize = stack.get(stack.size()-2).value().add(stack.get(stack.size()-3).value());
newMemSize = memNeeded(stack.get(stack.size()-2), stack.get(stack.size()-3));
break;
default:
break;
@ -877,7 +876,7 @@ public class VM {
} break;
case CALL: case CALLSTATELESS: {
DataWord gas = program.stackPop();
DataWord toAddress = program.stackPop();
DataWord codeAddress = program.stackPop();
DataWord value = program.stackPop();
DataWord inDataOffs = program.stackPop();
@ -887,7 +886,7 @@ public class VM {
DataWord outDataSize = program.stackPop();
if (logger.isInfoEnabled()) {
hint = "addr: " + Hex.toHexString(toAddress.getLast20Bytes())
hint = "addr: " + Hex.toHexString(codeAddress.getLast20Bytes())
+ " gas: " + gas.shortHex()
+ " inOff: " + inDataOffs.shortHex()
+ " inSize: " + inDataSize.shortHex();
@ -899,8 +898,8 @@ public class VM {
MessageCall msg = new MessageCall(
op.equals(CALL) ? MsgType.CALL : MsgType.STATELESS,
gas, toAddress, program.getOwnerAddress(), value,
inDataOffs, inDataSize, outDataOffs, outDataSize);
gas, codeAddress, value, inDataOffs, inDataSize,
outDataOffs, outDataSize);
program.callToAddress(msg);
program.step();
@ -908,14 +907,14 @@ public class VM {
case POST:{
program.stackRequire(5);
DataWord gas = program.stackPop();
DataWord toAddress = program.stackPop();
DataWord codeAddress= program.stackPop();
DataWord value = program.stackPop();
DataWord inDataOffs = program.stackPop();
DataWord inDataSize = program.stackPop();
if (logger.isInfoEnabled()) {
hint = "addr: " + Hex.toHexString(toAddress.getLast20Bytes())
hint = "addr: " + Hex.toHexString(codeAddress.getLast20Bytes())
+ " gas: " + gas.shortHex()
+ " inOff: " + inDataOffs.shortHex()
+ " inSize: " + inDataSize.shortHex();
@ -925,9 +924,9 @@ public class VM {
program.invokeData.getCallDeep(), hint);
}
MessageCall msgCall = new MessageCall(MsgType.POST, gas, toAddress,
program.getOwnerAddress(), value, inDataOffs, inDataSize);
program.queue(msgCall);
MessageCall msgCall = new MessageCall(MsgType.POST, gas,
codeAddress, value, inDataOffs, inDataSize);
program.getMessageQueue().add(msgCall);
program.step();
} break;
@ -997,6 +996,20 @@ public class VM {
}
}
/**
* Utility to calculate new total memory size needed for an operation.
* <br/> Basically just offset + size, unless size is 0, in which case the result is also 0.
*
* @param offset starting position of the memory
* @param size number of bytes needed
* @return offset + size, unless size is 0. In that case memNeeded is also 0.
*/
private BigInteger memNeeded(DataWord offset, DataWord size) {
if (size.isZero())
return BigInteger.ZERO;
return offset.value().add(size.value());
}
/*
* Dumping the VM state at the current operation in various styles
* - standard Not Yet Implemented

View File

@ -148,7 +148,7 @@ public class ProgramMemoryTest {
int size = 0;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(96, program.getMemSize());
assertEquals(64, program.getMemSize());
}
/************************************************/
@ -162,7 +162,7 @@ public class ProgramMemoryTest {
int size = 0;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(32, program.getMemSize());
assertEquals(0, program.getMemSize());
}
@Test