Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/transaction from raw #65

Merged
merged 17 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 16 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
58 changes: 56 additions & 2 deletions packages/kos-mobile/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use hex::FromHexError;
use hex::ToHex;

use kos_crypto::cipher;
use kos_crypto::cipher::CipherAlgo;
use kos_sdk::chain::Chain;
use kos_sdk::models::PathOptions;
use kos_sdk::models::{PathOptions, Transaction};
use kos_sdk::wallet::Wallet;
use kos_types::error::Error as KosError;

Expand Down Expand Up @@ -41,6 +40,32 @@ struct KOSAccount {
pub path: String,
}

#[derive(uniffi::Record)]
struct KOSTransaction {
pub chain_id: i32,
pub raw: String,
pub sender: String,
pub signature: String,
}
pedrxlz marked this conversation as resolved.
Show resolved Hide resolved

#[uniffi::export]
fn sign_transaction(account: KOSAccount, raw: String) -> Result<KOSTransaction, KOSError> {
let chain = get_chain_by(account.chain_id)?;
let wallet = Wallet::from_private_key(chain, account.private_key.to_string())?;
let transaction = Transaction::from_raw(chain, &raw)?;
let signed_transaction = wallet.sign(transaction)?;
let signature = signed_transaction
.get_signature()
.ok_or(KOSError::KOSDelegate("Signature not found".to_string()))?;

Ok(KOSTransaction {
chain_id: account.chain_id,
raw: signed_transaction.get_raw()?,
sender: signed_transaction.sender,
signature,
})
}

#[uniffi::export]
fn generate_mnemonic(size: i32) -> Result<String, KOSError> {
Ok(kos_crypto::mnemonic::generate_mnemonic(size as usize)?.to_phrase())
Expand Down Expand Up @@ -319,4 +344,33 @@ mod tests {
Err(e) => assert!(matches!(e, KOSError::KOSDelegate(..)), "Invalid error"),
}
}

// Transaction from raw
#[test]
fn should_sign_raw_transaction() {
let chain_id = 38;

let raw = "{\"RawData\":{\"BandwidthFee\":1000000,\"ChainID\":\"MTAwNDIw\",\"Contract\":[{\"Parameter\":{\"type_url\":\"type.googleapis.com/proto.TransferContract\",\"value\":\"CiAysyg0Aj8xj/rr5XGU6iJ+ATI29mnRHS0W0BrC1vz0CBgK\"}}],\"KAppFee\":500000,\"Nonce\":39,\"Sender\":\"5BsyOlcf2VXgnNQWYP9EZcP0RpPIfy+upKD8QIcnyOo=\",\"Version\":1}}";

let account = generate_wallet_from_mnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(),
chain_id,
0,
false
).unwrap();

let transaction = sign_transaction(account, raw.to_string()).unwrap();

println!("raw: {:?}", transaction.raw);

assert_eq!(transaction.chain_id, chain_id, "The chain_id doesn't match");
assert_eq!(
transaction.sender, "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy",
"The sender doesn't match"
);
assert_eq!(
transaction.raw, "{\"RawData\":{\"Nonce\":39,\"Sender\":\"5BsyOlcf2VXgnNQWYP9EZcP0RpPIfy+upKD8QIcnyOo=\",\"Contract\":[{\"Parameter\":{\"typeUrl\":\"type.googleapis.com/proto.TransferContract\",\"value\":\"CiAysyg0Aj8xj/rr5XGU6iJ+ATI29mnRHS0W0BrC1vz0CBgK\"}}],\"KAppFee\":500000,\"BandwidthFee\":1000000,\"Version\":1,\"ChainID\":\"MTAwNDIw\"},\"Signature\":[\"gUZDIPSxSq40QjTBM38/DAAuWTm7D1THo2KWVqhiTYCum5O+OSWwTYlgIU0RgJ6ungg1cuCJPcmYWNgjDKA/DA==\"]}",
"The raw doesn't match"
);
}
pedrxlz marked this conversation as resolved.
Show resolved Hide resolved
}
4 changes: 4 additions & 0 deletions packages/kos-sdk/src/chains/bitcoin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,12 @@ impl BTC {
// redeem script
btc_tx.finalize()?;

let signature = btc_tx.get_signature().unwrap_or("".to_string());

Ok(Transaction {
hash: btc_tx.txid_hash()?,
data: Some(TransactionRaw::Bitcoin(btc_tx)),
signature: Some(signature),
..tx
})
}
Expand Down Expand Up @@ -267,6 +270,7 @@ impl BTC {
sender,
hash: Hash::new(&tx.txid().to_string())?,
data: Some(TransactionRaw::Bitcoin(tx)),
signature: None,
})
}

