Skip to content

Commit

Permalink
Prevent front-running runes
Browse files Browse the repository at this point in the history
  • Loading branch information
casey committed Mar 12, 2024
1 parent fd15242 commit bac3149
Show file tree
Hide file tree
Showing 32 changed files with 2,612 additions and 2,483 deletions.
2 changes: 2 additions & 0 deletions crates/test-bitcoincore-rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ pub struct TransactionTemplate<'a> {
pub op_return_index: Option<usize>,
pub output_values: &'a [u64],
pub outputs: usize,
pub p2tr: bool,
}

#[derive(Serialize, Deserialize)]
Expand Down Expand Up @@ -181,6 +182,7 @@ impl<'a> Default for TransactionTemplate<'a> {
op_return_index: None,
output_values: &[],
outputs: 1,
p2tr: false,
}
}
}
Expand Down
111 changes: 67 additions & 44 deletions crates/test-bitcoincore-rpc/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,26 +221,36 @@ impl Api for Server {
vout: u32,
_include_mempool: Option<bool>,
) -> Result<Option<GetTxOutResult>, jsonrpc_core::Error> {
Ok(
self
.state()
.utxos
.get(&OutPoint { txid, vout })
.map(|&value| GetTxOutResult {
bestblock: BlockHash::all_zeros(),
confirmations: 0,
value,
script_pub_key: GetRawTransactionResultVoutScriptPubKey {
asm: String::new(),
hex: Vec::new(),
req_sigs: None,
type_: None,
addresses: Vec::new(),
address: None,
},
coinbase: false,
}),
)
let state = self.state();

let Some(value) = state.utxos.get(&OutPoint { txid, vout }) else {
return Ok(None);
};

let mut confirmations = None;

for (height, hash) in state.hashes.iter().enumerate() {
for tx in &state.blocks[hash].txdata {
if tx.txid() == txid {
confirmations = Some(state.hashes.len() - height);
}
}
}

Ok(Some(GetTxOutResult {
bestblock: BlockHash::all_zeros(),
coinbase: false,
confirmations: confirmations.unwrap().try_into().unwrap(),
script_pub_key: GetRawTransactionResultVoutScriptPubKey {
asm: String::new(),
hex: Vec::new(),
req_sigs: None,
type_: None,
addresses: Vec::new(),
address: None,
},
value: *value,
}))
}

fn get_wallet_info(&self) -> Result<GetWalletInfoResult, jsonrpc_core::Error> {
Expand Down Expand Up @@ -519,32 +529,45 @@ impl Api for Server {
txid: Txid,
_include_watchonly: Option<bool>,
) -> Result<Value, jsonrpc_core::Error> {
match self.state.lock().unwrap().transactions.get(&txid) {
Some(tx) => Ok(
serde_json::to_value(GetTransactionResult {
info: WalletTxInfo {
txid,
confirmations: 0,
time: 0,
timereceived: 0,
blockhash: None,
blockindex: None,
blockheight: None,
blocktime: None,
wallet_conflicts: Vec::new(),
bip125_replaceable: Bip125Replaceable::Unknown,
},
amount: SignedAmount::from_sat(0),
fee: None,
details: Vec::new(),
hex: serialize(tx),
})
.unwrap(),
),
None => Err(jsonrpc_core::Error::new(
let state = self.state();

let Some(tx) = state.transactions.get(&txid) else {
return Err(jsonrpc_core::Error::new(
jsonrpc_core::types::error::ErrorCode::ServerError(-8),
)),
));
};

let mut confirmations = None;

for (height, hash) in state.hashes.iter().enumerate() {
for tx in &state.blocks[hash].txdata {
if tx.txid() == txid {
confirmations = Some(state.hashes.len() - height);
}
}
}

Ok(
serde_json::to_value(GetTransactionResult {
info: WalletTxInfo {
txid,
confirmations: confirmations.unwrap().try_into().unwrap(),
time: 0,
timereceived: 0,
blockhash: None,
blockindex: None,
blockheight: None,
blocktime: None,
wallet_conflicts: Vec::new(),
bip125_replaceable: Bip125Replaceable::Unknown,
},
amount: SignedAmount::from_sat(0),
fee: None,
details: Vec::new(),
hex: serialize(tx),
})
.unwrap(),
)
}

fn get_raw_transaction(
Expand Down
21 changes: 18 additions & 3 deletions crates/test-bitcoincore-rpc/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
use super::*;
use {
super::*,
bitcoin::{
key::{KeyPair, Secp256k1, XOnlyPublicKey},
secp256k1::rand,
},
};

#[derive(Debug)]
pub(crate) struct State {
Expand Down Expand Up @@ -105,7 +111,9 @@ impl State {

for tx in block.txdata.iter() {
for input in tx.input.iter() {
self.utxos.remove(&input.previous_output);
if !input.previous_output.is_null() {
assert!(self.utxos.remove(&input.previous_output).is_some());
}
}

for (vout, txout) in tx.output.iter().enumerate() {
Expand Down Expand Up @@ -173,7 +181,14 @@ impl State {
.get(i)
.cloned()
.unwrap_or(value_per_output),
script_pubkey: script::Builder::new().into_script(),
script_pubkey: if template.p2tr {
let secp = Secp256k1::new();
let keypair = KeyPair::new(&secp, &mut rand::thread_rng());
let internal_key = XOnlyPublicKey::from_keypair(&keypair);
ScriptBuf::new_v1_p2tr(&secp, internal_key.0, None)
} else {
script::Builder::new().into_script()
},
})
.collect(),
};
Expand Down
Loading

0 comments on commit bac3149

Please sign in to comment.