Skip to content

Commit

Permalink
Merge #1014: Fix crash when creating large inputs
Browse files Browse the repository at this point in the history
f7e1fb6 blind: add functional test that attempting to spend more than 256 inputs fails "gracefully" (Andrew Poelstra)
ba538cb blind.cpp: refuse to create surjection proofs that would trigger an assertation failure (Andrew Poelstra)

Pull request description:

  There isn't really any reasonable way to recover from this situation but we can at least gracefully fail instead of triggering an assertation failure.

  Fixes #979

  Fixes #880

ACKs for top commit:
  gwillen:
    utACK f7e1fb6.

Tree-SHA512: 6f95732964503e5ac6fc85fd6b45958f1b1719b1f9ecf3d6295d571489112837cdb4429a02a389da82e8d5fcd55527241cba50bceff00bb4facd8be037c7dcab
  • Loading branch information
apoelstra committed Aug 30, 2021
2 parents 6419449 + f7e1fb6 commit d973e11
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/blind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,15 @@ bool SurjectOutput(CTxOutWitness& txoutwit, const std::vector<secp256k1_fixed_as
secp256k1_surjectionproof proof;
secp256k1_fixed_asset_tag tag;
memcpy(&tag, asset.begin(), 32);
// FIXME [hardfork] Elements currently cannot handle surjection proofs on transactions
// with more than 256 inputs. The Elements verification code will always try to give
// secp-zkp the complete list of inputs, and if this exceeds 256 then surjectionproof_verify
// will always return false, so there is no way to work around this situation at signing time
if (surjection_targets.size() > SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) {
// We must return false here to avoid triggering an assertation within
// secp256k1_surjectionproof_initialize on the next line.
return false;
}
// Find correlation between asset tag and listed input tags
if (secp256k1_surjectionproof_initialize(secp256k1_blind_context, &proof, &input_index, &surjection_targets[0], surjection_targets.size(), nInputsToSelect, &tag, 100, randseed) == 0) {
return false;
Expand Down
29 changes: 29 additions & 0 deletions test/functional/rpc_fundrawtransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ def run_test(self):
self.test_address_reuse()
self.test_option_subtract_fee_from_outputs()
self.test_subtract_fee_with_presets()
self.test_surjectionproof_many_inputs()

def test_change_position(self):
"""Ensure setting changePosition in fundraw with an exact match is handled properly."""
Expand Down Expand Up @@ -967,5 +968,33 @@ def test_subtract_fee_with_presets(self):
signedtx = self.nodes[0].signrawtransactionwithwallet(blindedtx)
self.nodes[0].sendrawtransaction(signedtx['hex'])

def test_surjectionproof_many_inputs(self):
self.log.info("Test fundrawtx with more than 256 inputs")

self.nodes[0].createwallet("surjection")
wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
recipient = self.nodes[0].get_wallet_rpc("surjection")

# Make 500 0.1 BTC outputs...
for j in range(0, 10):
outputs = {}
for i in range(0, 50):
outputs[recipient.getnewaddress()] = 0.1
wallet.sendmany("", outputs)
self.nodes[0].generate(10)

# ...and try to send them all in one transaction
# This should fail but we should not see an assertation failure.
rawtx = recipient.createrawtransaction([], {wallet.getnewaddress(): 49.99})
assert_raises_rpc_error(-4, "Unable to blind the transaction properly. This should not happen.", recipient.fundrawtransaction, rawtx)

# Try to send them across two transactions. This should succeed.
rawtx = recipient.createrawtransaction([], {wallet.getnewaddress(): 24.99})
for i in range(0, 2):
fundedtx = recipient.fundrawtransaction(rawtx)
blindedtx = recipient.blindrawtransaction(fundedtx['hex'])
signedtx = recipient.signrawtransactionwithwallet(blindedtx)
self.nodes[0].sendrawtransaction(signedtx['hex'])

if __name__ == '__main__':
RawTransactionsTest().main()

0 comments on commit d973e11

Please sign in to comment.