Skip to content

Commit

Permalink
Merge 36e3783 into 1dfecef
Browse files Browse the repository at this point in the history
  • Loading branch information
greenhat committed Oct 9, 2020
2 parents 1dfecef + 36e3783 commit 74e2519
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -50,7 +50,7 @@ matrix:
- cargo clean
- cargo build
after_success:
- cargo tarpaulin --ciserver travis-ci --coveralls $TRAVIS_JOB_ID
- cargo tarpaulin --ciserver travis-ci --coveralls $TRAVIS_JOB_ID --exclude-files bindings/*

- name: JS tests
dist: bionic
Expand Down
43 changes: 43 additions & 0 deletions bindings/ergo-lib-wasm/src/data_input.rs
@@ -0,0 +1,43 @@
//! DataInput type

use ergo_lib::chain;
use wasm_bindgen::prelude::*;

/// Inputs, that are used to enrich script context, but won't be spent by the transaction
#[wasm_bindgen]
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct DataInput(chain::data_input::DataInput);

/// DataInput collection
#[wasm_bindgen]
pub struct DataInputs(Vec<DataInput>);

#[wasm_bindgen]
impl DataInputs {
/// Create empty DataInputs
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
DataInputs(vec![])
}

/// Returns the number of elements in the collection
pub fn len(&self) -> usize {
self.0.len()
}

/// Returns the element of the collection with a given index
pub fn get(&self, index: usize) -> DataInput {
self.0[index].clone()
}

/// Adds an elements to the collection
pub fn add(&mut self, elem: &DataInput) {
self.0.push(elem.clone());
}
}

impl From<&DataInputs> for Vec<chain::data_input::DataInput> {
fn from(v: &DataInputs) -> Self {
v.0.clone().iter().map(|i| i.0.clone()).collect()
}
}
8 changes: 4 additions & 4 deletions bindings/ergo-lib-wasm/src/ergo_box/box_builder.rs
Expand Up @@ -29,8 +29,8 @@ impl ErgoBoxCandidateBuilder {
}

/// Set minimal value (per byte of the serialized box size)
pub fn set_min_box_value_per_byte(&mut self, new_min_value_per_byte: u32) {
self.0.set_min_box_value_per_byte(new_min_value_per_byte);
pub fn set_min_box_value_per_byte(self, new_min_value_per_byte: u32) -> Self {
ErgoBoxCandidateBuilder(self.0.set_min_box_value_per_byte(new_min_value_per_byte))
}

/// Get minimal value (per byte of the serialized box size)
Expand All @@ -39,8 +39,8 @@ impl ErgoBoxCandidateBuilder {
}

/// Set new box value
pub fn set_value(&mut self, new_value: BoxValue) {
self.0.set_value(new_value.into());
pub fn set_value(self, new_value: BoxValue) -> Self {
ErgoBoxCandidateBuilder(self.0.set_value(new_value.into()))
}

/// Get box value
Expand Down
4 changes: 4 additions & 0 deletions bindings/ergo-lib-wasm/src/lib.rs
Expand Up @@ -10,12 +10,16 @@
#![deny(unused_imports)]
#![deny(missing_docs)]
#![allow(unused_variables)]
// Clippy warnings
#![allow(clippy::new_without_default)]
#![allow(clippy::len_without_is_empty)]

pub mod address;
pub mod ast;
pub mod box_coll;
pub mod box_selector;
pub mod contract;
pub mod data_input;
pub mod ergo_box;
pub mod ergo_state_ctx;
pub mod secret_key;
Expand Down
20 changes: 12 additions & 8 deletions bindings/ergo-lib-wasm/src/tx_builder.rs
@@ -1,7 +1,8 @@
//! Unsigned transaction builder
use ergo_lib::{chain::ergo_box::ErgoBox, wallet};
use ergo_lib::{chain, wallet};
use wasm_bindgen::prelude::*;

use crate::data_input::DataInputs;
use crate::{
address::Address,
box_coll::{ErgoBoxCandidates, ErgoBoxes},
Expand All @@ -12,7 +13,7 @@ use crate::{

/// Unsigned transaction builder
#[wasm_bindgen]
pub struct TxBuilder(wallet::tx_builder::TxBuilder<ErgoBox>);
pub struct TxBuilder(wallet::tx_builder::TxBuilder<chain::ergo_box::ErgoBox>);

#[wasm_bindgen]
impl TxBuilder {
Expand All @@ -33,18 +34,21 @@ impl TxBuilder {
fee_amount: &BoxValue,
change_address: &Address,
min_change_value: &BoxValue,
) -> Result<TxBuilder, JsValue> {
ergo_lib::wallet::tx_builder::TxBuilder::new(
box_selector.inner::<ErgoBox>(),
) -> TxBuilder {
TxBuilder(ergo_lib::wallet::tx_builder::TxBuilder::new(
box_selector.inner::<chain::ergo_box::ErgoBox>(),
inputs.clone().into(),
output_candidates.clone().into(),
current_height,
fee_amount.clone().into(),
change_address.clone().into(),
min_change_value.clone().into(),
)
.map_err(|e| JsValue::from_str(&format!("{}", e)))
.map(TxBuilder)
))
}

/// Set transaction's data inputs
pub fn set_data_inputs(self, data_inputs: &DataInputs) -> TxBuilder {
TxBuilder(self.0.set_data_inputs(data_inputs.into()))
}

/// Build the unsigned transaction
Expand Down
6 changes: 4 additions & 2 deletions bindings/ergo-lib-wasm/tests/test_transaction.js
Expand Up @@ -3,7 +3,7 @@ import { expect, assert } from 'chai';
import {
Address, Wallet, ErgoBox, ErgoBoxCandidateBuilder, Contract,
ErgoBoxes, ErgoBoxCandidates,
ErgoStateContext, TxBuilder, BoxValue, UnsignedTransaction, BoxSelector, SecretKey, TxId,
ErgoStateContext, TxBuilder, BoxValue, UnsignedTransaction, BoxSelector, SecretKey, TxId, DataInputs
} from '../pkg/ergo_lib_wasm';

it('TxBuilder test', async () => {
Expand All @@ -26,7 +26,9 @@ it('TxBuilder test', async () => {
const fee = BoxValue.from_u32(1000000);
const change_address = Address.from_testnet_str('3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN');
const min_change_value = BoxValue.SAFE_USER_MIN();
const tx_builder = TxBuilder.new(BoxSelector.SelectAll, unspent_boxes, tx_outputs, 0, fee, change_address, min_change_value);
const data_inputs = new DataInputs();
const tx_builder = TxBuilder.new(BoxSelector.SelectAll, unspent_boxes, tx_outputs, 0, fee, change_address, min_change_value)
.set_data_inputs(data_inputs);
const tx = tx_builder.build();
assert(tx != null);
});
Expand Down
6 changes: 2 additions & 4 deletions bindings/ergo-lib-wasm/tests/transaction.rs
Expand Up @@ -60,8 +60,7 @@ fn test_sign_transaction() {
&fee,
&change_address,
&min_change_value,
)
.unwrap();
);
let tx = tx_builder.build().unwrap();
let wallet = Wallet::from_secret(&sk);
let dummy_ctx = ErgoStateContext::dummy();
Expand Down Expand Up @@ -117,7 +116,6 @@ fn test_tx_builder() {
&fee,
&change_address,
&min_change_value,
)
.unwrap();
);
let _tx = tx_builder.build().unwrap();
}
2 changes: 1 addition & 1 deletion ergo-lib/src/chain/data_input.rs
Expand Up @@ -15,7 +15,7 @@ use proptest_derive::Arbitrary;
use serde::{Deserialize, Serialize};

/// Inputs, that are used to enrich script context, but won't be spent by the transaction
#[derive(PartialEq, Debug, Clone)]
#[derive(PartialEq, Eq, Debug, Clone)]
#[cfg_attr(test, derive(Arbitrary))]
#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
pub struct DataInput {
Expand Down
12 changes: 7 additions & 5 deletions ergo-lib/src/chain/ergo_box/box_builder.rs
Expand Up @@ -52,8 +52,9 @@ impl ErgoBoxCandidateBuilder {
}

/// Set minimal value (per byte of the serialized box size)
pub fn set_min_box_value_per_byte(&mut self, new_min_value_per_byte: u32) {
pub fn set_min_box_value_per_byte(mut self, new_min_value_per_byte: u32) -> Self {
self.min_value_per_byte = new_min_value_per_byte;
self
}

/// Get minimal value (per byte of the serialized box size)
Expand All @@ -62,8 +63,9 @@ impl ErgoBoxCandidateBuilder {
}

/// Set new box value
pub fn set_value(&mut self, new_value: BoxValue) {
pub fn set_value(mut self, new_value: BoxValue) -> Self {
self.value = new_value;
self
}

/// Get box value
Expand Down Expand Up @@ -134,10 +136,10 @@ mod tests {

#[test]
fn test_set_value() {
let mut builder =
ErgoBoxCandidateBuilder::new(BoxValue::SAFE_USER_MIN, force_any_val::<ErgoTree>(), 1);
let new_value = BoxValue::SAFE_USER_MIN.checked_mul_u32(10).unwrap();
builder.set_value(new_value);
let builder =
ErgoBoxCandidateBuilder::new(BoxValue::SAFE_USER_MIN, force_any_val::<ErgoTree>(), 1)
.set_value(new_value);
assert_eq!(builder.value(), &new_value);
let b = builder.build().unwrap();
assert_eq!(b.value, new_value);
Expand Down
48 changes: 30 additions & 18 deletions ergo-lib/src/wallet/tx_builder.rs
Expand Up @@ -7,6 +7,7 @@ use crate::chain::address::Address;
use crate::chain::address::AddressEncoder;
use crate::chain::address::NetworkPrefix;
use crate::chain::contract::Contract;
use crate::chain::data_input::DataInput;
use crate::chain::ergo_box::box_builder::ErgoBoxCandidateBuilder;
use crate::chain::ergo_box::box_builder::ErgoBoxCandidateBuilderError;
use crate::chain::ergo_box::box_value;
Expand All @@ -26,6 +27,7 @@ use super::box_selector::{BoxSelection, BoxSelector, BoxSelectorError};
pub struct TxBuilder<S: ErgoBoxAssets> {
box_selector: Box<dyn BoxSelector<S>>,
boxes_to_spend: Vec<S>,
data_inputs: Vec<DataInput>,
output_candidates: Vec<ErgoBoxCandidate>,
current_height: u32,
fee_amount: BoxValue,
Expand All @@ -51,30 +53,37 @@ impl<S: ErgoBoxAssets + ErgoBoxId + Clone> TxBuilder<S> {
fee_amount: BoxValue,
change_address: Address,
min_change_value: BoxValue,
) -> Result<TxBuilder<S>, TxBuilderError> {
if boxes_to_spend.is_empty() {
return Err(TxBuilderError::InvalidArgs(
"boxes_to_spend is empty".to_string(),
));
}
if output_candidates.is_empty() {
return Err(TxBuilderError::InvalidArgs(
"output_candidates is empty".to_string(),
));
}
Ok(TxBuilder {
) -> TxBuilder<S> {
TxBuilder {
box_selector,
boxes_to_spend,
data_inputs: vec![],
output_candidates,
current_height,
fee_amount,
change_address,
min_change_value,
})
}
}

/// Set transaction's data inputs
pub fn set_data_inputs(mut self, data_inputs: Vec<DataInput>) -> Self {
self.data_inputs = data_inputs;
self
}

/// Build the unsigned transaction
pub fn build(self) -> Result<UnsignedTransaction, TxBuilderError> {
if self.boxes_to_spend.is_empty() {
return Err(TxBuilderError::InvalidArgs(
"boxes_to_spend is empty".to_string(),
));
}
if self.output_candidates.is_empty() {
return Err(TxBuilderError::InvalidArgs(
"output_candidates is empty".to_string(),
));
}
let total_output_value: BoxValue =
box_value::checked_sum(self.output_candidates.iter().map(|b| b.value))?
.checked_add(&self.fee_amount)?;
Expand Down Expand Up @@ -109,7 +118,7 @@ impl<S: ErgoBoxAssets + ErgoBoxId + Clone> TxBuilder<S> {
.into_iter()
.map(UnsignedInput::from)
.collect(),
vec![],
self.data_inputs,
output_candidates,
))
}
Expand Down Expand Up @@ -195,7 +204,7 @@ mod tests {
force_any_val::<Address>(),
BoxValue::SAFE_USER_MIN,
);
assert!(r.is_err());
assert!(r.build().is_err());
}

#[test]
Expand All @@ -210,7 +219,7 @@ mod tests {
force_any_val::<Address>(),
BoxValue::SAFE_USER_MIN,
);
assert!(r.is_err());
assert!(r.build().is_err());
}

proptest! {
Expand All @@ -219,7 +228,8 @@ mod tests {
fn test_build_tx(inputs in vec(any_with::<ErgoBox>((BoxValue::MIN_RAW * 5000 .. BoxValue::MIN_RAW * 10000).into()), 1..10),
outputs in vec(any_with::<ErgoBoxCandidate>((BoxValue::MIN_RAW * 1000 ..BoxValue::MIN_RAW * 2000).into()), 1..2),
change_address in any::<Address>(),
miners_fee in any_with::<BoxValue>((BoxValue::MIN_RAW * 100..BoxValue::MIN_RAW * 200).into())) {
miners_fee in any_with::<BoxValue>((BoxValue::MIN_RAW * 100..BoxValue::MIN_RAW * 200).into()),
data_inputs in vec(any::<DataInput>(), 0..2)) {
let min_change_value = BoxValue::SAFE_USER_MIN;

let all_outputs = box_value::checked_sum(outputs.iter().map(|b| b.value)).unwrap()
Expand All @@ -237,7 +247,8 @@ mod tests {
miners_fee,
change_address.clone(),
min_change_value,
).unwrap();
)
.set_data_inputs(data_inputs.clone());
let tx = tx_builder.build().unwrap();
prop_assert!(outputs.into_iter().all(|i| tx.output_candidates.iter().any(|o| *o == i)),
"tx.output_candidates is missing some outputs");
Expand All @@ -253,6 +264,7 @@ mod tests {
prop_assert!(tx.output_candidates.iter().any(|b| {
b.value == miners_fee
}), "box with miner's fee {:?} is not found in outputs: {:?}", miners_fee, tx.output_candidates);
prop_assert_eq!(tx.data_inputs, data_inputs, "unexpected data inputs");
}
}
}

0 comments on commit 74e2519

Please sign in to comment.