Closed Jean-Lessa closed 7 months ago
Docs have been updated on sparq-docs, under a new branch called events
. Once this is merged I'll do the merge there accordingly. Would also appreciate some feedback there, it should be pretty easy to follow.
Included merges for the SDKTestSuite, global parameters in Options and other minor fixes developed on separate branches. All that's left is to merge this into development
and we should be good to go. Would appreciate a final look into it before merging. @itamarcps @jcarraror
Branch
75-events
contains an initial implementation of Solidity events in general. Some things remain to be done, but I believe things are solid enough to be merged right now (just in time for Christmas even, hopefully it'll serve as a nice gift for those who waited so long :eyes:).What was implemented
src/contract/event.{h,cpp}
has the baseEvent
class, responsible for abstracting the event itself and its data (including whether the event is anonymous, has argument indexing and topics), and theEventManager
class, responsible for maintaining emitted events in memory and the database (including built-in commit/revert logic with a temporary buffer)ContractManager
is now responsible for committing/reverting events depending on whether a contract call is successful or noteth_getLogs
was implemented in thenet/http/jsonrpc
submodule, reponsible for querying and retrieving events in the same way as it is done on Solidity (same inputs, same outputs)DB
has a new prefix:0x0008 = events
, where all of the emitted events are stored upon calling the destructor ofEventManager
(in the same fashion as theStorage
class)ContractReflectionInterface
now has a map for storing a contract's event data (eventsMap
) and two functions for generating event ABI:registerContractEvents()
(used by the contracts as an extra step for registering said events) andgetEventDataStructure()
(used byJsonAbi
to write the JSON ABI itself)SimpleContract
was updated to include an example of how to declare, register and emit events (see below for more info)blockHeight_
andblockTimestamp_
inContractGlobals
were changed fromuint256_t
touint64_t
(this was done before in theorderbook
branch but it wasn't merged (yet) so this could require a bit of attention later)DB::getKeys()
was implemented (get only the keys from a database prefix, it helps a bit)TxBlock
andContractReflectionInterface
had their code indentations fixedABI::getABIEnumFromBaseTypes
was renamed toABI::BaseTypesToEnum
and squished into 4 lines (that's a win to me)Flux of how events work in OrbiterSDK
SimpleContract
for a practical example):protected void eventName(args...) { this->emitEvent(__func__, { {arg1, indexed}, {arg2, indexed}, ... }, anonymous); }
, where__func__
is the event name,indexed
is a flag that indicates whether the argument is indexed or not, andanonymous
is a flag that indicates whether the event is anonymous or not (defaults tofalse
so you can omit it if you want)this->eventName(args...);
registerContract()
, using theregisterContractEvent()
fromContractReflectionInterface
, giving it the required parameters as tuples with the event's name, whether it is anonymous or not, and sub-tuples with each of the arguments' names, types (written as if they were in Solidity, so "uint256" instead of "uint256_t" for example), and whether they're indexed or notDynamicContract::emitEvent()
, anEvent
object is created on the spot and sent directly toContractManagerInterface::emitContractEvent()
(please note that at this specific moment the Event object lacks State data - this is on purpose, as events can be reverted if a transaction fails)ContractManagerInterface::emitContractEvent()
checks if the event was emitted during a contract call - if not, throws, otherwise the event is sent directly toEventManager::registerEvent()
EventManager::registerEvent()
adds the event to the temporary buffer so it can be properly committed or reverted laterState::processNextBlock()
provides the rest of the data required by the event, passing them throughState::processTransaction()
, thenContractManager::callContract()
, and finallyEventManager::commitEvents()
EventManager::commitEvents()
inserts the required State data on all emitted events so far in that block (by calling each event'ssetStateData()
function) and effectively commits them all by moving them to the permanent event list so they can be queried and/or saved into the database laterEventManager::revertEvents()
is called instead, discarding all events emitted so far in that blockContractReflectionInterface::registerContractEvents()
, each event tuple is registered inside theeventsMap
mapContractReflectionInterface::getEventDataStructure()
retrieves said event data and puts it into a struct thatJsonAbi
can parse when./contractabigenerator
is runJsonAbi::registerContractAndGetData()
calls the previous function and writes the event data to JSON, effectively generating each registered event's ABI alongside the rest of the contract's ABIImplementation details
Event
:topics_
is a vector with tecnically no limits in and of itself, but the class constructor actually limits how many topics can exist (3 or 4 for anonymous events)data_
fieldEventManager
:registerEvent()
andcommitEvents()
MOVE the event objects around instead of copying them (push_back(std::move(event))
instead ofpush_back(event)
)eth_getLogs
has hardcoded sane limits for querying (you can query up to a range of 2000 blocks and a total of 10000 events at once)What is missing
ContractManager::callContract()
should be revised, as there are extra parameters required now due toEventManager
existing inBlockchain
as a pointer (nullptr
was passed to those calls as a temporary fix, this is only a placeholder value and should be removed later)Storage
class still lacks this functionality, both would have pretty much the same logic anyway)Storage
where we only keep the 1000 most recent blocks in memory)