Expand Down
5 changes: 5 additions & 0 deletions packages/kos-sdk/src/chains/bitcoin/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ impl BTCTransaction {
bitcoin::consensus::encode::serialize_hex(&self.tx.clone().extract_tx())
}

pub fn get_signature(&self) -> Result<String, Error> {
let sig = self.tx.clone().extract_tx().wtxid();
Ok(sig.to_string())
}

pub fn finalize(&mut self) -> Result<(), Error> {
for inp_idx in 0..self.tx.inputs.len() {
// todo!("multi sig and redeem type");
Expand Down
9 changes: 9 additions & 0 deletions packages/kos-sdk/src/chains/ethereum/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ impl ETH {
sender: tx.sender,
hash: Hash::from_vec(new_hash)?,
data: Some(TransactionRaw::Ethereum(new_tx)),
signature: Some(hex::encode(sig)),
};

Ok(result)
Expand Down Expand Up @@ -284,6 +285,7 @@ impl ETH {
sender,
hash: Hash::from_vec(digest)?,
data: Some(TransactionRaw::Ethereum(tx)),
signature: None,
})
}

Expand Down Expand Up @@ -444,12 +446,16 @@ impl ETH {
}
};

let signature = tx.signature.map(|sig| sig.to_standard().to_string());

let digest = hash_transaction(&tx)?;

Ok(crate::models::Transaction {
chain: chain::Chain::ETH,
sender: "".to_string(), //TODO: implement sender on eth decode
hash: Hash::from_vec(digest)?,
data: Some(TransactionRaw::Ethereum(tx)),
signature,
})
}

Expand All @@ -465,11 +471,14 @@ impl ETH {
None => "".to_string(),
};

let signature = tx.signature.map(|sig| sig.to_standard().to_string());

Ok(crate::models::Transaction {
chain: chain::Chain::ETH,
sender,
hash: Hash::from_vec(digest)?,
data: Some(TransactionRaw::Ethereum(tx)),
signature,
})
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/kos-sdk/src/chains/klever/klever_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ mod klever_test {
hash: Hash::new("0x0000000000000000000000000000000000000000000000000000000000000000")
.unwrap(),
data: Some(TransactionRaw::Klever(klv_tx)),
signature: None,
};

let result = tokio_test::block_on(KLV::broadcast(to_broadcast, None));
Expand Down
7 changes: 7 additions & 0 deletions packages/kos-sdk/src/chains/klever/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
use kos_crypto::{ed25519::Ed25519KeyPair, keypair::KeyPair};
use kos_types::{error::Error, hash::Hash, number::BigNumber};

use pbjson::private::base64;
use std::str::FromStr;
use wasm_bindgen::prelude::*;

Expand Down Expand Up @@ -102,12 +103,15 @@ impl KLV {
let digest = KLV::hash_transaction(&klv_tx)?;
let sig = KLV::sign_digest(digest.as_slice(), keypair)?;

let signature = base64::encode(sig.clone());

new_tx.signature.push(sig);
let result = Transaction {
chain: tx.chain,
sender: tx.sender,
hash: Hash::from_vec(digest)?,
data: Some(TransactionRaw::Klever(new_tx)),
signature: Some(signature),
};

Ok(result)
Expand Down Expand Up @@ -210,6 +214,7 @@ impl KLV {
sender: tx.sender,
hash: tx_hash,
data: tx.data,
signature: None,
}))
}
None => match result.get("error") {
Expand Down Expand Up @@ -282,12 +287,14 @@ impl KLV {

let sender = address::Address::from_bytes(&data.sender);
let digest = KLV::hash_transaction(&tx)?;
let signature = tx.signature.first().map(|sig| base64::encode(sig.clone()));

Ok(crate::models::Transaction {
chain: crate::chain::Chain::KLV,
sender: sender.to_string(),
hash: Hash::from_slice(&digest)?,
data: Some(TransactionRaw::Klever(tx)),
signature,
})
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/kos-sdk/src/chains/klever/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,6 @@ pub async fn make_request(
sender,
hash: Hash::new(&tx.tx_hash)?,
data: Some(TransactionRaw::Klever(tx.tx)),
signature: None,
})
}
51 changes: 51 additions & 0 deletions packages/kos-sdk/src/chains/tron/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,15 @@ impl TRX {
let mut new_tx = trx_tx.clone();
let digest = TRX::hash_transaction(&trx_tx)?;
let sig = TRX::sign_digest(digest.as_slice(), keypair)?;
let hex_sig = hex::encode(sig.clone());

new_tx.signature.push(sig);
let result = Transaction {
chain: tx.chain,
sender: tx.sender,
hash: Hash::from_vec(digest)?,
data: Some(TransactionRaw::Tron(new_tx)),
signature: Some(hex_sig),
};

Ok(result)
Expand Down Expand Up @@ -328,6 +330,7 @@ impl TRX {
sender,
hash: Hash::from_vec(digest)?,
data: Some(TransactionRaw::Tron(tx)),
signature: None,
})
}

