Skip to content

Commit 8863ba2

Browse files
916aafb Merge bitcoin#27461: verify-commits: error and exit cleanly when git is too old. (Andrew Chow) 920d790 Merge bitcoin#27220: doc: update broken str util reference links on developer-notes (fanquake) 2c8b994 Merge bitcoin#27058: contrib: Improve verify-commits.py to work with maintainers leaving (glozow) 13fd397 Merge bitcoin#26714: test: add coverage for unparsable `-maxuploadtarget` (merge-script) 84c07bf Merge bitcoin#26738: test: add coverage for unknown wallet flag in `setwalletflag` (MarcoFalke) 08707f8 Merge bitcoin#26517: test: Changed small_txpuzzle_randfee to return the virtual size instead of the transaction hex for feerate calculation. (MarcoFalke) 12d52b2 Merge bitcoin#24226: rpc: warn that nodes ignore requests for old stale blocks (MarcoFalke) 94b3905 Merge bitcoin#26443: doc: mention BIP86 in doc/bips.md (fanquake) dfc97a9 Merge bitcoin#26243: test: Remove unused fCheckpointsEnabled from miner_tests (fanquake) 5759d18 Merge bitcoin#26116: rpc: Allow importmulti watchonly imports with locked wallet (Andrew Chow) Pull request description: ## What was done? Trivial backports from Bitcoin Core v25 ## How Has This Been Tested? Run & unit functional tests ## Breaking Changes N/A ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK 916aafb kwvg: utACK 916aafb Tree-SHA512: c728963e5bd2c86c9f4ebfdfe22fcf277701dced1bf1edf38b64738a4ceda3850f7c709912fc64e6b820885845b27f5741ad536b5b86ccb651009663029ff1ab
2 parents c353016 + 916aafb commit 8863ba2

File tree

13 files changed

+105
-61
lines changed

13 files changed

+105
-61
lines changed

contrib/verify-commits/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ Note that the above isn't a good UI/UX yet, and needs significant improvements
2727
to make it more convenient and reduce the chance of errors; pull-reqs
2828
improving this process would be much appreciated.
2929

30+
Unless `--clean-merge 0` is specified, `verify-commits.py` will attempt to verify that
31+
each merge commit applies cleanly (with some exceptions). This requires using at least
32+
git v2.38.0.
33+
3034
Configuration files
3135
-------------------
3236

contrib/verify-commits/gpg.sh

Lines changed: 6 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@
55

66
export LC_ALL=C
77
INPUT=$(cat /dev/stdin)
8-
VALID=false
9-
REVSIG=false
10-
IFS='
11-
'
128
if [ "$BITCOIN_VERIFY_COMMITS_ALLOW_SHA1" = 1 ]; then
13-
GPG_RES="$(printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null)"
9+
printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null
10+
exit $?
1411
else
1512
# Note how we've disabled SHA1 with the --weak-digest option, disabling
1613
# signatures - including selfsigs - that use SHA1. While you might think that
@@ -20,46 +17,19 @@ else
2017
# an attacker could construct a pull-req that results in a commit object that
2118
# they've created a collision for. Not the most likely attack, but preventing
2219
# it is pretty easy so we do so as a "belt-and-suspenders" measure.
23-
GPG_RES=""
2420
for LINE in $(gpg --version); do
2521
case "$LINE" in
2622
"gpg (GnuPG) 1.4.1"*|"gpg (GnuPG) 2.0."*)
2723
echo "Please upgrade to at least gpg 2.1.10 to check for weak signatures" > /dev/stderr
28-
GPG_RES="$(printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null)"
24+
printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null
25+
exit $?
2926
;;
3027
# We assume if you're running 2.1+, you're probably running 2.1.10+
3128
# gpg will fail otherwise
3229
# We assume if you're running 1.X, it is either 1.4.1X or 1.4.20+
3330
# gpg will fail otherwise
3431
esac
3532
done
36-
[ "$GPG_RES" = "" ] && GPG_RES="$(printf '%s\n' "$INPUT" | gpg --trust-model always --weak-digest sha1 "$@" 2>/dev/null)"
37-
fi
38-
for LINE in $GPG_RES; do
39-
case "$LINE" in
40-
"[GNUPG:] VALIDSIG "*)
41-
while read KEY; do
42-
[ "${LINE#?GNUPG:? VALIDSIG * * * * * * * * * }" = "$KEY" ] && VALID=true
43-
done < ./contrib/verify-commits/trusted-keys
44-
;;
45-
"[GNUPG:] REVKEYSIG "*)
46-
[ "$BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG" != 1 ] && exit 1
47-
REVSIG=true
48-
GOODREVSIG="[GNUPG:] GOODSIG ${LINE#* * *}"
49-
;;
50-
"[GNUPG:] EXPKEYSIG "*)
51-
[ "$BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG" != 1 ] && exit 1
52-
REVSIG=true
53-
GOODREVSIG="[GNUPG:] GOODSIG ${LINE#* * *}"
54-
;;
55-
esac
56-
done
57-
if ! $VALID; then
58-
exit 1
59-
fi
60-
if $VALID && $REVSIG; then
61-
printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null | grep "^\[GNUPG:\] \(NEWSIG\|SIG_ID\|VALIDSIG\)"
62-
echo "$GOODREVSIG"
63-
else
64-
printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null
33+
printf '%s\n' "$INPUT" | gpg --trust-model always --weak-digest sha1 "$@" 2>/dev/null
34+
exit $?
6535
fi

