Skip to content

Commit

Permalink
Merge branch 'develop' into i141-clean-crate-exports
Browse files Browse the repository at this point in the history
  • Loading branch information
greenhat committed Nov 18, 2020
2 parents 738ca48 + ad5b17b commit 252e9df
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 70 deletions.
10 changes: 7 additions & 3 deletions bindings/ergo-lib-wasm/src/address.rs
Expand Up @@ -119,9 +119,13 @@ pub struct Address(chain::address::Address);

#[wasm_bindgen]
impl Address {
/// Create a P2PK address from an ergo tree if ProveDlog is the root of the tree, otherwise returns an error
pub fn p2pk_from_ergo_tree(ergo_tree: &ErgoTree) -> Result<Address, JsValue> {
chain::address::Address::p2pk_from_ergo_tree(&ergo_tree.clone().into())
/// Re-create the address from ErgoTree that was built from the address
///
/// At some point in the past a user entered an address from which the ErgoTree was built.
/// Re-create the address from this ErgoTree.
/// `tree` - ErgoTree that was created from an Address
pub fn recreate_from_ergo_tree(ergo_tree: &ErgoTree) -> Result<Address, JsValue> {
chain::address::Address::recreate_from_ergo_tree(&ergo_tree.clone().into())
.map(Address)
.map_err(|e| JsValue::from_str(&format!("{}", e)))
}
Expand Down
13 changes: 11 additions & 2 deletions bindings/ergo-lib-wasm/tests/test_address.js
Expand Up @@ -5,9 +5,18 @@ import {
ErgoTree
} from '../pkg/ergo_lib_wasm';

it('new_p2pk from base16 ergo tree', async () => {
it('P2PK from base16 ergo tree', async () => {
// ProveDlog in ErgoTree root
let tree_bytes_base16_str = '0008cd0327e65711a59378c59359c3e1d0f7abe906479eccb76094e50fe79d743ccc15e6';
let tree = ErgoTree.from_base16_bytes(tree_bytes_base16_str);
let addr = Address.p2pk_from_ergo_tree(tree);
let addr = Address.recreate_from_ergo_tree(tree);
assert(addr != null);
});

it('P2S from base16 ergo tree', async () => {
// Non ProveDlog in ErgoTree root
let tree_bytes_base16_str = "100204a00b08cd021dde34603426402615658f1d970cfa7c7bd92ac81a8b16eeebff264d59ce4604ea02d192a39a8cc7a70173007301";
let tree = ErgoTree.from_base16_bytes(tree_bytes_base16_str);
let addr = Address.recreate_from_ergo_tree(tree);
assert(addr != null);
});
2 changes: 1 addition & 1 deletion ergo-lib/CHANGELOG.md
Expand Up @@ -10,9 +10,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added

- Support for parsing ErgoBox id also from "id" JSON field name [#134](https://github.com/ergoplatform/sigma-rust/pull/134)
- Address::p2pk_from_ergo_tree to make an Address from ErgoTree with root ProveDlog [#136](https://github.com/ergoplatform/sigma-rust/pull/136)
- Address::p2pk_from_pk_bytes to make an Address from serialized PK [#136](https://github.com/ergoplatform/sigma-rust/pull/136)
- Address::from_str to parse an Address without checking the network prefix [#136](https://github.com/ergoplatform/sigma-rust/pull/136)
- Address::recreate_from_ergo_tree to re-create the address from ErgoTree (built from the address) [#146](https://github.com/ergoplatform/sigma-rust/pull/146)

### Changed

Expand Down
23 changes: 23 additions & 0 deletions ergo-lib/src/ast/expr.rs
Expand Up @@ -80,3 +80,26 @@ impl fmt::Display for Expr {
todo!()
}
}

impl From<Constant> for Expr {
fn from(c: Constant) -> Self {
Self::Const(c)
}
}

#[cfg(test)]
mod tests {
#![allow(unused_imports)]
use super::*;
use crate::sigma_protocol::sigma_boolean::SigmaProp;
use proptest::prelude::*;

impl Arbitrary for Expr {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;

fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
prop_oneof![any::<Constant>().prop_map(Expr::Const)].boxed()
}
}
}
26 changes: 26 additions & 0 deletions ergo-lib/src/ast/value.rs
@@ -1,5 +1,10 @@
use std::convert::TryFrom;

use crate::chain::ergo_box::ErgoBox;
use crate::sigma_protocol::dlog_group::EcPoint;
use crate::sigma_protocol::sigma_boolean::ProveDlog;
use crate::sigma_protocol::sigma_boolean::SigmaBoolean;
use crate::sigma_protocol::sigma_boolean::SigmaProofOfKnowledgeTree;
use crate::sigma_protocol::sigma_boolean::SigmaProp;
use crate::types::LiftIntoSType;
use crate::types::SType;
Expand Down Expand Up @@ -213,3 +218,24 @@ impl TryExtractFrom<Value> for SigmaProp {
}
}
}

impl TryFrom<Value> for ProveDlog {
type Error = TryExtractFromError;
fn try_from(cv: Value) -> Result<Self, Self::Error> {
match cv {
Value::SigmaProp(sp) => match sp.value() {
SigmaBoolean::ProofOfKnowledge(SigmaProofOfKnowledgeTree::ProveDlog(
prove_dlog,
)) => Ok(prove_dlog.clone()),
_ => Err(TryExtractFromError(format!(
"expected ProveDlog, found {:?}",
sp
))),
},
_ => Err(TryExtractFromError(format!(
"expected SigmaProp, found {:?}",
cv
))),
}
}
}
87 changes: 39 additions & 48 deletions ergo-lib/src/chain/address.rs
Expand Up @@ -3,7 +3,7 @@
use super::digest32;
use crate::ast::constant::Constant;
use crate::ast::expr::Expr;
use crate::ast::value::Value;
use crate::types::SType;
use crate::{
ergo_tree::{ErgoTree, ErgoTreeParsingError},
serialization::{SerializationError, SigmaSerializable},
Expand Down Expand Up @@ -71,36 +71,33 @@ pub enum Address {
}

impl Address {
/// Create a P2PK address from an ergo tree if ProveDlog is the root of the tree, otherwise returns an error
pub fn p2pk_from_ergo_tree(tree: &ErgoTree) -> Result<Address, AddressError> {
let expr = &*tree.proposition()?;
match expr {
Expr::Const(Constant {
tpe: _,
v: Value::SigmaProp(sp),
}) => match sp.value() {
SigmaBoolean::ProofOfKnowledge(SigmaProofOfKnowledgeTree::ProveDlog(
prove_dlog,
)) => Ok(Address::P2PK(prove_dlog.clone())),
_ => Err(AddressError::UnexpectedErgoTree(
tree.clone(),
"Expected ErgoTree with ProveDlog as root".to_string(),
)),
},
_ => Err(AddressError::UnexpectedErgoTree(
tree.clone(),
"Expected ErgoTree with ProveDlog as root".to_string(),
)),
}
}

/// Create a P2PK address from serialized PK bytes(EcPoint/GroupElement)
pub fn p2pk_from_pk_bytes(bytes: &[u8]) -> Result<Address, SerializationError> {
EcPoint::sigma_parse_bytes(bytes.to_vec())
.map(ProveDlog::from)
.map(Address::P2PK)
}

/// Re-create the address from ErgoTree that was built from the address
///
/// At some point in the past a user entered an address from which the ErgoTree was built.
/// Re-create the address from this ErgoTree.
/// `tree` - ErgoTree that was created from an Address
pub fn recreate_from_ergo_tree(tree: &ErgoTree) -> Result<Address, AddressError> {
match tree.proposition() {
Ok(expr) => Ok(match &*expr {
Expr::Const(Constant {
tpe: SType::SSigmaProp,
v,
}) => ProveDlog::try_from(v.clone())
.map(Address::P2PK)
.unwrap_or_else(|_| Address::P2S(tree.sigma_serialize_bytes())),
_ => Address::P2S(tree.sigma_serialize_bytes()),
}),
Err(_) => Ok(Address::P2S(tree.sigma_serialize_bytes())),
}
}

/// address type prefix (for encoding)
pub fn address_type_prefix(&self) -> AddressTypePrefix {
match self {
Expand Down Expand Up @@ -322,8 +319,7 @@ impl AddressEncoder {

#[cfg(test)]
mod tests {
use crate::ast::value::Value;
use crate::types::SType;
use crate::chain::Base16DecodedBytes;

use super::*;
use proptest::prelude::*;
Expand All @@ -333,36 +329,24 @@ mod tests {
type Strategy = BoxedStrategy<Self>;

fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
let non_parseable_tree = "100204a00b08cd021dde34603426402615658f1d970cfa7c7bd92ac81a8b16eeebff264d59ce4604ea02d192a39a8cc7a70173007301";
prop_oneof![
any::<ProveDlog>().prop_map(Address::P2PK),
any::<ErgoTree>().prop_map(|t| Address::P2S(t.sigma_serialize_bytes())),
any::<ErgoTree>().prop_map(|t| match ProveDlog::try_from(t.clone()) {
Ok(dlog) => Address::P2PK(dlog),
Err(_) => Address::P2S(t.sigma_serialize_bytes()),
}),
Just(Address::P2S(
Base16DecodedBytes::try_from(non_parseable_tree)
.unwrap()
.into()
))
]
.boxed()
}
}

#[test]
fn new_p2pk_non_provedlog_error() {
let tree = ErgoTree::from(Rc::new(Expr::Const(Constant {
tpe: SType::SBoolean,
v: Value::Boolean(true),
})));
assert!(Address::p2pk_from_ergo_tree(&tree).is_err());
}

proptest! {

#[test]
fn ergo_tree_p2pk_roundtrip(prove_dlog in any::<ProveDlog>()) {
let encoder = AddressEncoder::new(NetworkPrefix::Testnet);
let address = Address::P2PK(prove_dlog);
let ergo_tree = address.script().unwrap();
let address_copy = Address::p2pk_from_ergo_tree(&ergo_tree).unwrap();
let encoded_addr = encoder.address_to_str(&address);
let encoded_addr_copy = encoder.address_to_str(&address_copy);
prop_assert_eq![encoded_addr, encoded_addr_copy];
}

#[test]
fn str_roundtrip(v in any::<Address>()) {
let encoder = AddressEncoder::new(NetworkPrefix::Testnet);
Expand All @@ -371,6 +355,13 @@ mod tests {
prop_assert_eq![decoded_addr, v];
}

#[test]
fn recreate_roundtrip(v in any::<Address>()) {
let tree = v.script().unwrap();
let recreated = Address::recreate_from_ergo_tree(&tree).unwrap();
prop_assert_eq![recreated, v];
}

#[test]
fn doesnt_crash_on_invalid_input(s in "\\w+") {
let encoder = AddressEncoder::new(NetworkPrefix::Testnet);
Expand Down
13 changes: 13 additions & 0 deletions ergo-lib/src/chain/base16_bytes.rs
Expand Up @@ -35,3 +35,16 @@ impl TryFrom<String> for Base16DecodedBytes {
Ok(Base16DecodedBytes(base16::decode(&str)?))
}
}

impl TryFrom<&str> for Base16DecodedBytes {
type Error = base16::DecodeError;
fn try_from(v: &str) -> Result<Self, Self::Error> {
Base16DecodedBytes::try_from(v.to_string())
}
}

impl From<Base16DecodedBytes> for Vec<u8> {
fn from(b: Base16DecodedBytes) -> Self {
b.0
}
}
59 changes: 43 additions & 16 deletions ergo-lib/src/ergo_tree.rs
@@ -1,16 +1,19 @@
//! ErgoTree
use crate::ast::constant::Constant;
use crate::ast::constant::TryExtractFromError;
use crate::ast::expr::Expr;
use crate::serialization::{
sigma_byte_reader::{SigmaByteRead, SigmaByteReader},
sigma_byte_writer::{SigmaByteWrite, SigmaByteWriter},
SerializationError, SigmaSerializable,
};
use crate::sigma_protocol::sigma_boolean::ProveDlog;
use crate::types::SType;
use io::{Cursor, Read};

use crate::serialization::constant_store::ConstantStore;
use sigma_ser::{peekable_reader::PeekableReader, vlq_encode};
use std::convert::TryFrom;
use std::io;
use std::rc::Rc;
use thiserror::Error;
Expand Down Expand Up @@ -174,15 +177,18 @@ impl SigmaSerializable for ErgoTree {

fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
let header = ErgoTreeHeader::sigma_parse(r)?;
if header.is_constant_segregation() {
let constants = if header.is_constant_segregation() {
let constants_len = r.get_u32()?;
if constants_len != 0 {
return Err(SerializationError::NotImplementedYet(
"separate constants serialization is not yet supported".to_string(),
));
let mut constants = Vec::with_capacity(constants_len as usize);
for _ in 0..constants_len {
let c = Constant::sigma_parse(r)?;
constants.push(c);
}
}
let constants = Vec::new();
constants
} else {
vec![]
};
r.set_constant_store(ConstantStore::new(constants.clone()));
let root = Expr::sigma_parse(r)?;
Ok(ErgoTree {
header,
Expand Down Expand Up @@ -250,27 +256,48 @@ impl SigmaSerializable for ErgoTree {
}
}

impl TryFrom<ErgoTree> for ProveDlog {
type Error = TryExtractFromError;

fn try_from(tree: ErgoTree) -> Result<Self, Self::Error> {
let expr = &*tree
.proposition()
.map_err(|_| TryExtractFromError("cannot read root expr".to_string()))?;
match expr {
Expr::Const(Constant {
tpe: SType::SSigmaProp,
v,
}) => ProveDlog::try_from(v.clone()),
_ => Err(TryExtractFromError(
"expected ProveDlog in the root".to_string(),
)),
}
}
}

#[cfg(test)]
mod tests {
#![allow(unused_imports)]
use super::*;
use crate::ast::value::Value;
use crate::chain::Base16DecodedBytes;
use crate::serialization::sigma_serialize_roundtrip;
use crate::{chain, sigma_protocol::sigma_boolean::SigmaProp, types::SType};
use crate::sigma_protocol::sigma_boolean::SigmaProp;
use crate::{chain, types::SType};
use proptest::prelude::*;

impl Arbitrary for ErgoTree {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;

fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(any::<SigmaProp>())
.prop_map(|p| {
ErgoTree::from(Rc::new(Expr::Const(Constant {
tpe: SType::SSigmaProp,
v: Value::SigmaProp(Box::new(p)),
})))
})
.boxed()
prop_oneof![
// make sure that P2PK tree is included
any::<ProveDlog>().prop_map(|p| ErgoTree::from(Rc::new(Expr::from(
Constant::from(SigmaProp::from(p))
)))),
]
.boxed()
}
}

Expand Down
7 changes: 7 additions & 0 deletions ergo-lib/src/serialization/sigma_byte_reader.rs
Expand Up @@ -41,6 +41,9 @@ pub trait SigmaByteRead: ReadSigmaVlqExt {

/// Option to substitute ConstantPlaceholder with Constant from the store
fn substitute_placeholders(&self) -> bool;

/// Set new constant store
fn set_constant_store(&mut self, constant_store: ConstantStore);
}

impl<R: Peekable> Read for SigmaByteReader<R> {
Expand All @@ -63,4 +66,8 @@ impl<R: ReadSigmaVlqExt> SigmaByteRead for SigmaByteReader<R> {
fn substitute_placeholders(&self) -> bool {
self.substitute_placeholders
}

fn set_constant_store(&mut self, constant_store: ConstantStore) {
self.constant_store = constant_store;
}
}

0 comments on commit 252e9df

Please sign in to comment.