diff --git a/coins/bitcoin/src/rpc.rs b/coins/bitcoin/src/rpc.rs index 73a215e0..687b6eea 100644 --- a/coins/bitcoin/src/rpc.rs +++ b/coins/bitcoin/src/rpc.rs @@ -149,11 +149,11 @@ impl Rpc { /// Get the hash of a block by the block's number. pub async fn get_block_hash(&self, number: usize) -> Result<[u8; 32], RpcError> { - let mut hash = *self + let mut hash = self .rpc_call::("getblockhash", json!([number])) .await? .as_raw_hash() - .as_byte_array(); + .to_byte_array(); // bitcoin stores the inner bytes in reverse order. hash.reverse(); Ok(hash) diff --git a/coins/bitcoin/src/wallet/send.rs b/coins/bitcoin/src/wallet/send.rs index e6997c5b..a33fe4f1 100644 --- a/coins/bitcoin/src/wallet/send.rs +++ b/coins/bitcoin/src/wallet/send.rs @@ -13,6 +13,7 @@ use k256::{elliptic_curve::sec1::ToEncodedPoint, Scalar}; use frost::{curve::Secp256k1, Participant, ThresholdKeys, FrostError, sign::*}; use bitcoin::{ + hashes::Hash, sighash::{TapSighashType, SighashCache, Prevouts}, absolute::LockTime, script::{PushBytesBuf, ScriptBuf}, @@ -245,6 +246,13 @@ impl SignableTransaction { }) } + /// Returns the TX ID of the transaction this will create. + pub fn txid(&self) -> [u8; 32] { + let mut res = self.tx.txid().to_byte_array(); + res.reverse(); + res + } + /// Returns the outputs this transaction will create. pub fn outputs(&self) -> &[TxOut] { &self.tx.output diff --git a/coins/bitcoin/tests/wallet.rs b/coins/bitcoin/tests/wallet.rs index 7106a456..01a035c9 100644 --- a/coins/bitcoin/tests/wallet.rs +++ b/coins/bitcoin/tests/wallet.rs @@ -279,6 +279,7 @@ async_sequential! { FEE ).unwrap(); let needed_fee = tx.needed_fee(); + let expected_id = tx.txid(); let tx = sign(&keys, tx); assert_eq!(tx.output.len(), 3); @@ -322,6 +323,7 @@ async_sequential! { let mut hash = *tx.txid().as_raw_hash().as_byte_array(); hash.reverse(); assert_eq!(tx, rpc.get_transaction(&hash).await.unwrap()); + assert_eq!(expected_id, hash); } async fn test_data() { diff --git a/processor/src/networks/bitcoin.rs b/processor/src/networks/bitcoin.rs index b0c37b56..a4c8c89e 100644 --- a/processor/src/networks/bitcoin.rs +++ b/processor/src/networks/bitcoin.rs @@ -21,7 +21,7 @@ use bitcoin_serai::{ consensus::{Encodable, Decodable}, script::Instruction, address::{NetworkChecked, Address as BAddress}, - OutPoint, TxOut, Transaction, Block, Network as BNetwork, + Transaction, Block, Network as BNetwork, }, wallet::{ tweak_keys, address_payload, ReceivedOutput, Scanner, TransactionError, @@ -37,7 +37,7 @@ use bitcoin_serai::bitcoin::{ sighash::{EcdsaSighashType, SighashCache}, script::{PushBytesBuf, Builder}, absolute::LockTime, - Sequence, Script, Witness, TxIn, Amount as BAmount, + Amount as BAmount, Sequence, Script, Witness, OutPoint, TxOut, TxIn, transaction::Version, }; @@ -206,32 +206,22 @@ impl TransactionTrait for Transaction { } #[derive(Clone, PartialEq, Eq, Debug)] -pub struct Eventuality { - // We need to bind to the plan. While we could bind to the plan ID via an OP_RETURN, plans will - // use distinct inputs and this is accordingly valid as a binding to a specific plan. - plan_binding_input: OutPoint, - outputs: Vec, -} +pub struct Eventuality([u8; 32]); impl EventualityTrait for Eventuality { fn lookup(&self) -> Vec { - let mut buf = Vec::with_capacity(32 + 4); - self.plan_binding_input.consensus_encode(&mut buf).unwrap(); - buf + self.0.to_vec() } fn read(reader: &mut R) -> io::Result { - let plan_binding_input = OutPoint::consensus_decode(reader) - .map_err(|_| io::Error::other("couldn't decode outpoint in eventuality"))?; - let outputs = Vec::::consensus_decode(reader) - .map_err(|_| io::Error::other("couldn't decode outputs in eventuality"))?; - Ok(Eventuality { plan_binding_input, outputs }) + let mut id = [0; 32]; + reader + .read_exact(&mut id) + .map_err(|_| io::Error::other("couldn't decode ID in eventuality"))?; + Ok(Eventuality(id)) } fn serialize(&self) -> Vec { - let mut buf = Vec::with_capacity(32 + 4 + 4 + (self.outputs.len() * (8 + 32))); - self.plan_binding_input.consensus_encode(&mut buf).unwrap(); - self.outputs.consensus_encode(&mut buf).unwrap(); - buf + self.0.to_vec() } } @@ -653,23 +643,7 @@ impl Network for Bitcoin { res: &mut HashMap<[u8; 32], (usize, Transaction)>, ) { for tx in &block.txdata[1 ..] { - let input = &tx.input[0].previous_output; - let mut lookup = Vec::with_capacity(4 + 32); - input.consensus_encode(&mut lookup).unwrap(); - if let Some((plan, eventuality)) = eventualities.map.remove(&lookup) { - // Sanity, as this is guaranteed by how the lookup is performed - assert_eq!(input, &eventuality.plan_binding_input); - // If the multisig is honest, then the Eventuality's outputs should match the outputs of - // this transaction - // This panic is fine as this multisig being dishonest will require intervention on - // Substrate to trigger a slash, and then an update to the processor to handle the exact - // adjustments needed - // Panicking here is effectively triggering the halt we need to perform anyways - assert_eq!( - tx.output, eventuality.outputs, - "dishonest multisig spent input on distinct set of outputs" - ); - + if let Some((plan, _)) = eventualities.map.remove(tx.id().as_slice()) { res.insert(plan, (eventualities.block_number, tx.clone())); } } @@ -744,13 +718,8 @@ impl Network for Bitcoin { RecommendedTranscript::new(b"Serai Processor Bitcoin Transaction Transcript"); transcript.append_message(b"plan", plan_id); - let plan_binding_input = *inputs[0].output.outpoint(); - let outputs = signable.outputs().to_vec(); - - ( - SignableTransaction { transcript, actual: signable }, - Eventuality { plan_binding_input, outputs }, - ) + let eventuality = Eventuality(signable.txid()); + (SignableTransaction { transcript, actual: signable }, eventuality) }, )) } @@ -785,8 +754,7 @@ impl Network for Bitcoin { } fn confirm_completion(&self, eventuality: &Self::Eventuality, tx: &Transaction) -> bool { - (eventuality.plan_binding_input == tx.input[0].previous_output) && - (eventuality.outputs == tx.output) + eventuality.0 == tx.id() } #[cfg(test)] diff --git a/tests/full-stack/src/tests/mint_and_burn.rs b/tests/full-stack/src/tests/mint_and_burn.rs index 617b6385..c74a28d9 100644 --- a/tests/full-stack/src/tests/mint_and_burn.rs +++ b/tests/full-stack/src/tests/mint_and_burn.rs @@ -35,7 +35,7 @@ async fn mint_and_burn_test() { // Helper to mine a block on each network async fn mine_blocks( - handles: &Vec, + handles: &[Handles], ops: &DockerOperations, producer: &mut usize, count: usize,