summaryrefslogtreecommitdiff
path: root/btc-wire
diff options
context:
space:
mode:
authorAntoine A <>2022-02-17 13:03:25 +0100
committerAntoine A <>2022-02-17 13:03:25 +0100
commit17bbd0949052db402f9fbb532439fd03ab46d219 (patch)
tree128b70f1a5878d6fed1bef3fce91dc78ee4adc4a /btc-wire
parent0b51ecda47a222c02b66f65d2d55d104ecfab7e4 (diff)
downloaddepolymerization-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.md45
-rw-r--r--btc-wire/src/bin/segwit-demo.rs25
-rw-r--r--btc-wire/src/segwit.rs44
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()))