Skip to content

Commit eb63b8f

Browse files
author
MarcoFalke
committed
Merge bitcoin/bitcoin#23718: PSBT: hash preimages fields
a9256dc rpc: output all hash preimages in 'decodepsbt' (Antoine Poinsot) 4d6b532 psbt: implement hash preimages fields (Antoine Poinsot) Pull request description: This implements the [bip174 input fields for hash preimages](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki). One motivation is that we will need those once we implement signing support for Miniscript descriptors. ACKs for top commit: achow101: ACK a9256dc Sjors: re-tACK a9256dc w0xlt: reACK a9256dc Tree-SHA512: 145d39f7de86256d4174d063dcee217ea6f9ec7a138bbd5205941d17ca99dcccef0ace05f6d0d6a77dd863e3877b05e0752f2bc36ecd8c508e2c8adae2e03ae1
2 parents 83f8e6e + a9256dc commit eb63b8f

File tree

4 files changed

+178
-2
lines changed

4 files changed

+178
-2
lines changed

src/psbt.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ void PSBTInput::Merge(const PSBTInput& input)
153153
}
154154

155155
partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end());
156+
ripemd160_preimages.insert(input.ripemd160_preimages.begin(), input.ripemd160_preimages.end());
157+
sha256_preimages.insert(input.sha256_preimages.begin(), input.sha256_preimages.end());
158+
hash160_preimages.insert(input.hash160_preimages.begin(), input.hash160_preimages.end());
159+
hash256_preimages.insert(input.hash256_preimages.begin(), input.hash256_preimages.end());
156160
hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end());
157161
unknown.insert(input.unknown.begin(), input.unknown.end());
158162

src/psbt.h

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ static constexpr uint8_t PSBT_IN_WITNESSSCRIPT = 0x05;
3737
static constexpr uint8_t PSBT_IN_BIP32_DERIVATION = 0x06;
3838
static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07;
3939
static constexpr uint8_t PSBT_IN_SCRIPTWITNESS = 0x08;
40+
static constexpr uint8_t PSBT_IN_RIPEMD160 = 0x0A;
41+
static constexpr uint8_t PSBT_IN_SHA256 = 0x0B;
42+
static constexpr uint8_t PSBT_IN_HASH160 = 0x0C;
43+
static constexpr uint8_t PSBT_IN_HASH256 = 0x0D;
4044
static constexpr uint8_t PSBT_IN_PROPRIETARY = 0xFC;
4145

4246
// Output types
@@ -171,6 +175,10 @@ struct PSBTInput
171175
CScriptWitness final_script_witness;
172176
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
173177
std::map<CKeyID, SigPair> partial_sigs;
178+
std::map<uint160, std::vector<unsigned char>> ripemd160_preimages;
179+
std::map<uint256, std::vector<unsigned char>> sha256_preimages;
180+
std::map<uint160, std::vector<unsigned char>> hash160_preimages;
181+
std::map<uint256, std::vector<unsigned char>> hash256_preimages;
174182
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
175183
std::set<PSBTProprietary> m_proprietary;
176184
std::optional<int> sighash_type;
@@ -221,6 +229,30 @@ struct PSBTInput
221229

222230
// Write any hd keypaths
223231
SerializeHDKeypaths(s, hd_keypaths, CompactSizeWriter(PSBT_IN_BIP32_DERIVATION));
232+
233+
// Write any ripemd160 preimage
234+
for (const auto& [hash, preimage] : ripemd160_preimages) {
235+
SerializeToVector(s, CompactSizeWriter(PSBT_IN_RIPEMD160), Span{hash});
236+
s << preimage;
237+
}
238+
239+
// Write any sha256 preimage
240+
for (const auto& [hash, preimage] : sha256_preimages) {
241+
SerializeToVector(s, CompactSizeWriter(PSBT_IN_SHA256), Span{hash});
242+
s << preimage;
243+
}
244+
245+
// Write any hash160 preimage
246+
for (const auto& [hash, preimage] : hash160_preimages) {
247+
SerializeToVector(s, CompactSizeWriter(PSBT_IN_HASH160), Span{hash});
248+
s << preimage;
249+
}
250+
251+
// Write any hash256 preimage
252+
for (const auto& [hash, preimage] : hash256_preimages) {
253+
SerializeToVector(s, CompactSizeWriter(PSBT_IN_HASH256), Span{hash});
254+
s << preimage;
255+
}
224256
}
225257

