Repository: ergoplatform/sigma-rust (language: rust)

Issue:
Expose box, tx building + tx signing + serialization in WASM(JS/TS) API
The following workflow should be supported:
1. Create a box (with P2PK contract);
2. Create a transaction;
3. Specify transaction inputs;
4. Sign created transaction;
5. Serialize the signed transaction to JSON.


Discussion / hints:

Fix patch:
diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644
index 50910bd6f..000000000
--- a/.appveyor.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-install:
-  - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
-  - if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly
-  - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
-  - rustc -V
-  - cargo -V
-
-build: false
-
-test_script:
-  - cargo test --locked
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..be000868e
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*.js]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+
+[*.{md, markdown, eex}]
+trim_trailing_whitespace = false
+
diff --git a/.gitignore b/.gitignore
index 8ed29ffdd..b21b12eeb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,7 @@ pkg/
 wasm-pack.log
 .metals
 .vscode/
+**/node_modules
+**/dist
+**/docs
+package-lock.json
diff --git a/.travis.yml b/.travis.yml
index 5c97ca94e..fefaf71a5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -61,13 +61,31 @@ matrix:
       before_script:
         - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
       script:
-        - cd sigma-chain
+        - cd sigma-wasm
         - wasm-pack build
         - wasm-pack test  --firefox --headless
         - wasm-pack test  --firefox --headless --release
         - wasm-pack test  --chrome  --headless
         - wasm-pack test  --chrome  --headless --release
 
+    - name: JS tests
+      dist: bionic
+      sudo: true
+      rust: stable
+      before_cache:
+        - rm -rf /home/travis/.cargo/bin/wasm-pack
+      before_install:
+        - sudo apt-get update
+        - sudo apt-get install curl
+        - curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
+        - sudo apt-get -y install nodejs
+      before_script:
+        - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
+      script:
+        - cd sigma-wasm
+        - wasm-pack build
+        - npm install && npm run test
+
     - name: build for iOS
       os: osx
       osx_image: xcode11.3
@@ -85,9 +103,6 @@ before_cache:
   - rm -rf /home/travis/.cargo/git
   - rm -rf /home/travis/.cargo/registry
 
-before_install:
-  - cargo install cbindgen
-
 script:
   - cargo build --verbose --all
   - cargo test --verbose --all
