diff --git a/cxrml/bridge/btc/src/b58.rs b/cxrml/bridge/btc/src/b58.rs new file mode 100644 index 0000000000000..9be1e572e8f32 --- /dev/null +++ b/cxrml/bridge/btc/src/b58.rs @@ -0,0 +1,67 @@ +// Copyright 2018 Chainpool + +use rstd::prelude::Vec; + +static BASE58_CHARS: &'static [u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +static BASE58_DIGITS: [Option; 128] = [ + None, None, None, None, None, None, None, None, // 0-7 + None, None, None, None, None, None, None, None, // 8-15 + None, None, None, None, None, None, None, None, // 16-23 + None, None, None, None, None, None, None, None, // 24-31 + None, None, None, None, None, None, None, None, // 32-39 + None, None, None, None, None, None, None, None, // 40-47 + None, Some(0), Some(1), Some(2), Some(3), Some(4), Some(5), Some(6), // 48-55 + Some(7), Some(8), None, None, None, None, None, None, // 56-63 + None, Some(9), Some(10), Some(11), Some(12), Some(13), Some(14), Some(15), // 64-71 + Some(16), None, Some(17), Some(18), Some(19), Some(20), Some(21), None, // 72-79 + Some(22), Some(23), Some(24), Some(25), Some(26), Some(27), Some(28), Some(29), // 80-87 + Some(30), Some(31), Some(32), None, None, None, None, None, // 88-95 + None, Some(33), Some(34), Some(35), Some(36), Some(37), Some(38), Some(39), // 96-103 + Some(40), Some(41), Some(42), Some(43), None, Some(44), Some(45), Some(46), // 104-111 + Some(47), Some(48), Some(49), Some(50), Some(51), Some(52), Some(53), Some(54), // 112-119 + Some(55), Some(56), Some(57), None, None, None, None, None, // 120-127 +]; + +pub fn from(data: Vec) -> Result, &'static str> { + // 11/15 is just over log_256(58) + let mut scratch = vec![0u8; 1 + data.len() * 11 / 15]; + // Build in base 256 + for d58 in data.clone() { + // Compute "X = X * 58 + next_digit" in base 256 + if d58 as usize > BASE58_DIGITS.len() { + return Err("BadByte"); + } + let mut carry = match BASE58_DIGITS[d58 as usize] { + Some(d58) => d58 as u32, + None => { return Err("BadByte"); } + }; + for d256 in scratch.iter_mut().rev() { + carry += *d256 as u32 * 58; + *d256 = carry as u8; + carry /= 256; + } + assert_eq!(carry, 0); + } + + // Copy leading zeroes directly + let mut ret: Vec = data.iter().take_while(|&x| *x == BASE58_CHARS[0]) + .map(|_| 0) + .collect(); + // Copy rest of string + ret.extend(scratch.into_iter().skip_while(|&x| x == 0)); + Ok(ret) +} + +#[cfg(test)] +mod tests { + use super::from; + #[test] + fn test_from() { + let s = String::from("mjKE11gjVN4JaC9U8qL6ZB5vuEBgmwik7b"); + let v = &[111, 41, 168, 159, 89, 51, 97, 179, 153, 104, 9, 74, + 184, 193, 251, 6, 131, 166, 121, 3, 1, 241, 112, 101, 146]; + assert_eq!(from(s.as_bytes().to_vec()).unwrap(), v); + } +} + diff --git a/cxrml/bridge/btc/src/keys.rs b/cxrml/bridge/btc/src/keys.rs index 417711368813d..a63f885744821 100644 --- a/cxrml/bridge/btc/src/keys.rs +++ b/cxrml/bridge/btc/src/keys.rs @@ -331,3 +331,16 @@ impl From for CompactSignature { CompactSignature(h) } } + +#[cfg(test)] +mod tests { + use super::Address; + use super::DisplayLayout; + + #[test] + fn test_layout() { + let v = &[111, 41, 168, 159, 89, 51, 97, 179, 153, 104, 9, 74, + 184, 193, 251, 6, 131, 166, 121, 3, 1, 241, 112, 101, 146]; + Address::from_layout(v).unwrap(); + } +} diff --git a/cxrml/bridge/btc/src/lib.rs b/cxrml/bridge/btc/src/lib.rs index 501d4c7fd81e7..f46bbb4f67e79 100644 --- a/cxrml/bridge/btc/src/lib.rs +++ b/cxrml/bridge/btc/src/lib.rs @@ -53,18 +53,19 @@ mod blockchain; mod tx; mod keys; mod script; +mod b58; use codec::Decode; use rstd::prelude::*; //use rstd::result::Result as StdResult; -use runtime_support::dispatch::Result; +use runtime_support::dispatch::{Result, Parameter}; use runtime_support::{StorageValue, StorageMap}; use runtime_primitives::traits::OnFinalise; use system::ensure_signed; use ser::deserialize; -use chain::{BlockHeader, Transaction}; +use chain::{BlockHeader, Transaction as BTCTransaction}; use primitives::{hash::H256, compact::Compact}; use primitives::hash; @@ -92,6 +93,7 @@ decl_module! { pub struct Module for enum Call where origin: T::Origin { fn push_header(origin, header: Vec) -> Result; fn push_transaction(origin, tx: Vec) -> Result; + fn propose_transaction(origin, tx: Vec) -> Result; } } @@ -153,6 +155,13 @@ pub enum TxType { RegisterDeposit, } +#[derive(PartialEq, Clone, Encode, Decode)] +pub struct Proposal { + proposer: Vec, + tx: BTCTransaction, + perfection: bool, +} + impl Default for TxType { fn default() -> Self { TxType::Deposit } } @@ -182,9 +191,10 @@ decl_storage! { pub UTXOSet get(utxo_set): map u64 => UTXO; pub UTXOMaxIndex get(utxo_max_index) config(): u64; - pub TxSet get(tx_set): map H256 => Option<(Transaction, T::AccountId, keys::Address, TxType, u64)>; // Address, type, balance + pub TxSet get(tx_set): map H256 => Option<(T::AccountId, keys::Address, TxType, u64, BTCTransaction)>; // Address, type, balance pub BlockTxids get(block_txids): map H256 => Vec; pub AddressMap get(address_map): map keys::Address => T::AccountId; + pub TxProposal get(tx_proposal): map H256 => Option>; // pub AccountMap get(account_map): map T::AccountId => keys::Address; // ===== @@ -236,6 +246,14 @@ impl Module { Self::process_tx(tx, &from)?; Ok(()) } + + pub fn propose_transaction(origin: T::Origin, tx: Vec) -> Result { + let from = ensure_signed(origin)?; + + let tx: BTCTransaction = Decode::decode(&mut tx.as_slice()).ok_or("parse transaction err")?; + Self::process_btc_tx(tx, &from)?; + Ok(()) + } } @@ -283,4 +301,8 @@ impl Module { Ok(()) } + + pub fn process_btc_tx(tx: BTCTransaction, who: &T::AccountId) -> Result { + Ok(()) + } } diff --git a/cxrml/bridge/btc/src/script/script.rs b/cxrml/bridge/btc/src/script/script.rs index 5142c8dcc432b..7455849d3bb75 100644 --- a/cxrml/bridge/btc/src/script/script.rs +++ b/cxrml/bridge/btc/src/script/script.rs @@ -425,6 +425,21 @@ impl Script { return 1; } + pub fn extract_multi_scriptsig(&self) -> Result<(Vec, Script), keys::Error> { //[sig], redeem + let mut pc = 1; + let mut vec: Vec = Vec::new(); + while pc < self.len() - 2 { + let instruction = self.get_instruction(pc).expect("this method depends on previous check in script_type()"); + let data = instruction.data.expect("this method depends on previous check in script_type()"); + vec.push(data.into()); + pc += instruction.step; + } + if let Some(script) = vec.pop() { + return Ok((vec, script.into())); + } + return Err(keys::Error::InvalidSignature); + } + pub fn extract_destinations(&self) -> Result, keys::Error> { match self.script_type() { ScriptType::NonStandard => { @@ -605,225 +620,40 @@ pub fn is_witness_commitment_script(script: &[u8]) -> bool { #[cfg(test)] mod tests { - use {Builder, Opcode}; - /// Maximum number of bytes pushable to the stack - const MAX_SCRIPT_ELEMENT_SIZE: usize = 520; - - - use super::{Script, ScriptType, ScriptAddress}; - use keys::{Address, Public}; - - #[test] - fn test_is_pay_to_script_hash() { - let script: Script = "a9143b80842f4ea32806ce5e723a255ddd6490cfd28d87".into(); - let script2: Script = "a9143b80842f4ea32806ce5e723a255ddd6490cfd28d88".into(); - assert!(script.is_pay_to_script_hash()); - assert!(!script2.is_pay_to_script_hash()); - } - - #[test] - fn test_is_pay_to_witness_key_hash() { - let script: Script = "00140000000000000000000000000000000000000000".into(); - let script2: Script = "01140000000000000000000000000000000000000000".into(); - assert!(script.is_pay_to_witness_key_hash()); - assert!(!script2.is_pay_to_witness_key_hash()); - } - - #[test] - fn test_is_pay_to_witness_script_hash() { - let script: Script = "00203b80842f4ea32806ce5e723a255ddd6490cfd28dac38c58bf9254c0577330693".into(); - let script2: Script = "01203b80842f4ea32806ce5e723a255ddd6490cfd28dac38c58bf9254c0577330693".into(); - assert!(script.is_pay_to_witness_script_hash()); - assert!(!script2.is_pay_to_witness_script_hash()); - } - - #[cfg(feature = "std")] - #[test] - fn test_script_debug() { - use std::fmt::Write; - - let script = Builder::default() - .push_num(3.into()) - .push_num(2.into()) - .push_opcode(Opcode::OP_ADD) - .into_script(); - let s = "Script { data: 0103010293 }"; - let mut res = String::new(); - write!(&mut res, "{:?}", script).unwrap(); - assert_eq!(s.to_string(), res); - } - - #[test] - fn test_script_display() { - let script = Builder::default() - .push_num(3.into()) - .push_num(2.into()) - .push_opcode(Opcode::OP_ADD) - .into_script(); - let s = r#"OP_PUSHBYTES_1 0x03 -OP_PUSHBYTES_1 0x02 -OP_ADD -"#; - assert_eq!(script.to_string(), s.to_string()); - } - - #[test] - fn test_script_without_op_codeseparator() { - let script: Script = "ab00270025512102e485fdaa062387c0bbb5ab711a093b6635299ec155b7b852fce6b992d5adbfec51ae".into(); - let scr_goal: Script = "00270025512102e485fdaa062387c0bbb5ab711a093b6635299ec155b7b852fce6b992d5adbfec51ae".into(); - assert_eq!(script.without_separators(), scr_goal); - } - - #[test] - fn test_script_is_multisig() { - let script: Script = "524104a882d414e478039cd5b52a92ffb13dd5e6bd4515497439dffd691a0f12af9575fa349b5694ed3155b136f09e63975a1700c9f4d4df849323dac06cf3bd6458cd41046ce31db9bdd543e72fe3039a1f1c047dab87037c36a669ff90e28da1848f640de68c2fe913d363a51154a0c62d7adea1b822d05035077418267b1a1379790187410411ffd36c70776538d079fbae117dc38effafb33304af83ce4894589747aee1ef992f63280567f52f5ba870678b4ab4ff6c8ea600bd217870a8b4f1f09f3a8e8353ae".into(); - let not: Script = "ab00270025512102e485fdaa062387c0bbb5ab711a093b6635299ec155b7b852fce6b992d5adbfec51ae".into(); - assert!(script.is_multisig_script()); - assert!(!not.is_multisig_script()); - } - - // https://github.com/libbtc/libbtc/blob/998badcdac95a226a8f8c00c8f6abbd8a77917c1/test/tx_tests.c#L640 - #[test] - fn test_script_type() { - assert_eq!(ScriptType::PubKeyHash, Script::from("76a914aab76ba4877d696590d94ea3e02948b55294815188ac").script_type()); - assert_eq!(ScriptType::Multisig, Script::from("522102004525da5546e7603eefad5ef971e82f7dad2272b34e6b3036ab1fe3d299c22f21037d7f2227e6c646707d1c61ecceb821794124363a2cf2c1d2a6f28cf01e5d6abe52ae").script_type()); - assert_eq!(ScriptType::ScriptHash, Script::from("a9146262b64aec1f4a4c1d21b32e9c2811dd2171fd7587").script_type()); - assert_eq!(ScriptType::PubKey, Script::from("4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac").script_type()); - } - - #[test] - fn test_sigops_count() { - assert_eq!(1usize, Script::from("76a914aab76ba4877d696590d94ea3e02948b55294815188ac").sigops_count(false)); - assert_eq!(2usize, Script::from("522102004525da5546e7603eefad5ef971e82f7dad2272b34e6b3036ab1fe3d299c22f21037d7f2227e6c646707d1c61ecceb821794124363a2cf2c1d2a6f28cf01e5d6abe52ae").sigops_count(true)); - assert_eq!(20usize, Script::from("522102004525da5546e7603eefad5ef971e82f7dad2272b34e6b3036ab1fe3d299c22f21037d7f2227e6c646707d1c61ecceb821794124363a2cf2c1d2a6f28cf01e5d6abe52ae").sigops_count(false)); - assert_eq!(0usize, Script::from("a9146262b64aec1f4a4c1d21b32e9c2811dd2171fd7587").sigops_count(false)); - assert_eq!(1usize, Script::from("4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac").sigops_count(false)); - } - - #[test] - fn test_sigops_count_b73() { - let max_block_sigops = 20000; - let block_sigops = 0; - let mut script = vec![Opcode::OP_CHECKSIG as u8; max_block_sigops - block_sigops + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 + 1]; - script[max_block_sigops - block_sigops] = Opcode::OP_PUSHDATA4 as u8; - let overmax = MAX_SCRIPT_ELEMENT_SIZE + 1; - script[max_block_sigops - block_sigops + 1] = overmax as u8; - script[max_block_sigops - block_sigops + 2] = (overmax >> 8) as u8; - script[max_block_sigops - block_sigops + 3] = (overmax >> 16) as u8; - script[max_block_sigops - block_sigops + 4] = (overmax >> 24) as u8; - let script: Script = script.into(); - assert_eq!(script.sigops_count(false), 20001); - } - - #[test] - fn test_sigops_count_b74() { - let max_block_sigops = 20000; - let block_sigops = 0; - let mut script = vec![Opcode::OP_CHECKSIG as u8; max_block_sigops - block_sigops + MAX_SCRIPT_ELEMENT_SIZE + 42]; - script[max_block_sigops - block_sigops + 1] = Opcode::OP_PUSHDATA4 as u8; - script[max_block_sigops - block_sigops + 2] = 0xfe; - script[max_block_sigops - block_sigops + 3] = 0xff; - script[max_block_sigops - block_sigops + 4] = 0xff; - script[max_block_sigops - block_sigops + 5] = 0xff; - let script: Script = script.into(); - assert_eq!(script.sigops_count(false), 20001); - } - - #[test] - fn test_script_empty_find_and_delete() { - let s: Script = vec![Opcode::OP_0 as u8].into(); - let result = s.find_and_delete(&[]); - assert_eq!(s, result); - } - - #[test] - fn test_extract_destinations_pub_key_compressed() { - let pubkey_bytes = [0; 33]; - let address = Public::from_slice(&pubkey_bytes).unwrap().address_hash(); - let script = Builder::default() - .push_bytes(&pubkey_bytes) - .push_opcode(Opcode::OP_CHECKSIG) - .into_script(); - assert_eq!(script.script_type(), ScriptType::PubKey); - assert_eq!(script.extract_destinations(), Ok(vec![ - ScriptAddress::new_p2pkh(address), - ])); - } - - #[test] - fn test_extract_destinations_pub_key_normal() { - let pubkey_bytes = [0; 65]; - let address = Public::from_slice(&pubkey_bytes).unwrap().address_hash(); - let script = Builder::default() - .push_bytes(&pubkey_bytes) - .push_opcode(Opcode::OP_CHECKSIG) - .into_script(); - assert_eq!(script.script_type(), ScriptType::PubKey); - assert_eq!(script.extract_destinations(), Ok(vec![ - ScriptAddress::new_p2pkh(address), - ])); - } - - #[test] - fn test_extract_destinations_pub_key_hash() { - let address = Address::from("13NMTpfNVVJQTNH4spP4UeqBGqLdqDo27S").hash; - let script = Builder::build_p2pkh(&address); - assert_eq!(script.script_type(), ScriptType::PubKeyHash); - assert_eq!(script.extract_destinations(), Ok(vec![ - ScriptAddress::new_p2pkh(address), - ])); - } - - #[test] - fn test_extract_destinations_script_hash() { - let address = Address::from("13NMTpfNVVJQTNH4spP4UeqBGqLdqDo27S").hash; - let script = Builder::build_p2sh(&address); - assert_eq!(script.script_type(), ScriptType::ScriptHash); - assert_eq!(script.extract_destinations(), Ok(vec![ - ScriptAddress::new_p2sh(address), - ])); - } - - #[test] - fn test_extract_destinations_multisig() { - let pubkey1_bytes = [0; 33]; - let address1 = Public::from_slice(&pubkey1_bytes).unwrap().address_hash(); - let pubkey2_bytes = [1; 65]; - let address2 = Public::from_slice(&pubkey2_bytes).unwrap().address_hash(); - let script = Builder::default() - .push_opcode(Opcode::OP_2) - .push_bytes(&pubkey1_bytes) - .push_bytes(&pubkey2_bytes) - .push_opcode(Opcode::OP_2) - .push_opcode(Opcode::OP_CHECKMULTISIG) - .into_script(); - assert_eq!(script.script_type(), ScriptType::Multisig); - assert_eq!(script.extract_destinations(), Ok(vec![ - ScriptAddress::new_p2pkh(address1), - ScriptAddress::new_p2pkh(address2), - ])); - } - - #[test] - fn test_num_signatures_required() { - let script = Builder::default() - .push_opcode(Opcode::OP_3) - .push_bytes(&[0; 33]) - .push_bytes(&[0; 65]) - .push_bytes(&[0; 65]) - .push_bytes(&[0; 65]) - .push_opcode(Opcode::OP_4) - .push_opcode(Opcode::OP_CHECKMULTISIG) - .into_script(); - assert_eq!(script.script_type(), ScriptType::Multisig); - assert_eq!(script.num_signatures_required(), 3); - - let script = Builder::default() - .push_opcode(Opcode::OP_HASH160) - .push_bytes(&[0; 20]) - .push_opcode(Opcode::OP_EQUAL) - .into_script(); - assert_eq!(script.script_type(), ScriptType::ScriptHash); - assert_eq!(script.num_signatures_required(), 1); - } + use super::*; + const redeem: &'static str = "52210257aff1270e3163aaae9d972b3d09a2385e0d4877501dbeca3ee045f8de00d21c2103fd58c689594b87bbe20a9a00091d074dc0d9f49a988a7ad4c2575adeda1b507c2102bb2a5aa53ba7c0d77bdd86bb9553f77dd0971d3a6bb6ad609787aa76eb17b6b653ae"; + const scriptsig1: &'static str = "00483045022100c0076941e39126f1bd0102d6df278470802ca8b694f8e39467121dc9ecc4d46802204ab7e3128bd0a93a30d1d5ea4db57cc8ba2d4c39172c2d2e536787e0b152bffe014c6952210257aff1270e3163aaae9d972b3d09a2385e0d4877501dbeca3ee045f8de00d21c2103fd58c689594b87bbe20a9a00091d074dc0d9f49a988a7ad4c2575adeda1b507c2102bb2a5aa53ba7c0d77bdd86bb9553f77dd0971d3a6bb6ad609787aa76eb17b6b653ae"; + const scriptsig2: &'static str = "00483045022100c0076941e39126f1bd0102d6df278470802ca8b694f8e39467121dc9ecc4d46802204ab7e3128bd0a93a30d1d5ea4db57cc8ba2d4c39172c2d2e536787e0b152bffe014730440220731394ffbf7d068393a2b6146e09f16bd9e39c16d04f38461a4c6991a725609202202633acd7cbf14883736f8e6376aa9090d0adacf73bc76ff5f95dca069caad593014c6952210257aff1270e3163aaae9d972b3d09a2385e0d4877501dbeca3ee045f8de00d21c2103fd58c689594b87bbe20a9a00091d074dc0d9f49a988a7ad4c2575adeda1b507c2102bb2a5aa53ba7c0d77bdd86bb9553f77dd0971d3a6bb6ad609787aa76eb17b6b653ae"; + const sig1: &'static str = "3045022100c0076941e39126f1bd0102d6df278470802ca8b694f8e39467121dc9ecc4d46802204ab7e3128bd0a93a30d1d5ea4db57cc8ba2d4c39172c2d2e536787e0b152bffe01"; + const sig2: &'static str = "30440220731394ffbf7d068393a2b6146e09f16bd9e39c16d04f38461a4c6991a725609202202633acd7cbf14883736f8e6376aa9090d0adacf73bc76ff5f95dca069caad59301"; + #[test] + fn redeem_script() { + let script: Script = redeem.into(); + assert_eq!(script.is_multisig_script(), true); + } + + #[test] + fn input_1() { + let script: Script = scriptsig1.into(); + let (sigs, dem) = script.extract_multi_scriptsig().unwrap(); + let sig_bytes: Bytes = sig1.into(); + assert_eq!(sig_bytes, sigs[0]); + let script: Script = redeem.into(); + assert_eq!(script, dem); + assert_eq!(sigs.len(), 1); + } + + #[test] + fn input_2() { + let script: Script = scriptsig2.into(); + let (sigs, dem) = script.extract_multi_scriptsig().unwrap(); + let sig_bytes1: Bytes = sig1.into(); + assert_eq!(sig_bytes1, sigs[0]); + let sig_bytes2: Bytes = sig2.into(); + assert_eq!(sig_bytes2, sigs[1]); + let script: Script = redeem.into(); + assert_eq!(script, dem); + assert_eq!(sigs.len(), 2); + + } } diff --git a/cxrml/bridge/btc/src/script/stack.rs b/cxrml/bridge/btc/src/script/stack.rs index 187bf83c40228..3c71121ca67bc 100644 --- a/cxrml/bridge/btc/src/script/stack.rs +++ b/cxrml/bridge/btc/src/script/stack.rs @@ -156,7 +156,7 @@ impl Stack { #[cfg(test)] mod tests { - use Error; + use super::Error; use super::Stack; #[test] diff --git a/cxrml/bridge/btc/src/tests.rs b/cxrml/bridge/btc/src/tests.rs index 30aa89ad7fd0f..3b1fe0e43617f 100644 --- a/cxrml/bridge/btc/src/tests.rs +++ b/cxrml/bridge/btc/src/tests.rs @@ -7,7 +7,7 @@ use runtime_primitives::traits::BlakeTwo256; use runtime_primitives::testing::{Digest, DigestItem, Header}; use runtime_io; use runtime_io::with_externalities; - +use self::base58::FromBase58; use codec::{Decode, Encode}; use runtime_support::{StorageMap, StorageValue}; use super::*; @@ -80,6 +80,9 @@ pub fn new_test_ext() -> runtime_io::TestExternalities { 2 * 7 * 24 * 60 * 60, // target_timespan_seconds 10 * 60, // target_spacing_seconds 4), // retargeting_factor + network_id: 1, + utxo_max_index: 0, + receive_address: "mjKE11gjVN4JaC9U8qL6ZB5vuEBgmwik7b".from_base58().unwrap(), fee: 0, }.build_storage().unwrap()); r.into() @@ -161,6 +164,9 @@ pub fn new_test_mock_ext() -> runtime_io::TestExternalities { 2 * 7 * 24 * 60 * 60, // target_timespan_seconds 10 * 60, // target_spacing_seconds 4), // retargeting_factor + network_id: 1, + utxo_max_index: 0, + receive_address: "mjKE11gjVN4JaC9U8qL6ZB5vuEBgmwik7b".from_base58().unwrap(), fee: 0, }.build_storage().unwrap()); r.into() @@ -416,6 +422,9 @@ pub fn new_test_ext2() -> runtime_io::TestExternalities { 2 * 7 * 24 * 60 * 60, // target_timespan_seconds 10 * 60, // target_spacing_seconds 4), // retargeting_factor + network_id: 1, + utxo_max_index: 0, + receive_address: "mjKE11gjVN4JaC9U8qL6ZB5vuEBgmwik7b".from_base58().unwrap(), fee: 0, }.build_storage().unwrap()); r.into() @@ -459,6 +468,9 @@ pub fn new_test_ext3() -> runtime_io::TestExternalities { 2 * 7 * 24 * 60 * 60, // target_timespan_seconds 10 * 60, // target_spacing_seconds 4), // retargeting_factor + network_id: 1, + utxo_max_index: 0, + receive_address: "mjKE11gjVN4JaC9U8qL6ZB5vuEBgmwik7b".from_base58().unwrap(), fee: 0, }.build_storage().unwrap()); r.into() diff --git a/cxrml/bridge/btc/src/tx/mod.rs b/cxrml/bridge/btc/src/tx/mod.rs index eee9906df168c..207f491ce2e22 100644 --- a/cxrml/bridge/btc/src/tx/mod.rs +++ b/cxrml/bridge/btc/src/tx/mod.rs @@ -47,32 +47,36 @@ impl UTXOStorage { >::mutate(|inc| *inc = index); } - fn update(utxos: Vec, is_spent: bool) { + fn update(utxos: Vec, is_spent: bool) -> u64 { + let mut update_balance = 0; for u in utxos { let mut index = >::get(); while index > 0 { index -= 1; let utxo = >::get(index); if utxo == u { - >::mutate(index, |utxo| utxo.is_spent = is_spent); + >::mutate(index, |utxo| { update_balance += utxo.balance; utxo.is_spent = is_spent; } ); break; } } } + update_balance } - fn update_from_outpoint(out_point_set: Vec, is_spent: bool) { + fn update_from_outpoint(out_point_set: Vec, is_spent: bool) -> u64 { + let mut update_balance = 0; for out_point in out_point_set { let mut index = >::get(); while index > 0 { index -= 1; let utxo = >::get(index); if out_point.hash == utxo.txid && out_point.index == utxo.index { - >::mutate(index, |utxo| utxo.is_spent = is_spent ); + >::mutate(index, |utxo| { update_balance += utxo.balance; utxo.is_spent = is_spent; } ); break; } } } + update_balance } } @@ -92,7 +96,7 @@ impl TxStorage { // todo 检查block是否存在 >::mutate(block_hash, |v| v.push(hash.clone())); - >::insert(hash, (tx, who.clone(), address, tx_type, balance)); + >::insert(hash, (who.clone(), address, tx_type, balance, tx)); } fn check_previous(txid: &H256) -> bool { @@ -110,11 +114,18 @@ impl RollBack for TxStorage { let txids = >::get(header); for txid in txids.iter() { - let (tx, _, _, tx_type, _) = >::get(txid).unwrap(); + let (_, _, tx_type, _, tx) = >::get(txid).unwrap(); match tx_type { TxType::Withdraw => { let out_point_set = tx.inputs.iter().map(|input| input.previous_output.clone()).collect(); >::update_from_outpoint(out_point_set, false); + let receive_address = keys::Address::from_layout(&receive_address).unwrap(); + let mut out_point_set2: Vec = Vec::new(); + let mut index = 0; + tx.outputs.iter().map(|output| { + if is_key(&output.script_pubkey, &receive_address) { + out_point_set2.push(OutPoint{hash: txid.clone(), index: index}) } index += 1; () }); + >::update_from_outpoint(out_point_set2, true); }, TxType::Register => {}, _ => { @@ -175,29 +186,37 @@ pub fn validate_transaction( } // detect deposit for output in tx.raw.outputs.iter() { - let script = output.script_pubkey.clone().take(); + if is_key(&output.script_pubkey, &receive_address) { + return Ok(TxType::RegisterDeposit); + } + } + + Err("not found our pubkey, may be an unrelated tx") +} + +fn is_key(script_pubkey: &[u8], receive_address: &keys::Address) -> bool { runtime_io::print("------script"); - runtime_io::print(script.as_slice()); - let script: Script = script.clone().into(); + runtime_io::print(script_pubkey); + let script: Vec = script_pubkey.iter().cloned().collect(); + let script: Script = script.into(); let script_addresses = script.extract_destinations().unwrap_or(vec![]); if script_addresses.len() == 1 { if receive_address.hash == script_addresses[0].hash { - return Ok(TxType::RegisterDeposit); + return true; } } - } - - Err("not found our pubkey, may be an unrelated tx") + return false; } pub fn handle_input(tx: &Transaction, block_hash: &H256, who: &T::AccountId, receive_address: &[u8]){ let out_point_set = tx.inputs.iter().map(|input| { input.previous_output.clone() }).collect(); - >::update_from_outpoint(out_point_set, true); + let mut update_balance = >::update_from_outpoint(out_point_set, true); let receive_address = keys::Address::from_layout(receive_address).unwrap(); - let mut total_balance = 0; - tx.outputs.iter().map(|output| { total_balance += output.value; ()}); - >::store_tx(tx, block_hash, who, receive_address.clone(), TxType::Withdraw, total_balance); + tx.outputs.iter().map(|output| { + if is_key(&output.script_pubkey, &receive_address) { + update_balance -= output.value; } () }); + >::store_tx(tx, block_hash, who, receive_address.clone(), TxType::Withdraw, update_balance); } fn deposit_token(_address: keys::Address, _balance: u64) { diff --git a/src/genesis_config.rs b/src/genesis_config.rs index fadec12e0e83b..891a1eeb7671a 100644 --- a/src/genesis_config.rs +++ b/src/genesis_config.rs @@ -38,7 +38,7 @@ pub fn testnet_genesis(chainspec: ChainSpec) -> GenesisConfig { // const CENTS: u128 = 1_000 * MILLICENTS; // assume this is worth about a cent. // const DOLLARS: u128 = 100 * CENTS; - const SECS_PER_BLOCK: u64 = 3; + const SECS_PER_BLOCK: u64 = 1; const MINUTES: u64 = 60 / SECS_PER_BLOCK; const HOURS: u64 = MINUTES * 60; const DAYS: u64 = HOURS * 24;