contrib/verify-commits/verify-commits.py

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ def main():
9292
unclean_merge_allowed = f.read().splitlines()
9393
with open(dirname + "/allow-incorrect-sha512-commits", "r", encoding="utf8") as f:
9494
incorrect_sha512_allowed = f.read().splitlines()
95+
with open(dirname + "/trusted-keys", "r", encoding="utf8") as f:
96+
trusted_keys = f.read().splitlines()
9597

96-
# Set commit and branch and set variables
98+
# Set commit and variables
9799
current_commit = args.commit
98100
if ' ' in current_commit:
99101
print("Commit must not contain spaces", file=sys.stderr)
@@ -102,7 +104,6 @@ def main():
102104
no_sha1 = True
103105
prev_commit = ""
104106
initial_commit = current_commit
105-
branch = subprocess.check_output([GIT, 'show', '-s', '--format=%H', initial_commit]).decode('utf8').splitlines()[0]
106107

107108
# Iterate through commits
108109
while True:
@@ -113,17 +114,41 @@ def main():
113114
if current_commit == verified_root:
114115
print('There is a valid path from "{}" to {} where all commits are signed!'.format(initial_commit, verified_root))
115116
sys.exit(0)
116-
if current_commit == verified_sha512_root:
117-
if verify_tree:
117+
else:
118+
# Make sure this commit isn't older than trusted roots
119+
check_root_older_res = subprocess.run([GIT, "merge-base", "--is-ancestor", verified_root, current_commit])
120+
if check_root_older_res.returncode != 0:
121+
print(f"\"{current_commit}\" predates the trusted root, stopping!")
122+
sys.exit(0)
123+
124+
if verify_tree:
125+
if current_commit == verified_sha512_root:
118126
print("All Tree-SHA512s matched up to {}".format(verified_sha512_root), file=sys.stderr)
119-
verify_tree = False
120-
no_sha1 = False
127+
verify_tree = False
128+
no_sha1 = False
129+
else:
130+
# Skip the tree check if we are older than the trusted root
131+
check_root_older_res = subprocess.run([GIT, "merge-base", "--is-ancestor", verified_sha512_root, current_commit])
132+
if check_root_older_res.returncode != 0:
133+
print(f"\"{current_commit}\" predates the trusted SHA512 root, disabling tree verification.")
134+
verify_tree = False
135+
no_sha1 = False
136+
121137

122138
os.environ['BITCOIN_VERIFY_COMMITS_ALLOW_SHA1'] = "0" if no_sha1 else "1"
123-
os.environ['BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG'] = "1" if current_commit in revsig_allowed else "0"
139+
allow_revsig = current_commit in revsig_allowed
124140

