Skip to content

Commit

Permalink
Merge fab986e into 47a3344
Browse files Browse the repository at this point in the history
  • Loading branch information
greenhat committed Apr 21, 2023
2 parents 47a3344 + fab986e commit b65076b
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 33 deletions.
4 changes: 2 additions & 2 deletions bindings/ergo-lib-c-core/src/ergo_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ pub unsafe fn ergo_box_candidate_register_value(
.additional_registers
.get_constant(register_id.into())
{
*constant_out = Box::into_raw(Box::new(Constant(c.clone())));
*constant_out = Box::into_raw(Box::new(Constant(c)));
Ok(true)
} else {
Ok(false)
Expand Down Expand Up @@ -295,7 +295,7 @@ pub unsafe fn ergo_box_register_value(
.additional_registers
.get_constant(register_id.into())
{
*constant_out = Box::into_raw(Box::new(Constant(c.clone())));
*constant_out = Box::into_raw(Box::new(Constant(c)));
Ok(true)
} else {
Ok(false)
Expand Down
2 changes: 0 additions & 2 deletions bindings/ergo-lib-wasm/src/ergo_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ impl ErgoBoxCandidate {
self.0
.additional_registers
.get_constant(register_id.into())
.cloned()
.map(Constant::from)
}

Expand Down Expand Up @@ -200,7 +199,6 @@ impl ErgoBox {
self.0
.additional_registers
.get_constant(register_id.into())
.cloned()
.map(Constant::from)
}

Expand Down
2 changes: 1 addition & 1 deletion ergo-lib/src/chain/ergo_box/box_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ mod tests {
builder.set_register_value(R4, reg_value.clone());
assert_eq!(builder.register_value(&R4).unwrap(), &reg_value);
let b = builder.build().unwrap();
assert_eq!(b.additional_registers.get_constant(R4).unwrap(), &reg_value);
assert_eq!(b.additional_registers.get_constant(R4).unwrap(), reg_value);
}

#[test]
Expand Down
4 changes: 1 addition & 3 deletions ergotree-ir/src/chain/ergo_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,7 @@ impl ErgoBox {
MandatoryRegisterId::R2 => Some(self.tokens_raw().into()),
MandatoryRegisterId::R3 => Some(self.creation_info().into()),
},
RegisterId::NonMandatoryRegisterId(id) => {
self.additional_registers.get_constant(id).cloned()
}
RegisterId::NonMandatoryRegisterId(id) => self.additional_registers.get_constant(id),
}
}

Expand Down
95 changes: 81 additions & 14 deletions ergotree-ir/src/chain/ergo_box/register.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Box registers

use crate::mir::constant::Constant;
use crate::mir::constant::Literal;
use crate::mir::expr::Expr;
use crate::mir::tuple::Tuple;
use crate::serialization::sigma_byte_reader::SigmaByteRead;
use crate::serialization::sigma_byte_writer::SigmaByteWrite;
use crate::serialization::SigmaParsingError;
Expand Down Expand Up @@ -209,10 +212,10 @@ impl NonMandatoryRegisters {
}

/// Get register value as a Constant (returns None, if there is no value for the given register id or if it's an unparseable)
pub fn get_constant(&self, reg_id: NonMandatoryRegisterId) -> Option<&Constant> {
pub fn get_constant(&self, reg_id: NonMandatoryRegisterId) -> Option<Constant> {
self.0
.get(reg_id as usize - NonMandatoryRegisterId::START_INDEX)
.and_then(|rv| rv.as_option_constant())
.and_then(|rv| rv.as_constant().ok())
}
}

Expand All @@ -221,37 +224,90 @@ impl NonMandatoryRegisters {
pub enum RegisterValue {
/// Constant value
Parsed(Constant),
/// Parsed evaluated Tuple expression
/// see https://github.com/ergoplatform/sigma-rust/issues/700
ParsedTupleExpr(Tuple),
/// Unparseable bytes
Unparseable {
Invalid {
/// Bytes that were not parsed (whole register bytes)
bytes: Vec<u8>,
/// Error message on parsing
error_msg: String,
},
}

/// Errors on parsing register values
#[derive(Debug)]
pub enum RegisterValueError {
/// Failed to parse register value
Invalid(String),
/// Invalid Tuple expression in the parsed regiser value
InvalidTupleExpr(String),
}

impl RegisterValue {
/// Return a Constant if it's parsed, otherwise None
pub fn as_option_constant(&self) -> Option<&Constant> {
pub fn as_constant(&self) -> Result<Constant, RegisterValueError> {
match self {
RegisterValue::Parsed(c) => Some(c),
RegisterValue::Unparseable {
RegisterValue::Parsed(c) => Ok(c.clone()),
RegisterValue::ParsedTupleExpr(t) => tuple_to_constant(t),
RegisterValue::Invalid {
bytes: _,
error_msg: _,
} => None,
error_msg,
} => Err(RegisterValueError::Invalid(error_msg.to_string())),
}
}

#[allow(clippy::unwrap_used)] // it could only fail on OOM, etc.
fn sigma_serialize_bytes(&self) -> Vec<u8> {
match self {
RegisterValue::Parsed(c) => c.sigma_serialize_bytes().unwrap(),
RegisterValue::Unparseable {
RegisterValue::ParsedTupleExpr(t) => t.sigma_serialize_bytes().unwrap(),
RegisterValue::Invalid {
bytes,
error_msg: _,
} => bytes.clone(),
}
}

/// Parse bytes to RegisterValue
pub fn sigma_parse_bytes(bytes: &[u8]) -> Self {
if let Ok(expr) = Expr::sigma_parse_bytes(bytes) {
match expr {
Expr::Const(c) => RegisterValue::Parsed(c),
Expr::Tuple(t) => RegisterValue::ParsedTupleExpr(t),
e => RegisterValue::Invalid {
bytes: bytes.to_vec(),
error_msg: format!(
"Unexpected parsed register value: {e:?} from bytes {0:?}",
bytes
),
},
}
} else {
RegisterValue::Invalid {
bytes: bytes.to_vec(),
error_msg: format!("failed to parse register value: {0:?}", bytes),
}
}
}
}

/// Convert evaluated Tuple expression to Constant
/// see https://github.com/ergoplatform/sigma-rust/issues/700
fn tuple_to_constant(t: &Tuple) -> Result<Constant, RegisterValueError> {
let values = t.items.try_mapped_ref(|tuple_item| match tuple_item {
Expr::Const(c) => Ok(c.v.clone()),
Expr::Tuple(t) => Ok(tuple_to_constant(t)?.v),
e => {
return Err(RegisterValueError::InvalidTupleExpr(format!(
"Unexpected parsed register value: {e:?}"
)))
}
})?;
let v = Literal::Tup(values);
let c = Constant { tpe: t.tpe(), v };
Ok(c)
}

/// Create new from ordered values (first element will be R4, and so on)
Expand Down Expand Up @@ -287,7 +343,8 @@ impl SigmaSerializable for NonMandatoryRegisters {
for (idx, reg_value) in self.0.iter().enumerate() {
match reg_value {
RegisterValue::Parsed(c) => c.sigma_serialize(w)?,
RegisterValue::Unparseable { bytes, error_msg } => {
RegisterValue::ParsedTupleExpr(t) => t.sigma_serialize(w)?,
RegisterValue::Invalid { bytes, error_msg } => {
let bytes_str = base16::encode_lower(bytes);
return Err(SigmaSerializationError::NotSupported(format!("unparseable register value at {0:?} (parsing error: {error_msg}) cannot be serialized in the stream (writer), because it cannot be parsed later. Register value as base16-encoded bytes: {bytes_str}", NonMandatoryRegisterId::get_by_zero_index(idx))));
}
Expand All @@ -300,8 +357,18 @@ impl SigmaSerializable for NonMandatoryRegisters {
let regs_num = r.get_u8()?;
let mut additional_regs = Vec::with_capacity(regs_num as usize);
for _ in 0..regs_num {
let v = Constant::sigma_parse(r)?;
additional_regs.push(v);
let expr = Expr::sigma_parse(r)?;
let reg_val = match expr {
Expr::Const(c) => RegisterValue::Parsed(c),
Expr::Tuple(t) => RegisterValue::ParsedTupleExpr(t),
_ => {
return Err(SigmaParsingError::InvalidRegisterValue(format!(
"invalid register value: {0:?} (expected Constant or Tuple)",
expr
)))
}
};
additional_regs.push(reg_val);
}
Ok(additional_regs.try_into()?)
}
Expand Down Expand Up @@ -435,7 +502,7 @@ pub(crate) mod arbitrary {
prop_oneof![
any::<Constant>().prop_map(RegisterValue::Parsed),
vec(any::<u8>(), 0..100).prop_map({
|bytes| RegisterValue::Unparseable {
|bytes| RegisterValue::Invalid {
bytes,
error_msg: "unparseable".to_string(),
}
Expand Down Expand Up @@ -476,7 +543,7 @@ mod tests {
fn get(regs in any::<NonMandatoryRegisters>()) {
let hash_map: HashMap<NonMandatoryRegisterId, RegisterValue> = regs.clone().into();
hash_map.keys().try_for_each(|reg_id| {
prop_assert_eq![regs.get_constant(*reg_id), hash_map.get(reg_id).unwrap().as_option_constant()];
prop_assert_eq![regs.get_constant(*reg_id), hash_map.get(reg_id).unwrap().as_constant().ok()];
Ok(())
})?;
}
Expand Down
10 changes: 1 addition & 9 deletions ergotree-ir/src/chain/json/ergo_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ use crate::chain::ergo_box::RegisterValue;
use crate::chain::token::Token;
use crate::chain::tx_id::TxId;
use crate::ergo_tree::ErgoTree;
use crate::mir::constant::Constant;
use crate::serialization::SigmaParsingError;
use crate::serialization::SigmaSerializable;
use crate::serialization::SigmaSerializationError;
use ergo_chain_types::Base16DecodedBytes;
use std::convert::TryFrom;
Expand Down Expand Up @@ -200,13 +198,7 @@ pub struct ConstantHolder(#[serde(deserialize_with = "super::t_as_string_or_stru

impl From<ConstantHolder> for RegisterValue {
fn from(ch: ConstantHolder) -> Self {
match Constant::sigma_parse_bytes(ch.0.raw_value.0.as_slice()) {
Ok(c) => RegisterValue::Parsed(c),
Err(e) => RegisterValue::Unparseable {
bytes: ch.0.raw_value.0,
error_msg: format!("{e}"),
},
}
RegisterValue::sigma_parse_bytes(ch.0.raw_value.0.as_slice())
}
}

Expand Down
38 changes: 38 additions & 0 deletions ergotree-ir/src/serialization/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,13 @@ impl SigmaSerializable for Constant {
#[allow(clippy::panic, clippy::unwrap_used)]
mod tests {
use super::*;
use crate::base16_str::Base16Str;
use crate::mir::constant::arbitrary::ArbConstantParams;
use crate::mir::constant::Literal;
use crate::mir::constant::Literal::Tup;
use crate::mir::expr::Expr;
use crate::serialization::sigma_serialize_roundtrip;
use crate::types::stuple::STuple;
use proptest::prelude::*;

proptest! {
Expand Down Expand Up @@ -78,4 +83,37 @@ mod tests {
assert!(c_res.is_err());
assert!(matches!(c_res, Err(SigmaParsingError::ValueOutOfBounds(_))));
}

#[test]
fn test_invalid_type134_i700() {
// see https://github.com/ergoplatform/sigma-rust/issues/700
//
// below is an expression and not a constant encoded.
// 0x86 is the op code for Tuple expression following by 0x02 (size), 0x0266
// (SByte type 0x02, value 0x66), 0x0263 (SByte type 0x02, value 0x63).
let expr_bytes_str = "860202660263";
let expr_bytes = base16::decode(expr_bytes_str).unwrap();
let try_parse_constant = Constant::sigma_parse_bytes(&expr_bytes);
dbg!(&try_parse_constant);
if let Err(e) = &try_parse_constant {
println!("Failed to parse constant: {}", e);
}
assert!(try_parse_constant.is_err());
assert!(matches!(
try_parse_constant,
Err(SigmaParsingError::InvalidTypeCode(134))
));
let try_parse_expr: Expr = Expr::sigma_parse_bytes(&expr_bytes).unwrap();
dbg!(&try_parse_expr);
assert!(matches!(try_parse_expr, Expr::Tuple(_)));

// now let's construct a proper constant for (102, 99) byte tuple
let expected_c: Constant = Constant {
tpe: SType::STuple(STuple::pair(SType::SByte, SType::SByte)),
v: Tup([Literal::Byte(102), Literal::Byte(99)].into()),
};
let expected_str = expected_c.base16_str().unwrap();
dbg!(expected_c.sigma_serialize_bytes().unwrap());
assert_eq!(expected_str, "566663");
}
}
5 changes: 4 additions & 1 deletion ergotree-ir/src/serialization/serializable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub enum SigmaParsingError {
#[error("not implemented op error: {0}")]
NotImplementedOpCode(String),
/// Failed to parse type
#[error("type parsing error, invalid type code: {0}")]
#[error("type parsing error, invalid type code: {0}({0:#04X})")]
InvalidTypeCode(u8),
/// Failed to decode VLQ
#[error("vlq encode error: {0}")]
Expand Down Expand Up @@ -101,6 +101,9 @@ pub enum SigmaParsingError {
/// ErgoTreeHeaderError
#[error("ErgoTreeHeaderError: {0}")]
ErgoTreeHeaderError(#[from] ErgoTreeHeaderError),
/// Invalid register value
#[error("Invalid register value: {0}")]
InvalidRegisterValue(String),
}

impl From<io::Error> for SigmaParsingError {
Expand Down
2 changes: 1 addition & 1 deletion sigma-ser/src/scorex_serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub enum ScorexParsingError {
#[error("not implemented op error")]
NotImplementedOpCode(String),
/// Failed to parse type
#[error("type parsing error")]
#[error("type parsing error, invalid type code: {0}({0:#04X})")]
InvalidTypeCode(u8),
/// Failed to decode VLQ
#[error("vlq encode error: {0}")]
Expand Down

0 comments on commit b65076b

Please sign in to comment.