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

Issue:
Add `ErgoBoxCandidate` to `ErgoBox` conversion in WASM
The use case is to be able to use outputs of the signed tx as inputs in the new tx.


Discussion / hints:

Fix patch:
diff --git a/bindings/ergo-lib-wasm/src/block_header.rs b/bindings/ergo-lib-wasm/src/block_header.rs
index 94bdc4774..019fddaa9 100644
--- a/bindings/ergo-lib-wasm/src/block_header.rs
+++ b/bindings/ergo-lib-wasm/src/block_header.rs
@@ -29,8 +29,8 @@ pub struct BlockHeaders(Vec<BlockHeader>);
 impl BlockHeaders {
     /// parse BlockHeader array from JSON (Node API)
     #[allow(clippy::boxed_local, clippy::or_fun_call)]
-    pub fn from_json(boxes: Box<[JsValue]>) -> Result<BlockHeaders, JsValue> {
-        boxes
+    pub fn from_json(json_vals: Box<[JsValue]>) -> Result<BlockHeaders, JsValue> {
+        json_vals
             .iter()
             .try_fold(vec![], |mut acc, jb| {
                 let b: chain::block_header::BlockHeader = if jb.is_string() {
diff --git a/bindings/ergo-lib-wasm/src/box_coll.rs b/bindings/ergo-lib-wasm/src/box_coll.rs
index 61b38b317..b72fa61ee 100644
--- a/bindings/ergo-lib-wasm/src/box_coll.rs
+++ b/bindings/ergo-lib-wasm/src/box_coll.rs
@@ -13,8 +13,8 @@ pub struct ErgoBoxes(Vec<ErgoBox>);
 impl ErgoBoxes {
     /// parse ErgoBox array from json
     #[allow(clippy::boxed_local, clippy::or_fun_call)]
-    pub fn from_boxes_json(boxes: Box<[JsValue]>) -> Result<ErgoBoxes, JsValue> {
-        boxes
+    pub fn from_boxes_json(json_vals: Box<[JsValue]>) -> Result<ErgoBoxes, JsValue> {
+        json_vals
             .iter()
             .try_fold(vec![], |mut acc, jb| {
                 let b: chain::ergo_box::ErgoBox = if jb.is_string() {
diff --git a/bindings/ergo-lib-wasm/src/transaction.rs b/bindings/ergo-lib-wasm/src/transaction.rs
index daf8444be..1a498e44f 100644
--- a/bindings/ergo-lib-wasm/src/transaction.rs
+++ b/bindings/ergo-lib-wasm/src/transaction.rs
@@ -1,6 +1,7 @@
 //! Ergo transaction
 
 use crate::box_coll::ErgoBoxCandidates;
+use crate::box_coll::ErgoBoxes;
 use crate::data_input::DataInputs;
 use crate::input::{Inputs, UnsignedInputs};
 use ergo_lib::chain;
@@ -83,10 +84,15 @@ impl Transaction {
         self.0.data_inputs.clone().into()
     }
 
-    /// Outputs for transaction
-    pub fn outputs(&self) -> ErgoBoxCandidates {
+    /// Output candidates for transaction
+    pub fn output_candidates(&self) -> ErgoBoxCandidates {
         self.0.output_candidates.clone().into()
     }
+
+    /// Returns ErgoBox's created from ErgoBoxCandidate's with tx id and indices
+    pub fn outputs(&self) -> ErgoBoxes {
+        self.0.outputs().into()
+    }
 }
 
 impl From<chain::transaction::Transaction> for Transaction {
@@ -117,8 +123,8 @@ impl UnsignedTransaction {
         self.0.data_inputs.clone().into()
     }
 
-    /// Outputs for transaction
-    pub fn outputs(&self) -> ErgoBoxCandidates {
+    /// Output candidates for transaction
+    pub fn output_candidates(&self) -> ErgoBoxCandidates {
         self.0.output_candidates.clone().into()
     }
 
diff --git a/ergo-lib/CHANGELOG.md b/ergo-lib/CHANGELOG.md
index 2ed68496b..c8fae9b70 100644
--- a/ergo-lib/CHANGELOG.md
+++ b/ergo-lib/CHANGELOG.md
@@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 
 <!-- next-header -->
 ## [Unreleased] - ReleaseDate
+
+### Added 
+- add `Transaction::outputs()` returning `ErgoBoxes`;
+
+### Changed 
+- rename  `Transaction::outputs()` and `UnsignedTransaction::outputs()` to `output_candidates()` in WASM (both return `ErgoBoxCandidates`);
+
+
 ## [0.11.0] - 2021-05-19
 
 ### Added 
diff --git a/ergo-lib/src/chain/transaction.rs b/ergo-lib/src/chain/transaction.rs
index 71b48fb13..a996d5d70 100644
--- a/ergo-lib/src/chain/transaction.rs
+++ b/ergo-lib/src/chain/transaction.rs
@@ -116,7 +116,7 @@ impl Transaction {
         }
     }
 
-    /// create ErgoBox from ErgoBoxCandidate with tx id and indices
+    /// Returns ErgoBox's created from ErgoBoxCandidate's with tx id and indices
     pub fn outputs(&self) -> Vec<ErgoBox> {
         assert!(self.output_candidates.len() < u16::MAX as usize);
         self.output_candidates


Test patch:
diff --git a/bindings/ergo-lib-wasm/tests/test_transaction.js b/bindings/ergo-lib-wasm/tests/test_transaction.js
index 5da07af51..fcf552fc1 100644
--- a/bindings/ergo-lib-wasm/tests/test_transaction.js
+++ b/bindings/ergo-lib-wasm/tests/test_transaction.js
@@ -174,3 +174,38 @@ it('TxBuilder burn token test', async () => {
   const tx = tx_builder.build();
   assert(tx != null);
 });
+
+it('use signed tx outputs as inputs in a new tx', async () => {
+  const sk = SecretKey.random_dlog();
+  // simulate existing box guarded by the sk key
+  const input_contract = Contract.pay_to_address(sk.get_address());
+  const input_box = new ErgoBox(BoxValue.from_i64(I64.from_str('100000000000')), 0, input_contract, TxId.zero(), 0, new Tokens());
+  // create a transaction that spends the "simulated" box
+  const recipient = Address.from_testnet_str('3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN');
+  const unspent_boxes = new ErgoBoxes(input_box);
+  const contract = Contract.pay_to_address(recipient);
+  const outbox_value = BoxValue.from_i64(I64.from_str('10000000000'));
+  const outbox = new ErgoBoxCandidateBuilder(outbox_value, contract, 0).build();
+  const tx_outputs = new ErgoBoxCandidates(outbox);
+  const fee = TxBuilder.SUGGESTED_TX_FEE();
+  const change_address = Address.from_testnet_str('3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN');
+  const min_change_value = BoxValue.SAFE_USER_MIN();
+  const box_selector = new SimpleBoxSelector();
+  const target_balance = BoxValue.from_i64(outbox_value.as_i64().checked_add(fee.as_i64()));
+  const box_selection = box_selector.select(unspent_boxes, target_balance, new Tokens());
+  const tx_builder = TxBuilder.new(box_selection, tx_outputs, 0, fee, change_address, min_change_value);
+  const tx = tx_builder.build();
+  const tx_data_inputs = ErgoBoxes.from_boxes_json([]);
+  const pre_header = PreHeader.from_block_header(block_headers.get(0));
+  const ctx = new ErgoStateContext(pre_header);
+  const sks = new SecretKeys();
+  sks.add(sk);
+  const wallet = Wallet.from_secrets(sks);
+  const signed_tx = wallet.sign_transaction(ctx, tx, unspent_boxes, tx_data_inputs);
+  assert(signed_tx != null);
+  // new tx
+  const new_box_selection = box_selector.select(signed_tx.outputs(), BoxValue.SAFE_USER_MIN(), new Tokens());
+  const new_tx_builder = TxBuilder.new(new_box_selection, tx_outputs, 0, fee, change_address, min_change_value);
+  const new_tx = tx_builder.build();
+  assert(new_tx != null);
+});