Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bootstrap taproot and new key manager #126

Merged
merged 3 commits into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ edition = "2018"
[features]
rpc = []
experimental = ["ecdsa_fun", "rand", "sha2", "rand_chacha", "bincode"]
taproot = []
serde = ["serde_crate", "bitcoin/use-serde", "monero/serde_support", "inet2_addr/serde"]

default = ["experimental"]
default = ["experimental", "taproot"]

[dependencies]
amplify = "3"
Expand All @@ -45,6 +46,7 @@ ecdsa_fun = { version = "0.6", default-features = false, features = ["all"], opt
sha2 = { version = "0.9", optional = true }
rand_chacha = { version = "0.3.1", optional = true }
bincode = { version = "1.3.3", optional = true }
curve25519-dalek = "3"

# blockchain specific
bitcoin = "0.26"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ As default the `experimental` feature is enable.

- **serde**: enable serde implementation on some of the types in the library.
- **experimental**: enable experimental cryptography, i.e. not battle tested nor peer reviewed and not intended for production use.
- **taproot**: enable support for Bitcoin Taproot on-chain scripts as the arbitrating engine method.

### Adding blockchain support
To add a blockchain implementation you must implements `Aribtrating` or `Accordant` trait on your blockchain definition, the trait implemented depends on its blockchain on-chain features, see [RFCs](https://github.com/farcaster-project/RFCs) for more details.
Expand Down
67 changes: 15 additions & 52 deletions src/bitcoin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,21 @@
use std::fmt::Debug;
use std::marker::PhantomData;

use bitcoin::secp256k1::{
key::{PublicKey, SecretKey},
Signature,
};
use bitcoin::util::psbt::PartiallySignedTransaction;
use bitcoin::Address;
use bitcoin::Amount;

use crate::blockchain::{self, Asset, Onchain, Timelock};
use crate::consensus::{self, CanonicalBytes};

pub(crate) mod address;
pub(crate) mod amount;
pub mod fee;
#[cfg(feature = "experimental")]
#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
pub mod segwitv0;
#[cfg(all(feature = "experimental", feature = "taproot"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "experimental", feature = "taproot"))))]
pub mod taproot;
pub mod tasks;
pub mod timelock;
pub mod transaction;
Expand All @@ -31,16 +29,27 @@ pub mod transaction;
#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
pub type BitcoinSegwitV0 = Bitcoin<segwitv0::SegwitV0>;

/// Bitcoin blockchain using SegWit version 1 transaction outputs and Schnorr cryptography. This
/// type is experimental because its cryptography for Adaptor Signatures is not ready for
/// production and battle tested.
#[cfg(all(feature = "experimental", feature = "taproot"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "experimental", feature = "taproot"))))]
pub type BitcoinTaproot = Bitcoin<taproot::Taproot>;

/// Helper type enumerating over all Bitcoin inner variants available.
#[non_exhaustive]
pub enum Btc {
#[cfg(feature = "experimental")]
#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
SegwitV0(BitcoinSegwitV0),
#[cfg(all(feature = "experimental", feature = "taproot"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "experimental", feature = "taproot"))))]
Taproot(BitcoinTaproot),
}

/// Variations of a Bitcoin implementation. Strategy allows different Bitcoin implementations based
/// on, e.g., the SegWit version such as [`SegwitV0`][segwitv0::SegwitV0].
/// on, e.g., the SegWit version such as [`SegwitV0`][segwitv0::SegwitV0] or
/// [`Taproot`][taproot::Taproot].
pub trait Strategy: Clone + Copy + Debug {}

/// The generic blockchain implementation of Bitcoin. [`Bitcoin`] takes a generic parameter
Expand All @@ -66,7 +75,6 @@ impl<S: Strategy> Default for Bitcoin<S> {
}

impl<S: Strategy> Asset for Bitcoin<S> {
/// Type for quantifying the traded asset.
type AssetUnit = Amount;

fn from_u32(bytes: u32) -> Option<Self> {
Expand All @@ -82,59 +90,14 @@ impl<S: Strategy> Asset for Bitcoin<S> {
}

impl<S: Strategy> blockchain::Address for Bitcoin<S> {
/// Defines the address format used in Bitcoin.
type Address = Address;
}

impl<S: Strategy> Timelock for Bitcoin<S> {
/// Defines the type of timelock used in Bitcoin.
type Timelock = timelock::CSVTimelock;
}

impl<S: Strategy> Onchain for Bitcoin<S> {
/// Defines the transaction format used to transfer partial transaction between participants in
/// Bitcoin.
type PartialTransaction = PartiallySignedTransaction;

/// Defines the finalized transaction format for Bitcoin used by the syncers.
type Transaction = bitcoin::Transaction;
}

impl CanonicalBytes for SecretKey {
fn as_canonical_bytes(&self) -> Vec<u8> {
(&self.as_ref()[..]).into()
}

fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, consensus::Error>
where
Self: Sized,
{
SecretKey::from_slice(bytes).map_err(consensus::Error::new)
}
}

impl CanonicalBytes for PublicKey {
fn as_canonical_bytes(&self) -> Vec<u8> {
self.serialize().as_ref().into()
}

fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, consensus::Error>
where
Self: Sized,
{
PublicKey::from_slice(bytes).map_err(consensus::Error::new)
}
}

impl CanonicalBytes for Signature {
fn as_canonical_bytes(&self) -> Vec<u8> {
self.serialize_compact().into()
}

fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, consensus::Error>
where
Self: Sized,
{
Signature::from_compact(bytes).map_err(consensus::Error::new)
}
}
44 changes: 40 additions & 4 deletions src/bitcoin/segwitv0/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl TryFrom<Btc> for Bitcoin<SegwitV0> {
fn try_from(v: Btc) -> Result<Self, consensus::Error> {
match v {
Btc::SegwitV0(v) => Ok(v),
//_ => Err(consensus::Error::TypeMismatch),
_ => Err(consensus::Error::TypeMismatch),
}
}
}
Expand All @@ -100,10 +100,7 @@ impl Transactions for Bitcoin<SegwitV0> {
}

impl Keys for Bitcoin<SegwitV0> {
/// Private key type for the blockchain
type PrivateKey = SecretKey;

/// Public key type for the blockchain
type PublicKey = PublicKey;

fn extra_keys() -> Vec<u16> {
Expand All @@ -112,6 +109,32 @@ impl Keys for Bitcoin<SegwitV0> {
}
}

impl CanonicalBytes for SecretKey {
fn as_canonical_bytes(&self) -> Vec<u8> {
(&self.as_ref()[..]).into()
}

fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, consensus::Error>
where
Self: Sized,
{
SecretKey::from_slice(bytes).map_err(consensus::Error::new)
}
}

impl CanonicalBytes for PublicKey {
fn as_canonical_bytes(&self) -> Vec<u8> {
self.serialize().as_ref().into()
}

fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, consensus::Error>
where
Self: Sized,
{
PublicKey::from_slice(bytes).map_err(consensus::Error::new)
}
}

impl SharedPrivateKeys for Bitcoin<SegwitV0> {
type SharedPrivateKey = SecretKey;

Expand All @@ -127,6 +150,19 @@ impl Signatures for Bitcoin<SegwitV0> {
type AdaptorSignature = EncryptedSignature;
}

impl CanonicalBytes for Signature {
fn as_canonical_bytes(&self) -> Vec<u8> {
self.serialize_compact().into()
}

fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, consensus::Error>
where
Self: Sized,
{
Signature::from_compact(bytes).map_err(consensus::Error::new)
}
}

impl CanonicalBytes for EncryptedSignature {
fn as_canonical_bytes(&self) -> Vec<u8> {
bincode::serialize(&self).expect("serialization should always work")
Expand Down
134 changes: 134 additions & 0 deletions src/bitcoin/taproot/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
//! Implementation of a Taproot strategy with on-chain scripts for the arbitrating blockchain
//! as Bitcoin. Inner implementation of [`BitcoinTaproot`].

use std::convert::{TryFrom, TryInto};
use std::str::FromStr;

use crate::bitcoin::{Bitcoin, BitcoinTaproot, Btc, Strategy};
use crate::consensus::{self, CanonicalBytes};
use crate::crypto::{Keys, SharedKeyId, SharedPrivateKeys, Signatures};
//use crate::role::Arbitrating;

use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::secp256k1::{
constants::SECRET_KEY_SIZE,
schnorrsig::{KeyPair, PublicKey, Signature},
};

/// Inner type for the Taproot strategy with on-chain scripts.
#[derive(Clone, Debug, Copy, Eq, PartialEq)]
pub struct Taproot;

impl Strategy for Taproot {}

impl FromStr for Bitcoin<Taproot> {
type Err = consensus::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Taproot" | "taproot" => Ok(Self::new()),
_ => Err(consensus::Error::UnknownType),
}
}
}

impl From<BitcoinTaproot> for Btc {
fn from(v: BitcoinTaproot) -> Self {
Self::Taproot(v)
}
}

//impl Arbitrating for Bitcoin<Taproot> {}

impl TryFrom<Btc> for Bitcoin<Taproot> {
type Error = consensus::Error;

fn try_from(v: Btc) -> Result<Self, consensus::Error> {
match v {
Btc::Taproot(v) => Ok(v),
_ => Err(consensus::Error::TypeMismatch),
}
}
}

impl Keys for Bitcoin<Taproot> {
type PrivateKey = KeyPair;
type PublicKey = PublicKey;

fn extra_keys() -> Vec<u16> {
// No extra key
vec![]
}
}

impl CanonicalBytes for PublicKey {
fn as_canonical_bytes(&self) -> Vec<u8> {
self.serialize().as_ref().into()
}

fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, consensus::Error>
where
Self: Sized,
{
PublicKey::from_slice(bytes).map_err(consensus::Error::new)
}
}

/// Schnorr secret key shareable over the network if needed by the protocol.
#[derive(Clone, Debug, Copy, Eq, PartialEq)]
pub struct SecretSharedKey([u8; SECRET_KEY_SIZE]);

impl SecretSharedKey {
/// Return a slice to the secret key bytes.
pub fn as_bytes(&self) -> &[u8] {
&self.0
}

/// Return the secret key bytes.
pub fn to_bytes(self) -> [u8; SECRET_KEY_SIZE] {
self.0
}
}

impl CanonicalBytes for SecretSharedKey {
fn as_canonical_bytes(&self) -> Vec<u8> {
self.0.into()
}

fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, consensus::Error>
where
Self: Sized,
{
Ok(SecretSharedKey(
bytes.try_into().map_err(consensus::Error::new)?,
))
}
}

impl SharedPrivateKeys for Bitcoin<Taproot> {
type SharedPrivateKey = SecretSharedKey;

fn shared_keys() -> Vec<SharedKeyId> {
// No shared key in Bitcoin, transparent ledger
vec![]
}
}

impl Signatures for Bitcoin<Taproot> {
type Message = Sha256dHash;
type Signature = Signature;
type AdaptorSignature = Signature;
}

impl CanonicalBytes for Signature {
fn as_canonical_bytes(&self) -> Vec<u8> {
(*self.as_ref()).into()
}

fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, consensus::Error>
where
Self: Sized,
{
Signature::from_slice(bytes).map_err(consensus::Error::new)
}
}
5 changes: 5 additions & 0 deletions src/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ use crate::consensus::{self, deserialize, serialize, CanonicalBytes, Decodable,
use crate::crypto::{Keys, Signatures};
use crate::transaction::{Buyable, Cancelable, Fundable, Lockable, Punishable, Refundable};

pub enum Blockchain {
Bitcoin,
Monero,
}

/// Defines the type for a blockchain address, this type is used when manipulating transactions.
pub trait Address {
/// Defines the address format for the arbitrating blockchain.
Expand Down
Loading