From 37d8674dd367ced63d9dd8cd477327800cbeb651 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 26 Sep 2022 14:32:19 +0200 Subject: [PATCH 01/11] rebased --- src/subcommand/server.rs | 41 +++++++++++++- src/test.rs | 117 ++++++++++++++++++++++++++++++++------- tests/server.rs | 27 --------- 3 files changed, 137 insertions(+), 48 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 1037cc90bd..af523d880f 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -598,7 +598,7 @@ mod tests { assert_eq!(response.text().unwrap(), expected_response); } - fn assert_response_regex(&self, path: &str, status: StatusCode, regex: &'static str) { + fn assert_response_regex(&self, path: &str, status: StatusCode, regex: &str) { let response = self.get(path); assert_eq!(response.status(), status); assert_regex_match!(response.text().unwrap(), regex); @@ -1090,4 +1090,43 @@ mod tests { fn clock_is_served_with_svg_extension() { TestServer::new().assert_response_regex("clock.svg", StatusCode::OK, "Block [[:xdigit:]]{64} +

Transactions

+.*", + ); + } + + #[test] + fn transaction() { + let test_server = TestServer::new(); + + let txid = test_server.bitcoin_rpc_server.broadcast_dummy_tx(); + let _block_hash = test_server.bitcoin_rpc_server.mine_blocks(1)[0]; + + test_server.assert_response_regex( + &format!("tx/{txid}"), + StatusCode::OK, + &format!( + ".*Transaction {txid}.*

Transaction {txid}

+

Outputs

+.*" + ), + //
  • 30b037a346d31902f146a53d9ac8fa90541f43ca4a5e321914e86acdbf28394c:0
  • + ); + } } diff --git a/src/test.rs b/src/test.rs index 9e8afe7fb1..087550063e 100644 --- a/src/test.rs +++ b/src/test.rs @@ -3,16 +3,17 @@ use { bitcoin::BlockHeader, bitcoincore_rpc::Auth, jsonrpc_core::IoHandler, + jsonrpc_derive::rpc, jsonrpc_http_server::{CloseHandle, ServerBuilder}, std::collections::BTreeMap, }; pub(crate) use {bitcoincore_rpc::RpcApi, tempfile::TempDir}; + macro_rules! assert_regex_match { ($string:expr, $pattern:expr $(,)?) => { - let pattern: &'static str = $pattern; - let regex = Regex::new(&format!("^(?s){}$", pattern)).unwrap(); + let regex = Regex::new(&format!("^(?s){}$", $pattern)).unwrap(); let string = $string; if !regex.is_match(string.as_ref()) { @@ -27,44 +28,80 @@ macro_rules! assert_regex_match { struct Blocks { hashes: Vec, blocks: BTreeMap, + transactions: BTreeMap, + mempool: Vec, } impl Blocks { fn new() -> Self { let mut hashes = Vec::new(); let mut blocks = BTreeMap::new(); + let transactions = BTreeMap::new(); + let mempool = Vec::new(); let genesis_block = bitcoin::blockdata::constants::genesis_block(Network::Bitcoin); let genesis_block_hash = genesis_block.block_hash(); hashes.push(genesis_block_hash); blocks.insert(genesis_block_hash, genesis_block); - Self { hashes, blocks } + Self { + hashes, + blocks, + transactions, + mempool, + } } - fn push_block(&mut self, mut block: Block) -> BlockHash { + fn push_block(&mut self, header: BlockHeader) -> BlockHash { + let coinbase = Transaction { + version: 1, + lock_time: 0, + input: Vec::new(), + output: Vec::new(), + }; + // This feels like a bad way to do this + let mut transactions = self.mempool.clone(); + transactions.push(coinbase.clone()); + self.build_tx_index(transactions); + + let mut txdata = vec![coinbase]; + txdata.append(&mut self.mempool); + + let mut block = Block { header, txdata }; block.header.prev_blockhash = *self.hashes.last().unwrap(); let block_hash = block.block_hash(); self.hashes.push(block_hash); self.blocks.insert(block_hash, block); block_hash } + + fn build_tx_index(&mut self, txs: Vec) { + for tx in txs { + self.transactions.insert(tx.txid(), tx); + } + } + + fn broadcast_tx(&mut self, tx: Transaction) { + self.mempool.push(tx); + } } pub struct BitcoinRpcServer { - blocks: Mutex, + blocks: Arc>, } impl BitcoinRpcServer { fn new() -> Self { Self { - blocks: Mutex::new(Blocks::new()), + blocks: Arc::new(Mutex::new(Blocks::new())), } } pub(crate) fn spawn() -> BitcoinRpcServerHandle { + let bitcoin_rpc_server = BitcoinRpcServer::new(); + let blocks = bitcoin_rpc_server.blocks.clone(); let mut io = IoHandler::default(); - io.extend_with(BitcoinRpcServer::new().to_delegate()); + io.extend_with(bitcoin_rpc_server.to_delegate()); let rpc_server = ServerBuilder::new(io) .threads(1) @@ -79,11 +116,12 @@ impl BitcoinRpcServer { BitcoinRpcServerHandle { close_handle: Some(close_handle), port, + blocks, } } } -#[jsonrpc_derive::rpc] +#[rpc(server)] pub trait BitcoinRpc { #[rpc(name = "getblockhash")] fn getblockhash(&self, height: usize) -> Result; @@ -104,6 +142,14 @@ pub trait BitcoinRpc { count: usize, address: Address, ) -> Result, jsonrpc_core::Error>; + + #[rpc(name = "getrawtransaction")] + fn get_raw_transaction( + &self, + txid: String, // why doesn't Txid work? Do I need to implement a from trait for Txid? + verbose: bool, + blockhash: Option, + ) -> Result; } impl BitcoinRpc for BitcoinRpcServer { @@ -151,26 +197,44 @@ impl BitcoinRpc for BitcoinRpcServer { let mut blocks = self.blocks.lock().unwrap(); for _ in 0..count { - block_hashes.push(blocks.push_block(Block { - header: BlockHeader { - version: 0, - prev_blockhash: BlockHash::default(), - merkle_root: Default::default(), - time: 0, - bits: 0, - nonce: 0, - }, - txdata: Vec::new(), + block_hashes.push(blocks.push_block(BlockHeader { + version: 0, + prev_blockhash: BlockHash::default(), + merkle_root: Default::default(), + time: 0, + bits: 0, + nonce: 0, })); } Ok(block_hashes) } + + fn get_raw_transaction( + &self, + txid: String, + verbose: bool, + blockhash: Option, + ) -> Result { + // TODO: correct way of doing this? + assert_eq!(verbose, false, "Verbose param is unsupported"); + assert_eq!(blockhash, None, "Blockhash param is unsupported"); + + let txid_hash = bitcoin::hashes::sha256d::Hash::from_str(&txid).unwrap(); + let txid = Txid::from_hash(txid_hash); + match self.blocks.lock().unwrap().transactions.get(&txid) { + Some(tx) => Ok(tx.clone()), + None => Err(jsonrpc_core::Error::new( + jsonrpc_core::types::error::ErrorCode::ServerError(-8), + )), + } + } } pub(crate) struct BitcoinRpcServerHandle { pub(crate) port: u16, close_handle: Option, + blocks: Arc>, } impl BitcoinRpcServerHandle { @@ -182,11 +246,24 @@ impl BitcoinRpcServerHandle { bitcoincore_rpc::Client::new(&self.url(), Auth::None).unwrap() } - pub(crate) fn mine_blocks(&self, num: u64) { + pub(crate) fn mine_blocks(&self, num: u64) -> Vec { self .client() .generate_to_address(num, &"1BitcoinEaterAddressDontSendf59kuE".parse().unwrap()) - .unwrap(); + .unwrap() + } + + pub(crate) fn broadcast_dummy_tx(&self) -> Txid { + let tx = Transaction { + version: 1, + lock_time: 0, + input: Vec::new(), + output: Vec::new(), + }; + let txid = tx.txid(); + self.blocks.lock().unwrap().broadcast_tx(tx); + + txid } } diff --git a/tests/server.rs b/tests/server.rs index a29d11785d..37a78e64f2 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -50,33 +50,6 @@ fn spent_output_returns_200() { ); } -#[test] -fn block() { - let mut state = State::new(); - - state.blocks(101); - - state.transaction(TransactionOptions { - slots: &[(1, 0, 0)], - output_count: 1, - fee: 0, - recipient: None, - }); - - let blocks = state.blocks(1); - - state.request_regex( - &format!("block/{}", blocks[0]), - 200, - ".*

    Block [[:xdigit:]]{64}

    -

    Transactions

    -.*", - ); -} - #[test] fn transaction() { let mut state = State::new(); From d243a7ad4c6a82d439d4201bdcde41c74c1a13e0 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 26 Sep 2022 18:14:16 +0200 Subject: [PATCH 02/11] "small fixes/reversions" --- src/subcommand/server.rs | 20 +++++++------- src/test.rs | 59 ++++++++++++++++++++++++++++------------ 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index af523d880f..8eae197b1c 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -974,7 +974,7 @@ mod tests { let test_server = TestServer::new(); test_server.assert_response_regex( - "output/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0", + "/output/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0", StatusCode::OK, ".*Output 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0.*

    Output 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0

    Ordinal Ranges

    @@ -987,7 +987,7 @@ mod tests { #[test] fn unknown_output_returns_404() { TestServer::new().assert_response( - "output/0000000000000000000000000000000000000000000000000000000000000000:0", + "/output/0000000000000000000000000000000000000000000000000000000000000000:0", StatusCode::NOT_FOUND, "Output unknown.", ); @@ -996,7 +996,7 @@ mod tests { #[test] fn invalid_output_returns_400() { TestServer::new().assert_response( - "output/foo:0", + "/output/foo:0", StatusCode::BAD_REQUEST, "Invalid URL: error parsing TXID: odd hex string length 3", ); @@ -1038,7 +1038,7 @@ mod tests { #[test] fn block_not_found() { TestServer::new().assert_response( - "block/467a86f0642b1d284376d13a98ef58310caa49502b0f9a560ee222e0a122fe16", + "/block/467a86f0642b1d284376d13a98ef58310caa49502b0f9a560ee222e0a122fe16", StatusCode::NOT_FOUND, "Not Found", ); @@ -1047,7 +1047,7 @@ mod tests { #[test] fn unmined_ordinal() { TestServer::new().assert_response_regex( - "ordinal/0", + "/ordinal/0", StatusCode::OK, ".*
    time
    2009-01-03 18:15:05
    .*", ); @@ -1056,7 +1056,7 @@ mod tests { #[test] fn mined_ordinal() { TestServer::new().assert_response_regex( - "ordinal/5000000000", + "/ordinal/5000000000", StatusCode::OK, ".*
    time
    .* \\(expected\\)
    .*", ); @@ -1065,7 +1065,7 @@ mod tests { #[test] fn static_asset() { TestServer::new().assert_response_regex( - "static/index.css", + "/static/index.css", StatusCode::OK, r".*\.rare \{ background-color: cornflowerblue; @@ -1075,7 +1075,7 @@ mod tests { #[test] fn favicon() { - TestServer::new().assert_response_regex("favicon.ico", StatusCode::OK, r".*"); + TestServer::new().assert_response_regex("/favicon.ico", StatusCode::OK, r".*"); } #[test] @@ -1099,7 +1099,7 @@ mod tests { let block_hash = test_server.bitcoin_rpc_server.mine_blocks(1)[0]; test_server.assert_response_regex( - &format!("block/{block_hash}"), + &format!("/block/{block_hash}"), StatusCode::OK, ".*

    Block [[:xdigit:]]{64}

    Transactions

    @@ -1118,7 +1118,7 @@ mod tests { let _block_hash = test_server.bitcoin_rpc_server.mine_blocks(1)[0]; test_server.assert_response_regex( - &format!("tx/{txid}"), + &format!("/tx/{txid}"), StatusCode::OK, &format!( ".*Transaction {txid}.*

    Transaction {txid}

    diff --git a/src/test.rs b/src/test.rs index 087550063e..412e310c3b 100644 --- a/src/test.rs +++ b/src/test.rs @@ -3,14 +3,13 @@ use { bitcoin::BlockHeader, bitcoincore_rpc::Auth, jsonrpc_core::IoHandler, - jsonrpc_derive::rpc, + // jsonrpc_core::{IoHandler, Params}, jsonrpc_http_server::{CloseHandle, ServerBuilder}, std::collections::BTreeMap, }; pub(crate) use {bitcoincore_rpc::RpcApi, tempfile::TempDir}; - macro_rules! assert_regex_match { ($string:expr, $pattern:expr $(,)?) => { let regex = Regex::new(&format!("^(?s){}$", $pattern)).unwrap(); @@ -59,10 +58,12 @@ impl Blocks { input: Vec::new(), output: Vec::new(), }; - // This feels like a bad way to do this - let mut transactions = self.mempool.clone(); - transactions.push(coinbase.clone()); - self.build_tx_index(transactions); + + let mut txs = self.mempool.clone(); + txs.push(coinbase.clone()); + for tx in txs.into_iter() { + self.transactions.insert(tx.txid(), tx); + } let mut txdata = vec![coinbase]; txdata.append(&mut self.mempool); @@ -75,12 +76,6 @@ impl Blocks { block_hash } - fn build_tx_index(&mut self, txs: Vec) { - for tx in txs { - self.transactions.insert(tx.txid(), tx); - } - } - fn broadcast_tx(&mut self, tx: Transaction) { self.mempool.push(tx); } @@ -121,7 +116,8 @@ impl BitcoinRpcServer { } } -#[rpc(server)] +// Why not declared at in use statements? +#[jsonrpc_derive::rpc] pub trait BitcoinRpc { #[rpc(name = "getblockhash")] fn getblockhash(&self, height: usize) -> Result; @@ -146,10 +142,14 @@ pub trait BitcoinRpc { #[rpc(name = "getrawtransaction")] fn get_raw_transaction( &self, - txid: String, // why doesn't Txid work? Do I need to implement a from trait for Txid? + txid: Txid, verbose: bool, blockhash: Option, ) -> Result; + + // Was for debugging + // #[rpc(name = "getrawtransaction", params = "raw")] + // fn get_raw_transaction(&self, params: Params) -> Result; } impl BitcoinRpc for BitcoinRpcServer { @@ -212,7 +212,7 @@ impl BitcoinRpc for BitcoinRpcServer { fn get_raw_transaction( &self, - txid: String, + txid: Txid, verbose: bool, blockhash: Option, ) -> Result { @@ -220,8 +220,7 @@ impl BitcoinRpc for BitcoinRpcServer { assert_eq!(verbose, false, "Verbose param is unsupported"); assert_eq!(blockhash, None, "Blockhash param is unsupported"); - let txid_hash = bitcoin::hashes::sha256d::Hash::from_str(&txid).unwrap(); - let txid = Txid::from_hash(txid_hash); + dbg!(&txid); match self.blocks.lock().unwrap().transactions.get(&txid) { Some(tx) => Ok(tx.clone()), None => Err(jsonrpc_core::Error::new( @@ -229,6 +228,32 @@ impl BitcoinRpc for BitcoinRpcServer { )), } } + + // fn get_raw_transaction(&self, params: Params) -> Result { + // dbg!(¶ms); + // // TODO: how to do nested struct matching + // match params.clone() { + // Params::Array(vec) => { + // match &vec.clone()[0] { + // serde_json::Value::String(txid) => { + // dbg!(&txid); + // let txid_hash = bitcoin::hashes::sha256d::Hash::from_str(&txid).unwrap(); + // let txid = Txid::from_hash(txid_hash); + // match self.blocks.lock().unwrap().transactions.get(&txid) { + // Some(tx) => Ok(tx.clone()), + // None => Err(jsonrpc_core::Error::new( + // jsonrpc_core::types::error::ErrorCode::ServerError(-8), + // )), + // } + // _ => Err(jsonrpc_core::Error::new( + // jsonrpc_core::types::error::ErrorCode::ServerError(-8), + // }, + // _ => Err(jsonrpc_core::Error::new( + // jsonrpc_core::types::error::ErrorCode::ServerError(-8), + // )), + // } + // } + // } } pub(crate) struct BitcoinRpcServerHandle { From 8433968d64d380fccf846706038590a466af822e Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 26 Sep 2022 20:37:40 +0200 Subject: [PATCH 03/11] fixed JSON RPC error --- src/test.rs | 41 +++-------------------------------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/src/test.rs b/src/test.rs index 412e310c3b..76d1e20fe2 100644 --- a/src/test.rs +++ b/src/test.rs @@ -3,7 +3,6 @@ use { bitcoin::BlockHeader, bitcoincore_rpc::Auth, jsonrpc_core::IoHandler, - // jsonrpc_core::{IoHandler, Params}, jsonrpc_http_server::{CloseHandle, ServerBuilder}, std::collections::BTreeMap, }; @@ -116,7 +115,6 @@ impl BitcoinRpcServer { } } -// Why not declared at in use statements? #[jsonrpc_derive::rpc] pub trait BitcoinRpc { #[rpc(name = "getblockhash")] @@ -145,11 +143,7 @@ pub trait BitcoinRpc { txid: Txid, verbose: bool, blockhash: Option, - ) -> Result; - - // Was for debugging - // #[rpc(name = "getrawtransaction", params = "raw")] - // fn get_raw_transaction(&self, params: Params) -> Result; + ) -> Result; } impl BitcoinRpc for BitcoinRpcServer { @@ -215,45 +209,16 @@ impl BitcoinRpc for BitcoinRpcServer { txid: Txid, verbose: bool, blockhash: Option, - ) -> Result { - // TODO: correct way of doing this? + ) -> Result { assert_eq!(verbose, false, "Verbose param is unsupported"); assert_eq!(blockhash, None, "Blockhash param is unsupported"); - - dbg!(&txid); match self.blocks.lock().unwrap().transactions.get(&txid) { - Some(tx) => Ok(tx.clone()), + Some(tx) => Ok(hex::encode(bitcoin::consensus::encode::serialize(tx))), None => Err(jsonrpc_core::Error::new( jsonrpc_core::types::error::ErrorCode::ServerError(-8), )), } } - - // fn get_raw_transaction(&self, params: Params) -> Result { - // dbg!(¶ms); - // // TODO: how to do nested struct matching - // match params.clone() { - // Params::Array(vec) => { - // match &vec.clone()[0] { - // serde_json::Value::String(txid) => { - // dbg!(&txid); - // let txid_hash = bitcoin::hashes::sha256d::Hash::from_str(&txid).unwrap(); - // let txid = Txid::from_hash(txid_hash); - // match self.blocks.lock().unwrap().transactions.get(&txid) { - // Some(tx) => Ok(tx.clone()), - // None => Err(jsonrpc_core::Error::new( - // jsonrpc_core::types::error::ErrorCode::ServerError(-8), - // )), - // } - // _ => Err(jsonrpc_core::Error::new( - // jsonrpc_core::types::error::ErrorCode::ServerError(-8), - // }, - // _ => Err(jsonrpc_core::Error::new( - // jsonrpc_core::types::error::ErrorCode::ServerError(-8), - // )), - // } - // } - // } } pub(crate) struct BitcoinRpcServerHandle { From 7cd3f65a6073f169009255166b49673db239c503 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 26 Sep 2022 11:50:37 -0700 Subject: [PATCH 04/11] placate clippy --- src/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test.rs b/src/test.rs index 76d1e20fe2..49c8af6601 100644 --- a/src/test.rs +++ b/src/test.rs @@ -210,7 +210,7 @@ impl BitcoinRpc for BitcoinRpcServer { verbose: bool, blockhash: Option, ) -> Result { - assert_eq!(verbose, false, "Verbose param is unsupported"); + assert!(verbose, "Verbose param is unsupported"); assert_eq!(blockhash, None, "Blockhash param is unsupported"); match self.blocks.lock().unwrap().transactions.get(&txid) { Some(tx) => Ok(hex::encode(bitcoin::consensus::encode::serialize(tx))), From 6821dbb43024e6cf27f1939e84dabcedaff007b7 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 26 Sep 2022 22:09:19 +0200 Subject: [PATCH 05/11] some refactoring --- src/subcommand/server.rs | 9 ++-- src/test.rs | 108 +++++++++++++++++---------------------- 2 files changed, 52 insertions(+), 65 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 8eae197b1c..e48229a57c 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -1096,7 +1096,7 @@ mod tests { let test_server = TestServer::new(); let _txid = test_server.bitcoin_rpc_server.broadcast_dummy_tx(); - let block_hash = test_server.bitcoin_rpc_server.mine_blocks(1)[0]; + let block_hash = test_server.bitcoin_rpc_server.mine_blocks(1)[0].block_hash(); test_server.assert_response_regex( &format!("/block/{block_hash}"), @@ -1114,8 +1114,9 @@ mod tests { fn transaction() { let test_server = TestServer::new(); - let txid = test_server.bitcoin_rpc_server.broadcast_dummy_tx(); - let _block_hash = test_server.bitcoin_rpc_server.mine_blocks(1)[0]; + _ = test_server.bitcoin_rpc_server.broadcast_dummy_tx(); + let coinbase_tx = test_server.bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].clone(); + let txid = coinbase_tx.txid(); test_server.assert_response_regex( &format!("/tx/{txid}"), @@ -1124,9 +1125,9 @@ mod tests { ".*Transaction {txid}.*

    Transaction {txid}

    Outputs

    .*" ), - //
  • 30b037a346d31902f146a53d9ac8fa90541f43ca4a5e321914e86acdbf28394c:0
  • ); } } diff --git a/src/test.rs b/src/test.rs index 49c8af6601..f7cc9ddb8b 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,13 +1,12 @@ use { super::*, - bitcoin::BlockHeader, - bitcoincore_rpc::Auth, + bitcoin::{blockdata::script, BlockHeader, TxIn, Witness}, jsonrpc_core::IoHandler, jsonrpc_http_server::{CloseHandle, ServerBuilder}, std::collections::BTreeMap, }; -pub(crate) use {bitcoincore_rpc::RpcApi, tempfile::TempDir}; +pub(crate) use tempfile::TempDir; macro_rules! assert_regex_match { ($string:expr, $pattern:expr $(,)?) => { @@ -23,14 +22,14 @@ macro_rules! assert_regex_match { }; } -struct Blocks { +struct BitcoinRpcData { hashes: Vec, blocks: BTreeMap, transactions: BTreeMap, mempool: Vec, } -impl Blocks { +impl BitcoinRpcData { fn new() -> Self { let mut hashes = Vec::new(); let mut blocks = BTreeMap::new(); @@ -50,12 +49,22 @@ impl Blocks { } } - fn push_block(&mut self, header: BlockHeader) -> BlockHash { + fn push_block(&mut self, header: BlockHeader) -> Block { let coinbase = Transaction { - version: 1, + version: 0, lock_time: 0, - input: Vec::new(), - output: Vec::new(), + input: vec![TxIn { + previous_output: OutPoint::null(), + script_sig: script::Builder::new() + .push_scriptint(self.blocks.len().try_into().unwrap()) + .into_script(), + sequence: 0, + witness: Witness::new(), + }], + output: vec![TxOut { + value: 50 * 100_000_000, + script_pubkey: script::Builder::new().into_script(), + }], }; let mut txs = self.mempool.clone(); @@ -71,8 +80,8 @@ impl Blocks { block.header.prev_blockhash = *self.hashes.last().unwrap(); let block_hash = block.block_hash(); self.hashes.push(block_hash); - self.blocks.insert(block_hash, block); - block_hash + self.blocks.insert(block_hash, block.clone()); + block } fn broadcast_tx(&mut self, tx: Transaction) { @@ -81,19 +90,19 @@ impl Blocks { } pub struct BitcoinRpcServer { - blocks: Arc>, + data: Arc>, } impl BitcoinRpcServer { fn new() -> Self { Self { - blocks: Arc::new(Mutex::new(Blocks::new())), + data: Arc::new(Mutex::new(BitcoinRpcData::new())), } } pub(crate) fn spawn() -> BitcoinRpcServerHandle { let bitcoin_rpc_server = BitcoinRpcServer::new(); - let blocks = bitcoin_rpc_server.blocks.clone(); + let data = bitcoin_rpc_server.data.clone(); let mut io = IoHandler::default(); io.extend_with(bitcoin_rpc_server.to_delegate()); @@ -110,13 +119,13 @@ impl BitcoinRpcServer { BitcoinRpcServerHandle { close_handle: Some(close_handle), port, - blocks, + data, } } } #[jsonrpc_derive::rpc] -pub trait BitcoinRpc { +pub trait BitcoinRpcApi { #[rpc(name = "getblockhash")] fn getblockhash(&self, height: usize) -> Result; @@ -130,13 +139,6 @@ pub trait BitcoinRpc { #[rpc(name = "getblock")] fn getblock(&self, blockhash: BlockHash, verbosity: u64) -> Result; - #[rpc(name = "generatetoaddress")] - fn generate_to_address( - &self, - count: usize, - address: Address, - ) -> Result, jsonrpc_core::Error>; - #[rpc(name = "getrawtransaction")] fn get_raw_transaction( &self, @@ -146,9 +148,9 @@ pub trait BitcoinRpc { ) -> Result; } -impl BitcoinRpc for BitcoinRpcServer { +impl BitcoinRpcApi for BitcoinRpcServer { fn getblockhash(&self, height: usize) -> Result { - match self.blocks.lock().unwrap().hashes.get(height) { + match self.data.lock().unwrap().hashes.get(height) { Some(block_hash) => Ok(*block_hash), None => Err(jsonrpc_core::Error::new( jsonrpc_core::types::error::ErrorCode::ServerError(-8), @@ -162,7 +164,7 @@ impl BitcoinRpc for BitcoinRpcServer { verbose: bool, ) -> Result { assert!(!verbose); - match self.blocks.lock().unwrap().blocks.get(&block_hash) { + match self.data.lock().unwrap().blocks.get(&block_hash) { Some(block) => Ok(hex::encode(bitcoin::consensus::encode::serialize( &block.header, ))), @@ -174,7 +176,7 @@ impl BitcoinRpc for BitcoinRpcServer { fn getblock(&self, block_hash: BlockHash, verbosity: u64) -> Result { assert_eq!(verbosity, 0, "Verbosity level {verbosity} is unsupported"); - match self.blocks.lock().unwrap().blocks.get(&block_hash) { + match self.data.lock().unwrap().blocks.get(&block_hash) { Some(block) => Ok(hex::encode(bitcoin::consensus::encode::serialize(block))), None => Err(jsonrpc_core::Error::new( jsonrpc_core::types::error::ErrorCode::ServerError(-8), @@ -182,28 +184,6 @@ impl BitcoinRpc for BitcoinRpcServer { } } - fn generate_to_address( - &self, - count: usize, - _address: Address, - ) -> Result, jsonrpc_core::Error> { - let mut block_hashes = Vec::new(); - let mut blocks = self.blocks.lock().unwrap(); - - for _ in 0..count { - block_hashes.push(blocks.push_block(BlockHeader { - version: 0, - prev_blockhash: BlockHash::default(), - merkle_root: Default::default(), - time: 0, - bits: 0, - nonce: 0, - })); - } - - Ok(block_hashes) - } - fn get_raw_transaction( &self, txid: Txid, @@ -212,7 +192,7 @@ impl BitcoinRpc for BitcoinRpcServer { ) -> Result { assert!(verbose, "Verbose param is unsupported"); assert_eq!(blockhash, None, "Blockhash param is unsupported"); - match self.blocks.lock().unwrap().transactions.get(&txid) { + match self.data.lock().unwrap().transactions.get(&txid) { Some(tx) => Ok(hex::encode(bitcoin::consensus::encode::serialize(tx))), None => Err(jsonrpc_core::Error::new( jsonrpc_core::types::error::ErrorCode::ServerError(-8), @@ -224,7 +204,7 @@ impl BitcoinRpc for BitcoinRpcServer { pub(crate) struct BitcoinRpcServerHandle { pub(crate) port: u16, close_handle: Option, - blocks: Arc>, + data: Arc>, } impl BitcoinRpcServerHandle { @@ -232,15 +212,21 @@ impl BitcoinRpcServerHandle { format!("http://127.0.0.1:{}", self.port) } - pub(crate) fn client(&self) -> bitcoincore_rpc::Client { - bitcoincore_rpc::Client::new(&self.url(), Auth::None).unwrap() - } - - pub(crate) fn mine_blocks(&self, num: u64) -> Vec { - self - .client() - .generate_to_address(num, &"1BitcoinEaterAddressDontSendf59kuE".parse().unwrap()) - .unwrap() + pub(crate) fn mine_blocks(&self, num: u64) -> Vec { + let mut mined_blocks = Vec::new(); + let mut bitcoin_rpc_data = self.data.lock().unwrap(); + for _ in 0..num { + let block = bitcoin_rpc_data.push_block(BlockHeader { + version: 0, + prev_blockhash: BlockHash::default(), + merkle_root: Default::default(), + time: 0, + bits: 0, + nonce: 0, + }); + mined_blocks.push(block); + } + mined_blocks } pub(crate) fn broadcast_dummy_tx(&self) -> Txid { @@ -251,7 +237,7 @@ impl BitcoinRpcServerHandle { output: Vec::new(), }; let txid = tx.txid(); - self.blocks.lock().unwrap().broadcast_tx(tx); + self.data.lock().unwrap().broadcast_tx(tx); txid } From 20b344afde5f76ab75576bb9b1eecfeed9c06bb1 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 26 Sep 2022 22:18:31 +0200 Subject: [PATCH 06/11] fixing casey's mistake --- src/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test.rs b/src/test.rs index f7cc9ddb8b..346f03bf71 100644 --- a/src/test.rs +++ b/src/test.rs @@ -190,7 +190,7 @@ impl BitcoinRpcApi for BitcoinRpcServer { verbose: bool, blockhash: Option, ) -> Result { - assert!(verbose, "Verbose param is unsupported"); + assert!(!verbose, "Verbose param is unsupported"); assert_eq!(blockhash, None, "Blockhash param is unsupported"); match self.data.lock().unwrap().transactions.get(&txid) { Some(tx) => Ok(hex::encode(bitcoin::consensus::encode::serialize(tx))), From d6bba425c7d48ef342bda1802058f630f3d8ec72 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 26 Sep 2022 13:28:03 -0700 Subject: [PATCH 07/11] Update src/subcommand/server.rs --- src/subcommand/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index e48229a57c..08db4e57df 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -1088,7 +1088,7 @@ mod tests { #[test] fn clock_is_served_with_svg_extension() { - TestServer::new().assert_response_regex("clock.svg", StatusCode::OK, " Date: Mon, 26 Sep 2022 22:32:16 +0200 Subject: [PATCH 08/11] small fixes --- src/subcommand/server.rs | 3 +-- src/test.rs | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 08db4e57df..744547aca4 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -1095,7 +1095,7 @@ mod tests { fn block() { let test_server = TestServer::new(); - let _txid = test_server.bitcoin_rpc_server.broadcast_dummy_tx(); + test_server.bitcoin_rpc_server.broadcast_dummy_tx(); let block_hash = test_server.bitcoin_rpc_server.mine_blocks(1)[0].block_hash(); test_server.assert_response_regex( @@ -1114,7 +1114,6 @@ mod tests { fn transaction() { let test_server = TestServer::new(); - _ = test_server.bitcoin_rpc_server.broadcast_dummy_tx(); let coinbase_tx = test_server.bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].clone(); let txid = coinbase_tx.txid(); diff --git a/src/test.rs b/src/test.rs index 346f03bf71..73756c5144 100644 --- a/src/test.rs +++ b/src/test.rs @@ -33,8 +33,6 @@ impl BitcoinRpcData { fn new() -> Self { let mut hashes = Vec::new(); let mut blocks = BTreeMap::new(); - let transactions = BTreeMap::new(); - let mempool = Vec::new(); let genesis_block = bitcoin::blockdata::constants::genesis_block(Network::Bitcoin); let genesis_block_hash = genesis_block.block_hash(); @@ -44,8 +42,8 @@ impl BitcoinRpcData { Self { hashes, blocks, - transactions, - mempool, + transactions: BTreeMap::new(), + mempool: Vec::new(), } } From 1ccbd813d26daf7c9c5c7501a0a8df6e28c31e62 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 26 Sep 2022 22:35:26 +0200 Subject: [PATCH 09/11] add constant --- src/test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test.rs b/src/test.rs index 73756c5144..de04cfd101 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,6 +1,6 @@ use { super::*, - bitcoin::{blockdata::script, BlockHeader, TxIn, Witness}, + bitcoin::{blockdata::script, BlockHeader, TxIn, Witness, blockdata::constants::COIN_VALUE}, jsonrpc_core::IoHandler, jsonrpc_http_server::{CloseHandle, ServerBuilder}, std::collections::BTreeMap, @@ -60,7 +60,7 @@ impl BitcoinRpcData { witness: Witness::new(), }], output: vec![TxOut { - value: 50 * 100_000_000, + value: 50 * COIN_VALUE, script_pubkey: script::Builder::new().into_script(), }], }; From 561315387984b5849634d579b17f5682fc9575ab Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 26 Sep 2022 22:46:24 +0200 Subject: [PATCH 10/11] clean up push_block function --- src/test.rs | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/test.rs b/src/test.rs index de04cfd101..ca6f8e708f 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,6 +1,6 @@ use { super::*, - bitcoin::{blockdata::script, BlockHeader, TxIn, Witness, blockdata::constants::COIN_VALUE}, + bitcoin::{blockdata::constants::COIN_VALUE, blockdata::script, BlockHeader, TxIn, Witness}, jsonrpc_core::IoHandler, jsonrpc_http_server::{CloseHandle, ServerBuilder}, std::collections::BTreeMap, @@ -48,37 +48,36 @@ impl BitcoinRpcData { } fn push_block(&mut self, header: BlockHeader) -> Block { - let coinbase = Transaction { - version: 0, - lock_time: 0, - input: vec![TxIn { - previous_output: OutPoint::null(), - script_sig: script::Builder::new() - .push_scriptint(self.blocks.len().try_into().unwrap()) - .into_script(), - sequence: 0, - witness: Witness::new(), - }], - output: vec![TxOut { - value: 50 * COIN_VALUE, - script_pubkey: script::Builder::new().into_script(), + let mut block = Block { + header, + txdata: vec![Transaction { + version: 0, + lock_time: 0, + input: vec![TxIn { + previous_output: OutPoint::null(), + script_sig: script::Builder::new() + .push_scriptint(self.blocks.len().try_into().unwrap()) + .into_script(), + sequence: 0, + witness: Witness::new(), + }], + output: vec![TxOut { + value: 50 * COIN_VALUE, + script_pubkey: script::Builder::new().into_script(), + }], }], }; - let mut txs = self.mempool.clone(); - txs.push(coinbase.clone()); - for tx in txs.into_iter() { - self.transactions.insert(tx.txid(), tx); - } - - let mut txdata = vec![coinbase]; - txdata.append(&mut self.mempool); - - let mut block = Block { header, txdata }; block.header.prev_blockhash = *self.hashes.last().unwrap(); + block.txdata.append(&mut self.mempool); + let block_hash = block.block_hash(); self.hashes.push(block_hash); self.blocks.insert(block_hash, block.clone()); + for tx in &block.txdata { + self.transactions.insert(tx.txid(), tx.clone()); + } + block } From dce898e78782c8a951d381d81c80944cd9b125ee Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 26 Sep 2022 22:49:47 +0200 Subject: [PATCH 11/11] remove old test --- tests/server.rs | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/tests/server.rs b/tests/server.rs index 37a78e64f2..86deb05650 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -49,29 +49,3 @@ fn spent_output_returns_200() { ), ); } - -#[test] -fn transaction() { - let mut state = State::new(); - - state.blocks(101); - - state.transaction(TransactionOptions { - slots: &[(1, 0, 0)], - output_count: 1, - fee: 0, - recipient: None, - }); - - state.blocks(1); - - state.request_regex( - "tx/30b037a346d31902f146a53d9ac8fa90541f43ca4a5e321914e86acdbf28394c", - 200, - ".*Transaction 30b037a346d31902f146a53d9ac8fa90541f43ca4a5e321914e86acdbf28394c.*

    Transaction 30b037a346d31902f146a53d9ac8fa90541f43ca4a5e321914e86acdbf28394c

    -

    Outputs

    -.*" - ); -}