Expand Down Expand Up @@ -361,6 +364,7 @@ impl TRX {
sender: tx.sender,
hash: tx.hash,
data: tx.data,
signature: tx.signature,
}))
}

Expand Down Expand Up @@ -405,6 +409,39 @@ impl TRX {
let bytes = kos_proto::write_message(&raw_data);
Ok(hex::encode(bytes))
}

pub fn tx_from_raw(data: &str) -> Result<Transaction, Error> {
let raw_data_bytes = hex::decode(data)?;
let raw_data: kos_proto::tron::transaction::Raw =
kos_proto::from_bytes(raw_data_bytes).unwrap();

let parameter: kos_proto::tron::TransferContract =
kos_proto::unpack_from_option_any(&raw_data.contract[0].parameter).ok_or(
Error::InvalidTransaction("Failed to unpack transfer contract".to_string()),
)?;

let tx = kos_proto::tron::Transaction {
raw_data: Some(raw_data),
signature: Vec::new(),
ret: Vec::new(),
};

let hash = Hash::from_vec(TRX::hash_transaction(&tx)?)?;

let sender = address::Address::from_bytes(parameter.owner_address.as_slice()).to_string();

let binding = tx.clone();
let sig = binding.signature.first();
let signature = sig.map(hex::encode);

Ok(Transaction {
chain: chain::Chain::TRX,
sender,
hash,
data: Some(TransactionRaw::Tron(tx)),
signature,
})
}
}

#[cfg(test)]
Expand Down Expand Up @@ -630,4 +667,18 @@ mod tests {
let result = TRX::serialize_raw_data_into_hex_string(invalid_tx);
assert!(result.is_err());
}

#[test]
fn test_tx_from_raw() {
let tx = "0a02d8372208e9c73b516bcd78844088c6e8ad9a325a67080112630a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412320a1541e825d52582eec346c839b4875376117904a76cbc12154120ab1300cf70c048e4cf5d5b1b33f59653ed662618c0843d70fdfee4ad9a32";
let result = TRX::tx_from_raw(tx);
assert!(result.is_ok());
let t = result.unwrap();
assert_eq!(t.chain, chain::Chain::TRX);
assert_eq!(t.sender, "TX8h6Df74VpJsXF6sTDz1QJsq3Ec8dABc3");
assert_eq!(
t.hash.to_string(),
"3e6c463b2e88d78e912dee3aa31a14aa71c0e56bbdfbdbba012c8af4e45b8834"
);
}
}
32 changes: 28 additions & 4 deletions packages/kos-sdk/src/models.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// use crate::klever;
use crate::chain::Chain;
use crate::chains::{ETH, KLV, TRX};
use kos_types::{error::Error, hash::Hash};
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;

use crate::chain::Chain;

#[derive(Debug, Clone, Serialize)]
#[wasm_bindgen]
pub struct BroadcastResult {
Expand Down Expand Up @@ -149,6 +148,8 @@ pub struct Transaction {
pub hash: Hash,
#[wasm_bindgen(skip)]
pub data: Option<TransactionRaw>,
#[wasm_bindgen(skip)]
pub signature: Option<String>,
}

#[wasm_bindgen]
Expand Down Expand Up @@ -184,8 +185,12 @@ impl Transaction {
None => Err(Error::InvalidTransaction("no data found".to_string())),
}
}
}

#[wasm_bindgen(js_name = getSignature)]
pub fn get_signature(&self) -> Option<String> {
self.signature.clone()
}
}
impl Transaction {
pub fn new_data(&self, chain: Chain, data: TransactionRaw) -> Transaction {
let mut tx = self.clone();
Expand All @@ -195,6 +200,25 @@ impl Transaction {
}
}

#[wasm_bindgen]
impl Transaction {
#[wasm_bindgen(js_name = fromRaw)]
pub fn from_raw(chain: Chain, data: &str) -> Result<Transaction, Error> {
match chain {
Chain::KLV => KLV::tx_from_raw(data),
Chain::TRX => TRX::tx_from_raw(data),
Chain::ETH => ETH::tx_from_json(data),
Chain::MATIC => Err(Error::InvalidTransaction(
"MATIC chain not implemented".to_string(),
)),
Chain::BTC => Err(Error::InvalidTransaction(
"BTC chain not implemented".to_string(),
)),
Chain::NONE => Err(Error::InvalidTransaction("Invalid chain".to_string())),
}
}
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[wasm_bindgen]
pub struct AddressOptions {
Expand Down
Loading