poc-7 gas adjustments:

+ EXP new cost
+ Transaction data new cost
+ mem data copy size cost
+ small fixes
This commit is contained in:
romanman 2014-12-01 07:25:25 +01:00
parent 883ca6e5dc
commit 1f22e1e9f9
16 changed files with 217 additions and 30 deletions

View File

@ -150,6 +150,7 @@ public class AccountState {
accountState.setNonce(this.getNonce());
accountState.setCodeHash(this.getCodeHash());
accountState.setStateRoot(this.getStateRoot());
accountState.setDirty(false);
return accountState;
}

View File

@ -9,7 +9,6 @@ import org.ethereum.manager.WorldManager;
import org.ethereum.net.BlockQueue;
import org.ethereum.net.server.ChannelManager;
import org.ethereum.util.AdvancedDeviceUtils;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -321,14 +320,17 @@ public class BlockchainImpl implements Blockchain {
int i = 0;
long totalGasUsed = 0;
for (Transaction tx : block.getTransactionsList()) {
stateLogger.debug("apply block: [{}] tx: [{}] ", block.getNumber(), i);
stateLogger.info("apply block: [{}] tx: [{}] ", block.getNumber(), i);
TransactionReceipt receipt = applyTransaction(block, tx);
totalGasUsed += receipt.getCumulativeGasLong();
receipt.setCumulativeGas(totalGasUsed);
track.commit();
receipt.setPostTxState(repository.getRoot());
stateLogger.info("executed block: [{}] tx: [{}] \n state: [{}]", block.getNumber(), i,
Hex.toHexString(repository.getRoot()));
if(block.getNumber() >= CONFIG.traceStartBlock())
repository.dumpState(block, totalGasUsed, i++, tx.getHash());
@ -552,8 +554,8 @@ public class BlockchainImpl implements Blockchain {
}
trackTx.commit();
} else {
// REFUND GASDEBIT EXCEPT FOR FEE (500 + 5*TXDATA)
long dataCost = tx.getData() == null ? 0: tx.getData().length * GasCost.TXDATA;
// REFUND GASDEBIT EXCEPT FOR FEE (500 + 5*TX_NO_ZERO_DATA)
long dataCost = tx.getData() == null ? 0: tx.getData().length * GasCost.TX_NO_ZERO_DATA;
gasUsed = GasCost.TRANSACTION + dataCost;
BigInteger refund = gasDebit.subtract(BigInteger.valueOf(gasUsed).multiply(gasPrice));

View File

@ -88,16 +88,15 @@ public class RepositoryImpl implements Repository {
public void updateBatch(HashMap<ByteArrayWrapper, AccountState> stateCache,
HashMap<ByteArrayWrapper, ContractDetails> detailsCache) {
WriteBatch writeBatch = detailsDB.getDb().createWriteBatch();
for (ByteArrayWrapper hash : detailsCache.keySet()) {
ContractDetails contractDetails = detailsCache.get(hash);
if (contractDetails.isDeleted())
writeBatch.delete(hash.getData());
detailsDB.delete(hash.getData());
else{
if (contractDetails.isDirty())
writeBatch.put(hash.getData(), contractDetails.getEncoded());
detailsDB.put(hash.getData(), contractDetails.getEncoded());
}
if (contractDetails.isDirty() || contractDetails.isDeleted()){
@ -110,7 +109,6 @@ public class RepositoryImpl implements Repository {
contractDetails.setDeleted(false);
contractDetails.setDirty(false);
}
detailsDB.getDb().write(writeBatch);
for (ByteArrayWrapper hash : detailsCache.keySet()) {
@ -119,8 +117,18 @@ public class RepositoryImpl implements Repository {
if (accountState.isDeleted())
worldState.delete(hash.getData());
else{
if (accountState.isDirty())
if (accountState.isDirty()){
worldState.update(hash.getData(), accountState.getEncoded());
if (logger.isDebugEnabled()){
logger.debug("update: [{}],nonce: [{}] balance: [{}]",
Hex.toHexString(hash.getData()),
accountState.getNonce(),
accountState.getBalance());
}
}
}
accountState.setDeleted(false);

View File

@ -126,10 +126,12 @@ public class RepositoryTrack implements Repository {
accountState = createAccount(addr);
}
logger.trace("adding to balance addr: [{}], balance: [{}], delta: [{}]", Hex.toHexString(addr),
accountState.getBalance(), value);
BigInteger newBalance = accountState.addToBalance(value);
return accountState.addToBalance(value);
logger.trace("adding to balance addr: [{}], balance: [{}], delta: [{}]", Hex.toHexString(addr),
newBalance, value);
return newBalance;
}
@Override
@ -188,6 +190,8 @@ public class RepositoryTrack implements Repository {
public void commit() {
logger.debug("commit changes");
repository.updateBatch(cacheAccounts, cacheDetails);
cacheAccounts.clear();
cacheDetails.clear();
}

View File

@ -231,12 +231,24 @@ public class ByteUtil {
return baos.toByteArray();
}
public static int firstNonZeroByte(byte[] data){
int firstNonZero = -1;
int i = 0;
for (; i < data.length; ++i) {
if (data[i] != 0) {
firstNonZero = i;
break;
}
}
return firstNonZero;
}
public static byte[] stripLeadingZeroes(byte[] data) {
if (data == null)
return null;
int firstNonZero = 0;
int firstNonZero = firstNonZeroByte(data);
int i = 0;
for (; i < data.length; ++i) {
if (data[i] != 0) {

View File

@ -302,4 +302,10 @@ public class DataWord implements Comparable<DataWord> {
this.data[31 - i] = mask;
}
}
public int bytesOccupied(){
int firstNonZero = ByteUtil.firstNonZeroByte(data);
if (firstNonZero == -1) return 0;
return 31 - firstNonZero + 1;
}
}

View File

@ -20,8 +20,10 @@ public class GasCost {
public static int STOP = 0;
/** Cost 0 gas */
public static int SUICIDE = 0;
/** Cost 100 gas */
public static int SSTORE = 100;
/** Cost 300 gas */
public static int SSTORE = 300;
/** Cost 100 gas */
public static int RESET_SSTORE = 100;
/** Cost 100 gas */
public static int CREATE = 100;
/** Cost 20 gas */
@ -29,7 +31,9 @@ public class GasCost {
/** Cost 1 gas */
public static int MEMORY = 1;
/** Cost 5 gas */
public static int TXDATA = 5;
public static int TX_NO_ZERO_DATA = 5;
/** Cost 1 gas */
public static int TX_ZERO_DATA = 1;
/** Cost 500 gas */
public static int TRANSACTION = 500;
/** Cost 32 gas */
@ -38,4 +42,12 @@ public class GasCost {
public static int LOG_DATA_GAS = 1;
/** Cost 32 gas */
public static int LOG_TOPIC_GAS = 32;
/** Cost 1 gas */
public static int COPY_GAS = 1;
/** Cost 1 gas */
public static int EXP_GAS = 1;
/** Cost 1 gas */
public static int EXP_BYTE_GAS = 1;
}

View File

@ -19,8 +19,9 @@ public interface ProgramInvoke {
public DataWord getDataSize();
public DataWord getDataValue(DataWord indexData);
public byte[] getDataCopy(DataWord offsetData, DataWord lengthData);
public int countNonZeroData();
public DataWord getPrevHash();
public DataWord getPrevHash();
public DataWord getCoinbase();
public DataWord getTimestamp();
public DataWord getNumber();

View File

@ -31,8 +31,8 @@ public class ProgramInvokeImpl implements ProgramInvoke {
private boolean byTestingSuite = false;
private int callDeep = 0;
public ProgramInvokeImpl(DataWord address, DataWord origin, DataWord caller, DataWord balance,
DataWord gasPrice, DataWord gas, DataWord callValue, byte[] msgData,
public ProgramInvokeImpl(DataWord address, DataWord origin, DataWord caller, DataWord balance,
DataWord gasPrice, DataWord gas, DataWord callValue, byte[] msgData,
DataWord lastHash, DataWord coinbase, DataWord timestamp, DataWord number, DataWord difficulty,
DataWord gaslimit, Repository repository, int callDeep) {
@ -182,6 +182,18 @@ public class ProgramInvokeImpl implements ProgramInvoke {
return data;
}
@Override
public int countNonZeroData(){
int counter = 0;
for (int i = 0; i < msgData.length; ++i){
if (msgData[i] != 0) ++counter;
}
return counter;
}
/* PREVHASH op */
public DataWord getPrevHash() {
return prevHash;

View File

@ -146,6 +146,18 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
return new DataWord(prevHash);
}
@Override
public int countNonZeroData(){
int counter = 0;
for (int i = 0; i < msgData.length; ++i){
if (msgData[i] != 0) ++counter;
}
return counter;
}
@Override
public DataWord getCoinbase() {
byte[] coinBase = Hex.decode("E559DE5527492BCB42EC68D07DF0742A98EC3F1E");

View File

@ -76,6 +76,7 @@ public class ProgramResult {
}
public void addLogInfos(List<LogInfo> logInfos){
if (logInfos == null) return;
if (this.logInfoList == null) logInfoList = new ArrayList<>();
this.logInfoList.addAll(logInfos);
}

View File

@ -86,6 +86,7 @@ public class VM {
long oldMemSize = program.getMemSize();
BigInteger newMemSize = BigInteger.ZERO;
long copySize = 0;
Stack<DataWord> stack = program.getStack();
String hint = "";
@ -104,11 +105,12 @@ public class VM {
DataWord newValue = stack.get(stack.size()-2);
DataWord oldValue = program.storageLoad(stack.peek());
if (oldValue == null && !newValue.isZero())
gasCost = GasCost.SSTORE * 2;
gasCost = GasCost.SSTORE;
else if (oldValue != null && newValue.isZero())
gasCost = GasCost.SSTORE * 0;
// todo: GASREFUND counter policy
System.currentTimeMillis();
else
gasCost = GasCost.SSTORE;
gasCost = GasCost.RESET_SSTORE;
break;
case SLOAD:
gasCost = GasCost.SLOAD;
@ -135,12 +137,15 @@ public class VM {
newMemSize = memNeeded(stack.peek(), stack.get(stack.size()-2));
break;
case CALLDATACOPY:
copySize = stack.get(stack.size()-3).longValue();
newMemSize = memNeeded(stack.peek(), stack.get(stack.size()-3));
break;
case CODECOPY:
copySize = stack.get(stack.size()-3).longValue();
newMemSize = memNeeded(stack.peek(), stack.get(stack.size()-3));
break;
case EXTCODECOPY:
copySize = stack.get(stack.size()-4).longValue();
newMemSize = memNeeded(stack.get(stack.size()-2), stack.get(stack.size()-4));
break;
case CALL: case CALLCODE:
@ -167,7 +172,12 @@ public class VM {
GasCost.LOG_DATA_GAS * stack.get(stack.size()-2).longValue();
break;
case EXP:
DataWord exp = stack.get(stack.size()-2);
int bytesOccupied = exp.bytesOccupied();
gasCost = (bytesOccupied == 0) ? 0 : GasCost.EXP_GAS + GasCost.EXP_BYTE_GAS * bytesOccupied;
break;
default:
break;
}
@ -176,7 +186,7 @@ public class VM {
// Avoid overflows
if(newMemSize.compareTo(MAX_GAS) == 1)
throw program.new OutOfGasException();
// memory gas calc
long memoryUsage = (newMemSize.longValue() + 31) / 32 * 32;
if (memoryUsage > oldMemSize) {
@ -186,6 +196,12 @@ public class VM {
gasCost += memGas;
}
if (copySize > 0){
long copyGas = GasCost.COPY_GAS * (copySize + 31) / 32;
gasCost += copyGas;
program.spendGas(copyGas, op.name() + " (copy usage)");
}
// Log debugging line for VM
if(program.getNumber().intValue() == CONFIG.dumpBlock())
this.dumpLine(op, gasBefore, gasCost+callGas, memWords, program);
@ -750,7 +766,7 @@ public class VM {
hint = logInfo.toString();
program.getResult().addLogInfo(logInfo);
program.step();
} break;
case MLOAD:{
DataWord addr = program.stackPop();
@ -820,9 +836,12 @@ public class VM {
if (!cond.isZero()) {
int nextPC = pos.intValue(); // possible overflow
if (nextPC != 0 && program.getOp(nextPC-1) != OpCode.JUMPDEST.val())
if (nextPC != 0 && program.getOp(nextPC) != OpCode.JUMPDEST.val())
throw program.new BadJumpDestinationException();
// todo: in case destination is not JUMPDEST, check if prev was strict push
// todo: in EP: (ii) If a jump is preceded by a push, no jumpdest required;
if (logger.isInfoEnabled())
hint = "~> " + nextPC;
@ -977,7 +996,12 @@ public class VM {
// charged by CALL op
if (program.invokeData.byTransaction()) {
program.spendGas(GasCost.TRANSACTION, "TRANSACTION");
program.spendGas(GasCost.TXDATA * program.invokeData.getDataSize().intValue(), "DATA");
int dataSize = program.invokeData.getDataSize().intValue();
int nonZeroesVals = program.invokeData.countNonZeroData();
int zeroVals = dataSize - nonZeroesVals;
program.spendGas(GasCost.TX_NO_ZERO_DATA * nonZeroesVals, "DATA");
program.spendGas(GasCost.TX_ZERO_DATA * zeroVals, "DATA");
}
while(!program.isStopped())

View File

@ -254,6 +254,34 @@ public class BlockTest {
assertArrayEquals(root, worldManager.getRepository().getRoot());
}
@Test
public void testScenario4() throws URISyntaxException, IOException {
BlockchainImpl blockchain = (BlockchainImpl)worldManager.getBlockchain();
URL scenario1 = ClassLoader
.getSystemResource("blockload/scenario4.dmp");
File file = new File(scenario1.toURI());
List<String> strData = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
byte[] root = Genesis.getInstance().getStateRoot();
for(String blockRLP : strData){
Block block = new Block(
Hex.decode(blockRLP));
logger.info("sending block.hash: {}", Hex.toHexString( block.getHash() ));
blockchain.tryToConnect(block);
root = block.getStateRoot();
}
logger.info("asserting root state is: {}", Hex.toHexString( root ));
//expected root: dedd258f4cee2d1b45f137a2a74a2052e14a6d7fe1b1184be0a6adcec6a1d1d3
assertEquals(Hex.toHexString(root),
Hex.toHexString(worldManager.getRepository().getRoot()));
}
@Test
@Ignore
public void testUncleValidGenerationGap() {

View File

@ -208,4 +208,35 @@ public class ByteUtilTest {
System.out.println(System.currentTimeMillis() - start2 + "ms to reach: " + Hex.toHexString(BigIntegers.asUnsignedByteArray(4, counter2)));
}
}
@Test
public void firstNonZeroByte_1() {
byte[] data = Hex.decode("0000000000000000000000000000000000000000000000000000000000000000");
int result = ByteUtil.firstNonZeroByte(data);
assertEquals(-1, result);
}
@Test
public void firstNonZeroByte_2() {
byte[] data = Hex.decode("0000000000000000000000000000000000000000000000000000000000332211");
int result = ByteUtil.firstNonZeroByte(data);
assertEquals(29, result);
}
@Test
public void firstNonZeroByte_3() {
byte[] data = Hex.decode("2211009988776655443322110099887766554433221100998877665544332211");
int result = ByteUtil.firstNonZeroByte(data);
assertEquals(0, result);
}
}

View File

@ -1738,13 +1738,14 @@ public class VMTest {
public void testJUMPI_1() {
VM vm = new VM();
program = new Program(Hex.decode("60016006575B60CC"), invoke);
program = new Program(Hex.decode("60016005575B60CC"), invoke);
String s_expected = "00000000000000000000000000000000000000000000000000000000000000CC";
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
assertEquals(s_expected, Hex.toHexString(program.getStack().peek().getData()).toUpperCase());
}
@ -1824,7 +1825,7 @@ public class VMTest {
public void testJUMPDEST_2() {
VM vm = new VM();
program = new Program(Hex.decode("60236001600a5760015b600255"), invoke);
program = new Program(Hex.decode("6023600160095760015b600255"), invoke);
String s_expected_key = "0000000000000000000000000000000000000000000000000000000000000002";
String s_expected_val = "0000000000000000000000000000000000000000000000000000000000000023";
@ -1835,7 +1836,8 @@ public class VMTest {
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
DataWord key = new DataWord(Hex.decode(s_expected_key));
DataWord val = program.getResult().getRepository().getStorageValue(invoke.getOwnerAddress().getNoLeadZeroesData(), key);
@ -2342,7 +2344,10 @@ public class VMTest {
vm.step(program);
DataWord item1 = program.stackPop();
long gas = program.getResult().getGasUsed();
assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase());
assertEquals(4, gas);
}
@Test // EXP OP
@ -2357,12 +2362,34 @@ public class VMTest {
vm.step(program);
DataWord item1 = program.stackPop();
long gas = program.getResult().getGasUsed();
assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase());
assertEquals(2, gas);
}
@Test(expected=StackTooSmallException.class) // EXP OP mal
@Test // EXP OP
public void testEXP_3() {
VM vm = new VM();
program = new Program(Hex.decode("61112260010a"), invoke);
String s_expected_1 = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
vm.step(program);
DataWord item1 = program.stackPop();
long gas = program.getResult().getGasUsed();
assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase());
assertEquals(5, gas);
}
@Test(expected=StackTooSmallException.class) // EXP OP mal
public void testEXP_4() {
VM vm = new VM();
program = new Program(Hex.decode("621234560a"), invoke);
try {
@ -2465,7 +2492,9 @@ public class VMTest {
vm.step(program);
vm.step(program);
long gas = program.getResult().getGasUsed();
assertEquals(m_expected_1, Hex.toHexString(program.getMemory().array()).toUpperCase());
assertEquals(6, gas);
}
@Test // CODECOPY OP
@ -2482,7 +2511,9 @@ public class VMTest {
vm.step(program);
vm.step(program);
long gas = program.getResult().getGasUsed();
assertEquals(m_expected_1, Hex.toHexString(program.getMemory().array()).toUpperCase());
assertEquals(10 , gas);
}
@Test // CODECOPY OP

File diff suppressed because one or more lines are too long