Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[profile.default]
retries = { backoff = "exponential", count = 3, delay = "5s" }
23 changes: 10 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
override: true
- uses: taiki-e/install-action@nextest
- name: cargo fetch
uses: actions-rs/cargo@v1
with:
Expand All @@ -53,12 +54,12 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --verbose --release --tests
args: --verbose --tests
- name: Run tests
uses: actions-rs/cargo@v1
with:
command: test
args: --verbose --release
command: nextest
args: run --verbose

test_coverage:
name: Code coverage in tests
Expand All @@ -69,16 +70,12 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
override: true
- name: Install cargo-binstall
uses: cargo-bins/[email protected]
- name: Install cargo-tarpaulin
uses: actions-rs/cargo@v1
with:
command: binstall
args: cargo-tarpaulin --no-confirm
components: llvm-tools-preview
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: taiki-e/install-action@nextest
- name: Generate code coverage
run: |
cargo tarpaulin --avoid-cfg-tarpaulin --timeout=360 --out lcov --exclude-files 'bindings/**/*.*' --exclude-files 'ergo-rest/src/reqwest.rs' --exclude-files 'ergo-rest/src/reqwest/**/*.*' --exclude-files 'ergo-rest/src/wasm_timer.rs' --exclude-files 'ergo-rest/src/wasm_timer/**/*.*'
cargo llvm-cov --lcov --output-path lcov.info --ignore-filename-regex "bindings/.*" nextest
- name: Push code coverage results to coveralls.io
uses: coverallsapp/github-action@master
with:
Expand Down Expand Up @@ -215,7 +212,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: doc
args: --document-private-items
args: --document-private-items --no-deps

ios_tests:
name: Test Swift(iOS) bindings
Expand All @@ -238,7 +235,7 @@ jobs:

- uses: actions-rs/toolchain@v1
with:
toolchain: nightly-2025-03-28
toolchain: nightly-2025-11-10
override: true

