mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-14 12:43:40 +02:00
Merge branch 'master' into 2021-10--rc2
This commit is contained in:
commit
af441ca7bb
10 changed files with 287 additions and 54 deletions
|
|
@ -242,7 +242,8 @@ assert all([x["next"] == "updater" for x in analysis["inputs"]])
|
|||
# in turn, or by both updating their copies and then one party calls
|
||||
# `combinepsbt` to combine the results.
|
||||
#
|
||||
# We will do the latter for symmetry reasons.
|
||||
# We will do the latter since `combinepsbt` will automatically check that
|
||||
# neither party changed the transaction out from under the other.
|
||||
#
|
||||
|
||||
print ("3. Both parties fill in their UTXO data.")
|
||||
|
|
@ -257,17 +258,23 @@ assert all([x["has_utxo"] for x in analysis["inputs"]])
|
|||
assert not any([x["is_final"] for x in analysis["inputs"]])
|
||||
assert all([x["next"] == "signer" for x in analysis["inputs"]])
|
||||
|
||||
## 3. Blind the PSET
|
||||
## 4. Blind the PSET
|
||||
#
|
||||
# Both parties now blind the PSET. Importantly, the final person to blind must
|
||||
# have a combined PSET that has everyone else's blinding data included, so that
|
||||
# they can make the blinding factors add up.
|
||||
#
|
||||
# In the 2-party case this is particularly simple because there is no need to
|
||||
# combine anything.
|
||||
# The most straightforward way to implement this is to have each party blind
|
||||
# the PSET, then pass to the next party, who blinds in-turn. If there are more
|
||||
# than 2 parties, then you can have all parties but one blind independently,
|
||||
# `combinepsbt` the results, and give this to the final party to blind.
|
||||
#
|
||||
# In the two-party case these are equivalent.
|
||||
#
|
||||
|
||||
# Just for fun, try to have both parties blind independently and combine
|
||||
# Just for fun, try to have both parties blind independently and combine. This
|
||||
# will fail because `combinepsbt` needs there to be at least one remaining
|
||||
# unblinded output.
|
||||
alice_blinded = alice.walletprocesspsbt(filled_pset)
|
||||
carol_blinded = carol.walletprocesspsbt(filled_pset)
|
||||
try:
|
||||
|
|
@ -278,13 +285,20 @@ else:
|
|||
raise Exception("combinepsbt should return 'Cannot combine PSETs as the values and blinders would become imbalanced'")
|
||||
|
||||
# Ok, back to the tutorial
|
||||
print ("3. One party blinds the PSET and passes to the other party, who also blinds")
|
||||
print ("4. One party blinds the PSET and passes to the other party, who also blinds")
|
||||
# We set sign=False here, because otherwise carol's `walletprocesspsbt`
|
||||
# will sign the transaction (since after she does her blinding, the
|
||||
# transaction will be completely blinded and therefore signable).
|
||||
# But for this tutorial we want that to happen only in the next step.
|
||||
alice_blinded = alice.walletprocesspsbt(filled_pset)
|
||||
# Alice gives `alice_blinded` to Carol, who uses `combinepsbt` to verify
|
||||
# that Alice blinded honestly (not changing any of the output amounts/assets)
|
||||
carol.combinepsbt([filled_pset, alice_blinded["psbt"]])
|
||||
# If this passes (does not throw any exception) she can blind.
|
||||
carol_blinded = carol.walletprocesspsbt(psbt=alice_blinded["psbt"], sign=False)
|
||||
# Similarly, Carol passes the result to Alice, who checks it
|
||||
carol.combinepsbt([alice_blinded["psbt"], carol_blinded["psbt"]])
|
||||
# Now both parties have a copy of the fully-blinded transaction.
|
||||
blinded = carol_blinded
|
||||
|
||||
# We won't print these out because they're very big now
|
||||
|
|
@ -300,7 +314,7 @@ assert all([x["has_utxo"] for x in analysis["inputs"]])
|
|||
assert not any([x["is_final"] for x in analysis["inputs"]])
|
||||
assert all([x["next"] == "signer" for x in analysis["inputs"]])
|
||||
|
||||
## 4. Sign the PSET
|
||||
## 5. Sign the PSET
|
||||
#
|
||||
# When signing, there are a couple options for workflows. Each party can sign
|
||||
# in turn, like we did when blinding, or they can each sign independently and
|
||||
|
|
@ -308,7 +322,7 @@ assert all([x["next"] == "signer" for x in analysis["inputs"]])
|
|||
# demonstration purposes.
|
||||
#
|
||||
|
||||
print ("4. Both parties sign the PSET")
|
||||
print ("5. Both parties sign the PSET")
|
||||
|
||||
alice_signed = alice.walletprocesspsbt(blinded["psbt"])
|
||||
carol_signed = carol.walletprocesspsbt(blinded["psbt"])
|
||||
|
|
@ -324,7 +338,7 @@ assert all([x["has_utxo"] for x in analysis["inputs"]])
|
|||
assert all([x["is_final"] for x in analysis["inputs"]])
|
||||
assert all([x["next"] == "extractor" for x in analysis["inputs"]])
|
||||
|
||||
print ("5. Finalize and Extract")
|
||||
print ("6. Finalize and Extract")
|
||||
x = alice.finalizepsbt(complete, True)
|
||||
assert x["complete"]
|
||||
# The complete transaction is in x["hex"] but again we won't print it because
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ bool CreateAssetSurjectionProof(std::vector<unsigned char>& output_proof, const
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool VerifyBlindAssetProof(const uint256& asset, const std::vector<unsigned char>& proof, const CConfidentialAsset& conf_asset)
|
||||
bool VerifyBlindAssetProof(const uint256& asset, const std::vector<unsigned char>& proof, const CConfidentialAsset& conf_asset)
|
||||
{
|
||||
secp256k1_surjectionproof surj_proof;
|
||||
if (secp256k1_surjectionproof_parse(secp256k1_blind_context, &surj_proof, proof.data(), proof.size()) == 0) {
|
||||
|
|
@ -173,7 +173,7 @@ static bool CreateBlindAssetProof(std::vector<unsigned char>& assetproof, const
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool VerifyBlindValueProof(CAmount value, const CConfidentialValue& conf_value, const std::vector<unsigned char>& proof, const CConfidentialAsset& conf_asset)
|
||||
bool VerifyBlindValueProof(CAmount value, const CConfidentialValue& conf_value, const std::vector<unsigned char>& proof, const CConfidentialAsset& conf_asset)
|
||||
{
|
||||
secp256k1_pedersen_commitment value_commit;
|
||||
if (secp256k1_pedersen_commitment_parse(secp256k1_blind_context, &value_commit, conf_value.vchCommitment.data()) == 0) {
|
||||
|
|
@ -263,6 +263,7 @@ bool SubtractScalars(uint256& a, const uint256& b)
|
|||
|
||||
// Compute the scalar offset used for the final blinder computation
|
||||
// value * asset_blinder + value_blinder
|
||||
// FIXME this method should be in libsecp, as should `ComputeAndAddToScalarOffset`
|
||||
bool CalculateScalarOffset(uint256& out, CAmount value, const uint256& asset_blinder, const uint256& value_blinder)
|
||||
{
|
||||
// If the asset_blinder is 0, then the equation resolves to just the value_blinder
|
||||
|
|
@ -276,8 +277,24 @@ bool CalculateScalarOffset(uint256& out, CAmount value, const uint256& asset_bli
|
|||
// tweak_mul expects a 32 byte, big endian tweak.
|
||||
// We need to pack the 8 byte CAmount into a uint256 with the correct padding, so start it at 24 bytes from the front
|
||||
WriteBE64(val.begin() + 24, value);
|
||||
if (secp256k1_ec_privkey_tweak_mul(secp256k1_blind_context, out.begin(), val.begin()) != 1) return false;
|
||||
if (!value_blinder.IsNull() && secp256k1_ec_privkey_tweak_add(secp256k1_blind_context, out.begin(), value_blinder.begin()) != 1) return false;
|
||||
if (value > 0) {
|
||||
if (secp256k1_ec_privkey_tweak_mul(secp256k1_blind_context, out.begin(), val.begin()) != 1) return false;
|
||||
} else {
|
||||
out = value_blinder;
|
||||
return true;
|
||||
}
|
||||
if (!value_blinder.IsNull()) {
|
||||
uint256 value_negated = value_blinder;
|
||||
if (secp256k1_ec_seckey_negate(secp256k1_blind_context, value_negated.begin()) != 1) {
|
||||
return false;
|
||||
}
|
||||
// Special-case zero, which would otherwise cause `secp256k1_ec_privkey_tweak_add` to fail
|
||||
if (value_negated == out) {
|
||||
out = uint256{};
|
||||
return true;
|
||||
}
|
||||
if (secp256k1_ec_privkey_tweak_add(secp256k1_blind_context, out.begin(), value_blinder.begin()) != 1) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -294,8 +311,17 @@ bool ComputeAndAddToScalarOffset(uint256& a, CAmount value, const uint256& asset
|
|||
if (a.IsNull()) {
|
||||
a = scalar;
|
||||
} else {
|
||||
// If we have a, then add the scalar to it.
|
||||
if (secp256k1_ec_privkey_tweak_add(secp256k1_blind_context, a.begin(), scalar.begin()) != 1) return false;
|
||||
uint256 scalar_negated = scalar;
|
||||
if (secp256k1_ec_seckey_negate(secp256k1_blind_context, scalar_negated.begin()) != 1) {
|
||||
return false;
|
||||
}
|
||||
// Special-case zero, which would otherwise cause `secp256k1_ec_privkey_tweak_add` to fail
|
||||
if (scalar_negated == a) {
|
||||
a = uint256{};
|
||||
} else {
|
||||
// If we have a, then add the scalar to it.
|
||||
if (secp256k1_ec_privkey_tweak_add(secp256k1_blind_context, a.begin(), scalar.begin()) != 1) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ bool CreateValueRangeProof(std::vector<unsigned char>& rangeproof, const uint256
|
|||
void CreateAssetCommitment(CConfidentialAsset& conf_asset, secp256k1_generator& asset_gen, const CAsset& asset, const uint256& asset_blinder);
|
||||
void CreateValueCommitment(CConfidentialValue& conf_value, secp256k1_pedersen_commitment& value_commit, const uint256& value_blinder, const secp256k1_generator& asset_gen, const CAmount amount);
|
||||
BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map<uint32_t, std::tuple<CAmount, CAsset, uint256, uint256>> our_input_data, std::map<uint32_t, std::pair<CKey, CKey>> our_issuances_to_blind);
|
||||
|
||||
|
||||
bool VerifyBlindValueProof(CAmount value, const CConfidentialValue& conf_value, const std::vector<unsigned char>& proof, const CConfidentialAsset& conf_asset);
|
||||
bool VerifyBlindAssetProof(const uint256& asset, const std::vector<unsigned char>& proof, const CConfidentialAsset& conf_asset);
|
||||
BlindProofResult VerifyBlindProofs(const PSBTOutput& o);
|
||||
|
||||
#endif //BITCOIN_BLINDPSBT_H
|
||||
|
|
|
|||
|
|
@ -916,8 +916,8 @@ public:
|
|||
|
||||
// Legacy PAK list
|
||||
consensus.first_extension_space = {
|
||||
ParseHex("0362f0cf4898e44a20472664daed460156976bab5cc8bb8431b206bbafddd230c9"
|
||||
"0399dadeeedc2cefe9042ffa596c553cad1967cda04de6aa0f9fbd96b6044292e7"),
|
||||
ParseHex("02555f97c44ad9286ef060a02b00e8e6be2626ed3eb9230705d3ca2f977daae61e"
|
||||
"03cddbc847f64f898b883d717a7f637bedf9ac2ecd243721eada223f1b1790f75b"),
|
||||
ParseHex("033fad80bd2b818d1ca8a8d4a25dafcf5e740be07db6788be1f2f15266e3c6805d"
|
||||
"0253ff3f140ef8f594d54996eab810a82550c79204279920d95681afe699d00da5"),
|
||||
ParseHex("03f2d35e88741f930a3938bfa7075377ec2da4f1d7699a779e2cbf7a389195dc67"
|
||||
|
|
@ -940,8 +940,12 @@ public:
|
|||
"034e93391cea816e5141dace7e5477bbed90c9daa0670b68b7acc8a44af556bbc1"),
|
||||
ParseHex("03156b39a4bce80e68c1582aa78f81f0252ccbb039766b5395ee9a0224f41c236d"
|
||||
"0399a5d1d42f5b6cb587560394e1581eb0c76916db317c0d644a1b9f509a06c4e6"),
|
||||
ParseHex("029ce033e1dc81164deb04b4c55966b823a025ef47bb1f767017696b68ab9ae201"
|
||||
"03e612d646e71b07e5ce0eaa3a0178e4606dd9a6e8f0d5ace9171fb1e808a3865b"),
|
||||
ParseHex("029797b15de24dc43a6556e58159c5aa0b69ea390ccdebcd7be10751d8085da08f"
|
||||
"03248e52371b2c3bce2478a3c3aaf37e4f0d6ba711e058ba407f44fdaaf280ac95"),
|
||||
ParseHex("03d6a14ab496777401e2eae7992404011537860af7b46c3a8fdea65d29fe4bf26c"
|
||||
"02dca82e552228f3808b1ea9b38b3342b51e9453dcb1414c551ce08bd726311e30"),
|
||||
ParseHex("035c9c770ed88e29b364038d68b1c623fbf71e93e6d5357e278e9b64160984ed3c"
|
||||
"02659aabb69b8413bc46026830ad1e2284901350a75c2bc97906f49cff01503f0f"),
|
||||
ParseHex("02a8300f0cff92b23e402459e83c52ec5824de82ee4004cf9d254e788304027ef6"
|
||||
"0389cbda672fa9efea51706863f1d7ae5e5015b2e519003ef0178c99f71be6e8be"),
|
||||
ParseHex("03fcba7ecf41bc7e1be4ee122d9d22e3333671eb0a3a87b5cdf099d59874e1940f"
|
||||
|
|
@ -950,30 +954,48 @@ public:
|
|||
"02228dfd7ff95506dd67b1118803eb8ab49352b2e24cd5f38da043847e722009ba"),
|
||||
ParseHex("03fcc2963daaf8249bfd220e52c693626254b9295ac4f947ae2e0cddb3046724c1"
|
||||
"02dac03530ac9712a71eafb87766644b61cf4be85d0fdc6a859875b41e7a1dc8e6"),
|
||||
ParseHex("039bfd22bf5c41ce14d3fbd50ef226d2066e826b2efba455150d23d958d52bfddf"
|
||||
"03211678d22c45402c993d96ea4a6d861d3e1da33798aebd5424fe5725a7ce8f4b"),
|
||||
ParseHex("02d67fcb027c5d8fe354fb36235192cb4fffabffdcc6ce74be255fe869f62d8675"
|
||||
"03d61d857b2a8cb060fd4b9a98a862f250df5825068665a3c8d93f2ac8a7085888"),
|
||||
ParseHex("02cfe983eb588975958e9ce832937ba7f24592882cf5c0fc0f07896097fd66a8e7"
|
||||
"0344744d01c091eacea5730ed1205b0a83378418644ea7938ed664649e88dcbb29"),
|
||||
ParseHex("02cddb51ea42acf38762418939be0a9227f0212ff96a870a2c1d85ec65905a7629"
|
||||
"03d986a2181a38cfef5b5e2a1915aa2d37f193fcbafab9bf311d6138209f316f5b"),
|
||||
ParseHex("029ec6dd0c310513b3720800025a7ad9013d60a7fb041f6e9b9d3963485ba28657"
|
||||
"0277247f28eb9481dd21d664093a2bc19a496c7ffebeca0026a1726a5041e671ba"),
|
||||
ParseHex("03f9dea372c4a667dcfe234ff8e0410c22341149ff7d8780c46954ff74998fbe44"
|
||||
"0340c4e534906c06b73874cef00a880ab602641c7883de94296f0f601e6517ae7e"),
|
||||
ParseHex("03cf8520f2db93e1ba75fa9043ac7e3476719b2a33a12d7e725688a2de68852c88"
|
||||
"0343b7551ba662fa7071ac93e7e25517967bb8a9420af64d35d41c6d88056ad4ba"),
|
||||
ParseHex("027661f1530dfc88b34b0c8f606d215f30fb0edfa116b331ff44b2fbe040893c6f"
|
||||
"029d3160731eddc316121b2a31c82270baa4bbe7f08549891af3b444eb690b2df1"),
|
||||
ParseHex("03f79461a5559f360c407069b92a8075958bf1f70918872d9dd702db145bccbd42"
|
||||
"0395058fc702f126176ae13e0ebed05107288900a5a35b121f62923e58798b7b2f"),
|
||||
ParseHex("02d7f049d9e87c861fc9decfbe167cb13ccc87cce99113f69e3a5dca8bb71b6aed"
|
||||
"03e82197b2e9cc0ee11a59808cfdb52e824445f8fa99e44dc9c30d1e49950ff9d6"),
|
||||
ParseHex("0281bfeffcc6841d1355dce039f5d64f72714a4c3adc4d351eaf3c28acbcee15f0"
|
||||
"0270a16ee1cdfc78755a783efbdb66fe822605cc5f53af707e5038615e22b288e2"),
|
||||
ParseHex("022d58f7f198f3fe7e0ae45f93aa28fdb483ac25a258663ac593860e11ac1d1abc"
|
||||
"035049635f866b921f7cd0481c6165f19e14ba52c67f7c4fade1dcd22f9aacea20"),
|
||||
ParseHex("02d40ea20996c882a75fd8cd433484bd8af92791752b4c2d2f24660de36a9f3f82"
|
||||
"02d874a87df633068c2eacceed3345ce5fb2dbc9f94c30b93ef4c844a77f2651c0"),
|
||||
ParseHex("024158f76e16888a49492d4913e45c1b4cba19d87dd5bd24346ef601d31d062537"
|
||||
"0366e9ad4ce16b65a95fb63aae98fdff6bcbd31816d6336039e529a40a828e9851"),
|
||||
ParseHex("02d2283a929584cdf557096a7f473ae25c04fd6f73467657c4bc49dfb3095892bd"
|
||||
"03599136ea1f66a80a2eb1a144458561f4791d2fc5fcd06e32a88c9cb2976c8aac"),
|
||||
ParseHex("036f4b5f3ae46163fb53b0d6c19c78ea2fdf49c8b419c354f3c24fa1ce9547e6b8"
|
||||
"0340a79f2477ff2a077fb0b8ebb96714a9aaf242f4b96253260264ed031f2a7ee4"),
|
||||
ParseHex("02d6825aaa063083567f6d4f35ea62c2af8d34f67ef4c2afa565791fd7efc5f3a6"
|
||||
"02b1e0d671f91f756a7613797d84c33daddc1dc1df9badf68d4e2c2216a288c923"),
|
||||
ParseHex("03effb766a6f3729c220b0ffa156ffa66d656e5ec16f15bc513b8d0b1298c761d2"
|
||||
"0252831192e573788271e235afca8f72736d97e26b3a1406cac34711b6ab670c26"),
|
||||
ParseHex("038c245fa632a0b6c2712cbadb6f6e346284ee0fba3202875abd774faee2deca29"
|
||||
"032ff781357db141528b1c7ea2cfed3ebe6bb9a028954665cfba355bbcf3d14c8e"),
|
||||
ParseHex("0356c22fab025b3e661331ed4dcf8645a4a4fd4a2cae69680339e05df209ef4556"
|
||||
"032d60805593864388d073193fb9fcf66c389813778dcd4a2e93c8fd164d387f7f"),
|
||||
ParseHex("0238de9c098e83c4d244294ac394355c8e80b49af10f7c1e23001e6c88be5d45b8"
|
||||
"03bc04885be94ceffbac90178ef18d4dd6958d7488f7861f0994c659412d9e9463"),
|
||||
ParseHex("025651f14b6347a000e15473eaf631fd78c9307e07db85e177e31fcde0b3f2a574"
|
||||
"03d5303909fe1c6665cbc96a538b17274068c8e79757705f68db3df2b561a4c110"),
|
||||
ParseHex("03627a4855be1edc657927f30a4a869ad830041c1f0e74ab4670588af9532b8de8"
|
||||
"03444cb85aef9fbba10b3e2662d533858db771010b57b7aedb1ecaa1c5a34918f1"),
|
||||
ParseHex("0286951fdc1e81652cdd10a10971966792e5c2a2bbe524f32a561f585b2b3d2057"
|
||||
"034294862542484e49c6fb835919212352527298c689ff7be57e445bf0fe3536de"),
|
||||
ParseHex("032d9af13c8d5f5316fd27a14bafb8ec55684ef2e3b5c64b2645e088f570e5d2cb"
|
||||
"0239590f39508465decfd8a1bdc61b42333297e80588ed826ddd43678edfa6caae")
|
||||
};
|
||||
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
||||
|
|
@ -1155,7 +1177,7 @@ public:
|
|||
assert(IsHex(extprvprefix) && extprvprefix.size() == 8 && "-extprvkeyprefix must be hex string of length 8");
|
||||
base58Prefixes[EXT_SECRET_KEY] = ParseHex(extprvprefix);
|
||||
|
||||
const std::string magic_str = args.GetArg("-pchmessagestart", "FABFB5DA");
|
||||
const std::string magic_str = args.GetArg("-pchmessagestart", "143EFCB1");
|
||||
assert(IsHex(magic_str) && magic_str.size() == 8 && "-pchmessagestart must be hex string of length 8");
|
||||
const std::vector<unsigned char> magic_byte = ParseHex(magic_str);
|
||||
std::copy(begin(magic_byte), end(magic_byte), pchMessageStart);
|
||||
|
|
|
|||
23
src/psbt.cpp
23
src/psbt.cpp
|
|
@ -2,6 +2,7 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <blindpsbt.h>
|
||||
#include <psbt.h>
|
||||
|
||||
#include <chainparams.h>
|
||||
|
|
@ -135,13 +136,29 @@ CMutableTransaction PartiallySignedTransaction::GetUnsignedTx(bool force_unblind
|
|||
CTxOut txout;
|
||||
CTxOutWitness txoutwit;
|
||||
txout.scriptPubKey = *output.script;
|
||||
if (output.m_value_commitment.IsNull() || (output.amount != nullopt && force_unblinded)) {
|
||||
|
||||
bool exp_value = output.m_value_commitment.IsNull() || force_unblinded;
|
||||
exp_value = exp_value && output.amount != nullopt;
|
||||
if (!output.m_value_commitment.IsNull() && output.amount != nullopt) {
|
||||
exp_value = exp_value && !output.m_blind_value_proof.empty();
|
||||
exp_value = exp_value && !output.m_asset_commitment.IsNull();
|
||||
exp_value = exp_value && VerifyBlindValueProof(*output.amount, output.m_value_commitment, output.m_blind_value_proof, output.m_asset_commitment);
|
||||
}
|
||||
if (exp_value) {
|
||||
txout.nValue.SetToAmount(*output.amount);
|
||||
} else {
|
||||
txout.nValue = output.m_value_commitment;
|
||||
txoutwit.vchRangeproof = output.m_value_rangeproof;
|
||||
}
|
||||
if (output.m_asset_commitment.IsNull() || (!output.m_asset.IsNull() && force_unblinded)) {
|
||||
|
||||
bool exp_asset = output.m_asset_commitment.IsNull() || force_unblinded;
|
||||
exp_asset = exp_asset && !output.m_asset.IsNull();
|
||||
if (!output.m_asset_commitment.IsNull() && !output.m_asset.IsNull()) {
|
||||
exp_asset = exp_asset && !output.m_blind_asset_proof.empty();
|
||||
exp_asset = exp_asset && !output.m_asset.IsNull();
|
||||
exp_asset = exp_asset && VerifyBlindAssetProof(output.m_asset, output.m_blind_asset_proof, output.m_asset_commitment);
|
||||
}
|
||||
if (exp_asset) {
|
||||
txout.nAsset.SetToAsset(CAsset(output.m_asset));
|
||||
} else {
|
||||
txout.nAsset = output.m_asset_commitment;
|
||||
|
|
@ -417,7 +434,7 @@ bool PSBTInput::Merge(const PSBTInput& input)
|
|||
}
|
||||
if (m_issuance_rangeproof.empty() && !input.m_issuance_rangeproof.empty()) m_issuance_rangeproof = input.m_issuance_rangeproof;
|
||||
if (m_issuance_inflation_keys_rangeproof.empty() && !input.m_issuance_inflation_keys_rangeproof.empty()) m_issuance_inflation_keys_rangeproof = input.m_issuance_inflation_keys_rangeproof;
|
||||
if (m_issuance_inflation_keys_amount == nullopt && m_issuance_inflation_keys_commitment.IsNull() && input.m_issuance_inflation_keys_amount != nullopt) m_issuance_inflation_keys_amount = m_issuance_inflation_keys_amount;
|
||||
if (m_issuance_inflation_keys_amount == nullopt && m_issuance_inflation_keys_commitment.IsNull() && input.m_issuance_inflation_keys_amount != nullopt) m_issuance_inflation_keys_amount = input.m_issuance_inflation_keys_amount;
|
||||
if (m_issuance_inflation_keys_commitment.IsNull() && !input.m_issuance_inflation_keys_commitment.IsNull()) {
|
||||
m_issuance_inflation_keys_commitment = input.m_issuance_inflation_keys_commitment;
|
||||
m_issuance_inflation_keys_amount.reset();
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ bilingual_str TransactionErrorString(const TransactionError err)
|
|||
return Untranslated("Proof of blinded value is invalid");
|
||||
case TransactionError::INVALID_ASSET_PROOF:
|
||||
return Untranslated("Proof of blinded asset is invalid");
|
||||
case TransactionError::MISSING_BLINDING_KEY:
|
||||
return Untranslated("Wallet does not have necessary blinding key");
|
||||
case TransactionError::MISSING_SIDECHANNEL_DATA:
|
||||
return Untranslated("A rangeproof did not encode necessary blinding data");
|
||||
// no default case, so the compiler can warn about missing cases
|
||||
}
|
||||
assert(false);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ enum class TransactionError {
|
|||
UTXOS_MISSING_BALANCE_CHECK,
|
||||
INVALID_VALUE_PROOF,
|
||||
INVALID_ASSET_PROOF,
|
||||
MISSING_BLINDING_KEY,
|
||||
MISSING_SIDECHANNEL_DATA,
|
||||
};
|
||||
|
||||
bilingual_str TransactionErrorString(const TransactionError error);
|
||||
|
|
|
|||
|
|
@ -4951,8 +4951,16 @@ static RPCHelpMan walletcreatefundedpsbt()
|
|||
// Automatically select coins, unless at least one is manually selected. Can
|
||||
// be overridden by options.add_inputs.
|
||||
coin_control.m_add_inputs = rawTx.vin.size() == 0;
|
||||
// FundTransaction expects blinding keys, if present, to appear in the output nonces
|
||||
for (CTxOut& txout : rawTx.vout) {
|
||||
auto search_it = psbt_outs.find(txout);
|
||||
assert (search_it != psbt_outs.end());
|
||||
CPubKey& blind_pub = search_it->second.m_blinding_pubkey;
|
||||
if (blind_pub.IsFullyValid()) {
|
||||
txout.nNonce.vchCommitment = std::vector<unsigned char>(blind_pub.begin(), blind_pub.end());
|
||||
}
|
||||
}
|
||||
FundTransaction(pwallet, rawTx, fee, change_position, request.params[3], coin_control, /* solving_data */ request.params[5], /* override_min_fee */ true);
|
||||
PartiallySignedTransaction psbtx(rawTx, psbt_version);
|
||||
// Find an input that is ours
|
||||
unsigned int blinder_index = 0;
|
||||
{
|
||||
|
|
@ -4965,6 +4973,17 @@ static RPCHelpMan walletcreatefundedpsbt()
|
|||
}
|
||||
}
|
||||
assert(blinder_index < rawTx.vin.size()); // We added inputs, or existing inputs are ours, we should have a blinder index at this point.
|
||||
// It may add outputs (change, and in some edge case OP_RETURN) which need to be
|
||||
// blinded. So pull these into `psbt_outs`.
|
||||
for (const CTxOut& txout : rawTx.vout) {
|
||||
if (!txout.nNonce.IsNull() && !psbt_outs.count(txout)) {
|
||||
PSBTOutput new_out{2}; // psbtv2 output
|
||||
new_out.m_blinding_pubkey.Set(txout.nNonce.vchCommitment.begin(), txout.nNonce.vchCommitment.end());
|
||||
new_out.m_blinder_index = blinder_index;
|
||||
psbt_outs.insert(std::make_pair(txout, new_out));
|
||||
}
|
||||
}
|
||||
PartiallySignedTransaction psbtx(rawTx, psbt_version);
|
||||
for (unsigned int i = 0; i < rawTx.vout.size(); ++i) {
|
||||
PSBTOutput& output = psbtx.outputs[i];
|
||||
auto it = psbt_outs.find(rawTx.vout.at(i));
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <policy/policy.h>
|
||||
#include <primitives/block.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <rpc/util.h> // for GetDestinationBlindingKey and IsBlindDestination
|
||||
#include <script/descriptor.h>
|
||||
#include <script/pegins.h>
|
||||
#include <script/script.h>
|
||||
|
|
@ -2853,7 +2854,7 @@ TransactionError CWallet::SignPSBT(PartiallySignedTransaction& psbtx, bool& comp
|
|||
|
||||
CConfidentialNonce nonce;
|
||||
nonce.vchCommitment.insert(nonce.vchCommitment.end(), o.m_ecdh_pubkey.begin(), o.m_ecdh_pubkey.end());
|
||||
if (!UnblindConfidentialPair(blinding_key, o.m_value_commitment, o.m_asset_commitment, nonce, o.script.get(), o.m_value_rangeproof, value, value_factor, asset, asset_factor)) {
|
||||
if (UnblindConfidentialPair(blinding_key, o.m_value_commitment, o.m_asset_commitment, nonce, o.script.get(), o.m_value_rangeproof, value, value_factor, asset, asset_factor)) {
|
||||
// These assertions are cryptographically impossible to trigger, as we
|
||||
// checked the proofs above, and then `UnblindConfidentialPair` checks
|
||||
// the extracted value/asset against the commitments.
|
||||
|
|
@ -2863,10 +2864,11 @@ TransactionError CWallet::SignPSBT(PartiallySignedTransaction& psbtx, bool& comp
|
|||
if (!o.m_asset.IsNull()) {
|
||||
assert(CAsset(o.m_asset) == asset);
|
||||
}
|
||||
return TransactionError::INVALID_ASSET_PROOF; // FIXME
|
||||
} else {
|
||||
return TransactionError::MISSING_SIDECHANNEL_DATA;
|
||||
}
|
||||
} else {
|
||||
return TransactionError::INVALID_ASSET_PROOF; // FIXME
|
||||
return TransactionError::MISSING_BLINDING_KEY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3226,6 +3228,8 @@ bool fillBlindDetails(BlindDetails* det, CWallet* wallet, CMutableTransaction& t
|
|||
// We need to make sure to dupe an asset that is in input set
|
||||
//TODO Have blinding do some extremely minimal rangeproof
|
||||
CTxOut newTxOut(det->o_assets.back(), 0, CScript() << OP_RETURN);
|
||||
CPubKey blind_pub = wallet->GetBlindingPubKey(newTxOut.scriptPubKey); // irrelevent, just needs to be non-null
|
||||
newTxOut.nNonce.vchCommitment = std::vector<unsigned char>(blind_pub.begin(), blind_pub.end());
|
||||
txNew.vout.push_back(newTxOut);
|
||||
det->o_pubkeys.push_back(wallet->GetBlindingPubKey(newTxOut.scriptPubKey));
|
||||
det->o_amount_blinds.push_back(uint256());
|
||||
|
|
@ -3346,6 +3350,9 @@ bool CWallet::CreateTransactionInternal(
|
|||
// A map that keeps track of the change script for each asset and also
|
||||
// the index of the reservedest used for that script (-1 if none).
|
||||
std::map<CAsset, std::pair<int, CScript>> mapScriptChange;
|
||||
// For manually set change, we need to use the blinding pubkey associated
|
||||
// with the manually-set address rather than generating one from the wallet
|
||||
std::map<CAsset, Optional<CPubKey> > mapBlindingKeyChange;
|
||||
|
||||
LOCK(cs_wallet);
|
||||
txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight());
|
||||
|
|
@ -3360,6 +3367,11 @@ bool CWallet::CreateTransactionInternal(
|
|||
for (const auto& dest : coin_control.destChange) {
|
||||
// No need to test we cover all assets. We produce error for that later.
|
||||
mapScriptChange[dest.first] = std::pair<int, CScript>(-1, GetScriptForDestination(dest.second));
|
||||
if (IsBlindDestination(dest.second)) {
|
||||
mapBlindingKeyChange[dest.first] = GetDestinationBlindingKey(dest.second);
|
||||
} else {
|
||||
mapBlindingKeyChange[dest.first] = nullopt;
|
||||
}
|
||||
}
|
||||
} else { // no coin control: send change to newly generated address
|
||||
// Note: We use a new key here to keep it from being obvious which side is the change.
|
||||
|
|
@ -3642,15 +3654,36 @@ bool CWallet::CreateTransactionInternal(
|
|||
}
|
||||
|
||||
std::vector<CTxOut>::iterator position = txNew.vout.begin()+vChangePosInOut[assetChange.first];
|
||||
Optional<CPubKey> blind_pub;
|
||||
if (blind_details) {
|
||||
CPubKey blind_pub = GetBlindingPubKey(itScript->second.second);
|
||||
blind_details->o_pubkeys.insert(blind_details->o_pubkeys.begin() + vChangePosInOut[assetChange.first], blind_pub);
|
||||
assert(blind_pub.IsFullyValid());
|
||||
const auto itBlindingKey = mapBlindingKeyChange.find(assetChange.first);
|
||||
if (itBlindingKey != mapBlindingKeyChange.end()) {
|
||||
// If the change output was specified, use the blinding key that
|
||||
// came with the specified address (if any)
|
||||
blind_pub = itBlindingKey->second;
|
||||
} else {
|
||||
// Otherwise, we generated it from our own wallet, so get the
|
||||
// blinding key from our own wallet.
|
||||
blind_pub = GetBlindingPubKey(itScript->second.second);
|
||||
}
|
||||
} else {
|
||||
// ...and if we are not blinding at all, use no blinding key. (This
|
||||
// assignment is technically unnecessary as `blind_pub` was initialized
|
||||
// above to nullopt, but we leave it here for clarity.)
|
||||
blind_pub = nullopt;
|
||||
}
|
||||
|
||||
if (blind_pub) {
|
||||
blind_details->o_pubkeys.insert(blind_details->o_pubkeys.begin() + vChangePosInOut[assetChange.first], *blind_pub);
|
||||
assert(blind_pub->IsFullyValid());
|
||||
blind_details->num_to_blind++;
|
||||
blind_details->change_to_blind++;
|
||||
blind_details->only_change_pos = vChangePosInOut[assetChange.first];
|
||||
// Place the blinding pubkey here in case of fundraw calls
|
||||
newTxOut.nNonce.vchCommitment = std::vector<unsigned char>(blind_pub.begin(), blind_pub.end());
|
||||
newTxOut.nNonce.vchCommitment = std::vector<unsigned char>(blind_pub->begin(), blind_pub->end());
|
||||
} else if (blind_details) {
|
||||
// Insert placeholder
|
||||
blind_details->o_pubkeys.insert(blind_details->o_pubkeys.begin() + vChangePosInOut[assetChange.first], CPubKey{});
|
||||
}
|
||||
txNew.vout.insert(position, newTxOut);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,17 @@ import os
|
|||
|
||||
MAX_BIP125_RBF_SEQUENCE = 0xfffffffd
|
||||
|
||||
# Utility function to extract info from outputs for use in assert_equal
|
||||
# Returns a set of tuples (address, is blinded, is OP_RETURN). (It would
|
||||
# feel nicer to return a set of dicts, but you can't do that in Python.)
|
||||
def outputs_info(outputs):
|
||||
return {(
|
||||
"" if x["script"].get("addresses") is None else x["script"]["addresses"][0],
|
||||
x.get("blinding_pubkey") is not None,
|
||||
x["script"]["asm"] == "OP_RETURN",
|
||||
) for x in outputs}
|
||||
|
||||
|
||||
# Create one-input, one-output, no-fee transaction:
|
||||
class PSBTTest(BitcoinTestFramework):
|
||||
|
||||
|
|
@ -317,13 +328,49 @@ class PSBTTest(BitcoinTestFramework):
|
|||
assert_raises_rpc_error(-4, msg, self.nodes[1].walletcreatefundedpsbt, inputs, outputs_array, 0, {"feeRate": 1, "add_inputs": bool_add})
|
||||
|
||||
self.log.info("Test various PSBT operations")
|
||||
addr = self.get_address(confidential, 1)
|
||||
unconf_addr = self.nodes[1].getaddressinfo(addr)['unconfidential']
|
||||
change_addr = self.nodes[1].getrawchangeaddress()
|
||||
conf_change_addr = self.nodes[1].getaddressinfo(change_addr)['confidential']
|
||||
unconf_change_addr = self.nodes[1].getaddressinfo(change_addr)['unconfidential']
|
||||
# partially sign multisig things with node 1
|
||||
psbtx = wmulti.walletcreatefundedpsbt(inputs=[{"txid":txid,"vout":p2wsh_pos},{"txid":txid,"vout":p2sh_pos},{"txid":txid,"vout":p2sh_p2wsh_pos}], outputs=[{self.get_address(confidential, 1):29.99}], options={'changeAddress': self.nodes[1].getrawchangeaddress()})['psbt']
|
||||
psbtx = wmulti.walletcreatefundedpsbt(inputs=[{"txid":txid,"vout":p2wsh_pos},{"txid":txid,"vout":p2sh_pos},{"txid":txid,"vout":p2sh_p2wsh_pos}], outputs=[{addr:29.99}], options={'changeAddress': unconf_change_addr})['psbt']
|
||||
filled = wmulti.walletprocesspsbt(psbtx)
|
||||
# have both nodes fill before we try to blind and sign
|
||||
walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(filled["psbt"])
|
||||
psbtx = walletprocesspsbt_out['psbt']
|
||||
assert_equal(walletprocesspsbt_out['complete'], False)
|
||||
# check that the unblinded change address led to unblinded change
|
||||
assert_equal(
|
||||
outputs_info(self.nodes[1].decodepsbt(psbtx)["outputs"]),
|
||||
{
|
||||
(unconf_addr, confidential, False),
|
||||
(unconf_change_addr, False, False),
|
||||
("", False, False), # fee
|
||||
},
|
||||
)
|
||||
|
||||
# Repeat the above, with a confidential change address
|
||||
psbtx = wmulti.walletcreatefundedpsbt(inputs=[{"txid":txid,"vout":p2wsh_pos},{"txid":txid,"vout":p2sh_pos},{"txid":txid,"vout":p2sh_p2wsh_pos}], outputs=[{addr:29.99}], options={'changeAddress': conf_change_addr})['psbt']
|
||||
filled = wmulti.walletprocesspsbt(psbtx)
|
||||
# have both nodes fill before we try to blind and sign
|
||||
walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(filled["psbt"])
|
||||
psbtx = walletprocesspsbt_out['psbt']
|
||||
assert_equal(walletprocesspsbt_out['complete'], False)
|
||||
# check that the blinded change address led to blinded change (and below,
|
||||
# when we call `walletprocesspsbt` with nodes[2], it will make sure that
|
||||
# node 2 is able to unblind this change, even though wmulti created it).
|
||||
# Notice that if `confidential` is False, the change is not blinded. This
|
||||
# is a quirk of the wallet.cpp blinding logic and will go away when we
|
||||
# overhaul this.
|
||||
assert_equal(
|
||||
outputs_info(self.nodes[1].decodepsbt(psbtx)["outputs"]),
|
||||
{
|
||||
(unconf_addr, confidential, False),
|
||||
(unconf_change_addr, confidential, False),
|
||||
("", False, False), # fee
|
||||
},
|
||||
)
|
||||
|
||||
# Unload wmulti, we don't need it anymore
|
||||
wmulti.unloadwallet()
|
||||
|
|
@ -717,7 +764,42 @@ class PSBTTest(BitcoinTestFramework):
|
|||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
# Regression for #1049
|
||||
# 1. Create a one-blinded-output PSET and check that it is blinded correctly
|
||||
addr = self.nodes[0].getnewaddress()
|
||||
conf_addr = self.nodes[0].getaddressinfo(addr)['confidential']
|
||||
unconf_addr = self.nodes[0].getaddressinfo(addr)['unconfidential']
|
||||
# 1a. Funding should succeed and *not* add a OP_RETURN output
|
||||
funded = self.nodes[1].walletcreatefundedpsbt([], [{conf_addr: self.nodes[1].getbalance()['bitcoin']}], 0, {"subtractFeeFromOutputs": [0]})["psbt"]
|
||||
assert_equal(
|
||||
outputs_info(self.nodes[1].decodepsbt(funded)["outputs"]),
|
||||
{
|
||||
(unconf_addr, True, False),
|
||||
("", False, False), # fee
|
||||
},
|
||||
)
|
||||
# 1b. `walletprocesspsbt` should then succeed in creating a full transaction
|
||||
signed = self.nodes[1].walletprocesspsbt(funded)["psbt"]
|
||||
tx = self.nodes[1].finalizepsbt(signed)["hex"]
|
||||
assert self.nodes[1].testmempoolaccept([tx])[0]['allowed']
|
||||
# 2. Create a one-unblinded-output PSET and check that it is blinded correctly
|
||||
# 2a. Funding should succeed and add a OP_RETURN output
|
||||
funded = self.nodes[1].walletcreatefundedpsbt([], [{unconf_addr: self.nodes[1].getbalance()['bitcoin']}], 0, {"subtractFeeFromOutputs": [0]})["psbt"]
|
||||
assert_equal(
|
||||
outputs_info(self.nodes[1].decodepsbt(funded)["outputs"]),
|
||||
{
|
||||
(unconf_addr, False, False),
|
||||
("", True, True), # blinded OP_RETURN
|
||||
("", False, False), # fee
|
||||
},
|
||||
)
|
||||
# 2b. `walletprocesspsbt` should then succeed in creating a full transaction
|
||||
signed = self.nodes[1].walletprocesspsbt(funded)["psbt"]
|
||||
tx = self.nodes[1].finalizepsbt(signed)["hex"]
|
||||
assert self.nodes[1].testmempoolaccept([tx])[0]['allowed']
|
||||
|
||||
def pset_confidential_proofs(self):
|
||||
UNBLINDED = "cHNldP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAfsEAgAAAAABAP1UAQIAAAAAASopobdl5W15RSedscp/8bxEXKuKIMOZw+JTqgD8qJEKBAAAAAD9////Awrye7Xu4kI5VnpTDeGaq8sYdXP3qdzYaHrLDRzaC8y51ggl1U8hJxSo+8GcTzHv926wsqTTkOrdBnJo8qcLwLQauQKktt71EJU7HTH5HsgG4kJV/tC32F992/WgieIPRkUkmxYAFPrs/iioimRS5hoJKl/hua83d7rwC1uuuLvfuQh38wHS+0Vg2ecXzypsUabYofOFaGSrICByCKvjgTF6TdHNp2el7Cwi+94dy4qMDrEh/25Aqnc+5qABAqWPEY9ZNCz7m64pANrr04bVgPxaWCr7LvvWGH5FLzvRFgAU96wAzcLFRah7B8gq17sVY9Uso18BIw9PXUt8b6hFgG7k9ncTRZ4baejmD87i5JQMeg1d4bIBAAAAAAAAKfQAAAAAAAABAXoK8nu17uJCOVZ6Uw3hmqvLGHVz96nc2Gh6yw0c2gvMudYIJdVPIScUqPvBnE8x7/dusLKk05Dq3QZyaPKnC8C0GrkCpLbe9RCVOx0x+R7IBuJCVf7Qt9hffdv1oIniD0ZFJJsWABT67P4oqIpkUuYaCSpf4bmvN3e68CIGA3pgD7iheh1WkyCWvviXQBa9KOJk6JBeYxEpPuxiRBOvEElHIxkAAACAAQAAgAMAAIABDiB25bQww62kp1L1uQVb7MxEVoem8kCzSmM5DW09I9V6DQEPBAAAAAABEAT/////B/wEcHNldA79CwFgAgAHY7+9IRzxAXWemL7C9M7CBAqQoSrXRoxI5/YnMLV6nV/GBMEhmvoDFJcNzRXI/LrIRMLZFvNrP5IupN8OZ+4q+++aJTnuYCZIDR1pssb0JHA0z2UXkEYdHv26qoW26RbLf2LNh29yVIOHG3jqqc7+L7F4UELZmjlEs6R1sulqQ0ePCUUgAsqURkdnNKtl0nORiyLN/9JfqGGTC30WhsdXifWRmqOfkWil0Va1bDYumMU7zJdW/go83ODuZ5VZVWFsBLFSn9HxF1SaFCGt197qo8dr+vhPZwb72k13A72D+5Lx7UKoYqamRJsoAZdUZ/oVd9GRlPbAmRPV7iOxmPYf+t9AQiEd0Z4AIgICuujF5+Lk/uCeX9+RWtJ8ioG51rogGduwt+iY1tZFtjUQSUcjGQAAAIAAAACACwAAgAEEFgAUg+8ATSQ8VvNg+WJAuweXm6kXlFkBAwgAv3xIGAkAAAf8BHBzZXQCICMPT11LfG+oRYBu5PZ3E0WeG2no5g/O4uSUDHoNXeGyB/wEcHNldAYhAwxmNPa94Vg9u/nZBWC/8IYTgnp85V5TMOEFWTTAcF2pB/wEcHNldAgEAAAAAAABBAABAwgA4fUFAAAAAAf8BHBzZXQCICMPT11LfG+oRYBu5PZ3E0WeG2no5g/O4uSUDHoNXeGyB/wEcHNldAgEAAAAAAA="
|
||||
BLINDED = "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"
|
||||
NO_VALUE_PROOF = "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"
|
||||
BAD_VALUE_PROOF = "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"
|
||||
|
|
@ -726,6 +808,14 @@ class PSBTTest(BitcoinTestFramework):
|
|||
ONLY_BLIND = "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"
|
||||
|
||||
## Check warnings for PSETs
|
||||
stats = [output.get("status") for output in self.nodes[0].decodepsbt(UNBLINDED)["outputs"]]
|
||||
assert_equal(stats, ["needs blinding", None])
|
||||
stats = [output for output in self.nodes[0].analyzepsbt(UNBLINDED)["outputs"]]
|
||||
assert_equal(stats, [
|
||||
{"blind": True, "status": "unblinded" },
|
||||
{"blind": False, "status": "done" },
|
||||
])
|
||||
|
||||
for output in self.nodes[0].decodepsbt(BLINDED)["outputs"]:
|
||||
assert "status" not in output
|
||||
for output in self.nodes[0].analyzepsbt(BLINDED)["outputs"]:
|
||||
|
|
@ -783,20 +873,22 @@ class PSBTTest(BitcoinTestFramework):
|
|||
{"blind": False, "status": "done" },
|
||||
])
|
||||
|
||||
# Check that we can combine these in any combination, as they all have explicit data
|
||||
assert_equal (self.nodes[0].combinepsbt([BLINDED, BLINDED]), BLINDED)
|
||||
for pset1 in [ BLINDED, NO_VALUE_PROOF, BAD_VALUE_PROOF, NO_ASSET_PROOF, BAD_ASSET_PROOF ]:
|
||||
for pset2 in [ BLINDED, NO_VALUE_PROOF, BAD_VALUE_PROOF, NO_ASSET_PROOF, BAD_ASSET_PROOF ]:
|
||||
combo = self.nodes[0].combinepsbt([pset1, pset2])
|
||||
if combo != pset1:
|
||||
assert_equal (combo, BLINDED)
|
||||
|
||||
# On the other hand, none of these can be combined with the "only confidential, no
|
||||
# explicit values" version
|
||||
assert_equal (self.nodes[0].combinepsbt([ONLY_BLIND, ONLY_BLIND]), ONLY_BLIND)
|
||||
for pset in [ BLINDED, NO_VALUE_PROOF, BAD_VALUE_PROOF, NO_ASSET_PROOF, BAD_ASSET_PROOF ]:
|
||||
assert_raises_rpc_error(-8, "PSBTs not compatible (different transactions)", self.nodes[0].combinepsbt, [pset, ONLY_BLIND])
|
||||
assert_raises_rpc_error(-8, "PSBTs not compatible (different transactions)", self.nodes[0].combinepsbt, [ONLY_BLIND, pset])
|
||||
# The fully-blinded (with proofs) PSET will combine with the blinded one,
|
||||
# and will copy the blinded data
|
||||
assert_equal (self.nodes[0].combinepsbt([UNBLINDED, BLINDED]), BLINDED)
|
||||
assert_equal (self.nodes[0].combinepsbt([BLINDED, UNBLINDED]), BLINDED)
|
||||
# When combining, "bad proofs" are the same as blinded data. So these transactions
|
||||
# will be interpreted as partially blinded (exp asset blind value, or vice-versa),
|
||||
# and will combine accordingly. These details are not so important, but what *IS*
|
||||
# important is that you cannot combine any of the bad-proof PSETs with the
|
||||
# unblinded one.
|
||||
self.nodes[0].combinepsbt([NO_VALUE_PROOF, BAD_VALUE_PROOF])
|
||||
self.nodes[0].combinepsbt([NO_ASSET_PROOF, BAD_ASSET_PROOF])
|
||||
assert_raises_rpc_error(-8, "PSBTs not compatible (different transactions)", self.nodes[0].combinepsbt, [BAD_VALUE_PROOF, BAD_ASSET_PROOF])
|
||||
for bad_pset in [ NO_VALUE_PROOF, BAD_VALUE_PROOF, NO_ASSET_PROOF, BAD_ASSET_PROOF ]:
|
||||
assert_raises_rpc_error(-8, "PSBTs not compatible (different transactions)", self.nodes[0].combinepsbt, [UNBLINDED, bad_pset])
|
||||
assert_raises_rpc_error(-8, "PSBTs not compatible (different transactions)", self.nodes[0].combinepsbt, [ONLY_BLIND, bad_pset])
|
||||
assert_raises_rpc_error(-8, "PSBTs not compatible (different transactions)", self.nodes[0].combinepsbt, [BLINDED, bad_pset])
|
||||
|
||||
def run_test(self):
|
||||
self.nodes[0].generate(200)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue