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

Issue:
Distributed signing: Spending box depends on another input box with multisig 
>box2 could spend according to it's contract when box 1 is signed successfully so in the partial sign steps there is no proofbytes generated for box2 and so in extract_hint function the function returns Prover error (tx input index 1): Unexpected: Unexpected Sigma tree Value

Box1:
```json
{
  "boxId": "2f4cd0df4db787875b3a071e098b72ba4923bd2460e08184b34359563febe047",
  "value": 10000000,
  "ergoTree": "100504000400040004000402d804d601b2a5730000d602e4c6a7041ad603e4c6a70510d604ad7202d901040ecdee7204ea02d19683020193c27201c2a7938cb2db63087201730100018cb2db6308a773020001eb02ea02d19683020193e4c67201041a720293e4c672010510720398b27203730300720498b272037304007204",
  "assets": [
    {
      "tokenId": "a6ac381e6fa99929fd1477b3ba9499790a775e91d4c14c5aa86e9a118dfac853",
      "amount": 1
    }
  ],
  "additionalRegisters": {
    "R4": "1a04210302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189210399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f8521024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b6621027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bd",
    "R5": "10020608"
  },
  "creationHeight": 247249,
  "transactionId": "65c7e0d4a77ccd605b3e4812d38140f7e68fdf740cb6cdc1d8957b75138d1e4c",
  "index": 0
}
```

Box2:
```json
{
  "boxId": "5e8269c8e2b975a43dc6e74a9c5b10b273313c6d32c1dd40c171fc0a8852ca01",
  "value": 10000000000,
  "ergoTree": "10010e20a6ac381e6fa99929fd1477b3ba9499790a775e91d4c14c5aa86e9a118dfac853d1aea4d9010163aedb63087201d901034d0e938c7203017300",
  "assets": [],
  "additionalRegisters": {},
  "creationHeight": 247249,
  "transactionId": "2eca6f88ea99fb4fe313d94cb05c576b1b7a94ec7166aec958b36bcea4b8ff1a",
  "index": 1
} 
```

