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
//! ErgoTree header
use derive_more::From;
use thiserror::Error;
use crate::serialization::sigma_byte_reader::SigmaByteRead;
use crate::serialization::sigma_byte_writer::SigmaByteWrite;
/// Currently we define meaning for only first byte, which may be extended in future versions.
/// 7 6 5 4 3 2 1 0
/// -------------------------
/// | | | | | | | | |
/// -------------------------
/// Bit 7 == 1 if the header contains more than 1 byte (default == 0)
/// Bit 6 - reserved for GZIP compression (should be 0)
/// Bit 5 == 1 - reserved for context dependent costing (should be = 0)
/// Bit 4 == 1 if constant segregation is used for this ErgoTree (default = 0)
/// (see <https://github.com/ScorexFoundation/sigmastate-interpreter/issues/264>)
/// Bit 3 == 1 if size of the whole tree is serialized after the header byte (default = 0)
/// Bits 2-0 - language version (current version == 0)
///
/// Currently we don't specify interpretation for the second and other bytes of the header.
/// We reserve the possibility to extend header by using Bit 7 == 1 and chain additional bytes as in VLQ.
/// Once the new bytes are required, a new version of the language should be created and implemented.
/// That new language will give an interpretation for the new bytes.
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct ErgoTreeHeader {
version: ErgoTreeVersion,
is_constant_segregation: bool,
has_size: bool,
}
impl ErgoTreeHeader {
/// Serialization
pub fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), std::io::Error> {
w.put_u8(self.serialized())
}
/// Deserialization
pub fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, ErgoTreeHeaderError> {
let header_byte = r
.get_u8()
.map_err(|e| ErgoTreeHeaderError::IoError(e.to_string()))?;
ErgoTreeHeader::new(header_byte)
}
}
impl ErgoTreeHeader {
const CONSTANT_SEGREGATION_FLAG: u8 = 0b0001_0000;
const HAS_SIZE_FLAG: u8 = 0b0000_1000;
/// Parse from byte
pub fn new(header_byte: u8) -> Result<Self, ErgoTreeHeaderError> {
let version = ErgoTreeVersion::parse_version(header_byte)?;
let has_size = header_byte & Self::HAS_SIZE_FLAG != 0;
let is_constant_segregation = header_byte & Self::CONSTANT_SEGREGATION_FLAG != 0;
Ok(ErgoTreeHeader {
version,
is_constant_segregation,
has_size,
})
}
/// Serialize to byte
pub fn serialized(&self) -> u8 {
let mut header_byte: u8 = self.version.0;
if self.is_constant_segregation {
header_byte |= Self::CONSTANT_SEGREGATION_FLAG;
}
if self.has_size {
header_byte |= Self::HAS_SIZE_FLAG;
}
header_byte
}
/// Return a header with version set to 0 and constant segregation flag set to the given value
pub fn v0(constant_segregation: bool) -> Self {
ErgoTreeHeader {
version: ErgoTreeVersion::V0,
is_constant_segregation: constant_segregation,
has_size: false,
}
}
/// Return a header with version set to 1 (with size flag set) and constant segregation flag set to the given value
pub fn v1(constant_segregation: bool) -> Self {
ErgoTreeHeader {
version: ErgoTreeVersion::V1,
is_constant_segregation: constant_segregation,
has_size: true,
}
}
/// Returns true if constant segregation flag is set
pub fn is_constant_segregation(&self) -> bool {
self.is_constant_segregation
}
/// Returns true if size flag is set
pub fn has_size(&self) -> bool {
self.has_size
}
/// Returns ErgoTree version
pub fn version(&self) -> &ErgoTreeVersion {
&self.version
}
}
/// Header parsing error
#[derive(Error, PartialEq, Eq, Debug, Clone, From)]
pub enum ErgoTreeHeaderError {
/// Invalid version
#[error("Invalid version: {0}")]
VersionError(ErgoTreeVersionError),
/// IO error
#[error("IO error: {0}")]
IoError(String),
}
/// ErgoTree version 0..=7, should fit in 3 bits
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct ErgoTreeVersion(u8);
impl ErgoTreeVersion {
/// Header mask to extract version bits.
pub const VERSION_MASK: u8 = 0x07;
/// Version 0
pub const V0: Self = ErgoTreeVersion(0);
/// Version 1 (size flag is mandatory)
pub const V1: Self = ErgoTreeVersion(1);
/// Returns a value of the version bits from the given header byte.
pub fn parse_version(header_byte: u8) -> Result<Self, ErgoTreeVersionError> {
let version = header_byte & ErgoTreeVersion::VERSION_MASK;
if version <= 1 {
Ok(ErgoTreeVersion(version))
} else {
Err(ErgoTreeVersionError::InvalidVersion(version))
}
}
}
/// Version parsing error
#[derive(Error, PartialEq, Eq, Debug, Clone, From)]
pub enum ErgoTreeVersionError {
/// Invalid version
#[error("Invalid version: {0}")]
InvalidVersion(u8),
}