Merge e5c00282fc2b8fbab563bac31cf352ea92b28ec0 into 7473c2f7a9ab8da65870442f0b90c96293982446

This commit is contained in:
jonesmarvin8 2026-04-09 03:30:02 +00:00 committed by GitHub
commit 1fa86a1d4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 166 additions and 40 deletions

View File

@ -11,11 +11,11 @@
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101",
"initial_accounts": [
{
"account_id": "CbgR6tj5kWx5oziiFptM7jMvrQeYY3Mzaao6ciuhSr2r",
"account_id": "jZvdpERLqEkzk6CAz6vDuDJ1wx5aoyFpDa1VFmRvuPX",
"balance": 10000
},
{
"account_id": "2RHZhw9h534Zr3eq2RGhQete2Hh667foECzXPmSkGni2",
"account_id": "3jQfsyRyvVpBfdkZegf8QpjfcDq1M5RAXB4H4eJ4kTtf",
"balance": 20000
}
],

View File

@ -5,6 +5,7 @@ use crate::key_management::key_tree::traits::KeyNode;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ChildKeysPublic {
pub cssk: nssa::PrivateKey,
pub csk: nssa::PrivateKey,
pub cpk: nssa::PublicKey,
pub ccc: [u8; 32],
@ -20,14 +21,14 @@ impl ChildKeysPublic {
// Non-harden.
// BIP-032 compatibility requires 1-byte header from the public_key;
// Not stored in `self.cpk.value()`.
let sk = k256::SecretKey::from_bytes(self.csk.value().into())
let sk = k256::SecretKey::from_bytes(self.cssk.value().into())
.expect("32 bytes, within curve order");
let pk = sk.public_key();
hash_input.extend_from_slice(pk.to_encoded_point(true).as_bytes());
} else {
// Harden.
hash_input.extend_from_slice(&[0_u8]);
hash_input.extend_from_slice(self.csk.value());
hash_input.extend_from_slice(self.cssk.value());
}
#[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")]
@ -41,16 +42,19 @@ impl KeyNode for ChildKeysPublic {
fn root(seed: [u8; 64]) -> Self {
let hash_value = hmac_sha512::HMAC::mac(seed, "LEE_master_pub");
let csk = nssa::PrivateKey::try_new(
let cssk = nssa::PrivateKey::try_new(
*hash_value
.first_chunk::<32>()
.expect("hash_value is 64 bytes, must be safe to get first 32"),
)
.expect("Expect a valid Private Key");
let csk = nssa::PrivateKey::tweak(cssk.value()).unwrap();
let ccc = *hash_value.last_chunk::<32>().unwrap();
let cpk = nssa::PublicKey::new_from_private_key(&csk);
Self {
cssk,
csk,
cpk,
ccc,
@ -61,21 +65,25 @@ impl KeyNode for ChildKeysPublic {
fn nth_child(&self, cci: u32) -> Self {
let hash_value = self.compute_hash_value(cci);
let csk = nssa::PrivateKey::try_new({
let hash_value = hash_value
.first_chunk::<32>()
.expect("hash_value is 64 bytes, must be safe to get first 32");
let value_1 =
k256::Scalar::from_repr((*hash_value).into()).expect("Expect a valid k256 scalar");
let value_2 = k256::Scalar::from_repr((*self.csk.value()).into())
.expect("Expect a valid k256 scalar");
let sum = value_1.add(&value_2);
sum.to_bytes().into()
})
let cssk = nssa::PrivateKey::try_new(
(k256::Scalar::from_repr(
(*hash_value
.first_chunk::<32>()
.expect("hash_value is 64 bytes, must be safe to get first 32"))
.into(),
)
.expect("Expect a valid k256 scalar"))
.add(
&k256::Scalar::from_repr((*self.cssk.value()).into())
.expect("Expect a valid k256 scalar"),
)
.to_bytes()
.into(),
)
.expect("Expect a valid private key");
let csk = nssa::PrivateKey::tweak(cssk.value()).expect("Expect a valid Private Key");
let ccc = *hash_value
.last_chunk::<32>()
.expect("hash_value is 64 bytes, must be safe to get last 32");
@ -83,6 +91,7 @@ impl KeyNode for ChildKeysPublic {
let cpk = nssa::PublicKey::new_from_private_key(&csk);
Self {
cssk,
csk,
cpk,
ccc,
@ -134,19 +143,26 @@ mod tests {
13, 146, 126, 232, 239, 113, 9, 194, 219, 190, 48, 187, 155,
];
let expected_csk: PrivateKey = PrivateKey::try_new([
let expected_cssk: PrivateKey = PrivateKey::try_new([
40, 35, 239, 19, 53, 178, 250, 55, 115, 12, 34, 3, 153, 153, 72, 170, 190, 36, 172, 36,
202, 148, 181, 228, 35, 222, 58, 84, 156, 24, 146, 86,
])
.unwrap();
let expected_csk: PrivateKey = PrivateKey::try_new([
207, 4, 246, 223, 104, 72, 19, 85, 14, 122, 194, 82, 32, 163, 60, 57, 8, 25, 209, 91,
254, 107, 76, 238, 31, 68, 236, 192, 154, 78, 105, 118,
])
.unwrap();
let expected_cpk: PublicKey = PublicKey::try_new([
219, 141, 130, 105, 11, 203, 187, 124, 112, 75, 223, 22, 11, 164, 153, 127, 59, 247,
244, 166, 75, 66, 242, 224, 35, 156, 161, 75, 41, 51, 76, 245,
188, 163, 203, 45, 151, 154, 230, 254, 123, 114, 158, 130, 19, 182, 164, 143, 150, 131,
176, 7, 27, 58, 204, 116, 5, 247, 0, 255, 111, 160, 52, 201,
])
.unwrap();
assert!(expected_ccc == keys.ccc);
assert!(expected_cssk == keys.cssk);
assert!(expected_csk == keys.csk);
assert!(expected_cpk == keys.cpk);
}
@ -168,19 +184,26 @@ mod tests {
130, 126, 123, 20, 90, 34, 173, 209, 101, 248, 155, 36,
];
let expected_csk: PrivateKey = PrivateKey::try_new([
let expected_cssk: PrivateKey = PrivateKey::try_new([
9, 65, 33, 228, 25, 82, 219, 117, 91, 217, 11, 223, 144, 85, 246, 26, 123, 216, 107,
213, 33, 52, 188, 22, 198, 246, 71, 46, 245, 174, 16, 47,
])
.unwrap();
let expected_csk: PrivateKey = PrivateKey::try_new([
100, 37, 212, 81, 40, 233, 72, 156, 177, 139, 50, 114, 136, 157, 202, 132, 203, 246,
252, 242, 13, 81, 42, 100, 159, 240, 187, 252, 202, 108, 25, 105,
])
.unwrap();
let expected_cpk: PublicKey = PublicKey::try_new([
142, 143, 238, 159, 105, 165, 224, 252, 108, 62, 53, 209, 176, 219, 249, 38, 90, 241,
201, 81, 194, 146, 236, 5, 83, 152, 238, 243, 138, 16, 229, 15,
210, 59, 119, 137, 21, 153, 82, 22, 195, 82, 12, 16, 80, 156, 125, 199, 19, 173, 46,
224, 213, 144, 165, 126, 70, 129, 171, 141, 77, 212, 108, 233,
])
.unwrap();
assert!(expected_ccc == child_keys.ccc);
assert!(expected_cssk == child_keys.cssk);
assert!(expected_csk == child_keys.csk);
assert!(expected_cpk == child_keys.cpk);
}
@ -202,19 +225,26 @@ mod tests {
16, 28, 79, 80, 232, 216, 101, 145, 19, 101, 220, 217, 141,
];
let expected_csk: PrivateKey = PrivateKey::try_new([
let expected_cssk: PrivateKey = PrivateKey::try_new([
185, 147, 32, 242, 145, 91, 123, 77, 42, 33, 134, 84, 12, 165, 117, 70, 158, 201, 95,
153, 14, 12, 92, 235, 128, 156, 194, 169, 68, 35, 165, 127,
])
.unwrap();
let expected_csk: PrivateKey = PrivateKey::try_new([
215, 157, 181, 165, 200, 92, 8, 103, 239, 104, 39, 41, 150, 199, 17, 205, 77, 179, 188,
27, 168, 216, 198, 12, 94, 11, 72, 131, 148, 44, 166, 128,
])
.unwrap();
let expected_cpk: PublicKey = PublicKey::try_new([
119, 16, 145, 121, 97, 244, 186, 35, 136, 34, 140, 171, 206, 139, 11, 208, 207, 121,
158, 45, 28, 22, 140, 98, 161, 179, 212, 173, 238, 220, 2, 34,
210, 66, 25, 100, 233, 50, 82, 94, 139, 83, 39, 52, 196, 241, 123, 248, 177, 10, 249,
206, 71, 167, 198, 5, 202, 184, 178, 148, 106, 231, 214, 235,
])
.unwrap();
assert!(expected_ccc == child_keys.ccc);
assert!(expected_cssk == child_keys.cssk);
assert!(expected_csk == child_keys.csk);
assert!(expected_cpk == child_keys.cpk);
}
@ -236,19 +266,26 @@ mod tests {
58, 215, 40, 246, 111, 166, 113, 183, 145, 173, 11, 27, 182,
];
let expected_csk: PrivateKey = PrivateKey::try_new([
let expected_cssk: PrivateKey = PrivateKey::try_new([
223, 29, 87, 189, 126, 24, 117, 225, 190, 57, 0, 143, 207, 168, 231, 139, 170, 192, 81,
254, 126, 10, 115, 42, 141, 157, 70, 171, 199, 231, 198, 132,
])
.unwrap();
let expected_csk: PrivateKey = PrivateKey::try_new([
35, 70, 190, 115, 134, 106, 151, 84, 164, 16, 139, 204, 100, 203, 36, 219, 91, 200, 6,
52, 120, 67, 35, 82, 14, 197, 163, 27, 248, 162, 129, 159,
])
.unwrap();
let expected_cpk: PublicKey = PublicKey::try_new([
96, 123, 245, 51, 214, 216, 215, 205, 70, 145, 105, 221, 166, 169, 122, 27, 94, 112,
228, 110, 249, 177, 85, 173, 180, 248, 185, 199, 112, 246, 83, 33,
61, 182, 68, 167, 177, 158, 173, 101, 79, 212, 191, 179, 169, 131, 220, 232, 123, 203,
235, 244, 72, 251, 159, 98, 215, 85, 103, 49, 124, 137, 98, 39,
])
.unwrap();
assert!(expected_ccc == child_keys.ccc);
assert!(expected_cssk == child_keys.cssk);
assert!(expected_csk == child_keys.csk);
assert!(expected_cpk == child_keys.cpk);
}

View File

@ -283,8 +283,8 @@ mod tests {
assert!(tree.key_map.contains_key(&ChainIndex::root()));
assert!(tree.account_id_map.contains_key(&AccountId::new([
172, 82, 222, 249, 164, 16, 148, 184, 219, 56, 92, 145, 203, 220, 251, 89, 214, 178,
38, 30, 108, 202, 251, 241, 148, 200, 125, 185, 93, 227, 189, 247
10, 231, 159, 65, 236, 46, 205, 5, 172, 89, 250, 29, 123, 195, 212, 137, 155, 111, 40,
120, 53, 28, 124, 54, 224, 170, 119, 208, 2, 72, 75, 50
])));
}

View File

@ -1,6 +1,8 @@
use std::str::FromStr;
use k256::elliptic_curve::{PrimeField as _, sec1::ToEncodedPoint as _};
use rand::{Rng as _, rngs::OsRng};
use risc0_zkvm::sha::{Impl, Sha256 as _};
use serde_with::{DeserializeFromStr, SerializeDisplay};
use crate::error::NssaError;
@ -60,6 +62,27 @@ impl PrivateKey {
pub const fn value(&self) -> &[u8; 32] {
&self.0
}
pub fn tweak(value: &[u8; 32]) -> Result<Self, NssaError> {
if !Self::is_valid_key(*value) {
return Err(NssaError::InvalidPrivateKey);
}
let sk = k256::SecretKey::from_slice(value).expect("Expect a valid secret key");
let hashed: [u8; 32] = Impl::hash_bytes(sk.public_key().to_encoded_point(true).as_bytes())
.as_bytes()
.try_into()
.expect("Sha256 outputs a 32-byte array");
let sk = sk.to_nonzero_scalar();
Self::try_new(
sk.add(&k256::Scalar::from_repr((hashed).into()).expect("Expect a valid k256 scalar"))
.to_bytes()
.into(),
)
}
}
#[cfg(test)]

View File

@ -18,11 +18,11 @@
"indexer_rpc_url": "ws://localhost:8779",
"initial_accounts": [
{
"account_id": "CbgR6tj5kWx5oziiFptM7jMvrQeYY3Mzaao6ciuhSr2r",
"account_id": "jZvdpERLqEkzk6CAz6vDuDJ1wx5aoyFpDa1VFmRvuPX",
"balance": 10000
},
{
"account_id": "2RHZhw9h534Zr3eq2RGhQete2Hh667foECzXPmSkGni2",
"account_id": "3jQfsyRyvVpBfdkZegf8QpjfcDq1M5RAXB4H4eJ4kTtf",
"balance": 20000
}
],

View File

@ -18,11 +18,11 @@
"indexer_rpc_url": "ws://localhost:8779",
"initial_accounts": [
{
"account_id": "CbgR6tj5kWx5oziiFptM7jMvrQeYY3Mzaao6ciuhSr2r",
"account_id": "jZvdpERLqEkzk6CAz6vDuDJ1wx5aoyFpDa1VFmRvuPX",
"balance": 10000
},
{
"account_id": "2RHZhw9h534Zr3eq2RGhQete2Hh667foECzXPmSkGni2",
"account_id": "3jQfsyRyvVpBfdkZegf8QpjfcDq1M5RAXB4H4eJ4kTtf",
"balance": 20000
}
],

View File

@ -7,14 +7,80 @@
"initial_accounts": [
{
"Public": {
"account_id": "CbgR6tj5kWx5oziiFptM7jMvrQeYY3Mzaao6ciuhSr2r",
"pub_sign_key": "7f273098f25b71e6c005a9519f2678da8d1c7f01f6a27778e2d9948abdf901fb"
"account_id": "jZvdpERLqEkzk6CAz6vDuDJ1wx5aoyFpDa1VFmRvuPX",
"pub_sign_key": [
157,
102,
173,
116,
76,
167,
130,
165,
77,
104,
14,
233,
114,
43,
180,
98,
59,
187,
165,
28,
80,
130,
126,
164,
224,
181,
203,
53,
31,
168,
169,
23
]
}
},
{
"Public": {
"account_id": "2RHZhw9h534Zr3eq2RGhQete2Hh667foECzXPmSkGni2",
"pub_sign_key": "f434f8741720014586ae43356d2aec6257da086222f604ddb75d69733b86fc4c"
"account_id": "3jQfsyRyvVpBfdkZegf8QpjfcDq1M5RAXB4H4eJ4kTtf",
"pub_sign_key": [
230,
17,
4,
52,
87,
162,
72,
137,
119,
205,
163,
211,
118,
157,
15,
164,
67,
12,
124,
50,
159,
23,
184,
6,
109,
154,
2,
219,
147,
239,
125,
20
]
}
},
{