Code:
```js
import * as wasm from 'ergo-lib-wasm-nodejs';


const publicKeys: Array<string> = [
    "0302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189",
    "0399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f85",
    "024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b66",
    "027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bd",
]

const secrets = [
    "00eda6c0e9fc808d4cf050fc4e98705372b9f0786a6b63aa4013d1a20539b104",
    "cc2e48e5e53059e0d68866eff97a6037cb39945ea9f09f40fcec82d12cd8cb8b",
    "c97250f41cfa8d545c2f8d75b2ee24002b5feec32340c2bb81fa4e2d4c7527d3",
    "53ceef0ece83401cf5cd853fd0c1a9bbfab750d76f278b3187f1a14768d6e9c4",
]

const firstSK=wasm.SecretKey.dlog_from_bytes(Uint8Array.from(Buffer.from("00eda6c0e9fc808d4cf050fc4e98705372b9f0786a6b63aa4013d1a20539b104","hex")));
const firstSKS=new wasm.SecretKeys();
firstSKS.add(firstSK);
const firstWallet=wasm.Wallet.from_secrets(firstSKS);

const secondSK=wasm.SecretKey.dlog_from_bytes(Uint8Array.from(Buffer.from("cc2e48e5e53059e0d68866eff97a6037cb39945ea9f09f40fcec82d12cd8cb8b","hex")));
const secondSKS=new wasm.SecretKeys();
secondSKS.add(secondSK);
const secondWallet=wasm.Wallet.from_secrets(secondSKS);

const thirdSK=wasm.SecretKey.dlog_from_bytes(Uint8Array.from(Buffer.from("c97250f41cfa8d545c2f8d75b2ee24002b5feec32340c2bb81fa4e2d4c7527d3","hex")));
const thirdSKS=new wasm.SecretKeys();
thirdSKS.add(thirdSK);
const thirdWallet=wasm.Wallet.from_secrets(thirdSKS);


const forthSK=wasm.SecretKey.dlog_from_bytes(Uint8Array.from(Buffer.from("53ceef0ece83401cf5cd853fd0c1a9bbfab750d76f278b3187f1a14768d6e9c4","hex")));
const forthSKS=new wasm.SecretKeys();
forthSKS.add(forthSK);
const forthWallet=wasm.Wallet.from_secrets(forthSKS);


const box1 = wasm.ErgoBox.sigma_parse_bytes(Uint8Array.from(Buffer.from("80ade204100504000400040004000402d804d601b2a5730000d602e4c6a7041ad603e4c6a70510d604ad7202d901040ecdee7204ea02d19683020193c27201c2a7938cb2db63087201730100018cb2db6308a773020001eb02ea02d19683020193e4c67201041a720293e4c672010510720398b27203730300720498b272037304007204d18b0f01a6ac381e6fa99929fd1477b3ba9499790a775e91d4c14c5aa86e9a118dfac85301021a04210302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189210399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f8521024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b6621027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bd1002060865c7e0d4a77ccd605b3e4812d38140f7e68fdf740cb6cdc1d8957b75138d1e4c00", "hex")))
const box2 = wasm.ErgoBox.sigma_parse_bytes(Uint8Array.from(Buffer.from("80c8afa02510010e20a6ac381e6fa99929fd1477b3ba9499790a775e91d4c14c5aa86e9a118dfac853d1aea4d9010163aedb63087201d901034d0e938c7203017300d18b0f00002eca6f88ea99fb4fe313d94cb05c576b1b7a94ec7166aec958b36bcea4b8ff1a01", "hex")))
console.log(box1.to_json())
console.log(box2.to_json())
const boxes = wasm.ErgoBoxes.empty()
boxes.add(box1)

boxes.add(box2)

const tempBoxes = wasm.ErgoBoxes.empty()
tempBoxes.add(box1)

const reduced = wasm.ReducedTransaction.sigma_parse_bytes(Uint8Array.from(Buffer.from("ce04022f4cd0df4db787875b3a071e098b72ba4923bd2460e08184b34359563febe04700005e8269c8e2b975a43dc6e74a9c5b10b273313c6d32c1dd40c171fc0a8852ca0100000001a6ac381e6fa99929fd1477b3ba9499790a775e91d4c14c5aa86e9a118dfac8530480ade204100504000400040004000402d804d601b2a5730000d602e4c6a7041ad603e4c6a70510d604ad7202d901040ecdee7204ea02d19683020193c27201c2a7938cb2db63087201730100018cb2db6308a773020001eb02ea02d19683020193e4c67201041a720293e4c672010510720398b27203730300720498b272037304007204d18b0f010001021a04210302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189210399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f8521024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b6621027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bd100206088094ebdc030008cd0314368e16c9c99c5a6e20dda917aeb826b3a908becff543b3a36b38e6b3355ff5d18b0f0000c0843d1005040004000e36100204a00b08cd0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ea02d192a39a8cc7a701730073011001020402d19683030193a38cc7b2a57300000193c2b2a57301007473027303830108cdeeac93b1a57304d18b0f0000c0af87c3210008cd0314368e16c9c99c5a6e20dda917aeb826b3a908becff543b3a36b38e6b3355ff5d18b0f00009702980304cd0302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189cd0399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f85cd024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b66cd027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bd9604cd0302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189cd0399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f85cd024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b66cd027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bdf39b03d3cb9e02d073", "hex")))

let firstHints = firstWallet.generate_commitments_for_reduced_transaction(reduced).all_hints_for_input(0);
const firstKnown = [firstHints.get(0)];
const firstOwn = [firstHints.get(1)];


let secondHints = secondWallet.generate_commitments_for_reduced_transaction(reduced).all_hints_for_input(0);
const secondKnown = [secondHints.get(0)];
const secondOwn = [secondHints.get(1)];


let thirdHints = thirdWallet.generate_commitments_for_reduced_transaction(reduced).all_hints_for_input(0);
const thirdKnown = [thirdHints.get(0)];
const thirdOwn = [thirdHints.get(1)];



let forthHints = forthWallet.generate_commitments_for_reduced_transaction(reduced).all_hints_for_input(0);
const forthKnown = [forthHints.get(0)];
const forthOwn = [forthHints.get(1)];


const firstTransactionHintsBagKnown=wasm.TransactionHintsBag.empty();
const firstHintsBag=wasm.HintsBag.empty();
firstHintsBag.add_commitment(firstKnown[0]);
firstTransactionHintsBagKnown.add_hints_for_input(0,firstHintsBag);


const secondTransactionHintsBagKnown=wasm.TransactionHintsBag.empty();
const secondHintsBag=wasm.HintsBag.empty();
secondHintsBag.add_commitment(secondKnown[0]);
secondTransactionHintsBagKnown.add_hints_for_input(0,secondHintsBag);


const thirdTransactionHintsBagKnown=wasm.TransactionHintsBag.empty();
const thirdHintsBag=wasm.HintsBag.empty();
thirdHintsBag.add_commitment(thirdKnown[0]);
thirdTransactionHintsBagKnown.add_hints_for_input(0,thirdHintsBag);


const forthTransactionHintsBagKnown=wasm.TransactionHintsBag.empty();
const forthHintsBag=wasm.HintsBag.empty();
forthHintsBag.add_commitment(forthKnown[0]);
forthTransactionHintsBagKnown.add_hints_for_input(0,forthHintsBag);


console.log(firstTransactionHintsBagKnown.to_json().publicHints['0'])
console.log(secondTransactionHintsBagKnown.to_json().publicHints['0'])
console.log(thirdTransactionHintsBagKnown.to_json().publicHints['0'])
console.log(forthTransactionHintsBagKnown.to_json().publicHints['0'])

const firstCommitment=JSON.stringify(firstTransactionHintsBagKnown.to_json())
const secondCommitment=JSON.stringify(secondTransactionHintsBagKnown.to_json())
const thirdCommitment=JSON.stringify(thirdTransactionHintsBagKnown.to_json())
const forthCommitment=JSON.stringify(forthTransactionHintsBagKnown.to_json())

const firstHintsBagFromJson=wasm.TransactionHintsBag.from_json(firstCommitment);
const secondHintsBagFromJson=wasm.TransactionHintsBag.from_json(secondCommitment);
const thirdHintsBagFromJson=wasm.TransactionHintsBag.from_json(thirdCommitment);
const forthHintsBagFromJson=wasm.TransactionHintsBag.from_json(forthCommitment);

const firstTxHintsBag=wasm.TransactionHintsBag.empty();


firstTxHintsBag.add_hints_for_input(0,secondHintsBagFromJson.all_hints_for_input(0));
firstTxHintsBag.add_hints_for_input(0,thirdHintsBagFromJson.all_hints_for_input(0));
firstTxHintsBag.add_hints_for_input(0,forthHintsBagFromJson.all_hints_for_input(0));


const secondTxHintsBag=wasm.TransactionHintsBag.empty();

secondTxHintsBag.add_hints_for_input(0,firstHintsBagFromJson.all_hints_for_input(0));
secondTxHintsBag.add_hints_for_input(0,thirdHintsBagFromJson.all_hints_for_input(0));
secondTxHintsBag.add_hints_for_input(0,forthHintsBagFromJson.all_hints_for_input(0));


const forthTxHintsBag=wasm.TransactionHintsBag.empty();
forthTxHintsBag.add_hints_for_input(0,firstHintsBagFromJson.all_hints_for_input(0));
forthTxHintsBag.add_hints_for_input(0,secondHintsBagFromJson.all_hints_for_input(0));
forthTxHintsBag.add_hints_for_input(0,thirdHintsBagFromJson.all_hints_for_input(0));



const forthSign=forthWallet.sign_reduced_transaction_multi(reduced,forthTxHintsBag);

const firstRealPropositions=new wasm.Propositions;
const firstSimulatedPropositions=new wasm.Propositions;

const firstPK = Uint8Array.from(Buffer.from("cd0302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189", "hex"));
const secondPK = Uint8Array.from(Buffer.from("cd0399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f85", "hex"));
const thirdPK = Uint8Array.from(Buffer.from("cd024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b66", "hex"));
const forthPK = Uint8Array.from(Buffer.from("cd027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bd", "hex"));
firstRealPropositions.add_proposition_from_byte(forthPK);

const secondHintsBagExtract=wasm.extract_hints(forthSign,ctx,boxes,wasm.ErgoBoxes.from_boxes_json([]),firstRealPropositions,firstSimulatedPropositions);

```


