-
Notifications
You must be signed in to change notification settings - Fork 47
Expand file tree
/
Copy pathexec.rs
More file actions
241 lines (209 loc) 路 8.26 KB
/
Copy pathexec.rs
File metadata and controls
241 lines (209 loc) 路 8.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
// Copyright 2022-2024 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
use anyhow::Context;
use async_trait::async_trait;
use std::collections::HashMap;
use fendermint_vm_actor_interface::{chainmetadata, cron, customsyscall, system};
use fvm::executor::ApplyRet;
use fvm_ipld_blockstore::Blockstore;
use fvm_shared::{address::Address, ActorID, MethodNum, BLOCK_GAS_LIMIT};
use tendermint_rpc::Client;
use crate::ExecInterpreter;
use super::{
checkpoint::{self, PowerUpdates},
state::FvmExecState,
FvmMessage, FvmMessageInterpreter,
};
/// The return value extended with some things from the message that
/// might not be available to the caller, because of the message lookups
/// and transformations that happen along the way, e.g. where we need
/// a field, we might just have a CID.
pub struct FvmApplyRet {
pub apply_ret: ApplyRet,
pub from: Address,
pub to: Address,
pub method_num: MethodNum,
pub gas_limit: u64,
/// Delegated addresses of event emitters, if they have one.
pub emitters: HashMap<ActorID, Address>,
}
#[async_trait]
impl<DB, TC> ExecInterpreter for FvmMessageInterpreter<DB, TC>
where
DB: Blockstore + Clone + 'static + Send + Sync,
TC: Client + Clone + Send + Sync + 'static,
{
type State = FvmExecState<DB>;
type Message = FvmMessage;
type BeginOutput = FvmApplyRet;
type DeliverOutput = FvmApplyRet;
/// Return validator power updates.
/// Currently ignoring events as there aren't any emitted by the smart contract,
/// but keep in mind that if there were, those would have to be propagated.
type EndOutput = PowerUpdates;
async fn begin(
&self,
mut state: Self::State,
) -> anyhow::Result<(Self::State, Self::BeginOutput)> {
// Block height (FVM epoch) as sequence is intentional
let height = state.block_height();
// Arbitrarily large gas limit for cron (matching how Forest does it, which matches Lotus).
// XXX: Our blocks are not necessarily expected to be 30 seconds apart, so the gas limit might be wrong.
let gas_limit = BLOCK_GAS_LIMIT * 10000;
let from = system::SYSTEM_ACTOR_ADDR;
let to = cron::CRON_ACTOR_ADDR;
let method_num = cron::Method::EpochTick as u64;
// Cron.
let msg = FvmMessage {
from,
to,
sequence: height as u64,
gas_limit,
method_num,
params: Default::default(),
value: Default::default(),
version: Default::default(),
gas_fee_cap: Default::default(),
gas_premium: Default::default(),
};
let (apply_ret, emitters) = state.execute_implicit(msg)?;
// Failing cron would be fatal.
if let Some(err) = apply_ret.failure_info {
anyhow::bail!("failed to apply block cron message: {}", err);
}
// Push the current block hash to the chainmetadata actor
//
if let Some(block_hash) = state.block_hash() {
let params = fvm_ipld_encoding::RawBytes::serialize(
fendermint_actor_chainmetadata::PushBlockParams {
epoch: height,
block: block_hash,
},
)?;
let msg = FvmMessage {
from: system::SYSTEM_ACTOR_ADDR,
to: chainmetadata::CHAINMETADATA_ACTOR_ADDR,
sequence: height as u64,
gas_limit,
method_num: fendermint_actor_chainmetadata::Method::PushBlockHash as u64,
params,
value: Default::default(),
version: Default::default(),
gas_fee_cap: Default::default(),
gas_premium: Default::default(),
};
let (apply_ret, _) = state.execute_implicit(msg)?;
if let Some(err) = apply_ret.failure_info {
anyhow::bail!("failed to apply chainmetadata message: {}", err);
}
}
{
let msg = FvmMessage {
from: system::SYSTEM_ACTOR_ADDR,
to: customsyscall::CUSTOMSYSCALL_ACTOR_ADDR,
sequence: height as u64,
gas_limit,
method_num: fendermint_actor_customsyscall::Method::Invoke as u64,
params: Default::default(),
value: Default::default(),
version: Default::default(),
gas_fee_cap: Default::default(),
gas_premium: Default::default(),
};
let (apply_ret, _) = state.execute_implicit(msg)?;
if let Some(err) = apply_ret.failure_info {
anyhow::bail!("failed to apply customsyscall message: {}", err);
}
let val: u64 = apply_ret.msg_receipt.return_data.deserialize().unwrap();
println!("customsyscall actor returned: {}", val);
}
let ret = FvmApplyRet {
apply_ret,
from,
to,
method_num,
gas_limit,
emitters,
};
Ok((state, ret))
}
async fn deliver(
&self,
mut state: Self::State,
msg: Self::Message,
) -> anyhow::Result<(Self::State, Self::DeliverOutput)> {
let from = msg.from;
let to = msg.to;
let method_num = msg.method_num;
let gas_limit = msg.gas_limit;
let (apply_ret, emitters) = if from == system::SYSTEM_ACTOR_ADDR {
state.execute_implicit(msg)?
} else {
state.execute_explicit(msg)?
};
tracing::info!(
height = state.block_height(),
from = from.to_string(),
to = to.to_string(),
method_num = method_num,
exit_code = apply_ret.msg_receipt.exit_code.value(),
gas_used = apply_ret.msg_receipt.gas_used,
"tx delivered"
);
let ret = FvmApplyRet {
apply_ret,
from,
to,
method_num,
gas_limit,
emitters,
};
Ok((state, ret))
}
async fn end(&self, mut state: Self::State) -> anyhow::Result<(Self::State, Self::EndOutput)> {
let updates = if let Some((checkpoint, updates)) =
checkpoint::maybe_create_checkpoint(&self.gateway, &mut state)
.context("failed to create checkpoint")?
{
// Asynchronously broadcast signature, if validating.
if let Some(ref ctx) = self.validator_ctx {
// Do not resend past signatures.
if !self.syncing().await {
// Fetch any incomplete checkpoints synchronously because the state can't be shared across threads.
let incomplete_checkpoints =
checkpoint::unsigned_checkpoints(&self.gateway, &mut state, ctx.public_key)
.context("failed to fetch incomplete checkpoints")?;
debug_assert!(
incomplete_checkpoints
.iter()
.any(|cp| cp.block_height == checkpoint.block_height
&& cp.block_hash == checkpoint.block_hash),
"the current checkpoint is incomplete"
);
let client = self.client.clone();
let gateway = self.gateway.clone();
let chain_id = state.chain_id();
let height = checkpoint.block_height;
let validator_ctx = ctx.clone();
tokio::spawn(async move {
let res = checkpoint::broadcast_incomplete_signatures(
&client,
&validator_ctx,
&gateway,
chain_id,
incomplete_checkpoints,
)
.await;
if let Err(e) = res {
tracing::error!(error =? e, height = height.as_u64(), "error broadcasting checkpoint signature");
}
});
}
}
updates
} else {
PowerUpdates::default()
};
Ok((state, updates))
}
}