6个版本 (3个稳定版)
使用旧Rust 2015版
1.1.0 | 2020年1月2日 |
---|---|
1.0.1 | 2019年11月10日 |
1.0.0 | 2019年10月3日 |
0.2.1 | 2019年7月26日 |
0.1.0 | 2018年9月23日 |
#16 in #bip-32
每月41次下载
200KB
3.5K SLoC
Rust语言编写的比特币钱包库
这是一个使用Rust语言编写的构建比特币钱包的库。它使用BIP32密钥派生、BIP39助记词和BIP44、BIP48、BIP84密钥层次结构,使其与TREZOR、Ledger和其他许多钱包兼容。
此库支持TREZOR Model T中可用的SLIP-0039 Shamir共享秘密方案
它支持用于单密钥签名的旧P2PKH、过渡P2SHWPKH和原生segwit P2WPKH,以及用于任意脚本的原生P2WSH。
基本账户使用
MasterAccount
保存一个加密种子,该种子意味着BIP32根密钥。向其中添加任何数量的 Account
以根据BIP44派生层次结构。账户将具有统一类型的地址。
const PASSPHRASE: &str = "correct horse battery staple";
// create a new random master account. This holds the root BIP32 key
// PASSPHRASE is used to encrypt the seed in memory and in storage
let mut master = MasterAccount::new(MasterKeyEntropy::Sufficient, Network::Bitcoin, PASSPHRASE).unwrap();
// or re-create a master from a known mnemonic
let words = "announce damage viable ticket engage curious yellow ten clock finish burden orient faculty rigid smile host offer affair suffer slogan mercy another switch park";
let mnemonic = Mnemonic::from_str(words).unwrap();
// PASSPHRASE is used to encrypt the seed in memory and in storage
// last argument is option password for plausible deniability
let mut master = MasterAccount::from_mnemonic(&mnemonic, 0, Network::Bitcoin, PASSPHRASE, None).unwrap();
// The master accounts only store public keys
// Private keys are created on-demand from encrypted seed with an Unlocker and forgotten as soon as possible
// create an unlocker that is able to decrypt the encrypted mnemonic and then calculate private keys
let mut unlocker = Unlocker::new_for_master(&master, PASSPHRASE).unwrap();
// The unlocker is needed to create accounts within the master account as
// key derivation follows BIP 44, which requires private key derivation
// create a P2PKH (pay-to-public-key-hash) (legacy) account.
// account number 0, sub-account 0 (which usually means receiver) BIP32 look-ahead 10
let account = Account::new(&mut unlocker, AccountAddressType::P2PKH, 0, 0, 10).unwrap();
master.add_account(account);
// create a P2SHWPKH (pay-to-script-hash-witness-public-key-hash) (transitional single key segwit) account.
// account number 0, sub-account 1 (which usually means change) BIP32 look-ahead 10
let account = Account::new(&mut unlocker, AccountAddressType::P2SHWPKH, 0, 1, 10).unwrap();
master.add_account(account);
// create a P2WPKH (pay-to-witness-public-key-hash) (native single key segwit) account.
// account number 1, sub-account 0 (which usually means receiver) BIP32 look-ahead 10
let account = Account::new(&mut unlocker, AccountAddressType::P2WPKH, 1, 0, 10).unwrap();
master.add_account(account);
// account number 1, sub-account 0 (which usually means change) BIP32 look-ahead 10
let account = Account::new(&mut unlocker, AccountAddressType::P2WPKH, 1, 1, 10).unwrap();
master.add_account(account);
// get next legacy receiver address
let source = master.get_mut((0,0)).unwrap().next_key().unwrap().address.clone();
// pay to some native segwit address
let target = master.get_mut((1,0)).unwrap().next_key().unwrap().address.clone();
// change to some transitional address
let change = master.get_mut((0,1)).unwrap().next_key().unwrap().address.clone();
// a dummy transaction to send to source
let input_transaction = Transaction {
input: vec![
TxIn {
previous_output: OutPoint { txid: sha256d::Hash::default(), vout: 0 },
sequence: RBF,
witness: Vec::new(),
script_sig: Script::new(),
}
],
output: vec![
TxOut {
script_pubkey: source.script_pubkey(),
value: 5000000000,
}
],
lock_time: 0,
version: 2,
};
let txid = input_transaction.txid();
const RBF: u32 = 0xffffffff - 2;
// a dummy transaction that spends source
let mut spending_transaction = Transaction {
input: vec![
TxIn {
previous_output: OutPoint { txid, vout: 0 },
sequence: RBF,
witness: Vec::new(),
script_sig: Script::new(),
}
],
output: vec![
TxOut {
script_pubkey: target.script_pubkey(),
value: 4000000000,
},
TxOut {
script_pubkey: change.script_pubkey(),
value: 999999000,
}
],
lock_time: 0,
version: 2,
};
// helper to find previous outputs
let mut spent = HashMap::new();
spent.insert(txid, input_transaction.clone());
// sign the spend
master.sign(&mut spending_transaction, SigHashType::All,
&(|_| Some(input_transaction.output[0].clone())),
&mut unlocker).expect("can not sign");
// verify the spend with the bitcoinconsensus library
spending_transaction.verify(|point|
spent.get(&point.txid).and_then(|t| t.output.get(point.vout as usize).cloned())
).expect("Bitcoin Core would not like this")
高级账户使用
const CSV:u16 = 10; // 10 blocks relative lock
// create a P2WSH (pay-to-witness-script-hash) (native segwit for arbitrary scripts) account
let account = Account::new(&mut unlocker, AccountAddressType::P2WSH(4711), 2, 0, 0).unwrap();
master.add_account(account);
{
let account = master.get_mut((2, 0)).unwrap();
let scripter = |pk: &PublicKey, csv: Option<u16>| Builder::new()
.push_int(csv.unwrap() as i64)
.push_opcode(all::OP_CSV)
.push_opcode(all::OP_DROP)
.push_slice(pk.to_bytes().as_slice())
.push_opcode(all::OP_CHECKSIG)
.into_script();
account.add_script_key(scripter, Some(&[0x01; 32]), Some(CSV)).unwrap();
}
硬币使用
// create a coin store
let mut coins = Coins::new();
// put all coins from block into coin store that are ours (means master can sign for them)
coins.process(&mut master, &block);
// calculate balances
let confirmed = coins.confirmed_balance();
let unconfirmed = coins.unconfirmed_balance();
// means not OP_CSV time locked
let available = coins.available_balance();
// undo the highest block as it was removed through re-org
coins.unwind_tip(block_hash);
// choose inputs to spend
let inputs = choose_inputs (minimum_amount_needed, current_block_height, |h| height_of_block(h));
Shamir的密钥份额
// create an new random account
let master = MasterAccount::new(MasterKeyEntropy::Low, Network::Bitcoin, PASSPHRASE).unwrap();
// extract seed
let seed = master.seed(Network::Bitcoin, PASSPHRASE).unwrap();
// cut seed into 5 shares such that any 3 of them is sufficient to re-construct
let shares = ShamirSecretSharing::generate(1, &[(3,5)], &seed, None, 1).unwrap();
// re-construct seed from the first 3
let reconstructed_seed = ShamirSecretSharing::combine(&shares[..3], None).unwrap();
// re-construct master from seed
let reconstructed_master = MasterAccount::from_seed(&reconstructed_seed, 0, Network::Bitcoin, PASSPHRASE).unwrap();
// prove that everything went fine
assert_eq!(master.master_public(), reconstructed_master.master_public());
assert_eq!(master.encrypted(), reconstructed_master.encrypted());
依赖项
~7.5MB
~123K SLoC