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}