226258
// Write script sig
@@ -373,6 +405,90 @@ struct PSBTInput
373405
UnserializeFromVector(s, final_script_witness.stack);
374406
break;
375407
}
408+
case PSBT_IN_RIPEMD160:
409+
{
410+
// Make sure that the key is the size of a ripemd160 hash + 1
411+
if (key.size() != CRIPEMD160::OUTPUT_SIZE + 1) {
412+
throw std::ios_base::failure("Size of key was not the expected size for the type ripemd160 preimage");
413+
}
414+
// Read in the hash from key
415+
std::vector<unsigned char> hash_vec(key.begin() + 1, key.end());
416+
uint160 hash(hash_vec);
417+
if (ripemd160_preimages.count(hash) > 0) {
418+
throw std::ios_base::failure("Duplicate Key, input ripemd160 preimage already provided");
419+
}
420+
421+
// Read in the preimage from value
422+
std::vector<unsigned char> preimage;
423+
s >> preimage;
424+
425+
// Add to preimages list
426+
ripemd160_preimages.emplace(hash, std::move(preimage));
427+
break;
428+
}
429+
case PSBT_IN_SHA256:
430+
{
431+
// Make sure that the key is the size of a sha256 hash + 1
432+
if (key.size() != CSHA256::OUTPUT_SIZE + 1) {
433+
throw std::ios_base::failure("Size of key was not the expected size for the type sha256 preimage");
434+
}
435+
// Read in the hash from key
436+
std::vector<unsigned char> hash_vec(key.begin() + 1, key.end());
437+
uint256 hash(hash_vec);
438+
if (sha256_preimages.count(hash) > 0) {
439+
throw std::ios_base::failure("Duplicate Key, input sha256 preimage already provided");
440+
}
441+
442+
// Read in the preimage from value
443+
std::vector<unsigned char> preimage;
444+
s >> preimage;
445+
446+
// Add to preimages list
447+
sha256_preimages.emplace(hash, std::move(preimage));
448+
break;
449+
}
450+
case PSBT_IN_HASH160:
451+
{
452+
// Make sure that the key is the size of a hash160 hash + 1
453+
if (key.size() != CHash160::OUTPUT_SIZE + 1) {
454+
throw std::ios_base::failure("Size of key was not the expected size for the type hash160 preimage");
455+
}
456+
// Read in the hash from key
457+
std::vector<unsigned char> hash_vec(key.begin() + 1, key.end());
458+
uint160 hash(hash_vec);
459+
if (hash160_preimages.count(hash) > 0) {
460+
throw std::ios_base::failure("Duplicate Key, input hash160 preimage already provided");
461+
}
462+
463+
// Read in the preimage from value
464+
std::vector<unsigned char> preimage;
465+
s >> preimage;
466+
467+
// Add to preimages list
468+
hash160_preimages.emplace(hash, std::move(preimage));
469+
break;
470+
}
471+
case PSBT_IN_HASH256:
472+
{
473+
// Make sure that the key is the size of a hash256 hash + 1
474+
if (key.size() != CHash256::OUTPUT_SIZE + 1) {
475+
throw std::ios_base::failure("Size of key was not the expected size for the type hash256 preimage");
476+
}
477+
// Read in the hash from key
478+
std::vector<unsigned char> hash_vec(key.begin() + 1, key.end());
479+
uint256 hash(hash_vec);
480+
if (hash256_preimages.count(hash) > 0) {
481+
throw std::ios_base::failure("Duplicate Key, input hash256 preimage already provided");
482+
}
483+
484+
// Read in the preimage from value
485+
std::vector<unsigned char> preimage;
486+
s >> preimage;
487+
488+
// Add to preimages list
489+
hash256_preimages.emplace(hash, std::move(preimage));
490+
break;
491+
}
376492
case PSBT_IN_PROPRIETARY:
377493
{
378494
PSBTProprietary this_prop;

src/rpc/rawtransaction.cpp

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,7 +1154,23 @@ static RPCHelpMan decodepsbt()
11541154
{
11551155
{RPCResult::Type::STR_HEX, "", "hex-encoded witness data (if any)"},
11561156
}},
1157-
{RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/true, "The unknown global fields",
1157+
{RPCResult::Type::OBJ_DYN, "ripemd160_preimages", /*optional=*/ true, "",
1158+
{
1159+
{RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."},
1160+
}},
1161+
{RPCResult::Type::OBJ_DYN, "sha256_preimages", /*optional=*/ true, "",
1162+
{
1163+
{RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."},
1164+
}},
1165+
{RPCResult::Type::OBJ_DYN, "hash160_preimages", /*optional=*/ true, "",
1166+
{
1167+
{RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."},
1168+
}},
1169+
{RPCResult::Type::OBJ_DYN, "hash256_preimages", /*optional=*/ true, "",
1170+
{
1171+
{RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."},
1172+
}},
1173+
{RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/ true, "The unknown input fields",
11581174
{
11591175
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
11601176
}},
@@ -1373,6 +1389,42 @@ static RPCHelpMan decodepsbt()
13731389
in.pushKV("final_scriptwitness", txinwitness);
13741390
}
13751391

1392+
// Ripemd160 hash preimages
1393+
if (!input.ripemd160_preimages.empty()) {
1394+
UniValue ripemd160_preimages(UniValue::VOBJ);
1395+
for (const auto& [hash, preimage] : input.ripemd160_preimages) {
1396+
ripemd160_preimages.pushKV(HexStr(hash), HexStr(preimage));
1397+
}
1398+
in.pushKV("ripemd160_preimages", ripemd160_preimages);
1399+
}
1400+
1401+
// Sha256 hash preimages
1402+
if (!input.sha256_preimages.empty()) {
1403+
UniValue sha256_preimages(UniValue::VOBJ);
1404+
for (const auto& [hash, preimage] : input.sha256_preimages) {
1405+
sha256_preimages.pushKV(HexStr(hash), HexStr(preimage));
1406+
}
1407+
in.pushKV("sha256_preimages", sha256_preimages);
1408+
}
1409+
1410+
// Hash160 hash preimages
1411+
if (!input.hash160_preimages.empty()) {
1412+
UniValue hash160_preimages(UniValue::VOBJ);
1413+
for (const auto& [hash, preimage] : input.hash160_preimages) {
1414+
hash160_preimages.pushKV(HexStr(hash), HexStr(preimage));
1415+
}
1416+
in.pushKV("hash160_preimages", hash160_preimages);
1417+
}
1418+
1419+
// Hash256 hash preimages
1420+
if (!input.hash256_preimages.empty()) {
1421+
UniValue hash256_preimages(UniValue::VOBJ);
1422+
for (const auto& [hash, preimage] : input.hash256_preimages) {
1423+
hash256_preimages.pushKV(HexStr(hash), HexStr(preimage));
1424+
}
1425+
in.pushKV("hash256_preimages", hash256_preimages);
1426+
}
1427+
13761428
// Proprietary
13771429
if (!input.m_proprietary.empty()) {
13781430
UniValue proprietary(UniValue::VARR);

test/functional/data/rpc_psbt.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@
3939
"cHNidP8B+wQAAAAAAQB1AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAAAA/v///wLT3/UFAAAAABl2qRTQxZkDxbrChodg6Q/VIaRmWqdlIIisAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4ezLhMAAAEA/aUBAQAAAAABAomjxx6rTSDgNxu7pMxpj6KVyUY6+i45f4UzzLYvlWflAQAAABcWABS+GNFSqbASA52vPafeT1M0nuy5hf////+G+KpDpx3/FEiJOlMKcjfva0YIu7LdLQFx5jrsakiQtAEAAAAXFgAU/j6e8adF6XTZAsQ1WUOryzS9U1P/////AgDC6wsAAAAAGXapFIXP8Ql/2eAIuzSvcJxiGXs4l4pIiKxy/vhOLAAAABepFDOXJboh79Yqx1OpvNBn1semo50FhwJHMEQCICcSviLgJw85T1aDEdx8qaaJcLgCX907JAIp8H+KXzokAiABizjX3NMU5zTJJ2vW+0D2czJbxLqhRMgA0vLwLbJ2XAEhA9LhVnSUG61KmWNyy4fhhW02UmBtmFYv45xenn5BPyEFAkgwRQIhANErhS2F3Nlh0vX0q2YGVN9u7cx5TAwzzlzDCf+1/OWNAiBnM4qODhclwZf7GoivWfUeROQlWyAWfIaEAxwF0fJZKgEhAiO3K+7wll0Qvgd47+zWH8rG95pOoWk5M4BzRGT4TyqzAAAAAAAAAA==",
4040
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAD/AAAAaoF/AKqqgABqgABAP2lAQEAAAAAAQKJo8ceq00g4Dcbu6TMaY+ilclGOvouOX+FM8y2L5Vn5QEAAAAXFgAUvhjRUqmwEgOdrz2n3k9TNJ7suYX/////hviqQ6cd/xRIiTpTCnI372tGCLuy3S0BceY67GpIkLQBAAAAFxYAFP4+nvGnRel02QLENVlDq8s0vVNT/////wIAwusLAAAAABl2qRSFz/EJf9ngCLs0r3CcYhl7OJeKSIiscv74TiwAAAAXqRQzlyW6Ie/WKsdTqbzQZ9bHpqOdBYcCRzBEAiAnEr4i4CcPOU9WgxHcfKmmiXC4Al/dOyQCKfB/il86JAIgAYs419zTFOc0ySdr1vtA9nMyW8S6oUTIANLy8C2ydlwBIQPS4VZ0lButSpljcsuH4YVtNlJgbZhWL+OcXp5+QT8hBQJIMEUCIQDRK4UthdzZYdL19KtmBlTfbu3MeUwMM85cwwn/tfzljQIgZzOKjg4XJcGX+xqIr1n1HkTkJVsgFnyGhAMcBdHyWSoBIQIjtyvu8JZdEL4HeO/s1h/KxveaTqFpOTOAc0Rk+E8qswAAAAAF/AKqqgEBqwAABfwCqqoCAawA",
4141
"cHNidP8BAFICAAAAAZ38ZijCbFiZ/hvT3DOGZb/VXXraEPYiCXPfLTht7BJ2AQAAAAD/////AfA9zR0AAAAAFgAUezoAv9wU0neVwrdJAdCdpu8TNXkAAAAATwEENYfPAto/0AiAAAAAlwSLGtBEWx7IJ1UXcnyHtOTrwYogP/oPlMAVZr046QADUbdDiH7h1A3DKmBDck8tZFmztaTXPa7I+64EcvO8Q+IM2QxqT64AAIAAAACATwEENYfPAto/0AiAAAABuQRSQnE5zXjCz/JES+NTzVhgXj5RMoXlKLQH+uP2FzUD0wpel8itvFV9rCrZp+OcFyLrrGnmaLbyZnzB1nHIPKsM2QxqT64AAIABAACAAAEBKwBlzR0AAAAAIgAgLFSGEmxJeAeagU4TcV1l82RZ5NbMre0mbQUIZFuvpjIBBUdSIQKdoSzbWyNWkrkVNq/v5ckcOrlHPY5DtTODarRWKZyIcSEDNys0I07Xz5wf6l0F1EFVeSe+lUKxYusC4ass6AIkwAtSriIGAp2hLNtbI1aSuRU2r+/lyRw6uUc9jkO1M4NqtFYpnIhxENkMak+uAACAAAAAgAAAAAAiBgM3KzQjTtfPnB/qXQXUQVV5J76VQrFi6wLhqyzoAiTACxDZDGpPrgAAgAEAAIAAAAAAACICA57/H1R6HV+S36K6evaslxpL0DukpzSwMVaiVritOh75EO3kXMUAAACAAAAAgAEAAIAA",
42-
"cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAATwEENYfPAAAAAAAAAAAAG3t93NmzqdwlifBjtWBRnFrHYkoMdmriSG1s74PiZ8ID3+4wNJ18fPeMDsRRe9iTAopsKogDQfxLmL6Kgj07xScE2QxqTwAAAAAA"
42+
"cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAATwEENYfPAAAAAAAAAAAAG3t93NmzqdwlifBjtWBRnFrHYkoMdmriSG1s74PiZ8ID3+4wNJ18fPeMDsRRe9iTAopsKogDQfxLmL6Kgj07xScE2QxqTwAAAAAA",
43+
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAFQoYn3yLGjhv/o7tkbODDHp7zR53jAIBAhUK8pG6UBXfNIyAhT+luw95RvXJ4bMBAQAAAA==",
44+
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAIQtL9RIvNEVUxTveLruM0rfj0WAK1jHDhaXXzOI8d4VFmgEBIQuhKHH+4hD7hhkpHq6hlFgcvSUx5LI3WdIl9oBpI/YyIgIBAgAAAA==",
45+
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAFQwVzEnhkcvFINkZRGAKXLd69qoykQIBAhUMxRtmvO1eRJEAG9cCZpdw3M9ECYIBAQAAAA==",
46+
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAIQ12pWrO2RXSUT3NhMLDeLLoqlzWMrW3HKLyrFsOOmSb2wIBAiENnBLP3ATHRYTXh6w9I3chMsGFJLx6so3sQhm4/FtCX3ABAQAAAA=="
4347
],
4448
"creator" : [
4549
{

0 commit comments

Comments
 (0)