Skip to content

Commit 4d25b1a

Browse files
authored
Merge pull request #830 from sethdusek/headerserialization6.0
Add Header serialization
2 parents ecf71a8 + 695f52a commit 4d25b1a

File tree

8 files changed

+73
-6
lines changed

8 files changed

+73
-6
lines changed

ergo-chain-generation/src/chain_generation.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ fn prove_block(
191191
extension_root,
192192
autolykos_solution: dummy_autolykos_solution,
193193
votes,
194+
unparsed_bytes: Box::new([]),
194195
};
195196
let msg = blake2b256_hash(&header.serialize_without_pow().unwrap())
196197
.0

ergo-chain-generation/src/fake_pow_scheme.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ mod tests {
141141
extension_root,
142142
autolykos_solution: dummy_autolykos_solution,
143143
votes,
144+
unparsed_bytes: Box::new([]),
144145
};
145146

146147
let x = DlogProverInput::random();

ergo-chain-types/src/header.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ pub struct Header {
5656
/// 3 bytes in accordance to Scala implementation, but will use `Vec` until further improvements
5757
#[cfg_attr(feature = "json", serde(rename = "votes"))]
5858
pub votes: Votes,
59+
/// Unparsed bytes that encode new possible fields
60+
#[cfg_attr(
61+
feature = "json",
62+
serde(
63+
rename = "unparsedBytes",
64+
default,
65+
serialize_with = "crate::json::autolykos_solution::as_base16_string",
66+
deserialize_with = "crate::json::autolykos_solution::from_base16_string"
67+
)
68+
)]
69+
pub unparsed_bytes: Box<[u8]>,
5970
}
6071

