Skip to content
This repository has been archived by the owner on Oct 28, 2022. It is now read-only.

Gas support #12

Merged
merged 3 commits into from
Oct 19, 2020
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
57 changes: 56 additions & 1 deletion include/ScillaVM/JITD.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ struct ScillaParams {
};

// Each ScillaJIT object compiles an LLVM-IR module and provides access
// to the symbols inside it.
// to the symbols inside it. It is recommended use ScillaJIT_Safe,
// which hides many private methods in this class and makes it safer.
class ScillaJIT {
private:
// Use the Create method to build a ScillaJIT object.
Expand Down Expand Up @@ -132,12 +133,19 @@ class ScillaJIT {
ScillaCacheManager * = nullptr);
// Get address for @Symbol inside the compiled IR, ready to be used.
void *getAddressFor(const std::string &Symbol);

// Initialize gas-remaining field in the code and initialize libraries.
uint64_t *initGasAndLibs(uint64_t GasRem);
// Execute a message.
Json::Value execMsg(const std::string &Balance, uint64_t GasLimit,
Json::Value &Msg);
// Initialize the contract state to field initialization values in the source.
// This is to be called only during deployment of the contract. Never again.
Json::Value initState(uint64_t GasLimit);
// What's the gas remaining from previous execution (initState / execMsg).
// Useful if execution was interrupted due to an exception.
// Use with care if you don't want to end up with a stale value.
uint64_t getGasRem();

// Allocate and own the memory for code owned by this object.
void *sAlloc(size_t Size);
Expand All @@ -152,4 +160,51 @@ class ScillaJIT {
~ScillaJIT();
};

// Typical usage:
// ScillaJIT_Safe::init(); : One time ever
// 1. auto SJ = ScillaJIT_Safe::create(...);
// 2. (a) Deployment (b) Transition execution
// a. auto Output = ScillaJIT_Safe::initState(...);
// OR
// b. auto Output = ScillaJIT_Safe::execMsg(...);
// 3. If ScillaError exception, check remaining gas with getGasRem().
class ScillaJIT_Safe : private ScillaJIT {
public:
// One time initialization.
static void init() { ScillaJIT::init(); }
// JIT Compile LLVM-IR @FileName. ModuleID is derived from @FileName.
// Optionally, a cache manager can be provided.
static std::unique_ptr<ScillaJIT_Safe>
create(const ScillaParams &SPs, const std::string &FileName,
const Json::Value &ContrParams, ScillaCacheManager *SCM = nullptr) {
ScillaJIT *Ptr =
ScillaJIT::create(SPs, FileName, ContrParams, SCM).release();
return std::unique_ptr<ScillaJIT_Safe>(static_cast<ScillaJIT_Safe *>(Ptr));
}
// JIT Compile LLVM-IR in @IR, affixing @ModuleID to it.
// Optionally, a cache manager can be provided.
static std::unique_ptr<ScillaJIT_Safe>
create(const ScillaParams &SPs, const std::string &IR,
const std::string &ModuleID, const Json::Value &ContrParams,
ScillaCacheManager *SCM = nullptr) {
ScillaJIT *Ptr =
ScillaJIT::create(SPs, IR, ModuleID, ContrParams, SCM).release();
return std::unique_ptr<ScillaJIT_Safe>(static_cast<ScillaJIT_Safe *>(Ptr));
}
// Execute a message.
Json::Value execMsg(const std::string &Balance, uint64_t GasLimit,
Json::Value &Msg) {
return ScillaJIT::execMsg(Balance, GasLimit, Msg);
}
// Initialize the contract state to field initialization values in the source.
// This is to be called only during deployment of the contract. Never again.
Json::Value initState(uint64_t GasLimit) {
return ScillaJIT::initState(GasLimit);
}
// What's the gas remaining from previous execution (initState / execMsg).
// Useful if execution was interrupted due to an exception.
// Use with care if you don't want to end up with a stale value.
uint64_t getGasRem() { return ScillaJIT::getGasRem(); }
};

} // namespace ScillaVM
32 changes: 26 additions & 6 deletions libjitd/JITD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,10 +294,7 @@ std::unique_ptr<ScillaJIT> ScillaJIT::create(const ScillaParams &SPs,
// Set execptr in the generated code to THIS
auto ExecPtr = THIS->getAddressFor("_execptr");
*reinterpret_cast<ScillaJIT **>(ExecPtr) = THIS;
// Call the library initialisation function
auto initLibs =
reinterpret_cast<void (*)()>(THIS->getAddressFor("_init_libs"));
initLibs();

// Initialize contract parameters.
THIS->initContrParams(ContrParams);

Expand Down Expand Up @@ -350,14 +347,34 @@ void ScillaJIT::initContrParams(const Json::Value &CP) {
}
}

