Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Charge gas for EEI methods #86

Merged
merged 1 commit into from
Mar 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 80 additions & 4 deletions src/eei.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ namespace HeraVM {
HERA_DEBUG << "getGasLeft\n";

static_assert(is_same<decltype(result.gasLeft), uint64_t>::value, "uint64_t type expected");

takeGas(GasSchedule::base);

return Literal(result.gasLeft);
}

Expand All @@ -164,6 +167,8 @@ namespace HeraVM {

storeUint160(msg.destination, resultOffset);

takeGas(GasSchedule::base);

return Literal();
}

Expand All @@ -175,6 +180,8 @@ namespace HeraVM {

evm_address address = loadUint160(addressOffset);
evm_uint256be result;

takeGas(GasSchedule::balance);
context->fn_table->get_balance(&result, context, &address);
storeUint128(result, resultOffset);

Expand All @@ -188,6 +195,8 @@ namespace HeraVM {
HERA_DEBUG << "getBlockHash " << hex << number << " " << resultOffset << dec << "\n";

evm_uint256be blockhash;

takeGas(GasSchedule::blockhash);
context->fn_table->get_block_hash(&blockhash, context, number);
storeUint256(blockhash, resultOffset);

Expand All @@ -196,6 +205,9 @@ namespace HeraVM {

if (import->base == Name("getCallDataSize")) {
HERA_DEBUG << "callDataSize\n";

takeGas(GasSchedule::base);

return Literal(static_cast<uint32_t>(msg.input_size));
}

Expand All @@ -206,6 +218,13 @@ namespace HeraVM {

HERA_DEBUG << "callDataCopy " << hex << resultOffset << " " << dataOffset << " " << length << dec << "\n";

heraAssert(ffs(GasSchedule::copy) + (ffs(length) - 5) <= 64, "Gas charge overflow");
heraAssert(
numeric_limits<uint64_t>::max() - GasSchedule::verylow >= GasSchedule::copy * ((uint64_t(length) + 31) / 32),
"Gas charge overflow"
);
takeGas(GasSchedule::verylow + GasSchedule::copy * ((uint64_t(length) + 31) / 32));

vector<uint8_t> input(msg.input_data, msg.input_data + msg.input_size);
storeMemory(input, dataOffset, resultOffset, length);

Expand All @@ -217,6 +236,7 @@ namespace HeraVM {

HERA_DEBUG << "getCaller " << hex << resultOffset << dec << "\n";

takeGas(GasSchedule::base);
storeUint160(msg.sender, resultOffset);

return Literal();
Expand All @@ -227,6 +247,7 @@ namespace HeraVM {

HERA_DEBUG << "getCallValue " << hex << resultOffset << dec << "\n";

takeGas(GasSchedule::base);
storeUint128(msg.value, resultOffset);

return Literal();
Expand All @@ -239,6 +260,12 @@ namespace HeraVM {

HERA_DEBUG << "codeCopy " << hex << resultOffset << " " << codeOffset << " " << length << dec << "\n";

heraAssert(ffs(GasSchedule::copy) + (ffs(length) - 5) <= 64, "Gas charge overflow");
heraAssert(
numeric_limits<uint64_t>::max() - GasSchedule::verylow >= GasSchedule::copy * ((uint64_t(length) + 31) / 32),
"Gas charge overflow"
);
takeGas(GasSchedule::verylow + GasSchedule::copy * ((uint64_t(length) + 31) / 32));
storeMemory(code, codeOffset, resultOffset, length);

return Literal();
Expand All @@ -247,6 +274,8 @@ namespace HeraVM {
if (import->base == Name("getCodeSize")) {
HERA_DEBUG << "getCodeSize\n";

takeGas(GasSchedule::base);

return Literal(static_cast<uint32_t>(code.size()));
}

Expand All @@ -262,8 +291,10 @@ namespace HeraVM {
const uint8_t *code;
size_t code_size = context->fn_table->get_code(&code, context, &address);

heraAssert(ffs(GasSchedule::copy) + (ffs(length) - 5) <= 64, "Gas charge overflow");
heraAssert(numeric_limits<uint64_t>::max() - GasSchedule::extcode >= GasSchedule::copy * ((uint64_t(length) + 31) / 32), "Gas charge overflow");
takeGas(GasSchedule::extcode + GasSchedule::copy * ((uint64_t(length) + 31) / 32));
// NOTE: code will be freed by the callee (client)

// FIXME: optimise this so not vector needs to be created
storeMemory(vector<uint8_t>(code, code + code_size), codeOffset, resultOffset, length);

Expand All @@ -276,6 +307,7 @@ namespace HeraVM {
HERA_DEBUG << "getExternalCodeSize " << hex << addressOffset << dec << "\n";

evm_address address = loadUint160(addressOffset);
takeGas(GasSchedule::extcode);
size_t code_size = context->fn_table->get_code(NULL, context, &address);

return Literal(static_cast<uint32_t>(code_size));
Expand All @@ -287,6 +319,8 @@ namespace HeraVM {
HERA_DEBUG << "getBlockCoinbase " << hex << resultOffset << dec << "\n";

evm_tx_context tx_context;

takeGas(GasSchedule::base);
context->fn_table->get_tx_context(&tx_context, context);
storeUint160(tx_context.block_coinbase, resultOffset);

Expand All @@ -299,6 +333,8 @@ namespace HeraVM {
HERA_DEBUG << "getBlockDifficulty " << hex << offset << dec << "\n";

evm_tx_context tx_context;

takeGas(GasSchedule::base);
context->fn_table->get_tx_context(&tx_context, context);
storeUint256(tx_context.block_difficulty, offset);

Expand All @@ -309,9 +345,12 @@ namespace HeraVM {
HERA_DEBUG << "getBlockGasLimit\n";

evm_tx_context tx_context;

takeGas(GasSchedule::base);
context->fn_table->get_tx_context(&tx_context, context);

static_assert(is_same<decltype(tx_context.block_gas_limit), int64_t>::value, "int64_t type expected");

return Literal(tx_context.block_gas_limit);
}

Expand All @@ -321,6 +360,8 @@ namespace HeraVM {
HERA_DEBUG << "getTxGasPrice " << hex << valueOffset << dec << "\n";

evm_tx_context tx_context;

takeGas(GasSchedule::base);
context->fn_table->get_tx_context(&tx_context, context);
storeUint128(tx_context.tx_gas_price, valueOffset);

Expand All @@ -335,7 +376,6 @@ namespace HeraVM {
HERA_DEBUG << "log " << hex << dataOffset << " " << length << " " << numberOfTopics << dec << "\n";

heraAssert(!(msg.flags & EVM_STATIC), "\"log\" attempted in static mode");

heraAssert(numberOfTopics <= 4, "Too many topics specified");

evm_uint256be topics[numberOfTopics];
Expand All @@ -347,6 +387,12 @@ namespace HeraVM {
vector<uint8_t> data(length);
loadMemory(dataOffset, data, length);

heraAssert(ffs(length) + ffs(GasSchedule::logData) <= 64, "Gas charge overflow");
heraAssert(
numeric_limits<uint64_t>::max() - (GasSchedule::log + GasSchedule::logTopic * numberOfTopics) >= static_cast<uint64_t>(length) * GasSchedule::logData,
"Gas charge overflow"
);
takeGas(GasSchedule::log + (length * GasSchedule::logData) + (GasSchedule::logTopic * numberOfTopics));
context->fn_table->emit_log(context, &msg.destination, data.data(), length, topics, numberOfTopics);

return Literal();
Expand All @@ -356,19 +402,25 @@ namespace HeraVM {
HERA_DEBUG << "getBlockNumber\n";

evm_tx_context tx_context;

takeGas(GasSchedule::base);
context->fn_table->get_tx_context(&tx_context, context);

static_assert(is_same<decltype(tx_context.block_number), int64_t>::value, "int64_t type expected");

return Literal(tx_context.block_number);
}

if (import->base == Name("getBlockTimestamp")) {
HERA_DEBUG << "getBlockTimestamp\n";

evm_tx_context tx_context;

takeGas(GasSchedule::base);
context->fn_table->get_tx_context(&tx_context, context);

static_assert(is_same<decltype(tx_context.block_timestamp), int64_t>::value, "int64_t type expected");

return Literal(tx_context.block_timestamp);
}

Expand All @@ -378,6 +430,8 @@ namespace HeraVM {
HERA_DEBUG << "getTxOrigin " << hex << resultOffset << dec << "\n";

evm_tx_context tx_context;

takeGas(GasSchedule::base);
context->fn_table->get_tx_context(&tx_context, context);
storeUint160(tx_context.tx_origin, resultOffset);

Expand All @@ -394,8 +448,8 @@ namespace HeraVM {

evm_uint256be path = loadUint256(pathOffset);
evm_uint256be value = loadUint256(valueOffset);

evm_uint256be current;

context->fn_table->get_storage(&current, context, &msg.destination, &path);

// We do not need to take care about the delete case (gas refund), the client does it.
Expand All @@ -417,8 +471,9 @@ namespace HeraVM {
HERA_DEBUG << "storageLoad " << hex << pathOffset << " " << resultOffset << dec << "\n";

evm_uint256be path = loadUint256(pathOffset);

evm_uint256be result;

takeGas(GasSchedule::storageLoad);
context->fn_table->get_storage(&result, context, &msg.destination, &path);

storeUint256(result, resultOffset);
Expand All @@ -442,6 +497,9 @@ namespace HeraVM {

if (import->base == Name("getReturnDataSize")) {
HERA_DEBUG << "getReturnDataSize\n";

takeGas(GasSchedule::base);

return Literal(static_cast<uint32_t>(lastReturnData.size()));
}

Expand All @@ -452,6 +510,7 @@ namespace HeraVM {

HERA_DEBUG << "returnDataCopy " << hex << dataOffset << " " << offset << " " << size << dec << "\n";

takeGas(GasSchedule::verylow);
storeMemory(lastReturnData, offset, dataOffset, size);

return Literal();
Expand Down Expand Up @@ -527,6 +586,13 @@ namespace HeraVM {
}

evm_result call_result;

if (import->base == Name("call") && !context->fn_table->account_exists(context, &call_message.destination))
takeGas(GasSchedule::callNewAccount);
if (!isZeroUint256(call_message.value))
takeGas(GasSchedule::valuetransfer);
takeGas(call_message.gas);
takeGas(GasSchedule::call);
context->fn_table->call(&call_result, context, &call_message);

if (call_result.output_data) {
Expand All @@ -538,6 +604,9 @@ namespace HeraVM {
if (call_result.release)
call_result.release(&call_result);

/* Return unspent gas */
result.gasLeft += call_result.gas_left;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this to work, the entire passed gas must be used via takeGas before doing an EVM-C call.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done by just performing:
takeGas(gas);
before the call execution, correct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep


switch (call_result.status_code) {
case EVM_SUCCESS:
return Literal(uint32_t(0));
Expand Down Expand Up @@ -583,7 +652,11 @@ namespace HeraVM {
create_message.flags = 0;

evm_result create_result;

takeGas(create_message.gas);
takeGas(GasSchedule::create);
context->fn_table->call(&create_result, context, &create_message);

if (create_result.status_code == EVM_SUCCESS) {
storeUint160(create_result.create_address, resultOffset);
lastReturnData.clear();
Expand Down Expand Up @@ -615,6 +688,9 @@ namespace HeraVM {

evm_address address = loadUint160(addressOffset);

if (!context->fn_table->account_exists(context, &address))
takeGas(GasSchedule::callNewAccount);
takeGas(GasSchedule::selfdestruct);
context->fn_table->selfdestruct(context, &msg.destination, &address);

return Literal();
Expand Down
15 changes: 15 additions & 0 deletions src/eei.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,23 @@ struct EthereumInterface : ShellExternalInterface {
};

struct GasSchedule {
static constexpr unsigned storageLoad = 200;
static constexpr unsigned storageStoreCreate = 20000;
static constexpr unsigned storageStoreChange = 5000;
static constexpr unsigned log = 375;
static constexpr unsigned logData = 8;
static constexpr unsigned logTopic = 375;
static constexpr unsigned create = 32000;
static constexpr unsigned call = 700;
static constexpr unsigned copy = 3;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which instructions does this copy refer to?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the copies except externalCodeCopy, hence its general naming.

static constexpr unsigned blockhash = 800;
static constexpr unsigned balance = 400;
static constexpr unsigned base = 2;
static constexpr unsigned verylow = 3;
static constexpr unsigned extcode = 700;
static constexpr unsigned selfdestruct = 5000;
static constexpr unsigned valuetransfer = 9000;
static constexpr unsigned callNewAccount = 25000;
};

}