diff options
author | Antoine A <> | 2022-02-17 13:03:25 +0100 |
---|---|---|
committer | Antoine A <> | 2022-02-17 13:03:25 +0100 |
commit | 17bbd0949052db402f9fbb532439fd03ab46d219 (patch) | |
tree | 128b70f1a5878d6fed1bef3fce91dc78ee4adc4a /btc-wire | |
parent | 0b51ecda47a222c02b66f65d2d55d104ecfab7e4 (diff) | |
download | depolymerization-17bbd0949052db402f9fbb532439fd03ab46d219.tar.gz depolymerization-17bbd0949052db402f9fbb532439fd03ab46d219.tar.bz2 depolymerization-17bbd0949052db402f9fbb532439fd03ab46d219.zip |
btc-wire: rename magic id to random prefix and improve deposit metadata format documentation
Diffstat (limited to 'btc-wire')
-rw-r--r-- | btc-wire/README.md | 45 | ||||
-rw-r--r-- | btc-wire/src/bin/segwit-demo.rs | 25 | ||||
-rw-r--r-- | btc-wire/src/segwit.rs | 44 |
3 files changed, 81 insertions, 33 deletions
diff --git a/btc-wire/README.md b/btc-wire/README.md index f8823e0..c41e745 100644 --- a/btc-wire/README.md +++ b/btc-wire/README.md @@ -52,12 +52,49 @@ with the same random pattern, at the exception of the first bit with must be 0 for the first half and 1 for the second one. You must then send a single transaction with the three addresses as recipients. -As a few lines of code can carry more meaning that many words you can find a +Segwit addresses are encoded using a bitcoin specific format: +[bech32](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) + +As a few lines of code can carry more meaning that many words, you can find a [simple rust example](src/bin/segwit-demo.rs) in this project and run it with -`make segwit_demo`. +`make segwit_demo`. -Segwit addresses are encoded using a bitcoin specific format: [bech32]( -[bech32](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)) +``` +Ⅰ - Parse payto uri +Got payto uri: payto://bitcoin/bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4?amount=BTC:0.1&subject=0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00 +Send 0.1 BTC to bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 with reserve public key 0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00 + +Ⅱ - Generate fake segwit addresses +Decode reserve public key: 0x07f3d46620a0c138f5131f82d553706df68175cf4b6018b097a5c77e7b4453c0 +Generate random prefix 0x7ea4c272 +Split reserve public key in two: +0x07f3d46620a0c138f5131f82d553706d +0xf68175cf4b6018b097a5c77e7b4453c0 +Concatenate random prefix with each reserve public key half: +0x7ea4c27207f3d46620a0c138f5131f82d553706d +0x7ea4c272f68175cf4b6018b097a5c77e7b4453c0 +Set first bit of the first half: +0x7ea4c27207f3d46620a0c138f5131f82d553706d +Unset first bit of the second half: +0xfea4c272f68175cf4b6018b097a5c77e7b4453c0 +Encode each half using bech32 to generate a segwit address: +bc1q06jvyus8702xvg9qcyu02yclst24xurdjvsnqz +bc1ql6jvyuhks96u7jmqrzcf0fw80ea5g57q2eccn6 + +Ⅲ - Send to many +Send a single bitcoin transaction with the three addresses as recipient as follow: + +In bitcoincore wallet use 'Add Recipient' button to add two additional recipient and copy adresses and amounts +bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 0.10000000 BTC +bc1q06jvyus8702xvg9qcyu02yclst24xurdjvsnqz 0.00000294 BTC +bc1ql6jvyuhks96u7jmqrzcf0fw80ea5g57q2eccn6 0.00000294 BTC + +In Electrum wallet paste the following three lines in 'Pay to' field : +bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4,0.10000000 +bc1q06jvyus8702xvg9qcyu02yclst24xurdjvsnqz,0.00000294 +bc1ql6jvyuhks96u7jmqrzcf0fw80ea5g57q2eccn6,0.00000294 +Make sure the amount show 0.10000588 BTC, else you have to change the base unit to BTC +``` ## Implementation details diff --git a/btc-wire/src/bin/segwit-demo.rs b/btc-wire/src/bin/segwit-demo.rs index 607b6e2..319437c 100644 --- a/btc-wire/src/bin/segwit-demo.rs +++ b/btc-wire/src/bin/segwit-demo.rs @@ -6,7 +6,7 @@ use common::{ }; pub fn main() { - let address = "bc1qgkgxkjj27g3f7s87mcvjjsghay7gh34cx39prj"; + let address = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"; let amount = Amount::from_sat(10000000); let reserve_pub = "0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00"; let btc = amount.as_btc(); @@ -21,17 +21,17 @@ pub fn main() { .try_into() .unwrap(); println!("Decode reserve public key: 0x{}", hex::encode(&decoded[..])); - let magic_id: [u8; 4] = rand_slice(); - println!("Generate magic id: 0x{}", hex::encode(&magic_id)); + let prefix: [u8; 4] = rand_slice(); + println!("Generate random prefix 0x{}", hex::encode(&prefix)); println!( "Split reserve public key in two:\n0x{}\n0x{}", hex::encode(&decoded[..16]), hex::encode(&decoded[16..]) ); - let mut first_half = [&magic_id, &decoded[..16]].concat(); - let mut second_half = [&magic_id, &decoded[16..]].concat(); + let mut first_half = [&prefix, &decoded[..16]].concat(); + let mut second_half = [&prefix, &decoded[16..]].concat(); println!( - "Concatenate magic id with each reserve public key half:\n0x{}\n0x{}", + "Concatenate random prefix with each reserve public key half:\n0x{}\n0x{}", hex::encode(&first_half), hex::encode(&second_half) ); @@ -52,5 +52,16 @@ pub fn main() { println!("\nⅢ - Send to many"); let minimum = rpc_utils::segwit_min_amount().as_btc(); - println!("Send a single bitcoin transaction with the three addresses as recipient as follow:\n{address} {btc}BTC\n{first} {minimum}BTC\n{second} {minimum}BTC"); + println!("Send a single bitcoin transaction with the three addresses as recipient as follow:"); + println!("\nIn bitcoincore wallet use 'Add Recipient' button to add two additional recipient and copy adresses and amounts"); + for (address, amount) in [(address, btc), (&first, minimum), (&second, minimum)] { + println!("{address} {amount:.8} BTC"); + } + println!("\nIn Electrum wallet paste the following three lines in 'Pay to' field :"); + for (address, amount) in [(address, btc), (&first, minimum), (&second, minimum)] { + println!("{address},{amount:.8}"); + } + println!( + "Make sure the amount show 0.10000588 BTC, else you have to change the base unit to BTC" + ) } diff --git a/btc-wire/src/segwit.rs b/btc-wire/src/segwit.rs index 3555ffd..0788936 100644 --- a/btc-wire/src/segwit.rs +++ b/btc-wire/src/segwit.rs @@ -14,7 +14,7 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
use bech32::{u5, FromBase32, ToBase32, Variant};
-use common::rand::{rngs::OsRng, RngCore};
+use common::{rand::rngs::OsRng, rand_slice};
/// Encode metadata into a segwit address
pub fn encode_segwit_addr(hrp: &str, metada: &[u8; 20]) -> String {
@@ -28,12 +28,12 @@ pub fn encode_segwit_addr(hrp: &str, metada: &[u8; 20]) -> String { fn encode_segwit_key_half(
hrp: &str,
is_first: bool,
- magic_id: &[u8; 4],
+ prefix: &[u8; 4],
key_half: &[u8; 16],
) -> String {
- // Combine magic_it and the key half
+ // Combine prefix and the key half
let mut buf = [0u8; 20];
- buf[..4].copy_from_slice(magic_id);
+ buf[..4].copy_from_slice(prefix);
buf[4..].copy_from_slice(key_half);
// Toggle first bit for ordering
if is_first {
@@ -47,15 +47,14 @@ fn encode_segwit_key_half( /// Encode a 32B key into two segwit adresses
pub fn encode_segwit_key(hrp: &str, msg: &[u8; 32]) -> [String; 2] {
- // Generate a random magic identifier
- let mut magic_id = [0; 4];
- OsRng.fill_bytes(&mut magic_id);
+ // Generate a random prefix
+ let prefix = rand_slice();
// Split key in half;
let split: (&[u8; 16], &[u8; 16]) =
(msg[..16].try_into().unwrap(), msg[16..].try_into().unwrap());
[
- encode_segwit_key_half(hrp, true, &magic_id, split.0),
- encode_segwit_key_half(hrp, false, &magic_id, split.1),
+ encode_segwit_key_half(hrp, true, &prefix, split.0),
+ encode_segwit_key_half(hrp, false, &prefix, split.1),
]
}
@@ -63,28 +62,28 @@ pub fn encode_segwit_key(hrp: &str, msg: &[u8; 32]) -> [String; 2] { pub enum DecodeSegWitErr {
#[error("There is less than 2 segwit addresses")]
MissingSegWitAddress,
- #[error("No adresses are sharing a magic id")]
- NoMagicIdMatch,
- #[error("More than two addresses are sharing a magic id")]
- MagicIdCollision,
+ #[error("No adresses are sharing a common prefix")]
+ NoPrefixMatch,
+ #[error("More than two addresses are sharing a common prefix")]
+ PrefixCollision,
}
/// Decode a 32B key into from adresses
pub fn decode_segwit_msg(segwit_addrs: &[impl AsRef<str>]) -> Result<[u8; 32], DecodeSegWitErr> {
// Extract parts from every addresses
- let decoded: Vec<(bool, [u8; 4], [u8; 16])> = segwit_addrs
+ let mut decoded: Vec<(bool, [u8; 4], [u8; 16])> = segwit_addrs
.iter()
.filter_map(|addr| {
bech32::decode(addr.as_ref()).ok().and_then(|(_, wp, _)| {
// Skip version
let pg: Vec<u8> = Vec::from_base32(&wp[1..]).unwrap();
if pg.len() == 20 {
- let mut magic_id: [u8; 4] = pg[..4].try_into().unwrap();
+ let mut prefix: [u8; 4] = pg[..4].try_into().unwrap();
let key_half: [u8; 16] = pg[4..].try_into().unwrap();
let is_first = !pg[0] & 0b1000_0000 == 0;
// Clear first bit
- magic_id[0] &= 0b0111_1111;
- Some((is_first, magic_id, key_half))
+ prefix[0] &= 0b0111_1111;
+ Some((is_first, prefix, key_half))
} else {
None
}
@@ -95,20 +94,21 @@ pub fn decode_segwit_msg(segwit_addrs: &[impl AsRef<str>]) -> Result<[u8; 32], D if decoded.len() < 2 {
return Err(DecodeSegWitErr::MissingSegWitAddress);
}
- // Keep only the addresses with duplicated magic id
+ // Keep only the addresses with duplicated prefix
+ // TODO use sort_unstable_by and partition_dedup_by_key when stable
let matches: Vec<&(bool, [u8; 4], [u8; 16])> = decoded
.iter()
- .filter(|(_, magic, _)| {
+ .filter(|(_, prefix, _)| {
decoded
.iter()
- .filter(|(_, other, _)| other == magic)
+ .filter(|(_, other, _)| other == prefix)
.count()
> 1
})
.collect();
if matches.len() > 2 {
- return Err(DecodeSegWitErr::MagicIdCollision);
+ return Err(DecodeSegWitErr::PrefixCollision);
} else if matches.len() < 2 {
return Err(DecodeSegWitErr::MissingSegWitAddress);
}
@@ -122,7 +122,7 @@ pub fn decode_segwit_msg(segwit_addrs: &[impl AsRef<str>]) -> Result<[u8; 32], D // TODO find a way to hide that function while using it in test and benchmark
pub fn rand_addresses(hrp: &str, key: &[u8; 32]) -> Vec<String> {
- use common::{rand::prelude::SliceRandom, rand_slice};
+ use common::rand::prelude::SliceRandom;
let mut rng_address: Vec<String> =
std::iter::repeat_with(|| encode_segwit_addr(hrp, &rand_slice()))
|