Skip to content

Commit

Permalink
multi: implement create_deposit_transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
torkelrogstad committed Oct 24, 2024
1 parent a69646e commit dc7fa85
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 60 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
password=password
signetblocktime=60
signetchallenge=00141f61d57873d70d28bd28b3c9f9d6bf818b5a0d6a
acceptnonstdtxn=1 # Important! Otherwise Core rejects OP_DRIVECHAIN TXs
# this can also be set to a different address, as long
# as you set the CLI arg for bip300301_enforcer
Expand Down Expand Up @@ -35,7 +36,7 @@ $ cargo run -- --help
# Adjust these parameters to match your local Bitcoin
# Core instance
$ cargo run -- \
--node-rpc-port=38332 \
--node-rpc-addr-=localhost:38332 \
--node-rpc-user=user \
--node-rpc-pass=password \
--node-zmq-addr-sequence=tcp://0.0.0.0:29000
Expand Down
32 changes: 32 additions & 0 deletions src/convert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
pub fn bdk_block_hash_to_bitcoin_block_hash(hash: bdk::bitcoin::BlockHash) -> bitcoin::BlockHash {
use bdk::bitcoin::hashes::Hash as _;
let bytes = hash.as_byte_array().to_vec();

use bitcoin::hashes::sha256d::Hash;
use bitcoin::hashes::Hash as Hm;
let hash: bitcoin::hashes::sha256d::Hash = Hash::from_slice(&bytes).unwrap();

bitcoin::BlockHash::from_raw_hash(hash)
}

pub fn bdk_txid_to_bitcoin_txid(hash: bdk::bitcoin::Txid) -> bitcoin::Txid {
use bdk::bitcoin::hashes::Hash as _;
let bytes = hash.as_byte_array().to_vec();

use bitcoin::hashes::sha256d::Hash;
use bitcoin::hashes::Hash as _;
let hash: bitcoin::hashes::sha256d::Hash = Hash::from_slice(&bytes).unwrap();

bitcoin::Txid::from_raw_hash(hash)
}

pub fn bitcoin_txid_to_bdk_txid(hash: bitcoin::Txid) -> bdk::bitcoin::Txid {
use bitcoin::hashes::Hash as _;
let bytes = hash.as_byte_array().to_vec();

use bdk::bitcoin::hashes::sha256d::Hash;
use bdk::bitcoin::hashes::Hash as _;
let hash: bdk::bitcoin::hashes::sha256d::Hash = Hash::from_slice(&bytes).unwrap();

bdk::bitcoin::Txid::from_raw_hash(hash)
}
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use tower_http::trace::{DefaultOnFailure, DefaultOnResponse, TraceLayer};
use tracing_subscriber::{filter as tracing_filter, layer::SubscriberExt};

mod cli;
mod convert;
mod messages;
mod proto;
mod rpc_client;
Expand Down
21 changes: 20 additions & 1 deletion src/messages.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use bitcoin::script::{Instruction, Instructions};
use bitcoin::{
hashes::Hash,
opcodes::{
all::{OP_NOP5, OP_PUSHBYTES_1, OP_RETURN},
OP_TRUE,
},
script::{Instruction, Instructions, PushBytesBuf},
script::PushBytesBuf,
Amount, Opcode, Script, ScriptBuf, Transaction, TxOut,
};
use byteorder::{ByteOrder, LittleEndian};
Expand Down Expand Up @@ -218,6 +219,24 @@ pub fn parse_op_drivechain(input: &[u8]) -> IResult<&[u8], u8> {
Ok((input, sidechain_number))
}

pub fn create_m5_deposit_output(
sidechain_number: SidechainNumber,
old_ctip_amount: Amount,
amount: Amount,
) -> TxOut {
let script_pubkey = ScriptBuf::from_bytes(vec![
OP_DRIVECHAIN.to_u8(),
OP_PUSHBYTES_1.to_u8(),
sidechain_number.into(),
OP_TRUE.to_u8(),
]);
TxOut {
script_pubkey,
// All deposits INCREASE the amount locked in the OP_DRIVECHAIN output.
value: old_ctip_amount + amount,
}
}

fn parse_m1_propose_sidechain(input: &[u8]) -> IResult<&[u8], CoinbaseMessage> {
let (input, sidechain_number) = take(1usize)(input)?;
let sidechain_number = sidechain_number[0];
Expand Down
98 changes: 66 additions & 32 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ use futures::{stream::BoxStream, StreamExt, TryStreamExt as _};
use miette::{IntoDiagnostic, Result};
use tonic::{Request, Response, Status};

use crate::types;
pub use crate::validator::Validator;
use crate::{convert, types};

fn invalid_field_value<Message>(field_name: &str, value: &str) -> tonic::Status
where
Expand All @@ -53,26 +53,6 @@ where
tonic::Status::invalid_argument(err.to_string())
}

fn bdk_block_hash_to_bitcoin_block_hash(hash: bdk::bitcoin::BlockHash) -> bitcoin::BlockHash {
let bytes = hash.as_byte_array().to_vec();

use bitcoin::hashes::sha256d::Hash;
use bitcoin::hashes::Hash as Hm;
let hash: bitcoin::hashes::sha256d::Hash = Hash::from_slice(&bytes).unwrap();

bitcoin::BlockHash::from_raw_hash(hash)
}

fn bdk_txid_to_bitcoin_txid(hash: bdk::bitcoin::Txid) -> bitcoin::Txid {
let bytes = hash.as_byte_array().to_vec();

use bitcoin::hashes::sha256d::Hash;
use bitcoin::hashes::Hash as Hm;
let hash: bitcoin::hashes::sha256d::Hash = Hash::from_slice(&bytes).unwrap();

bitcoin::Txid::from_raw_hash(hash)
}

