From 68bdf5ccb55b6eb21b347359bace1ffa36eb9ffa Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Sun, 1 Dec 2024 08:55:34 +0500 Subject: [PATCH 1/5] Precompute DlogProverInput.public_image This avoids repeated multiplications by group element when signing --- Cargo.toml | 1 + ergo-chain-types/Cargo.toml | 9 +++- ergo-chain-types/src/ec_point.rs | 2 +- ergo-lib/src/wallet/deterministic.rs | 2 +- ergotree-interpreter/src/eval/sglobal.rs | 2 +- .../src/sigma_protocol/private_input.rs | 48 +++++++++++++------ 6 files changed, 45 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7c20d043f..6fbaed35f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,4 @@ + [workspace] resolver = "2" members = [ diff --git a/ergo-chain-types/Cargo.toml b/ergo-chain-types/Cargo.toml index e00b6f556..92675a06e 100644 --- a/ergo-chain-types/Cargo.toml +++ b/ergo-chain-types/Cargo.toml @@ -31,6 +31,13 @@ core2 = { workspace = true } default = ["std", "json"] arbitrary = ["proptest", "proptest-derive", "std"] json = ["serde", "serde_json", "serde_with"] -std = ["dep:url", "base16/std", "base64/std", "serde/std"] +std = [ + "dep:url", + "base16/std", + "base64/std", + "serde/std", + "k256/precomputed-tables", + "k256/std", +] [dev-dependencies] diff --git a/ergo-chain-types/src/ec_point.rs b/ergo-chain-types/src/ec_point.rs index ecc141474..abffb08da 100644 --- a/ergo-chain-types/src/ec_point.rs +++ b/ergo-chain-types/src/ec_point.rs @@ -11,7 +11,7 @@ use sigma_ser::vlq_encode::{ReadSigmaVlqExt, WriteSigmaVlqExt}; use sigma_ser::{ScorexParsingError, ScorexSerializable, ScorexSerializeResult}; /// Elliptic curve point -#[derive(PartialEq, Clone, Default, From, Into)] +#[derive(PartialEq, Clone, Copy, Default, From, Into)] #[cfg_attr( feature = "json", derive(serde::Serialize, serde::Deserialize), diff --git a/ergo-lib/src/wallet/deterministic.rs b/ergo-lib/src/wallet/deterministic.rs index ed9c46c08..bd64e2ecb 100644 --- a/ergo-lib/src/wallet/deterministic.rs +++ b/ergo-lib/src/wallet/deterministic.rs @@ -77,7 +77,7 @@ mod test { use crate::wallet::Wallet; fn gen_boxes() -> impl Strategy)> { any::() - .prop_map(|s| SecretKey::DlogSecretKey(DlogProverInput { w: s })) + .prop_map(|s| SecretKey::DlogSecretKey(DlogProverInput::new(s))) .prop_flat_map(|sk: SecretKey| { ( Just(sk.clone()), diff --git a/ergotree-interpreter/src/eval/sglobal.rs b/ergotree-interpreter/src/eval/sglobal.rs index f4af7d6fb..8be5ae527 100644 --- a/ergotree-interpreter/src/eval/sglobal.rs +++ b/ergotree-interpreter/src/eval/sglobal.rs @@ -413,7 +413,7 @@ mod tests { )) .unwrap(); let get_encoded = MethodCall::new( - Constant::from(ec_point.clone()).into(), + Constant::from(ec_point).into(), GET_ENCODED_METHOD.clone(), vec![], ) diff --git a/ergotree-interpreter/src/sigma_protocol/private_input.rs b/ergotree-interpreter/src/sigma_protocol/private_input.rs index 4b8ebed3d..100893863 100644 --- a/ergotree-interpreter/src/sigma_protocol/private_input.rs +++ b/ergotree-interpreter/src/sigma_protocol/private_input.rs @@ -3,6 +3,7 @@ use core::convert::TryInto; use core::fmt::Formatter; use alloc::vec::Vec; +use elliptic_curve::ops::MulByGenerator; use ergo_chain_types::EcPoint; use ergotree_ir::serialization::SigmaSerializable; use ergotree_ir::sigma_protocol::sigma_boolean::ProveDhTuple; @@ -13,6 +14,7 @@ use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean; extern crate derive_more; use derive_more::From; use k256::elliptic_curve::PrimeField; +use k256::ProjectivePoint; use num_bigint::BigUint; use num_traits::ToPrimitive; @@ -20,11 +22,12 @@ use super::wscalar::Wscalar; /// Secret key of discrete logarithm signature protocol #[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "json", serde(transparent))] -#[derive(PartialEq, Eq, Clone, derive_more::From)] +#[cfg_attr(feature = "json", serde(from = "Wscalar", into = "Wscalar"))] +#[derive(PartialEq, Eq, Clone)] pub struct DlogProverInput { /// secret key value pub w: Wscalar, + pk: EcPoint, } impl core::fmt::Debug for DlogProverInput { @@ -34,26 +37,46 @@ impl core::fmt::Debug for DlogProverInput { } } +impl From for DlogProverInput { + fn from(scalar: Wscalar) -> Self { + DlogProverInput::new(scalar) + } +} + +impl From for Wscalar { + fn from(prover_input: DlogProverInput) -> Self { + prover_input.w + } +} + impl DlogProverInput { /// Scalar(secret key) size in bytes pub const SIZE_BYTES: usize = 32; + /// Create new DlogProverInput + pub fn new(w: Wscalar) -> DlogProverInput { + Self { + pk: EcPoint::from(ProjectivePoint::mul_by_generator(w.as_scalar_ref())), + w, + } + } /// generates random secret in the range [0, n), where n is DLog group order. #[cfg(feature = "std")] pub fn random() -> DlogProverInput { - DlogProverInput { - w: ergotree_ir::sigma_protocol::dlog_group::random_scalar_in_group_range( - super::crypto_utils::secure_rng(), - ) - .into(), - } + use ergotree_ir::sigma_protocol::dlog_group; + + use crate::sigma_protocol::crypto_utils; + + DlogProverInput::new( + dlog_group::random_scalar_in_group_range(crypto_utils::secure_rng()).into(), + ) } /// Attempts to parse the given byte array as an SEC-1-encoded scalar(secret key). /// Returns None if the byte array does not contain a big-endian integer in the range [0, modulus). pub fn from_bytes(bytes: &[u8; DlogProverInput::SIZE_BYTES]) -> Option { k256::Scalar::from_repr((*bytes).into()) - .map(|s| DlogProverInput::from(Wscalar::from(s))) + .map(|s| DlogProverInput::new(Wscalar::from(s))) .into() } @@ -90,12 +113,7 @@ impl DlogProverInput { /// public key of discrete logarithm signature protocol pub fn public_image(&self) -> ProveDlog { - // test it, see https://github.com/ergoplatform/sigma-rust/issues/38 - let g = ergo_chain_types::ec_point::generator(); - ProveDlog::new(ergo_chain_types::ec_point::exponentiate( - &g, - self.w.as_scalar_ref(), - )) + ProveDlog::new(self.pk) } /// Return true if the secret is 0 From 3ea2d31a2ecda1f68064d5ef06d6abccd43c1eba Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Sun, 1 Dec 2024 11:44:58 +0500 Subject: [PATCH 2/5] Use pre-computed generator tables --- ergo-chain-types/src/ec_point.rs | 6 +++++ .../src/sigma_protocol/dlog_protocol.rs | 22 +++++++++---------- .../src/sigma_protocol/private_input.rs | 17 ++++++-------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/ergo-chain-types/src/ec_point.rs b/ergo-chain-types/src/ec_point.rs index abffb08da..2dfe623f3 100644 --- a/ergo-chain-types/src/ec_point.rs +++ b/ergo-chain-types/src/ec_point.rs @@ -4,6 +4,7 @@ use alloc::string::String; use core::convert::TryFrom; use core::ops::{Add, Mul, Neg}; use derive_more::{From, Into}; +use elliptic_curve::ops::MulByGenerator; use k256::elliptic_curve::group::prime::PrimeCurveAffine; use k256::elliptic_curve::sec1::ToEncodedPoint; use k256::{ProjectivePoint, PublicKey, Scalar}; @@ -117,6 +118,11 @@ pub fn exponentiate(base: &EcPoint, exponent: &Scalar) -> EcPoint { } } +/// Raise the generator g to the exponent. This is faster than exponentiate(&generator(), exponent) +pub fn exponentiate_gen(exponent: &Scalar) -> EcPoint { + ProjectivePoint::mul_by_generator(exponent).into() +} + impl ScorexSerializable for EcPoint { fn scorex_serialize(&self, w: &mut W) -> ScorexSerializeResult { let caff = self.0.to_affine(); diff --git a/ergotree-interpreter/src/sigma_protocol/dlog_protocol.rs b/ergotree-interpreter/src/sigma_protocol/dlog_protocol.rs index 55e02e87e..999736248 100644 --- a/ergotree-interpreter/src/sigma_protocol/dlog_protocol.rs +++ b/ergotree-interpreter/src/sigma_protocol/dlog_protocol.rs @@ -48,15 +48,15 @@ pub mod interactive_prover { use crate::sigma_protocol::{private_input::DlogProverInput, Challenge}; use blake2::Blake2b; use blake2::Digest; - use elliptic_curve::ops::MulByGenerator; + use ergo_chain_types::ec_point::exponentiate_gen; use ergo_chain_types::{ - ec_point::{exponentiate, generator, inverse}, + ec_point::{exponentiate, inverse}, EcPoint, }; use ergotree_ir::serialization::SigmaSerializable; use ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog; use k256::elliptic_curve::ops::Reduce; - use k256::{ProjectivePoint, Scalar}; + use k256::Scalar; /// Step 5 from /// For every leaf marked “simulated”, use the simulator of the sigma protocol for that leaf @@ -77,7 +77,7 @@ pub mod interactive_prover { let e: Scalar = challenge.clone().into(); let minus_e = e.negate(); let h_to_e = exponentiate(&public_input.h, &minus_e); - let g_to_z = exponentiate(&generator(), &z); + let g_to_z = exponentiate_gen(&z); let a = g_to_z * &h_to_e; ( FirstDlogProverMessage { a: a.into() }, @@ -91,11 +91,10 @@ pub mod interactive_prover { #[cfg(feature = "std")] pub fn first_message() -> (Wscalar, FirstDlogProverMessage) { use ergotree_ir::sigma_protocol::dlog_group; - let r = dlog_group::random_scalar_in_group_range( - crate::sigma_protocol::crypto_utils::secure_rng(), - ); - let g = generator(); - let a = exponentiate(&g, &r); + + use crate::sigma_protocol::crypto_utils; + let r = dlog_group::random_scalar_in_group_range(crypto_utils::secure_rng()); + let a = exponentiate_gen(&r); (r.into(), FirstDlogProverMessage { a: a.into() }) } @@ -137,7 +136,7 @@ pub mod interactive_prover { ( r.into(), FirstDlogProverMessage { - a: Box::new(ProjectivePoint::mul_by_generator(&r).into()), + a: Box::new(exponentiate_gen(&r)), }, ) } @@ -169,10 +168,9 @@ pub mod interactive_prover { challenge: &Challenge, second_message: &SecondDlogProverMessage, ) -> EcPoint { - let g = generator(); let h = *proposition.h.clone(); let e: Scalar = challenge.clone().into(); - let g_z = exponentiate(&g, second_message.z.as_scalar_ref()); + let g_z = exponentiate_gen(second_message.z.as_scalar_ref()); let h_e = exponentiate(&h, &e); g_z * &inverse(&h_e) } diff --git a/ergotree-interpreter/src/sigma_protocol/private_input.rs b/ergotree-interpreter/src/sigma_protocol/private_input.rs index 100893863..276a45d41 100644 --- a/ergotree-interpreter/src/sigma_protocol/private_input.rs +++ b/ergotree-interpreter/src/sigma_protocol/private_input.rs @@ -3,7 +3,7 @@ use core::convert::TryInto; use core::fmt::Formatter; use alloc::vec::Vec; -use elliptic_curve::ops::MulByGenerator; +use ergo_chain_types::ec_point::exponentiate_gen; use ergo_chain_types::EcPoint; use ergotree_ir::serialization::SigmaSerializable; use ergotree_ir::sigma_protocol::sigma_boolean::ProveDhTuple; @@ -14,7 +14,6 @@ use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean; extern crate derive_more; use derive_more::From; use k256::elliptic_curve::PrimeField; -use k256::ProjectivePoint; use num_bigint::BigUint; use num_traits::ToPrimitive; @@ -56,7 +55,7 @@ impl DlogProverInput { /// Create new DlogProverInput pub fn new(w: Wscalar) -> DlogProverInput { Self { - pk: EcPoint::from(ProjectivePoint::mul_by_generator(w.as_scalar_ref())), + pk: exponentiate_gen(w.as_scalar_ref()), w, } } @@ -154,15 +153,13 @@ impl DhTupleProverInput { pub fn random() -> DhTupleProverInput { use ergo_chain_types::ec_point::{exponentiate, generator}; use ergotree_ir::sigma_protocol::dlog_group; - let g = generator(); - let h = exponentiate( - &generator(), - &dlog_group::random_scalar_in_group_range(super::crypto_utils::secure_rng()), - ); + let h = exponentiate_gen(&dlog_group::random_scalar_in_group_range( + super::crypto_utils::secure_rng(), + )); let w = dlog_group::random_scalar_in_group_range(super::crypto_utils::secure_rng()); - let u = exponentiate(&g, &w); + let u = exponentiate_gen(&w); let v = exponentiate(&h, &w); - let common_input = ProveDhTuple::new(g, h, u, v); + let common_input = ProveDhTuple::new(generator(), h, u, v); DhTupleProverInput { w: w.into(), common_input, From d91ba4e762d1d6c09b1369893b15cb800e344496 Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Sun, 1 Dec 2024 13:38:25 +0500 Subject: [PATCH 3/5] Optimize From, distinct_token_ids --- bindings/ergo-lib-wasm/src/transaction.rs | 2 +- ergo-chain-types/src/ec_point.rs | 4 ++-- ergo-lib/src/chain/transaction.rs | 18 ++++++------------ ergo-lib/src/chain/transaction/unsigned.rs | 2 +- .../src/eval/create_provedlog.rs | 2 +- .../src/eval/multiply_group.rs | 4 ++-- ergotree-interpreter/src/eval/sgroup_elem.rs | 14 +++++--------- .../src/sigma_protocol/dht_protocol.rs | 6 +++--- .../src/sigma_protocol/dlog_protocol.rs | 8 ++++---- .../src/sigma_protocol/prover.rs | 5 ++--- .../src/sigma_protocol/verifier.rs | 2 +- .../src/sigma_protocol/wscalar.rs | 11 +++++------ ergotree-ir/src/mir/constant.rs | 2 +- ergotree-ir/src/mir/value.rs | 2 +- 14 files changed, 35 insertions(+), 47 deletions(-) diff --git a/bindings/ergo-lib-wasm/src/transaction.rs b/bindings/ergo-lib-wasm/src/transaction.rs index cdca918f1..ff67a95de 100644 --- a/bindings/ergo-lib-wasm/src/transaction.rs +++ b/bindings/ergo-lib-wasm/src/transaction.rs @@ -401,7 +401,7 @@ impl UnsignedTransaction { /// Returns distinct token id from output_candidates as array of byte arrays pub fn distinct_token_ids(&self) -> Vec { - distinct_token_ids(self.0.output_candidates.clone()) + distinct_token_ids(&self.0.output_candidates) .iter() .map(|id| Uint8Array::from(id.as_ref())) .collect() diff --git a/ergo-chain-types/src/ec_point.rs b/ergo-chain-types/src/ec_point.rs index 2dfe623f3..a94b98f02 100644 --- a/ergo-chain-types/src/ec_point.rs +++ b/ergo-chain-types/src/ec_point.rs @@ -105,7 +105,7 @@ pub fn is_identity(ge: &EcPoint) -> bool { /// Calculates the inverse of the given group element pub fn inverse(ec: &EcPoint) -> EcPoint { - -ec.clone() + -*ec } /// Raises the base GroupElement to the exponent. The result is another GroupElement. @@ -114,7 +114,7 @@ pub fn exponentiate(base: &EcPoint, exponent: &Scalar) -> EcPoint { // we treat EC as a multiplicative group, therefore, exponentiate point is multiply. EcPoint(base.0 * exponent) } else { - base.clone() + *base } } diff --git a/ergo-lib/src/chain/transaction.rs b/ergo-lib/src/chain/transaction.rs index 928a00f03..f328d6b82 100644 --- a/ergo-lib/src/chain/transaction.rs +++ b/ergo-lib/src/chain/transaction.rs @@ -233,20 +233,14 @@ pub enum TransactionSignatureVerificationError { } /// Returns distinct token ids from all given ErgoBoxCandidate's -pub fn distinct_token_ids(output_candidates: I) -> IndexSet +pub fn distinct_token_ids<'a, I>(output_candidates: I) -> IndexSet where - I: IntoIterator, + I: IntoIterator, { - let token_ids: Vec = output_candidates + let token_ids = output_candidates .into_iter() - .flat_map(|b| { - b.tokens - .into_iter() - .flatten() - .map(|t| t.token_id) - .collect::>() - }) - .collect(); + .flat_map(|b| b.tokens.iter().flatten().map(|t| t.token_id)); + IndexSet::<_>::from_iter(token_ids) } @@ -264,7 +258,7 @@ impl SigmaSerializable for Transaction { } // Serialize distinct ids of tokens in transaction outputs. - let distinct_token_ids = distinct_token_ids(self.output_candidates.clone()); + let distinct_token_ids = distinct_token_ids(&self.output_candidates); // Note that `self.output_candidates` is of type `TxIoVec` which has a max length of // `u16::MAX`. Therefore the following unwrap is safe. diff --git a/ergo-lib/src/chain/transaction/unsigned.rs b/ergo-lib/src/chain/transaction/unsigned.rs index e0b3e78c8..61912f76b 100644 --- a/ergo-lib/src/chain/transaction/unsigned.rs +++ b/ergo-lib/src/chain/transaction/unsigned.rs @@ -124,7 +124,7 @@ impl UnsignedTransaction { /// Returns distinct token ids from all output_candidates pub fn distinct_token_ids(&self) -> IndexSet { - distinct_token_ids(self.output_candidates.clone()) + distinct_token_ids(&self.output_candidates) } } diff --git a/ergotree-interpreter/src/eval/create_provedlog.rs b/ergotree-interpreter/src/eval/create_provedlog.rs index 15838791d..ca85b4523 100644 --- a/ergotree-interpreter/src/eval/create_provedlog.rs +++ b/ergotree-interpreter/src/eval/create_provedlog.rs @@ -16,7 +16,7 @@ impl Evaluable for CreateProveDlog { let value_v = self.input.eval(env, ctx)?; match value_v { Value::GroupElement(ecpoint) => { - let prove_dlog = ProveDlog::new((*ecpoint).clone()); + let prove_dlog = ProveDlog::new(*ecpoint); Ok(prove_dlog.into()) } _ => Err(EvalError::UnexpectedValue(format!( diff --git a/ergotree-interpreter/src/eval/multiply_group.rs b/ergotree-interpreter/src/eval/multiply_group.rs index aabab7bbe..4d201fc07 100644 --- a/ergotree-interpreter/src/eval/multiply_group.rs +++ b/ergotree-interpreter/src/eval/multiply_group.rs @@ -17,7 +17,7 @@ impl Evaluable for MultiplyGroup { match (&left_v, &right_v) { (Value::GroupElement(left), Value::GroupElement(right)) => { - Ok(((**left).clone() * right).into()) + Ok(((**left) * right).into()) } _ => Err(EvalError::UnexpectedValue(format!( "Expected MultiplyGroup input to be GroupElement, got: {0:?}", @@ -46,7 +46,7 @@ mod tests { #[test] fn eval_any(left in any::(), right in any::()) { - let expected_mul = left.clone() * &right; + let expected_mul = left * &right; let expr: Expr = MultiplyGroup { left: Box::new(Expr::Const(left.into())), diff --git a/ergotree-interpreter/src/eval/sgroup_elem.rs b/ergotree-interpreter/src/eval/sgroup_elem.rs index b8f633192..243cb78ef 100644 --- a/ergotree-interpreter/src/eval/sgroup_elem.rs +++ b/ergotree-interpreter/src/eval/sgroup_elem.rs @@ -22,7 +22,7 @@ pub(crate) static GET_ENCODED_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| { pub(crate) static NEGATE_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| { let negated: EcPoint = match obj { - Value::GroupElement(ec_point) => Ok(-(*ec_point).clone()), + Value::GroupElement(ec_point) => Ok(-(*ec_point)), _ => Err(EvalError::UnexpectedValue(format!( "expected obj to be Value::GroupElement, got: {0:?}", obj @@ -49,7 +49,7 @@ mod tests { fn eval_get_encoded() { let input = force_any_val::(); let expr: Expr = MethodCall::new( - input.clone().into(), + input.into(), sgroup_elem::GET_ENCODED_METHOD.clone(), vec![], ) @@ -66,13 +66,9 @@ mod tests { #[test] fn eval_negate() { let input = force_any_val::(); - let expr: Expr = MethodCall::new( - input.clone().into(), - sgroup_elem::NEGATE_METHOD.clone(), - vec![], - ) - .unwrap() - .into(); + let expr: Expr = MethodCall::new(input.into(), sgroup_elem::NEGATE_METHOD.clone(), vec![]) + .unwrap() + .into(); assert_eq!(-input, eval_out_wo_ctx::(&expr)) } } diff --git a/ergotree-interpreter/src/sigma_protocol/dht_protocol.rs b/ergotree-interpreter/src/sigma_protocol/dht_protocol.rs index fefcf236b..182aebd3e 100644 --- a/ergotree-interpreter/src/sigma_protocol/dht_protocol.rs +++ b/ergotree-interpreter/src/sigma_protocol/dht_protocol.rs @@ -74,7 +74,7 @@ pub mod interactive_prover { ); // COMPUTE a = g^z*u^(-e) and b = h^z*v^{-e} (where -e here means -e mod q) - let e: Scalar = challenge.clone().into(); + let e: Scalar = challenge.into(); let minus_e = e.negate(); let h_to_z = exponentiate(&public_input.h, &z); let g_to_z = exponentiate(&public_input.g, &z); @@ -114,7 +114,7 @@ pub mod interactive_prover { rnd: &Wscalar, challenge: &Challenge, ) -> SecondDhTupleProverMessage { - let e: Scalar = challenge.clone().into(); + let e: Scalar = challenge.into(); // modulo multiplication, no need to explicit mod op let ew = e.mul(private_input.w.as_scalar_ref()); // modulo addition, no need to explicit mod op @@ -141,7 +141,7 @@ pub mod interactive_prover { let z = second_message.z.clone(); - let e: Scalar = challenge.clone().into(); + let e: Scalar = challenge.into(); use ergo_chain_types::ec_point::{exponentiate, inverse}; diff --git a/ergotree-interpreter/src/sigma_protocol/dlog_protocol.rs b/ergotree-interpreter/src/sigma_protocol/dlog_protocol.rs index 999736248..f2e03d807 100644 --- a/ergotree-interpreter/src/sigma_protocol/dlog_protocol.rs +++ b/ergotree-interpreter/src/sigma_protocol/dlog_protocol.rs @@ -74,7 +74,7 @@ pub mod interactive_prover { ); //COMPUTE a = g^z*h^(-e) (where -e here means -e mod q) - let e: Scalar = challenge.clone().into(); + let e: Scalar = challenge.into(); let minus_e = e.negate(); let h_to_e = exponentiate(&public_input.h, &minus_e); let g_to_z = exponentiate_gen(&z); @@ -150,7 +150,7 @@ pub mod interactive_prover { rnd: Wscalar, challenge: &Challenge, ) -> SecondDlogProverMessage { - let e: Scalar = challenge.clone().into(); + let e: Scalar = challenge.into(); // modulo multiplication, no need to explicit mod op let ew = e.mul(private_input.w.as_scalar_ref()); // modulo addition, no need to explicit mod op @@ -168,8 +168,8 @@ pub mod interactive_prover { challenge: &Challenge, second_message: &SecondDlogProverMessage, ) -> EcPoint { - let h = *proposition.h.clone(); - let e: Scalar = challenge.clone().into(); + let h = *proposition.h; + let e: Scalar = challenge.into(); let g_z = exponentiate_gen(second_message.z.as_scalar_ref()); let h_e = exponentiate(&h, &e); g_z * &inverse(&h_e) diff --git a/ergotree-interpreter/src/sigma_protocol/prover.rs b/ergotree-interpreter/src/sigma_protocol/prover.rs index 7a3193131..89a6aae87 100644 --- a/ergotree-interpreter/src/sigma_protocol/prover.rs +++ b/ergotree-interpreter/src/sigma_protocol/prover.rs @@ -216,12 +216,11 @@ fn prove_to_unchecked( // Prover Steps 7: convert the relevant information in the tree (namely, tree structure, node types, // the statements being proven and commitments at the leaves) // to a string - let var_name = fiat_shamir_tree_to_bytes(&step6.clone().into())?; - let mut s = var_name; + let mut s = fiat_shamir_tree_to_bytes(&step6.clone().into())?; // Prover Step 8: compute the challenge for the root of the tree as the Fiat-Shamir hash of s // and the message being signed. - s.append(&mut message.to_vec()); + s.extend_from_slice(message); let root_challenge: Challenge = fiat_shamir_hash_fn(s.as_slice()).into(); let step8 = step6.with_challenge(root_challenge); // dbg!(&step8); diff --git a/ergotree-interpreter/src/sigma_protocol/verifier.rs b/ergotree-interpreter/src/sigma_protocol/verifier.rs index 64202fcc6..46802fbbe 100644 --- a/ergotree-interpreter/src/sigma_protocol/verifier.rs +++ b/ergotree-interpreter/src/sigma_protocol/verifier.rs @@ -115,7 +115,7 @@ fn check_commitments(sp: UncheckedTree, message: &[u8]) -> Result for Wscalar { } } -impl From for Scalar { - fn from(v: Challenge) -> Self { - let v: [u8; SOUNDNESS_BYTES] = v.0.into(); - // prepend zeroes to 32 bytes (big-endian) - let mut prefix = vec![0u8; 8]; - prefix.append(&mut v.to_vec()); +impl From<&Challenge> for Scalar { + fn from(v: &Challenge) -> Self { + let v: [u8; SOUNDNESS_BYTES] = *v.0 .0; + let mut prefix = [0u8; 32]; + prefix[32 - SOUNDNESS_BYTES..].copy_from_slice(&v); >::reduce_bytes(&GenericArray::clone_from_slice(&prefix)) } } diff --git a/ergotree-ir/src/mir/constant.rs b/ergotree-ir/src/mir/constant.rs index f07107bbd..4f98c31c0 100644 --- a/ergotree-ir/src/mir/constant.rs +++ b/ergotree-ir/src/mir/constant.rs @@ -705,7 +705,7 @@ impl TryExtractFrom for i64 { impl TryExtractFrom for EcPoint { fn try_extract_from(cv: Literal) -> Result { match cv { - Literal::GroupElement(v) => Ok((*v).clone()), + Literal::GroupElement(v) => Ok(*v), _ => Err(TryExtractFromError(format!( "expected EcPoint, found {:?}", cv diff --git a/ergotree-ir/src/mir/value.rs b/ergotree-ir/src/mir/value.rs index 9991bb703..843bc2f86 100644 --- a/ergotree-ir/src/mir/value.rs +++ b/ergotree-ir/src/mir/value.rs @@ -495,7 +495,7 @@ impl TryExtractFrom> for i64 { impl TryExtractFrom> for EcPoint { fn try_extract_from(cv: Value) -> Result { match cv { - Value::GroupElement(v) => Ok((*v).clone()), + Value::GroupElement(v) => Ok(*v), _ => Err(TryExtractFromError(format!( "expected EcPoint, found {:?}", cv From c3945d9f6833780f578d78619bba995800f40e7d Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Wed, 4 Dec 2024 15:45:10 +0500 Subject: [PATCH 4/5] Use Wscalar instead of DlogProverInput in ExtSecretKey Now that DlogProverInput eagerly evaluates public key, it's a bad fit for ExtSecretKey since it causes unnecessary EC operations --- Cargo.toml | 1 - ergo-lib/src/wallet/ext_pub_key.rs | 3 +- ergo-lib/src/wallet/ext_secret_key.rs | 48 ++++++++++--------- .../src/sigma_protocol/private_input.rs | 4 +- .../src/sigma_protocol/wscalar.rs | 21 ++++++++ ergotree-ir/src/chain/address.rs | 4 +- 6 files changed, 53 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6fbaed35f..7c20d043f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,3 @@ - [workspace] resolver = "2" members = [ diff --git a/ergo-lib/src/wallet/ext_pub_key.rs b/ergo-lib/src/wallet/ext_pub_key.rs index cec8b3710..2c271c451 100644 --- a/ergo-lib/src/wallet/ext_pub_key.rs +++ b/ergo-lib/src/wallet/ext_pub_key.rs @@ -28,7 +28,8 @@ type HmacSha512 = Hmac; pub struct ExtPubKey { /// Parsed public key (EcPoint) pub public_key: EcPoint, - chain_code: ChainCode, + /// Chain code bytes + pub chain_code: ChainCode, /// Derivation path for this extended public key pub derivation_path: DerivationPath, } diff --git a/ergo-lib/src/wallet/ext_secret_key.rs b/ergo-lib/src/wallet/ext_secret_key.rs index 16bfaaa8e..cb2df6310 100644 --- a/ergo-lib/src/wallet/ext_secret_key.rs +++ b/ergo-lib/src/wallet/ext_secret_key.rs @@ -1,6 +1,4 @@ //! Extended private key operations according to BIP-32 -use core::convert::TryInto; - use super::{ derivation_path::{ChildIndex, ChildIndexError, DerivationPath}, ext_pub_key::ExtPubKey, @@ -28,14 +26,23 @@ type HmacSha512 = Hmac; /// Extended secret key /// implemented according to BIP-32 -#[derive(PartialEq, Eq, Debug, Clone)] +#[derive(PartialEq, Eq, Clone)] pub struct ExtSecretKey { - /// The secret key - private_input: DlogProverInput, + private_input: Wscalar, chain_code: ChainCode, derivation_path: DerivationPath, } +impl core::fmt::Debug for ExtSecretKey { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("ExtSecretKey") + .field("private_input", &"*****") // disable debug output for secret key to prevent key leakage in logs + .field("chain_code", &self.chain_code) + .field("derivation_path", &self.derivation_path) + .finish() + } +} + /// Extended secret key errors #[derive(Error, PartialEq, Eq, Debug, Clone)] pub enum ExtSecretKeyError { @@ -66,8 +73,8 @@ impl ExtSecretKey { chain_code: ChainCode, derivation_path: DerivationPath, ) -> Result { - let private_input = DlogProverInput::from_bytes(&secret_key_bytes) - .ok_or(ExtSecretKeyError::ScalarEncodingError)?; + let private_input = + Wscalar::from_bytes(&secret_key_bytes).ok_or(ExtSecretKeyError::ScalarEncodingError)?; Ok(Self { private_input, chain_code, @@ -82,7 +89,7 @@ impl ExtSecretKey { /// Returns secret key pub fn secret_key(&self) -> SecretKey { - self.private_input.clone().into() + DlogProverInput::new(self.private_input.clone()).into() } /// Byte representation of the underlying scalar @@ -92,7 +99,7 @@ impl ExtSecretKey { /// Public image associated with the private input pub fn public_image(&self) -> ProveDlog { - self.private_input.public_image() + DlogProverInput::new(self.private_input.clone()).public_image() } /// Public image bytes in SEC-1 encoded & compressed format @@ -103,12 +110,11 @@ impl ExtSecretKey { /// The extended public key associated with this secret key pub fn public_key(&self) -> Result { #[allow(clippy::unwrap_used)] - Ok(ExtPubKey::new( - // unwrap is safe as it is used on an Infallible result type - self.public_image_bytes()?.try_into().unwrap(), - self.chain_code, - self.derivation_path.clone(), - )?) + Ok(ExtPubKey { + public_key: *self.public_image().h, + chain_code: self.chain_code, + derivation_path: self.derivation_path.clone(), + }) } /// Derive a child extended secret key using the provided index @@ -127,16 +133,14 @@ impl ExtSecretKey { let mac_bytes = mac.finalize().into_bytes(); let mut secret_key_bytes = [0; SecretKeyBytes::LEN]; secret_key_bytes.copy_from_slice(&mac_bytes[..32]); - if let Some(dlog_prover) = DlogProverInput::from_bytes(&secret_key_bytes) { + if let Some(wscalar) = Wscalar::from_bytes(&secret_key_bytes) { // parse256(IL) + kpar (mod n). // via https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions - let child_secret_key: DlogProverInput = Wscalar::from( - dlog_prover - .w + let child_secret_key = Wscalar::from( + wscalar .as_scalar_ref() - .add(self.private_input.w.as_scalar_ref()), - ) - .into(); + .add(self.private_input.as_scalar_ref()), + ); if child_secret_key.is_zero() { // ki == 0 case of: // > In case parse256(IL) ≥ n or ki = 0, the resulting key is invalid, and one diff --git a/ergotree-interpreter/src/sigma_protocol/private_input.rs b/ergotree-interpreter/src/sigma_protocol/private_input.rs index 276a45d41..78be43bda 100644 --- a/ergotree-interpreter/src/sigma_protocol/private_input.rs +++ b/ergotree-interpreter/src/sigma_protocol/private_input.rs @@ -107,7 +107,7 @@ impl DlogProverInput { /// byte representation of the underlying scalar pub fn to_bytes(&self) -> [u8; DlogProverInput::SIZE_BYTES] { - self.w.as_scalar_ref().to_bytes().into() + self.w.to_bytes() } /// public key of discrete logarithm signature protocol @@ -117,7 +117,7 @@ impl DlogProverInput { /// Return true if the secret is 0 pub fn is_zero(&self) -> bool { - self.w.as_scalar_ref().is_zero().into() + self.w.is_zero() } } diff --git a/ergotree-interpreter/src/sigma_protocol/wscalar.rs b/ergotree-interpreter/src/sigma_protocol/wscalar.rs index e960e1882..ae9d69e7e 100644 --- a/ergotree-interpreter/src/sigma_protocol/wscalar.rs +++ b/ergotree-interpreter/src/sigma_protocol/wscalar.rs @@ -7,6 +7,7 @@ use core::fmt::Formatter; use derive_more::From; use derive_more::Into; +use elliptic_curve::PrimeField; use ergo_chain_types::Base16DecodedBytes; use ergo_chain_types::Base16EncodedBytes; use k256::elliptic_curve::generic_array::GenericArray; @@ -31,10 +32,30 @@ use super::SOUNDNESS_BYTES; pub struct Wscalar(Scalar); impl Wscalar { + /// Scalar(secret key) size in bytes + pub const SIZE_BYTES: usize = 32; + /// Returns a reference to underlying Scalar pub fn as_scalar_ref(&self) -> &Scalar { &self.0 } + + /// Attempts to parse the given byte array as an SEC-1-encoded scalar(secret key). + /// Returns None if the byte array does not contain a big-endian integer in the range [0, modulus). + pub fn from_bytes(bytes: &[u8; Self::SIZE_BYTES]) -> Option { + k256::Scalar::from_repr((*bytes).into()) + .map(Wscalar::from) + .into() + } + + /// Convert scalar to big-endian byte representation + pub fn to_bytes(&self) -> [u8; Self::SIZE_BYTES] { + self.0.to_bytes().into() + } + /// Return true if the scalar is 0 + pub fn is_zero(&self) -> bool { + self.0.is_zero().into() + } } impl From for Wscalar { diff --git a/ergotree-ir/src/chain/address.rs b/ergotree-ir/src/chain/address.rs index 0c2c33fdf..f8fa60012 100644 --- a/ergotree-ir/src/chain/address.rs +++ b/ergotree-ir/src/chain/address.rs @@ -542,8 +542,8 @@ impl AddressEncoder { let mut address_bytes = address.content_bytes(); let mut bytes = vec![prefix_byte]; bytes.append(&mut address_bytes); - let mut calculated_checksum = AddressEncoder::calc_checksum(&bytes[..]).to_vec(); - bytes.append(&mut calculated_checksum); + let calculated_checksum = AddressEncoder::calc_checksum(&bytes[..]); + bytes.extend_from_slice(&calculated_checksum); bytes } From 5b13f5e1e0350a073122dd4ad496677f5537a14f Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Thu, 5 Dec 2024 11:03:02 +0500 Subject: [PATCH 5/5] CI: change jni runner to ubuntu-latest --- .github/workflows/ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 918ba2d32..9ddb06b23 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -253,10 +253,15 @@ jobs: android_tests: name: Test JNI(Android) bindings - runs-on: macos-12 + runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v2 + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm - uses: actions-rs/toolchain@v1 with: