verbs_rs/agent/
traits.rs

1//! Traits designating required simulation agent functionality
2//!
3//! The traits are intended to be used in a hierarchical manner:
4//!
5//! * [SimState] collect all simulation agents, where fields
6//!   may be different agent types. This trait then describes
7//!   functions called during simulation execution
8//! * [AgentSet] is intended as a homogeneous collection of
9//!   an agent type
10//! * [Agent] is an individual agent that may be member of an
11//!   [AgentSet]
12//!
13//! Implementers have the flexibility to only use part of this
14//! structure though, for instance an implementation of
15//! [SimState] could implement an individual agent.
16//!
17//! Since it is a common use case to want to iterate over a
18//! agents of different types, the macro `#[derive(SimState)]`
19//! will automatically implement functions that iterate
20//! over field containing agents.
21//!
22
23use crate::contract::Transaction;
24use crate::env::{Env, Validator};
25use crate::DB;
26use alloy_primitives::Address;
27use rand::RngCore;
28pub use verbs_macros::SimState;
29
30/// Simulation agent state trait
31///
32/// Trait providing an interface to update the
33/// state of all agents over the course of a
34/// simulation. This trait can be automatically
35/// derived for a struct where the fields
36/// are sets of agents of a single type using the
37/// [SimState] macro. This will generate the
38/// code to automatically iterate
39/// over each set of agents in turn.
40///
41/// # Examples
42///
43/// ```
44/// use rand::RngCore;
45/// use alloy_primitives::Address;
46/// use verbs_rs::{DB, env::{Env, Validator}};
47/// use verbs_rs::agent::{Agent, RecordedAgent, AgentVec, AgentSet, SimState};
48/// use verbs_rs::contract::Transaction;
49///
50/// struct DummyAgent{}
51///
52/// impl Agent for DummyAgent {
53///     fn update<D: DB, V: Validator, R: RngCore>(
54///         &mut self, rng: &mut R, network: &mut Env<D, V>
55///     ) -> Vec<Transaction> {
56///         Vec::default()
57///     }
58///
59///     fn get_address(&self) -> Address {
60///         Address::ZERO
61///     }
62/// }
63///
64/// impl RecordedAgent<bool> for DummyAgent {
65///     fn record<D: DB, V: Validator>(&mut self, _env: &mut Env<D, V>) -> bool {
66///         true
67///     }
68/// }
69///
70/// #[derive(SimState)]
71/// struct TestState {
72///     a: AgentVec::<bool, DummyAgent>,
73///     b: AgentVec::<bool, DummyAgent>,
74/// }
75/// ```
76pub trait SimState {
77    /// Update the state of all agents, and return any transactions
78    fn call_agents<D: DB, V: Validator, R: RngCore>(
79        &mut self,
80        rng: &mut R,
81        env: &mut Env<D, V>,
82    ) -> Vec<Transaction>;
83    /// Record the current state of the agents in this set
84    fn record_agents<D: DB, V: Validator>(&mut self, env: &mut Env<D, V>);
85}
86
87/// Trait defining behaviour for a single agent
88///
89/// This is intended to be called for each individual
90/// agent at each step of the simulation, updating the
91/// state of the agent and recording state data.
92/// # Examples
93///
94/// ```
95/// use rand::RngCore;
96/// use alloy_primitives::Address;
97/// use verbs_rs::{DB, env::{Env, Validator}};
98/// use verbs_rs::agent::{Agent, RecordedAgent, AgentVec, AgentSet};
99/// use verbs_rs::contract::Transaction;
100///
101/// struct DummyAgent{
102///     state: i32,
103/// }
104///
105/// impl Agent for DummyAgent {
106///     fn update<D: DB, V: Validator, R: RngCore>(
107///         &mut self, rng: &mut R, network: &mut Env<D, V>
108///     ) -> Vec<Transaction> {
109///         self.state += 1;
110///         Vec::default()
111///     }
112///
113///     fn get_address(&self) -> Address {
114///         Address::ZERO
115///     }
116/// }
117/// ```
118pub trait Agent {
119    /// Update the agent and optionally return a [Transaction]
120    /// this should not update the state of the evm directly.
121    ///
122    /// # Arguments
123    ///
124    /// * `rng`: Random generate
125    /// * `env`: Simulation environment
126    ///
127    fn update<D: DB, V: Validator, R: RngCore>(
128        &mut self,
129        rng: &mut R,
130        env: &mut Env<D, V>,
131    ) -> Vec<Transaction>;
132    /// Get the address of the agent.
133    fn get_address(&self) -> Address;
134}
135
136/// Trait used to record the state of the agent over the course of the simulation
137///
138/// Each step this is called after the state of the simulation
139/// is updated, and is intended to record the state of an agent
140/// or some part of the state of the EVM. The actual type of data
141/// returned is left to the implementer.
142///
143/// # Examples
144///
145/// ```
146/// use verbs_rs::{DB, env::{Env, Validator}};
147/// use verbs_rs::agent::RecordedAgent;
148///
149/// struct DummyAgent{
150///     current_state: i32
151/// }
152///
153/// impl RecordedAgent<i32> for DummyAgent {
154///     fn record<D: DB, V: Validator>(&mut self, _env: &mut Env<D, V>) -> i32 {
155///         self.current_state
156///     }
157/// }
158/// ```
159pub trait RecordedAgent<R> {
160    /// Get a record of the current state of the agent. Records are
161    /// collected as a vector of vectors representing the state of a
162    /// collection of agents over the history of the simulation.
163    fn record<D: DB, V: Validator>(&mut self, env: &mut Env<D, V>) -> R;
164}
165
166/// A homogenous collection of agents
167///
168/// Designed to represent a group of agents of a uniform type
169/// and update and record the group state at each step of the
170/// simulation.
171pub trait AgentSet {
172    /// Update all the agents in the set, collecting any EVM calls generated by the agents
173    ///
174    /// # Arguments
175    ///
176    /// * `rng` - Random generate
177    /// * `env` - Simulation environment
178    ///
179    fn call<D: DB, V: Validator, R: RngCore>(
180        &mut self,
181        rng: &mut R,
182        env: &mut Env<D, V>,
183    ) -> Vec<Transaction>;
184    /// Record the state of all the agents
185    fn record<D: DB, V: Validator>(&mut self, env: &mut Env<D, V>);
186    /// Get a vector of agent addresses contained in this set
187    fn get_addresses(&self) -> Vec<Address>;
188}
189
190/// Take ownership of time-series data from a set of agents
191///
192/// Returns a time series of vectors of records across
193/// all the agents in the set.
194pub trait RecordedAgentSet<R> {
195    fn take_records(&mut self) -> Vec<Vec<R>>;
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201    use crate::{env::RandomValidator, LocalDB};
202    use alloy_primitives::{Address, U256};
203
204    struct DummyAgentSet {
205        v: bool,
206    }
207
208    impl AgentSet for DummyAgentSet {
209        fn call<D: DB, V: Validator, R: RngCore>(
210            &mut self,
211            _rng: &mut R,
212            _env: &mut Env<D, V>,
213        ) -> Vec<Transaction> {
214            vec![Transaction {
215                function_selector: [0, 0, 0, 0],
216                callee: Address::ZERO,
217                transact_to: Address::ZERO,
218                args: Vec::default(),
219                value: U256::ZERO,
220                checked: self.v,
221                gas_priority_fee: None,
222                nonce: None,
223            }]
224        }
225
226        fn record<D: DB, V: Validator>(&mut self, _env: &mut Env<D, V>) {}
227
228        fn get_addresses(&self) -> Vec<Address> {
229            vec![Address::ZERO]
230        }
231    }
232
233    #[test]
234    fn test_macro() {
235        #[derive(SimState)]
236        struct TestState {
237            a: DummyAgentSet,
238            b: DummyAgentSet,
239        }
240
241        let mut x = TestState {
242            a: DummyAgentSet { v: true },
243            b: DummyAgentSet { v: false },
244        };
245
246        let mut rng = <rand_xoshiro::Xoroshiro128StarStar as rand::SeedableRng>::seed_from_u64(101);
247        let mut network =
248            &mut Env::<LocalDB, RandomValidator>::init(U256::ZERO, U256::ZERO, RandomValidator {});
249
250        let calls = x.call_agents(&mut rng, &mut network);
251
252        assert_eq!(calls.len(), 2);
253        assert_eq!(calls[0].checked, true);
254        assert_eq!(calls[1].checked, false);
255    }
256}