diff --git a/packages/kos/src/chains/icp/mod.rs b/packages/kos/src/chains/icp/mod.rs index 0d0772a..a46a4ad 100644 --- a/packages/kos/src/chains/icp/mod.rs +++ b/packages/kos/src/chains/icp/mod.rs @@ -76,16 +76,51 @@ impl Chain for ICP { Ok(addr) } - fn sign_tx(&self, _private_key: Vec, _tx: Transaction) -> Result { - todo!() + fn sign_tx( + &self, + private_key: Vec, + mut tx: Transaction, + ) -> Result { + let hex = hex::decode(tx.raw_data.clone()).map_err(|_| ChainError::DecodeRawTx)?; + let raw_data_str = String::from_utf8(hex).map_err(|_| ChainError::DecodeRawTx)?; + + let icp_hashes: Vec = + serde_json::from_str(raw_data_str.as_str()).map_err(|_| ChainError::DecodeHash)?; + + let mut pvk_bytes = private_key_from_vec(&private_key)?; + + let mut signatures: Vec = Vec::new(); + + for hash_hex in icp_hashes { + let hash_bytes = hex::decode(&hash_hex).map_err(|_| ChainError::DecodeHash)?; + + let signature = Ed25519::sign(&pvk_bytes, &hash_bytes)?; + signatures.push(hex::encode(signature)); + } + + pvk_bytes.fill(0); + + let signatures_json = tiny_json_rs::encode(signatures); + + tx.signature = signatures_json.into_bytes(); + + if tx.tx_hash.is_empty() { + tx.tx_hash = Vec::new(); + } + + Ok(tx) } - fn sign_message( - &self, - _private_key: Vec, - _message: Vec, - ) -> Result, ChainError> { - todo!() + fn sign_message(&self, private_key: Vec, message: Vec) -> Result, ChainError> { + let public_key = self.get_pbk(private_key.clone())?; + + let signature = self.sign_raw(private_key, message)?; + + let mut signature_bytes = Vec::new(); + signature_bytes.extend_from_slice(&signature); + signature_bytes.extend_from_slice(&public_key); + + Ok(signature_bytes) } fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { @@ -148,8 +183,8 @@ pub fn crc_calc_singletable(buffer: &[u8]) -> u32 { #[cfg(test)] mod test { - use super::*; + use crate::crypto::base64::simple_base64_decode; #[test] fn test_icp_get_address() { let icp = ICP {}; @@ -166,4 +201,43 @@ mod test { "11d238129427ef0e44d86bd27cb6d9da4d7e8934cb0306a93a540e657082d885" ); } + + #[test] + fn test_icp_sign_tx() { + let icp = ICP {}; + + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let seed = icp.mnemonic_to_seed(mnemonic, "".to_string()).unwrap(); + let path = icp.get_path(0, false); + let pvk = icp.derive(seed, path).unwrap(); + + let raw_data = simple_base64_decode("NWIyMjMwNjEzNjM5MzYzMzMyNjQzNzMyMzYzNTM3MzEzNzM1MzYzNTM3MzMzNzM0MzUzMjMxMzc2NTM1MzYzNjM0Mzk2NjY2MzIzMTMyMzgzMzM2NjYzMDYxMzIzMjM2MzIzNjM3NjYzMzMwNjM2MTMwMzA2NTM4NjUzNDY2MzI2NTM4MzMzNDYyNjEzNjYzMzA2NDMyNjQzODMyMzQzMjM3MzIzNjM5MzAzNzMwMzAyMjJjMjIzMDYxMzYzOTM2MzMzMjY0MzczMjM2MzUzNzMxMzczNTM2MzUzNzMzMzczNDMxNjIzMTM0MzA2MjYyNjQzNzM2MzUzNzMxNjMzMDYxMzMzNDMzMzMzMTMzMzEzODM1MzQzMzM3NjI2NTM4NjIzODYyNjMzNDY0MzU2NTMyMzgzOTM4MzAzMTYxNjU2MTMxMzc2NTYxMzAzNjYxNjYzMjMwNjQzMTY1MzkzNzM1MjI1ZA==").unwrap(); + + let tx = Transaction { + raw_data, + signature: vec![], + tx_hash: vec![], + options: None, + }; + + let signed_tx = icp.sign_tx(pvk, tx).unwrap(); + + assert_eq!(signed_tx.signature.len(), 263); + } + + #[test] + fn test_icp_sign_message() { + let icp = ICP {}; + + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let seed = icp.mnemonic_to_seed(mnemonic, "".to_string()).unwrap(); + let path = icp.get_path(0, false); + + let pvk = icp.derive(seed, path).unwrap(); + + let message = "Hello, World!".as_bytes().to_vec(); + let signature = icp.sign_message(pvk, message).unwrap(); + + assert_eq!(signature.len(), 96, "Signature length should be 96"); + } } diff --git a/packages/kos/src/chains/mod.rs b/packages/kos/src/chains/mod.rs index 80143fe..d7bcd65 100644 --- a/packages/kos/src/chains/mod.rs +++ b/packages/kos/src/chains/mod.rs @@ -59,6 +59,8 @@ pub enum ChainError { MissingOptions, InvalidOptions, InvalidHex, + DecodeRawTx, + DecodeHash, } impl Display for ChainError { @@ -127,6 +129,12 @@ impl Display for ChainError { ChainError::InvalidHex => { write!(f, "invalid hex") } + ChainError::DecodeRawTx => { + write!(f, "decode raw tx") + } + ChainError::DecodeHash => { + write!(f, "decode hash") + } } } } @@ -218,6 +226,8 @@ impl ChainError { ChainError::MissingOptions => 21, ChainError::InvalidOptions => 22, ChainError::InvalidHex => 23, + ChainError::DecodeRawTx => 24, + ChainError::DecodeHash => 25, } } } @@ -525,7 +535,7 @@ impl ChainRegistry { constants::ICP, ChainInfo { factory: || Box::new(icp::ICP {}), - supported: false, + supported: true, }, ), (