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

fix: provide coinbase proof #505

Merged
merged 1 commit into from
Jul 27, 2023
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
187 changes: 76 additions & 111 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,4 @@ sp-tracing = { git = "https://github.com/paritytech//substrate", branch = "polka
sp-trie = { git = "https://github.com/paritytech//substrate", branch = "polkadot-v0.9.42" }
sp-version = { git = "https://github.com/paritytech//substrate", branch = "polkadot-v0.9.42" }
sp-wasm-interface = { git = "https://github.com/paritytech//substrate", branch = "polkadot-v0.9.42" }
sp-weights = { git = "https://github.com/paritytech//substrate", branch = "polkadot-v0.9.42" }

[patch."https://github.com/paritytech/polkadot"]
xcm = { git = "https://github.com/paritytech//polkadot", branch = "release-v0.9.42" }
sp-weights = { git = "https://github.com/paritytech//substrate", branch = "polkadot-v0.9.42" }
3 changes: 3 additions & 0 deletions bitcoin/src/electrs/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ pub enum Error {
TryFromIntError(#[from] TryFromIntError),
#[error("ParseIntError: {0}")]
ParseIntError(#[from] ParseIntError),

#[error("No txids in block")]
EmptyBlock,
}

impl Error {
Expand Down
8 changes: 8 additions & 0 deletions bitcoin/src/electrs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ impl ElectrsClient {
Ok(txs)
}

pub(crate) async fn get_coinbase_txid(&self, block_hash: &BlockHash) -> Result<Txid, Error> {
self.get_and_decode::<Vec<String>>(&format!("/block/{block_hash}/txids"))
.await?
.first()
.ok_or(Error::EmptyBlock)
.and_then(|raw_txid| Ok(Txid::from_str(raw_txid)?))
}

pub(crate) async fn get_block(&self, hash: &BlockHash) -> Result<Block, Error> {
let (header, txdata) = try_join(self.get_block_header(hash), self.get_transactions_in_block(hash)).await?;
Ok(Block { header, txdata })
Expand Down
2 changes: 2 additions & 0 deletions bitcoin/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ pub enum Error {
FailedToConstructWalletName,
#[error("AddressError: {0}")]
AddressError(#[from] AddressError),
#[error("Failed to fetch coinbase tx")]
CoinbaseFetchingFailure,
}

impl Error {
Expand Down
2 changes: 1 addition & 1 deletion bitcoin/src/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ mod tests {
Block {
txdata: transactions.into_iter().map(dummy_tx).collect(),
header: BlockHeader {
version: Version::from_consensus(4),
version: Version::from_consensus(2),
bits: CompactTarget::from_consensus(0),
nonce: 0,
time: 0,
Expand Down
35 changes: 26 additions & 9 deletions bitcoin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,21 @@ fn get_exponential_backoff() -> ExponentialBackoff {
}
}

#[derive(PartialEq, Eq, PartialOrd, Clone, Debug)]
pub struct RawTransactionProof {
pub user_tx_proof: Vec<u8>,
pub raw_user_tx: Vec<u8>,
pub coinbase_tx_proof: Vec<u8>,
pub raw_coinbase_tx: Vec<u8>,
}

#[derive(PartialEq, Eq, PartialOrd, Clone, Copy, Debug)]
pub struct SatPerVbyte(pub u64);

#[derive(Debug, Clone)]
pub struct TransactionMetadata {
pub txid: Txid,
pub proof: Vec<u8>,
pub raw_tx: Vec<u8>,
pub proof: RawTransactionProof,
pub block_height: u32,
pub block_hash: BlockHash,
pub fee: Option<SignedAmount>,
Expand Down Expand Up @@ -885,19 +892,29 @@ impl BitcoinCoreApi for BitcoinCore {
.await?;

let proof = retry(get_exponential_backoff(), || async {
Ok(self.get_proof(txid, &block_hash).await?)
})
.await?;

let raw_tx = retry(get_exponential_backoff(), || async {
Ok(self.get_raw_tx(&txid, &block_hash).await?)
// fetch coinbase info..
let block = self.get_block(&block_hash).await?;
let coinbase_tx = block.coinbase().ok_or(Error::CoinbaseFetchingFailure)?;
let coinbase_txid = coinbase_tx.txid();
let coinbase_tx_proof = self.get_proof(coinbase_txid, &block_hash).await?;
let raw_coinbase_tx = self.get_raw_tx(&coinbase_txid, &block_hash).await?;

// fetch user tx info..
let raw_user_tx = self.get_raw_tx(&txid, &block_hash).await?;
let user_tx_proof = self.get_proof(txid, &block_hash).await?;
sander2 marked this conversation as resolved.
Show resolved Hide resolved

Ok(RawTransactionProof {
raw_coinbase_tx,
coinbase_tx_proof,
raw_user_tx,
user_tx_proof,
})
})
.await?;

Ok(TransactionMetadata {
txid,
proof,
raw_tx,
block_height,
block_hash,
fee,
Expand Down
25 changes: 21 additions & 4 deletions bitcoin/src/light/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub use error::Error;

use async_trait::async_trait;
use backoff::future::retry;
use futures::future::{join_all, try_join, try_join_all};
use futures::future::{join_all, try_join, try_join4, try_join_all};
use std::{convert::TryFrom, sync::Arc, time::Duration};
use tokio::{sync::Mutex, time::sleep};

Expand Down Expand Up @@ -82,6 +82,10 @@ impl BitcoinLight {
let txid = self.electrs.send_transaction(transaction.transaction).await?;
Ok(txid)
}

async fn get_coinbase_txid(&self, block_hash: &BlockHash) -> Result<Txid, BitcoinError> {
Ok(self.electrs.get_coinbase_txid(&block_hash).await?)
}
}

#[async_trait]
Expand Down Expand Up @@ -236,15 +240,28 @@ impl BitcoinCoreApi for BitcoinLight {
})
.await?;

let (proof, raw_tx) = retry(get_exponential_backoff(), || async {
Ok(try_join(self.get_proof(txid, &block_hash), self.get_raw_tx(&txid, &block_hash)).await?)
let proof = retry(get_exponential_backoff(), || async {
let coinbase_txid = self.get_coinbase_txid(&block_hash).await?;

let (coinbase_tx_proof, raw_coinbase_tx, user_tx_proof, raw_user_tx) = try_join4(
self.get_proof(coinbase_txid, &block_hash),
self.get_raw_tx(&coinbase_txid, &block_hash),
self.get_proof(txid, &block_hash),
self.get_raw_tx(&txid, &block_hash),
)
.await?;
Ok(RawTransactionProof {
coinbase_tx_proof,
raw_coinbase_tx,
user_tx_proof,
raw_user_tx,
})
})
.await?;

Ok(TransactionMetadata {
txid,
proof,
raw_tx,
block_height,
block_hash,
fee: Some(fee),
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: "3.8"
services:
interbtc:
image: "interlayhq/interbtc:1.25.0-rc1"
image: "interlayhq/interbtc:1.25.0-rc4"
command:
- --rpc-external
- --ws-external
Expand Down
9 changes: 5 additions & 4 deletions runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,23 @@ rand = { version = "0.7", optional = true }

[dependencies.primitives]
git = "https://github.com/interlay/interbtc"
rev = "2ec5b959a547f28d7bab7799568321c9cff40343"
rev = "1b90616692dcce55749616b4c60bdab228279909"
package = "interbtc-primitives"

[dependencies.module-bitcoin]
git = "https://github.com/interlay/interbtc"
rev = "2ec5b959a547f28d7bab7799568321c9cff40343"
rev = "1b90616692dcce55749616b4c60bdab228279909"
package = "bitcoin"
features = ["parser"]

[dependencies.module-btc-relay]
git = "https://github.com/interlay/interbtc"
rev = "2ec5b959a547f28d7bab7799568321c9cff40343"
rev = "1b90616692dcce55749616b4c60bdab228279909"
package = "btc-relay"

[dependencies.module-oracle-rpc-runtime-api]
git = "https://github.com/interlay/interbtc"
rev = "2ec5b959a547f28d7bab7799568321c9cff40343"
rev = "1b90616692dcce55749616b4c60bdab228279909"
package = "oracle-rpc-runtime-api"

[dev-dependencies]
Expand Down
Binary file modified runtime/metadata-parachain-interlay.scale
Binary file not shown.
Binary file modified runtime/metadata-parachain-kintsugi.scale
Binary file not shown.
49 changes: 40 additions & 9 deletions runtime/src/integration/bitcoin_simulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use bitcoin::{
},
secp256k1::{self, constants::SECRET_KEY_SIZE, Secp256k1, SecretKey},
serialize, Address, Amount, BitcoinCoreApi, Block, BlockHash, BlockHeader, Error as BitcoinError, GetBlockResult,
Hash, Network, OutPoint, PartialMerkleTree, PrivateKey, PublicKey, SatPerVbyte, Script, Transaction,
TransactionExt, TransactionMetadata, TxIn, TxMerkleNode, TxOut, Txid, PUBLIC_KEY_SIZE,
Hash, Network, OutPoint, PartialMerkleTree, PrivateKey, PublicKey, RawTransactionProof, SatPerVbyte, Script,
Transaction, TransactionExt, TransactionMetadata, TxIn, TxMerkleNode, TxOut, Txid, PUBLIC_KEY_SIZE,
};
use rand::{thread_rng, Rng};
use std::{convert::TryInto, sync::Arc, time::Duration};
Expand Down Expand Up @@ -54,7 +54,7 @@ impl MockBitcoinCore {
ret.parachain_rpc
.initialize_btc_relay(RawBlockHeader(raw_block_header), 0)
.await
.unwrap();
.expect("failed to initialize relay");

// submit blocks in order to prevent the WaitingForRelayerInitialization error in request_issue
let headers = futures::future::join_all((0..7u32).map(|_| ret.generate_block_with_transaction(&dummy_tx)))
Expand Down Expand Up @@ -192,13 +192,28 @@ impl MockBitcoinCore {
fn generate_coinbase_transaction(address: &BtcAddress, reward: u64, height: u32) -> Transaction {
let address = ScriptBuf::from(address.to_script_pub_key().as_bytes().to_vec());

// construct height: see https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki
// first byte is number of bytes in the number (will be 0x03 on main net for the next
// 150 or so years with 223-1 blocks), following bytes are little-endian representation
// of the number (including a sign bit)
let mut height_bytes = height.to_le_bytes().to_vec();
for i in (1..4).rev() {
// remove trailing zeroes, but always keep first byte even if it's zero
if height_bytes[i] == 0 {
height_bytes.remove(i);
} else {
break;
}
}
height_bytes.insert(0, height_bytes.len() as u8);
Comment on lines +195 to +208
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use the logic in the parsing crate here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess technically yes, but (1) it's not exposed and (2) we're not using the parachain's bitcoin types here, but rather the types used by the bitcoin core rpc. Both of these are not hard blockers but I think it makes it not worth it


// note that we set lock_time to height, otherwise we might generate blocks with
// identical block hashes
Transaction {
input: vec![TxIn {
previous_output: OutPoint::null(), // coinbase
witness: Witness::from_slice::<&[u8]>(&[]),
script_sig: Default::default(),
script_sig: ScriptBuf::from(height_bytes),
sequence: Sequence(u32::max_value()),
}],
output: vec![TxOut {
Expand Down Expand Up @@ -360,7 +375,16 @@ impl BitcoinCoreApi for MockBitcoinCore {

// part two: info about the transactions (we assume the txid is at index 1)
let txids = block.txdata.iter().map(|x| x.txid()).collect::<Vec<_>>();
let partial_merkle_tree = PartialMerkleTree::from_txids(&txids, &[false, true]);
assert_eq!(txids.len(), 2); // expect coinbase and user tx

let partial_merkle_tree = if txids[0] == txid {
PartialMerkleTree::from_txids(&txids, &[true, false])
} else if txids[1] == txid {
PartialMerkleTree::from_txids(&txids, &[false, true])
} else {
panic!("txid not in block")
};

proof.append(&mut serialize(&partial_merkle_tree));

Ok(proof)
Expand Down Expand Up @@ -441,13 +465,20 @@ impl BitcoinCoreApi for MockBitcoinCore {
tokio::time::sleep(Duration::from_secs(1)).await;
};
let block_hash = block.block_hash();
let proof = self.get_proof(txid, &block_hash).await.unwrap();
let raw_tx = self.get_raw_tx(&txid, &block_hash).await.unwrap();
let coinbase_txid = block.coinbase().unwrap().txid();
let coinbase_tx_proof = self.get_proof(coinbase_txid, &block_hash).await.unwrap();
let raw_coinbase_tx = self.get_raw_tx(&coinbase_txid, &block_hash).await.unwrap();
let user_tx_proof = self.get_proof(txid, &block_hash).await.unwrap();
let raw_user_tx = self.get_raw_tx(&txid, &block_hash).await.unwrap();

Ok(TransactionMetadata {
block_hash,
proof,
raw_tx,
proof: RawTransactionProof {
user_tx_proof,
raw_user_tx,
coinbase_tx_proof,
raw_coinbase_tx,
},
txid,
block_height: block_height as u32,
fee: None,
Expand Down
3 changes: 2 additions & 1 deletion runtime/src/integration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub use bitcoin_simulator::MockBitcoinCore;
pub async fn default_root_provider(key: AccountKeyring) -> (InterBtcParachain, TempDir) {
let tmp = TempDir::new("btc-parachain-").expect("failed to create tempdir");
let root_provider = setup_provider(key).await;

try_join(
root_provider.set_bitcoin_confirmations(1),
root_provider.set_parachain_confirmations(1),
Expand Down Expand Up @@ -81,7 +82,7 @@ pub async fn assert_issue(
.unwrap();

parachain_rpc
.execute_issue(*issue.issue_id, &metadata.proof, &metadata.raw_tx)
.execute_issue(*issue.issue_id, &metadata.proof)
.await
.unwrap();
}
Expand Down
12 changes: 8 additions & 4 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ pub const DISABLE_DIFFICULTY_CHECK: &str = "DisableDifficultyCheck";
),
derive_for_type(path = "interbtc_primitives::VaultCurrencyPair", derive = "Eq, PartialEq"),
derive_for_type(path = "interbtc_primitives::VaultId", derive = "Eq, PartialEq"),
derive_for_type(path = "security::types::ErrorCode", derive = "Eq, PartialEq, Ord, PartialOrd"),
derive_for_type(path = "security::types::StatusCode", derive = "Eq, PartialEq"),
substitute_type(path = "primitive_types::H256", with = "::subxt::utils::Static<crate::H256>"),
substitute_type(path = "primitive_types::U256", with = "::subxt::utils::Static<crate::U256>"),
substitute_type(path = "primitive_types::H160", with = "::subxt::utils::Static<crate::H160>"),
Expand Down Expand Up @@ -106,6 +104,10 @@ pub const DISABLE_DIFFICULTY_CHECK: &str = "DisableDifficultyCheck";
path = "bitcoin::types::Transaction",
with = "::subxt::utils::Static<::module_bitcoin::types::Transaction>"
),
substitute_type(
path = "bitcoin::types::FullTransactionProof",
with = "::subxt::utils::Static<::module_bitcoin::types::FullTransactionProof>"
),
)
)]
#[cfg_attr(
Expand All @@ -123,8 +125,6 @@ pub const DISABLE_DIFFICULTY_CHECK: &str = "DisableDifficultyCheck";
),
derive_for_type(path = "interbtc_primitives::VaultCurrencyPair", derive = "Eq, PartialEq"),
derive_for_type(path = "interbtc_primitives::VaultId", derive = "Eq, PartialEq"),
derive_for_type(path = "security::types::ErrorCode", derive = "Eq, PartialEq, Ord, PartialOrd"),
derive_for_type(path = "security::types::StatusCode", derive = "Eq, PartialEq"),
substitute_type(path = "primitive_types::H256", with = "::subxt::utils::Static<crate::H256>"),
substitute_type(path = "primitive_types::U256", with = "::subxt::utils::Static<crate::U256>"),
substitute_type(path = "primitive_types::H160", with = "::subxt::utils::Static<crate::H160>"),
Expand Down Expand Up @@ -158,6 +158,10 @@ pub const DISABLE_DIFFICULTY_CHECK: &str = "DisableDifficultyCheck";
path = "bitcoin::types::Transaction",
with = "::subxt::utils::Static<::module_bitcoin::types::Transaction>"
),
substitute_type(
path = "bitcoin::types::FullTransactionProof",
with = "::subxt::utils::Static<::module_bitcoin::types::FullTransactionProof>"
),
)
)]

Expand Down
Loading