125141
# Check that the commit (and parents) was signed with a trusted key
126-
if subprocess.call([GIT, '-c', 'gpg.program={}/gpg.sh'.format(dirname), 'verify-commit', current_commit], stdout=subprocess.DEVNULL):
142+
valid_sig = False
143+
verify_res = subprocess.run([GIT, '-c', 'gpg.program={}/gpg.sh'.format(dirname), 'verify-commit', "--raw", current_commit], capture_output=True)
144+
for line in verify_res.stderr.decode().splitlines():
145+
if line.startswith("[GNUPG:] VALIDSIG "):
146+
key = line.split(" ")[-1]
147+
valid_sig = key in trusted_keys
148+
elif (line.startswith("[GNUPG:] REVKEYSIG ") or line.startswith("[GNUPG:] EXPKEYSIG ")) and not allow_revsig:
149+
valid_sig = False
150+
break
151+
if not valid_sig:
127152
if prev_commit != "":
128153
print("No parent of {} was signed with a trusted key!".format(prev_commit), file=sys.stderr)
129154
print("Parents are:", file=sys.stderr)
@@ -153,15 +178,24 @@ def main():
153178
allow_unclean = current_commit in unclean_merge_allowed
154179
if len(parents) == 2 and check_merge and not allow_unclean:
155180
current_tree = subprocess.check_output([GIT, 'show', '--format=%T', current_commit]).decode('utf8').splitlines()[0]
156-
subprocess.call([GIT, 'checkout', '--force', '--quiet', parents[0]])
157-
subprocess.call([GIT, 'merge', '--no-ff', '--quiet', '--no-gpg-sign', parents[1]], stdout=subprocess.DEVNULL)
158-
recreated_tree = subprocess.check_output([GIT, 'show', '--format=format:%T', 'HEAD']).decode('utf8').splitlines()[0]
181+
182+
# This merge-tree functionality requires git >= 2.38. The
183+
# --write-tree option was added in order to opt-in to the new
184+
# behavior. Older versions of git will not recognize the option and
185+
# will instead exit with code 128.
186+
try:
187+
recreated_tree = subprocess.check_output([GIT, "merge-tree", "--write-tree", parents[0], parents[1]]).decode('utf8').splitlines()[0]
188+
except subprocess.CalledProcessError as e:
189+
if e.returncode == 128:
190+
print("git v2.38+ is required for this functionality.", file=sys.stderr)
191+
sys.exit(1)
192+
else:
193+
raise e
194+
159195
if current_tree != recreated_tree:
160196
print("Merge commit {} is not clean".format(current_commit), file=sys.stderr)
161-
subprocess.call([GIT, 'diff', current_commit])
162-
subprocess.call([GIT, 'checkout', '--force', '--quiet', branch])
197+
subprocess.call([GIT, 'diff', recreated_tree, current_tree])
163198
sys.exit(1)
164-
subprocess.call([GIT, 'checkout', '--force', '--quiet', branch])
165199

166200
prev_commit = current_commit
167201
current_commit = parents[0]