Discussion / hints:

Fix patch:
diff --git a/ergo-lib/src/wallet/multi_sig.rs b/ergo-lib/src/wallet/multi_sig.rs
index a7f69de4b..2e9cacda0 100644
--- a/ergo-lib/src/wallet/multi_sig.rs
+++ b/ergo-lib/src/wallet/multi_sig.rs
@@ -140,6 +140,9 @@ pub fn bag_for_multi_sig(
     simulated_propositions: &[SigmaBoolean],
     proof: &[u8],
 ) -> Result<HintsBag, SigParsingError> {
+    if let SigmaBoolean::TrivialProp(_) = sigma_tree {
+        return Ok(HintsBag::empty());
+    }
     let ut = compute_commitments(parse_sig_compute_challenges(&sigma_tree, proof.to_owned())?);
     // Traversing node of sigma tree
     fn traverse_node(
@@ -213,7 +216,7 @@ pub fn bag_for_multi_sig(
                         }
                     }
                     None => {
-                        return Err(SigParsingError::Unexpected("empty commitment"));
+                        return Err(SigParsingError::EmptyCommitment(leaf.proposition()));
                     }
                 };
             }
diff --git a/ergotree-interpreter/src/sigma_protocol/sig_serializer.rs b/ergotree-interpreter/src/sigma_protocol/sig_serializer.rs
index 354c1aaa1..f46be0e28 100644
--- a/ergotree-interpreter/src/sigma_protocol/sig_serializer.rs
+++ b/ergotree-interpreter/src/sigma_protocol/sig_serializer.rs
@@ -20,12 +20,10 @@ use ergotree_ir::serialization::sigma_byte_reader;
 use ergotree_ir::serialization::sigma_byte_reader::SigmaByteRead;
 use ergotree_ir::serialization::sigma_byte_writer::SigmaByteWrite;
 use ergotree_ir::serialization::sigma_byte_writer::SigmaByteWriter;
