Skip to content

Commit

Permalink
Add generate block command
Browse files Browse the repository at this point in the history
  • Loading branch information
benthecarman committed Feb 14, 2023
1 parent bde02d7 commit ce915de
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 1 deletion.
26 changes: 25 additions & 1 deletion client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use serde_json;
use crate::bitcoin::hashes::hex::{FromHex, ToHex};
use crate::bitcoin::secp256k1::ecdsa::Signature;
use crate::bitcoin::{
Address, Amount, Block, BlockHeader, OutPoint, PrivateKey, PublicKey, Script, Transaction,
Address, Amount, Block, BlockHeader, OutPoint, PrivateKey, PublicKey, Script, Transaction, Txid,
};
use log::Level::{Debug, Trace, Warn};

Expand Down Expand Up @@ -186,6 +186,13 @@ impl RawTx for String {
}
}

/// The different ways to add a transaction to a block in generate block
#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum MineableTx {
RawTx(Transaction),
Txid(Txid),
}

/// The different authentication methods for the client.
#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum Auth {
Expand Down Expand Up @@ -866,6 +873,23 @@ pub trait RpcApi: Sized {
self.call("generate", &[block_num.into(), opt_into_json(maxtries)?])
}

/// Mine a set of ordered transactions to a specified address
/// and return the block hash.
fn generate_block(
&self,
address: &Address,
txs: &[MineableTx],
) -> Result<json::GenerateBlockResult> {
let tx_strs: Vec<_> = txs
.iter()
.map(|t| match t.to_owned() {
MineableTx::RawTx(tx) => tx.raw_hex(),
MineableTx::Txid(txid) => txid.to_hex(),
})
.collect();
self.call("generateblock", &[address.to_string().into(), tx_strs.into()])
}

/// Mark a block as invalid by `block_hash`
fn invalidate_block(&self, block_hash: &bitcoin::BlockHash) -> Result<()> {
self.call("invalidateblock", &[into_json(block_hash)?])
Expand Down
102 changes: 102 additions & 0 deletions integration_test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ fn main() {
test_generate(&cl);
test_get_balance_generate_to_address(&cl);
test_get_balances_generate_to_address(&cl);
test_generate_block_raw_tx(&cl);
test_generate_block_txid(&cl);
test_get_best_block_hash(&cl);
test_get_block_count(&cl);
test_get_block_hash(&cl);
Expand Down Expand Up @@ -277,6 +279,106 @@ fn test_get_balances_generate_to_address(cl: &Client) {
}
}

fn test_generate_block_raw_tx(cl: &Client) {
let sk = PrivateKey {
network: Network::Regtest,
inner: secp256k1::SecretKey::new(&mut secp256k1::rand::thread_rng()),
compressed: true,
};
let addr = Address::p2wpkh(&sk.public_key(&SECP), Network::Regtest).unwrap();

let options = json::ListUnspentQueryOptions {
minimum_amount: Some(btc(2)),
..Default::default()
};
let unspent = cl.list_unspent(Some(6), None, None, None, Some(options)).unwrap();
let unspent = unspent.into_iter().nth(0).unwrap();

let tx = Transaction {
version: 1,
lock_time: PackedLockTime::ZERO,
input: vec![TxIn {
previous_output: OutPoint {
txid: unspent.txid,
vout: unspent.vout,
},
sequence: Sequence::MAX,
script_sig: Script::new(),
witness: Witness::new(),
}],
output: vec![TxOut {
value: (unspent.amount - *FEE).to_sat(),
script_pubkey: addr.script_pubkey(),
}],
};

let input = json::SignRawTransactionInput {
txid: unspent.txid,
vout: unspent.vout,
script_pub_key: unspent.script_pub_key,
redeem_script: None,
amount: Some(unspent.amount),
};
let res = cl.sign_raw_transaction_with_wallet(&tx, Some(&[input]), None).unwrap();
assert!(res.complete);

let raw_tx = bitcoincore_rpc::MineableTx::RawTx(res.transaction().unwrap());
let result = cl.generate_block(&cl.get_new_address(None, None).unwrap(), &[raw_tx]).unwrap();
let tip = cl.get_best_block_hash().unwrap();
assert_eq!(result.hash, tip);
}

fn test_generate_block_txid(cl: &Client) {
let sk = PrivateKey {
network: Network::Regtest,
inner: secp256k1::SecretKey::new(&mut secp256k1::rand::thread_rng()),
compressed: true,
};
let addr = Address::p2wpkh(&sk.public_key(&SECP), Network::Regtest).unwrap();

let options = json::ListUnspentQueryOptions {
minimum_amount: Some(btc(2)),
..Default::default()
};
let unspent = cl.list_unspent(Some(6), None, None, None, Some(options)).unwrap();
let unspent = unspent.into_iter().nth(0).unwrap();

let tx = Transaction {
version: 1,
lock_time: PackedLockTime::ZERO,
input: vec![TxIn {
previous_output: OutPoint {
txid: unspent.txid,
vout: unspent.vout,
},
sequence: Sequence::MAX,
script_sig: Script::new(),
witness: Witness::new(),
}],
output: vec![TxOut {
value: (unspent.amount - *FEE).to_sat(),
script_pubkey: addr.script_pubkey(),
}],
};

let input = json::SignRawTransactionInput {
txid: unspent.txid,
vout: unspent.vout,
script_pub_key: unspent.script_pub_key,
redeem_script: None,
amount: Some(unspent.amount),
};
let res = cl.sign_raw_transaction_with_wallet(&tx, Some(&[input]), None).unwrap();
assert!(res.complete);

let tx = res.transaction().unwrap();
let txid = bitcoincore_rpc::MineableTx::Txid(tx.txid());
cl.send_raw_transaction(&tx).unwrap();
let result = cl.generate_block(&cl.get_new_address(None, None).unwrap(), &[txid]).unwrap();
let tip = cl.get_best_block_hash().unwrap();
assert_eq!(result.hash, tip);
}

fn test_get_best_block_hash(cl: &Client) {
let _ = cl.get_best_block_hash().unwrap();
}
Expand Down
7 changes: 7 additions & 0 deletions json/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,13 @@ pub struct GetAddressInfoResult {
pub label: Option<String>,
}

/// Models the result of "generateblock"
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct GenerateBlockResult {
/// Hash of the block generated
pub hash: bitcoin::BlockHash,
}

/// Models the result of "getblockchaininfo"
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct GetBlockchainInfoResult {
Expand Down

0 comments on commit ce915de

Please sign in to comment.