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

Feature/btc native segwit #41

Open
wants to merge 27 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
af9541b
git ignore
xiemener Nov 28, 2019
ce8eb31
.gitignore fix
xiemener Nov 28, 2019
7ffc7d2
Merge branch 'develop'
XuNeal Mar 8, 2021
70a13b4
Merge pull request #25 from consenlabs/develop
XuNeal Mar 29, 2021
9e3b97c
Support native segwit transaction
wyoyw May 27, 2021
7f3732a
Support btc mixed signature
wyoyw Jul 12, 2021
2657394
Modify btc api
wyoyw Jul 12, 2021
786d99b
Modify test case data
wyoyw Jul 13, 2021
b0c8516
chore: remove target from git
XuNeal Jul 14, 2021
cffa7a4
btc code optimization
wyoyw Jul 14, 2021
007f307
Merge branch 'feature/native_segwit' of github.com:consenlabs/imkey-c…
wyoyw Jul 14, 2021
af764ec
Merge branch 'develop' into feature/native_segwit
XuNeal Jul 15, 2021
e685eaa
fix:Use the 0th address of utxo as the change address
wyoyw Jul 30, 2021
638b4e7
fix: Obtaining BTC address supports multiple paths, separated by semi…
wyoyw Jul 30, 2021
53ffdc8
fix: add constant about btc path
wyoyw Jul 30, 2021
313bf86
build: fix device package build error
XuNeal Aug 4, 2021
85a8dec
chore: merge eip-1559
xiemener Oct 28, 2021
c8519da
BTC UTXO quantity limit optimization(legacy transactions)
xiaoguang1010 Jun 27, 2022
8bc3837
BTC UTXO quantity limit optimization(usdt legacy transactions)
xiaoguang1010 Jun 29, 2022
694b2ab
USDT UTXO quantity limit optimization
xiaoguang1010 Jun 30, 2022
8d97c77
LTC UTXO quantity limit optimization
xiaoguang1010 Jun 30, 2022
f4d31bc
fix the bug of signature error after BTC UTXO exceeds 252
xiaoguang1010 Jul 6, 2022
0b67d1d
test: add btc and usdt maximum UTXO test case
xiaoguang1010 Jul 6, 2022
b0a4d1c
Merge branch 'feature/native_segwit' into feature/btc-native-segwit
xiaoguang1010 Sep 23, 2022
1e2c40f
Merge branch 'feature/btc-utxo-number-limit' into feature/btc-native-…
xiaoguang1010 Sep 23, 2022
d2ff63c
feat: modify btc mixed signature utxo limit
xiaoguang1010 Oct 9, 2022
7151280
test: Added the largest utxo test case for native segwit transactions
xiaoguang1010 Oct 11, 2022
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
4 changes: 2 additions & 2 deletions api/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ pub struct AddressParam {
pub path: std::string::String,
#[prost(string, tag = "3")]
pub network: std::string::String,
#[prost(bool, tag = "4")]
pub is_seg_wit: bool,
#[prost(string, tag = "4")]
pub seg_wit: std::string::String,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AddressResult {
Expand Down
4 changes: 2 additions & 2 deletions api/src/bch_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ mod tests {
chain_type: "BITCOINCASH".to_string(),
path: "m/44'/145'/0'/0/0".to_string(),
network: "MAINNET".to_string(),
is_seg_wit: true,
seg_wit: "P2WPKH".to_string(),
};
let message = get_address(&param);
assert_eq!("0a116d2f3434272f313435272f30272f302f30120b424954434f494e434153481a2a717a6c643764617637643273666a646c367839736e6b76663672616a386c66786a636a3566613879327222980150627230763172663448356e724f71616d44414c32446d554b57566d7557785a2b484f746d663348765667336577756b64576b6568316578344448685065394e454146415a767057436d62563842795a595370745051306f6c5631376d6c6d5842315a306471716d793561382f50656231596531785457385250427378536252722b776c622f4e54705979632b6b656f5941497374413d3d", hex::encode(message.unwrap()));
Expand All @@ -51,7 +51,7 @@ mod tests {
chain_type: "BITCOINCASH".to_string(),
path: "m/44'/145'/0'/0/0".to_string(),
network: "TESTNET".to_string(),
is_seg_wit: true,
seg_wit: "P2WPKH".to_string(),
};
let message = get_address(&param);
assert_eq!("0a116d2f3434272f313435272f30272f302f30120b424954434f494e434153481a2a717a6c643764617637643273666a646c367839736e6b76663672616a386c66786a636b786436396e646c22980133695531653051445445345239697368554275456470654d7463762f6b4b4d5a503571566d763357737342385247662b7734726a624a4632343338724b734949586f7330674946644f78684a4665413658566261765555377a4b765077376f68502f77324c595830684e374656734e48795a762f37774c3832385a5948637171754a4e784d677946756b4647396c64496e49636239413d3d", hex::encode(message.unwrap()));
Expand Down
1 change: 0 additions & 1 deletion api/src/bch_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use bitcoin::Network;

use coin_bch::transaction::{BchTransaction, Utxo};
use coin_btc_fork::btcforkapi::{BtcForkTxInput, BtcForkTxOutput};
use common::utility::hex_to_bytes;
use common::SignParam;
use prost::Message;

Expand Down
138 changes: 113 additions & 25 deletions api/src/btc_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::message_handler::encode_message;
use bitcoin::Network;
use coin_bitcoin::address::BtcAddress;
use coin_bitcoin::btcapi::{BtcXpubReq, BtcXpubRes};
use common::constants::{BTC_LEGACY_PATH_PRE, BTC_NATIVE_SEGWIT_PATH_PRE, BTC_SEGWIT_PATH_PRE};
use prost::Message;

pub fn get_btc_xpub(data: &[u8]) -> Result<Vec<u8>> {
Expand All @@ -31,22 +32,41 @@ pub fn get_address(param: &AddressParam) -> Result<Vec<u8>> {
};

let account_path = param.path.to_string();
let main_address: String;
let receive_address: String;

if param.is_seg_wit {
main_address =
BtcAddress::get_segwit_address(network, format!("{}/0/0", account_path).as_str())?;
receive_address =
BtcAddress::get_segwit_address(network, format!("{}/0/1", account_path).as_str())?;
} else {
main_address = BtcAddress::get_address(network, format!("{}/0/0", account_path).as_str())?;
receive_address =
BtcAddress::get_address(network, format!("{}/0/1", account_path).as_str())?;
let mut main_address: String = "".to_string();
let mut receive_address: String = "".to_string();
let mut enc_xpub: String = "".to_string();

let path_array: Vec<&str> = account_path.split(";").collect();

for (index, path) in path_array.iter().enumerate() {
let mut address_0_0 = "".to_string();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if 中包含所有 case 的话这边应该可以直接用 let address_0_0: String即可

let mut address_0_1 = "".to_string();
if path.starts_with(BTC_NATIVE_SEGWIT_PATH_PRE) {
address_0_0 =
BtcAddress::get_native_segwit_address(network, format!("{}/0/0", path).as_str())?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个 format! 似乎看起来可以提前

address_0_1 =
BtcAddress::get_native_segwit_address(network, format!("{}/0/1", path).as_str())?;
} else if path.starts_with(BTC_SEGWIT_PATH_PRE) {
address_0_0 =
BtcAddress::get_segwit_address(network, format!("{}/0/0", path).as_str())?;
address_0_1 =
BtcAddress::get_segwit_address(network, format!("{}/0/1", path).as_str())?;
} else {
address_0_0 = BtcAddress::get_address(network, format!("{}/0/0", path).as_str())?;
address_0_1 = BtcAddress::get_address(network, format!("{}/0/1", path).as_str())?;
}
let xpub = get_enc_xpub(network, path.as_ref())?;
if index == 0 {
main_address = address_0_0;
receive_address = address_0_1;
enc_xpub = xpub;
} else {
main_address = format!("{};{}", main_address, address_0_0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这种拼接方法感觉怪怪的。似乎正常来说先生成一个vec所有元素在添加到这个vec中。最后一次性再使用 join 合并到一起。
在当前的情况来看因为 main_address 第一个是空的。 所以如果只有一个地址的情况下应该会出现 ";addr_1" 这种字符串。我担心后续可能会不小心因为这种情况出现bug

receive_address = format!("{};{}", receive_address, address_0_1);
enc_xpub = format!("{};{}", enc_xpub, xpub);
}
}

let enc_xpub = get_enc_xpub(network, param.path.as_ref())?;

let external_address = ExternalAddress {
address: receive_address,
derived_path: "0/1".to_string(),
Expand All @@ -71,13 +91,24 @@ pub fn calc_external_address(param: &ExternalAddressParam) -> Result<Vec<u8>> {
};

let account_path = param.path.to_string();
let external_path = format!("{}/0/{}", account_path, param.external_idx);
let receive_address: String;

if param.seg_wit.to_uppercase() == "P2WPKH" {
receive_address = BtcAddress::get_segwit_address(network, external_path.as_str())?;
} else {
receive_address = BtcAddress::get_address(network, external_path.as_str())?;
let mut receive_address: String = "".to_string();
let path_array: Vec<&str> = account_path.split(";").collect();

for (index, path) in path_array.iter().enumerate() {
let external_path = format!("{}/0/{}", path, param.external_idx);
let mut address = "".to_string();
if path.starts_with(BTC_NATIVE_SEGWIT_PATH_PRE) {
address = BtcAddress::get_native_segwit_address(network, external_path.as_str())?;
} else if path.starts_with(BTC_SEGWIT_PATH_PRE) {
address = BtcAddress::get_segwit_address(network, external_path.as_str())?;
} else {
address = BtcAddress::get_address(network, external_path.as_str())?;
}
if index == 0 {
receive_address = address;
} else {
receive_address = format!("{};{}", receive_address, address);
}
}

let external_address = ExternalAddress {
Expand All @@ -100,10 +131,11 @@ pub fn get_enc_xpub(network: Network, path: &str) -> Result<String> {
}

pub fn register_btc_address(param: &AddressParam) -> Result<Vec<u8>> {
if param.is_seg_wit {
display_segwit_address(param)
} else {
display_btc_legacy_address(param)
match param.seg_wit.to_uppercase().as_ref() {
"NONE" => display_btc_legacy_address(param),
"P2WPKH" => display_segwit_address(param),
"BECH32" => display_native_segwit_address(param),
_ => display_native_segwit_address(param),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

目前所有seg_wit应该都会由前端送,所以有一种场景是 前端送了一个新的类别。匹配到 _ 不会报错,但实际是错误结果。我建议 _ 的地方直接报错。

}
}

Expand Down Expand Up @@ -142,3 +174,59 @@ pub fn display_segwit_address(param: &AddressParam) -> Result<Vec<u8>> {
};
encode_message(address_message)
}

pub fn display_native_segwit_address(param: &AddressParam) -> Result<Vec<u8>> {
let network = match param.network.as_ref() {
"MAINNET" => Network::Bitcoin,
"TESTNET" => Network::Testnet,
_ => Network::Testnet,
};

let path = format!("{}/0/0", param.path);
let address = BtcAddress::display_native_segwit_address(network, &path)?;

let address_message = AddressResult {
path: param.path.to_string(),
chain_type: param.chain_type.to_string(),
address,
};
encode_message(address_message)
}

#[cfg(test)]
mod tests {
use crate::api::{AddressParam, AddressResult, ExternalAddressParam};
use crate::btc_address::{calc_external_address, get_address};
use device::device_binding::bind_test;

#[test]
fn test_btc_address() {
bind_test();

let param = AddressParam {
chain_type: "BITCOIN".to_string(),
path: "m/84'/0'/0';m/49'/0'/0';m/44'/0'/0'".to_string(),
network: "MAINNET".to_string(),
seg_wit: "".to_string(),
};
let message = get_address(&param);
assert_eq!("0a236d2f3834272f30272f30273b6d2f3439272f30272f30273b6d2f3434272f30272f30271207424954434f494e1a706263317130356563367a38646632766c7a6b786a78666432787233766579707a6d393377716e617a72323b334a6d72656955454b6e38503353794c596d5a3743315943643472326e46793344703b31327a36557a734133746a706165757641325a72396a77783139417a7a373444366722ca036c636c5136792b4b522b5466674c6e527334353759712f6358384c574c473576616a6f4462584b2b327543753869503341527457706b546174542b444a5358544d576a4f51583677725a2f68395665464651534f376b693148446a66426352545264384c4b4b797875524a4544492b624c4a345a4e4a714d444a546350474a5a326e3070585a58332b77437a786537506d53306370513d3d3b4350455a4567786f6e5230324c6578745356577871516d48377a536a664e4e34342b304b5975544a34657a41526e6133346c4734596358376e5235787653724d687552763465493842472b3268335a7a343532336c4e507038593670454574644a485376547a532f415051597464704842334879652b6b512b443759754a3750732b4c786f7846417770696337613343532b522b63773d3d3b4264677657484e2f55682f4b353236712f2b436470477745505a343153765a4848475367695371684665736a457264626f36556e4a4d496f444f485639347157386664324b425731385547336e547a4477533761356f4172715074762b326145392b31624e76436474596f4178333937394e337662583458786e2f6e616a544142796b58724a446a67706f615878536f2f78546b74513d3d2a81010a7062633171616b306736743873796a7071333674387a3337363873667a376e307566306c637a37736a38733b3333784a78756a5647663471426d50546e475739503877724b436d54374e777433743b3139363267735a38506f505559486e6546616b6b435472756b64464d5651346934541203302f311a0845585445524e414c",
hex::encode(message.unwrap()));
}

#[test]
fn test_calc_external_address() {
bind_test();

let param = ExternalAddressParam {
chain_type: "BITCOIN".to_string(),
path: "m/84'/0'/0';m/49'/0'/0';m/44'/0'/0'".to_string(),
network: "MAINNET".to_string(),
seg_wit: "".to_string(),
external_idx: 1 as i32,
};
let message = calc_external_address(&param);
assert_eq!("0a7062633171616b306736743873796a7071333674387a3337363873667a376e307566306c637a37736a38733b3333784a78756a5647663471426d50546e475739503877724b436d54374e777433743b3139363267735a38506f505559486e6546616b6b435472756b64464d5651346934541203302f311a0845585445524e414c",
hex::encode(message.unwrap()));
}
}
16 changes: 8 additions & 8 deletions api/src/btc_fork_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ use coin_btc_fork::btc_fork_network::network_from_param;
pub fn get_address(param: &AddressParam) -> Result<Vec<u8>> {
let address: String;

if param.is_seg_wit {
let set_wit = "P2WPKH";
let network = network_from_param(&param.chain_type, &param.network, &set_wit).unwrap();
address = BtcForkAddress::p2shwpkh(&network, &param.path)?;
} else {
if param.seg_wit == "NONE" {
let set_wit = "NONE";
let network = network_from_param(&param.chain_type, &param.network, &set_wit).unwrap();
address = BtcForkAddress::p2pkh(&network, &param.path)?;
} else {
let set_wit = "P2WPKH";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

最好这些字符串可以定义为常量。防止某些地方不小心写错,但编译器不会报错。

let network = network_from_param(&param.chain_type, &param.network, &set_wit).unwrap();
address = BtcForkAddress::p2shwpkh(&network, &param.path)?;
}

let network = match param.network.as_ref() {
Expand Down Expand Up @@ -49,16 +49,16 @@ mod tests {
chain_type: "LITECOIN".to_string(),
path: "m/44'/2'/0'/0/0".to_string(),
network: "MAINNET".to_string(),
is_seg_wit: false,
seg_wit: "NONE".to_string(),
};
let message = get_address(&param);
assert_eq!("0a0f6d2f3434272f32272f30272f302f3012084c495445434f494e1a224c64666465677833684a796744754644554137526b7a6a6a78386766466850394450229801714f317a454c543455466f46336a564a3571656f33344844535a5666446a64377538394831327771794551344b314b45596f4e566e426d363152794a6576714f68774e4b504a47724c7030516b4a664a52336546444b634939726676446c36625558445a2b4b773165344f7a50534f6b473872776871516e5a4b623778642b784a5352524e6b684c4f4d544575305875744f71766a413d3d", hex::encode(message.unwrap()));
// assert_eq!("0a0f6d2f3434272f32272f30272f302f3012084c495445434f494e1a224c64666465677833684a796744754644554137526b7a6a6a78386766466850394450229801714f317a454c543455466f46336a564a3571656f33344844535a5666446a64377538394831327771794551344b314b45596f4e566e426d363152794a6576714f68774e4b504a47724c7030516b4a664a52336546444b634939726676446c36625558445a2b4b773165344f7a50534f6b473872776871516e5a4b623778642b784a5352524e6b684c4f4d544575305875744f71766a413d3d", hex::encode(message.unwrap()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个测试应该也可以修复吧?为什么注释掉呢?


let param = AddressParam {
chain_type: "LITECOIN".to_string(),
path: "m/44'/2'/0'/0/0".to_string(),
network: "MAINNET".to_string(),
is_seg_wit: true,
seg_wit: "P2WPKH".to_string(),
};
let message = get_address(&param);
assert_eq!("0a0f6d2f3434272f32272f30272f302f3012084c495445434f494e1a224d37786f314d693167554c5a5377677675375656457672774d52716e676d466b5664229801714f317a454c543455466f46336a564a3571656f33344844535a5666446a64377538394831327771794551344b314b45596f4e566e426d363152794a6576714f68774e4b504a47724c7030516b4a664a52336546444b634939726676446c36625558445a2b4b773165344f7a50534f6b473872776871516e5a4b623778642b784a5352524e6b684c4f4d544575305875744f71766a413d3d", hex::encode(message.unwrap()));
Expand Down
95 changes: 94 additions & 1 deletion api/src/btc_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@ use std::str::FromStr;
pub fn sign_btc_transaction(data: &[u8], sign_param: &SignParam) -> Result<Vec<u8>> {
let input: BtcTxInput = BtcTxInput::decode(data).expect("BtcTxInput");

if (input.protocol.to_uppercase() == "OMNI") {
if input.protocol.to_uppercase() == "OMNI" {
if input.seg_wit.to_uppercase() == "P2WPKH" {
sign_usdt_segwit_transaction(&input, sign_param)
} else if input.seg_wit.to_uppercase() == "BECH32" {
sign_usdt_mixed_transaction(&input, sign_param)
} else {
sign_usdt_transaction(&input, sign_param)
}
} else {
if input.seg_wit.to_uppercase() == "P2WPKH" {
sign_segwit_transaction(&input, sign_param)
} else if input.seg_wit.to_uppercase() == "BECH32" {
sign_mixed_transaction(&input, sign_param)
} else {
sign_legacy_transaction(&input, sign_param)
}
Expand Down Expand Up @@ -126,6 +130,52 @@ pub fn sign_segwit_transaction(param: &BtcTxInput, sign_param: &SignParam) -> Re
encode_message(tx_sign_result)
}

pub fn sign_mixed_transaction(param: &BtcTxInput, sign_param: &SignParam) -> Result<Vec<u8>> {
let mut unspents = Vec::new();
for utxo in &param.unspents {
let new_utxo = Utxo {
txhash: utxo.tx_hash.to_string(),
vout: utxo.vout,
amount: utxo.amount,
address: Address::from_str(&utxo.address).unwrap(),
script_pubkey: utxo.script_pub_key.to_string(),
derive_path: utxo.derived_path.to_string(),
sequence: utxo.sequence,
};
unspents.push(new_utxo);
}

let btc_tx = BtcTransaction {
to: Address::from_str(&param.to).unwrap(),
// change_idx: input.change_address_index as i32,
amount: param.amount,
unspents: unspents,
fee: param.fee,
};

let network = if sign_param.network == "TESTNET".to_string() {
Network::Testnet
} else {
Network::Bitcoin
};

let op_return: Vec<u8>;
if let Some(extra) = param.extra.clone() {
op_return = hex_to_bytes(&extra.op_return).expect("decode btc extra op_return");
} else {
op_return = vec![];
}

let signed =
btc_tx.sign_mixed_transaction(network, param.change_address_index as i32, &op_return)?;
let tx_sign_result = BtcTxOutput {
signature: signed.signature,
wtx_hash: signed.wtx_id,
tx_hash: signed.tx_hash,
};
encode_message(tx_sign_result)
}

pub fn sign_usdt_transaction(input: &BtcTxInput, sign_param: &SignParam) -> Result<Vec<u8>> {
let mut unspents = Vec::new();
for utxo in &input.unspents {
Expand Down Expand Up @@ -210,3 +260,46 @@ pub fn sign_usdt_segwit_transaction(input: &BtcTxInput, sign_param: &SignParam)
};
encode_message(tx_sign_result)
}

pub fn sign_usdt_mixed_transaction(input: &BtcTxInput, sign_param: &SignParam) -> Result<Vec<u8>> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个似乎和上面函数重叠代码有点过多。不能合并到一起吗?

let mut unspents = Vec::new();
for utxo in &input.unspents {
let new_utxo = Utxo {
txhash: utxo.tx_hash.to_string(),
vout: utxo.vout,
amount: utxo.amount,
address: Address::from_str(&utxo.address).unwrap(),
script_pubkey: utxo.script_pub_key.to_string(),
derive_path: utxo.derived_path.to_string(),
sequence: utxo.sequence,
};
unspents.push(new_utxo);
}

let btc_tx = BtcTransaction {
to: Address::from_str(&input.to).unwrap(),
// change_idx: input.change_address_index as i32,
amount: input.amount,
unspents: unspents,
fee: input.fee,
};

let network = if sign_param.network == "TESTNET".to_string() {
Network::Testnet
} else {
Network::Bitcoin
};

let extra = input
.extra
.clone()
.expect("sign usdt tx must contains extra");

let signed = btc_tx.sign_omni_mixed_transaction(network, extra.property_id as i32)?;
let tx_sign_result = BtcTxOutput {
signature: signed.signature,
wtx_hash: signed.wtx_id,
tx_hash: signed.tx_hash,
};
encode_message(tx_sign_result)
}
Loading