6172
impl Header {
@@ -82,7 +93,8 @@ impl Header {
8293
// For block version >= 2, this new byte encodes length of possible new fields.
8394
// Set to 0 for now, so no new fields.
8495
if self.version > 1 {
85-
w.put_i8(0)?;
96+
w.put_u8(self.unparsed_bytes.len().try_into()?)?;
97+
w.write_all(&self.unparsed_bytes)?;
8698
}
8799
Ok(data)
88100
}
@@ -127,13 +139,18 @@ impl ScorexSerializable for Header {
127139

128140
// For block version >= 2, a new byte encodes length of possible new fields. If this byte >
129141
// 0, we read new fields but do nothing, as semantics of the fields is not known.
130-
if version > 1 {
142+
let unparsed_bytes: Box<[u8]> = if version > 1 {
131143
let new_field_size = r.get_u8()?;
132144
if new_field_size > 0 {
133145
let mut field_bytes: Vec<u8> = vec![0; new_field_size as usize];
134146
r.read_exact(&mut field_bytes)?;
147+
field_bytes.into()
148+
} else {
149+
Box::new([])
135150
}
136-
}
151+
} else {
152+
Box::new([])
153+
};
137154

138155
// Parse `AutolykosSolution`
139156
let autolykos_solution = if version == 1 {
@@ -182,6 +199,7 @@ impl ScorexSerializable for Header {
182199
extension_root,
183200
autolykos_solution: autolykos_solution.clone(),
184201
votes,
202+
unparsed_bytes,
185203
};
186204

187205
let mut id_bytes = header.serialize_without_pow()?;
@@ -296,6 +314,7 @@ mod arbitrary {
296314
prop::sample::select(vec![1_u8, 2]),
297315
any::<Box<AutolykosSolution>>(),
298316
uniform3(1u8..),
317+
proptest::collection::vec(any::<u8>(), 0..=255),
299318
)
300319
.prop_map(
301320
|(
@@ -309,6 +328,7 @@ mod arbitrary {
309328
version,
310329
autolykos_solution,
311330
votes,
331+
unparsed_bytes,
312332
)| {
313333
let parent_id = BlockId(Digest(parent_id));
314334
let ad_proofs_root = Digest(ad_proofs_root);
@@ -332,6 +352,11 @@ mod arbitrary {
332352
extension_root,
333353
autolykos_solution: *autolykos_solution.clone(),
334354
votes,
355+
unparsed_bytes: if version > 1 {
356+
unparsed_bytes.into()
357+
} else {
358+
Box::new([])
359+
},
335360
};
336361
let mut id_bytes = header.serialize_without_pow().unwrap();
337362
let mut data = Vec::new();

ergo-chain-types/src/json/autolykos_solution.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ where
1616
serializer.serialize_str(&base16::encode_lower(value))
1717
}
1818

19-
pub(crate) fn from_base16_string<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
19+
pub(crate) fn from_base16_string<'de, D, T: From<Vec<u8>>>(deserializer: D) -> Result<T, D::Error>
2020
where
2121
D: Deserializer<'de>,
2222
{
2323
use serde::de::Error;
2424
String::deserialize(deserializer)
2525
.and_then(|string| base16::decode(&string).map_err(|err| Error::custom(err.to_string())))
26+
.map(From::from)
2627
}
2728

2829
/// Serialize `BigInt` as a string

ergotree-interpreter/src/eval/sglobal.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ pub(crate) static DECODE_NBITS_EVAL_FN: EvalFn = |_mc, _env, _ctx, _obj, args| {
250250
#[cfg(test)]
251251
#[cfg(feature = "arbitrary")]
252252
mod tests {
253-
use ergo_chain_types::EcPoint;
253+
use ergo_chain_types::{EcPoint, Header};
254254
use ergotree_ir::bigint256::BigInt256;
255255
use ergotree_ir::ergo_tree::ErgoTreeVersion;
256256
use ergotree_ir::mir::constant::Constant;
@@ -682,5 +682,9 @@ mod tests {
682682
fn serialize_unsigned_bigint(v in any::<UnsignedBigInt>()) {
683683
assert_eq!(deserialize(&serialize(v), SType::SUnsignedBigInt), Constant::from(v));
684684
}
685+
#[test]
686+
fn serialize_header(h in any::<Header>()) {
687+
assert_eq!(deserialize(&serialize(h.clone()), SType::SHeader), Constant::from(h));
688+
}
685689
}
686690
}

ergotree-ir/src/mir/constant.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use ergo_chain_types::ADDigest;
3131
use ergo_chain_types::Base16DecodedBytes;
3232
use ergo_chain_types::Digest32;
3333
use ergo_chain_types::EcPoint;
34+
use ergo_chain_types::Header;
3435
use impl_trait_for_tuples::impl_for_tuples;
3536
use sigma_util::AsVecI8;
3637
use sigma_util::AsVecU8;
@@ -83,6 +84,8 @@ pub enum Literal {
8384
GroupElement(Arc<EcPoint>),
8485
/// AVL tree
8586
AvlTree(Box<AvlTreeData>),
87+
/// Block Header type
88+
Header(Box<Header>),
8689
/// Ergo box
8790
CBox(Ref<'static, ErgoBox>),
8891
/// Collection
@@ -165,6 +168,7 @@ impl core::fmt::Debug for Literal {
165168
Literal::GroupElement(v) => v.fmt(f),
166169
Literal::UnsignedBigInt(v) => v.fmt(f),
167170
Literal::AvlTree(v) => v.fmt(f),
171+
Literal::Header(v) => v.fmt(f),
168172
Literal::CBox(v) => v.fmt(f),
169173
Literal::String(v) => v.fmt(f),
170174
}
@@ -224,6 +228,7 @@ impl core::fmt::Display for Literal {
224228
Literal::UnsignedBigInt(v) => v.fmt(f),
225229
Literal::GroupElement(v) => v.fmt(f),
226230
Literal::AvlTree(v) => write!(f, "AvlTree({:?})", v),
231+
Literal::Header(v) => write!(f, "Header({:?})", v),
227232
Literal::CBox(v) => write!(f, "ErgoBox({:?})", v),
228233
Literal::String(v) => write!(f, "String({v})"),
229234
}
@@ -422,9 +427,9 @@ impl<'ctx> TryFrom<Value<'ctx>> for Constant {
422427
}
423428
}
424429
Value::AvlTree(a) => Ok(Constant::from(*a)),
430+
Value::Header(h) => Ok(Constant::from(*h)),
425431
Value::String(s) => Ok(Constant::from(s)),
426432
Value::Context => Err("Cannot convert Value::Context into Constant".into()),
427-
Value::Header(_) => Err("Cannot convert Value::Header(_) into Constant".into()),
428433
Value::PreHeader(_) => Err("Cannot convert Value::PreHeader(_) into Constant".into()),
429434
Value::Global => Err("Cannot convert Value::Global into Constant".into()),
430435
Value::Lambda(_) => Err("Cannot convert Value::Lambda(_) into Constant".into()),
@@ -643,6 +648,15 @@ impl From<AvlTreeData> for Constant {
643648
}
644649
}
645650

651+
impl From<Header> for Constant {
652+
fn from(h: Header) -> Self {
653+
Constant {
654+
tpe: SType::SHeader,
655+
v: Literal::Header(h.into()),
656+
}
657+
}
658+
}
659+
646660
impl From<AvlTreeFlags> for Constant {
647661
fn from(a: AvlTreeFlags) -> Self {
648662
Constant {
@@ -1209,6 +1223,8 @@ pub(crate) mod arbitrary {
12091223
#[cfg(feature = "arbitrary")]
12101224
#[allow(clippy::panic)]
12111225
mod tests {
1226+
use crate::{ergo_tree::ErgoTreeVersion, serialization::roundtrip_new_feature};
1227+
12121228
use super::*;
12131229
use core::fmt;
12141230
use proptest::prelude::*;
@@ -1424,5 +1440,10 @@ mod tests {
14241440
test_constant_roundtrip(v);
14251441
}
14261442

1443+
#[test]
1444+
fn header_ser_roundtrip(h in any::<Header>()) {
1445+
roundtrip_new_feature(&Constant::from(h), ErgoTreeVersion::V3);
1446+
}
1447+
14271448
}
14281449
}

ergotree-ir/src/mir/value.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ impl From<Literal> for Value<'static> {
386386
Value::Coll(converted_coll)
387387
}
388388
Literal::AvlTree(a) => Value::AvlTree(a),
389+
Literal::Header(h) => Value::Header(h),
389390
Literal::Opt(lit) => Value::Opt(lit.map(|boxed| *boxed).map(Value::from).map(Box::new)),
390391
Literal::Tup(t) => Value::Tup(t.mapped(Value::from)),
391392
}

ergotree-ir/src/serialization/data.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use alloc::boxed::Box;
2+
use ergo_chain_types::Header;
3+
use sigma_ser::ScorexSerializable;
24

35
use alloc::string::String;
46
use alloc::string::ToString;
@@ -93,13 +95,21 @@ impl DataSerializer {
9395
Literal::Tup(items) => items
9496
.iter()
9597
.try_for_each(|i| DataSerializer::sigma_serialize(i, w))?,
98+
Literal::Header(h) if w.tree_version() >= ErgoTreeVersion::V3 => {
99+
h.scorex_serialize(w)?;
100+
}
96101
// unsupported, see
97102
// https://github.com/ScorexFoundation/sigmastate-interpreter/issues/659
98103
Literal::Opt(_) => {
99104
return Err(SigmaSerializationError::NotSupported(
100105
"Option serialization is not supported".to_string(),
101106
));
102107
}
108+
Literal::Header(_) => {
109+
return Err(SigmaSerializationError::NotSupported(
110+
"Header serialization is not supported".to_string(),
111+
));
112+
}
103113
})
104114
}
105115

@@ -174,6 +184,9 @@ impl DataSerializer {
174184
}
175185
SBox => Literal::CBox(Arc::new(ErgoBox::sigma_parse(r)?).into()),
176186
SAvlTree => Literal::AvlTree(Box::new(AvlTreeData::sigma_parse(r)?)),
187+
SHeader if r.tree_version() >= ErgoTreeVersion::V3 => {
188+
Literal::Header(Box::new(Header::scorex_parse(r)?))
189+
}
177190
STypeVar(_) => return Err(SigmaParsingError::NotSupported("TypeVar data")),
178191
SAny => return Err(SigmaParsingError::NotSupported("SAny data")),
179192
SOption(_) => return Err(SigmaParsingError::NotSupported("SOption data")),

0 commit comments

Comments
 (0)