-
Notifications
You must be signed in to change notification settings - Fork 924
/
payload.rs
172 lines (152 loc) · 5.93 KB
/
payload.rs
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
//! Contains types required for building a payload.
use reth_primitives::{Address, ChainSpec, Header, SealedBlock, Withdrawal, H256, U256};
use reth_revm_primitives::config::revm_spec_by_timestamp_after_merge;
use reth_rlp::Encodable;
use reth_rpc_types::engine::{
ExecutionPayload, ExecutionPayloadEnvelope, PayloadAttributes, PayloadId,
};
use revm_primitives::{BlockEnv, CfgEnv};
/// Contains the built payload.
///
/// According to the [engine API specification](https://github.com/ethereum/execution-apis/blob/main/src/engine/README.md) the execution layer should build the initial version of the payload with an empty transaction set and then keep update it in order to maximize the revenue.
/// Therefore, the empty-block here is always available and full-block will be set/updated
/// afterwards.
#[derive(Debug, Clone)]
pub struct BuiltPayload {
/// Identifier of the payload
pub(crate) id: PayloadId,
/// The built block
pub(crate) block: SealedBlock,
/// The fees of the block
pub(crate) fees: U256,
}
// === impl BuiltPayload ===
impl BuiltPayload {
/// Initializes the payload with the given initial block.
pub fn new(id: PayloadId, block: SealedBlock, fees: U256) -> Self {
Self { id, block, fees }
}
/// Returns the identifier of the payload.
pub fn id(&self) -> PayloadId {
self.id
}
/// Returns the built block(sealed)
pub fn block(&self) -> &SealedBlock {
&self.block
}
/// Fees of the block
pub fn fees(&self) -> U256 {
self.fees
}
/// Converts the type into the response expected by `engine_getPayloadV1`
pub fn into_v1_payload(self) -> ExecutionPayload {
self.into()
}
/// Converts the type into the response expected by `engine_getPayloadV2`
pub fn into_v2_payload(self) -> ExecutionPayloadEnvelope {
self.into()
}
}
// V1 engine_getPayloadV1 response
impl From<BuiltPayload> for ExecutionPayload {
fn from(value: BuiltPayload) -> Self {
value.block.into()
}
}
// V2 engine_getPayloadV2 response
impl From<BuiltPayload> for ExecutionPayloadEnvelope {
fn from(value: BuiltPayload) -> Self {
let BuiltPayload { block, fees, .. } = value;
ExecutionPayloadEnvelope {
block_value: fees,
payload: block.into(),
should_override_builder: None,
}
}
}
/// Container type for all components required to build a payload.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PayloadBuilderAttributes {
/// Id of the payload
pub id: PayloadId,
/// Parent block to build the payload on top
pub parent: H256,
/// Timestamp for the generated payload
pub timestamp: u64,
/// Address of the recipient for collecting transaction fee
pub suggested_fee_recipient: Address,
/// Randomness value for the generated payload
pub prev_randao: H256,
/// Withdrawals for the generated payload
pub withdrawals: Vec<Withdrawal>,
}
// === impl PayloadBuilderAttributes ===
impl PayloadBuilderAttributes {
/// Creates a new payload builder for the given parent block and the attributes.
///
/// Derives the unique [PayloadId] for the given parent and attributes
pub fn new(parent: H256, attributes: PayloadAttributes) -> Self {
let id = payload_id(&parent, &attributes);
Self {
id,
parent,
timestamp: attributes.timestamp.as_u64(),
suggested_fee_recipient: attributes.suggested_fee_recipient,
prev_randao: attributes.prev_randao,
withdrawals: attributes.withdrawals.unwrap_or_default(),
}
}
/// Returns the configured [CfgEnv] and [BlockEnv] for the targeted payload (that has the
/// `parent` as its parent).
///
/// The `chain_spec` is used to determine the correct chain id and hardfork for the payload
/// based on its timestamp.
///
/// Block related settings are derived from the `parent` block and the configured attributes.
///
/// NOTE: This is only intended for beacon consensus (after merge).
pub fn cfg_and_block_env(&self, chain_spec: &ChainSpec, parent: &Header) -> (CfgEnv, BlockEnv) {
// configure evm env based on parent block
let cfg = CfgEnv {
chain_id: U256::from(chain_spec.chain().id()),
// ensure we're not missing any timestamp based hardforks
spec_id: revm_spec_by_timestamp_after_merge(chain_spec, self.timestamp),
..Default::default()
};
let block_env = BlockEnv {
number: U256::from(parent.number + 1),
coinbase: self.suggested_fee_recipient,
timestamp: U256::from(self.timestamp),
difficulty: U256::ZERO,
prevrandao: Some(self.prev_randao),
gas_limit: U256::from(parent.gas_limit),
// calculate basefee based on parent block's gas usage
basefee: U256::from(
parent.next_block_base_fee(chain_spec.base_fee_params).unwrap_or_default(),
),
};
(cfg, block_env)
}
/// Returns the identifier of the payload.
pub fn payload_id(&self) -> PayloadId {
self.id
}
}
/// Generates the payload id for the configured payload
///
/// Returns an 8-byte identifier by hashing the payload components with sha256 hash.
pub(crate) fn payload_id(parent: &H256, attributes: &PayloadAttributes) -> PayloadId {
use sha2::Digest;
let mut hasher = sha2::Sha256::new();
hasher.update(parent.as_bytes());
hasher.update(&attributes.timestamp.as_u64().to_be_bytes()[..]);
hasher.update(attributes.prev_randao.as_bytes());
hasher.update(attributes.suggested_fee_recipient.as_bytes());
if let Some(withdrawals) = &attributes.withdrawals {
let mut buf = Vec::new();
withdrawals.encode(&mut buf);
hasher.update(buf);
}
let out = hasher.finalize();
PayloadId::new(out.as_slice()[..8].try_into().expect("sufficient length"))
}