diff --git a/Cargo.toml b/Cargo.toml
index ede621f9c..08e944975 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,8 +1,8 @@
 [workspace]
 members = [
-    "sigma-chain",
-    "sigma-chain-c",
     "sigma-ser",
     "sigma-tree",
+    "sigma-wasm",
+    "sigma-tree-c",
     "sigma-testutil",
 ]
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..2a98f724f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,1 @@
+Rust implementation of [ErgoScript](https://github.com/ScorexFoundation/sigmastate-interpreter) cryptocurrency scripting language. 
diff --git a/sigma-chain/src/lib.rs b/sigma-chain/src/lib.rs
deleted file mode 100644
index ac48ab043..000000000
--- a/sigma-chain/src/lib.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-//! Ergo blockchain entities
-
-// Coding conventions
-#![forbid(unsafe_code)]
-#![deny(non_upper_case_globals)]
-#![deny(non_camel_case_types)]
-#![deny(non_snake_case)]
-#![deny(unused_mut)]
-#![deny(dead_code)]
-#![deny(unused_imports)]
-#![deny(missing_docs)]
-
-mod misc;
-mod utils;
-
-pub use misc::*;
diff --git a/sigma-chain/src/misc.rs b/sigma-chain/src/misc.rs
deleted file mode 100644
index 62be19a21..000000000
--- a/sigma-chain/src/misc.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-use wasm_bindgen::prelude::*;
-
-/// comment
-#[wasm_bindgen]
-pub fn add(a: i32, b: i32) -> i64 {
-    a as i64 + b as i64
-}
diff --git a/sigma-ser/Cargo.toml b/sigma-ser/Cargo.toml
index 2076565f9..6dd639cdd 100644
--- a/sigma-ser/Cargo.toml
+++ b/sigma-ser/Cargo.toml
@@ -22,8 +22,8 @@ console_error_panic_hook = { version = "0.1.6", optional = true }
 
 [dev-dependencies]
 wasm-bindgen-test = "0.3.10"
-proptest = "0.9"
-proptest-derive = "0.1.2"
+proptest = "0.10"
+proptest-derive = "0.2"
 
 [profile.release]
 # Tell `rustc` to optimize for small code size.
diff --git a/sigma-chain-c/Cargo.toml b/sigma-tree-c/Cargo.toml
similarity index 64%
rename from sigma-chain-c/Cargo.toml
rename to sigma-tree-c/Cargo.toml
index 5a014934a..c0ca6eb12 100644
--- a/sigma-chain-c/Cargo.toml
+++ b/sigma-tree-c/Cargo.toml
@@ -1,17 +1,18 @@
 [package]
-name = "sigma-chain-c"
+name = "sigma-tree-c"
 version = "0.1.0"
 authors = ["Denys Zadorozhnyi <denys@zadorozhnyi.com>"]
 edition = "2018"
+description = "C bindings for sigma-tree"
 
 [lib]
 crate-type = ["staticlib"]
 
 [dependencies]
-sigma-chain = { path = "../sigma-chain", version = "0.1.0" }
+sigma-tree = { path = "../sigma-tree", version = "0.1.0" }
 
 [build-dependencies]
-cbindgen = "0.13.2"
+cbindgen = "0.14"
 
 [profile.release]
 # Tell `rustc` to optimize for small code size.
diff --git a/sigma-chain-c/src/lib.rs b/sigma-tree-c/src/lib.rs
similarity index 100%
rename from sigma-chain-c/src/lib.rs
rename to sigma-tree-c/src/lib.rs
diff --git a/sigma-chain-c/src/misc_c.rs b/sigma-tree-c/src/misc_c.rs
similarity index 77%
rename from sigma-chain-c/src/misc_c.rs
rename to sigma-tree-c/src/misc_c.rs
index 448c5a949..a0d2511b2 100644
--- a/sigma-chain-c/src/misc_c.rs
+++ b/sigma-tree-c/src/misc_c.rs
@@ -1,7 +1,6 @@
-use sigma_chain::add;
 use std::os::raw::{c_int, c_longlong};
 
 #[no_mangle]
 pub extern "C" fn add_numbers(x: c_int, y: c_int) -> c_longlong {
-    add(x, y)
+    x as i64 + y as i64
 }
diff --git a/sigma-tree/Cargo.toml b/sigma-tree/Cargo.toml
index 7a19809fd..3f96127de 100644
--- a/sigma-tree/Cargo.toml
+++ b/sigma-tree/Cargo.toml
@@ -11,18 +11,22 @@ crate-type = ["cdylib", "rlib"]
 sigma-ser = { path = "../sigma-ser" }
 indexmap = "1.3.2"
 base16 = "0.2.1"
-k256 = "0.2.0"
-elliptic-curve = {version = "0.3.0", features = ["getrandom"]}
+k256 = "0.3.0"
+elliptic-curve = {version = "0.4.0", features = ["getrandom"]}
+serde = { version = "1.0", features = ["derive"], optional = true }
+
+[features]
+with-serde = ["serde"]
 
 [dev-dependencies]
 wasm-bindgen-test = "0.3.10"
-proptest-derive = "0.1.2"
+proptest-derive = "0.2"
 sigma-testutil = { path = "../sigma-testutil"}
 rand = "0.7.3"
 
 [dev-dependencies.proptest]
 # wasm support, via https://altsysrq.github.io/proptest-book/proptest/wasm.html
-version = "0.9"
+version = "0.10"
 # The default feature set includes things like process forking which are not
 # supported in Web Assembly.
 default-features = false
diff --git a/sigma-tree/src/ast.rs b/sigma-tree/src/ast.rs
index bfe8fee59..2ed20916a 100644
--- a/sigma-tree/src/ast.rs
+++ b/sigma-tree/src/ast.rs
@@ -1,47 +1,14 @@
-use crate::{data::SigmaProp, serialization::op_code::OpCode, types::*};
+use crate::{serialization::op_code::OpCode, types::*};
 use core::fmt;
 use Expr::*;
 
+mod constant;
 pub mod ops;
 
-#[derive(PartialEq, Eq, Debug)]
-pub struct RegisterId(u8);
-
-#[derive(PartialEq, Eq, Debug)]
-pub enum CollPrim {
-    CollBoolean(Vec<bool>),
-    CollByte(Vec<i8>),
-    CollShort(Vec<i16>),
-    CollInt(Vec<i32>),
-    CollLong(Vec<i64>),
-}
-
-// TODO: extract and wrap ErgoBoxCandidate
-#[derive(PartialEq, Eq, Debug)]
-pub struct ErgoBox {}
-
-#[derive(PartialEq, Eq, Debug)]
-pub enum ConstantVal {
-    Boolean(bool),
-    Byte(i8),
-    Short(i16),
-    Int(i32),
-    Long(i64),
-    BigInt,
-    GroupElement,
-    SigmaProp(Box<SigmaProp>),
-    CBox(Box<ErgoBox>),
-    AvlTree,
-    CollPrim(CollPrim),
-    Coll(Vec<ConstantVal>),
-    Tup(Vec<ConstantVal>),
-}
+pub use constant::*;
 
 #[derive(PartialEq, Eq, Debug)]
-pub struct Constant {
-    pub tpe: SType,
-    pub v: ConstantVal,
-}
+pub struct RegisterId(u8);
 
 #[derive(PartialEq, Eq, Debug)]
 pub enum Expr {
diff --git a/sigma-tree/src/ast/constant.rs b/sigma-tree/src/ast/constant.rs
new file mode 100644
index 000000000..77058b119
--- /dev/null
+++ b/sigma-tree/src/ast/constant.rs
@@ -0,0 +1,48 @@
+use crate::{chain::ErgoBox, sigma_protocol::SigmaProp, types::SType};
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub enum CollPrim {
+    CollBoolean(Vec<bool>),
+    CollByte(Vec<i8>),
+    CollShort(Vec<i16>),
+    CollInt(Vec<i32>),
+    CollLong(Vec<i64>),
+}
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub enum ConstantVal {
+    Boolean(bool),
+    Byte(i8),
+    Short(i16),
+    Int(i32),
+    Long(i64),
+    BigInt,
+    GroupElement,
+    SigmaProp(Box<SigmaProp>),
+    CBox(Box<ErgoBox>),
+    AvlTree,
+    CollPrim(CollPrim),
+    Coll(Vec<ConstantVal>),
+    Tup(Vec<ConstantVal>),
+}
+
+impl ConstantVal {
+    pub fn sigma_prop(prop: SigmaProp) -> ConstantVal {
+        ConstantVal::SigmaProp(Box::new(prop))
+    }
+}
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct Constant {
+    pub tpe: SType,
+    pub v: ConstantVal,
+}
+
+impl Constant {
+    pub fn sigma_prop(prop: SigmaProp) -> Constant {
+        Constant {
+            tpe: SType::SSigmaProp,
+            v: ConstantVal::sigma_prop(prop),
+        }
+    }
+}
diff --git a/sigma-tree/src/chain.rs b/sigma-tree/src/chain.rs
index 84b7875da..cf96834ee 100644
--- a/sigma-tree/src/chain.rs
+++ b/sigma-tree/src/chain.rs
@@ -1,10 +1,21 @@
 //! Ergo chain types
 
+mod address;
 mod box_id;
 mod context_extension;
+mod contract;
 mod data_input;
 mod ergo_box;
 mod input;
 mod prover_result;
+mod secret_key;
 mod token;
 mod transaction;
+
+pub use address::*;
+pub use box_id::*;
+pub use contract::*;
+pub use ergo_box::*;
+pub use input::*;
+pub use secret_key::*;
+pub use transaction::*;
diff --git a/sigma-tree/src/chain/address.rs b/sigma-tree/src/chain/address.rs
new file mode 100644
index 000000000..82eef736a
--- /dev/null
+++ b/sigma-tree/src/chain/address.rs
@@ -0,0 +1,136 @@
+use crate::{
+    ast::{Constant, Expr},
+    ecpoint::EcPoint,
+    sigma_protocol::{ProveDlog, SigmaBoolean, SigmaProofOfKnowledgeTree, SigmaProp},
+    ErgoTree,
+};
+use std::fmt;
+use std::rc::Rc;
+
+/// Address types
+pub enum AddressTypePrefix {
+    /// 0x01 - Pay-to-PublicKey(P2PK) address
+    P2PKAddress = 1,
+    /// 0x02 - Pay-to-Script-Hash(P2SH)
+    Pay2SHAddress = 2,
+    /// 0x03 - Pay-to-Script(P2S)
+    Pay2SAddress = 3,
+}
+
+/**
+ * An address is a short string corresponding to some script used to protect a box. Unlike (string-encoded) binary
+ * representation of a script, an address has some useful characteristics:
+ *
+ * - Integrity of an address could be checked., as it is incorporating a checksum.
+ * - A prefix of address is showing network and an address type.
+ * - An address is using an encoding (namely, Base58) which is avoiding similarly l0Oking characters, friendly to
+ * double-clicking and line-breaking in emails.
+ *
+ *
+ *
+ * An address is encoding network type, address type, checksum, and enough information to watch for a particular scripts.
+ *
+ * Possible network types are:
+ * Mainnet - 0x00
+ * Testnet - 0x10
+ *
+ * For an address type, we form content bytes as follows:
+ *
+ * P2PK - serialized (compressed) public key
+ * P2SH - first 192 bits of the Blake2b256 hash of serialized script bytes
+ * P2S  - serialized script
+ *
+ * Address examples for testnet:
+ *
+ * 3   - P2PK (3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN)
+ * ?   - P2SH (rbcrmKEYduUvADj9Ts3dSVSG27h54pgrq5fPuwB)
+ * ?   - P2S (Ms7smJwLGbUAjuWQ)
+ *
+ * for mainnet:
+ *
+ * 9  - P2PK (9fRAWhdxEsTcdb8PhGNrZfwqa65zfkuYHAMmkQLcic1gdLSV5vA)
+ * ?  - P2SH (8UApt8czfFVuTgQmMwtsRBZ4nfWquNiSwCWUjMg)
+ * ?  - P2S (4MQyML64GnzMxZgm, BxKBaHkvrTvLZrDcZjcsxsF7aSsrN73ijeFZXtbj4CXZHHcvBtqSxQ)
+ *
+ *
+ * Prefix byte = network type + address type
+ *
+ * checksum = blake2b256(prefix byte ++ content bytes)
+ *
+ * address = prefix byte ++ content bytes ++ checksum
+ *
+ */
+pub trait Address {
+    /// address type (P2PK, P2SH or P2S)
+    fn address_type_prefix(&self) -> AddressTypePrefix;
+
+    /// script encoded in the address
+    fn script(&self) -> ErgoTree;
+}
+
+/// P2PK - serialized (compressed) public key
+pub struct P2PKAddress {
+    /// pubkey (encoded in the address)
+    pubkey: ProveDlog,
+}
+
+impl P2PKAddress {
+    /// create from ProveDlog
+    pub fn new(pubkey: ProveDlog) -> P2PKAddress {
+        P2PKAddress { pubkey }
+    }
+}
+
+impl Address for P2PKAddress {
+    fn address_type_prefix(&self) -> AddressTypePrefix {
+        AddressTypePrefix::P2PKAddress
+    }
+    fn script(&self) -> ErgoTree {
+        ErgoTree::from_proposition(Rc::new(Expr::Const(Constant::sigma_prop(SigmaProp::new(
+            SigmaBoolean::ProofOfKnowledge(SigmaProofOfKnowledgeTree::ProveDlog(
+                self.pubkey.clone(),
+            )),
+        )))))
+    }
+}
+
+/// Network type
+pub enum NetworkPrefix {
+    /// Mainnet
+    Mainnet = 0,
+    /// Testnet
+    Testnet = 16,
+}
+
+/// Errors on encoding/decoding of addresses
+#[derive(Debug)]
+pub enum AddressEncoderError {
+    /// Failed to decode Base58
+    Base58DecodingError(String),
+}
+
+impl fmt::Display for AddressEncoderError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            AddressEncoderError::Base58DecodingError(e) => write!(f, "Error({:?}, {})", self, e),
+        }
+    }
+}
+
+/// Encodes/Decodes address to/from string
+pub struct AddressEncoder(NetworkPrefix);
+
+impl AddressEncoder {
+    /// create a new AddressEncoder for a given network type
+    pub fn new(network_prefix: NetworkPrefix) -> AddressEncoder {
+        AddressEncoder(network_prefix)
+    }
+
+    /// parse address from Base58 encoded string
+    pub fn parse_address_from_str(&self, _: &str) -> Result<Box<dyn Address>, AddressEncoderError> {
+        // not implemented, see https://github.com/ergoplatform/sigma-rust/issues/35
+        Ok(Box::new(P2PKAddress::new(
+            ProveDlog::new(EcPoint::random()),
+        )))
+    }
+}
diff --git a/sigma-tree/src/chain/box_id.rs b/sigma-tree/src/chain/box_id.rs
index 13f956346..a0d938e19 100644
--- a/sigma-tree/src/chain/box_id.rs
+++ b/sigma-tree/src/chain/box_id.rs
@@ -4,6 +4,9 @@ use sigma_ser::serializer::SigmaSerializable;
 use sigma_ser::vlq_encode;
 use std::io;
 
