Dryrunning a Trap ⛓️
Ideally you would be able to test your trap with real block/state data before deploying it for real, on-chain. Well, with the dryrun
command you can. In normal trap operation, a trap will be "monitored" by one or more opted-in operators, but with the dryrun
command you can easily test two lifecycles (a bootstrap and a normal lifecycle) of this same process on your local machine with what is for all intents and purposes, a locally spun-up ad-hoc operator.
Under The Hood
The lifecycle will be performed with block(s) pulled from your specified evm endpoint, ethereum_rpc
in your drosera.toml
config file. The number of blocks pulled is determined by your configured block_sample_size
(also drosera.toml
) for the trap.
Lifecycle
-
The
block_sample_size
latest blocks are processed by the trap'scollect
function:-
Bootstrap Lifecycle: For the first lifecycle, the system starts from scratch with no cached data. It fetches each block from your RPC endpoint and runs the
collect
function on it. During this process, account and slot data are fetched one by one, as needed during trap (solidity contract) execution. All collect results are then cached for future use. -
Runtime Lifecycle: For subsequent lifecycles, the system has cached results for
collect
calls on previous blocks. This way it only needs to runcollect
for the one new block. This onecollect
also runs faster as potential needed account and slot data are pre-fetched in a batch call, so the trap runs much faster due to significantly less RPC calls being made.
-
-
The outputs of the
collect
calls are passed as input to one call of theshould_respond
function. -
should_respond
will return a value offalse
for do nothing, ortrue
for execute the specifiedresponse_function
(drosera.toml
).
You've now been able to verify that your trap is functioning end-to-end, as if it was already deployed to the Drosera network.
Example
Command: drosera dryrun
This trap has a
block_sample_size
of 3 in this example, so each lifecycle performs 3 collects. The 1st (bootstrap) lifecycle starts with no cached collect results so it has to perform all of the computation and block fetching, resulting in a longer lifecycle time of 5.25 seconds compared to the 2nd lifecycle's 2.42 seconds.
In the 2nd (normal) lifecycle, it speeds through collects for blocks 4,203,641 and 4,203,642 because the collect results for those blocks were already computed in the previous lifecycle, saving RPC network call time and collect
computation time. Only until it reaches the new block of 4,203,643 that it hasn't encountered before, does it have to fetch it from the RPC and run the collect
function on it. So the bootstrap lifecycle always takes longer than normal lifecycles.
Theory-crafting
With the optional --block-number
argument, dryrun
can be used to test traps on past state. This allows anything from more robust testing of a trap, all the way to verifying that a trap successfully catches a past exploit. An excellent addition to the security researcher tool belt.
How To Run
drosera dryrun --block-number <block_number>
where --block-number
is optional. Without, it defaults to the current block number.
Lifecycle Limitations
With traps, you have significantly more gas to work with than a contract on-chain. Blocks on Ethereum currently have a total block.gasLimit
of ~30 million gas units. So the sum of gas used by all transactions in a block cannot excede this number. In another stratosphere, Drosera is configured to allow up to 1 billion gas units per collect
function and 1 billion gas units per shouldRespond
function. This means that one trap lifecycle with a block_sample_size
of 1 could use up to 2 billion gas units, or approximately 66x more gas than what can fit in an entire Ethereum block.
But, with great power comes great responsibility. Ethereum blocks average about 12 seconds per block, and so in order for a trap to retain its ability of responding in the next block after the configured condition is detected (shouldRespond
returns true), it needs to be able to be executed within a few seconds by the recommended system requirements. If the trap takes too long to execute on recommended spec operator hardware, then the operators running the trap will struggle or be unable to keep up with Ethereum block times and begin falling further and further behind.
Therefore dryrun
has a safety check built in. If a trap's normal lifecycle (>= 2nd lifecycle) takes longer than a recommended upper time limit, it will emit a warning. dryrun
is running on your local computer, so if your computer specs are equal to or lower than the recommended operator specs, this trap (when deployed) may struggle or entirely fail to keep up with on-chain blocks, thus falling behind and either reducing the effectivness of the trap or defeating its security use case entirely. If your computer specs are better than the recommended operator specs, and dryrun
yields this warning, the trap will most certainly have the previously mentioned issues when deployed. Additionally, for certain trap designs querying many accounts/slots, RPC latency may be a factor at play here as well.