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

Issue:
Increase minimum ergo node version from 4.0.28 to 4.0.100 for SPV workflow tests
Due to the proposed EIP-37 hard-fork, the SPV tests were breaking since a number of nodes have already upgraded and we use string-comparison for ergo node versions (so that "4.0.100" is judged to be less than "4.0.28"). EIP-37 looks like it will pass, so let's bump up the version to 4.0.100.


Discussion / hints:

Fix patch:
diff --git a/bindings/ergo-lib-c-core/src/rest/node_info.rs b/bindings/ergo-lib-c-core/src/rest/node_info.rs
index a3304b096..eb287bda3 100644
--- a/bindings/ergo-lib-c-core/src/rest/node_info.rs
+++ b/bindings/ergo-lib-c-core/src/rest/node_info.rs
@@ -10,11 +10,11 @@ pub unsafe fn node_info_get_name(node_info_ptr: NodeInfoPtr) -> String {
     node_info.0.name.clone()
 }
 
-/// Returns true iff the ergo node is at least v4.0.28. This is important since nipopow proofs only
-/// work correctly from this version onwards.
-pub unsafe fn node_info_is_at_least_version_4_0_28(
+/// Returns true iff the ergo node is at least v4.0.100. This is important due to the EIP-37
+/// hard-fork.
+pub unsafe fn node_info_is_at_least_version_4_0_100(
     node_info_ptr: NodeInfoPtr,
 ) -> Result<bool, Error> {
     let node_info = const_ptr_as_ref(node_info_ptr, "node_info_ptr")?;
-    Ok(node_info.0.is_at_least_version_4_0_28())
+    Ok(node_info.0.is_at_least_version_4_0_100())
 }
diff --git a/bindings/ergo-lib-c/src/rest/node_info.rs b/bindings/ergo-lib-c/src/rest/node_info.rs
index b51fe465a..f1b2338af 100644
--- a/bindings/ergo-lib-c/src/rest/node_info.rs
+++ b/bindings/ergo-lib-c/src/rest/node_info.rs
@@ -2,7 +2,7 @@ use std::ffi::CString;
 use std::os::raw::c_char;
 
 use ergo_lib_c_core::rest::node_info::NodeInfoPtr;
-use ergo_lib_c_core::rest::node_info::{node_info_get_name, node_info_is_at_least_version_4_0_28};
+use ergo_lib_c_core::rest::node_info::{node_info_get_name, node_info_is_at_least_version_4_0_100};
 
 use crate::delete_ptr;
 
@@ -27,9 +27,9 @@ pub unsafe extern "C" fn ergo_lib_node_info_get_name(
 /// work correctly from this version onwards.
 #[allow(clippy::unwrap_used)]
 #[no_mangle]
-pub unsafe extern "C" fn ergo_lib_node_info_is_at_least_version_4_0_28(
+pub unsafe extern "C" fn ergo_lib_node_info_is_at_least_version_4_0_100(
     node_info_ptr: NodeInfoPtr,
 ) -> bool {
     #[allow(clippy::unwrap_used)]
-    node_info_is_at_least_version_4_0_28(node_info_ptr).unwrap()
+    node_info_is_at_least_version_4_0_100(node_info_ptr).unwrap()
 }
diff --git a/bindings/ergo-lib-ios/Sources/ErgoLib/NodeInfo.swift b/bindings/ergo-lib-ios/Sources/ErgoLib/NodeInfo.swift
index 34493e516..e3d3e5504 100644
--- a/bindings/ergo-lib-ios/Sources/ErgoLib/NodeInfo.swift
+++ b/bindings/ergo-lib-ios/Sources/ErgoLib/NodeInfo.swift
@@ -20,8 +20,8 @@ class NodeInfo: FromRawPtr {
         return str
     }
 
-    func isAtLeastVersion4028() -> Bool {
-        return ergo_lib_node_info_is_at_least_version_4_0_28(self.pointer)
+    func isAtLeastVersion40100() -> Bool {
+        return ergo_lib_node_info_is_at_least_version_4_0_100(self.pointer)
     }
 
     deinit {
diff --git a/bindings/ergo-lib-ios/Tests/ErgoLibTests/Common.swift b/bindings/ergo-lib-ios/Tests/ErgoLibTests/Common.swift
index 4623cad3a..bc4cf8657 100644
--- a/bindings/ergo-lib-ios/Tests/ErgoLibTests/Common.swift
+++ b/bindings/ergo-lib-ios/Tests/ErgoLibTests/Common.swift
@@ -42,7 +42,7 @@ func getNipopowProof(url: URL, headerId: BlockId) async throws -> NipopowProof?
     let nodeConf = try NodeConf(withAddrString: url.absoluteString)
     let restNodeApi = try RestNodeApi()
     let nodeInfo = try await restNodeApi.getInfoAsync(nodeConf: nodeConf)
-    if nodeInfo.isAtLeastVersion4028() {
+    if nodeInfo.isAtLeastVersion40100() {
         let proof = try await restNodeApi.getNipopowProofByHeaderIdAsync(nodeConf: nodeConf, minChainLength: UInt32(7), suffixLen: UInt32(6), headerId: headerId)
         return proof
     } else {
diff --git a/bindings/ergo-lib-ios/Tests/ErgoLibTests/RestNodeApiTests.swift b/bindings/ergo-lib-ios/Tests/ErgoLibTests/RestNodeApiTests.swift
index 0dff9cf4f..7c3f7be6e 100644
--- a/bindings/ergo-lib-ios/Tests/ErgoLibTests/RestNodeApiTests.swift
+++ b/bindings/ergo-lib-ios/Tests/ErgoLibTests/RestNodeApiTests.swift
@@ -106,7 +106,7 @@ final class RestNodeApiTests: XCTestCase {
         let txId = try TxId(withString: "258ddfc09b94b8313bca724de44a0d74010cab26de379be845713cc129546b78")
         let proofs = try await withThrowingTaskGroup(of: [NipopowProof].self) { group -> [NipopowProof] in
             group.addTask {
-                let proof = try await getNipopowProof(url: URL(string: "159.65.11.55:9053")!, headerId: headerId)!
+                let proof = try await getNipopowProof(url: URL(string: "167.235.75.51:9053")!, headerId: headerId)!
                 return [proof]
             }
             group.addTask {
@@ -124,7 +124,7 @@ final class RestNodeApiTests: XCTestCase {
         XCTAssertEqual(try bestProof.suffixHead().getHeader().getBlockId(), headerId)
         
         // Now verify with 3rd node
-        let nodeConf = try NodeConf(withAddrString: "159.65.11.55:9053")
+        let nodeConf = try NodeConf(withAddrString: "167.235.142.92:9053")
         let restNodeApi = try RestNodeApi()
         let header = try await restNodeApi.getHeaderAsync(nodeConf: nodeConf, blockId: headerId)
         let merkleProof = try await restNodeApi.getBlocksHeaderIdProofForTxIdAsync(nodeConf: nodeConf, blockId: headerId, txId: txId)
diff --git a/bindings/ergo-lib-wasm/Cargo.toml b/bindings/ergo-lib-wasm/Cargo.toml
index 9bef31a69..9d68ccb88 100644
--- a/bindings/ergo-lib-wasm/Cargo.toml
+++ b/bindings/ergo-lib-wasm/Cargo.toml
@@ -27,6 +27,11 @@ bounded-integer = { version = "^0.5", features = ["types"] }
 futures = "0.3"
 thiserror = "1"
 
+# This dependency is needed due to deprecation of `JsValue::[into|from]_serde` in `wasm-bindgen`.
+# Note that the deprecation comments suggest using `serde-wasm-bindgen` as another alternative; I
+# tried it but it breaks some of our tests.
+gloo-utils = {version = "0.1.5", features = ["serde"] }
+
 # used in elliptic-curve(in ergo-lib), compiled here with WASM support
 getrandom = {version = "0.2.7", features = ["js"]}
 
@@ -45,7 +50,7 @@ features = [ "json" ]
 
 [dependencies.wasm-bindgen]
 version = "0.2.79"
-features = ["serde-serialize"]
+features = []
 
 [dependencies.wasm-bindgen-futures]
 version = "0.4.28"
diff --git a/bindings/ergo-lib-wasm/src/ast.rs b/bindings/ergo-lib-wasm/src/ast.rs
index 412fd6f9b..a5b90da05 100644
--- a/bindings/ergo-lib-wasm/src/ast.rs
+++ b/bindings/ergo-lib-wasm/src/ast.rs
@@ -11,6 +11,7 @@ use ergo_lib::ergotree_ir::base16_str::Base16Str;
 use ergo_lib::ergotree_ir::mir::constant::{TryExtractFrom, TryExtractInto};
 use ergo_lib::ergotree_ir::serialization::SigmaSerializable;
 use ergo_lib::ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog;
+use gloo_utils::format::JsValueSerdeExt;
 use js_sys::Uint8Array;
 use std::convert::TryFrom;
 use wasm_bindgen::prelude::*;
@@ -136,11 +137,11 @@ impl Constant {
                         .ok_or_else(|| JsValue::from_str("i64 as a string"))?;
                     serde_json::from_str(l_str.as_str())
                 } else {
-                    l.into_serde::<i64>()
+                    JsValueSerdeExt::into_serde(l)
                 }
                 .map_err(|e| {
                     JsValue::from_str(&format!(
-                        "Failed to parse i64 from JSON string: {:?} \n with error: {}",
+                        "Failed to parse i64 from JSON string: {:?} \n with error: {:?}",
                         l, e
                     ))
                 })?;
diff --git a/bindings/ergo-lib-wasm/src/batchmerkleproof.rs b/bindings/ergo-lib-wasm/src/batchmerkleproof.rs
index 3be08dce5..d39aca304 100644
--- a/bindings/ergo-lib-wasm/src/batchmerkleproof.rs
+++ b/bindings/ergo-lib-wasm/src/batchmerkleproof.rs
@@ -1,5 +1,6 @@
 //! BatchMerkleProof (compact Merkle multi-proofs)
 use ergo_lib::ergo_merkle_tree;
+use gloo_utils::format::JsValueSerdeExt;
 use serde::{Deserialize, Serialize};
 use wasm_bindgen::prelude::*;
 
@@ -12,11 +13,11 @@ pub struct BatchMerkleProof(pub(crate) ergo_merkle_tree::BatchMerkleProof);
 impl BatchMerkleProof {
     /// Creates a new [`BatchMerkleProof`] from json representation
     pub fn from_json(json: &JsValue) -> Result<BatchMerkleProof, String> {
-        json.into_serde().map_err(|err| err.to_string())
+        JsValueSerdeExt::into_serde(json).map_err(|err| err.to_string())
     }
     /// Converts [`BatchMerkleProof`] to json representation
     pub fn to_json(&self) -> Result<JsValue, String> {
-        JsValue::from_serde(&self).map_err(|err| err.to_string())
+        <JsValue as JsValueSerdeExt>::from_serde(&self).map_err(|err| err.to_string())
     }
 
     /// Calculates root hash for [`BatchMerkleProof`] and compares it against expected root hash
diff --git a/bindings/ergo-lib-wasm/src/block_header.rs b/bindings/ergo-lib-wasm/src/block_header.rs
index 84990b4f0..91eb510d8 100644
--- a/bindings/ergo-lib-wasm/src/block_header.rs
+++ b/bindings/ergo-lib-wasm/src/block_header.rs
@@ -1,6 +1,7 @@
 //! Block header
 
 use ergo_lib::ergo_chain_types::Header;
+use gloo_utils::format::JsValueSerdeExt;
 use wasm_bindgen::prelude::*;
 
 extern crate derive_more;
@@ -73,7 +74,7 @@ impl BlockHeaders {
                         .ok_or(JsValue::from_str("Expected BlockHeader JSON as string"))?;
                     serde_json::from_str(jb_str.as_str())
                 } else {
-                    jb.into_serde::<ergo_lib::ergo_chain_types::Header>()
+                    JsValueSerdeExt::into_serde(jb)
                 }
                 .map_err(|e| {
                     JsValue::from_str(&format!(
diff --git a/bindings/ergo-lib-wasm/src/box_coll.rs b/bindings/ergo-lib-wasm/src/box_coll.rs
index a5fe6fdfa..a7eb11a7c 100644
--- a/bindings/ergo-lib-wasm/src/box_coll.rs
+++ b/bindings/ergo-lib-wasm/src/box_coll.rs
@@ -1,5 +1,6 @@
 //! Box collection types
 use ergo_lib::ergotree_ir::chain;
+use gloo_utils::format::JsValueSerdeExt;
 use wasm_bindgen::prelude::*;
 
 use crate::ergo_box::{ErgoBox, ErgoBoxCandidate};
@@ -23,11 +24,11 @@ impl ErgoBoxes {
                         .ok_or(JsValue::from_str("Expected ErgoBox JSON as string"))?;
                     serde_json::from_str(jb_str.as_str())
                 } else {
-                    jb.into_serde::<chain::ergo_box::ErgoBox>()
+                    JsValueSerdeExt::into_serde(jb)
                 }
                 .map_err(|e| {
                     JsValue::from_str(&format!(
-                        "Failed to parse ErgoBox from JSON string: {:?} \n with error: {}",
+                        "Failed to parse ErgoBox from JSON string: {:?} \n with error: {:?}",
                         jb, e
                     ))
                 })?;
diff --git a/bindings/ergo-lib-wasm/src/ergo_box.rs b/bindings/ergo-lib-wasm/src/ergo_box.rs
index 4080f722d..ed3b42479 100644
--- a/bindings/ergo-lib-wasm/src/ergo_box.rs
+++ b/bindings/ergo-lib-wasm/src/ergo_box.rs
@@ -23,6 +23,7 @@ use ergo_lib::ergotree_ir::chain::ergo_box::BoxTokens;
 use ergo_lib::ergotree_ir::chain::ergo_box::NonMandatoryRegisters;
 use ergo_lib::ergotree_ir::serialization::SigmaSerializable;
 use ergo_lib::wallet::tx_builder::new_miner_fee_box;
+use gloo_utils::format::JsValueSerdeExt;
 use js_sys::Uint8Array;
 use wasm_bindgen::prelude::*;
 
@@ -213,7 +214,8 @@ impl ErgoBox {
     /// (similar to [`Self::to_json`], but as JS object with box value and token amounts encoding as strings)
     pub fn to_js_eip12(&self) -> Result<JsValue, JsValue> {
         let box_dapp: ErgoBoxJsonEip12 = self.0.clone().into();
-        JsValue::from_serde(&box_dapp).map_err(|e| JsValue::from_str(&format!("{}", e)))
+        <JsValue as JsValueSerdeExt>::from_serde(&box_dapp)
+            .map_err(|e| JsValue::from_str(&format!("{}", e)))
     }
 
     /// parse from JSON
diff --git a/bindings/ergo-lib-wasm/src/rest/node_info.rs b/bindings/ergo-lib-wasm/src/rest/node_info.rs
index f3950497b..40fe96d77 100644
--- a/bindings/ergo-lib-wasm/src/rest/node_info.rs
+++ b/bindings/ergo-lib-wasm/src/rest/node_info.rs
@@ -15,9 +15,9 @@ impl NodeInfo {
         self.0.name.clone()
     }
 
-    /// Returns true iff the ergo node is at least v4.0.28. This is important since nipopow proofs
-    /// only work correctly from this version onwards.
-    pub fn is_at_least_version_4_0_28(&self) -> bool {
-        self.0.is_at_least_version_4_0_28()
+    /// Returns true iff the ergo node is at least v4.0.100. This is important due to the EIP-37
+    /// hard-fork.
+    pub fn is_at_least_version_4_0_100(&self) -> bool {
+        self.0.is_at_least_version_4_0_100()
     }
 }
diff --git a/bindings/ergo-lib-wasm/src/token.rs b/bindings/ergo-lib-wasm/src/token.rs
index 015ac07d4..8a47b191b 100644
--- a/bindings/ergo-lib-wasm/src/token.rs
+++ b/bindings/ergo-lib-wasm/src/token.rs
@@ -6,6 +6,7 @@ use bounded_vec::OptBoundedVecToVec;
 use ergo_lib::ergo_chain_types::Base16DecodedBytes;
 use ergo_lib::ergo_chain_types::Digest32;
 use ergo_lib::ergotree_ir::chain;
+use gloo_utils::format::JsValueSerdeExt;
 use js_sys::Uint8Array;
 use wasm_bindgen::prelude::*;
 
@@ -121,7 +122,8 @@ impl Token {
     /// (similar to [`Self::to_json`], but as JS object with token amount encoding as string)
     pub fn to_js_eip12(&self) -> Result<JsValue, JsValue> {
         let t_dapp: TokenJsonEip12 = self.0.clone().into();
-        JsValue::from_serde(&t_dapp).map_err(|e| JsValue::from_str(&format!("{}", e)))
+        <JsValue as JsValueSerdeExt>::from_serde(&t_dapp)
+            .map_err(|e| JsValue::from_str(&format!("{}", e)))
     }
 }
 
diff --git a/bindings/ergo-lib-wasm/src/transaction.rs b/bindings/ergo-lib-wasm/src/transaction.rs
index 00ffa2dad..bab075dda 100644
--- a/bindings/ergo-lib-wasm/src/transaction.rs
+++ b/bindings/ergo-lib-wasm/src/transaction.rs
@@ -13,6 +13,7 @@ use crate::json::UnsignedTransactionJsonEip12;
 use ergo_lib::chain;
 use ergo_lib::chain::transaction::{distinct_token_ids, TxIoVec};
 use ergo_lib::ergotree_ir::serialization::SigmaSerializable;
+use gloo_utils::format::JsValueSerdeExt;
 use js_sys::Uint8Array;
 use std::convert::{TryFrom, TryInto};
 use wasm_bindgen::prelude::*;
@@ -95,7 +96,7 @@ impl TransactionHintsBag {
 
     /// Return JSON object (node format)
     pub fn to_json(&self) -> Result<JsValue, JsValue> {
-        JsValue::from_serde(&self.0).map_err(to_js)
+        <JsValue as JsValueSerdeExt>::from_serde(&self.0).map_err(to_js)
     }
 
     /// Parse from JSON object (node format)
@@ -223,7 +224,8 @@ impl Transaction {
     /// (similar to [`Self::to_json`], but as JS object with box value and token amount encoding as strings)
     pub fn to_js_eip12(&self) -> Result<JsValue, JsValue> {
         let tx_dapp: TransactionJsonEip12 = self.0.clone().into();
-        JsValue::from_serde(&tx_dapp).map_err(|e| JsValue::from_str(&format!("{}", e)))
+        <JsValue as JsValueSerdeExt>::from_serde(&tx_dapp)
+            .map_err(|e| JsValue::from_str(&format!("{}", e)))
     }
 
     /// parse from JSON
@@ -365,7 +367,8 @@ impl UnsignedTransaction {
     /// (similar to [`Self::to_json`], but as JS object with box value and token amount encoding as strings)
     pub fn to_js_eip12(&self) -> Result<JsValue, JsValue> {
         let tx_dapp: UnsignedTransactionJsonEip12 = self.0.clone().into();
-        JsValue::from_serde(&tx_dapp).map_err(|e| JsValue::from_str(&format!("{}", e)))
+        <JsValue as JsValueSerdeExt>::from_serde(&tx_dapp)
+            .map_err(|e| JsValue::from_str(&format!("{}", e)))
     }
 
     /// parse from JSON
diff --git a/ergo-rest/src/node_info.rs b/ergo-rest/src/node_info.rs
index 7e954a8d1..c2996ef47 100644
--- a/ergo-rest/src/node_info.rs
+++ b/ergo-rest/src/node_info.rs
@@ -16,10 +16,9 @@ pub struct NodeInfo {
 }
 
 impl NodeInfo {
-    /// Returns true iff the ergo node is at least v4.0.28. This is important since nipopow proofs
-    /// only work correctly from this version onwards.
-    pub fn is_at_least_version_4_0_28(&self) -> bool {
-        let ord = self.app_version.cmp(&String::from("4.0.28"));
+    /// Returns true iff the ergo node is at least v4.0.100. This is due to the EIP-37 hard-fork.
+    pub fn is_at_least_version_4_0_100(&self) -> bool {
+        let ord = self.app_version.cmp(&String::from("4.0.100"));
         ord == Ordering::Equal || ord == Ordering::Greater
     }
 }


Test patch:
diff --git a/bindings/ergo-lib-wasm/tests_browser/test_rest_api.js b/bindings/ergo-lib-wasm/tests_browser/test_rest_api.js
index ddbbc5081..7401010d6 100644
--- a/bindings/ergo-lib-wasm/tests_browser/test_rest_api.js
+++ b/bindings/ergo-lib-wasm/tests_browser/test_rest_api.js
@@ -54,7 +54,7 @@ it('node REST API: example SPV workflow', async () => {
 
     // Get NiPoPow proofs from 2 separate ergo nodes
     let proofs = await Promise.all([
-        get_nipopow_proof(new URL("http://159.65.11.55:9053"), header_id),
+        get_nipopow_proof(new URL("http://167.235.75.51:9053"), header_id),
         get_nipopow_proof(new URL("http://213.239.193.208:9053"), header_id),
     ]);
 
@@ -68,7 +68,7 @@ it('node REST API: example SPV workflow', async () => {
     assert(best_proof.suffix_head().id().equals(header_id), "equality");
 
     // Verify with a 3rd node
-    let node_conf = new ergo_wasm.NodeConf(new URL("http://159.65.11.55:9053"));
+    let node_conf = new ergo_wasm.NodeConf(new URL("http://167.235.142.92:9053"));
     let header = await ergo_wasm.get_header(node_conf, header_id);
     assert(header != null, "header should be non-null");
     let merkle_proof = await ergo_wasm.get_blocks_header_id_proof_for_tx_id(node_conf, header_id, tx_id);
@@ -80,10 +80,9 @@ async function get_nipopow_proof(url, header_id) {
     let node_conf = new ergo_wasm.NodeConf(url);
     assert(node_conf != null);
 
-    // Make sure we're communicating with a node with version >= 4.0.28, otherwise we won't be able
-    // to get a proper Nipopow proof
+    // Make sure we're communicating with a node with version >= 4.0.100, due to the EIP-37 hard-fork.
     let node_info = await ergo_wasm.get_info(node_conf);
-    assert(node_info.is_at_least_version_4_0_28());
+    assert(node_info.is_at_least_version_4_0_100(), "Ergo node should be at least version 4.0.100");
 
     let proof = await ergo_wasm.get_nipopow_proof_by_header_id(node_conf, 7, 6, header_id);
     assert(proof != null);