- name: install deps
Expand Down
18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,17 @@ derive_more = { version = "2.0.1", default-features = false, features = [
] }
num-derive = "0.4.2"
thiserror = { version = "2.0.1", default-features = false }
bounded-vec = { version = "0.8.0", default-features = false }
bounded-vec = { version = "0.9.0", default-features = false }
bitvec = { version = "1.0.1", default-features = false, features = ["alloc"] }
blake2 = { version = "0.10.6", default-features = false }
sha2 = { version = "0.10", default-features = false }
num-traits = { version = "0.2.14", default-features = false }
num-integer = { version = "0.1.44", default-features = false }
num-bigint = { version = "0.4.0", default-features = false }
lazy_static = { version = "1.4", features = ["spin_no_std"] }
bs58 = { version = "0.4.0", default-features = false, features = ["alloc"] }
bs58 = { version = "0.5.1", default-features = false, features = ["alloc"] }
base16 = { version = "0.2.1", default-features = false, features = ["alloc"] }
base64 = { version = "0.13.0", default-features = false, features = ["alloc"] }
base64 = { version = "0.22.1", default-features = false, features = ["alloc"] }
indexmap = { version = "2.6.0", default-features = false }
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde_json = { version = "1.0", default-features = false, features = [
Expand All @@ -82,15 +82,15 @@ serde_with = { version = "3.11.0", default-features = false, features = [
rand = "0.8.5"
bytes = { version = "1.1", default-features = false }
futures = "0.3"
tokio = { version = "1.15.0", features = ["full"] }
tokio = { version = "1.15.0", default-features = false, features = ["rt", "rt-multi-thread"] }
tokio-stream = { version = "0.1.8", features = ["sync", "time"] }
tokio-util = { version = "0.6.9", features = ["codec"] }
bounded-integer = { version = "^0.5", features = ["types"] }
tokio-util = { version = "0.7.17", features = ["codec"] }
bounded-integer = { version = "0.6.1" }
url = "2.5.4"
getrandom = { version = "0.2.16" }
itertools = { version = "0.10.3", default-features = false }
miette = { version = "5", features = ["fancy"] }
hashbrown = { version = "0.14.3", features = ["serde"] }
itertools = { version = "0.14", default-features = false }
miette = { version = "7.6.0", features = ["fancy"] }
hashbrown = { version = "0.16.1", features = ["serde"] }
core2 = { version = "0.4.0", default-features = false, features = ["alloc"] }
# dev-dependencies
proptest = { version = "=1.6.0", default-features = false, features = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ final class RestNodeApiTests: XCTestCase {
}
group.addTask {
let proof = try await getNipopowProof(
url: URL(string: "http://213.239.193.208:9053")!, headerId: headerId)!
url: URL(string: "http://76.22.82.80:9053")!, headerId: headerId)!
return [proof]
}
return try await group.reduce(into: [NipopowProof]()) { $0 += $1 }
Expand All @@ -131,7 +131,7 @@ final class RestNodeApiTests: XCTestCase {
XCTAssertEqual(try bestProof.suffixHead().getHeader().getBlockId(), headerId)

// Now verify with 3rd node
let nodeConf = try NodeConf(withAddrString: "213.239.193.208:9053")
let nodeConf = try NodeConf(withAddrString: "76.22.82.80:9053")
let restNodeApi = try RestNodeApi()
let header = try await restNodeApi.getHeaderAsync(nodeConf: nodeConf, blockId: headerId)
let merkleProof = try await restNodeApi.getBlocksHeaderIdProofForTxIdAsync(
Expand Down
4 changes: 2 additions & 2 deletions bindings/ergo-lib-python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ name = "ergo_lib_python"
crate-type = ["cdylib"]

[dependencies]
pyo3 = { version = "0.24.2", features = ["indexmap"] }
pyo3 = { version = "0.27.1", features = ["indexmap"] }
base16 = { workspace = true }
derive_more = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
ergo-lib = { workspace = true, features = ["mnemonic_gen"] }
sigma-ser = { workspace = true }
serde-pyobject = "0.6.1"
serde-pyobject = "0.8.0"
8 changes: 4 additions & 4 deletions bindings/ergo-lib-python/src/chain/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl SType {
tpes.bind(py)
.iter()
.map(|tpe| -> PyResult<stype::SType> {
tpe.downcast::<SType>()?.get().to_stype(py)
tpe.cast::<SType>()?.get().to_stype(py)
})
.collect::<PyResult<Vec<_>>>()?,
)
Expand Down Expand Up @@ -145,9 +145,9 @@ impl SType {
.iter()
.zip(t2.bind(py).iter())
.map(|(t1, t2)| -> PyResult<bool> {
t1.downcast_into::<SType>()?
t1.cast_into::<SType>()?
.get()
.__eq__(t2.downcast_into::<SType>()?.get(), py)
.__eq__(t2.cast_into::<SType>()?.get(), py)
})
.reduce(|res1, res2| res1.and_then(|res1| res2.map(|res2| res1 == res2)))
.transpose()?
Expand All @@ -174,7 +174,7 @@ impl Constant {
if let Ok(bytes) = arg.extract::<&[u8]>() {
return Ok(Self(constant::Constant::from(bytes.to_owned())));
}
if let Ok(tuple) = arg.downcast_exact::<PyTuple>() {
if let Ok(tuple) = arg.cast_exact::<PyTuple>() {
return from_tuple(tuple);
}
if let Ok(arr) = arg.extract::<Vec<Constant>>() {
Expand Down
2 changes: 1 addition & 1 deletion bindings/ergo-lib-python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub(crate) fn to_value_error<E: std::error::Error>(e: E) -> PyErr {
}

pub(crate) fn from_json<T: DeserializeOwned>(json: Bound<'_, PyAny>) -> PyResult<T> {
let res = match json.downcast_into::<PyDict>() {
let res = match json.cast_into::<PyDict>() {
Ok(dict) => from_pyobject::<T, PyDict>(dict).map_err(to_value_error)?,
Err(json) => {
serde_json::from_str(json.into_inner().extract::<&str>()?).map_err(JsonError::from)?
Expand Down
2 changes: 1 addition & 1 deletion bindings/ergo-lib-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ thiserror = { workspace = true }
# 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"] }
gloo-utils = {version = "0.2.0", features = ["serde"] }
# used in elliptic-curve(in ergo-lib), compiled here with WASM support
getrandom = { workspace = true, features = ["js"] }
# The `console_error_panic_hook` crate provides better debugging of panics by
Expand Down
2 changes: 1 addition & 1 deletion bindings/ergo-lib-wasm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"mocha-loader": "^2.0.1",
"node-fetch": "^2.6.7",
"text-encoder": "0.0.4",
"webpack": "^5.37.1"
"webpack": "^5.103.0"
},
"dependencies": {
"babel": "^6.23.0"
Expand Down
2 changes: 1 addition & 1 deletion bindings/ergo-lib-wasm/src/ast/js_conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub(crate) fn constant_from_js(val: &JsValue) -> Result<Constant, ConvError> {
} else if let Ok(coll_longs) = coll_long_from_js(&arr) {
Ok(coll_longs)
} else {
return Err(ConvError::NotSupported(val.clone()));
Err(ConvError::NotSupported(val.clone()))
}
} else {
// regular array
Expand Down
2 changes: 1 addition & 1 deletion bindings/ergo-lib-wasm/src/box_selector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl BoxSelection {
Ok(BoxSelection(wallet::box_selector::BoxSelection::<
ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox,
> {
boxes: BoundedVec::from_vec(boxes.clone().into()).map_err(to_js)?,
boxes: BoundedVec::<_, 1, _>::from_vec(boxes.clone().into()).map_err(to_js)?,
change_boxes: change.clone().into(),
}))
}
Expand Down
3 changes: 2 additions & 1 deletion ergo-chain-types/src/digest32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use base64::Engine;
use core::array::TryFromSliceError;
use core::convert::TryFrom;
use core::convert::TryInto;
Expand Down Expand Up @@ -46,7 +47,7 @@

/// Parse `Digest<N>` from base64 encoded string
pub fn from_base64(s: &str) -> Result<Digest<N>, DigestNError> {
let bytes = base64::decode(s)?;
let bytes = base64::engine::general_purpose::STANDARD.decode(s)?;
let arr: [u8; N] = bytes.as_slice().try_into()?;
Ok(Digest(arr))
}
Expand Down Expand Up @@ -105,7 +106,7 @@
}
}

/// Decode Digest<N> from a base16-encoded string

Check warning on line 109 in ergo-chain-types/src/digest32.rs

View workflow job for this annotation

GitHub Actions / Check intra-documentation links

unclosed HTML tag `N`
impl<const N: usize> FromStr for Digest<N> {
type Err = DigestNError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Expand Down
2 changes: 1 addition & 1 deletion ergo-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ proptest-derive = { workspace = true, optional = true }
k256 = { workspace = true }
sha2 = { workspace = true }
hmac = { version = "0.12" }
pbkdf2 = "0.11"
pbkdf2 = "0.12.2"
rand = { workspace = true, optional = true }
bitvec = { workspace = true, optional = true }
unicode-normalization = { version = "0.1.19", default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion ergo-lib/src/chain/transaction/ergo_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ pub trait ErgoTransaction: ContextExtensionProvider {
fn outputs(&self) -> &[ErgoBox];

/// Stateless transaction validation (no blockchain context) for a transaction
/// Returns [`Ok(())`] if validation has succeeded or returns [`TxValidationError`]
/// Returns [Ok(())] if validation has succeeded or returns [`TxValidationError`]
fn validate_stateless(&self) -> Result<(), TxValidationError> {
// Note that we don't need to check if inputs/data inputs/outputs are >= 1 <= 32767 here since BoundedVec takes care of that
let inputs = self.inputs_ids();
Expand Down
99 changes: 53 additions & 46 deletions ergo-lib/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::ergotree_interpreter::sigma_protocol::prover::hint::{Hint, HintsBag};
use alloc::boxed::Box;
use alloc::vec::Vec;
use ergotree_interpreter::sigma_protocol::private_input::PrivateInput;
use ergotree_interpreter::sigma_protocol::prover::hint::CommitmentHint;
use ergotree_interpreter::sigma_protocol::prover::Prover;
use ergotree_interpreter::sigma_protocol::prover::ProverError;
use ergotree_interpreter::sigma_protocol::prover::TestProver;
Expand Down Expand Up @@ -282,66 +283,72 @@ impl TransactionHintsBag {
}
}

/// Private helper function to correctly segregate a HintsBag into secret and public parts.
fn segregate_hints(hints_bag: HintsBag) -> (HintsBag, HintsBag) {
let (secret, public): (Vec<Hint>, Vec<Hint>) =
hints_bag.hints.into_iter().partition(|hint| {
matches!(hint, Hint::CommitmentHint(CommitmentHint::OwnCommitment(_)))
});

(HintsBag { hints: secret }, HintsBag { hints: public })
}

/// Replacing Hints for an input index
pub fn replace_hints_for_input(&mut self, index: usize, hints_bag: HintsBag) {
let public: Vec<Hint> = hints_bag
.hints
.clone()
.into_iter()
.filter(|hint| matches!(hint, Hint::CommitmentHint(_)))
.collect();
let secret: Vec<Hint> = hints_bag
.hints
.into_iter()
.filter(|hint| matches!(hint, Hint::SecretProven(_)))
.collect();

self.secret_hints.insert(index, HintsBag { hints: secret });
self.public_hints.insert(index, HintsBag { hints: public });
let (secret_bag, public_bag) = Self::segregate_hints(hints_bag);
self.secret_hints.insert(index, secret_bag);
self.public_hints.insert(index, public_bag);
}

/// Adding hints for a input index
pub fn add_hints_for_input(&mut self, index: usize, hints_bag: HintsBag) {
let mut public: Vec<Hint> = hints_bag
.hints
.clone()
.into_iter()
.filter(|hint| matches!(hint, Hint::CommitmentHint(_)))
.collect();
let mut secret: Vec<Hint> = hints_bag
.hints
.into_iter()
.filter(|hint| matches!(hint, Hint::SecretProven(_)))
.collect();
let secret_bag = HintsBag::empty();
let public_bag = HintsBag::empty();
let old_secret: &Vec<Hint> = &self.secret_hints.get(&index).unwrap_or(&secret_bag).hints;
for hint in old_secret {
secret.push(hint.clone());
let (mut new_secret_bag, mut new_public_bag) = Self::segregate_hints(hints_bag);

// Get the existing secret hints, or an empty bag if none exist, and add the new ones.
if let Some(existing_secrets) = self.secret_hints.get_mut(&index) {
existing_secrets.hints.append(&mut new_secret_bag.hints);
} else {
self.secret_hints.insert(index, new_secret_bag);
}

let old_public: &Vec<Hint> = &self.public_hints.get(&index).unwrap_or(&public_bag).hints;
for hint in old_public {
public.push(hint.clone());
// Get the existing public hints, or an empty bag if none exist, and add the new ones.
if let Some(existing_public) = self.public_hints.get_mut(&index) {
existing_public.hints.append(&mut new_public_bag.hints);
} else {
self.public_hints.insert(index, new_public_bag);
}
self.secret_hints.insert(index, HintsBag { hints: secret });
self.public_hints.insert(index, HintsBag { hints: public });
}

/// Outputting HintsBag corresponding for an index
/// WARNING: This HintsBag also contains private randomness that should not be shared with other signers
pub fn all_hints_for_input(&self, index: usize) -> HintsBag {
let mut hints: Vec<Hint> = Vec::new();
let secret_bag = HintsBag::empty();
let public_bag = HintsBag::empty();
let secrets: &Vec<Hint> = &self.secret_hints.get(&index).unwrap_or(&secret_bag).hints;
for hint in secrets {
hints.push(hint.clone());
let mut all_hints = Vec::new();

if let Some(secret_bag) = self.secret_hints.get(&index) {
all_hints.extend_from_slice(&secret_bag.hints);
}

if let Some(public_bag) = self.public_hints.get(&index) {
all_hints.extend_from_slice(&public_bag.hints);
}
let public: &Vec<Hint> = &self.public_hints.get(&index).unwrap_or(&public_bag).hints;
for hint in public {
hints.push(hint.clone());

HintsBag { hints: all_hints }
}
}

#[cfg(test)]
#[cfg(feature = "arbitrary")]
mod test {
use ergotree_interpreter::sigma_protocol::prover::hint::{CommitmentHint, Hint, HintsBag};
use proptest::prelude::*;

use crate::wallet::TransactionHintsBag;
proptest! {
#[test]
fn test_segregate(hints in any::<HintsBag>()) {
let (secret, public) = TransactionHintsBag::segregate_hints(hints);
assert!(secret.hints.iter().all(|hint| matches!(hint, Hint::CommitmentHint(CommitmentHint::OwnCommitment(_)))));
assert!(public.hints.iter().all(|hint| !matches!(hint, Hint::CommitmentHint(CommitmentHint::OwnCommitment(_)))));
}
let hints_bag: HintsBag = HintsBag { hints };
hints_bag
}
}
5 changes: 4 additions & 1 deletion ergo-lib/src/wallet/mnemonic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ impl Mnemonic {
let mut seed: MnemonicSeed = [0u8; SHA512_OUTPUT_LEN];
let normalized_phrase = mnemonic_phrase.nfkd().collect::<String>();
let normalized_pass = mnemonic_pass.nfkd().collect::<String>();
#[allow(clippy::unwrap_used)]
// pbkdf2 only fails if the output size is not the same as the hash size
pbkdf2::<Hmac<Sha512>>(
normalized_phrase.as_bytes(),
format!("mnemonic{}", normalized_pass).as_bytes(),
Mnemonic::PBKDF2_ITERATIONS,
&mut seed,
);
)
.unwrap();

seed
}
Expand Down
Loading
Loading