-use ergotree_ir::serialization::SigmaParsingError;
 use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean;
 use ergotree_ir::sigma_protocol::sigma_boolean::SigmaConjecture;
 use ergotree_ir::sigma_protocol::sigma_boolean::SigmaProofOfKnowledgeTree;
 
-use derive_more::From;
 use gf2_192::Gf2_192Error;
 use thiserror::Error;
 
@@ -118,8 +116,12 @@ pub fn parse_sig_compute_challenges(
     exp: &SigmaBoolean,
     mut proof_bytes: Vec<u8>,
 ) -> Result<UncheckedTree, SigParsingError> {
+    if proof_bytes.is_empty() {
+        return Err(SigParsingError::EmptyProof(exp.clone()));
+    }
     let mut r = sigma_byte_reader::from_bytes(proof_bytes.as_mut_slice());
     parse_sig_compute_challenges_reader(exp, &mut r, None)
+        .map_err(|e| SigParsingError::TopLevelExpWrap(e.into(), exp.clone()))
 }
 
 /// Verifier Step 2: In a top-down traversal of the tree, obtain the challenges for the children of every
@@ -135,18 +137,17 @@ fn parse_sig_compute_challenges_reader<R: SigmaByteRead>(
     let challenge = if let Some(c) = challenge_opt {
         c
     } else {
-        Challenge::sigma_parse(r)?
+        Challenge::sigma_parse(r).map_err(|_| SigParsingError::ChallengeRead(exp.clone()))?
     };
 
     match exp {
-        SigmaBoolean::TrivialProp(_) => Err(SigParsingError::Unexpected(
-            "parse_sig_compute_challenges: TrivialProp should be handled before this call",
-        )),
+        SigmaBoolean::TrivialProp(b) => Err(SigParsingError::TrivialPropFound(*b)),
         SigmaBoolean::ProofOfKnowledge(tree) => match tree {
             SigmaProofOfKnowledgeTree::ProveDlog(dl) => {
                 // Verifier Step 3: For every leaf node, read the response z provided in the proof.
                 let mut scalar_bytes: [u8; super::GROUP_SIZE] = [0; super::GROUP_SIZE];
-                r.read_exact(&mut scalar_bytes)?;
+                r.read_exact(&mut scalar_bytes)
+                    .map_err(|_| SigParsingError::ScalarReadProveDlog(exp.clone()))?;
                 let z = Wscalar::from(GroupSizedBytes(scalar_bytes.into()));
                 Ok(UncheckedSchnorr {
                     proposition: dl.clone(),
@@ -159,7 +160,8 @@ fn parse_sig_compute_challenges_reader<R: SigmaByteRead>(
             SigmaProofOfKnowledgeTree::ProveDhTuple(dh) => {
                 // Verifier Step 3: For every leaf node, read the response z provided in the proof.
                 let mut scalar_bytes: [u8; super::GROUP_SIZE] = [0; super::GROUP_SIZE];
-                r.read_exact(&mut scalar_bytes)?;
+                r.read_exact(&mut scalar_bytes)
+                    .map_err(|_| SigParsingError::ScalarReadProveDhTuple(exp.clone()))?;
                 let z = Wscalar::from(GroupSizedBytes(scalar_bytes.into()));
                 Ok(UncheckedDhTuple {
                     proposition: dh.clone(),
@@ -219,7 +221,8 @@ fn parse_sig_compute_challenges_reader<R: SigmaByteRead>(
                 let n_coeff = n_children - ct.k as usize;
                 let buf_size = n_coeff * SOUNDNESS_BYTES;
                 let mut coeff_bytes = vec![0u8; buf_size];
-                r.read_exact(&mut coeff_bytes)?;
+                r.read_exact(&mut coeff_bytes)
+                    .map_err(|_| SigParsingError::CthresholdCoeffRead(exp.clone()))?;
                 let polynomial = gf2_192poly_from_byte_array(challenge.clone(), coeff_bytes)?;
 
                 let children =
@@ -246,24 +249,33 @@ fn parse_sig_compute_challenges_reader<R: SigmaByteRead>(
 }
 
 /// Errors when parsing proof tree signatures
-#[derive(Error, PartialEq, Eq, Debug, Clone, From)]
+#[allow(missing_docs)]
+#[derive(Error, PartialEq, Debug, Clone)]
 pub enum SigParsingError {
-    /// `gf2_192` error
+    #[error("Empty proof for exp: {0:?}")]
+    EmptyProof(SigmaBoolean),
+
+    #[error("Unexpected TrivialProp found: {0}")]
+    TrivialPropFound(bool),
+
     #[error("gf2_192 error: {0}")]
-    Gf2_192Error(Gf2_192Error),
-    /// IO error
-    #[error("IO error: {0}")]
-    IoError(String),
-    /// Serialization error
-    #[error("Serialization error: {0}")]
-    SerializationError(SigmaParsingError),
-    /// Unexpected error
-    #[error("Unexpected error: {0}")]
-    Unexpected(&'static str),
-}
+    Gf2_192Error(#[from] Gf2_192Error),
 
-impl From<std::io::Error> for SigParsingError {
-    fn from(e: std::io::Error) -> Self {
-        SigParsingError::IoError(e.to_string())
-    }
+    #[error("Empty commitment in UncheckedLeaf with proposition: {0:?}")]
+    EmptyCommitment(SigmaBoolean),
+
+    #[error("Challenge reading erorr with exp: {0:?}")]
+    ChallengeRead(SigmaBoolean),
+
+    #[error("Scalar in ProveDlog reading erorr with exp: {0:?}")]
+    ScalarReadProveDlog(SigmaBoolean),
+
+    #[error("Scalar in ProveDhTumple reading erorr with exp: {0:?}")]
+    ScalarReadProveDhTuple(SigmaBoolean),
+
+    #[error("Cthreshold coeff reading erorr with exp: {0:?}")]
+    CthresholdCoeffRead(SigmaBoolean),
+
+    #[error("Error: {0:?} for top level exp: {1:?}")]
+    TopLevelExpWrap(Box<SigParsingError>, SigmaBoolean),
 }


Test patch:
diff --git a/bindings/ergo-lib-wasm/tests/test_i608.js b/bindings/ergo-lib-wasm/tests/test_i608.js
new file mode 100644
index 000000000..11a4b6531
--- /dev/null
+++ b/bindings/ergo-lib-wasm/tests/test_i608.js
@@ -0,0 +1,162 @@
+import { expect, assert } from 'chai';
+
+import { generate_block_headers } from './utils';
+
+import * as ergo from "..";
+let wasm;
+beforeEach(async () => {
+  wasm = await ergo;
+});
+
+const publicKeys = [
+  "0302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189",
+  "0399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f85",
+  "024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b66",
+  "027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bd",
+];
+
+const secrets = [
+  "00eda6c0e9fc808d4cf050fc4e98705372b9f0786a6b63aa4013d1a20539b104",
+  "cc2e48e5e53059e0d68866eff97a6037cb39945ea9f09f40fcec82d12cd8cb8b",
+  "c97250f41cfa8d545c2f8d75b2ee24002b5feec32340c2bb81fa4e2d4c7527d3",
+  "53ceef0ece83401cf5cd853fd0c1a9bbfab750d76f278b3187f1a14768d6e9c4",
+];
+
+it('i608', async () => {
+
+  const firstSK = wasm.SecretKey.dlog_from_bytes(Uint8Array.from(Buffer.from("00eda6c0e9fc808d4cf050fc4e98705372b9f0786a6b63aa4013d1a20539b104", "hex")));
+  const firstSKS = new wasm.SecretKeys();
+  firstSKS.add(firstSK);
+  const firstWallet = wasm.Wallet.from_secrets(firstSKS);
+
+  const secondSK = wasm.SecretKey.dlog_from_bytes(Uint8Array.from(Buffer.from("cc2e48e5e53059e0d68866eff97a6037cb39945ea9f09f40fcec82d12cd8cb8b", "hex")));
+  const secondSKS = new wasm.SecretKeys();
+  secondSKS.add(secondSK);
+  const secondWallet = wasm.Wallet.from_secrets(secondSKS);
+
+  const thirdSK = wasm.SecretKey.dlog_from_bytes(Uint8Array.from(Buffer.from("c97250f41cfa8d545c2f8d75b2ee24002b5feec32340c2bb81fa4e2d4c7527d3", "hex")));
+  const thirdSKS = new wasm.SecretKeys();
+  thirdSKS.add(thirdSK);
+  const thirdWallet = wasm.Wallet.from_secrets(thirdSKS);
+
+
+  const forthSK = wasm.SecretKey.dlog_from_bytes(Uint8Array.from(Buffer.from("53ceef0ece83401cf5cd853fd0c1a9bbfab750d76f278b3187f1a14768d6e9c4", "hex")));
+  const forthSKS = new wasm.SecretKeys();
+  forthSKS.add(forthSK);
+  const forthWallet = wasm.Wallet.from_secrets(forthSKS);
+
+
+  const box1 = wasm.ErgoBox.sigma_parse_bytes(Uint8Array.from(Buffer.from("80ade204100504000400040004000402d804d601b2a5730000d602e4c6a7041ad603e4c6a70510d604ad7202d901040ecdee7204ea02d19683020193c27201c2a7938cb2db63087201730100018cb2db6308a773020001eb02ea02d19683020193e4c67201041a720293e4c672010510720398b27203730300720498b272037304007204d18b0f01a6ac381e6fa99929fd1477b3ba9499790a775e91d4c14c5aa86e9a118dfac85301021a04210302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189210399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f8521024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b6621027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bd1002060865c7e0d4a77ccd605b3e4812d38140f7e68fdf740cb6cdc1d8957b75138d1e4c00", "hex")))
+  const box2 = wasm.ErgoBox.sigma_parse_bytes(Uint8Array.from(Buffer.from("80c8afa02510010e20a6ac381e6fa99929fd1477b3ba9499790a775e91d4c14c5aa86e9a118dfac853d1aea4d9010163aedb63087201d901034d0e938c7203017300d18b0f00002eca6f88ea99fb4fe313d94cb05c576b1b7a94ec7166aec958b36bcea4b8ff1a01", "hex")))
+  // console.log(box1.to_json())
+  // console.log(box2.to_json())
+  const boxes = wasm.ErgoBoxes.empty()
+  boxes.add(box1)
+
+  boxes.add(box2)
+
+  const tempBoxes = wasm.ErgoBoxes.empty()
+  tempBoxes.add(box1)
+
+  const reduced = wasm.ReducedTransaction.sigma_parse_bytes(Uint8Array.from(Buffer.from("ce04022f4cd0df4db787875b3a071e098b72ba4923bd2460e08184b34359563febe04700005e8269c8e2b975a43dc6e74a9c5b10b273313c6d32c1dd40c171fc0a8852ca0100000001a6ac381e6fa99929fd1477b3ba9499790a775e91d4c14c5aa86e9a118dfac8530480ade204100504000400040004000402d804d601b2a5730000d602e4c6a7041ad603e4c6a70510d604ad7202d901040ecdee7204ea02d19683020193c27201c2a7938cb2db63087201730100018cb2db6308a773020001eb02ea02d19683020193e4c67201041a720293e4c672010510720398b27203730300720498b272037304007204d18b0f010001021a04210302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189210399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f8521024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b6621027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bd100206088094ebdc030008cd0314368e16c9c99c5a6e20dda917aeb826b3a908becff543b3a36b38e6b3355ff5d18b0f0000c0843d1005040004000e36100204a00b08cd0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ea02d192a39a8cc7a701730073011001020402d19683030193a38cc7b2a57300000193c2b2a57301007473027303830108cdeeac93b1a57304d18b0f0000c0af87c3210008cd0314368e16c9c99c5a6e20dda917aeb826b3a908becff543b3a36b38e6b3355ff5d18b0f00009702980304cd0302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189cd0399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f85cd024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b66cd027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bd9604cd0302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189cd0399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f85cd024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b66cd027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bdf39b03d3cb9e02d073", "hex")))
+
+  let firstHints = firstWallet.generate_commitments_for_reduced_transaction(reduced).all_hints_for_input(0);
+  const firstKnown = [firstHints.get(0)];
+  const firstOwn = [firstHints.get(1)];
+
+
+  let secondHints = secondWallet.generate_commitments_for_reduced_transaction(reduced).all_hints_for_input(0);
+  const secondKnown = [secondHints.get(0)];
+  const secondOwn = [secondHints.get(1)];
+
+
+  let thirdHints = thirdWallet.generate_commitments_for_reduced_transaction(reduced).all_hints_for_input(0);
+  const thirdKnown = [thirdHints.get(0)];
+  const thirdOwn = [thirdHints.get(1)];
+
+
+
+  let forthHints = forthWallet.generate_commitments_for_reduced_transaction(reduced).all_hints_for_input(0);
+  const forthKnown = [forthHints.get(0)];
+  const forthOwn = [forthHints.get(1)];
+
+
+  const firstTransactionHintsBagKnown = wasm.TransactionHintsBag.empty();
+  const firstHintsBag = wasm.HintsBag.empty();
+  firstHintsBag.add_commitment(firstKnown[0]);
+  firstTransactionHintsBagKnown.add_hints_for_input(0, firstHintsBag);
+
+
+  const secondTransactionHintsBagKnown = wasm.TransactionHintsBag.empty();
+  const secondHintsBag = wasm.HintsBag.empty();
+  secondHintsBag.add_commitment(secondKnown[0]);
+  secondTransactionHintsBagKnown.add_hints_for_input(0, secondHintsBag);
+
+
+  const thirdTransactionHintsBagKnown = wasm.TransactionHintsBag.empty();
+  const thirdHintsBag = wasm.HintsBag.empty();
+  thirdHintsBag.add_commitment(thirdKnown[0]);
+  thirdTransactionHintsBagKnown.add_hints_for_input(0, thirdHintsBag);
+
+
+  const forthTransactionHintsBagKnown = wasm.TransactionHintsBag.empty();
+  const forthHintsBag = wasm.HintsBag.empty();
+  forthHintsBag.add_commitment(forthKnown[0]);
+  forthTransactionHintsBagKnown.add_hints_for_input(0, forthHintsBag);
+
+
+  // console.log(firstTransactionHintsBagKnown.to_json().publicHints['0'])
+  // console.log(secondTransactionHintsBagKnown.to_json().publicHints['0'])
+  // console.log(thirdTransactionHintsBagKnown.to_json().publicHints['0'])
+  // console.log(forthTransactionHintsBagKnown.to_json().publicHints['0'])
+
+  const firstCommitment = JSON.stringify(firstTransactionHintsBagKnown.to_json())
+  const secondCommitment = JSON.stringify(secondTransactionHintsBagKnown.to_json())
+  const thirdCommitment = JSON.stringify(thirdTransactionHintsBagKnown.to_json())
+  const forthCommitment = JSON.stringify(forthTransactionHintsBagKnown.to_json())
+
+  const firstHintsBagFromJson = wasm.TransactionHintsBag.from_json(firstCommitment);
+  const secondHintsBagFromJson = wasm.TransactionHintsBag.from_json(secondCommitment);
+  const thirdHintsBagFromJson = wasm.TransactionHintsBag.from_json(thirdCommitment);
+  const forthHintsBagFromJson = wasm.TransactionHintsBag.from_json(forthCommitment);
+
+  const firstTxHintsBag = wasm.TransactionHintsBag.empty();
+
+
+  firstTxHintsBag.add_hints_for_input(0, secondHintsBagFromJson.all_hints_for_input(0));
+  firstTxHintsBag.add_hints_for_input(0, thirdHintsBagFromJson.all_hints_for_input(0));
+  firstTxHintsBag.add_hints_for_input(0, forthHintsBagFromJson.all_hints_for_input(0));
+
+
+  const secondTxHintsBag = wasm.TransactionHintsBag.empty();
+
+  secondTxHintsBag.add_hints_for_input(0, firstHintsBagFromJson.all_hints_for_input(0));
+  secondTxHintsBag.add_hints_for_input(0, thirdHintsBagFromJson.all_hints_for_input(0));
+  secondTxHintsBag.add_hints_for_input(0, forthHintsBagFromJson.all_hints_for_input(0));
+
+
+  const forthTxHintsBag = wasm.TransactionHintsBag.empty();
+  forthTxHintsBag.add_hints_for_input(0, firstHintsBagFromJson.all_hints_for_input(0));
+  forthTxHintsBag.add_hints_for_input(0, secondHintsBagFromJson.all_hints_for_input(0));
+  forthTxHintsBag.add_hints_for_input(0, thirdHintsBagFromJson.all_hints_for_input(0));
+
+
+
+  const forthSign = forthWallet.sign_reduced_transaction_multi(reduced, forthTxHintsBag);
+
+  const firstRealPropositions = new wasm.Propositions;
+  const firstSimulatedPropositions = new wasm.Propositions;
+
+  const firstPK = Uint8Array.from(Buffer.from("cd0302e57ca7ebf8cfa1802d4bc79a455008307a936b4f50f0629d9bef484fdd5189", "hex"));
+  const secondPK = Uint8Array.from(Buffer.from("cd0399f5724bbc4d08c6e146d61449c05a3e0546868b1d4f83411f325187d5ca4f85", "hex"));
+  const thirdPK = Uint8Array.from(Buffer.from("cd024e06e6c6073e13a03fa4629882a69108cd60e0a9fbb2e0fcc898ce68a7051b66", "hex"));
+  const forthPK = Uint8Array.from(Buffer.from("cd027a069cc972fc7816539a316ba1cfc0164656d63dd1873ee407670b0e8195f3bd", "hex"));
+  firstRealPropositions.add_proposition_from_byte(forthPK);
+
+  const block_headers = generate_block_headers();
+  const pre_header = wasm.PreHeader.from_block_header(block_headers.get(0));
+  const ctx = new wasm.ErgoStateContext(pre_header, block_headers);
+
+  const secondHintsBagExtract = wasm.extract_hints(forthSign, ctx, boxes, wasm.ErgoBoxes.from_boxes_json([]), firstRealPropositions, firstSimulatedPropositions);
+  assert(secondHintsBagExtract !== null);
+});