Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crash when creating large inputs #1014

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -953,5 +954,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()