Skip to content

Commit

Permalink
feat: imkey support bitcoin psbt transaction[R2D2-11602] (#114)
Browse files Browse the repository at this point in the history
* feat: add p2wpkhp2tr address generation

* feat: add bitcoin Mixed signature function

* test: modify p2wpkh test case

* feat: add bitcoin p2wpkh sign

* feat: add test case and add p2tr sign

* test: add test case

* test: modify btc test case

* feat: code optimization

* feat: modify display_addres and get_address

* feat: add bitcoin p2tr transaction

* test: add p2tr test case

* test: add bitcoin transaction sign function test

* feat: code optimization

* feat: pass in the tweaked public key when signing

* chore: code format

* feat: add imKey PSBT feature code

* feat: add p2wpkhp2tr address generation

* feat: add bitcoin Mixed signature function

* test: modify p2wpkh test case

* feat: add bitcoin p2wpkh sign

* feat: add test case and add p2tr sign

* test: add test case

* test: modify btc test case

* feat: code optimization

* feat: modify display_addres and get_address

* feat: add bitcoin p2tr transaction

* test: add p2tr test case

* test: add bitcoin transaction sign function test

* feat: code optimization

* feat: pass in the tweaked public key when signing

* chore: code format

* chore: switch to staging env

* feat: add imKey PSBT feature code

* feat: derive_account and derive_sub_account support native segwit address and bech32 address

* feat: add bitcoin psbt sign transaction

* feat: add bitcoin p2tr script sign

* fix: fix psbt legacy transaction bug

* chore: psbt code optimized

* fix: fix preview info calc error bug

* fix: modify change path to account path

* chore: code optimized

* feat: psbt transaction support sign message

* feat: add bip-322 sign message feature

* feat: add bitcoin sign message api

* chore: remove address verify function

* chore: get_utxo_pub_key function removes the network param

* chore: modify tcx version to 2.8.0

* chore: bip322 sign message  add path check and code optimization

* build: moify ios CI run-on version to macos-14

* fix: fix ios CI build error

* fix: fix cargo check error

* chore: optimization imKey public key conversion

* chore: PubKeyParam removes isSegwit field
  • Loading branch information
xiaoguang1010 authored Aug 16, 2024
1 parent 4039e2f commit e244c49
Show file tree
Hide file tree
Showing 41 changed files with 3,965 additions and 892 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.7.4
2.8.0
95 changes: 95 additions & 0 deletions imkey-core/ikc-common/src/apdu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,19 @@ impl BtcApdu {
apdu.to_hex().to_uppercase()
}

/**
*p2 00:sign psbt transaction 80: sign message
**/
pub fn btc_psbt_preview(data: &Vec<u8>, p2: u8) -> String {
if data.len() as u32 > LC_MAX {
panic!("data to long");
}
let mut apdu = ApduHeader::new(0x80, 0x4C, 0x00, p2, data.len() as u8).to_array();
apdu.extend(data.iter());
apdu.push(0x00);
apdu.to_hex().to_uppercase()
}

pub fn btc_sign(index: u8, hash_type: u8, path: &str) -> String {
let path_bytes = path.as_bytes();
let mut apdu =
Expand All @@ -93,6 +106,36 @@ impl BtcApdu {
apdu.to_hex().to_uppercase()
}

pub fn btc_taproot_sign(last_one: bool, data: Vec<u8>) -> String {
if data.len() as u32 > LC_MAX {
panic!("data to long");
}

let mut apdu = match last_one {
true => ApduHeader::new(0x80, 0x40, 0x80, 0x00, data.len() as u8).to_array(),
_ => ApduHeader::new(0x80, 0x40, 0x00, 0x00, data.len() as u8).to_array(),
};

apdu.extend(data.iter());
apdu.push(0x00);
apdu.to_hex().to_uppercase()
}

pub fn btc_taproot_script_sign(last_one: bool, data: Vec<u8>) -> String {
if data.len() as u32 > LC_MAX {
panic!("data to long");
}

let mut apdu = match last_one {
true => ApduHeader::new(0x80, 0x40, 0x80, 0x80, data.len() as u8).to_array(),
_ => ApduHeader::new(0x80, 0x40, 0x00, 0x80, data.len() as u8).to_array(),
};

apdu.extend(data.iter());
apdu.push(0x00);
apdu.to_hex().to_uppercase()
}

pub fn omni_prepare_data(p1: u8, data: Vec<u8>) -> String {
if data.len() as u32 > LC_MAX {
panic!("data to long");
Expand All @@ -111,6 +154,58 @@ impl BtcApdu {
data.extend(name);
Apdu::register_address(0x37, &data)
}

pub fn btc_single_utxo_sign_prepare(ins: u8, data: &Vec<u8>) -> Vec<String> {
let mut apdu_vec = Vec::new();
let apdu_number = (data.len() - 1) / LC_MAX as usize + 1;
for index in 0..apdu_number {
if index == 0 && index == apdu_number - 1 {
let length = if data.len() % LC_MAX as usize == 0 {
LC_MAX
} else {
(data.len() % LC_MAX as usize) as u32
};
let mut temp_apdu_vec =
ApduHeader::new(0x80, ins, 0x00, 0x80, length as u8).to_array();
temp_apdu_vec.extend_from_slice(&data[index * LC_MAX as usize..]);
apdu_vec.push(hex::encode_upper(temp_apdu_vec));
} else if index == 0 && index != apdu_number - 1 {
let mut temp_apdu_vec =
ApduHeader::new(0x80, ins, 0x00, 0x00, LC_MAX as u8).to_array();
temp_apdu_vec.extend_from_slice(
&data[index * LC_MAX as usize..((index + 1) * LC_MAX as usize) as usize],
);
apdu_vec.push(hex::encode_upper(temp_apdu_vec));
} else if index != 0 && index != apdu_number - 1 {
let mut temp_apdu_vec =
ApduHeader::new(0x80, ins, 0x80, 0x00, LC_MAX as u8).to_array();
temp_apdu_vec.extend_from_slice(
&data[index * LC_MAX as usize..((index + 1) * LC_MAX as usize) as usize],
);
apdu_vec.push(hex::encode_upper(temp_apdu_vec));
} else if index != 0 && index == apdu_number - 1 {
let length = if data.len() % LC_MAX as usize == 0 {
LC_MAX
} else {
(data.len() % LC_MAX as usize) as u32
};
let mut temp_apdu_vec =
ApduHeader::new(0x80, ins, 0x80, 0x80, length as u8).to_array();
temp_apdu_vec.extend_from_slice(&data[index * LC_MAX as usize..]);
apdu_vec.push(hex::encode_upper(temp_apdu_vec));
}
}
return apdu_vec;
}

pub fn btc_single_utxo_sign(index: u8, hash_type: u8, path: &str) -> String {
let path_bytes = path.as_bytes();
let mut apdu =
ApduHeader::new(0x80, 0x45, index, hash_type, path_bytes.len() as u8).to_array();
apdu.extend(path_bytes.iter());
apdu.push(0x00);
apdu.to_hex().to_uppercase()
}
}

pub struct EthApdu();
Expand Down
28 changes: 28 additions & 0 deletions imkey-core/ikc-common/src/coin_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,34 @@ lazy_static! {
network: "TESTNET".to_string(),
seg_wit: "P2WPKH".to_string(),
},
CoinInfo {
coin: "BITCOIN".to_string(),
derivation_path: "m/84'/0'/0'/0/0".to_string(),
curve: CurveType::SECP256k1,
network: "MAINNET".to_string(),
seg_wit: "VERSION_0".to_string(),
},
CoinInfo {
coin: "BITCOIN".to_string(),
derivation_path: "m/84'/1'/0'/0/0".to_string(),
curve: CurveType::SECP256k1,
network: "TESTNET".to_string(),
seg_wit: "VERSION_0".to_string(),
},
CoinInfo {
coin: "BITCOIN".to_string(),
derivation_path: "m/86'/0'/0'/0/0".to_string(),
curve: CurveType::SECP256k1,
network: "MAINNET".to_string(),
seg_wit: "VERSION_1".to_string(),
},
CoinInfo {
coin: "BITCOIN".to_string(),
derivation_path: "m/86'/1'/0'/0/0".to_string(),
curve: CurveType::SECP256k1,
network: "TESTNET".to_string(),
seg_wit: "VERSION_1".to_string(),
},
CoinInfo {
coin: "BITCOINCASH".to_string(),
derivation_path: "m/44'/145'/0'/0/0".to_string(),
Expand Down
2 changes: 2 additions & 0 deletions imkey-core/ikc-common/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ pub struct SignParam {
pub sender: ::prost::alloc::string::String,
#[prost(string, tag = "8")]
pub fee: ::prost::alloc::string::String,
#[prost(string, tag = "9")]
pub seg_wit: ::prost::alloc::string::String,
}
15 changes: 11 additions & 4 deletions imkey-core/ikc-common/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub const VERSION: &str = "2.15.2";
pub const VERSION: &str = "2.16.0";
pub const URL: &str = "https://imkey.online:1000/imkey";
// pub const URL: &str = "https://imkeyserver.com:10444/imkey";

Expand Down Expand Up @@ -44,11 +44,11 @@ pub const TRON_PATH: &str = "m/44'/195'/0'/0/0";

pub const MAX_UTXO_NUMBER: usize = 252;
pub const EACH_ROUND_NUMBER: usize = 5;
pub const DUST_THRESHOLD: i64 = 2730;
pub const MIN_NONDUST_OUTPUT: i64 = 546;
pub const DUST_THRESHOLD: u64 = 2730;
pub const MIN_NONDUST_OUTPUT: u64 = 546;
// max op return size
pub const MAX_OPRETURN_SIZE: usize = 80;
pub const BTC_FORK_DUST: i64 = 546;
pub const BTC_FORK_DUST: u64 = 546;

// imkey device status
pub const IMKEY_DEV_STATUS_INACTIVATED: &str = "inactivated";
Expand Down Expand Up @@ -118,6 +118,13 @@ pub const ETH_TRANSACTION_TYPE_EIP1559: &str = "02";

pub const ETH_MAX_SUPPORT_PAYMENT_LEN: usize = 255;

pub const BTC_PSBT_TRX_PER_PAGE_NUMBER: usize = 3;

pub const BTC_SEG_WIT_TYPE_LEGACY: &str = "NONE";
pub const BTC_SEG_WIT_TYPE_P2WPKH: &str = "P2WPKH";
pub const BTC_SEG_WIT_TYPE_VERSION_0: &str = "VERSION_0";
pub const BTC_SEG_WIT_TYPE_VERSION_1: &str = "VERSION_1";

lazy_static! {
/// Lazily initialized secp256k1 engine
pub static ref SECP256K1_ENGINE: secp256k1::Secp256k1<secp256k1::All> = secp256k1::Secp256k1::new();
Expand Down
4 changes: 4 additions & 0 deletions imkey-core/ikc-common/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,8 @@ pub enum CoinError {
InvalidVersion,
#[error("invalid addr length")]
InvalidAddrLength,
#[error("invalid_utxo")]
InvalidUtxo,
#[error("missing_signature")]
MissingSignature,
}
124 changes: 124 additions & 0 deletions imkey-core/ikc-common/src/hex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use super::Result;
use anyhow::anyhow;

pub trait ToHex {
fn to_hex(&self) -> String;

fn to_0x_hex(&self) -> String {
format!("0x{}", self.to_hex())
}
}

pub trait FromHex
where
Self: Sized,
{
fn from_hex<T: AsRef<[u8]>>(value: T) -> Result<Self>;

fn from_0x_hex<T: AsRef<[u8]>>(value: T) -> Result<Self> {
if value.as_ref().len() == 0 {
return Ok(Self::from_hex("")?);
}

let bytes = value.as_ref();
Self::from_hex(&bytes[2..bytes.len()])
}

fn from_hex_auto<T: AsRef<[u8]>>(value: T) -> Result<Self> {
let bytes = value.as_ref();
if bytes.len() >= 2 && bytes[0] == b'0' && (bytes[1] == b'x' || bytes[1] == b'X') {
Self::from_0x_hex(value)
} else {
Self::from_hex(value)
}
}
}

impl<T: AsRef<[u8]>> ToHex for T {
fn to_hex(&self) -> String {
hex::encode(self)
}
}

impl ToHex for [u8] {
fn to_hex(&self) -> String {
hex::encode(self)
}
}

impl FromHex for Vec<u8> {
fn from_hex<T: AsRef<[u8]>>(value: T) -> Result<Self> {
hex::decode(value).map_err(|e| anyhow!("{}", e.to_string()))
}
}

#[cfg(test)]
mod tests {
use super::{FromHex, ToHex};
#[test]
fn test_to_hex() {
let data = vec![0x01, 0x02, 0x03, 0x04];
assert_eq!(data.to_hex(), "01020304");
}

#[test]
fn test_to_hex_empty() {
let data = vec![];
assert_eq!(data.to_hex(), "");
}

#[test]
fn test_to_hex_with_prefix_empty() {
let data = vec![];
assert_eq!(data.to_0x_hex(), "0x");
}

#[test]
fn test_to_hex_from_slice() {
let data = vec![0x01, 0x02, 0x03, 0x04];
assert_eq!(data[0..2].to_hex(), "0102");
}

#[test]
fn test_to_hex_from_fixed_array() {
let data = [0x01, 0x02, 0x03, 0x04];
assert_eq!(data.to_hex(), "01020304");
}

#[test]
fn test_to_hex_with_prefix() {
let data = vec![0x01, 0x02, 0x03, 0x04];
assert_eq!(data.to_0x_hex(), "0x01020304");
}

#[test]
fn test_from_hex() {
let data = vec![0x01, 0x02, 0x03, 0x04];
assert_eq!(Vec::from_hex("01020304").unwrap(), data);
}

#[test]
fn test_from_hex_with_prefix() {
let value = "0x01020304";
assert_eq!(Vec::from_0x_hex(value).unwrap(), [0x01, 0x02, 0x03, 0x04]);
}

#[test]
fn test_from_hex_with_prefix_error() {
let value = "0x010203041";
assert!(Vec::from_0x_hex(value).is_err(),);
}

#[test]
fn test_from_hex_auto() {
assert_eq!(
Vec::from_hex_auto("0x01020304").unwrap(),
[0x01, 0x02, 0x03, 0x04]
);

assert_eq!(
Vec::from_hex_auto("01020304").unwrap(),
[0x01, 0x02, 0x03, 0x04]
);
}
}
1 change: 1 addition & 0 deletions imkey-core/ikc-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod common;
pub mod constants;
pub mod curve;
pub mod error;
mod hex;
pub mod https;
pub mod path;
pub mod utility;
Expand Down
16 changes: 15 additions & 1 deletion imkey-core/ikc-common/src/utility.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::aes::cbc::encrypt_pkcs7;
use crate::constants::SECP256K1_ENGINE;
use crate::error::CommonError;
use crate::hex::FromHex;
use crate::Result;
use bitcoin::hashes::{sha256, Hash};
use bitcoin::util::base58;
Expand Down Expand Up @@ -65,7 +66,7 @@ pub fn secp256k1_sign_verify(public: &[u8], signed: &[u8], message: &[u8]) -> Re
.is_ok())
}

pub fn bigint_to_byte_vec(val: i64) -> Vec<u8> {
pub fn bigint_to_byte_vec(val: u64) -> Vec<u8> {
let mut return_data = BigInt::from(val).to_signed_bytes_be();
while return_data.len() < 8 {
return_data.insert(0, 0x00);
Expand Down Expand Up @@ -206,6 +207,19 @@ pub fn network_convert(network: &str) -> Network {
}
}

pub fn utf8_or_hex_to_bytes(value: &str) -> Result<Vec<u8>> {
if value.to_lowercase().starts_with("0x") {
let ret = FromHex::from_0x_hex(value);
if ret.is_err() {
Ok(value.as_bytes().to_vec())
} else {
ret
}
} else {
Ok(value.as_bytes().to_vec())
}
}

#[cfg(test)]
mod tests {
use crate::utility;
Expand Down
2 changes: 1 addition & 1 deletion imkey-core/ikc-device/src/device_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ pub fn bind_test() {
// pub const TEST_KEY_PATH: &str = "/tmp/";
// pub const TEST_BIND_CODE: &str = "MCYNK5AH";
pub const TEST_KEY_PATH: &str = "/tmp/";
pub const TEST_BIND_CODE: &str = "7FVRAJJ7";
pub const TEST_BIND_CODE: &str = "5PJT7223";

#[cfg(test)]
mod test {
Expand Down
Loading

0 comments on commit e244c49

Please sign in to comment.