doc/bips.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Versions and PRs are relevant to Bitcoin's core if not mentioned other.
2929
and it is disabled by default at build time since **v0.19.0** ([PR #15584](https://github.com/bitcoin/bitcoin/pull/15584)).
3030
It has been removed as of **v0.20.0** ([PR 17165](https://github.com/bitcoin/bitcoin/pull/17165)).
3131
* [`BIP 84`](https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki): The experimental descriptor wallets introduced in **v0.21.0** by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 84. ([PR #16528](https://github.com/bitcoin/bitcoin/pull/16528))
32+
* [`BIP 86`](https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki): Descriptor wallets by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 86 since **v23.0** ([PR #22364](https://github.com/bitcoin/bitcoin/pull/22364)).
3233
* [`BIP 90`](https://github.com/bitcoin/bips/blob/master/bip-0090.mediawiki): Trigger mechanism for activation of BIPs 34, 65, and 66 has been simplified to block height checks since **v0.14.0** ([PR #8391](https://github.com/bitcoin/bitcoin/pull/8391)).
3334
* [`BIP 111`](https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki): `NODE_BLOOM` service bit added, and enforced for all peer versions as of **v0.13.0** ([PR #6579](https://github.com/bitcoin/bitcoin/pull/6579) and [PR #6641](https://github.com/bitcoin/bitcoin/pull/6641)).
3435
* [`BIP 112`](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki): The CHECKSEQUENCEVERIFY opcode has been implemented since **v0.12.1** ([PR #7524](https://github.com/bitcoin/bitcoin/pull/7524)), and has been *buried* since **v0.19.0** ([PR #16060](https://github.com/bitcoin/bitcoin/pull/16060)).

doc/developer-notes.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -866,12 +866,12 @@ Strings and formatting
866866
buffer overflows, and surprises with `\0` characters. Also, some C string manipulations
867867
tend to act differently depending on platform, or even the user locale.
868868
869-
- Use `ParseInt32`, `ParseInt64`, `ParseUInt32`, `ParseUInt64`, `ParseDouble` from `utilstrencodings.h` for number parsing.
869+
- Use `ToIntegral` from [`strencodings.h`](/src/util/strencodings.h) for number parsing. In legacy code you might also find `ParseInt*` family of functions, `ParseDouble` or `LocaleIndependentAtoi`.
870870
871871
- *Rationale*: These functions do overflow checking and avoid pesky locale issues.
872872
873873
- Avoid using locale dependent functions if possible. You can use the provided
874-
[`lint-locale-dependence.sh`](/test/lint/lint-locale-dependence.sh)
874+
[`lint-locale-dependence.py`](/test/lint/lint-locale-dependence.py)
875875
to check for accidental use of locale dependent functions.
876876
877877
- *Rationale*: Unnecessary locale dependence can cause bugs that are very tricky to isolate and fix.

src/net_processing.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,6 +1935,8 @@ std::optional<std::string> PeerManagerImpl::FetchBlock(NodeId peer_id, const CBl
19351935

19361936
LOCK(cs_main);
19371937
// Mark block as in-flight unless it already is (for this peer).
1938+
// If the peer does not send us a block, vBlocksInFlight remains non-empty,
1939+
// causing us to timeout and disconnect.
19381940
// If a block was already in-flight for a different peer, its BLOCKTXN
19391941
// response will be dropped.
19401942
if (!BlockRequested(peer_id, block_index)) return "Already requested from this peer";

src/rpc/blockchain.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,9 @@ static RPCHelpMan getblockfrompeer()
483483
"getblockfrompeer",
484484
"Attempt to fetch block from a given peer.\n\n"
485485
"We must have the header for this block, e.g. using submitheader.\n"
486-
"Subsequent calls for the same block and a new peer will cause the response from the previous peer to be ignored.\n\n"
486+
"Subsequent calls for the same block and a new peer will cause the response from the previous peer to be ignored.\n"
487+
"Peers generally ignore requests for a stale block that they never fully verified, or one that is more than a month old.\n"
488+
"When a peer does not respond with a block, we will disconnect.\n\n"
487489
"Returns an empty JSON object if the request was successfully scheduled.",
488490
{
489491
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash to try to fetch"},

src/test/miner_tests.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -565,8 +565,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
565565
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
566566
std::unique_ptr<CBlockTemplate> pblocktemplate, pemptyblocktemplate;
567567

568-
fCheckpointsEnabled = false;
569-
570568
// Simple block creation, nothing special yet:
571569
BOOST_CHECK(pemptyblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
572570

@@ -643,8 +641,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
643641
m_node.mempool->clear();
644642

645643
TestPrioritisedMining(chainparams, scriptPubKey, txFirst);
646-
647-
fCheckpointsEnabled = true;
648644
}
649645

650646
BOOST_AUTO_TEST_SUITE_END()

src/wallet/rpc/backup.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1582,7 +1582,18 @@ RPCHelpMan importmulti()
15821582
UniValue response(UniValue::VARR);
15831583
{
15841584
LOCK(pwallet->cs_wallet);
1585-
EnsureWalletIsUnlocked(*pwallet);
1585+
1586+
// Check all requests are watchonly
1587+
bool is_watchonly{true};
1588+
for (size_t i = 0; i < requests.size(); ++i) {
1589+
const UniValue& request = requests[i];
1590+
if (!request.exists("watchonly") || !request["watchonly"].get_bool()) {
1591+
is_watchonly = false;
1592+
break;
1593+
}
1594+
}
1595+
// Wallet does not need to be unlocked if all requests are watchonly
1596+
if (!is_watchonly) EnsureWalletIsUnlocked(wallet);
15861597

15871598
// Verify all timestamps are present before importing any keys.
15881599
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nLowestTimestamp).mtpTime(now)));

test/functional/feature_fee_estimation.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def small_txpuzzle_randfee(
6161
unconflist.append({"txid": txid, "vout": 0, "value": total_in - amount - fee})
6262
unconflist.append({"txid": txid, "vout": 1, "value": amount})
6363

64-
return (tx.serialize().hex(), fee)
64+
return (tx.get_vsize(), fee)
6565

6666

6767
def check_raw_estimates(node, fees_seen):
@@ -148,7 +148,7 @@ def transact_and_mine(self, numblocks, mining_node):
148148
random.shuffle(self.confutxo)
149149
for _ in range(random.randrange(100 - 50, 100 + 50)):
150150
from_index = random.randint(1, 2)
151-
(txhex, fee) = small_txpuzzle_randfee(
151+
(tx_bytes, fee) = small_txpuzzle_randfee(
152152
self.wallet,
153153
self.nodes[from_index],
154154
self.confutxo,
@@ -157,7 +157,7 @@ def transact_and_mine(self, numblocks, mining_node):
157157
min_fee,
158158
min_fee,
159159
)
160-
tx_kbytes = (len(txhex) // 2) / 1000.0
160+
tx_kbytes = tx_bytes / 1000.0
161161
self.fees_per_kb.append(float(fee) / tx_kbytes)
162162
self.sync_mempools(wait=0.1)
163163
mined = mining_node.getblock(self.generate(mining_node, 1)[0], True)["tx"]

0 commit comments

Comments
 (0)