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

multi: implement create_deposit_transaction #60

Merged
merged 1 commit into from
Oct 29, 2024
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
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
44 changes: 44 additions & 0 deletions src/convert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use bdk::bitcoin::consensus::Decodable;

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 _;
let hash: bitcoin::hashes::sha256d::Hash = Hash::from_slice(&bytes).unwrap();

bitcoin::BlockHash::from_raw_hash(hash)
}

pub fn bitcoin_tx_to_bdk_tx(
tx: bitcoin::Transaction,
) -> Result<bdk::bitcoin::Transaction, bdk::bitcoin::consensus::encode::Error> {
let tx_bytes = bitcoin::consensus::serialize(&tx);

let decoded = bdk::bitcoin::Transaction::consensus_decode(&mut tx_bytes.as_slice())?;

Ok(decoded)
}

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

use bitcoin::hashes::sha256d::Hash;
use bitcoin::hashes::Hash as _;
let hash: bitcoin::hashes::sha256d::Hash = Hash::from_byte_array(bytes);

bitcoin::Txid::from_raw_hash(hash)
}

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

use bdk::bitcoin::hashes::sha256d::Hash;
use bdk::bitcoin::hashes::Hash as _;
let hash: bdk::bitcoin::hashes::sha256d::Hash = Hash::from_byte_array(bytes);

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::{sha256d, 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 @@ -228,6 +229,24 @@ pub fn parse_op_drivechain(input: &[u8]) -> IResult<&[u8], SidechainNumber> {
Ok((input, SidechainNumber::from(sidechain_number)))
}

pub fn create_m5_deposit_output(
sidechain_number: SidechainNumber,
old_ctip_amount: Amount,
deposit_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 + deposit_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
86 changes: 64 additions & 22 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use miette::IntoDiagnostic as _;
use tonic::{Request, Response, Status};

use crate::{
convert,
messages::CoinbaseMessage,
proto::{
common::{ConsensusHex, ReverseHex},
Expand Down Expand Up @@ -60,16 +61,6 @@ where
tonic::Status::invalid_argument(err.to_string())
}

fn bdk_block_hash_to_bitcoin_block_hash(hash: bdk::bitcoin::BlockHash) -> bitcoin::BlockHash {
use bdk::bitcoin::hashes::Hash as _;
bitcoin::BlockHash::from_byte_array(hash.to_byte_array())
}

fn bdk_txid_to_bitcoin_txid(txid: bdk::bitcoin::Txid) -> bitcoin::Txid {
use bdk::bitcoin::hashes::Hash as _;
bitcoin::Txid::from_byte_array(txid.to_byte_array())
}

trait IntoStatus {
fn into_status(self) -> tonic::Status;
}
Expand Down Expand Up @@ -294,7 +285,7 @@ impl ValidatorService for Validator {
};

let ctip = self
.get_ctip(sidechain_number)
.try_get_ctip(sidechain_number)
.map_err(|err| err.into_status())?;
if let Some(ctip) = ctip {
let sequence_number = self
Expand Down Expand Up @@ -413,16 +404,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 @@ -776,7 +767,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 All @@ -803,7 +794,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(ReverseHex::encode(&txid)),
Expand All @@ -813,12 +804,63 @@ 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)
.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 = ReverseHex::encode(&txid);
let response = CreateDepositTransactionResponse { txid: Some(txid) };
Ok(tonic::Response::new(response))
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/validator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,9 @@ impl Validator {
Ok(sequence_number)
}

pub fn get_ctip(
/// Returns `Some` with the Ctip for the given sidechain number. `None`
/// if there's no Ctip for the given sidechain number.
torkelrogstad marked this conversation as resolved.
Show resolved Hide resolved
pub fn try_get_ctip(
&self,
sidechain_number: SidechainNumber,
) -> Result<Option<Ctip>, miette::Report> {
Expand Down
5 changes: 5 additions & 0 deletions src/wallet/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,8 @@ pub struct BitcoinCoreRPC {
#[error("failed to consensus encode block")]
#[diagnostic(code(encode_block_error))]
pub struct EncodeBlock(#[from] pub bitcoin::io::Error);

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