Skip to content

Commit

Permalink
bitcoin: add silent payments crate to support sending to SP addresses
Browse files Browse the repository at this point in the history
BIP-352: https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki

A silent payment address contains two pubkeys, B_scan and
B_spend. From that, Taproot outputs can be created by using a ECDH
shared secret - see the BIP Overview section.

In general, a transaction can send to an arbitrary amount of silent
payment addresses. If there are multiple, then for each unique
B_scan (multiple outputs to the same recipient/B_scan), a counter `k`
is incremented. See
https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki#creating-outputs.

To be able to verify the silent payment address, we need to derive the
output and check that it matches the provided output. For each SP
output, we'd then need to know the SP address and the counter `k`.

The problem with this is that we also need to check, per B_scan, that
each `k=0, 1, ...` is used starting at zero with no holes. This
requires non-constant memory. We could still do it and support a
limited (but likely high) number of SP outputs per transaction, but
that complicates the code. For this reason, we restrict to only one SP
output per transaction.

The added vendored deps serde/serde_json etc. are dev-dependencies to
run the tests in the new crate, not used for firmware builds.
  • Loading branch information
benma committed Jun 9, 2024
1 parent 897e9d3 commit 14cfe3f
Show file tree
Hide file tree
Showing 233 changed files with 66,338 additions and 338 deletions.
66 changes: 63 additions & 3 deletions src/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ members = [
"bitbox02",
"bitbox02-sys",
"erc20_params",
"streaming-silent-payments",
]

resolver = "2"

[workspace.dependencies]
bech32 = { version = "0.10.0-beta", default-features = false }
bitcoin = { version = "0.31.0", default-features = false, features = ["no-std"] }
hex = { version = "0.4", default-features = false, features = ["alloc"] }
lazy_static = { version = "1.4.0" }
Expand Down
2 changes: 1 addition & 1 deletion src/rust/bitbox02-rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ num-bigint = { workspace = true, optional = true }
num-traits = { version = "0.2", default-features = false, optional = true }
# If you change this, also change src/rust/.cargo/config.toml.
bip32-ed25519 = { git = "https://github.com/digitalbitbox/rust-bip32-ed25519", tag = "v0.1.2", optional = true }
bech32 = { version = "0.10.0-beta", default-features = false, optional = true }
bech32 = { workspace = true, optional = true }
blake2 = { version = "0.10.6", default-features = false, optional = true }
minicbor = { version = "0.24.0", default-features = false, features = ["alloc"], optional = true }
crc = { version = "3.0.1", optional = true }
Expand Down
29 changes: 29 additions & 0 deletions src/rust/streaming-silent-payments/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2024 Shift Crypto AG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[package]
name = "streaming-silent-payments"
version = "0.1.0"
authors = ["Shift Crypto AG <[email protected]>"]
edition = "2021"
license = "Apache-2.0"

[dependencies]
bitcoin = { workspace = true }
bech32 = { version = "0.11.0", default-features = false }

[dev-dependencies]
serde = { version = "1.0", features = ["derive"] }
hex = { workspace = true }
serde_json = "1.0"
58 changes: 58 additions & 0 deletions src/rust/streaming-silent-payments/src/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// File copied and adapted from:
// https://github.com/cygnet3/rust-silentpayments/blob/395b153b6d98ea33a59306c1a8a189d4ca152571/src/utils/hash.rs

#![allow(non_snake_case)]

use bitcoin::hashes::{sha256t_hash_newtype, Hash, HashEngine};
use bitcoin::secp256k1::{PublicKey, Scalar};

sha256t_hash_newtype! {
struct InputsTag = hash_str("BIP0352/Inputs");

/// BIP0352-tagged hash with tag \"Inputs\".
///
/// This is used for computing the inputs hash.
#[hash_newtype(forward)]
struct InputsHash(_);

pub(crate) struct SharedSecretTag = hash_str("BIP0352/SharedSecret");

/// BIP0352-tagged hash with tag \"SharedSecret\".
///
/// This hash type is for computing the shared secret.
#[hash_newtype(forward)]
pub(crate) struct SharedSecretHash(_);
}

impl InputsHash {
pub(crate) fn from_outpoint_and_A_sum(
smallest_outpoint: &[u8; 36],
A_sum: PublicKey,
) -> InputsHash {
let mut eng = InputsHash::engine();
eng.input(smallest_outpoint);
eng.input(&A_sum.serialize());
InputsHash::from_engine(eng)
}
pub(crate) fn to_scalar(self) -> Scalar {
// This is statistically extremely unlikely to panic.
Scalar::from_be_bytes(self.to_byte_array()).unwrap()
}
}

impl SharedSecretHash {
pub(crate) fn from_ecdh_and_k(ecdh: &PublicKey, k: u32) -> SharedSecretHash {
let mut eng = SharedSecretHash::engine();
eng.input(&ecdh.serialize());
eng.input(&k.to_be_bytes());
SharedSecretHash::from_engine(eng)
}
}

pub(crate) fn calculate_input_hash(outpoint: &bitcoin::OutPoint, A_sum: PublicKey) -> Scalar {
let mut buffer = [0u8; 36];

buffer[..32].copy_from_slice(outpoint.txid.as_ref());
buffer[32..].copy_from_slice(&outpoint.vout.to_le_bytes());
InputsHash::from_outpoint_and_A_sum(&buffer, A_sum).to_scalar()
}
Loading

0 comments on commit 14cfe3f

Please sign in to comment.