Juvix imports
module arch.node.engines.executor;
import prelude open;
import arch.node.types.engine open;
import arch.node.engines.executor_config open public;
import arch.node.engines.executor_messages open public;
import arch.node.engines.executor_environment open public;
import arch.node.engines.executor_behaviour open public;
import arch.node.types.anoma as Anoma open;
open executor_config_example;
open executor_environment_example;
Executor Engine¶
The Executor Engine is responsible for executing transaction programs in Anoma, serving as the computational core that processes state transitions within the system. It operates as part of a distributed execution system, working in concert with Shard Engines that manage state access and Mempool Worker engines that take orders and spawn Executor engines based on those orders. Each Executor Engine instance is spawned to handle the execution of a single transaction in the form of a program which it is spawned with, making them ephemeral components that exist solely for the duration of their assigned transaction's lifecycle.
At its core, an Executor Engine receives read responses from shards and uses these to step through the transaction program's execution. Each transaction program defines a sequence of operations that may read from or write to various keys in the system's state. The Executor doesn't directly access this state - instead, it coordinates with Shard engines that manage actual state access.
The primary interface for the Executor Engine consists of three main message types
that facilitate its operation. It receives ShardMsgKVSRead
messages from Shards
containing the data for requested state reads. For each read, the Executor applies
this data to advance the transaction program's execution, potentially generating
new read requests (ShardMsgKVSReadRequest
) or write operations (ShardMsgKVSWrite
)
that are sent to the appropriate Shards. Once execution is complete, it sends
an ExecutorMsgExecutorFinished
message to both the Worker that spawned it and the
transaction's issuer, containing a summary of all reads and writes performed during
execution.
Engine components¶
The type for an executor engine¶
The executor engine is designed to be "agnostic" to choices of virtual machines and data formats, assuming only that the executable will run step by step (possibly involving program state updates) and interaction with replicated state machine state is via a key value storage interface.
ExecutorEngine : Type :=
Engine
ExecutorLocalCfg
ExecutorLocalState
ExecutorMailboxState
ExecutorTimerHandle
ExecutorActionArguments
Anoma.Msg
Anoma.Cfg
Anoma.Env;
Example of an executor engine¶
exampleExecutorEngine : ExecutorEngine :=
Engine.mk@{
cfg := executorCfg;
env := executorEnv;
behaviour := executorBehaviour;
};
where executorCfg
is defined as follows:
executorCfg : ExecutorCfg :=
EngineCfg.mk@{
node := PublicKey.Curve25519PubKey "0xabcd1234";
name := "executor";
cfg :=
ExecutorLocalCfg.mk@{
timestamp := 0;
executable := Noun.Atom 0;
lazy_read_keys := Set.empty;
eager_read_keys := Set.empty;
will_write_keys := Set.empty;
may_write_keys := Set.empty;
worker := mkPair none "";
issuer := mkPair none "";
keyToShard := \{_ := mkPair none "shard"};
};
};
executorEnv
is defined as follows:
executorEnv : ExecutorEnv :=
EngineEnv.mk@{
localState :=
ExecutorLocalState.mk@{
program_state :=
NockmaProgramState.mk@{
current_noun := Noun.Atom 0;
storage := emptyStorage;
gas_limit := 0;
};
completed_reads := Map.empty;
completed_writes := Map.empty;
};
mailboxCluster := Map.empty;
acquaintances := Set.empty;
timers := [];
};
and executorBehaviour
is defined as follows:
executorBehaviour : ExecutorBehaviour :=
EngineBehaviour.mk@{
guards := GuardEval.First [processReadGuard];
};