Skip to content

Commit

Permalink
Merge pull request #702 from ergoplatform/i700-invalid-type-code-134
Browse files Browse the repository at this point in the history
Tuple expression support in register values
  • Loading branch information
greenhat committed Apr 27, 2023
2 parents 47a3344 + 497a8a1 commit 8522da3
Show file tree
Hide file tree
Showing 15 changed files with 501 additions and 301 deletions.
8 changes: 4 additions & 4 deletions bindings/ergo-lib-c-core/src/ergo_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ pub unsafe fn ergo_box_candidate_register_value(
if let Some(c) = candidate
.0
.additional_registers
.get_constant(register_id.into())
.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 @@ -293,9 +293,9 @@ pub unsafe fn ergo_box_register_value(
if let Some(c) = ergo_box
.0
.additional_registers
.get_constant(register_id.into())
.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: 2 additions & 0 deletions bindings/ergo-lib-c-core/src/error_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use ergo_lib::ergotree_interpreter::sigma_protocol::verifier::VerifierError;
use ergo_lib::ergotree_ir::chain::address::AddressEncoderError;
use ergo_lib::ergotree_ir::chain::address::AddressError;
use ergo_lib::ergotree_ir::chain::ergo_box::box_value::BoxValueError;
use ergo_lib::ergotree_ir::chain::ergo_box::RegisterValueError;
use ergo_lib::ergotree_ir::chain::token::TokenAmountError;
use ergo_lib::wallet::derivation_path::ChildIndexError;
use ergo_lib::wallet::signing::TxSigningError;
Expand Down Expand Up @@ -75,3 +76,4 @@ convert_error_via_debug!(BoundedVecOutOfBounds);
convert_error_via_debug!(TransactionError);
convert_error_via_debug!(NipopowProofError);
convert_error_via_debug!(VerifierError);
convert_error_via_debug!(RegisterValueError);
24 changes: 16 additions & 8 deletions bindings/ergo-lib-wasm/src/ergo_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,16 @@ impl ErgoBoxCandidate {
}

/// Returns value (ErgoTree constant) stored in the register or None if the register is empty or cannot be parsed
pub fn register_value(&self, register_id: NonMandatoryRegisterId) -> Option<Constant> {
self.0
pub fn register_value(
&self,
register_id: NonMandatoryRegisterId,
) -> Result<Option<Constant>, JsValue> {
Ok(self
.0
.additional_registers
.get_constant(register_id.into())
.cloned()
.map(Constant::from)
.map_err(to_js)?
.map(Constant::from))
}

/// Get box creation height
Expand Down Expand Up @@ -196,12 +200,16 @@ impl ErgoBox {
}

/// Returns value (ErgoTree constant) stored in the register or None if the register is empty or cannot be parsed
pub fn register_value(&self, register_id: NonMandatoryRegisterId) -> Option<Constant> {
self.0
pub fn register_value(
&self,
register_id: NonMandatoryRegisterId,
) -> Result<Option<Constant>, JsValue> {
Ok(self
.0
.additional_registers
.get_constant(register_id.into())
.cloned()
.map(Constant::from)
.map_err(to_js)?
.map(Constant::from))
}

/// JSON representation as text (compatible with Ergo Node/Explorer API, numbers are encoded as numbers)
Expand Down
2 changes: 2 additions & 0 deletions bindings/ergo-lib-wasm/src/error_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use ergo_lib::ergotree_interpreter::sigma_protocol::verifier::VerifierError;
use ergo_lib::ergotree_ir::chain::address::AddressEncoderError;
use ergo_lib::ergotree_ir::chain::address::AddressError;
use ergo_lib::ergotree_ir::chain::ergo_box::box_value::BoxValueError;
use ergo_lib::ergotree_ir::chain::ergo_box::RegisterValueError;
use ergo_lib::ergotree_ir::chain::token::TokenAmountError;
use ergo_lib::wallet::derivation_path::ChildIndexError;
use ergo_lib::wallet::derivation_path::DerivationPathError;
Expand Down Expand Up @@ -96,6 +97,7 @@ from_error_to_wrap!(ParseError);
from_error_to_wrap!(ConvError);
from_error_to_wrap!(TransactionContextError);
from_error_to_wrap!(TxVerifyError);
from_error_to_wrap!(RegisterValueError);

macro_rules! from_error_to_wrap_via_debug {
($t:ident) => {
Expand Down
10 changes: 8 additions & 2 deletions ergo-lib/src/chain/ergo_box/box_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,10 @@ 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().unwrap(),
reg_value
);
}

#[test]
Expand All @@ -360,7 +363,7 @@ mod tests {
builder.delete_register_value(&R4);
assert!(builder.register_value(&R4).is_none());
let b = builder.build().unwrap();
assert!(b.additional_registers.get_constant(R4).is_none());
assert!(b.additional_registers.get_constant(R4).unwrap().is_none());
}

#[test]
Expand All @@ -384,6 +387,7 @@ mod tests {
.additional_registers
.get_constant(NonMandatoryRegisterId::R4)
.unwrap()
.unwrap()
.base16_str()
.unwrap(),
"0e03555344",
Expand All @@ -394,6 +398,7 @@ mod tests {
.additional_registers
.get_constant(NonMandatoryRegisterId::R5)
.unwrap()
.unwrap()
.base16_str()
.unwrap(),
"0e184e6f7468696e67206261636b65642055534420746f6b656e",
Expand All @@ -404,6 +409,7 @@ mod tests {
.additional_registers
.get_constant(NonMandatoryRegisterId::R6)
.unwrap()
.unwrap()
.base16_str()
.unwrap(),
"0e0132",
Expand Down
38 changes: 28 additions & 10 deletions ergotree-interpreter/src/eval/deserialize_register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ impl Evaluable for DeserializeRegister {
e
))
})?) {
Some(c) => {
Ok(Some(c)) => {
if c.tpe != SType::SColl(SType::SByte.into()) {
Err(EvalError::UnexpectedExpr(format!(
"DeserializeRegister: expected value to have type SColl(SByte), got {:?}",
Expand All @@ -39,23 +39,41 @@ impl Evaluable for DeserializeRegister {
}
}
}
None => match &self.default {
Some(default_expr) => {
if default_expr.tpe() != self.tpe {
Err(EvalError::UnexpectedExpr(format!("DeserializeRegister: expected default expr to have type {:?}, got {:?}", self.tpe, default_expr.tpe())))
} else {
default_expr.eval(env, ctx)
}
}
Ok(None) => match &self.default {
Some(default_expr) => eval_default(&self.tpe, default_expr, env, ctx),
None => Err(EvalError::NotFound(format!(
"DeserializeRegister: register {:?} is empty",
self.reg
))),
},
Err(e) => match &self.default {
Some(default_expr) => eval_default(&self.tpe, default_expr, env, ctx),
None => Err(EvalError::NotFound(format!(
"DeserializeRegister: register with id {} is empty",
"DeserializeRegister: failed to get the register id {} with error: {e:?}",
self.reg
))),
},
}
}
}

fn eval_default(
deserialize_reg_tpe: &SType,
default_expr: &Expr,
env: &Env,
ctx: &mut EvalContext,
) -> Result<Value, EvalError> {
if &default_expr.tpe() != deserialize_reg_tpe {
Err(EvalError::UnexpectedExpr(format!(
"DeserializeRegister: expected default expr to have type {:?}, got {:?}",
deserialize_reg_tpe,
default_expr.tpe()
)))
} else {
default_expr.eval(env, ctx)
}
}

#[allow(clippy::unwrap_used)]
#[cfg(feature = "arbitrary")]
#[cfg(test)]
Expand Down
20 changes: 10 additions & 10 deletions ergotree-interpreter/src/eval/extract_reg_as.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ impl Evaluable for ExtractRegisterAs {
.input
.eval(env, ctx)?
.try_extract_into::<Arc<ErgoBox>>()?;
Ok(Value::Opt(Box::new(
ir_box
.get_register(self.register_id.try_into().map_err(|e| {
EvalError::RegisterIdOutOfBounds(format!(
"register index is out of bounds: {:?} ",
e
))
})?)
.map(|c| Value::from(c.v)),
)))
let id = self.register_id.try_into().map_err(|e| {
EvalError::RegisterIdOutOfBounds(format!("register index is out of bounds: {:?} ", e))
})?;
let reg_val_opt = ir_box.get_register(id).map_err(|e| {
EvalError::NotFound(format!(
"Error getting the register id {:?} with error {e:?}",
id
))
})?;
Ok(Value::Opt(Box::new(reg_val_opt.map(|c| Value::from(c.v)))))
}
}

Expand Down
30 changes: 17 additions & 13 deletions ergotree-interpreter/src/eval/sbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,25 @@ pub(crate) static VALUE_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
};

pub(crate) static GET_REG_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
let reg_id = args
.get(0)
.cloned()
.ok_or_else(|| EvalError::NotFound("register index is missing".to_string()))?
.try_extract_into::<i8>()?
.try_into()
.map_err(|e| {
EvalError::RegisterIdOutOfBounds(format!("register index is out of bounds: {:?} ", e))
})?;

Ok(Value::Opt(Box::new(
obj.try_extract_into::<Arc<ErgoBox>>()?
.get_register(
args.get(0)
.cloned()
.ok_or_else(|| EvalError::NotFound("register index is missing".to_string()))?
.try_extract_into::<i8>()?
.try_into()
.map_err(|e| {
EvalError::RegisterIdOutOfBounds(format!(
"register index is out of bounds: {:?} ",
e
))
})?,
)
.get_register(reg_id)
.map_err(|e| {
EvalError::NotFound(format!(
"Error getting the register id {:?} with error {e:?}",
reg_id
))
})?
.map(|c| Value::from(c.v)),
)))
};
Expand Down
22 changes: 12 additions & 10 deletions ergotree-ir/src/chain/ergo_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ impl ErgoBox {
}

/// Get register value, or None if register is empty or cannot be parsed
pub fn get_register(&self, id: RegisterId) -> Option<Constant> {
match id {
pub fn get_register(&self, id: RegisterId) -> Result<Option<Constant>, RegisterValueError> {
Ok(match id {
RegisterId::MandatoryRegisterId(id) => match id {
MandatoryRegisterId::R0 => Some(self.value.into()),
// chance of box script is not serializable are tiny comparing to returning Result
Expand All @@ -157,10 +157,8 @@ 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)?,
})
}

/// Returns tokens as tuple of byte array and amount as primitive types
Expand Down Expand Up @@ -460,17 +458,20 @@ mod tests {
#[test]
fn get_register_mandatory() {
let b = force_any_val::<ErgoBox>();
assert_eq!(b.get_register(RegisterId::R0).unwrap(), b.value.into());
assert_eq!(
b.get_register(RegisterId::R1).unwrap(),
b.get_register(RegisterId::R0).unwrap().unwrap(),
b.value.into()
);
assert_eq!(
b.get_register(RegisterId::R1).unwrap().unwrap(),
b.script_bytes().unwrap().into()
);
assert_eq!(
b.get_register(RegisterId::R2).unwrap(),
b.get_register(RegisterId::R2).unwrap().unwrap(),
b.tokens_raw().into()
);
assert_eq!(
b.get_register(RegisterId::R3).unwrap(),
b.get_register(RegisterId::R3).unwrap().unwrap(),
b.creation_info().into()
);
}
Expand Down Expand Up @@ -519,3 +520,4 @@ mod tests {
}
}
}
// += a + b

0 comments on commit 8522da3

Please sign in to comment.