uint64_t *ScillaJIT::initGasAndLibs(uint64_t GasLimit) {
// Set gas limit in the JIT'ed code module.
auto GasRemPtr = reinterpret_cast<uint64_t *>(getAddressFor("_gasrem"));
*GasRemPtr = GasLimit;

// Call the library initialisation function.
auto initLibs =
reinterpret_cast<void (*)()>(getAddressFor("_init_libs"));
initLibs();

return GasRemPtr;
}

Json::Value ScillaJIT::initState(uint64_t GasLimit) {

auto GasRemPtr = initGasAndLibs(GasLimit);

// Let's setup the TransitionState for this transition.
TS = std::make_unique<TransitionState>("0", "0", GasLimit);
TS = std::make_unique<TransitionState>("0", "0", GasRemPtr);
auto fIS = reinterpret_cast<void (*)(void)>(getAddressFor("_init_state"));
fIS();
return TS->finalize();
}

uint64_t ScillaJIT::getGasRem() {
return *reinterpret_cast<uint64_t *>(getAddressFor("_gasrem"));
}

void *ScillaJIT::getAddressFor(const std::string &Symbol) {

auto SA = Jitter->lookup(Symbol);
Expand Down Expand Up @@ -402,8 +419,11 @@ Json::Value ScillaJIT::execMsg(const std::string &Balance, uint64_t GasLimit,
!AmountJ.isString())
CREATE_ERROR("Invalid Message");

auto GasRemPtr = initGasAndLibs(GasLimit);

// Let's setup the TransitionState for this transition.
TS = std::make_unique<TransitionState>(Balance, AmountJ.asString(), GasLimit);
TS =
std::make_unique<TransitionState>(Balance, AmountJ.asString(), GasRemPtr);

// Amount and Sender need to be prepended to the parameter list.
Json::Value AmountParam;
Expand Down
40 changes: 36 additions & 4 deletions libsrtl/ScillaBuiltins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ std::vector<ScillaFunctionsMap> getAllScillaBuiltins(void) {
ScillaFunctionsMap m[] = {
{"_print_scilla_val", (void *) _print_scilla_val},
{"_salloc", (void *) _salloc},
{"_out_of_gas", (void *) _out_of_gas},
{"_add_Int32", (void *) _add_Int32},
{"_add_Int64", (void *) _add_Int64},
{"_add_Int128", (void *) _add_Int128},
Expand Down Expand Up @@ -68,6 +69,8 @@ std::vector<ScillaFunctionsMap> getAllScillaBuiltins(void) {
{"_contains", (void *) _contains},
{"_remove", (void *) _remove},
{"_size", (void *) _size},
{"_literal_cost", (void *) _literal_cost},
{"_mapsortcost", (void *) _mapsortcost},
};
// clang-format on

Expand Down Expand Up @@ -124,7 +127,7 @@ Json::Value TransitionState::finalize(void) {
Es = Json::arrayValue;

// 3. Fill in other fields.
OutJ["gas_remaining"] = std::to_string(GasRemaining);
OutJ["gas_remaining"] = std::to_string(*GasRemPtr);
OutJ["_accepted"] = Accepted ? "true" : "false";
OutJ["scilla_major_version"] = "0";

Expand Down Expand Up @@ -228,6 +231,8 @@ void _print_scilla_val(const ScillaTypes::Typ *T, void *V) {

void *_salloc(ScillaJIT *SJ, size_t size) { return SJ->sAlloc(size); }

void _out_of_gas() { CREATE_ERROR("Ran out of gas"); }

ScillaTypes::Int32 _add_Int32(ScillaTypes::Int32 Lhs, ScillaTypes::Int32 Rhs) {
return SafeInt32(&Lhs) + SafeInt32(&Rhs);
}
Expand Down Expand Up @@ -432,7 +437,7 @@ void *_to_nat(ScillaJIT *SJ, ScillaTypes::Uint32 UI) {
return MemPrev;
}

void _send(ScillaJIT *SJ, const ScillaTypes::Typ *T, void *V) {
void _send(ScillaJIT *SJ, const ScillaTypes::Typ *T, const void *V) {
auto J = ScillaValues::toJSON(T, V);
// J is a Scilla list of Messages. Form a JSON array instead.
// TODO: Consider having a Scilla List -> std::vector and calling
Expand Down Expand Up @@ -460,12 +465,39 @@ void _send(ScillaJIT *SJ, const ScillaTypes::Typ *T, void *V) {
}
}

void _event(ScillaJIT *SJ, const ScillaTypes::Typ *T, void *V) {
uint64_t _literal_cost (const ScillaTypes::Typ *T, const void *V) {
return ScillaValues::literalCost(T, V);
}

uint64_t _mapsortcost (const ScillaParams::MapValueT *M) {
uint64_t Cost = 0;

// First calculate cost for sub-maps (if any).
for (auto &Itr : *M) {
if (boost::has_type<std::string>(Itr.second)) {
break;
}
ASSERT(boost::has_type<ScillaParams::MapValueT>(Itr.second));
auto *SubM = &boost::any_cast<const ScillaParams::MapValueT &>(Itr.second);
Cost += _mapsortcost(SubM);
}

// Cost of sorting *this* map.
auto Len = M->size();
if (Len > 0) {
auto LogLen = static_cast<int>(log(static_cast<float>(Len)));
Cost += (Len * LogLen);
}

return Cost;
}

void _event(ScillaJIT *SJ, const ScillaTypes::Typ *T, const void *V) {
auto J = ScillaValues::toJSON(T, V);
SJ->TS->processEvent(J);
}

void _throw(ScillaJIT *SJ, const ScillaTypes::Typ *T, void *V) {
void _throw(ScillaJIT *SJ, const ScillaTypes::Typ *T, const void *V) {
(void)SJ;
auto J = ScillaValues::toJSON(T, V);
SCILLA_EXCEPTION("Exception thrown: " + J.toStyledString());
Expand Down
20 changes: 14 additions & 6 deletions libsrtl/ScillaBuiltins.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ std::vector<ScillaFunctionsMap> getAllScillaBuiltins(void);
class TransitionState {
SafeUint128 Balance;
SafeUint128 InAmount;
uint64_t GasRemaining;
uint64_t *GasRemPtr;
bool Accepted;
// Contains the output messages of executing a transition.
Json::Value OutJ;
Expand All @@ -47,8 +47,8 @@ class TransitionState {

public:
TransitionState(std::string Balance_P, std::string InAmount_P,
uint64_t GasLimit_P)
: Balance(Balance_P), InAmount(InAmount_P), GasRemaining(GasLimit_P),
uint64_t *GasRemPtr_P)
: Balance(Balance_P), InAmount(InAmount_P), GasRemPtr(GasRemPtr_P),
Accepted(false), OutJ(Json::objectValue){};

void processSend(Json::Value &M);
Expand All @@ -72,6 +72,9 @@ void _print_scilla_val(const ScillaVM::ScillaTypes::Typ *T, void *V);
// Allocate memory for JIT code owned by @SJ
void *_salloc(ScillaVM::ScillaJIT *SJ, size_t size);

// Handler for out-of-gas during execution
void _out_of_gas();

// Fetch field @Name whose type is @T. For map accesses, FetchVal can be false,
// to indicate that the return value is a Scilla `Bool`, indicating found or
// not.
Expand Down Expand Up @@ -135,13 +138,13 @@ uint8_t *_eq_Uint256(ScillaVM::ScillaJIT *SJ,
void *_to_nat(ScillaVM::ScillaJIT *SJ, ScillaVM::ScillaTypes::Uint32 UI);

void _send(ScillaVM::ScillaJIT *SJ, const ScillaVM::ScillaTypes::Typ *T,
void *V);
const void *V);

void _event(ScillaVM::ScillaJIT *SJ, const ScillaVM::ScillaTypes::Typ *T,
void *V);
const void *V);

void _throw(ScillaVM::ScillaJIT *SJ, const ScillaVM::ScillaTypes::Typ *T,
void *V);
const void *V);

uint8_t *_eq_String(ScillaVM::ScillaJIT *SJ, ScillaVM::ScillaTypes::String Lhs,
ScillaVM::ScillaTypes::String Rhs);
Expand Down Expand Up @@ -178,7 +181,12 @@ void *_contains(ScillaVM::ScillaJIT *SJ, const ScillaVM::ScillaTypes::Typ *T,

void *_remove(ScillaVM::ScillaJIT *SJ, const ScillaVM::ScillaTypes::Typ *T,
const ScillaVM::ScillaParams::MapValueT *M, const void *K);

// Scilla builtin _size : The size of a map.
ScillaVM::ScillaTypes::Uint32 _size(const ScillaVM::ScillaParams::MapValueT *M);

uint64_t _literal_cost (const ScillaVM::ScillaTypes::Typ *T, const void *V);
uint64_t _mapsortcost (const ScillaVM::ScillaParams::MapValueT *M);

} // extern "C"
#pragma clang diagnostic pop
Loading