rpcclient: add bitcoind version dependent error matching
What changed, and why it matters
This commit updates btcd's RPC client so it can recognize new error messages that a future version of Bitcoin Core (bitcoind v30) will return. Without this change, btcd might misclassify those errors and behave incorrectly when talking to newer bitcoind nodes. It is a compatibility fix, not a fix for an active security vulnerability.
Review callers of MapRPCErr to ensure they handle ErrNonMandatoryScriptVerifyFlag and ErrScriptVerifyFlag correctly; consider adding fuzz or integration tests against bitcoind v30 release candidates; no urgent security patch is required.
Security signals we found
Error-string matching is inherently fragile and can break on upstream renames
Misclassification of script-verification errors could affect transaction validation logic in downstream callers
No input sanitization, memory safety, or cryptographic changes present
Evidence from the diff
The patch adds a BitcoindErrMap in rpcclient/errors.go to map renamed bitcoind v30 error strings (‘mempool script verify flag failed’ and ‘block script verify flag failed’) to existing semantic error constants. MapRPCErr now checks this map before falling back to the legacy BitcoindRPCErr matching. A test case is added for the new string. The change is defensive and improves error-handling parity with upstream bitcoind.
Changed components
rpcclient/errors.gorpcclient/errors_test.goInspect captured patch +35 / −1
diff --git a/rpcclient/errors.go b/rpcclient/errors.go
index 928881f..7bd4079 100644
--- a/rpcclient/errors.go
+++ b/rpcclient/errors.go
@@ -338,6 +338,23 @@ func (r BitcoindRPCErr) Error() string {
return "unknown error"
}
+// BitcoindErrMap is a map of additional errors bitcoind can throw that are
+// version dependent (e.g. versions up to v29 return the error as specified in
+// `Error()` above, while versions v30 and beyond return the error as mapped
+// here. We add a new map for errors that were simply renamed but have the same
+// semantic meaning. New errors should be added above as new error constants.
+var BitcoindErrMap = map[string]error{
+ // The error message was changed in
+ // https://github.com/bitcoin/bitcoin/pull/33050 which will be included
+ // in bitcoind v30.0 and beyond.
+ "mempool script verify flag failed": ErrNonMandatoryScriptVerifyFlag,
+
+ // The error message was changed in
+ // https://github.com/bitcoin/bitcoin/pull/33183 which will also be
+ // included in bitcoind v30.0 and beyond.
+ "block script verify flag failed": ErrScriptVerifyFlag,
+}
+
// BtcdErrMap takes the errors returned from btcd's `testmempoolaccept` and
// `sendrawtransaction` RPCs and map them to the errors defined above, which
// are results from calling either `testmempoolaccept` or `sendrawtransaction`
@@ -480,7 +497,7 @@ var BtcdErrMap = map[string]error{
//
// NOTE: we assume neutrino shares the same error strings as btcd.
func MapRPCErr(rpcErr error) error {
- // Iterate the map and find the matching error.
+ // Iterate the btcd error map and find the matching error.
for btcdErr, err := range BtcdErrMap {
// Match it against btcd's error first.
if matchErrStr(rpcErr, btcdErr) {
@@ -488,6 +505,15 @@ func MapRPCErr(rpcErr error) error {
}
}
+ // Also check the bitcoind error map, which is used for bitcoind version
+ // dependent errors.
+ for bitcoindErr, err := range BitcoindErrMap {
+ // Match it against bitcoind's error.
+ if matchErrStr(rpcErr, bitcoindErr) {
+ return err
+ }
+ }
+
// If not found, try to match it against bitcoind's error.
for i := uint32(0); i < uint32(errSentinel); i++ {
err := BitcoindRPCErr(i)
diff --git a/rpcclient/errors_test.go b/rpcclient/errors_test.go
index e074622..0b5428a 100644
--- a/rpcclient/errors_test.go
+++ b/rpcclient/errors_test.go
@@ -61,6 +61,14 @@ func TestMatchErrStr(t *testing.T) {
matchStr: "missingorspent",
matched: false,
},
+ {
+ name: "new bitcoind v30 error",
+ bitcoindErr: errors.New(
+ "mempool-script-verify-flag-failed",
+ ),
+ matchStr: "mempool script verify flag failed",
+ matched: true,
+ },
}
for _, tc := range testCases {
Why this scored 24/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.