trait IntoStatus {
fn into_status(self) -> tonic::Status;
}
Expand Down Expand Up @@ -417,16 +397,16 @@ impl ValidatorService for Validator {
})
.transpose()?
.map(|bytes| {
bdk_block_hash_to_bitcoin_block_hash(bdk::bitcoin::BlockHash::from_byte_array(
bytes,
))
convert::bdk_block_hash_to_bitcoin_block_hash(
bdk::bitcoin::BlockHash::from_byte_array(bytes),
)
});

let end_block_hash: BlockHash = end_block_hash
.ok_or_else(|| missing_field::<GetTwoWayPegDataRequest>("end_block_hash"))?
.decode_tonic::<GetTwoWayPegDataRequest, _>("end_block_hash")
.map(bdk::bitcoin::BlockHash::from_byte_array)
.map(bdk_block_hash_to_bitcoin_block_hash)?;
.map(convert::bdk_block_hash_to_bitcoin_block_hash)?;

match self.get_two_way_peg_data(start_block_hash, end_block_hash) {
Err(err) => Err(tonic::Status::from_error(Box::new(err))),
Expand Down Expand Up @@ -639,7 +619,7 @@ impl WalletService for Arc<crate::wallet::Wallet> {

// If the mainchain tip has progressed beyond this, the request is already
// expired.
if mainchain_tip != bdk_block_hash_to_bitcoin_block_hash(prev_bytes) {
if mainchain_tip != convert::bdk_block_hash_to_bitcoin_block_hash(prev_bytes) {
let message = format!(
"invalid prev_bytes {}: expected {}",
prev_bytes, mainchain_tip
Expand Down Expand Up @@ -667,7 +647,7 @@ impl WalletService for Arc<crate::wallet::Wallet> {
.await
.map_err(|err| err.into_status())?;

let txid = bdk_txid_to_bitcoin_txid(txid);
let txid = convert::bdk_txid_to_bitcoin_txid(txid);

let response = CreateBmmCriticalDataTransactionResponse {
txid: Some(ConsensusHex::encode(&txid)),
Expand All @@ -677,11 +657,65 @@ impl WalletService for Arc<crate::wallet::Wallet> {

async fn create_deposit_transaction(
&self,
_request: tonic::Request<CreateDepositTransactionRequest>,
request: tonic::Request<CreateDepositTransactionRequest>,
) -> std::result::Result<tonic::Response<CreateDepositTransactionResponse>, tonic::Status> {
Err(tonic::Status::new(
tonic::Code::Unimplemented,
"not implemented",
))
let CreateDepositTransactionRequest {
sidechain_id,
address,
value_sats,
fee_sats,
} = request.into_inner();

let value_sats = Amount::from_sat(value_sats);
let fee_sats = match fee_sats {
0 => None,
fee => Some(Amount::from_sat(fee)),
};

let sidechain_id: SidechainNumber = {
<u8 as TryFrom<_>>::try_from(sidechain_id)
.map_err(|_| {
invalid_field_value::<CreateDepositTransactionRequest>(
"sidechain_id",
&sidechain_id.to_string(),
)
})?
.into()
};

if value_sats.to_sat() == 0 {
return Err(invalid_field_value::<CreateDepositTransactionRequest>(
"value_sats",
&value_sats.to_string(),
));
}

if address.is_empty() {
return Err(invalid_field_value::<CreateDepositTransactionRequest>(
"address", &address,
));
}

if !self
.is_sidechain_active(sidechain_id)
.await
.map_err(|err| err.into_status())?
{
return Err(tonic::Status::new(
tonic::Code::FailedPrecondition,
format!("sidechain {sidechain_id} is not active"),
));
}

let txid = self
.create_deposit(sidechain_id, address, value_sats, fee_sats)
.await
.map_err(|err| err.into_status())?;

let txid = convert::bdk_txid_to_bitcoin_txid(txid);

let txid = ConsensusHex::encode(&txid);
let response = CreateDepositTransactionResponse { txid: Some(txid) };
Ok(tonic::Response::new(response))
}
}
8 changes: 8 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::fmt;
use std::fmt::Display;
use std::num::TryFromIntError;

use bitcoin::{Amount, BlockHash, OutPoint, TxOut, Work};
Expand All @@ -14,6 +16,12 @@ pub type Hash256 = [u8; 32];
#[serde(transparent)]
pub struct SidechainNumber(pub u8);

impl Display for SidechainNumber {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}

impl From<u8> for SidechainNumber {
#[inline(always)]
fn from(sidechain_number: u8) -> Self {
Expand Down
2 changes: 2 additions & 0 deletions src/validator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ impl Validator {
Ok(sequence_number)
}

/// Returns `Some` with the Ctip for the given sidechain number. `None`
/// if there's no Ctip for the given sidechain number.
pub fn get_ctip(
&self,
sidechain_number: SidechainNumber,
Expand Down
5 changes: 5 additions & 0 deletions src/wallet/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,8 @@ pub fn convert_bdk_error(err: bdk::Error) -> miette::Report {
Err(json_deserialize_err) => miette::Report::new(json_deserialize_err),
}
}

#[derive(Debug, Diagnostic, Error)]
#[error("BDK wallet error: {0}")]
#[diagnostic(code(bdk_wallet_error))]
pub struct WalletError(#[from] pub bdk::Error);
Loading

0 comments on commit dc7fa85

Please sign in to comment.