@@ -31,6 +31,7 @@ use blockstack_lib::net::api::postblock_proposal::{
31
31
} ;
32
32
use blockstack_lib:: net:: stackerdb:: MINER_SLOT_COUNT ;
33
33
use blockstack_lib:: util_lib:: boot:: boot_code_id;
34
+ use blockstack_lib:: version_string;
34
35
use clarity:: vm:: types:: serialization:: SerializationError ;
35
36
use clarity:: vm:: types:: QualifiedContractIdentifier ;
36
37
use serde:: { Deserialize , Serialize } ;
@@ -45,11 +46,13 @@ use stacks_common::types::chainstate::{
45
46
} ;
46
47
use stacks_common:: util:: hash:: { Hash160 , Sha512Trunc256Sum } ;
47
48
use stacks_common:: util:: HexError ;
49
+ use stacks_common:: versions:: STACKS_NODE_VERSION ;
48
50
use tiny_http:: {
49
51
Method as HttpMethod , Request as HttpRequest , Response as HttpResponse , Server as HttpServer ,
50
52
} ;
51
53
52
54
use crate :: http:: { decode_http_body, decode_http_request} ;
55
+ use crate :: v0:: messages:: BLOCK_RESPONSE_DATA_MAX_SIZE ;
53
56
use crate :: EventError ;
54
57
55
58
/// Define the trait for the event processor
@@ -69,24 +72,114 @@ pub struct BlockProposal {
69
72
pub burn_height : u64 ,
70
73
/// The reward cycle the block is mined during
71
74
pub reward_cycle : u64 ,
75
+ /// Versioned and backwards-compatible block proposal data
76
+ pub block_proposal_data : BlockProposalData ,
72
77
}
73
78
74
79
impl StacksMessageCodec for BlockProposal {
75
80
fn consensus_serialize < W : Write > ( & self , fd : & mut W ) -> Result < ( ) , CodecError > {
76
81
self . block . consensus_serialize ( fd) ?;
77
82
self . burn_height . consensus_serialize ( fd) ?;
78
83
self . reward_cycle . consensus_serialize ( fd) ?;
84
+ self . block_proposal_data . consensus_serialize ( fd) ?;
79
85
Ok ( ( ) )
80
86
}
81
87
82
88
fn consensus_deserialize < R : Read > ( fd : & mut R ) -> Result < Self , CodecError > {
83
89
let block = NakamotoBlock :: consensus_deserialize ( fd) ?;
84
90
let burn_height = u64:: consensus_deserialize ( fd) ?;
85
91
let reward_cycle = u64:: consensus_deserialize ( fd) ?;
92
+ let block_proposal_data = BlockProposalData :: consensus_deserialize ( fd) ?;
86
93
Ok ( BlockProposal {
87
94
block,
88
95
burn_height,
89
96
reward_cycle,
97
+ block_proposal_data,
98
+ } )
99
+ }
100
+ }
101
+
102
+ /// The latest version of the block response data
103
+ pub const BLOCK_PROPOSAL_DATA_VERSION : u8 = 2 ;
104
+
105
+ /// Versioned, backwards-compatible struct for block response data
106
+ #[ derive( Clone , Debug , PartialEq , Serialize , Deserialize ) ]
107
+ pub struct BlockProposalData {
108
+ /// The version of the block proposal data
109
+ pub version : u8 ,
110
+ /// The miner's server version
111
+ pub server_version : String ,
112
+ /// When deserializing future versions,
113
+ /// there may be extra bytes that we don't know about
114
+ pub unknown_bytes : Vec < u8 > ,
115
+ }
116
+
117
+ impl BlockProposalData {
118
+ /// Create a new BlockProposalData for the provided server version and unknown bytes
119
+ pub fn new ( server_version : String ) -> Self {
120
+ Self {
121
+ version : BLOCK_PROPOSAL_DATA_VERSION ,
122
+ server_version,
123
+ unknown_bytes : vec ! [ ] ,
124
+ }
125
+ }
126
+
127
+ /// Create a new BlockProposalData with the current build's version
128
+ pub fn from_current_version ( ) -> Self {
129
+ let server_version = version_string (
130
+ "stacks-node" ,
131
+ option_env ! ( "STACKS_NODE_VERSION" ) . or ( Some ( STACKS_NODE_VERSION ) ) ,
132
+ ) ;
133
+ Self :: new ( server_version)
134
+ }
135
+
136
+ /// Create an empty BlockProposalData
137
+ pub fn empty ( ) -> Self {
138
+ Self :: new ( String :: new ( ) )
139
+ }
140
+
141
+ /// Serialize the "inner" block response data. Used to determine the bytes length of the serialized block response data
142
+ fn inner_consensus_serialize < W : Write > ( & self , fd : & mut W ) -> Result < ( ) , CodecError > {
143
+ write_next ( fd, & self . server_version . as_bytes ( ) . to_vec ( ) ) ?;
144
+ // write_next(fd, &self.unknown_bytes)?;
145
+ fd. write_all ( & self . unknown_bytes )
146
+ . map_err ( CodecError :: WriteError ) ?;
147
+ Ok ( ( ) )
148
+ }
149
+ }
150
+
151
+ impl StacksMessageCodec for BlockProposalData {
152
+ /// Serialize the block response data.
153
+ /// When creating a new version of the block response data, we are only ever
154
+ /// appending new bytes to the end of the struct. When serializing, we use
155
+ /// `bytes_len` to ensure that older versions of the code can read through the
156
+ /// end of the serialized bytes.
157
+ fn consensus_serialize < W : Write > ( & self , fd : & mut W ) -> Result < ( ) , CodecError > {
158
+ write_next ( fd, & self . version ) ?;
159
+ let mut inner_bytes = vec ! [ ] ;
160
+ self . inner_consensus_serialize ( & mut inner_bytes) ?;
161
+ write_next ( fd, & inner_bytes) ?;
162
+ Ok ( ( ) )
163
+ }
164
+
165
+ /// Deserialize the block response data in a backwards-compatible manner.
166
+ /// When creating a new version of the block response data, we are only ever
167
+ /// appending new bytes to the end of the struct. When deserializing, we use
168
+ /// `bytes_len` to ensure that we read through the end of the serialized bytes.
169
+ fn consensus_deserialize < R : Read > ( fd : & mut R ) -> Result < Self , CodecError > {
170
+ let Ok ( version) = read_next ( fd) else {
171
+ return Ok ( Self :: empty ( ) ) ;
172
+ } ;
173
+ let inner_bytes: Vec < u8 > = read_next_at_most ( fd, BLOCK_RESPONSE_DATA_MAX_SIZE ) ?;
174
+ let mut inner_reader = inner_bytes. as_slice ( ) ;
175
+ let server_version: Vec < u8 > = read_next ( & mut inner_reader) ?;
176
+ let server_version = String :: from_utf8 ( server_version) . map_err ( |e| {
177
+ CodecError :: DeserializeError ( format ! ( "Failed to decode server version: {:?}" , & e) )
178
+ } ) ?;
179
+ Ok ( Self {
180
+ version,
181
+ server_version,
182
+ unknown_bytes : inner_reader. to_vec ( ) ,
90
183
} )
91
184
}
92
185
}
0 commit comments