Skip to content

Commit 0ed9979

Browse files
authored
Merge branch 'master' into bump-0.25
2 parents 637e895 + f081fd6 commit 0ed9979

File tree

8 files changed

+186
-40
lines changed

8 files changed

+186
-40
lines changed

.travis.yml

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,15 @@
11
language: rust
22
rust:
33
- stable
4-
- beta
54
- nightly
6-
- 1.24.0
5+
- 1.29.0
76
env:
87
- BITCOINVERSION=0.18.0
98
- BITCOINVERSION=0.18.1
109
- BITCOINVERSION=0.19.0.1
1110
- BITCOINVERSION=0.19.1
12-
13-
matrix:
14-
allow_failures:
15-
rust: 1.24.0
11+
- BITCOINVERSION=0.20.0
12+
- BITCOINVERSION=0.20.1
1613

1714
script:
18-
- |
19-
if [[ "$TRAVIS_RUST_VERSION" == stable ]]
20-
then
21-
rustup component add rustfmt
22-
cargo fmt --all -- --check
23-
fi
24-
- cargo build --verbose
25-
- cargo test --verbose
26-
- cargo build --verbose --examples
27-
# Integration test
28-
- wget https://bitcoincore.org/bin/bitcoin-core-$BITCOINVERSION/bitcoin-$BITCOINVERSION-x86_64-linux-gnu.tar.gz
29-
- tar -xzvf bitcoin-$BITCOINVERSION-x86_64-linux-gnu.tar.gz
30-
- export PATH=$PATH:$(pwd)/bitcoin-$BITCOINVERSION/bin
31-
- (cd integration_test && ./run.sh)
32-
15+
- ./contrib/test.sh

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,18 @@ fn main() {
3535
```
3636

3737
See `client/examples/` for more usage examples.
38+
39+
40+
# Minimum Supported Rust Version (MSRV)
41+
This library should always compile with any combination of features on **Rust 1.29**.
42+
43+
Because some dependencies have broken the build in minor/patch releases, to
44+
compile with 1.29.0 you will need to run the following version-pinning command:
45+
```
46+
cargo update --package "cc" --precise "1.0.41"
47+
cargo update --package "cfg-if" --precise "0.1.9"
48+
cargo update --package "unicode-normalization" --precise "0.1.9"
49+
cargo update --package "serde_json" --precise "1.0.39"
50+
cargo update --package "serde" --precise "1.0.98"
51+
cargo update --package "serde_derive" --precise "1.0.98"
52+
```

client/src/client.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ use bitcoin::{
2525
Address, Amount, Block, BlockHeader, OutPoint, PrivateKey, PublicKey, Script, Transaction,
2626
};
2727
use log::Level::{Debug, Trace, Warn};
28-
use serde::{Deserialize, Serialize};
2928

3029
use error::*;
3130
use json;
3231
use queryable;
32+
use serde_json::Value;
3333

3434
/// Crate-specific Result type, shorthand for `std::result::Result` with our
3535
/// crate-specific Error type;
@@ -363,11 +363,15 @@ pub trait RpcApi: Sized {
363363
use Error::UnexpectedStructure as err;
364364

365365
// First, remove both incompatible softfork fields.
366-
let map = raw.as_object_mut().ok_or(err)?;
367-
let bip9_softforks = map.remove("bip9_softforks").ok_or(err)?;
368-
let old_softforks = map.remove("softforks").ok_or(err)?;
369-
// Put back an empty "softforks" field.
370-
map.insert("softforks".into(), serde_json::Map::new().into());
366+
// We need to scope the mutable ref here for v1.29 borrowck.
367+
let (bip9_softforks, old_softforks) = {
368+
let map = raw.as_object_mut().ok_or(err)?;
369+
let bip9_softforks = map.remove("bip9_softforks").ok_or(err)?;
370+
let old_softforks = map.remove("softforks").ok_or(err)?;
371+
// Put back an empty "softforks" field.
372+
map.insert("softforks".into(), serde_json::Map::new().into());
373+
(bip9_softforks, old_softforks)
374+
};
371375
let mut ret: json::GetBlockchainInfoResult = serde_json::from_value(raw)?;
372376

373377
// Then convert both softfork types and add them.
@@ -399,7 +403,7 @@ pub trait RpcApi: Sized {
399403
}
400404
let sf: OldBip9SoftFork = serde_json::from_value(sf.clone())?;
401405
ret.softforks.insert(
402-
id.into(),
406+
id.clone(),
403407
json::Softfork {
404408
type_: json::SoftforkType::Bip9,
405409
bip9: Some(json::Bip9SoftforkInfo {
@@ -1013,6 +1017,13 @@ pub trait RpcApi: Sized {
10131017
fn uptime(&self) -> Result<u64> {
10141018
self.call("uptime", &[])
10151019
}
1020+
1021+
fn scan_tx_out_set_blocking(
1022+
&self,
1023+
descriptors: &[json::ScanTxOutRequest],
1024+
) -> Result<json::ScanTxOutResult> {
1025+
self.call("scantxoutset", &["start".into(), into_json(descriptors)?])
1026+
}
10161027
}
10171028

10181029
/// Client implements a JSON-RPC client for the Bitcoin Core daemon or compatible APIs.

contrib/test.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
# Pin dependencies for Rust v1.29
3+
if [ "$TRAVIS_RUST_VERSION" = "1.29.0" ]; then
4+
cargo generate-lockfile --verbose
5+
cargo update --verbose --package "cc" --precise "1.0.41"
6+
cargo update --verbose --package "cfg-if" --precise "0.1.9"
7+
cargo update --verbose --package "unicode-normalization" --precise "0.1.9"
8+
cargo update --verbose --package "serde_json" --precise "1.0.39"
9+
cargo update --verbose --package "serde" --precise "1.0.98"
10+
cargo update --verbose --package "serde_derive" --precise "1.0.98"
11+
fi
12+
13+
if [ "$TRAVIS_RUST_VERSION" = "stable" ]; then
14+
rustup component add rustfmt
15+
cargo fmt --all -- --check
16+
fi
17+
18+
cargo build --verbose
19+
cargo test --verbose
20+
cargo build --verbose --examples
21+
22+
# Integration test
23+
wget https://bitcoincore.org/bin/bitcoin-core-$BITCOINVERSION/bitcoin-$BITCOINVERSION-x86_64-linux-gnu.tar.gz
24+
tar -xzvf bitcoin-$BITCOINVERSION-x86_64-linux-gnu.tar.gz
25+
export PATH=$PATH:$(pwd)/bitcoin-$BITCOINVERSION/bin
26+
(cd integration_test && ./run.sh)
27+

integration_test/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ authors = ["Steven Roose <[email protected]>"]
55

66
[dependencies]
77
bitcoincore-rpc = { path = "../client" }
8-
lazy_static = "1.4.0"
98
bitcoin = { version = "0.25", features = [ "use-serde", "rand" ] }
9+
lazy_static = "1.4.0"
10+
log = "0.4"

integration_test/run.sh

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@ PID1=$!
1919
sleep 3
2020

2121
BLOCKFILTERARG=""
22-
if bitcoind -version | grep -q "0\.19"; then
22+
if bitcoind -version | grep -q "0\.19\|0\.20"; then
2323
BLOCKFILTERARG="-blockfilterindex=1"
2424
fi
2525

26-
bitcoind -regtest $BLOCKFILTERARG \
26+
FALLBACKFEEARG=""
27+
if bitcoind -version | grep -q "0\.20"; then
28+
FALLBACKFEEARG="-fallbackfee=0.00001000"
29+
fi
30+
31+
bitcoind -regtest $BLOCKFILTERARG $FALLBACKFEEARG \
2732
-datadir=${TESTDIR}/2 \
2833
-connect=127.0.0.1:12348 \
2934
-rpcport=12349 \
@@ -34,7 +39,10 @@ PID2=$!
3439
# Let it connect to the other node.
3540
sleep 5
3641

37-
RPC_URL=http://localhost:12349 RPC_COOKIE=${TESTDIR}/2/regtest/.cookie cargo run
42+
RPC_URL=http://localhost:12349 \
43+
RPC_COOKIE=${TESTDIR}/2/regtest/.cookie \
44+
cargo run
45+
3846
RESULT=$?
3947

4048
kill -9 $PID1 $PID2

integration_test/src/main.rs

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ extern crate bitcoin;
1414
extern crate bitcoincore_rpc;
1515
#[macro_use]
1616
extern crate lazy_static;
17+
extern crate log;
1718

1819
use std::collections::HashMap;
1920

@@ -29,6 +30,7 @@ use bitcoin::{
2930
Address, Amount, Network, OutPoint, PrivateKey, Script, SigHashType, SignedAmount, Transaction,
3031
TxIn, TxOut, Txid,
3132
};
33+
use bitcoincore_rpc::bitcoincore_rpc_json::ScanTxOutRequest;
3234

3335
lazy_static! {
3436
static ref SECP: secp256k1::Secp256k1<secp256k1::All> = secp256k1::Secp256k1::new();
@@ -39,6 +41,24 @@ lazy_static! {
3941
static ref FEE: Amount = Amount::from_btc(0.001).unwrap();
4042
}
4143

44+
struct StdLogger;
45+
46+
impl log::Log for StdLogger {
47+
fn enabled(&self, metadata: &log::Metadata) -> bool {
48+
metadata.target().contains("jsonrpc") || metadata.target().contains("bitcoincore_rpc")
49+
}
50+
51+
fn log(&self, record: &log::Record) {
52+
if self.enabled(record.metadata()) {
53+
println!("[{}][{}]: {}", record.level(), record.metadata().target(), record.args());
54+
}
55+
}
56+
57+
fn flush(&self) {}
58+
}
59+
60+
static LOGGER: StdLogger = StdLogger;
61+
4262
/// Assert that the call returns a "deprecated" error.
4363
macro_rules! assert_deprecated {
4464
($call:expr) => {
@@ -89,6 +109,8 @@ fn get_auth() -> bitcoincore_rpc::Auth {
89109
}
90110

91111
fn main() {
112+
log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::max())).unwrap();
113+
92114
let rpc_url = get_rpc_url();
93115
let auth = get_auth();
94116

@@ -137,6 +159,7 @@ fn main() {
137159
test_combine_psbt(&cl);
138160
test_finalize_psbt(&cl);
139161
test_list_received_by_address(&cl);
162+
test_scantxoutset(&cl);
140163
test_import_public_key(&cl);
141164
test_import_priv_key(&cl);
142165
test_import_address(&cl);
@@ -279,16 +302,33 @@ fn test_get_address_info(cl: &Client) {
279302
assert!(!info.hex.unwrap().is_empty());
280303
}
281304

305+
#[allow(deprecated)]
282306
fn test_set_label(cl: &Client) {
283307
let addr = cl.get_new_address(Some("label"), None).unwrap();
284308
let info = cl.get_address_info(&addr).unwrap();
285-
assert_eq!(&info.label, "label");
286-
assert_eq!(info.labels[0].name, "label");
309+
if version() >= 0_20_00_00 {
310+
assert!(info.label.is_none());
311+
assert_eq!(info.labels[0], json::GetAddressInfoResultLabel::Simple("label".into()));
312+
} else {
313+
assert_eq!(info.label.as_ref().unwrap(), "label");
314+
assert_eq!(info.labels[0], json::GetAddressInfoResultLabel::WithPurpose {
315+
name: "label".into(),
316+
purpose: json::GetAddressInfoResultLabelPurpose::Receive,
317+
});
318+
}
287319

288320
cl.set_label(&addr, "other").unwrap();
289321
let info = cl.get_address_info(&addr).unwrap();
290-
assert_eq!(&info.label, "other");
291-
assert_eq!(info.labels[0].name, "other");
322+
if version() >= 0_20_00_00 {
323+
assert!(info.label.is_none());
324+
assert_eq!(info.labels[0], json::GetAddressInfoResultLabel::Simple("other".into()));
325+
} else {
326+
assert_eq!(info.label.as_ref().unwrap(), "other");
327+
assert_eq!(info.labels[0], json::GetAddressInfoResultLabel::WithPurpose {
328+
name: "other".into(),
329+
purpose: json::GetAddressInfoResultLabelPurpose::Receive,
330+
});
331+
}
292332
}
293333

294334
fn test_send_to_address(cl: &Client) {
@@ -916,6 +956,20 @@ fn test_uptime(cl: &Client) {
916956
cl.uptime().unwrap();
917957
}
918958

959+
fn test_scantxoutset(cl: &Client) {
960+
let addr = cl.get_new_address(None, None).unwrap();
961+
962+
cl.generate_to_address(2, &addr).unwrap();
963+
cl.generate_to_address(7, &cl.get_new_address(None, None).unwrap()).unwrap();
964+
965+
let utxos = cl
966+
.scan_tx_out_set_blocking(&[ScanTxOutRequest::Single(format!("addr({})", addr))])
967+
.unwrap();
968+
969+
assert_eq!(utxos.unspents.len(), 2);
970+
assert_eq!(utxos.success, Some(true));
971+
}
972+
919973
fn test_stop(cl: Client) {
920974
println!("Stopping: '{}'", cl.stop().unwrap());
921975
}

json/src/lib.rs

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -646,9 +646,13 @@ pub enum GetAddressInfoResultLabelPurpose {
646646
}
647647

648648
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
649-
pub struct GetAddressInfoResultLabel {
650-
pub name: String,
651-
pub purpose: GetAddressInfoResultLabelPurpose,
649+
#[serde(untagged)]
650+
pub enum GetAddressInfoResultLabel {
651+
Simple(String),
652+
WithPurpose {
653+
name: String,
654+
purpose: GetAddressInfoResultLabelPurpose,
655+
}
652656
}
653657

654658
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
@@ -679,13 +683,15 @@ pub struct GetAddressInfoResult {
679683
pub embedded: Option<GetAddressInfoResultEmbedded>,
680684
#[serde(rename = "is_compressed")]
681685
pub is_compressed: Option<bool>,
682-
pub label: String,
683686
pub timestamp: Option<u64>,
684687
#[serde(rename = "hdkeypath")]
685688
pub hd_key_path: Option<bip32::DerivationPath>,
686689
#[serde(rename = "hdseedid")]
687690
pub hd_seed_id: Option<bitcoin::XpubIdentifier>,
688691
pub labels: Vec<GetAddressInfoResultLabel>,
692+
/// Deprecated in v0.20.0. See `labels` field instead.
693+
#[deprecated(note = "since Core v0.20.0")]
694+
pub label: Option<String>,
689695
}
690696

691697
/// Models the result of "getblockchaininfo"
@@ -1253,6 +1259,47 @@ pub enum PubKeyOrAddress<'a> {
12531259
PubKey(&'a PublicKey),
12541260
}
12551261

1262+
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
1263+
#[serde(untagged)]
1264+
/// Start a scan of the UTXO set for an [output descriptor](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md).
1265+
pub enum ScanTxOutRequest {
1266+
/// Scan for a single descriptor
1267+
Single(String),
1268+
/// Scan for a descriptor with xpubs
1269+
Extended {
1270+
/// Descriptor
1271+
desc: String,
1272+
/// Range of the xpub derivations to scan
1273+
range: (u64, u64),
1274+
},
1275+
}
1276+
1277+
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
1278+
pub struct ScanTxOutResult {
1279+
pub success: Option<bool>,
1280+
#[serde(rename = "txouts")]
1281+
pub tx_outs: Option<u64>,
1282+
pub height: Option<u64>,
1283+
#[serde(rename = "bestblock")]
1284+
pub best_block_hash: Option<bitcoin::BlockHash>,
1285+
pub unspents: Vec<Utxo>,
1286+
#[serde(with = "bitcoin::util::amount::serde::as_btc")]
1287+
pub total_amount: bitcoin::Amount,
1288+
}
1289+
1290+
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
1291+
#[serde(rename_all = "camelCase")]
1292+
pub struct Utxo {
1293+
pub txid: bitcoin::Txid,
1294+
pub vout: u32,
1295+
pub script_pub_key: bitcoin::Script,
1296+
#[serde(rename = "desc")]
1297+
pub descriptor: String,
1298+
#[serde(with = "bitcoin::util::amount::serde::as_btc")]
1299+
pub amount: bitcoin::Amount,
1300+
pub height: u64,
1301+
}
1302+
12561303
impl<'a> serde::Serialize for PubKeyOrAddress<'a> {
12571304
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
12581305
where

0 commit comments

Comments
 (0)