+#[cfg(feature = "with-serde")]
+use serde::{Deserialize, Serialize};
+
 /// Size of Box.id
 pub const BOX_ID_SIZE: usize = crate::constants::DIGEST32_SIZE;
 
@@ -12,6 +15,7 @@ use proptest_derive::Arbitrary;
 
 #[derive(PartialEq, Debug)]
 #[cfg_attr(test, derive(Arbitrary))]
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
 /// newtype for box ids
 pub struct BoxId(pub [u8; BOX_ID_SIZE]);
 
diff --git a/sigma-tree/src/chain/context_extension.rs b/sigma-tree/src/chain/context_extension.rs
index becb94aff..31bd22ab5 100644
--- a/sigma-tree/src/chain/context_extension.rs
+++ b/sigma-tree/src/chain/context_extension.rs
@@ -1,12 +1,15 @@
 //! ContextExtension type
+#[cfg(feature = "with-serde")]
+use serde::{Deserialize, Serialize};
 use sigma_ser::serializer::SerializationError;
 use sigma_ser::serializer::SigmaSerializable;
 use sigma_ser::vlq_encode;
 use std::collections::HashMap;
 use std::io;
 
-#[derive(Debug, PartialEq, Eq)]
 /// User-defined variables to be put into context
+#[derive(Debug, PartialEq, Eq)]
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
 pub struct ContextExtension {
     /// key-value pairs of variable id and it's value
     pub values: HashMap<u8, Vec<u8>>,
diff --git a/sigma-tree/src/chain/contract.rs b/sigma-tree/src/chain/contract.rs
new file mode 100644
index 000000000..6a02cf182
--- /dev/null
+++ b/sigma-tree/src/chain/contract.rs
@@ -0,0 +1,27 @@
+//! Ergo contract
+
+use super::Address;
+use crate::ErgoTree;
+
+/// High-level wrapper for ErgoTree
+#[allow(dead_code)]
+pub struct Contract {
+    ergo_tree: ErgoTree,
+}
+
+impl Contract {
+    /// create new contract from ErgoTree
+    pub fn new(ergo_tree: ErgoTree) -> Contract {
+        Contract { ergo_tree }
+    }
+
+    /// create new contract that allow spending for a given Address
+    pub fn pay_to_address(address: &dyn Address) -> Contract {
+        Contract::new(address.script())
+    }
+
+    /// get ErgoTree for this contract
+    pub fn get_ergo_tree(&self) -> ErgoTree {
+        self.ergo_tree.clone()
+    }
+}
diff --git a/sigma-tree/src/chain/data_input.rs b/sigma-tree/src/chain/data_input.rs
index a527417ce..421f624cc 100644
--- a/sigma-tree/src/chain/data_input.rs
+++ b/sigma-tree/src/chain/data_input.rs
@@ -10,10 +10,13 @@ use super::box_id::BoxId;
 use proptest::prelude::*;
 #[cfg(test)]
 use proptest_derive::Arbitrary;
+#[cfg(feature = "with-serde")]
+use serde::{Deserialize, Serialize};
 
+/// Inputs, that are used to enrich script context, but won't be spent by the transaction
 #[derive(PartialEq, Debug)]
 #[cfg_attr(test, derive(Arbitrary))]
-/// Inputs, that are used to enrich script context, but won't be spent by the transaction
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
 pub struct DataInput {
     /// id of the box to add into context (should be in UTXO)
     pub box_id: BoxId,
diff --git a/sigma-tree/src/chain/ergo_box.rs b/sigma-tree/src/chain/ergo_box.rs
index ff1ceebe8..f9d1b5832 100644
--- a/sigma-tree/src/chain/ergo_box.rs
+++ b/sigma-tree/src/chain/ergo_box.rs
@@ -2,6 +2,8 @@
 use super::token::{TokenAmount, TokenId};
 use crate::ergo_tree::ErgoTree;
 use indexmap::IndexSet;
+#[cfg(feature = "with-serde")]
+use serde::{Deserialize, Serialize};
 use sigma_ser::serializer::SerializationError;
 use sigma_ser::serializer::SigmaSerializable;
 use sigma_ser::vlq_encode;
@@ -12,11 +14,16 @@ use std::io;
 #[allow(dead_code)]
 const STARTING_NON_MANDATORY_INDEX: u8 = 4;
 
-#[derive(PartialEq, Eq, Hash, Debug)]
 /// newtype for additional registers R4 - R9
+#[derive(PartialEq, Eq, Hash, Debug, Clone)]
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
 pub struct NonMandatoryRegisterId(u8);
 
-#[derive(PartialEq, Debug)]
+/// Transaction id (ModifierId in sigmastate)
+#[derive(PartialEq, Eq, Hash, Debug, Clone)]
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
+pub struct TxId(String);
+
 /// Box (aka coin, or an unspent output) is a basic concept of a UTXO-based cryptocurrency.
 /// In Bitcoin, such an object is associated with some monetary value (arbitrary,
 /// but with predefined precision, so we use integer arithmetic to work with the value),
@@ -34,6 +41,30 @@ pub struct NonMandatoryRegisterId(u8);
 ///
 /// A transaction is unsealing a box. As a box can not be open twice, any further valid transaction
 /// can not be linked to the same box.
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct ErgoBox {
+    /// amount of money associated with the box
+    pub value: u64,
+    /// guarding script, which should be evaluated to true in order to open this box
+    pub ergo_tree: ErgoTree,
+    /// secondary tokens the box contains
+    pub tokens: Vec<TokenAmount>,
+    ///  additional registers the box can carry over
+    pub additional_registers: HashMap<NonMandatoryRegisterId, Box<[u8]>>,
+    /// height when a transaction containing the box was created.
+    /// This height is declared by user and should not exceed height of the block,
+    /// containing the transaction with this box.
+    pub creation_height: u32,
+    /// id of transaction which created the box
+    pub transaction_id: TxId,
+    /// number of box (from 0 to total number of boxes the transaction with transactionId created - 1)
+    pub index: u16,
+}
+
+/// Contains the same fields as `ErgoBox`, except if transaction id and index,
+/// that will be calculated after full transaction formation.
+#[derive(PartialEq, Eq, Debug)]
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
 pub struct ErgoBoxCandidate {
     /// amount of money associated with the box
     pub value: u64,
@@ -50,7 +81,19 @@ pub struct ErgoBoxCandidate {
 }
 
 impl ErgoBoxCandidate {
-    /// Box serialization with token ids optionally saved in transaction (in this case only token index is saved)
+    /// create box with value guarded by ErgoTree
+    pub fn new(value: u64, ergo_tree: ErgoTree, creation_height: u32) -> ErgoBoxCandidate {
+        ErgoBoxCandidate {
+            value,
+            ergo_tree,
+            tokens: vec![],
+            additional_registers: HashMap::new(),
+            creation_height,
+        }
+    }
+
+    /// Box serialization with token ids optionally saved in transaction
+    /// (in this case only token index is saved)
     pub fn serialize_body_with_indexed_digests<W: vlq_encode::WriteSigmaVlqExt>(
         &self,
         token_ids_in_tx: Option<&IndexSet<TokenId>>,
diff --git a/sigma-tree/src/chain/input.rs b/sigma-tree/src/chain/input.rs
index e9fa6c4be..0b68dddd6 100644
--- a/sigma-tree/src/chain/input.rs
+++ b/sigma-tree/src/chain/input.rs
@@ -9,10 +9,13 @@ use super::{box_id::BoxId, prover_result::ProverResult};
 use proptest::prelude::*;
 #[cfg(test)]
 use proptest_derive::Arbitrary;
+#[cfg(feature = "with-serde")]
+use serde::{Deserialize, Serialize};
 
+/// Fully signed transaction input
 #[derive(PartialEq, Debug)]
 #[cfg_attr(test, derive(Arbitrary))]
-/// Fully signed transaction input
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
 pub struct Input {
     /// id of the box to spent
     pub box_id: BoxId,
diff --git a/sigma-tree/src/chain/prover_result.rs b/sigma-tree/src/chain/prover_result.rs
index d1c4fbdae..4ad56d21b 100644
--- a/sigma-tree/src/chain/prover_result.rs
+++ b/sigma-tree/src/chain/prover_result.rs
@@ -7,9 +7,12 @@ use std::io;
 use super::context_extension::ContextExtension;
 #[cfg(test)]
 use proptest::{arbitrary::Arbitrary, collection::vec, prelude::*};
+#[cfg(feature = "with-serde")]
+use serde::{Deserialize, Serialize};
 
 /// Proof of correctness of tx spending
 #[derive(Debug, PartialEq, Eq)]
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
 pub struct ProverResult {
     /// proof that satisfies final sigma proposition
     pub proof: Vec<u8>,
diff --git a/sigma-tree/src/chain/secret_key.rs b/sigma-tree/src/chain/secret_key.rs
new file mode 100644
index 000000000..0b34240f2
--- /dev/null
+++ b/sigma-tree/src/chain/secret_key.rs
@@ -0,0 +1,14 @@
+use crate::sigma_protocol::DlogProverInput;
+
+/// Types of secrets
+pub enum SecretKey {
+    /// Secret exponent of a group element, i.e. secret w such as h = g^^w, where g is group generator, h is a public key.
+    DlogSecretKey(DlogProverInput),
+}
+
+impl SecretKey {
+    /// Generates random DlogProverInput
+    pub fn random_dlog() -> SecretKey {
+        SecretKey::DlogSecretKey(DlogProverInput::random())
+    }
+}
diff --git a/sigma-tree/src/chain/token.rs b/sigma-tree/src/chain/token.rs
index 84fb60092..fc1dd5c5f 100644
--- a/sigma-tree/src/chain/token.rs
+++ b/sigma-tree/src/chain/token.rs
@@ -11,15 +11,19 @@ pub const TOKEN_ID_SIZE: usize = crate::constants::DIGEST32_SIZE;
 
 #[cfg(test)]
 use proptest_derive::Arbitrary;
+#[cfg(feature = "with-serde")]
+use serde::{Deserialize, Serialize};
 
 /// newtype for token id
 #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
 #[cfg_attr(test, derive(Arbitrary))]
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
 pub struct TokenId(pub [u8; TOKEN_ID_SIZE]);
 
 /// Token amount represented with token id paired with it's amount
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 #[cfg_attr(test, derive(Arbitrary))]
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
 pub struct TokenAmount {
     /// token id
     pub token_id: TokenId,
diff --git a/sigma-tree/src/chain/transaction.rs b/sigma-tree/src/chain/transaction.rs
index b0eb5e8a5..354096861 100644
--- a/sigma-tree/src/chain/transaction.rs
+++ b/sigma-tree/src/chain/transaction.rs
@@ -2,6 +2,8 @@
 
 use super::{data_input::DataInput, ergo_box::ErgoBoxCandidate, input::Input, token::TokenId};
 use indexmap::IndexSet;
+#[cfg(feature = "with-serde")]
+use serde::{Deserialize, Serialize};
 use sigma_ser::serializer::SerializationError;
 use sigma_ser::serializer::SigmaSerializable;
 use sigma_ser::vlq_encode;
@@ -19,6 +21,7 @@ use std::iter::FromIterator;
  * Transactions are not encrypted, so it is possible to browse and view every transaction ever
  * collected into a block.
  */
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
 #[derive(PartialEq, Debug)]
 pub struct Transaction {
     /// inputs, that will be spent by this transaction.
diff --git a/sigma-tree/src/data.rs b/sigma-tree/src/data.rs
deleted file mode 100644
index 1857f47e0..000000000
--- a/sigma-tree/src/data.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-//! Underlying Sigma data types
-
-use crate::{ecpoint::EcPoint, serialization::op_code::OpCode};
-
-#[allow(dead_code)]
-#[derive(PartialEq, Eq, Debug)]
-pub enum SigmaBoolean {
-    ProveDHTuple {
-        gv: Box<EcPoint>,
-        hv: Box<EcPoint>,
-        uv: Box<EcPoint>,
-        vv: Box<EcPoint>,
-    },
-    ProveDlog(Box<EcPoint>),
-    CAND(Vec<SigmaBoolean>),
-}
-
-impl SigmaBoolean {
-    pub fn op_code(&self) -> OpCode {
-        match self {
-            SigmaBoolean::ProveDHTuple { .. } => todo!(),
-            SigmaBoolean::ProveDlog(_) => OpCode::PROVE_DLOG,
-            SigmaBoolean::CAND(_) => todo!(),
-        }
-    }
-}
-
-#[derive(PartialEq, Eq, Debug)]
-pub struct SigmaProp(SigmaBoolean);
-
-impl SigmaProp {
-    pub fn new(sbool: SigmaBoolean) -> Self {
-        SigmaProp { 0: sbool }
-    }
-
-    pub fn value(&self) -> &SigmaBoolean {
-        &self.0
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use proptest::prelude::*;
-
-    impl Arbitrary for SigmaBoolean {
-        type Parameters = ();
-        type Strategy = BoxedStrategy<Self>;
-
-        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
-            (any::<EcPoint>())
-                .prop_map(|ecp| SigmaBoolean::ProveDlog(Box::new(ecp)))
-                .boxed()
-        }
-    }
-
-    impl Arbitrary for SigmaProp {
-        type Parameters = ();
-        type Strategy = BoxedStrategy<Self>;
-
-        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
-            (any::<SigmaBoolean>()).prop_map(|sb| SigmaProp(sb)).boxed()
-        }
-    }
-}
diff --git a/sigma-tree/src/ecpoint.rs b/sigma-tree/src/ecpoint.rs
index f015a6b6a..33ac73f1f 100644
--- a/sigma-tree/src/ecpoint.rs
+++ b/sigma-tree/src/ecpoint.rs
@@ -1,3 +1,4 @@
+use crate::sigma_protocol::DlogProverInput;
 use k256::{
     arithmetic::{AffinePoint, ProjectivePoint, Scalar},
     PublicKey,
@@ -6,36 +7,39 @@ use sigma_ser::{
     serializer::{SerializationError, SigmaSerializable},
     vlq_encode,
 };
-use std::convert::TryInto;
 use std::io;
 
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq, Debug, Clone)]
 pub struct EcPoint(ProjectivePoint);
 
 impl EcPoint {
     pub const GROUP_SIZE: usize = 33;
 
     pub fn random() -> EcPoint {
-        let scalar = loop {
-            // Generate a new secret key using the operating system's
-            // cryptographically secure random number generator
-            let sk = k256::SecretKey::generate();
-            let bytes: [u8; 32] = sk
-                .secret_scalar()
-                .as_ref()
-                .as_slice()
-                .try_into()
-                .expect("expected 32 bytes");
-            // Returns None if the byte array does not contain
-            // a big-endian integer in the range [0, n), where n is group order.
-            let maybe_scalar = Scalar::from_bytes(bytes);
-            if bool::from(maybe_scalar.is_some()) {
-                break maybe_scalar.unwrap();
-            }
-        };
-        // we treat EC as a multiplicative group, therefore, exponentiate point is multiply.
-        let pkp = ProjectivePoint::generator() * &scalar;
-        EcPoint(pkp)
+        let sk = DlogProverInput::random();
+        EcPoint::generator().exponentiate(&sk.w)
+    }
+
+    pub fn generator() -> EcPoint {
+        EcPoint(ProjectivePoint::generator())
+    }
+
+    pub fn is_infinity(&self) -> bool {
+        let identity = ProjectivePoint::identity();
+        self.0 == identity
+    }
+
+    pub fn exponentiate(&self, exponent: &Scalar) -> EcPoint {
+        if !self.is_infinity() {
+            // implement for negative exponent
+            // see reference impl https://github.com/ScorexFoundation/sigmastate-interpreter/blob/ec71a6f988f7412bc36199f46e7ad8db643478c7/sigmastate/src/main/scala/sigmastate/basics/BcDlogGroup.scala#L201
+            // see https://github.com/ergoplatform/sigma-rust/issues/36
+
+            // we treat EC as a multiplicative group, therefore, exponentiate point is multiply.
+            EcPoint(self.0 * exponent)
+        } else {
+            self.clone()
+        }
     }
 }
 
diff --git a/sigma-tree/src/ergo_tree.rs b/sigma-tree/src/ergo_tree.rs
index 4b7275a23..04a2df8d3 100644
--- a/sigma-tree/src/ergo_tree.rs
+++ b/sigma-tree/src/ergo_tree.rs
@@ -3,6 +3,8 @@ use crate::{
     ast::{Constant, Expr},
     types::SType,
 };
+#[cfg(feature = "with-serde")]
+use serde::{Deserializer, Serializer};
 use sigma_ser::serializer::SerializationError;
 use sigma_ser::serializer::SigmaSerializable;
 use sigma_ser::vlq_encode;
@@ -12,7 +14,7 @@ use vlq_encode::{ReadSigmaVlqExt, WriteSigmaVlqExt};
 
 /** The root of ErgoScript IR. Serialized instances of this class are self sufficient and can be passed around.
  */
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 #[allow(dead_code)]
 pub struct ErgoTree {
     header: ErgoTreeHeader,
@@ -20,7 +22,7 @@ pub struct ErgoTree {
     root: Rc<Expr>,
 }
 
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 struct ErgoTreeHeader(u8);
 
 impl ErgoTree {
@@ -84,10 +86,33 @@ impl SigmaSerializable for ErgoTree {
     }
 }
 
+#[cfg(feature = "with-serde")]
+impl serde::Serialize for ErgoTree {
+    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        // not implmented
+        // serialize ergo tree and encode as Base16
+        // https://github.com/ergoplatform/sigma-rust/issues/37
+        s.serialize_str("")
+    }
+}
+
+#[cfg(feature = "with-serde")]
+impl<'de> serde::Deserialize<'de> for ErgoTree {
+    fn deserialize<D>(_: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        todo!()
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{ast::ConstantVal, data::SigmaProp};
+    use crate::{ast::ConstantVal, sigma_protocol::SigmaProp};
     use proptest::prelude::*;
     use sigma_ser::test_helpers::*;
 
diff --git a/sigma-tree/src/eval/value.rs b/sigma-tree/src/eval/value.rs
index 2a2a8b563..57b759252 100644
--- a/sigma-tree/src/eval/value.rs
+++ b/sigma-tree/src/eval/value.rs
@@ -1,4 +1,4 @@
-use crate::{ast::ErgoBox, data::SigmaBoolean};
+use crate::{chain::ErgoBox, sigma_protocol::SigmaBoolean};
 use std::ops::Add;
 
 #[allow(dead_code)]
diff --git a/sigma-tree/src/lib.rs b/sigma-tree/src/lib.rs
index be926c499..b7e9cdd52 100644
--- a/sigma-tree/src/lib.rs
+++ b/sigma-tree/src/lib.rs
@@ -12,11 +12,12 @@
 
 mod ast;
 mod constants;
-mod data;
 mod ecpoint;
+mod ergo_tree;
 mod eval;
 mod serialization;
+mod sigma_protocol;
 mod types;
 
 pub mod chain;
-pub mod ergo_tree;
+pub use ergo_tree::*;
diff --git a/sigma-tree/src/serialization/data.rs b/sigma-tree/src/serialization/data.rs
index 8464ccd44..4bafa7868 100644
--- a/sigma-tree/src/serialization/data.rs
+++ b/sigma-tree/src/serialization/data.rs
@@ -1,11 +1,8 @@
 use crate::{
-    ast::CollPrim,
-    ast::ConstantVal,
-    ast::ConstantVal::*,
-    data::{self, SigmaBoolean},
-    types::SType,
+    ast::CollPrim, ast::ConstantVal, ast::ConstantVal::*, sigma_protocol, types::SType,
     types::SType::*,
 };
+use sigma_protocol::{SigmaBoolean, SigmaProp};
 use sigma_ser::{
     serializer::{SerializationError, SigmaSerializable},
     vlq_encode::{ReadSigmaVlqExt, WriteSigmaVlqExt},
@@ -14,7 +11,7 @@ use std::io;
 
 pub struct DataSerializer {}
 
-// TODO: convert to SigmaSerializable impl for ConstantVal
+// TODO: convert to SigmaSerializable impl for ConstantVal?
 impl DataSerializer {
     pub fn sigma_serialize<W: WriteSigmaVlqExt>(
         c: &ConstantVal,
@@ -46,9 +43,7 @@ impl DataSerializer {
         let c = match tpe {
             SAny => todo!(),
             SByte => Byte(r.get_i8()?),
-            SSigmaProp => SigmaProp(Box::new(data::SigmaProp::new(SigmaBoolean::sigma_parse(
-                r,
-            )?))),
+            SSigmaProp => ConstantVal::sigma_prop(SigmaProp::new(SigmaBoolean::sigma_parse(r)?)),
             SColl(elem_type) => {
                 let len = r.get_u16()? as usize;
                 if **elem_type == SByte {
diff --git a/sigma-tree/src/serialization/sigmaboolean.rs b/sigma-tree/src/serialization/sigmaboolean.rs
index 5a3f4154b..3819563eb 100644
--- a/sigma-tree/src/serialization/sigmaboolean.rs
+++ b/sigma-tree/src/serialization/sigmaboolean.rs
@@ -1,5 +1,8 @@
 use super::op_code::OpCode;
-use crate::{data::SigmaBoolean, ecpoint::EcPoint};
+use crate::{
+    ecpoint::EcPoint,
+    sigma_protocol::{ProveDlog, SigmaBoolean, SigmaProofOfKnowledgeTree},
+};
 use sigma_ser::{
     serializer::{SerializationError, SigmaSerializable},
     vlq_encode,
@@ -10,8 +13,10 @@ impl SigmaSerializable for SigmaBoolean {
     fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
         self.op_code().sigma_serialize(w)?;
         match self {
-            SigmaBoolean::ProveDHTuple { .. } => todo!(),
-            SigmaBoolean::ProveDlog(v) => v.sigma_serialize(w),
+            SigmaBoolean::ProofOfKnowledge(proof) => match proof {
+                SigmaProofOfKnowledgeTree::ProveDHTuple { .. } => todo!(),
+                SigmaProofOfKnowledgeTree::ProveDlog(v) => v.h.sigma_serialize(w),
+            },
             SigmaBoolean::CAND(_) => todo!(),
         }
     }
@@ -21,7 +26,9 @@ impl SigmaSerializable for SigmaBoolean {
         match op_code {
             OpCode::PROVE_DLOG => {
                 let p = EcPoint::sigma_parse(r)?;
-                Ok(SigmaBoolean::ProveDlog(Box::new(p)))
+                Ok(SigmaBoolean::ProofOfKnowledge(
+                    SigmaProofOfKnowledgeTree::ProveDlog(ProveDlog::new(p)),
+                ))
             }
             _ => todo!(),
         }
diff --git a/sigma-tree/src/sigma_protocol.rs b/sigma-tree/src/sigma_protocol.rs
new file mode 100644
index 000000000..7c6bd239e
--- /dev/null
+++ b/sigma-tree/src/sigma_protocol.rs
@@ -0,0 +1,136 @@
+use k256::arithmetic::Scalar;
+
+use crate::{ecpoint::EcPoint, serialization::op_code::OpCode};
+use std::convert::TryInto;
+
+pub struct DlogProverInput {
+    pub w: Scalar,
+}
+
+impl DlogProverInput {
+    pub fn random() -> DlogProverInput {
+        let scalar = loop {
+            // Generate a new secret key using the operating system's
+            // cryptographically secure random number generator
+            let sk = k256::SecretKey::generate();
+            let bytes: [u8; 32] = sk
+                .secret_scalar()
+                .as_ref()
+                .as_slice()
+                .try_into()
+                .expect("expected 32 bytes");
+            // Returns None if the byte array does not contain
+            // a big-endian integer in the range [0, n), where n is group order.
+            let maybe_scalar = Scalar::from_bytes(bytes);
+            if bool::from(maybe_scalar.is_some()) {
+                break maybe_scalar.unwrap();
+            }
+        };
+        DlogProverInput { w: scalar }
+    }
+
+    #[allow(dead_code)]
+    fn public_image(&self) -> ProveDlog {
+        // test it, see https://github.com/ergoplatform/sigma-rust/issues/38
+        let g = EcPoint::generator();
+        ProveDlog::new(g.exponentiate(&self.w))
+    }
+}
+
+pub trait PrivateInput {
+    fn public_image(&self) -> SigmaProofOfKnowledgeTree;
+}
+
+impl PrivateInput for DlogProverInput {
+    #[allow(dead_code)]
+    fn public_image(&self) -> SigmaProofOfKnowledgeTree {
+        SigmaProofOfKnowledgeTree::ProveDlog(self.public_image())
+    }
+}
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct ProveDlog {
+    pub h: Box<EcPoint>,
+}
+
+impl ProveDlog {
+    pub fn new(ecpoint: EcPoint) -> ProveDlog {
+        ProveDlog {
+            h: Box::new(ecpoint),
+        }
+    }
+}
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct ProveDHTuple {
+    gv: Box<EcPoint>,
+    hv: Box<EcPoint>,
+    uv: Box<EcPoint>,
+    vv: Box<EcPoint>,
+}
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub enum SigmaProofOfKnowledgeTree {
+    ProveDHTuple(ProveDHTuple),
+    ProveDlog(ProveDlog),
+}
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub enum SigmaBoolean {
+    ProofOfKnowledge(SigmaProofOfKnowledgeTree),
+    CAND(Vec<SigmaBoolean>),
+}
+
+impl SigmaBoolean {
+    pub fn op_code(&self) -> OpCode {
+        match self {
+            SigmaBoolean::ProofOfKnowledge(SigmaProofOfKnowledgeTree::ProveDlog(_)) => {
+                OpCode::PROVE_DLOG
+            }
+            _ => todo!(),
+        }
+    }
+}
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct SigmaProp(SigmaBoolean);
+
+impl SigmaProp {
+    pub fn new(sbool: SigmaBoolean) -> Self {
+        SigmaProp { 0: sbool }
+    }
+
+    pub fn value(&self) -> &SigmaBoolean {
+        &self.0
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use proptest::prelude::*;
+
+    impl Arbitrary for SigmaBoolean {
+        type Parameters = ();
+        type Strategy = BoxedStrategy<Self>;
+
+        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+            (any::<EcPoint>())
+                .prop_map(|ecp| {
+                    SigmaBoolean::ProofOfKnowledge(SigmaProofOfKnowledgeTree::ProveDlog(
+                        ProveDlog::new(ecp),
+                    ))
+                })
+                .boxed()
+        }
+    }
+
+    impl Arbitrary for SigmaProp {
+        type Parameters = ();
+        type Strategy = BoxedStrategy<Self>;
+
+        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+            (any::<SigmaBoolean>()).prop_map(|sb| SigmaProp(sb)).boxed()
+        }
+    }
+}
diff --git a/sigma-tree/src/types.rs b/sigma-tree/src/types.rs
index 9bd2dc18f..226ca47be 100644
--- a/sigma-tree/src/types.rs
+++ b/sigma-tree/src/types.rs
@@ -7,7 +7,7 @@ pub struct MethodId(u8);
 #[derive(PartialEq, Eq, Debug)]
 pub struct TypeId(u8);
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 pub enum SType {
     SAny,
     SBoolean,
@@ -52,19 +52,19 @@ impl SType {
     }
 }
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 pub struct STypeVar {
     name: String,
 }
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 pub struct STypeParam {
     ident: STypeVar,
     upper_bound: Option<SType>,
     lower_bound: Option<SType>,
 }
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 pub struct SFunc {
     t_dom: Vec<SType>,
     t_range: SType,
diff --git a/sigma-wasm/.babelrc b/sigma-wasm/.babelrc
new file mode 100644
index 000000000..6fd3a852e
--- /dev/null
+++ b/sigma-wasm/.babelrc
@@ -0,0 +1,4 @@
+{
+  "plugins": ["@babel/plugin-syntax-dynamic-import"],
+  "presets": ["@babel/preset-env"]
+}
\ No newline at end of file
diff --git a/sigma-chain/Cargo.toml b/sigma-wasm/Cargo.toml
similarity index 69%
rename from sigma-chain/Cargo.toml
rename to sigma-wasm/Cargo.toml
index 5f9fc185b..811d8eb13 100644
--- a/sigma-chain/Cargo.toml
+++ b/sigma-wasm/Cargo.toml
@@ -1,35 +1,39 @@
 [package]
-name = "sigma-chain"
+name = "sigma-tree-wasm"
 version = "0.1.0"
 authors = ["Denys Zadorozhnyi <denys@zadorozhnyi.com>"]
 edition = "2018"
+description = "WASM bindings for sigma-tree"
 
 [lib]
 crate-type = ["cdylib", "rlib"]
 
 [features]
 default = ["console_error_panic_hook"]
-# testing = ["sigma-tree/testing"]
 
 [dependencies]
-wasm-bindgen = "0.2"
-sigma-ser = { path = "../sigma-ser" }
-sigma-tree = { path = "../sigma-tree" }
-
+serde = { version = "1.0", features = ["derive"]}
+sigma-tree = { path = "../sigma-tree", features = ["with-serde"] }
+# used in elliptic-curve(in sigma-tree), compiled here with WASM support
+getrandom = {version = "0.1", features = ["wasm-bindgen"]}
 # The `console_error_panic_hook` crate provides better debugging of panics by
 # logging them with `console.error`. This is great for development, but requires
 # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
 # code size when deploying.
 console_error_panic_hook = { version = "0.1.6", optional = true }
 
+[dependencies.wasm-bindgen]
+version = "0.2"
+features = ["serde-serialize"]
+
 [dev-dependencies]
 wasm-bindgen-test = "0.3.10"
-proptest-derive = "0.1.2"
+proptest-derive = "0.2"
 sigma-testutil = { path = "../sigma-testutil" }
 
 [dev-dependencies.proptest]
 # wasm support, via https://altsysrq.github.io/proptest-book/proptest/wasm.html
-version = "0.9"
+version = "0.10"
 # The default feature set includes things like process forking which are not
 # supported in Web Assembly.
 default-features = false
diff --git a/sigma-wasm/README.md b/sigma-wasm/README.md
new file mode 100644
index 000000000..53c944152
--- /dev/null
+++ b/sigma-wasm/README.md
@@ -0,0 +1,2 @@
+WebAssembly library with JS/TS bindings for [sigma-rust](https://github.com/ergoplatform/sigma-rust).
+
diff --git a/sigma-wasm/jsdoc.json b/sigma-wasm/jsdoc.json
new file mode 100644
index 000000000..b828e006f
--- /dev/null
+++ b/sigma-wasm/jsdoc.json
@@ -0,0 +1,3 @@
+{
+    "plugins": ["plugins/markdown"]
+}
diff --git a/sigma-wasm/package.json b/sigma-wasm/package.json
new file mode 100644
index 000000000..5f13c1b9d
--- /dev/null
+++ b/sigma-wasm/package.json
@@ -0,0 +1,26 @@
+{
+    "scripts": {
+        "build": "webpack",
+        "serve": "wasm-pack build --target browser && webpack-dev-server",
+        "test": "wasm-pack build --target nodejs && mocha --require @babel/register --require @babel/polyfill 'tests/test*.js'",
+        "doc": "jsdoc -c jsdoc.json pkg/sigma_tree_wasm.js README.md -d docs"
+    },
+    "devDependencies": {
+        "@babel/core": "^7.6.4",
+        "@babel/plugin-syntax-dynamic-import": "^7.2.0",
+        "@babel/polyfill": "^7.6.0",
+        "@babel/preset-env": "^7.6.3",
+        "@babel/register": "^7.6.2",
+        "html-webpack-plugin": "^3.2.0",
+        "jsdoc": "~3.6.3",
+        "mocha": "^6.1.4",
+        "mocha-loader": "^2.0.1",
+        "text-encoder": "0.0.4",
+        "webpack": "^4.11.1",
+        "webpack-cli": "^3.1.1",
+        "webpack-dev-server": "^3.1.0"
+    },
+    "dependencies": {
+        "chai": "^4.2.0"
+    }
+}
diff --git a/sigma-wasm/src/lib.rs b/sigma-wasm/src/lib.rs
new file mode 100644
index 000000000..70130511b
--- /dev/null
+++ b/sigma-wasm/src/lib.rs
@@ -0,0 +1,211 @@
+//! WASM bindings for sigma-tree
+
+// Coding conventions
+#![forbid(unsafe_code)]
+#![deny(non_upper_case_globals)]
+#![deny(non_camel_case_types)]
+#![deny(non_snake_case)]
+#![deny(unused_mut)]
+#![deny(dead_code)]
+#![deny(unused_imports)]
+#![deny(missing_docs)]
+
+use sigma_tree::chain;
+
+mod utils;
+
+use wasm_bindgen::prelude::*;
+
+/**
+ * An address is a short string corresponding to some script used to protect a box. Unlike (string-encoded) binary
+ * representation of a script, an address has some useful characteristics:
+ *
+ * - Integrity of an address could be checked., as it is incorporating a checksum.
+ * - A prefix of address is showing network and an address type.
+ * - An address is using an encoding (namely, Base58) which is avoiding similarly l0Oking characters, friendly to
+ * double-clicking and line-breaking in emails.
+ *
+ *
+ *
+ * An address is encoding network type, address type, checksum, and enough information to watch for a particular scripts.
+ *
+ * Possible network types are:
+ * Mainnet - 0x00
+ * Testnet - 0x10
+ *
+ * For an address type, we form content bytes as follows:
+ *
+ * P2PK - serialized (compressed) public key
+ * P2SH - first 192 bits of the Blake2b256 hash of serialized script bytes
+ * P2S  - serialized script
+ *
+ * Address examples for testnet:
+ *
+ * 3   - P2PK (3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN)
+ * ?   - P2SH (rbcrmKEYduUvADj9Ts3dSVSG27h54pgrq5fPuwB)
+ * ?   - P2S (Ms7smJwLGbUAjuWQ)
+ *
+ * for mainnet:
+ *
+ * 9  - P2PK (9fRAWhdxEsTcdb8PhGNrZfwqa65zfkuYHAMmkQLcic1gdLSV5vA)
+ * ?  - P2SH (8UApt8czfFVuTgQmMwtsRBZ4nfWquNiSwCWUjMg)
+ * ?  - P2S (4MQyML64GnzMxZgm, BxKBaHkvrTvLZrDcZjcsxsF7aSsrN73ijeFZXtbj4CXZHHcvBtqSxQ)
+ *
+ *
+ * Prefix byte = network type + address type
+ *
+ * checksum = blake2b256(prefix byte ++ content bytes)
+ *
+ * address = prefix byte ++ content bytes ++ checksum
+ *
+ */
+#[wasm_bindgen]
+pub struct Address(Box<dyn chain::Address>);
+
+#[wasm_bindgen]
+impl Address {
+    /// Decode (base58) testnet address from string
+    pub fn from_testnet_str(s: &str) -> Result<Address, JsValue> {
+        chain::AddressEncoder::new(chain::NetworkPrefix::Testnet)
+            .parse_address_from_str(s)
+            .map(|a| Address(a))
+            .map_err(|e| JsValue::from_str(&format!("{}", e)))
+    }
+}
+
+/// Secret key for prover
+#[wasm_bindgen]
+pub struct SecretKey(chain::SecretKey);
+
+#[wasm_bindgen]
+impl SecretKey {
+    /// Decode from string
+    pub fn parse(_: &str) -> Result<SecretKey, JsValue> {
+        // not implemented, see https://github.com/ergoplatform/sigma-rust/issues/33
+        Ok(SecretKey(chain::SecretKey::random_dlog()))
+    }
+}
+
+/// Transaction inputs, array of ErgoBoxCandidate
+#[wasm_bindgen]
+pub struct TxInputs(Vec<chain::ErgoBoxCandidate>);
+
+#[wasm_bindgen]
+impl TxInputs {
+    /// parse ErgoBoxCandidate array from json
+    #[allow(clippy::boxed_local)]
+    pub fn from_boxes(_boxes: Box<[JsValue]>) -> TxInputs {
+        // box in boxes.into_iter() {
+        //     let _box: chain::ErgoBoxCandidate = jbox.into_serde().unwrap();
+        // }
+        TxInputs(vec![])
+    }
+}
+
+/// Transaction outputs, array of ErgoBoxCandidate
+#[wasm_bindgen]
+pub struct TxOutputs(Vec<chain::ErgoBoxCandidate>);
+
+#[wasm_bindgen]
+impl TxOutputs {
+    /// parse ErgoBoxCandidate array from json
+    #[allow(clippy::boxed_local)]
+    pub fn from_boxes(_boxes: Box<[JsValue]>) -> TxOutputs {
+        // box in boxes.into_iter() {
+        //     let _box: chain::ErgoBoxCandidate = jbox.into_serde().unwrap();
+        // }
+        TxOutputs(vec![])
+    }
+}
+
+/// Box (aka coin, or an unspent output) is a basic concept of a UTXO-based cryptocurrency.
+/// In Bitcoin, such an object is associated with some monetary value (arbitrary,
+/// but with predefined precision, so we use integer arithmetic to work with the value),
+/// and also a guarding script (aka proposition) to protect the box from unauthorized opening.
+///
+/// In other way, a box is a state element locked by some proposition (ErgoTree).
+///
+/// In Ergo, box is just a collection of registers, some with mandatory types and semantics,
+/// others could be used by applications in any way.
+/// We add additional fields in addition to amount and proposition~(which stored in the registers R0 and R1).
+/// Namely, register R2 contains additional tokens (a sequence of pairs (token identifier, value)).
+/// Register R3 contains height when block got included into the blockchain and also transaction
+/// identifier and box index in the transaction outputs.
+/// Registers R4-R9 are free for arbitrary usage.
+///
+/// A transaction is unsealing a box. As a box can not be open twice, any further valid transaction
+/// can not be linked to the same box.
+#[wasm_bindgen]
+pub struct ErgoBoxCandidate(chain::ErgoBoxCandidate);
+
+#[wasm_bindgen]
+impl ErgoBoxCandidate {
+    /// make a new box with:
+    /// `value` - amount of money associated with the box
+    /// `contract` - guarding contract([`Contract`]), which should be evaluated to true in order
+    /// to open(spend) this box
+    /// `creation_height` - height when a transaction containing the box is created.
+    /// It should not exceed height of the block, containing the transaction with this box.
+    #[wasm_bindgen(constructor)]
+    pub fn new(value: u32, creation_height: u32, contract: Contract) -> ErgoBoxCandidate {
+        // value is u32, because u64 makes in BigInt in JS
+        let ergo_tree = contract.0.get_ergo_tree();
+        let b = chain::ErgoBoxCandidate::new(value as u64, ergo_tree, creation_height);
+        ErgoBoxCandidate(b)
+    }
+
+    /// JSON representation
+    pub fn to_json(&self) -> Result<JsValue, JsValue> {
+        JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("{}", e)))
+    }
+}
+
+/// Defines the contract(script) that will be guarding box contents
+#[wasm_bindgen]
+pub struct Contract(chain::Contract);
+
+#[wasm_bindgen]
+impl Contract {
+    /// create new contract that allow spending of the guarded box by a given recipient ([`Address`])
+    pub fn pay_to_address(recipient: Address) -> Contract {
+        Contract(chain::Contract::pay_to_address(&*recipient.0))
+    }
+}
+
+/**
+ * ErgoTransaction is an atomic state transition operation. It destroys Boxes from the state
+ * and creates new ones. If transaction is spending boxes protected by some non-trivial scripts,
+ * its inputs should also contain proof of spending correctness - context extension (user-defined
+ * key-value map) and data inputs (links to existing boxes in the state) that may be used during
+ * script reduction to crypto, signatures that satisfies the remaining cryptographic protection
+ * of the script.
+ * Transactions are not encrypted, so it is possible to browse and view every transaction ever
+ * collected into a block.
+ */
+#[wasm_bindgen]
+pub struct Transaction(chain::Transaction);
+
+#[wasm_bindgen]
+impl Transaction {
+    /// JSON representation
+    pub fn to_json(&self) -> Result<JsValue, JsValue> {
+        JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("{}", e)))
+    }
+}
+
+/// Create a signed transaction from:
+/// `inputs` - boxes [`ErgoBoxCandidate`] that will be spent
+/// `outputs` - boxes that will be created in this transaction
+/// `send_change_to` - address for the change (total value of input - total value of outputs)
+/// that will be put in a new box that will be added to `outputs`
+/// `sk` - secret key to sign the transaction (make proofs for inputs)
+#[wasm_bindgen]
+pub fn new_signed_transaction(
+    _inputs: TxInputs,
+    _outputs: TxOutputs,
+    _send_change_to: Address,
+    _sk: SecretKey,
+) -> Result<Transaction, JsValue> {
+    // not implemented, see https://github.com/ergoplatform/sigma-rust/issues/34
+    Err(JsValue::from_str("Error!"))
+}
diff --git a/sigma-chain/src/utils.rs b/sigma-wasm/src/utils.rs
similarity index 100%
rename from sigma-chain/src/utils.rs
rename to sigma-wasm/src/utils.rs
diff --git a/sigma-wasm/webpack.config.js b/sigma-wasm/webpack.config.js
new file mode 100644
index 000000000..24981e1dd
--- /dev/null
+++ b/sigma-wasm/webpack.config.js
@@ -0,0 +1,30 @@
+const path = require('path');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const webpack = require('webpack');
+
+module.exports = {
+  entry: './tests/index.js',
+  output: {
+    path: path.resolve(__dirname, 'dist'),
+    filename: 'index.js'
+  },
+  plugins: [
+    new HtmlWebpackPlugin(),
+    // Have this example work in Edge which doesn't ship `TextEncoder` or
+    // `TextDecoder` at this time.
+    new webpack.ProvidePlugin({
+      TextDecoder: ['text-encoder', 'TextDecoder'],
+      TextEncoder: ['text-encoder', 'TextEncoder']
+    })
+  ],
+  mode: 'development',
+  module: {
+    rules: [
+      {
+        test: /test.*\.js$/,
+        use: 'mocha-loader',
+        exclude: /node_modules/
+      }
+    ]
+  }
+};


Test patch:
diff --git a/sigma-chain/proptest-regressions/ergo_box.txt b/sigma-chain/proptest-regressions/ergo_box.txt
deleted file mode 100644
index d53737fd9..000000000
--- a/sigma-chain/proptest-regressions/ergo_box.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-# Seeds for failure cases proptest has generated in the past. It is
-# automatically read and these particular cases re-run before any
-# novel cases are generated.
-#
-# It is recommended to check this file in to source control so that
-# everyone who runs the test benefits from these saved cases.
-cc 6ab5726e2638ed62819c7c9523dab44614ecda53825bbb4e196441d09aff8fd8 # shrinks to v = ErgoBoxCandidate { value: 0, ergo_tree: ErgoTree, tokens: [], additional_registers: {}, creation_height: 0 }
diff --git a/sigma-chain/proptest-regressions/transaction.txt b/sigma-chain/proptest-regressions/transaction.txt
deleted file mode 100644
index 83acd8d5f..000000000
--- a/sigma-chain/proptest-regressions/transaction.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-# Seeds for failure cases proptest has generated in the past. It is
-# automatically read and these particular cases re-run before any
-# novel cases are generated.
-#
-# It is recommended to check this file in to source control so that
-# everyone who runs the test benefits from these saved cases.
-cc 2db82b148cdc5d9b0f0c044ab7391c8e920355dd92e94b14e3b998a054544e2b # shrinks to v = Transaction { inputs: [], data_inputs: [], outputs: [] }
diff --git a/sigma-chain/tests/web.rs b/sigma-chain/tests/web.rs
deleted file mode 100644
index f3894ff4b..000000000
--- a/sigma-chain/tests/web.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-//! Test suite for the Web and headless browsers.
-
-#![cfg(target_arch = "wasm32")]
-
-extern crate sigma_chain;
-extern crate wasm_bindgen_test;
-use wasm_bindgen_test::*;
-
-wasm_bindgen_test_configure!(run_in_browser);
-
-#[wasm_bindgen_test]
-fn test_add() {
-    assert_eq!(sigma_chain::add(1, 1), 2);
-}
diff --git a/sigma-testutil/Cargo.toml b/sigma-testutil/Cargo.toml
index 26067ce60..0874ae360 100644
--- a/sigma-testutil/Cargo.toml
+++ b/sigma-testutil/Cargo.toml
@@ -12,7 +12,7 @@ sigma-tree = { path = "../sigma-tree" }
 
 [dependencies.proptest]
 # wasm support, via https://altsysrq.github.io/proptest-book/proptest/wasm.html
-version = "0.9"
+version = "0.10"
 # The default feature set includes things like process forking which are not
 # supported in Web Assembly.
 default-features = false
@@ -21,7 +21,7 @@ features = ["std"]
 
 [dev-dependencies]
 wasm-bindgen-test = "0.3.10"
-proptest-derive = "0.1.2"
+proptest-derive = "0.2"
 
 [profile.release]
 # Tell `rustc` to optimize for small code size.
diff --git a/sigma-testutil/src/generator.rs b/sigma-testutil/src/generator.rs
index 7ece9e1a7..3add5aee7 100644
--- a/sigma-testutil/src/generator.rs
+++ b/sigma-testutil/src/generator.rs
@@ -1,4 +1,4 @@
-use sigma_tree::ergo_tree::ErgoTree;
+use sigma_tree::ErgoTree;
 
 #[derive(Debug)]
 pub struct ErgoTreeArb(pub ErgoTree);
diff --git a/sigma-wasm/tests/index.js b/sigma-wasm/tests/index.js
new file mode 100644
index 000000000..282850fa6
--- /dev/null
+++ b/sigma-wasm/tests/index.js
@@ -0,0 +1,2 @@
+// eslint-disable-next-line no-unused-vars
+import test_transaction from './test_transaction';
diff --git a/sigma-wasm/tests/test_transaction.js b/sigma-wasm/tests/test_transaction.js
new file mode 100644
index 000000000..3b3f3a590
--- /dev/null
+++ b/sigma-wasm/tests/test_transaction.js
@@ -0,0 +1,22 @@
+import { expect } from 'chai';
+
+import {TxInputs, SecretKey, ErgoBoxCandidate, Contract, TxOutputs} from '../pkg/sigma_tree_wasm';
+
+const sigma_rust = import('../pkg/sigma_tree_wasm');
+
+it('new transaction', async () => {
+  const {
+    Address,
+    new_signed_transaction,
+  } = await sigma_rust;
+
+  const recipient = Address.from_testnet_str('test');
+  const tx_inputs = TxInputs.from_boxes([]);
+  const send_change_to = Address.from_testnet_str('');
+  const sk = SecretKey.parse('');
+
+  let outbox = new ErgoBoxCandidate(1, 0, Contract.pay_to_address(recipient));
+  let tx_outputs = TxOutputs.from_boxes([outbox]);
+  expect(() => new_signed_transaction(tx_inputs, tx_outputs, send_change_to, sk)).to.throw();
+});
+
diff --git a/sigma-wasm/tests/transaction.rs b/sigma-wasm/tests/transaction.rs
new file mode 100644
index 000000000..5a409d991
--- /dev/null
+++ b/sigma-wasm/tests/transaction.rs
@@ -0,0 +1,22 @@
+//! Test suite for the Web and headless browsers.
+
+extern crate sigma_tree_wasm;
+extern crate wasm_bindgen_test;
+use sigma_tree_wasm::*;
+use wasm_bindgen_test::*;
+
+wasm_bindgen_test_configure!(run_in_browser);
+
+#[wasm_bindgen_test]
+fn test_signed_p2pk_transaction() {
+    let tx_inputs = TxInputs::from_boxes(Box::new([]));
+    let send_change_to = Address::from_testnet_str("").expect("failed");
+    let sk = SecretKey::parse("").expect("failed");
+    let recipient = Address::from_testnet_str("").expect("failed");
+
+    let outbox = ErgoBoxCandidate::new(1, 0, Contract::pay_to_address(recipient));
+    let out_boxes = [outbox.to_json().expect("failed")];
+    let tx_outputs = TxOutputs::from_boxes(Box::new(out_boxes));
+    let res = new_signed_transaction(tx_inputs, tx_outputs, send_change_to, sk);
+    assert!(res.is_err());
+}