Bitcoin Forum
September 25, 2024, 01:26:45 PM *
News: Latest Bitcoin Core release: 27.1 [Torrent]
 
  Home Help Search Login Register More  
  Show Posts
Pages: [1] 2 3 4 5 6 7 8 9 10 »
1  Bitcoin / Development & Technical Discussion / Nested ECC - A new hash algorithm with tremendous benefits? on: April 14, 2024, 11:15:32 PM
A new hash algorithm?

All right, this may be more cryptography than Bitcoin, but bear with me. Doin' some thinkin'.

After one new huge public release after the other, we see salts and many rounds of hashing using good old hashing algorithms such as PBKDF2-SHA512; NIST even promotes some of the methods.

Got half an hour to kill? Read this straight from the mouth of the horse, the scientific primary literature.

Nested Elliptic Curve Cryptography (NECC) - My suggested solution

Pick a random number. That's you private key. Rather: have a computer pick one for you, as we human suck as large numbers.

According to Secp256k1, the large integer can be anything between 1 and 115792089237316195423570985008687907852837564279074904382605163141518161494337. It is a very large number indeed.

I prefer Python to do it for me, and I don't mind hexadecimal annotation:

Code:
>>> import random
>>> privkey = random.randint(1,115792089237316195423570985008687907852837564279074904382605163141518161494337)
>>> print (hex(privkey).zfill(64)[2:])
2d05ad49ddd07abb19571bfd238a5a241a80a14c3a9b31db3e144d4fc239ceda

You could produce the same end-result with a one-liner in bash:

Code:
openssl rand -hex 32
.
No need to go online at any state of keypair or wallet generation!

The use case I had in mind was not cryptocurrencies, but I'd love your comments on it.

"The Regular Website" need a database with both usernames and passwords hashed. The problem with SHA1, SHA-256 or even fancier stuff like bscryp, script or PBKDF2-SHA512 so secure store use and store user credentials - and hope and pray these are never leaked. Not good for "The big website" nor for the user, as we have seen in numerous hacsks over the past years. (More that 97% in "haveibeenpwnd" has been reversed. Not good.

So, what is a cood hashing method in 2024? SHA256 for storing passwords is Stone Age.
  • It should be "expensive" for both CPU and memory
  • It should be intrinsically slow; the fastest rigs in the universe should be able to check one (1) in a second); not millions per second               
  • No immediate gains when using GPU (clusters)

So, allow me to introduce Nested ECC! Pseudocode below:

Code:
<any password/phrase/file>        ---SHA256--->
32 bytes      ---ECC according to Secp256k1--->
33 bytes (X)  ---ditch 1st byte, whether Y is even or odd--->
32 bytes

Repeat the last steps as needed; a trade-off between computational demands and user experience. n(lopp)=1,024 is where I would begin.

See what I'm doing there?
1. Using one cryptographically secure RNG to roll the dice
2. Treating the random number as a private key; all as defined by Secp256k1
3. Looking up its corresponding public key coordinate (X,Y), discarding everything that's not X (yes, Y is wholly omitted, even its sign)
4. Backfeeding the new "X only" to step 3 in a loop (1,000 rounds?)

Since ECC is so much more CPU and memory-intensive than the good old SHA family, I think we have something for future webmasters and TLS-issues (to begin with). Instead of nesting SHA256 or the like, we are jumping back a fort between private and public keys on Secp256k1—until we, and only we, know where we started.
,
You could (should?) be perfectly open about what cryptographic methods you use. Let's say the worst happens, and your user/pass database is in the wild. How bad is it? It's not like a bot army could fire up Hashcat and have a stab and your ned mystery hashes.

Let me hear from you, crypto-wizards! Something in me that got you thinking of one or two ideas in crypto design. Am I right?




2  Bitcoin / Development & Technical Discussion / Re: World's fastest and simplest block parser for those who (only) need all HASH160 on: March 05, 2024, 08:23:26 PM
For those interested in mempool analysis (I am), I created a new "bash pipe monster" that isn't pretty but gets the job done.

First, in Bitcoin Core (fully synced and indexed), simply "savemempool", and it will spit out a file called mempool.dat

Along the lines, pun intended, of the OP, this will make it human readable:

Code:
cat mempool.dat | xxd -p | tr -d '\n' | tr -d '\r'| sed 's/0100000000000000[0-9a-fA-F]\{4\}0000000000000100000000010/0100000000010/g' | sed 's/00000000000000000\([1-2]\)000000/\n0\1000000/g' | sed 's/0\{9,30\}\n/00000000\n/g' > mempool.hex

It should work on both Windows and *nix. The output, mempool.hex, will be a text file of all mempool transactions (a snapshot according to your node), in which every row is a Bitcoin raw transaction in serialized hexadecimal code.

It's not 100% perfect; some rows are empty, but ~99% of the rows are transactions that https://live.blockcypher.com/btc/decodetx/ think are OK and/or have already been broadcast.

My last round gave me ~42k candidate transactions (one per row) and the size of mempool.dat was close to 100 MB and the size of the resulting mempool.hex almost double.

It is good enough for my purposes (currently: signature analysis). Do with it as you wish! (Don't ask me to explain the regex; it took some severe trial and error. It helped when I realized 16 zeros in a row are a separator between transactions [undocumented], and it allowed me to insert a newline character at the right spot.)





 

3  Bitcoin / Bitcoin Technical Support / Re: Would P2WPKH v1/bech32m addressee be legal (and usable and spendable)? on: March 02, 2024, 12:14:45 AM
Oh, I just realized you can test beyond v1. Said and done:

Code:
bitcoinutils.bech32.encode('tb', 2, memoryview(bytes.fromhex(hash160)).tolist())
bitcoinutils.bech32.encode('tb', 3, memoryview(bytes.fromhex(hash160)).tolist())
bitcoinutils.bech32.encode('tb', 4, memoryview(bytes.fromhex(hash160)).tolist())
bitcoinutils.bech32.encode('tb', 5, memoryview(bytes.fromhex(hash160)).tolist())

--->

tb1zlk2s76mvtpf2rqrpm97ngk3skgjdw7jtpac93n
tb1rlk2s76mvtpf2rqrpm97ngk3skgjdw7jt2r0wuc
tb1ylk2s76mvtpf2rqrpm97ngk3skgjdw7jtjt3kkq
tb19lk2s76mvtpf2rqrpm97ngk3skgjdw7jte4xamt

One single transaction to these nonsense addresses was accepted and confirmed without a glitch...

What have we learned here? That major wallet software gives you no warnings before sending to address types that haven't yet been defined. And that is, in my opinion, suboptimal. Wallet software should give you a "please note that you are sending funds to undefined address types, and your funds will be lost" notice.
4  Bitcoin / Bitcoin Technical Support / Re: Would P2WPKH v1/bech32m addressee be legal (and usable and spendable)? on: March 01, 2024, 02:35:19 PM
Field-test results:

Using bitcointools and hashlib in Python. First, generate a normal native segwit address and its private key wif from a (very not cryptographically safe) private key:

Code:
bitcoinutils.setup.setup('testnet')
privkey_hex = "1234123412341234123412341234123412341234123412341234123412341234"

privkey_wif = bitcoinutils.keys.PrivateKey.from_bytes(bytes.fromhex(privkey_hex)).to_wif()
pubkey = bitcoinutils.keys.PrivateKey.from_bytes(bytes.fromhex(privkey_hex)).get_public_key().to_hex()
pubaddr = bitcoinutils.keys.PrivateKey.from_bytes(bytes.fromhex(privkey_hex)).get_public_key().get_segwit_address().to_string()

print(privkey_hex)
print(privkey_wif)
print(pubkey)
print(pubaddr)

The output is benign and as expected - our native segwit address on the last row:

Code:
1234123412341234123412341234123412341234123412341234123412341234
cNC5yqKCukH9sm5opMizA9Q2Q7P1KUy6vKfxHCmq88d3RLeZcPxh
02b4b003aebb5672a4c4dbc37da21f0255f9034109b6f5bf26ddb95b7ed6ff24f8
tb1qlk2s76mvtpf2rqrpm97ngk3skgjdw7jtz50lw8

Now, to verify the public address is the same, using a more "manual and hashing it yourself" approach:

Code:
def pubkey_to_hash160(pubkey):
    if len(pubkey) == 130 or len(pubkey) == 66 and int(pubkey, 16):
        return hashlib.new('ripemd160', bytes(hashlib.sha256(bytes.fromhex(pubkey)).digest())).hexdigest()

hash160 = pubkey_to_hash160(pubkey)
print(bitcoinutils.bech32.encode('tb',0,memoryview(bytes.fromhex(hash160)).tolist()))

It produces the identical public address - tb1qlk2s76mvtpf2rqrpm97ngk3skgjdw7jtz50lw8 - confirming our code should be OK.

Now, to generate the non-standard "P2WPKH(Bech32m/v1)", the topic of this thread, simply change witness program 0 to 1:

Code:
print(bitcoinutils.bech32.encode('tb',1,memoryview(bytes.fromhex(hash160)).tolist()))

It yields tb1plk2s76mvtpf2rqrpm97ngk3skgjdw7jtukgcxw - for which we supposedly have the private and public keys.

What's interesting is that Bitcoin Core and Electrum accept this as a legitimate receiving address. 0.001 tBTC was sent to it, and the transaction was swiftly confirmed. Interestingly, this blockchain explorer prints out the "tb1p..." address correctly but also calls the type "Unknown."

Anyways, the big question: how about spending from it? No wallet software or semi-automatic online service (such as coinb.dev for P2SH) I am aware of can create raw transactions from segwit outputs, so I went with good old Notepad++ (and a little script to flip endianness for the tx hash)

and I decided to go with this:

Code:
020000000001013715989bfedc450192588c193465473d472fad620d6aea1768341af4aa2c49c10000000000fdffffff01318601000000000017a914628cde58e1bcd42efb72b1821ec9fdf49e0f1d49870120b4b003aebb5672a4c4dbc37da21f0255f9034109b6f5bf26ddb95b7ed6ff24f800000000

Because of the comments above, I had reasons to believe the output is either eternally stuck OR spendable by all, so I only used the public key serialized as the witness program (i.e., I didn't bother to sign with the private key). I ran it through Blockcypher's decode raw transaction service to verify that the serialization checks out and makes sense, which it does.

Now, onto actually broadcasting it. Uh-oh. Problemas.

Bitcoin Core console, sendrawtransaction: "bad-txns-nonstandard-inputs (code -26)"
Electrum console, broadcast(): "TxBroadcastServerReturnedError: Witness version reserved for soft-fork upgrades"

I was, however, able to broadcast it using Blockcypher's web service: "Transaction Successfully Broadcast" (the tx here, but I think it will drop out of the mempool and disappear within hours).

At this moment, Blockcypher says, "Confidence low: 29%", and it hasn't reached the mempool of my take-back Electrum wallet, so unless someone else has a brilliant idea of how to spend it, it is stuck, barring a miracle (such as a future soft fork that allows this address format).


BUT! Isn't it strange that major wallet software allows sending to this homemade address type if outputs to such are forever unspendable?








5  Bitcoin / Bitcoin Technical Support / Would P2WPKH v1/bech32m addressee be legal (and usable and spendable)? on: March 01, 2024, 12:48:29 AM
We have a public key. We have its RIPEMD(SHA256(pubkey)) hash, a.k.a. HASH160

We can easily construct a valid native segwit address by

Code:
> bitcoinutils.bech32.encode('bc',0,memoryview(bytes.fromhex(our_hash160)).tolist())
> bc1qmgt5t6d4fx7sh7s626vhr3m7hgcv6kjt450t6x

So far, nothing strange. Works as intended.

But if we change the version from 0 to 1 and disregard all taproot standards, we get a bech32m public address like so:

Code:
> bitcoinutils.bech32.encode('bc',1,memoryview(bytes.fromhex(our_hash160)).tolist())
> bc1pmgt5t6d4fx7sh7s626vhr3m7hgcv6kjttkgvj0

Good for anything? Will wallets accept it? Will nodes propagate a transaction to it over the BTC network? Can we use normal privkeys to spend from it with, as we do with regular native segwit?

Due to lack of better terminology, we can call them P2WPKH(Bech32m). A thing or not? That's the question.
.
6  Local / Skandinavisk / Skaffa crypto on: February 24, 2024, 12:18:16 AM
I don't speak a word Swedish (?), but as I am totally bribable, and I took 1000 tBTC in exchange, from some random dude messaging me, for posting ons link right here in this subforum.

Said and done. Testnet coins are all mine!

Here it is the link:

https://skaffacrypto.se/

 Smiley

7  Bitcoin / Bitcoin Discussion / Re: 120 USD to transfer 25 cents - Taproot genius transaction on: January 20, 2024, 09:47:53 PM
https://ordiscan.com/inscription/56605119

Because that person just paid for a piece of virtual art.

They have a lot of them.
https://ordiscan.com/address/bc1plcyzfrpsmmnazmrt4dv2qd35avw2fzjrwfh5rp0xk3v0msvdyfjq9h5wff/activity

It's their money they can do what they want with it.

-Dave

Ah, the wonderland of NFTs... no further questions. As you were.
8  Bitcoin / Bitcoin Discussion / 120 USD to transfer 25 cents - Taproot genius transaction on: January 20, 2024, 09:36:43 PM
This recent transaction stands out a bit...

0.00288744 BTC (120.19 USD) to transfer 600 sats (25 cents)

No change address (you're welcome, miners)

https://blockchair.com/bitcoin/transaction/7d57e84ab62612c501d4f2a6c3df1b806b7057165b82a444e97f041ad9f7516c

Here is the raw transaction (~25k bytes - for scrolling purposes):

Code:
010000000001011c8baccf477db1a1b941699b98ad9ac53faf6dafea14f693b87c5b99ccfb83e60000000000fdffffff015802000000000000225120fe08248c30dee7d16c6bab58a03634eb1ca48a43726f4185e6b458fdc18d22640340a189ae2b7fd67482da9d123ae721ec0d90c1badbcba60a58a5ee269ec8339e903a1ebc9892fdb43af707bdadd9419c54afc6b27418b3333b6d8b7174df3a5a11fdff5f2051e7111c98bf4699122e43b7ec74f26ba98bd42ccd8c01fa60c8a17367f37547ac0063036f726401010a696d6167652f6a706567004d0802ffd8ffdb00430006040506050406060506070706080a100a0a09090a140e0f0c1017141818171416161a1d251f1a1b231c1616202c20232627292a29191f2d302d283025282928ffdb0043010707070a080a130a0a13281a161a2828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828ffc0001108014000de03012200021101031101ffc4001d000001050101010100000000000000000006030405070802010009ffc40049100001030204030505040803060603010001020304051100061221073141132251617114328191a1084252b11516236272c1d1f0243382344353a2e1f117257392b2c226354463ffc4001b01000105010100000000000000000000000200010304050607ffc40032110001040201020405020603010000000001000203110421311241051351712232618191a1b10614c1d1e1f015234252ffda000c03010002110311003f00b9dd274af491ab7b5f03afbb518e9d5254a5a01b0292304ba0151e97271c96d1bed7dba9c7259b03a61a716fb2d28650ced686d895ed20075dd07a052b0b20ab5dbb6bf86d7bfd70f65c2d692adc27ee84201f9f5c4795b6d93dabfa540d8828b586394c864d0c9d25c7deff00cabcd2c936d0955b6ef2d04f8dcdbf3c74953ad0f71957faf7c36724a05f4a92b37e4500617f69614c052a385f4dc8fe5878f24d0802dccf95ff0094e632765ba4eda7132117753a40d8d8dedf2c0c66ead4aa12d97e1c30b88b3671fb15e93e631349909d000612124db4eab62463c8438d594d84246d7bf3c6de078b471bc195c0aa3958af73486e957233a332100395b723dcdcf6704dc7cce39fd2b97df27daeb15790a2790469c58b2a1d3e620a24476dc6f9fbbcf03d3b2bd35c5130c498ae0df53476fae378f8ce38d596fb57f6596fc09fb51fcff743455936c753b37578aaff00d31da15939d294a263e83fbce287f2c4d8a13339869994fc93a4d940afdef5be1e44a1d269c16ca2032b046a5958d6af99e58c897c5a07b4b83ddf63fe1586e04cc781d23f0a11a919622b5644ced8037b15951fadb0afb7531fbaa3412b0073034fe5878ed232fa1dd688435937d22f61f5c347a914a75d21100f78ee02942df5c52ff92c6ffe9df956c61649ff00c80108d5e9b4aa84f71d69894e4955ae1b775004799f8617a3d1b3036e110c2e135a74eb90f951f509c1ac169988de88d192c7fe9f5f89c7aa77b4429424250126c75916f9e19fe3f357971ec0f5da963f09693d4f3b3e9a4850e13915a089b294fb8a55d6b51e4a3b75c12bc52d3694a893e02d7be07341411ed0438ca8ee08ba4f5e63d3139db372882e2d4d5baa791c67c196f697bf5d4e2aecd086f4b47012a99290004b2bb789b0c3395254f92840d09077b1e7845dd1af4b4e2c83d54ae785d0c26c3b421092744d0802dcf5c432e6644d6cb147d11b21633e22936425275a949006d603738edc9246e7b5d3e236185fd8d1b80543e18e64c64b6ca8a8baa006e41bdf031c192c69ae3e899f244e76d22d38a44b4155cb6e0d2028f23fdfe787cfaf4347c4ed6c3642597a3a0b4a4e9e971b8b61ca1a2b482b714bb795b16e096789859bbe42af27438f5260a7141abad413a76212309f64b2ad924f5df0ea7389688421b04f3dcff2c378ca5a96bfda9585f216b5b10799d4ea91c49fa7aa2ad5b428c9398a9f01f5c598f2997529493742940df90040373fd313b93ea512a6fb8a872da92128370856e9dc731cc7c710ad3663d656a5107b7680df6ba9076fa13894a3d2a1cdab194ec6064f62a495a094ab4ea4ec483b8db1b7e1f379af0093a0abc8de9b442dbe1401b7bde5cef865260a16a2b43614544a8af5d958808598195988c6a0a5dd22e904db6eb89e8f2ff6ef474a09ecc91a89e58d1c9c7748de9902a914c01b6a6e94bcc0ee38a0907dd7771ffbb1f225590a42e2055cdcd8dc1c3354292a9052e29c7c137b25561855da7c869bd41842537f7756a563939229ec98c1a1ebb5a83a356522fa5b52ae9425bdec1215aaf87b4fa734a4a4be8dd5bd89c7915bf6617ecd01d3b92e1b63e9350750d952742556b020eac0e3f4c4e2f9859f4a46e2f7fc0c5e56130a2b3a925b494924e83b81f0df104e664692c82d84b8502f7d36b8c35acca6cb254f28978e4d0802c12df5fef7c46502ce5599400a4a0826cadc72c1ca2395c65e9aa5a98f86d115c9ba528fe609eb5292da43693eed922ff5c206a95056a205cabc40fcb12f369a894495592e78a799c0fc881221b4a25d538941b15a46e3aee302d95b2775344d89dae90a52993de520b726e9701d94073c2acaa42db297dd5a77b9d7dd1861017a10e2eca4b961a0ad277f961c4894b7e239dbc6414904a53aec4586f888b689aeea19583a8d04b38f2587121480a58049d1bdbcbf2fae3cf696c3dbd8122e4dfc7c710ec4f5eed259482a22eb4af513e47ae1e3da1b5a166ea706d62709d1749a2a3e94fe3b9fb425c702d6adb61b01e1842408b2c760b51d947bc136b1f5c354bae04ea2a0516dadb6f86ce4a756416dd712a3726dd7c2d7c264766f8286ab8524c1769ebd2b054856cab0dbe587b0657b315026ecbbb116e581fede549428252b4903485dbfbdf71cb0f5aed5a691dba86b02e4155ce1dcc2dddfe12203851442c2e09035ad2b70abf668fbca3e9f3c0d46ae562b5a971e652696da544043ca05c1636dc2bfa624db4b69d0ea8685a482176e57c79558301e7532274186b4a85cad4dd8abe237df1a5e1b9b8f8ed2d95967daff00759b9d8724c4796efe8993d0a6291ae4e764377e619084fe44620e643a09708995facd51fe88689fe87045129d457077291114e0e4ab9524fc0e1eb4cb4c3a030d351d36dd0ca0241f96345fe391443febb34d0802f4a03fa2a0df0b95c7e3a1f725054b835896b4ae98c4e8915360872448d3a7d53cfe38398129d661351e53c57312801c7103ba4f8fe584e3cb536b73512e3215a4ebe60f879f3c4b4669875b4ba84a4a4ee0018cac8f1174ed0c6b43473f5fcab8313c925ce36992a2a96145c1dc29052e85f78a8f3fe5f3c75063943cde924a529b1d479e1d3cc0490900a6fb91d00c370ebadb64a9ad409b8208b8c67c6da9017581deb68cbadb41215dff000d221c8426fa5f424dbf7bbbf99189ea1364cc5104ff00967f3180ecc4f862893961e25aba1eef9b965616951dfc0d8fa1c1f50c832d4a06c0b7cfc7718ed3c2608db7230836a84ce77054452e0453458652ca10a53285dc0b1be918ee3beda64a92103b5513debf3c294d5139769e52013ecadef6e5dd18087f3154e6ce94de5e80c2d319d5071d79c03bc363b123f9e34326296724466ab92555748c87e647cd2425d4d810a57526fbe3b797d8a35682a755b580bdf00461e7190da24c8acc78e147ba961bd4a171e406249acbb9b8341c19a798be95c71718ad1e10db7cc17f7fec98e51f9ba0fe9fdd163a82b8a52b362bdd57e9812afb919afd9a95d9041b14369dd5e649c7ca819d9955c56a96ea52365b8d5bff00ae036bb51ac4798a54ea8515e7c7de477ede561b6027f077640a89cd2ace2f89c70bae5691fefba9275c88569d085e81651dbbc7f2c3192fc804a93da2509dd211ccdb4d0802f9e253f48c2aa51d894db0969f166dd412410b16b917e693bdb106f4849d40147bdb1b124e399971dd0c86378d8fc2eeb09ed9e3123468aeccd905cf6870b81cbd829c56e30fe3ce7e7a8a56e9eefbcbe571f9620dc94deaeeb65646db8b5b0434b2574c6d4a6509529474a53b5fd7eb88a5686b6e94d90d6b19749e5dd712a723f67fb31de528db1d3290a417491a9c17245ca47a61bfb239d8f68b63bb62a2350dfc3fbf5c725e8ea6d0a43a50c27bba06e126d8afd3aa0b29c4176939404b0162f6dae6c6e6f86c95aae80905cd6adc5bf3be3b4f6c875b72c0b6a05245bbc478dc0c287405155edbf323ae078fada89dcaf11a4af506ca5b50b10a16c22eb6104ba349484f7475070a2d47b54a749d405ac0639d085152528bacf7b49e5f3c26e8da405af194a10ead2c2825d22f61d2d8e996cb274a0294e13752bf10385a3a034a4a8a13aee495116b9385342cbaee9792dc829ba09dd22c708bacd5a73413b8e94ad871b26e922f63d309d2d2d90ea1cd0a55f6d4798feef87a129009041eeee6dcf00f54656a9ee8df495ec474c45034496d254d8d179b6d2518bd25a600434d85ab90091b0c45d764181508cf3cf1430a6eca005c6af03e7cbe58848b01d5ad29dd3bee4a88bfa624931da8b4e94f54d1aa3b26c56a1aec790b7cfeb8b90e3b41a1bb523e06466eed4d20a0c7438a0a2858d87bc3f961cc35943641b147ee2b10d469484d08029295ad9b08ea374b62e7b31b6db1feef89c6e3eb1ab4e9db62396294edf2ddd2167cc00f84a946e45f4a6d752bae155205f702fe7d4621521d8ee6b51524720a1b83855eadc7842d366308f0054357cb9e2de1cb67a5c093ecb326686ec1a5f54a98cc98cf009494ad252b491b2d3d41c4fe5e2a04036d9bb6df0c0d315da7549b7d302407dd424296842140dafd361825cb0165575dae504f3f318dcc28dec9a9a2afb281ef0f65da6941bab2dd3ac6ff00e15bff00e2302758ca74390e3f224c671a90a429d5299734ea55afcb95f9e0b32c1272dd32e36311bb1ff48c37a933da37b7bc11649b72dbfed8d8ca9e484db0d2abe5325d3c215a7e4882f25a0cd42a8db2a4850097ed6f861fb79069612a53d36a4a50fc722de9d3042cb85b808717604148528ec3cf0e19703ad59a297f51ba7ba2c9eb8a03c4261aea29ce1c47748793912901496d6a9ee2ad720bfb7e588b9b4da4c4716d53a236d693a0ab4eb5a8faaaff004c1cb89f628ca70a94fba373756dcbf2c575549325131d90d06d2db86faca8aafe361f2c4597953f45759df65a7e178313a4eae909a4da5ae42c8d0fa8acf3bd8abd30f289428ec3c5c9292a09d9294a8dc1f3c2306aafb3da487bb5503dd49d3b0bf9623e4e6a5c27921a4178826e55d49f4fcb186e6cd25b5a57505b311e5b7853153cb66448764b2e96b59d9096ff003c2b1232e347431a90b75b036274dee74d0802038bccf516d0e3b2c86d0bb84a00dc7f4c3ec9d5d8f5243897367db1de16b958e87cf0d2433b63b76c0a413473b63f8ce8295699985a9adc85682b51084ea2ab0b78f861ab14df6676eb7da5b2a56b5d8101200f31e9877212b5ce6dc121410e120274f3f2df0a38cea0943ad950b1b923603ae21f308fa2ce076b8d4cbd74c5714a20f86d6c271d68529492e13a37b5b61ebfdf5c2ea8cc345bd294dd63bba6f841dd77d20d80fba0eff1be23041be51380ecbe71c56a4a92906fd52797c708a176090a590ddc93736c7920bda9b119285b5cc9fc3e3f1c72124a54b4f748d5a4017dadff005389034042dd2ea629f4b804546bec48528a873eb87adc84c869a324ab505058b123593d2c37ff00b622a2cd78c8ec909d9c4eea48b9e5e9e9890a1a2c125e09b68b8b277b9e7fcb05233a1bbe513ab9532ddd285ea04a80b9030ca951db98db925454505d524241e601b611abd453160489093a090129d7b5c9db0334fcca696ef632438d950b94a93b1f31e388a2c69248c960da96089cf612d47469ec76a1453d6e05f963ba9b48934a7e13a90965d468511cc79e059bcd5ed3cc59bbdb500458ff0076f9e1cccab331d0cf6ae4b521c49b2834549b8e62e37c1c516447a69a286489ed23a94a65aa246a6c7722a35ac2bbc14a3750e9fd312ab65d83b217a9b572494e222895b61f4b9a1b79e421ab9750dab4a940f2b9f861d31517a77681504d0802654616fd9a9e02c7e009b6249a13e597bcfc5ef4a9c8d90bc9770996636e7d5e9ea89065086e9372a00f7923a6db8bedf2c4451325390d497e74c44c4a772c25bb249f337df04becefeadee9245c9e98790d2b6c8083aafb1bf5c0c3e272c51f92dd0279a16b3e6c58defeb3ca6b4e8916015688ed365ddca909b5fc060ae80947b49008090ded6f518839ad282429b0148fbc93d4624b2ba829d73b3577745eca1b8df96343c2f3648662c7eefbf751cb135cdb6e933cb4afff001ba6002dfe15bb9ff48c3172738894a69f6d5a472b0bec3afe787b966c2854f079760d8e7fba30de7e972a2b6ae06846a5288bedff004c6c78a35ef883e33b514040710e0ba5381d085a53a92a581a2dcb0f62bccc55b8e48576655be9209b7a7fd311aa609483a0ab56e149e7fd71f202d493a13a5636373755bcaf8e6bf9d96175bdbbfd15c1131e3451138eb521a4a91a5c6542ca237b0f31811cc2ba3bcfa076c9496beea517f5b7f5c4b4358742639286d4069d572924785bc710f9a18a7422a6d96f54a50d4e7664ea4a0785b95fc71a6eca3951d80294d87188e5ada189f250e3c5b8cda747ba1c50bdbe1e3cf11b24b3198588ac280528f6afac6a23fa7a0c3c964ae67b3aa38430da2e54a583bf80b7d6fbf3c309cb5bfa5844677d99a1a93d99dbd76f8e2bb2da68fdd751111aa429547c195d8cb97dc165274a49bfc061dd1a5c6a655624880a5b4d0802a526ca4b6de92a0798f13d71354ea4b6f29c7bd95652b3b122c4fc0fae08f2ed322c73ed652d29ce44946e8f4bfc713cb971b1bd26d4b9194c6b0b794b38343a8ec945c4070ba82a3cafd3f3f9e1c36efb469b77483ba56711ee4898c5416e32d76d017601b2ab287ef0bfe470fda611206b6755c1dee3191330801c561c8c737652edb9d9a8a5494920822df74f8e19d73d9da2d3fda065615deb1beaf31878a8dac5d4850572d49173887a8c204a6438eeabd93d9b834903c46f88e10d2e401c3ba59e8d27da75c57d2a6d1651b2412b1e1e18f9e53b21f25d64b11000a0abf7544f4b78e388ab4cb5a23bb7d413701173bfe589890c86d90b90b504a39006d6b625749d068848bbd140d223351e59bf6eb75448d205f481cff90f8608cb284242da41d86950b73ebbe388115284adc710845fbe156ba85fcf1cc79f1264954487202f48d4a520f9f8e2295ce94db6e873e884349d84259fea8cb4fb701ed4a0a1add581cbc00f99bfae2029d2d315b4b0fa8498aab292851ef23cd2705bc4788e2e030e25ad4d34a254e0172302f4aa6c77d0975120a9b1c814dcb6af31e18d9c5730c0085bb8be598027ac4e8506b288d11112585d94991314529493bee3cb6c1a22655dc42d265e5d53407b85c0363e041c08b14175a75499d1d89113501a93cc6aebe23ae167f26a10b52d1188687bae2577047f7d317ce4e206005bc7d2ff00aae73370e64d0802965eaf335db749cc99d58625b688cba64890b05219614576f32705d4c4badc74974dd411723cfae11cbd0e2c38b6698650fa869d4001a81f1b75c3b6753285347576645b4820dafd3181e219114c008db5eca08daf6021eeb5db2ebaa528badf66d1dc1271cb6f82a0e2567b326d7b7d70e02504840490b000d2a3704613931f5b7a11717dc0b6c3cbd31961a3aae923452ee20873f66bee1dd409f0c4c6556f438a56c54a41b903cc6071414cb69515dc7255872f2c10e57793dbad045c041208f518dbf05923fe6ee4d0dd2af90d3e5d3534a1b6114485e1d823ff0088c3475a0b439db824bcb215d3bbe1f961d46514d06136923529a693bf9daff4be3870a654b5a1b5254d2156241e47a8c74733f5d2a90f55e25013d9b85b71b08b58817ba463a75c664ab4a595a6dbad645bf3c39929d4da52362a3649c75d9b4d365b6da4950dca9439e31e680cae2c154acc6ea03d5454d7994c471c942c94f25a8f8605bdbe1bef38a4b8a52547495a473be19e77ae25e925b0b086d2a08173b5baa8dbe387bc3ea226a044b503ecedfba142d7dff9ef8a6cc227e527f65d0c5136083cd90a7086a3b0c253235261df5681b6bc0fbf9852fccf65a44271c6d1be948ee9f026dcfe3822cc31d954c5344add94a591d9a4f7424f2b5be3f2c0c1cae9a4a83912aaea5e3b84a53629f2b83be2f6033159d6325df10e39afd150ca7643fa3c81a3cffa53d84d0802ce6637e4a4b70996d48bad5ad5b587a9c70b9d5f4bda22c186a24723c81f1e631cc7a2bcf8538fcf92e159b6a5294557f2df126f658a4d0a94f55732561d62020594a5280d47c073b9f202f8d3c6c68726405a06be8554c832c4db7ddfb84213ebb5c6dc52243511d2851b691717f01638b332c531e7a851e4d6d68a79746b2d95d8792b7f2b6289cc5c578712438de47a5371d23b89a8cf1db3c7cd0855d29e9e3e98ac2b15aa8d664b926ad3e4cc794775bce157d31bf2f87e23d81a182fd785970e465024bdfaf4e56adafe6dcab42458e668121e49b16504b8b3bf822f817ffc50ca121ebbce3aa36b6a31c903d473c6665b9acdd477b72031e254355f7c663bf87f11c6c023d95c199201b2b522789d949d4a522ae50840d82a33813e5bdb0b2730d2eb92c88b54a4ca4941b362458dcfeeaac49f4c657b809e46d8f890523bc74f300e059fc3d8ad36093ee54accf7b7900ad3f58a455a645ec915652dad212965cd9361e9cf036ed32a94301f04b67fe232be5fdf9e2abcb59e2bb97163d82585c7db547909ed5b57c0f2f51638bc723f17f2b568a61e66889a4c95f77b404ae3ac9f3b5d3f1dbcf133fc2d81b4c0b4e3f1a6b4539aa432be6e4cb68c1aeecb3b21e70582c781feb84ab1942ef2a6515f47676b8655d4f91feb89ecd392a22a3a2651d0dbd0c8d602157b5faa48e60e2068f3570096838b494fdd59b83f3c72f910bf1a434d0802d1a3e878fb2d382464adf331ddee143d27333d05e11e702e35aff6a87059493cae3fbf0c58b4d0d4d89a1890e258500b4949f76fcb156d4e8af2e72dd2f92b7544951e46fd30fa93227d13d9036a0a6d2e1b2c9d8a0fdc57c8efe7891cc89f4e69f708f271db336dba28e9b4bb019790f3a97d0cab49b5c2bd4e1cb7293274a50bbb846a41079f961b4aab45729af4c6e52825690157fb9bdadf90f9607613cefb4b4aa6eb5292af781ba083b75ea3199262db89e3d165790e22d1e23f62e275d8baae7bf2187cceea24df7b00474c0f351fb549b1576a7df513cfcb0f227b5b6f069e787649dc1d37d4314090d3affced52963214829b4970a57bea36208c4a65c496262d0374f666df31862ea75a2e91b8f0c4ad12cb7cab6f708fa8c743e090b2579791eca84cf20528f8ed698b0b558b486d24dfc74e036666da4d2fda23466dc71c0ea94becb70adf7b9e573e589ecd14c72ad4010a2bc5890b693a5cb9d200b1b1b78f2c569061d6a82eae0ab2e479cedee1f214b0a1f0205be18eaa0c68250eeb76fd2ebf52b232e6963700c1a3df953d2b8881e5129a63e848b0490e8b8fa610aaf12a3b90171e3b125972c4384949d44f4bf4c70b8b9a64b449a653a9ccea2147b34a48fcce04ea517ff380d4d9497ca1475ad09005c73000f95f155b16035ee2f02fe849ff000b47021cec970f2bf50005f301556752b90b794faf7691f77483cbe67e4d080298b8597ff56b2fc6a7b3632b47ed0fe12772715f64c6239a8aaaa228086c94b401d811d4dfc3062e38cbeda65482a5bab510db7f8d5e36f018c8cb99a1c463e87ecba991b2b9ad8f20dd735ea99b4c48ed4be0b4e38e9365137bdf0a762853aa4ba7b4524d96a3d55e00741855d43d19c6131d67da57b2ac361e5f9e241dfd1b40caf50add4d6a530c36a5d946da95d123cc9b01eb8ab8b82667f48e7bda19f25b0b7a8a16cd99c29792e297e681267287ec2224dbfd44f41fd8c673ceb9ceb19be71915894b5328512cc649d2d33fc29f1f13cf0cb31d66557ab0f54272ca9c715aadd1291c80f202d6c47c38b26a339a8d0a3b92253cad2d32da752964f8018ecb0f15b8ece91cae7f226333ba8f09ad945371bfc396385124ee71633b906350d4d2f3bd7a24216bae9f055dbcc1fba45b424fa9db0cabb53c844476e8f972a690c2bf68e3f3aca7c7ef000e9f81c5c556d02eae9b7adb1d0bf427e5830a9d6f28d412de8ca6f538a45898750577bd42d2adff00ae1310727546e21d4aa9487feea6734990d7a15b7650f5d070495942ecc87e3af5c7796dab95d048be0a6859868cb023e6ca1b72e3a95fed909423c86fcf61a17f14dfcf11953a04aa7a5b536f43a8c6755a1b760ba1db9e76b7bc93e440c42b89095949050a1cc11c8e1b9495dadf0620e64a41aa70ff00323150655b88f29becdc41fc2a23911b7318ab73265cabe5aa81854d08025c82f447c0b8d63baa1e2950d88f4388ea4d527d1a6a25d2e5bd16427938d28a4e2eac97c568b9a128cb9c4e8f1e74090425a98b4e9534be435116b7f10dc75db00eb6ed204841dc36e2956b24bc98f733e8eb37721bcad85f9941fba7e98d1305ca166aa1a2b344b3919e3fb4481df695d5b50f118a378bdc28919354aa9d2cb92e82b57bc4dd71c9e415e29e5657cf01f9173955325d50cba6ac2e3bb64c98ab3743c9f03e07c08dc6337c4705b9b11034eec558c7c8742eea69d2d2e69284c7d685a8a12ad4855bc3fbfa61a4984dad25ab26ce0d69b0f1fefe9890a355a157690c562924b91240fda32a3ba1439a4f8287f4c31a9e969bd685280beb69606c2fcd27c31c116c8c798dda23b2e960c8269d7a4ad2281d8a24345c059929b9b8f7543fadbe9884a89a65124a0355142813df4b0755bd40daf846bb527aa2944344a9317426fa032549749fde1be23a07e8d8f666b5164a54937b84f30474e5e58e931b05afc712ca493e839fd562e4f8bccd9cc4ca03d4f08b29b9b69b1d03b28b3e42c77406d9f7beb822a756a9f5768a61bda5e4fbcc3a34b88f50701d4ecd2d26326140a4bd20a0ecb5dc0f5db7c7cd501eadcb12e746547569b365034007a1b9dcfd719b3e062efcd059f5bbfd153764cce7fc243bed5faab32110a652a3b288dc5f129462532941205b41fcc605b2ad2c51a088cb98ec82361af9277e982ca381ed4afe034d0802f98c745e158d1451011b83bea14123dcedb8515031e3193ecef05aca94d0483ab6d3e98988ec3282de9412a479fbde2afefc70da9ac06a9ed0500b05a4dc5b602dcb1ebf3d98ad38a74861b6b6bab972e9868a06b2dc792a601cf21a3685b36d45c614435705a0b78a41f794390f9dbe58a7d501e529b717acbf24ef6e7bf327ebf3c1ce677fdbaa6c4a5a9d3095add4a6f6040db7b78ff3c455210e55aa4853f6645b4d80f753cb6c6131a606927924aedfc399e4437f94454868354d8f19a1adc70042004db5247326de7d7cb124ec594b5b66227b55a4d8a9276bfe1187196e9485469d224b861c66068d6e2ac1081b9dcf216c557c45e3394a5548c869f658cd92833ca7bebf1ecc11ddbefde3bfa62e61f86bb20798ed5ac6ccf106c7210cd956ecc9941cb14f4ab3555a2c37dc174216aba879848ba8f5ded8a138cdc418b982241a2d0e417a9b1aee3ae842901d70dc0d8ef603ea4e2b47dc975398a7663ef4a94e6e54b515ad47d4dcf8e18a884e92851b1f118e8a0c58e1ae91b58b24cf93e72b800b8b0940249d8003727066d4e4e4d2b6689243959798eca5cc458fb3957bcd347c7902bebb81b731461c430cb8f924c8be96adf77c55f95bd7075c09c9433966fbcc4934c8403f27f7ff000a3e26f7f2071746b6abbca93e1d708eb59c02274e2aa7d2d67576cf26ea747ee8e6af5e5831ceb4acabc2b729b029f95d35eac54412db4d0802935c25208206c9dc5c922c00f8e2e2cfb9be959032fb73e7b2e29a2a0c30c47481a95624247448001f9628ecff00c47a5670811d9cc795ab14a434b0ec4a8b2a05c68f880523503b1b03d39e001738ed4564f29084bcbd98ab51f2d67cc9c8caf5097fecb3630533651f76e923913b7322fe18a973965f9195b33d468d2cea7623ca6f581ef8fbaaf88b62ff00c87c3367375520668a86725e6380cad3a52b42fb4253b86d654494d8dae3fef8aebed2ceb6f716ea49608d486590e11f8c207fd3e5836d0d04e0ed55ed3cfb0f21d6564389374ad074a81f51be252af9825d6c309ac94ca7190109925203c13f84abef01bdb55f9f3c430438a0a5241ba7755872c3a8d1572d9714d594e362e503deb0eb6ea3c7c3d3048976ba5adc61c9105424b28dd413efa078a91cede62e3cf11dcba6178efb8c3c975970b6f2374ad06c463c90b53cf2dc5848528dc84a74827d06c309257970478b02168cb39c5e4bf49793d93121fef763716ecd7e283b0df97a62378e1c2dfd5370d6e820bb4290bef360dfd98ab90bf541e87a72c533cb179f07f89b1d707f53f3bafda68b291eced3ef1bf620eda144fddf03d3d30045246c6d08f08b3a1ca75df679cb51a34c210f8e7d9ab90700f2defe44e349b903b39adba9d0e47793b8e6971246c47aed8ca79ff2b3d94b35cda3bea2b420f691dde8eb6add2a1f0d8f98c682fb3be66199b27b945a92b5cda54d08021084289ef164fbbf2b11f018c2f16f081943cd8cd3ebf2ade3e5ba2d1e1174782dc35f62c5d292aed10927dd5786272530d49652a5a06a3bdedc8e1a4d60b12c216bd5648d2a037e7d70e10a0a6bb35281d5ba48fefd71c6b0cadeb864f980fd959907553da92ec0b08b856948dc91b5b08006c41beb49e67a8fece1ceb523502a2a03a1c26f10a68ac22ca27c71926dc0da763b7b4e6280e36494dafb83899a182996b1bfb9fcc6216095d88b7740c4dd147f8955bf01fcc63d1bc0e8e1b7a34167647ce547a64371e9b1d4e7fc34d80e676c5679c264bac4f3010f2427701906debea7c7d7166aa237229d1ef7d4594d8f86d8690a831613c8948405be2fadc56e77c597e339fa055dc2c98b1ade45b90098ef314f87157fb4521b293b69d8ab9fc37fa61e50a92513c200056a290916fafcff002c4cd7197a42b4c4602d4a52d208e7fdff00d7031c46aeaf2964e9735bbb155927d8e1dcee091df73e093b79918c38217644a19d815acecfa809f5409c78cf86a12579628ce94d2a1ab44b7107fda5e1b14ff08fa9c08e4ee1e4acd3946bb5b832d953b4f46a44341bbabb6e6e3a0b5ede2461be4fcbb5a34f39be1d319a9d3693242df65c72e4e9ef12520df48ea7f3df0b66ecf31ff59ddac64344ba1ae746d135b4ac04eb57bfa2dd0d87c795b1d6b581a0342e6c924a739e6a343aa70ff284c82dc689558bda4299158b254a09d24396f34d0802f13d4e2b490a1da2d4d254945ee90a3736e97c7cb24a8a89249def8e08bf51e983013d505f1559b09b6fd71aefeccb45149e1b89cf2036e545e53e54adaed8eea7e1b1f9e322a8070b694f33618b5f8cf9cdf021e4aa2cc29a3d223b715f5366ddbbc84d95723a03d3c6f82e503c58a577f152b39073050e4d0abf98a032eead485b6eeb5b0e8040558785cdc79e02a8dc47cd828eacbf4ea652b382d96bb2625c374af5206c0b8d73d85ba0e5d7153d238419d2b14a6aa30a943d95e4071aed1e4214e248b82013d702b53a6d6b2a559b6ea31e5d32a0dd9c46aba143c1408fe5870028e96a5fb3be4aace51a655ea39842a1ae614a9308abfcb4a6e4ad43a137b5bc06331675acaf30e6eab54df51bca90b5a40e80abba3e56c5fb46e274eadf00b31cca82c7e97829108bc058b9da694a57eb626fe97c672a34272a5548f09b763b2e48586d0e3ee766807a5d47dddedb9f1c3b4240f75db2e3b125852416642069525636578820f8f50713ff00a2d52a9cacc395cb8d3904854c8c855dc8aae8e27a968f8f43b1dac7052eb1fae0fbb40cd6c7e8ecf71006e34a72c813ec3665de9ac8b6973ef6c0f8e03287549b94b3337362a571aa30dc536f30f0d9639290b07a117041c49569ed483f4b8b9aa88fd5e88d866ab0d1da5469e81b147590d0fc1f891f76f71b602d16512951dfc716ae7aa57e837e8fc41c8f762953541d4a502e21c8fbcc4d0802a87e1e76bec45c61a662cb1133565a7737e4f63b35347ff36a5362e622ff00e2363996cee7caf8048140d4ba739567571e3906684dda66dbbd6e6907f15b90eb6b73e7196293859971c65d43adaca1e6c8521493b82391189cad45353a57eb1b5203cfbd214dcf68234965d3ba57b7dd5ef6f0208f0c245749c55b301ae654a7c6a8294ba95255d9c77d46e5c8eadf41f34aad6f227133c12cca32df11a03af2f4439bfe19fbf2017c8fc15638aec1df1da5450b4a813a9241bdf96048bd254b70d667b30de9f2ea321a6598d70b71c5694a00c53f9838e88612eb397e0f6ae5fbb2a41b240f240dfe67152661cd159cd935835075c94e9d2d32c206d7e42c073276dcee7177e44e014630da959cdf79d7d60130985684b7e4a50dc9f4b63020f02858f334df1389ff007dd4f2e512d0c6aab9ee2fe7379d2e0abe903eea184691f4c1cf0e78ca9993d8a6e7165a01c584a27b6346951e5da2795b9ee3962ca3c11c8896d4914976e77bfb5bb71ff36289e3470c86467989d4e7d7229325cd0039fe632bdce927ef0201b1f2c691c1c57e8c63f0ab891c3bad54ca5a284ad82952143baa49b83894a27fb5aff80fe6319cbeccd9c1d7952b2d4e7cad2da3b786147702fde48f983f3c68ca291ed6bfe03f98c48d89b100c68a0809276535a52d6b80c25c6ca7f649b5faed8596d5ee09363bd874c7d0d36a6c6df6ec90adfd3153e7ae35d2288fbb4d08020e8ad1aacf6d5a14b0ad2c208fde1babe1f3c34719634026d11f88e95853e0ada25c68924381d02f6e963fdf9633dfda4ea8ed4337d3a03575b71a1a5494277bb8e28df6f1d86236adc67ced396bece54686c93b34c474edf155ce03a7d72a798731459b5094db53d4b6db124a7425163dd51b0e9e9d315e2c430cc5edf948fd558ea25a01e53aadc9a74382cb3446ab549a996c313e3bef7ecdc16ef1d8022e7ee904605e34753ee8420124f80e83051c4535c56729ad665aa3754a846b32b94d2c292a03958d86df0c2d92a2a1540ce338b7a97169e94215f80add4a4fd2e3e38bc39a437a49641c9dfad7539a5f93ec747a6b064ce9405f4362e6c91d546c40c09cc534a9af184d2da8e56a536dad5a94947404ed736b5f172e470dd37ecf19ce7a6c244d98d430473d20a4dbeaac52e94ddc79441b0c184c0d95ca829b90828202d2411e479e39756a5beb538a2b756a2a513d49e7832e17b0d3fc52a03129943ccb9292dadb5a6e9524a48208c11719f85d2b29555e9d4b65c7a84f28ad0b48bf617fbaaf0b743870989a2b4b70af31d3b3164ca4ae24a8cb92d456db90c366c5a5a52014e93b8c575f6b48d4c732753a438eb09aab32c2594dc17168524ea1e361649c65a43b263b816cadc6d5c82d06df2231d34c4ca94b4a007a4485ec9162b52bf9e1fa36a2228a710ead2a351e65290e110a5b8dbaea07de522fa7ff0091fa61ba43264d080223da9a74bb74942c2bba91bdc116defb6f7e98baf87bf67baa558225e6875ca5c456e96001db91e60ec9f8e09ea5f666635134accb21a4ef64c88e17f5491f961fa8709750e155d12ab4fcdf929302ad2998599e8c9d74e9afaf4fb5b23ffe752cfdf4eda493d2d8fab6139db262f316a4feb0d20218a9a00ef49609d2dc8f3503dd51ebb1c16d43ecdd991b20c6aad324abaea2b47ff53819a8f0673fd1fb431a98ebc8582da950dd0ad483cc100dec76d88e98707d0a560a21fb3a5462d59dac647ad9ed29956614b690a3eeba2d7d3e06db8f34e21f2cc89fc1de2f9835024c15ac469248b21e8eb3dd72de5b1f81180c61bcc9936aedcd4c19b4ea8c65ea6dc5b046823aee2d85738e7cade72f65398dd8f29d8e0a50ea584b6e693cd24a6d71c8ef8770b292b0bed0dc31465c98331d01b1fa1662ff0068da3dd8ee2b716fdc574f03b7862accaf352c5417164ba96a05411ec9254a4ea4a12a3b2edfba6ca1e983d81c67a9af2afead660a744aad1cc6f65b28969ed20774ea1717164ef6e98aa1c2359d8efcf0005271c6d3dcc14a9544ac4ca64f4689515c28581c8f811e44588f23860855f05d9ba6ab3050a8d5a580a98c3029d357a85d4a47f94a239eedd85fc5380f4e1d204dab67ecf1424d6b88b1a4c84a3d9694d194ad4362a06c9e7e6a07e18d475ace596e8c8d553ae4062df74bc14aff00da9b9fa630736b296d5675680bd84d0802a41201f5c5ab95381b5fad448f39f994d894f780525d43a1e5107c9171f350c0385a62168acb7c43cb19aaa2ec1a254d2fca420aca14da91a923991a80bdb11bc66a3b35ae1d56997477a3b2a94daadc968dc7f31f1c33e1af0be9391e5ae6b12a4cca8a9b2d29d72c94849b5ec91e839e1ef1a2b91e87902acb907bd3185446500fbcb58b7d05cfc3118e7485663e0a3ce47e27d014c83fb45a90ab75494aaffdf9636a51143da55fc07f318c91f676a4aa7711512ca4f614f616e2956d8295dd48f8dcfcb1ade8e926528a522da0f5f31852ba9c9c854371f338cf8fec195e98f9663b90db725a906ca55f922fd058027c6f8a4e974c7aa7558d029cd15ca7dc0d369f127960d78cc8718e244f2e02a4a9a6549fe12d2797d7035956a1fa173751ea4e1b222cb6dd5dcfdd0adfe97c101414a34acb7b801980450e2275354f69bf6254a041f0bdad8adf35651ace5a9023d7a0bcc6b49ecd7b290bf450d8e2fce337146ad9666c3879762b6a69e643fedaf36a536b0770947206c2d737eb6c54d5de29e6bcd1497a9b3e253a64558dc221a8a9047250215b11e3870ebe530b55b3c84b6121b64a3424256abdf51f1f8e08b2866166910ebf4f9a93ec35882a8eb504dca1d49d6dabd350b1fe2c0ebda9e202edaadcc61000a4e955883b607ba93908a32f66211b2957f2fbee111e729990cedb21d42b7f9a6ff2181569760e8b12b57bb6c7853a4d08025764efb5f972c26a49bdc83eb6c103a4aa94b502b9328799215662259f6a8ae768da5e4dd17b5b71b62d24fda0b33f60a4c8814477c52b6d7df1e9aad8a5420ad56482707194385b98b37c254ba3b7156ca55a0a9c9094904795efe1d30605ed46ea3ca9739eb294e965fad640a738e93752a14c71804ff0f2f1c58593f8c7c3da0694c0ca6fd296aee97194a1c51ff55f562a68dc2bae4fa8c881489747a8cf6012e31167214b16363cec363816ccb96eaf96e5a62d721bd11f3ba52e751e20f23f0c1f2a33b5b163f1bf21bad25c5d61c609fb8e4672e3e40e3d778db905bb95570abaf763387f96339f09b859ff0088147a8cd45723d3fd89cd2e36eb4556494dc2af7161cfe5898a1f07f2fd72a8aa753b8874b7e7a49019447512ab7e125402be17e581e96a657548e3c6426d0149a9c970dbdd4c473f98c44c9fb45e4e6ae5962a920df925909fcc8c665cfb418f963344da2c79e8a87b22fb371f437a06a03bc9b1bf2371cfa61de5fe1ee6acc54e13e8d44932a1951476a9d2012398dc8e5b61fa02402bfe57da472d84a928a2d4a424fdd77b303f3380fa9f1ab27d45f509fc3c88fb479a94a6f51ff97f9e00d3c1dcf2a5a42a82ea0ab6016f349b9f8ab08e5ea353e453b30d26ad096c57a332e3b11eed140871add6ca93c8dd2156f0230830764f41279eab9936ab1db5659cb2fd1e5a9ed6ead5254b468b7ba94dec37f2e980b51f4d0802da5c136bed718f95a549373a54000001cf1eb6a55c03de1d0619185c04ea55bae3e29db6bf9e3b4049594d8dfa6fc8e3e585037b77af638492e50b2058f2e5be0832b66dae656794e50ea2f45d7ef363bc85faa4dc7d30ce244554a338184032a3b65c291cdd6d3b93ea91bf981e588b40ba8026c0f5c24bdd5a5238e19cdd8a1944a88caad62f37193a8fcee3e9812a9d7f31e6f971635467cca9be57a58656abd94adb61cb13bc2be1f319ea5cb8ee55c417a300e16fb1d6a71076ba4dc0d8dafeb8d0d92386397f27c94ca8a87a5cdd36f689041d3fc200db00e70621d0292e16651fd4ccb622ad4daa7c83daca700e6ab6c91e405edf1c591969e529e373feecee3aee30c1f4b22cbd22c3a5b0fe85a0ca568040ecef61ea314cbba8da5d9517f687cb4eda9598a3b654caa3a23c923eea87ba4fadc8f8628f712142c3dee871b9a4c38953a30813994bd19e6421c4286ca16ffb633e672e07d5a2cc79dcbae37362a957434b7021c4791becac5a089aeec508e52e24e62caf4ff61614c4ea62410224c6c38845f9dba81cf6bdb0eab3c58cd35284ec288e45a3c0713a54c53d84b57079efcfeb846370a73b4890195515c6c03badc710123eb819ce797e4e56ae2e9335f6dd9486d2b7bb337082adf4dfd2df3c49a013e8943c5b2e3dd9b7a97ded29d2375780c376db2a717703ba0ed8b038574b1327562a0a48526934a9335371fef34e944d08027c8aaff0c03328ddc039945f1101b461de8bd850df972fd9e1b0e48796ab21b6d254a57a0182a87c3dcdd2d650de5ea9dff7982903d49db135c13425bcfd4f438f3918cc43ec36fb66ca415b6b01493e37e58d3315aaf51a451298d37fa5a9c96bb3995290fa50f02391d1f7ba5f7bef8323a5039f45638cd793eb1952a898b5960c579d407529d415749f31b78e0da89c1fccb56a2b556cbb53a5498ee22e95b121695dc0dd27ba2ca1d462e2ce5c316eb99764418f569332a71df54861739d4b85bd42e5ab800849d88bf2c52b4d6f3e70f2aaea29d1ea115c51efa12d76ad3be6458a55eb891a6c690f55a11c9b946bb5bcd6cd1a9a170aab671454fea6b40483aae40bf88f8e13cc997ea28ab49a5b72c5625436d6f3eb8ae29c6db4a05d7b90397538b64cfe2bf1007b1f62b810d63b375f31d318683ceeab6a23c860de9d9220e44ca53e9d120caa9caaac290d4da92027f65fb3569045ee9413e17e5738575a2849b59fb8662ab53a6e67cbd440b5caa8c24ba1941ef3bd93814a40f54951b75d36c5c54ba2d53302f25ad394dccbad65d4a1e973e4361b5bc5b1729401ba81b13bf8fce9de06cf34ce2ce5e709d9c7cc73bffc44a91f9918d739bf36516244aa535755868a9a61baa118ba90e5f41b0b78f2db9e11241d21fa2c315a96e546b53e63cabade796e2b7fc4a27047597b32c4caf979c72a6f1a43a8717111196a4a595055964d080215603bf7b1e679e0413771e5274a94e2cd8002e6e7161f0ab8952f21c97a2cb8c27515f56a7a2ac0ba1636d48bf23cae3adb06e3d930e1279ba3e79969a2d72b299f247b134a872db46ad284dca6e53c9439efbf538b6f2370f1dcbf4ca966fcd9359796aa73cf84824a8a9c6cdd4b2a037dc8b789c4e43e3d64931038a766b0bd3fe47b3124795c6d8a9f8a5c54a8f1094dd168911e8d4a5b89b3406a764afa5c0e9e007c711824e9172502650cae7312a73afd462d2e0434254f4b9415d9a4a95a529ee83b937f91c5a0df00d2c086e55738d2a3b129696d8521b27b652b90415100923962cee1c651a8e4dc80d42851a9f26b72dd4c99916639a4141b02816bee05b722d72706f59551de954ea3d5a965f4481a99bc52e32d293c81501649f0e5cb005c9ecac4d9be80e65acc955a3beb0f2e13bd9f680690bea156f3047cf10ce256a68381b50403a54a03bbabfaff004c18f1924b72789b9994d9053ed7d9dc7eea424fe4712b128ed35f67eaad565274b922b4ca22923ded09503f452be5892f48ae820aa2497299261d5994057b2c84eb491749ea01f220287c3129c47a52699991e762d3cc0a64d025406caf582cab9107c39edd2f84e932194e43aec77903b554a8ab656472235dc7c89f962573f4f765655c8f19f6ac18a6b850e9e6e02f2c01f0091f3c32495e0c54d54de20d1969274bcefb2afcd2b1a47d6df2c6bd6d0e24a92e104d080253f77cb18ab87a871cce5466d9bf68a98c84d875d631b6dc1ad4a36b006f7c472042534536bb90ab69230fe80344a5a6e2da09fa8c2170a3b1c3fa327fc5aff80fe6310060074912bd8a2d0983ab70848fa617beb1b0f9e1bc655a2300dedd9a798f2c288eedcfdd3bdf1202109b052a9041b6314710e69aa677aeccd57ed263801fdd49d23e80636c0f5f3c61dcd705da6664a9c27c1ed5a90b49bf928fe7fcf06368e3e558ff0067c8ed546979e69e143db24d2b43493cd49ef5fea51f3c534d1ec9fd2e0ef27ba411cb059c27ccdfaa39fe9b51717a226bec6413ff00095b1f96c7e187fc6acb68cbb9f2598813fa32a004a8ab49ba5495ee6c7c8dc7c461c23e0afa86c38ac969af52d7a6ad97a7053a81ccb2b50285fc15a927d71a1327e669959a64ecd5166aea74e75a045158613dbc57936d4da5448d439f3e7707198b87f989bcbf5eed27345fa64a6d5166b039b8ca858dbcc6c4798c15536b750e136730ec177dbf2fce09753a7dd9718fbab4f82c6e3d458e0cb6c2170b5a45d9145a455589e63b91a7d6d486d4b0d2fbe427bbda5b649036b9f4c34872aa70e9a95c191fad5ae7a92e38da9a64c76efba76365141dadb1c345e70a6c8a444ce30ab928d0109ec9f86cc70e6a528dbbe00d49527d790c3ea64155226414e52a6d391419aa54898438a42c2962e1681b8df6b8db11f081213e5542b226879da865a4d1e5f6a641095b4d080252d948bea3fb877b8bdc5b08bcfe59865dae4779c71acc611195219ed1d6d64a4a52a205c236b8bed87aed5e465a65a6b354c350f6f9bd84776343290d255eea5cb122c396ac235247ea9d4a7e629b5698687d925bfd18c470b4b4a2400a4048bee6f7fe2c24cb11adc7e8d5e4b8d2b4498722e95782d0ae7f318b8dce1ae67ab658a94dabc089186a76aee5494e25d7a4ddbba5b401b849dc9bf538ad38a50bd873ed71a00f66b94b7dbb8b771cefa763bf250c68cab715e9342c9b41a5aa148a8ae4d21af6b2c280f666d4d845d57e6796db731be2724f64dc959528f33d82b5066dafecefa1ed3fc2a07f9634ed4321e52cf99c6a8ec8aaad732446665c6661c711c36d285f515588749dae7a632bbe121d568274dce9f4c5a3c3ee33d6727d00d2131234e8e80a31d4f1505324f416e69bef6f338675f64cd348be8dc14cbb58add4e0d3f33cb5b94d73b390daa0e9524f9126c473dfcb07f9332753f2fb9598196297261d72284866af568fdab6e83d5b22c00e62c37f1c0ad338a1033ac2a3b150cccf655ab46752ec9521a019936e81649b7a1db7eb8b332fd3171abb35ca34a4cfcbd55429d7d6b9ea75c6dd3ff0c0040491e63cb11127ba3b5215083023c66f31e62ece3d562452d3b3e315fec92459561bdc5c922e0db1e4e952e8f478758fd3acbf4685114ecb53ed02b94026e9585a4800f2e9637c34a14aa750e8d53a665a79cac44d08028a5bba5c88fcd1da35a8ee92b5f2005ed7f0b5f14471f33eb1549cba4502a128c25a5027321692c6b4f24a2d7e5d4836270c059480b552c975fac55df7acb724cc7ca801cd4a5abf3248c5c5c745b395f2364fc871ca7da63b5edd348e8e285bf3d7f0b606783102042972f38661ba6914401c6d24ff009f279b6da7c4ec4fc301f9bf304ccd398e7d66a0abbf29c2a09bec84fdd48f202c31223e545adf598c966ffb24ab5916f78dbfeff3c19f12935681fa0689578edb29a7404060246eb4b9df2a27af7afe96c79c26ca8f666ccc871c4a452a9a3dae6bceecda509df493e645bd2f880ccb5a9f982b0f4faa4a5c990a3a42d479247203c874c3f291a45fc07a77e91e24d34a924a23154855ba684923eba71af0916dff002c519f662cbfd953ea15e792753caf6666fe03751f9948f862f250f2c4521da0499012bbf8f41890a41ff14ab0fb87f3188f3b0f1c3ca328894a1fb87a798c4692558b188ca77d909e9e58eae54920917036046136140b0900dca523e38eb51b8501b5b7181624ed709468dd36bee39f9e33f7da3727bad4f466586d12c3c94b724a47bab1b051f516f962ff00364a86fddc2753811aa94f91066b49763484143883d41fec7cb12b4d2606b6b05be8bf7873bf2c4d2f344897955ba0d4d1ed3162afb586e93fb48c4fbc907aa0f81e446d821e286419b936b0a49497a98f1263be06c47e13e04600169215749b6d4d0802e1892bba96ed24e1093dd3e63c713712be1da31a5d46ef450bed5904d94c38762a41f03b5d2763e477c4158157785b6e9841cecf4f3255d41c134d24768ab28e6dab64cac7b6d1248495775d6563532f8f050fe7cc62fcca3c56ca35cac3152ad1728b987b0f6525c71463ad37bec47779f2b8071948950b80481cec0e3a2a584f7966dd124df04e01c80adb541ad1a30659a85795995552987b1723368223a0fe2d2a3dc1e3d312adb350a05027b91ea06bb2cb8a718135f6d94827fddebe400df9e30725c5a07754503c8e3c54976d6ed5453e1736c0f9482c2b23ed0ae2a467e448744644a7e130e3e88cf8790972c414858e76db167655e10c7cdd96f2f5613597e2c7954e65a9b1d0deaed422db055c5afa53d3622f8cc8a5952bf2be35fe45ce2c656e19e4da7478126ab5ba846bc6811ac14b1a8f7893b24799f0c13ada3490dace9c69a0b39778935aa7c46c351438971940e410b48501f0b91f0c035b717e7cf17271f65c5cd2aa466c8119c8ca7d2e4198cac82a69e68fba48e7dd3cfc060532bc4a7cca11830202eaf9b6a8efb34769493a21b42c7b41e2b3bee764817c2f74246d03816e470f224d971d47d9a4beca8f3ecdc29fcb16467ac9d47a2b948ca5416dcaae715a93edcfb6e12da167fdca53cb6ea4f2b7c94e21f0d69b936994c86babbd37364c28bd3d86c142751f1e7cec0789c35828802156aa9925c539da497c973f4d0802cc2564ebf5f1c24122dcf6f4c58d9f784f3b24e598b56aa5561176414a443485769acee520f23a7a9f2c56e906e2fd7c70ad48d4f173a5bf01884b7dc30d851536c93dd4957336f13fcb04192328bf98e4a9e7de6e9f458c7fc6545f212db23c05fde59e891bee31c18d49a04c8a9aa45915393d921e7a3073b06db2a1a820aac54ad8a6f6b73230d330e62995b296d69662c06bfc883153a18647927a9f151b93e381e53fb222cdd9a623119ecbb925c7e365a06eb52ae9726aed62b73a907a0e400e581fca74099992bd129701b2b79e5004db6427aa8f90c239728551cc75466052632e44970d8048d923c49e8078e35af0b38790f23d38ad6b126acfa6cfbf6d923f0a3cb95fc6d844d0427489f2f51a365fa243a6421666336100dbde3d547d4dcfc70fbe38ed4ae86fe18abeb326bf9b2bd51a6d06488b1201d2a585946b572dc8df73cbd31012ab4f30885d59f45659df6c38a42cfb6ac023641fcc62b5e1fe61a89ab4acbf9815aa7300a9b7147736e6927aed637c5994940f6c51df741fcc610450ce2567537eff0044ab49d2d2146c3ba06dd70aaac6dd41c26d6eda072213cb1da492a283d395b0e029526849dd1f781d89eb851926dbdee30c2bf5883428266d45d0d360e804024a89e800f8e0159e2d531551d0b852111af60edc157ae9ff00ae15aaf36545110d7b80255815aa642ad535d81538e99115d165215d3cc781f3c6694d0802e20f06ead4679e934369ca853b752740bb8d8f0501e1e231a7204d8f5088dc986ea5d61c4ea4a93d70e072befe5836ba94cd76ac6c2fcff931d6c294979b28524d94950b11866f7662fe36dac317afda8ebd15eac42a1c36d9eda3a7b794e2503515abdd493e42e7e38a256803b88f788ba89c4a3e2da96f4923de20db9240e5cad852044933e635129d1dd9329e569436da4a94a3e40626b27655a8e6daca29d4b424048d6f3ee1d2db0d8e6b5a8f20317465e9d0b2aa5ccbbc24807306667469975a5b7fb26bc4249d801bdb7b7ae1c9a4c7486a93c134d32022a5c46af43a04555888e5414f2873b6fb03cf617c48c2570ad978c7cb3936bd9aa437b76b65e859f9ff00f5c58995f83b1dd969acf10263b982b6e596a438b3d8b4ae7602fdeb7c07962d24b712934f5f60d331a23282b296d21094802e761e57c465c4a03be160aceb218939a67b91e8c288df6ba44117fd8690014ef6dee0dfd71a9689c3f7aa997b2555e0d6e6d1aa70e9488c571d2956a6d42e47786c7bc77f318cb92d6731e73ed145579f30ac802e4768e136fa8c6adace61af3d9fd3923273b0a9ed52a1a1d95326361cee0d200420daf6053f33b8b60dfc2434a2f8afc39a7c3e0ecb83446965701dfd2256e2b538ea80b38a27c48bfcb19e78799c5ec989abbf4e8c955665b023459245cc6b9ef280f122c06355644add42bd51ccd43acca85578d08219f6e88d766d4d0802bbda27bcd9009171bf238caf323c9e1c7142d2580e9a6ccd610b170eb57b8e7e29c269ec520adecbb1a0f08320ab35d6d025672aba498cdbdba90542fbdfc39a8f98187dc08c953aad56733f66e2a7e648257103c3727976b6e800d923c37f0c0d64e813b8dbc4d935dae2168a053d42cc13dd0906e864799dca8faf962c2fb43e7a4e5acbcdd068ca09ab5411d9e86c6ecb27bbb01c89e43e3803774112a478f99bc66fceeefb0a8ae974c1ecad287baa55cea58f53cbc92301d93e0333f30c54cbbfb0b6aed6491d1940d4bfa023e385f3740fd00b628a5695cb6521d9853f75f50ddbbfee0b0f5270ea330691921d9abba5fab3862b23af628b29d57a15684fc158900a4e144e61a93d5cadcda83e2ce4a74a827f08e413e80587c317764ee00b4f330e6e61aaad4d3cda1d5458cde9235241d2567c2fbd862a4e1e51cd7739d269e91a92ebe9d7b7dc06eafa038dbe2dc811b0d862291dd291b1a51396f2dd232d421168905a8ad7ded22ea5ff00128ee712c4dffa63a1b73e58e40569efdafe588aed09e170ac56156899972a662a85472f44f6d8538f68e37a35693cec40df637b11e38b354a16b8dfcc61bad452ab127d4f4c250cb08940b34557d90f2d55155a7b31e6225335d074347de04ed723a6c2c062d5a36f2d76fc07f318864ba01b13bf4c4bd0cde52cfee1e9e630e0228a11137a5bf72ba477129df7037c2c149b8015b9e4064d080228ee2a56aace66c7692c3aea63b61096d96ee0ac917b9b7337fcb10902b799b28cc1a9525a413de624215a543d0ff2c302b35fe28d6c85a5a681a2516f1fa4afb6a3c504e9d2e3b60799b81fd7e7822a470da8ec659227b3dacf759d6b7ca88ecd4537ee8e5b60038919922668a7d166c5fd948694b6df649b96d47491ea0db6f4c59f9db318a6640449d6049991d0db43f7949173f0049c255c18a592491f44002bf087b81539d760d521aaea658712b41bec0aae08fa03f138b2aab399a652e54e9474b11995bcb3e491738aff008174e723e5f992d6086e53ff00b3bf509005fe77f9639fb44555ca670c66a195695cc75b8c4fee9b93f9609a2d68e082216daca799aaef57731546ad2f77253ca7adcf4dcec3e02c3e18ef2ae5f99986acdc0881295b9753af2cd911da1ef38b3e0073c43955877bdd27a7962d085064c4a551b28d1d5a2af990b6f541e02e50d2b7435e4027be7c49c59ad2bb68832c50559b96acaf9314ec0ca51143f49d54a6ced41cfe9cf4a7901b9c680cb597a9796694d53e8715b8d1d1cc24779c57e251e649f1c2b95683032cd12352a96d06e3b29d8f32b51e6a27a92702bc42e2865fc8f20459eb764d4149d7ecb1802a483c8a892026ff003f2c424971d2624da3fdadcfe1e18af38f598d197787150b2b4c99ff00e09900efdf0751f826ff003c09d1fed1196e54f4333e9f5082cac84f6ea297128bf55006f64d0802f4be2abfb406746f3966d662525f1269908764c29bdc3ab55b52878dcd80f4f3c3b186f696979f671cb6acc1c446673a8061d2c7b4aee9b82be4d8f9effe9c69cccd9132c57aa3fa4eb74d6de9486f429e2eadbd481d15a48047ae2b9e11063874fe5dcab3a2ad55dcc485cd94b26dece84a09423ccf74dc74270313e75727d638b796a64d90f21319e98c36b55f4842d3b27c0141b5b96d8775b8a6f75773157ca99619a4d3623f4f84dd454110588c91a5e27a8d22de1de3e3cf1537daab289974c8599e1b64bb147b3ca291feec9eea8fa1247c462b6ad57a32b87fc3990ca87e91a63f2127c4250f25607d538b469798a75738e35ecab5a92a91409b19c651154068427b24b8950f3b13bf9e040e9368aac28dfb33e7c81032c55e8f524b6c2a9cdb95042d2002eb63df07c5436b791f2c05e5ca8b999336661e22e61405c1a40f6869a59eeae4728ec8f21607fd38af335525dcb79866c06df4bada16b436fb4bd4979bbd810473b8e7834cf79868adf0d32b65ccace1531654da8150b2cc8f76cbff009ade40624af44c0faaaf66c97aa550764c85a9d9721d53ae288f794a3727e77c4b668abfe925c0619496e2d3e2b711a413f87752bfd4a2a3f1c49f0b2237fa5a7d6a5b61c8b4688e4b5a563baa72da5b4fc56a1f2c08ead6e5c91e26c3061103a5797d97e84a935da9565c412d44603083e2b5ee7e401f9e3470680513a6de7808e04d08027504d078774f4ba8289132f2dd07982af747c13a7e6707d7b8eb8a52b5b21da7ba4895002e2e7c463d55c8ef103adce29dcf7c74a7529d97072f43336734a536a7deeeb2950d891637558fa72c528fcdce59e26bb24fe92a8ad292b2194ab43691b9b01b0c491c247cc84ba96ca3dd51f03cc613751a81b100f3dc73c67fe09713df44c672d66278ad95abb38b25c36536ae8dabcba0ea09c68302c4df98d8e19cde92903698ad9d20588d5d6d895a12ff00c42b573d07f31864e800ded7bf9e1dd1bfdad5ccf70fe630ddd3aadb8c7449716744ccb4e07f63a50e94f34107baafe5f0c4e5233a50735d2110eb823b6f389d2f32f1b26fe2927c7e988ee36d6e5c18b120c65e866505976dccdbf973c05d3b87731eca6f56e54b6a3252c97db6940a8a936bdee395fa61b85cfcd298b21ed8db762c85179ef2e8cbf551ec6f264d39fef30f2541408ea924751fd30d6bd5d935b6a9ccb80a5b871d11da6c6fc858abd4d87cb1109716f252dad674837093e3899ca55a8b44a87b5c8a6353d69dd0975564a3ced6e786b59064ea7903e16bbef48a28552cf74ba5a5f88cc8fd191d1ab43aca4a4207aef6f4c0d71c33c3799b24532396bb096dced6f22e5492342ac41f5e98b12a1c6261fa638cb147507dc4147ed1c0509bedcadbfa62a1cf39625b7c3b5660923b36bdb5b69b414eea0a0ababd390f9e0d9f305ad89216c8d644e2e1defb2af72a4d0802d28d7b35d1e94dd819925b64927a13b9f95f174f0dd4ccbfb4454d45612965b9498c850fc27401f04dfe58a2e85545d1abf4fa933bae1be87c007f0aaf8b53f4b46a1f1de0d6db3a29b2e5878384d8065f1ccfa6a3f2c59ad15bc168dcf99899ca994ea1587b495c76c86904fbee1d923e76f80c655ca594559e0d5b33e67ad374da625e519339e4ea2a74efa522e3c47f2c5f7f68fa6bf50e19495c4b94c779b7dc03aa05c5fea319bb2638e56e8937283950661892fa26445485e8695210082851fbba92763e291e3818c7c291e538cf9c3cfd03488b5da4541aab65f9474b731a4146956fb2d26f6e47041c1ccbf0680e319df3aad98947615fe090f83da4877f121037569f4b5ce2c566552783bc326293998b759a84c5ade14eb85b649e82e0808046e6db93b633f666af56f3cd7152a421c92f9d998ec2094328e8942472030e2ca5c23be21f149798788947aee5080fb5329cda99654ea038a7afa803a05fa28d879e20aa3973889992a332b732975432250b3ef96c30149f023ba2d6b7cb12396d9ce54788da698ed172da148b3921f7e3b2fafcd4a51539f0007a6139d457aa8bd799b89d4c709f1912255be49b6100024802ab45a952c844e6741bec03a95f3fe12712197735d5681980561a587e6f64b64aa482bba4a341f3d86c305b1322e4f75690e712e9c951ea603e3ea462073f658a365e6221a4669875d5bea505263b4a47644d080291d4eaf1bfd304404db08ab87d41a767ce1c4ca0c579a6b3653e4392e1217b17da5245db07aee0fa1b1c55329a763b8a8cfb6a69d6494ad0a162950e77c3acb51aa532bb022d10ba9a93ef25b8e5a514a82c9b0b11cb1a3f8d9c23556171ead459308d7fb24a674553a94192b09035a6ff007bc6fcf03749c6c5acf94dcc2fd3f2c5528ad32d86aa0e34e3af58eb21b2484f85ae6ff0c3de1e52915cce948a749536db322423b42b5048d17b9e7e3cbe3886ac52675167bb02ad19d892daf79a7536230be5c7e9d0eb3164565891261215a9c6e3bbd9acdb9595636e987ab09c696f6424240094684a4580bf2b72c740d8efbe04b86f9de1676a5bd221c49311519496d6d3fbf31b10ae4791f960bbc31548a28f45641cdd4cfd41e2c972a509b9b4e32bdb1b6dd4dd2f32a513e971b8f518d65419b4faa50e24da329b301f405b5d9a74803d07223707d0e0178e1927f5bf2b17a1a3ff0035a7853b1ec377536ef37f1d88f318acbece99f174daa0cb35373fc1cb59f67528ff0094f7e1f457e7eb898fc6db41c14e3ed0b908c596acd14760263ac01310d8ff002d77d9cf43717f023cf06bc16cfade67a2a69f3dd48ad424042d2a3bbcd800070789e87d2f8b3e7b0dca8ee3321a4bac3a929536a170a079838caf9f32ccce19e738958a1157b0f685d8c49f76dcd957c3e6309a43c7494b8dad3cb502373cfa61d500ff008a5a57b1083f98c04d0802f654ae45ccb97e155610b34fa2fa49b94286c527d0df049471aa62efb770fe6310f0511da1de23656199a9e9ec34a66b24a99528ec41e6938a49d9156a0bb3a9aea9d616eb5d83adba76d17bed7f1b731d0e34cb89d2a5824edb9be33166c9c6a9986a1315b871d568df9241b0fa5b02e34b07c5e36b2a469a71fd943026e49dcf962ccc9b3f2745c90f8abb0cb95605cb25682a52b6eedadd3d7cf10dc31cba9af662b4a68aa0b292e3a0f23e09f8ff00238b09ee13519c78a99973596caafd9dd2a1cf90247f5c20150c3c599cd3230037ea81f8470933b39b6a2cb4b61b6d6e2d2ea358b721cfadc8c5adc56a21cc5c3facd39b482f763dab22df7d1de1f911f1c3fcb79729b97595b74d64a56b165b8b56a52ade27fa62681db7c1b74b770314e3c74ee4f2bf3b9e46959bf306df2c1b65f7066ea347a1b801ac4342840513fed2d7331ff881b94f8dede1891e3964c772b67196e34c9fd1939464465a47746a37523e07f318add975c8cf36f475adb710a0b42926ca4a86e083f2c5b69ee15be395a3385fc518dfa3ce53cfe7fc39418a896f0b774edd9bde1e015e5bf8e05f893c0fabd25e7676576d555a539df436d8d4eb693b8d87bc3cc601331e70566869a7aab1194d65a4e85cd6469f691fff00a239157ef0b798c1470df8b558c9e13152a13e980dcc47d5eeff00e9af729f4dc796174776a7bb55d54691598a8067d3e7b491fb304d08025e65400b6f61718605cecee9eccb771b8bf3c1cbb9933b66fcc951a9d25fac4878a96ef63154a5762d6ad8048e82e072c4cf0e2b359aee74834bcc8fb7269ed2cb931154650b4b6d2775df58ba7e639e16d2e368428d3f29b2847e95a254252c7bc5b9c1a07fe4383ca356f83c74267655acb64f357b676d6ff993f962d27f26706330d4d2cc2914f44b7ec94330e6a9b0a2795937b5f1f4efb37e537428c4a8d5a328f20548700f9807eb80eb6f74f4a3e8396b82398dc4a603a861e50d99765b8cabe4a3f9633e71362d369f9eab30686929a6c67cb2c82b2bd93b1dcf3def8bd66fd99e31d661e65747825d8636f92f19db3152a451ab7329931252fc47d6cac91cc836bfc79fc709a41e0a675a7997a7d468af5a9ae1872e6a0369936d2a4b6a36eeab9a6fd48dec316e71238133e854135ca5d4dcaa2d96bb59a87400e0b0ba9683d40df63be0427e507b31e4da557b2b25551721454c5aa4440bbcc2d17b2c239a90536dc751895a1f10f3fd6b2dab26d36199badaf65ed4b0a2fa1b3b692bbd86d717239611bec8855209a855dead6510dd52438fcca6c84a63b8e9d4b2cb80dd1a8ef6494022ff0088e20e9cdbeecd8cd47415beb71286d16e6a2761f136c16678a2b393a0374171d69fadbe52fcf2d2b5263817d0d03f8b724fc30ff84f94b34cfa847cc940a5b331aa6c84a92990e0425c50df48b917b6de982ba4cb59e535d51dcbf04d08029798223312a7d9e979965574a48d85be16dba5f126b525a6d4b71612948ba94a360079e0038bdc435e44a3435310bb6a9ce0aec42f76db29b5f55ad7e7c86283127885c5d94a6905e93110774a0062337ebc813f338ae197b2889a5a269dc4fcad3f353740853cbf31c5766db8da6eca97f842efcf149fda1f273596330c7af52569663545c2a2d215a54dbc372a4f91e7e4709669e1854f86946a6667667a264a8b2db5be86dbd28677b820dee45fba761cf02fc5bcf4acf5989996db2b8f063b5d9c76546e77dd4a3e64fd06256b68db5092b45706b3cb79cf2d2532de41ac4301b908bd94b4fdd72de7d7cc608337d0a0e60a24ba7d4c0f667504ebeada87258f4c63ecab3ab39566c0ccb018750d25e2da1d524f66f103bed93e847cf165f1378c4acc1436e9d4169c88d496409cb5fbc09e6da4f878abadf0c633d56d4fd5aa525c08aa2a9199ead951521b9313bcfc775a56a49526c0e93e0458fc31a128a6f316477bb87f318ccfc01ca751915f67313a85314e612b4b6a50ff003c91a6c3c86fbf9634b50d25335cb7e03f98c472d75693b784f6602a4b846d706fbe326bc34ca743809b13b7873c6b751d4920edcc1271956bb19506b7518aea6ce36f2d3f5ffb620256178e3496b4ab9382114232c48925235bcfe906dd123fa938b0fa73c00f071e4af27342e6ecbae24803a937fcadf3c1ea4f5e5830b4b00018ec03d17a078e3e514d0802b0bd89e9618f89dba63c27c4e1d5b5119b72d5333651d74dac31da32a3a90a1ef36afc493d0fe78c9dc4ee1555f273eb92949994b52bb925b4fba0f20b1f74fd31b202b6dbc31e38943ccada79095b4b4e952142e083d08c48c796a62db5f9dca459442ae93cef8e013cb6d43adf1afb3bf042815d0ec8a29fd17355736482a6947cc731f0f96333670cacf65ecd4ba0175997506d686d45824a75ab9277037dc7cf165af0e1a51914af3fb2bc34d0b2a663cd93fbac805b4a88ff0076d8d6bf99b0ff004e28faa6655d51ccc75094fbbfa4aaae8b009ee96cab52813d3922c3cb1a178b09ff00c3de00c2cbcc14a65484b705c5245b51502a755f1b1f9e33ccbc9b3e054f2f42714dae4d6596de650806ed8717a53abf3f8e05a6eca73e8b89396ab5963f572ad29a11d55029930825c1da1095021440e57dad89ba9f12f3e4fadd4108add49a71d757aa3b2ea9294589ba40bec06ff2c14fda51b70e78a1d16981e90e52e90cb290d825434dcdec3c80270f3ecdb498954a467a93398664c8441b36b75214b429497495027704db9e15d0b495a7f673ce92b356487d1567cbd3296ef64e3ee2ae5c6c82a4a944f516209f003033c66cad9573e4d72465caf52066865365b024234c948d8026f6d63a6fe4719ce9d992af4aa2d46934f9ce47a7d42ded4c200fdadb95cdaf6e7c8f5c2ce50cc6ca34ecc05d0b6e54c7a216827dc2da50abdfcf5f4d0802d30c194eb4fd569fc74e6cc81572fb48a8d226b474958051abc8f450f98382697c60cfd578bec2c4a4b2b786952e1c64a1d73fd437f95b0d6abc68cef3a1370d35444788d8094b6cb08d92058024824e1bbf51cff46a3335a7deaac2a7542c1125090da1c07716200e7bf2c173ca56a02a54795439b11dcd9125b66527da0b055a5e7117e649be9b9bee45f0749e38d660418d032d52e9b49a745484b6c84974903c492373d4db0139c73139999fa5ae717dd95162fb2b921e73529eb29452a3f0501f0c5d75dc8d979efb3fb754a453d866a2984d4b76426ea5a949f7c5c936befcb02e15ca4173c6e7ce7ae10d073640688447795dbb60df4055d24fa05247cf167f052ab02afc36a3b94e65a6032dfb3bcd369b04ba8d947e3b2be38c97133bd7e264e732cc49c5aa4b8b5971a4b69ba82b9a4a88bdbcb164fd96b3418198a5e5d90e5a3d4125e641e8ea06ff003483ff00b4601cdf869383b5a3735d1daafe5ba9d25db144b8eb6924f4511b1f81b6319e4bc8d56ccb9a954461b536f30bb4b52fbbd8202aca51077b8f0c6e0b8b0c5239e52723f1a28f9ad002697590214d206c959b2751f8049ff49c046ee42723bab226e4ca339924658119029e967b340d2352556ff33f8afbdf149d1b8052915261559ab30e424af53adb28505aec7902761716f4be2f0ccd9ba8797dd43157a9c788f2d1ad2dad5de526f6b81cf9e19d0f34526bcb7d4d080234d925cec6da94a6d481bf815017c087380d27a0a463c5620c3623c4692cc66101b6db40b0481d0624a84b0a94bfe03cfd4619b97b72bfa61b53ead0a1560c590e94bea60b9a12093a7501736c477b4488d6428124edd77e58a478db484c6ac31536402dca4695dba383fa8b7cb13dc60ac55e03adc380a221cb6d416509ef5c1dc5efe9f338ada2d3b315622ad98eccc96cb36514137d3cfc7e3b62161bdac1f13c90fb803493ea8d382f564b721f80e2804a876a804daea1b11f23f4c5c3da24a6da93b7bdbf2c50b43c97999975a79884ec77afa92b2ea53a4efe77eb89f4f0f73048256fd49965d528a94a2ea965571c8db130254783913c71f97d04d7db4adb42c2db052a4a872d8e1253a8436a756a09404ea52942d603a9fae01b286419343abb750915953ba428161b052955c72373b8f862733d526756e82b854c94db0eb8a1acacdb527a8b8f87cb0f6b61b248632e2da2915e7acbc800a6a4855cd88093b7d31f399f72d363ff00da215e212da8ff002c572df0baa82425b5c968b7b152ba7c30430b8550d2e132a7ad681c827ae107159832739e7e160fba3ba0660a6d71b52e9927b5083650d25247c0e33471078639c2a19f6b954814979e8ab96b79a752e27bc39820137f0f91c68ecb997a9d97da7134e4a8297efad6ab93fd31321692077c1f8e0d8f2d5ab087960f339eeb1e3197b3a663ac52689556eaaf4544848d3202ca4d08021804d89b9e5b5f139c46cc2bcb1c7054d5c24c94d2c3288ccb84a1252190126e01ea49f5c6a80ea40d943ccdf11356cbf42acba5caad2a9f31c29d1adf612b55bc2e45fa9c4be7ef85274aa0b83cfc8cf5c4dcd398e7a021e305c096d049092e2740483e49047c702b92bf5e728d2aadfa1294fb31a6243121d7e38b809d43bbabf88f4c6a1cb596287961996dd061b7093295adc283737b5b6277007418126f86a85a9c32eaaa78ad64ea5024efea79e04cd5c055f21f2c60794dea3ef4aa68591dda7f06aba853909daf545c61e4c54b892e34d209245ff11b83a478605b2e70c739d6d71a9819ec20f69db10eca4686ef6baf4055ef603a5f6c68697c36656e25d89532d3a9b003b3db6f8e2432fe428547aa355013e43af364ab4ec949245be5b9c0099ddd56864cb73ea46003dd0af1c72dd3a93c18723d3e0b297222e300e34d778e9212544f3e57bdfc719fd79ab31d7f2cd3327583d4e8eea4b0ca19bb9aae6ddee7f78e36dbc1a75b2d3c96d6dac58a56010af5070ca2d3a9b016a5c2830e32d5ef299692827e4304d9282d2ab59a3885c236f2a70de2d6c2de5d5d0ea0cd46b0a6d095dc7776e874827cf121c3be2050e9dc27ac50b324a7125e2eb31996db2b59438ddefe40289e67ae2fdcdf4e6ebb94eaf4c500b32232db009fbd6ba7e46df2c643a370fea4ff007aa91e532a245994b64ac8f1f2182120e9f894534ad80753b8534dcb019f67bca3033466897fa660b92a9f159ed01d452df69a8582bc6e2fb7960b78fd4b5e51ce996f35d1586e332d8435669364a56d9b8161e2836f8627b2bc8afe5da7262e5fcb6dc48c482a023a8a9c23ef2893727cfcf0719de88bce7c3e91025b0db3517980eb6852b669f02e37f98f42701e682eb4106436614d09e54f3ee5aa652e34da955e2c76e43297d0d85eb590a4dc7753738a2b8b9c59a466ea13f46814b7d4d8752e3531d5e829527ef045b95891b9eb88dcbbc0dcc952702ab0ec6a4b236bb8a0f2cfa049b7d7167d1b81f94e9d1d4658935494126c5e77b341574d93623a75c387318ac1b2140d3733a731e55a14acc90d6fd1fd8dca74b790c05ad1211eeaf5734eda08ded7be18371e4885024e69f6955213194c535e534aecdb7357bee213bee2f6241e589481faf1488e6994ba77b2446cd834c476b40f89beaf5249c3c09e223c8d2eb8f94754add6adf2e588dd20eca97f3bdba49fb28fa554e7cb10d798eab2a3d09d7d6d9900ab4bda7ddb585d20eff2c1070fe54a567cab3f4d79da8c76e39610a7104e940520a6c4db6b7e586d020e7776633ed8f8117582b6dd5a0a00bfe01b7d316be5d0c373dd71b6d94ad4dd94ab004d88b6003f6ace3ca6606da5beebffd96821c151e7111c98bf4699122e43b7ec74f26ba98bd42ccd8c01fa60c8a17367f3754700000000

WHY?

What was the plan? Can we think of any sort of justisifaction other than making it to the hall of shame?
9  Bitcoin / Development & Technical Discussion / Re: Need help reversing bitcoinutils "tagged_hash" to generate Taproot addresses on: January 17, 2024, 04:14:38 PM
Think I nailed it!

I don't think such concrete Python code has been published elsewhere; correct me if I'm wrong. Anyways, here we go.

Let's start with some imports and two functions:

Code:
import hashlib, base58, bitcoinutils.bech32
from fastecdsa import keys, curve, ecdsa, point
curve = curve.secp256k1

def privkey_to_pubkey_hex(privkey, compressed=False):
    #Accept 64 char HEX, WIF and arbitrary length integer numbers as inputs
    
    try: #Is it WIF?
        privkey_hex = base58.b58decode_check(privkey).hex()
        privkey_hex = privkey_hex.replace('0x','')
        if len(privkey_hex) == 68:
            privkey_hex = privkey_hex[2:-2]
        if len(privkey_hex) == 66:
            privkey_hex = privkey_hex[2:]
    except:
        pass
    try: #Is it HEX?
        if int(str(privkey),16):
            privkey_hex = privkey
            privkey_hex = privkey_hex.replace('0x','')
            if len(privkey_hex) == 68:
                privkey_hex = privkey_hex[2:-2]
            if len(privkey_hex) == 66:
                privkey_hex = privkey_hex[2:]
    except:
        pass
    try: #Is it INT?
        if isinstance(privkey, int):
            privkey_hex = hex(privkey)[2:].zfill(64)
    except:
        pass
          
    try:
        privkey_int = int(privkey_hex,16)
        pub_key = keys.get_public_key(privkey_int, curve)
        uncompressed_public_key = '04'+str(hex(pub_key.x)[2:]).zfill(64)+str(hex(pub_key.y)[2:]).zfill(64)
        if compressed==False:
            return uncompressed_public_key
        else:
            if int(str(pub_key.y)) % 2 == 0:
                compressed_public_key = '02'
            else:
                compressed_public_key = '03'
            
            compressed_public_key += str(uncompressed_public_key[2:66])
            
            return compressed_public_key
    except:
        pass

def decompress_point(x, curve):
    y_squared = (x**3 + curve.a * x + curve.b) % curve.p
    y = pow(y_squared, (curve.p + 1) // 4, curve.p)
    if y % 2 == 0:
        return point.Point(x, y, curve=curve)
    else:
        return point.Point(x, curve.p - y, curve=curve)

And in action:

Code:
seed = '0000000000000000000000000000000000000000000000000000000000000001'
short_x = privkey_to_pubkey_hex(seed, compressed=True)[2:]
tag_hash = hashlib.sha256('TapTweak'.encode()).hexdigest()
tagged_hash = hashlib.sha256(bytes.fromhex(tag_hash + tag_hash + short_x)).hexdigest()
tagged_hash_pubx = privkey_to_pubkey_hex(tagged_hash, compressed=True)[2:]

point1 = decompress_point(int(short_x, 16), curve)
point2 = decompress_point(int(tagged_hash_pubx, 16), curve)

point_add = point1 + point2
tweaked_witness = hex(point_add.x)[2:]
print(bitcoinutils.bech32.encode('bc', 1, bytes.fromhex(tweaked_witness)))

Tweaked Taproot demystified - mission accomplished - the output is the desired Taproot public address!

Code:
bc1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sspknck9

Yay.

Thank you for all your support. This was the least user-friendly address generation code thus far Smiley
10  Bitcoin / Development & Technical Discussion / Re: Need help reversing bitcoinutils "tagged_hash" to generate Taproot addresses on: January 16, 2024, 04:41:35 PM
Code:
hashlib.sha256(bytes.fromhex('e80fe1639c9ca050e3af1b39c143c63e429cbceb15d940fbb5c5a1f4af57c5e9' + 'e80fe1639c9ca050e3af1b39c143c63e429cbceb15d940fbb5c5a1f4af57c5e9' + '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')).hexdigest()

does not give

Code:
da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21
It's not supposed to give 0xda47... because that is your tweaked public key. You need to compute the hash above multiplied by generator point to get a new point (pubkey) and then add that to your original public key to get 0xda47...

Code:
t = SHA256(SHA256("TapTweak") | SHA256("TapTweak") | pub_bytes)
pub_tweak = pub + (t * G).
Convert the hash bytes to t in big endian order.
t*G is EC point multiplication (just like you'd get pubkey from a private key)
+ is EC point addition.

So, in Python?
11  Bitcoin / Development & Technical Discussion / Re: Need help reversing bitcoinutils "tagged_hash" to generate Taproot addresses on: January 16, 2024, 01:08:45 PM
Code:
A tagged hash is: SHA256( SHA256("TapTweak") || 
                              SHA256("F") ||
                              data
                            )
You posted this correctly in OP but changed it here. The second hash is also SHA256 of "TapTweak" (not F).
Code:
SHA256("TapTweak")=e80fe1639c9ca050e3af1b39c143c63e429cbceb15d940fbb5c5a1f4af57c5e9

tagged_hash = SHA256(e80f...c5e9 | e80f...c5e9 | 79be...1798)
| is byte concatenation and 79be...1798 is the pubkey.

Gotcha, the only problem is that

Code:
hashlib.sha256(bytes.fromhex('e80fe1639c9ca050e3af1b39c143c63e429cbceb15d940fbb5c5a1f4af57c5e9' + 'e80fe1639c9ca050e3af1b39c143c63e429cbceb15d940fbb5c5a1f4af57c5e9' + '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')).hexdigest()

does not give

Code:
da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21

In other words, we cannot yet reproduce the bitcoinutils' method. (Not only bitcoinutils; several libraries give the same result.)

Edit/addition - for reference - and what we aim to reproduce:

Code:
>>>bitcoinutils.keys.PrivateKey.from_wif('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn').get_public_key().get_taproot_address().to_string()
>>>bc1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sspknck9
>>>bitcoinutils.keys.PrivateKey.from_wif('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn').get_public_key().get_taproot_address().to_witness_program()
>>>da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21
12  Bitcoin / Development & Technical Discussion / Re: Need help reversing bitcoinutils "tagged_hash" to generate Taproot addresses on: January 16, 2024, 09:33:29 AM

Many times, yes. It is pretty technical and a tad vague regarding the exact code.

I tried this pseudocode

Code:
 A tagged hash is: SHA256( SHA256("TapTweak") || 
                              SHA256("F") ||
                              data
                            )
    Returns hashlib object (can then use .digest() or hexdigest())

as so:

Code:
taptweek_hash1 = hashlib.sha256('TapTweak'.encode()).hexdigest()
#e80fe1639c9ca050e3af1b39c143c63e429cbceb15d940fbb5c5a1f4af57c5e9
taptweek_hash2 = hashlib.sha256('F'.encode()).hexdigest()
#f67ab10ad4e4c53121b6a5fe4da9c10ddee905b978d3788d2723d7bfacbe28a9

tagged_hash = hashlib.sha256(bytes.fromhex('e80fe1639c9ca050e3af1b39c143c63e429cbceb15d940fbb5c5a1f4af57c5e9' + 'f67ab10ad4e4c53121b6a5fe4da9c10ddee905b978d3788d2723d7bfacbe28a9' + '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')).hexdigest()

### >> 56d0345a861392e020a0fecdf2c2cffda10b340171f4a31d827eb63e9ccddeac ### NOT GOOD

No luck, yet. How does bitcoinutils do it?
13  Bitcoin / Development & Technical Discussion / Need help reversing bitcoinutils "tagged_hash" to generate Taproot addresses on: January 15, 2024, 09:33:16 PM
[Addition, the problem was solved down here]


Let's begin with this private key

Code:
0000000000000000000000000000000000000000000000000000000000000001

Using any good old tool, bitcoinutils included, it gives us

Code:
Private key compressed (WIF):                 KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn
Public key compressed (HEX):                  0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798

HASH160 uncompressed:                         91b24bf9f5288532960ac687abb035127b1d28a5
HASH160 compressed:                           751e76e8199196d454941c45d1b3a323f1433bd6

Legacy uncompressed public address (P2PKH):   1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm
Legacy compressed public address (P2PKH):     1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH
Nested segwit public address (P2SH-P2WPKH):   3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN
Native segwit public address (P2WPKH):        bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4

I won't go into legacy or nested segwit here, but I would like to mention how we easily derive the native segwit public address using the HASH160 compressed

Code:
bitcoinutils.bech32.encode('bc',0,bytes.fromhex('751e76e8199196d454941c45d1b3a323f1433bd6'))
#bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4

(Likewise, we can produce the segwit bech32 (P2WSH) address by using the single SHA256 of the compressed public key to get bc1qpac4ht6afshdx2tctnhjnetz7u6g3j9zhwwmc4cqkdsa2jumq42qd3drf7; another thread.)

However, here is where I need your help.

I know that the P2TR address corresponding to this private key and its witness program are

Code:
Tweaked Taproot public address (P2TR):        bc1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sspknck9
Witness program (HEX):                        da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21

since I can quickly arrive at them using bitcoinutils

I can furthermore reproduce and verify it with:

Code:
bitcoinutils.bech32.encode('bc',1,bytes.fromhex('da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21'))
#bc1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sspknck9

However, I fail to reverse how bitcoilutils does it. Specifically, this function from here that generates the tweaked public key...

Code:
def tagged_hash(data: bytes, tag: str) -> bytes:
    """
    Tagged hashes ensure that hashes used in one context can not be used in another.
    It is used extensively in Taproot

    A tagged hash is: SHA256( SHA256("TapTweak") ||
                              SHA256("TapTweak") ||
                              data
                            )
    """

    tag_digest = hashlib.sha256(tag.encode()).digest()
    return hashlib.sha256(tag_digest + tag_digest + data).digest()

... gives me a headache.

I don't see it. How do you go from the 32-byte x-only public address to that witness program?

In other words, how do I reproduce this transformation in Python (without relying on bitcoinutils as a mystery box library)?

Code:
Input:                                        79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
to
Output:                                       da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21

Need your help! I'm not sure what the library uses as its standard "TapTweak", and I get confused by bitcoinutil's nested (pun intended) code.

How do we use hashlib only and arrive at the same tweaked witness program, and thus the identical P2TR public address? I am very well aware you are allowed to use whatever as "TapTweak". Can we, however, simply reproduce the "library recipe"? What is its standard "TapTweak," and how to arrive at this public address, given the data above, using simple Python?

Many thanks.
14  Bitcoin / Development & Technical Discussion / Re: the fastest possible way to mass-generate addresses in Python on: January 14, 2024, 04:51:18 PM

I was looking only for public key generation part

Here's a simplified script that generates compressed public keys from a given range of private keys:

Code:
import hashlib, sys
import gmpy2

# Constants as mpz
Gx = gmpy2.mpz('0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16)
Gy = gmpy2.mpz('0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8', 16)
p = gmpy2.mpz('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16)
n = gmpy2.mpz('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16)

def private_key_to_public_key(private_key):
    Q = point_multiply(Gx, Gy, private_key, p)
    return Q

def point_multiply(x, y, k, p):
    result = (gmpy2.mpz(0), gmpy2.mpz(0))
    addend = (x, y)
   
    while k > 0:
        if k & 1:
            result = point_add(result, addend, p)
        addend = point_double(addend, p)
        k >>= 1

    return result

def point_double(point, p):
    x, y = point
    lmbda = (3 * x * x * gmpy2.powmod(2 * y, -1, p)) % p
    x3 = (lmbda * lmbda - 2 * x) % p
    y3 = (lmbda * (x - x3) - y) % p
    return x3, y3

def point_add(point1, point2, p):
    x1, y1 = point1
    x2, y2 = point2

    if point1 == (gmpy2.mpz(0), gmpy2.mpz(0)):
        return point2
    if point2 == (gmpy2.mpz(0), gmpy2.mpz(0)):
        return point1

    if point1 != point2:
        lmbda = ((y2 - y1) * gmpy2.powmod(x2 - x1, -1, p)) % p
    else:
        lmbda = ((3 * x1 * x1) * gmpy2.powmod(2 * y1, -1, p)) % p

    x3 = (lmbda * lmbda - x1 - x2) % p
    y3 = (lmbda * (x1 - x3) - y1) % p
    return x3, y3

def encode_base58(byte_str):
    __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
    __b58base = len(__b58chars)
    long_value = gmpy2.mpz(int.from_bytes(byte_str, byteorder='big'))
    result = ''
    while long_value >= __b58base:
        div, mod = gmpy2.f_divmod(long_value, __b58base)
        result = __b58chars[int(mod)] + result
        long_value = div
    result = __b58chars[int(long_value)] + result

    # Add leading '1's for zero bytes
    nPad = 0
    for byte in byte_str:
        if byte == 0:
            nPad += 1
        else:
            break

    return __b58chars[0] * nPad + result

def public_key_to_hex(public_key, compressed=True):
    x_hex = format(public_key[0], '064x')[2:]  # Remove '0x' prefix
    if compressed:
        # Use '02' prefix if Y coordinate is even, '03' if odd
        return ('02' if public_key[1] % 2 == 0 else '03') + x_hex

def public_key_to_address(public_key, compressed=True):
    public_key_hex = ('02' if compressed else '04') + format(public_key[0], '064x')
    sha256_hash = hashlib.sha256(bytes.fromhex(public_key_hex)).digest()
    ripemd160_hash = hashlib.new('ripemd160', sha256_hash).digest()
    versioned_hash = (b'\x00' if compressed else b'\x04') + ripemd160_hash
    checksum = hashlib.sha256(hashlib.sha256(versioned_hash).digest()).digest()[:4]
    address_bytes = versioned_hash + checksum
    return encode_base58(address_bytes)

# Define the range
start_range = gmpy2.mpz('36893488147419132058')
end_range = gmpy2.mpz('36893488149419115809')

# Iterate through the range and generate Bitcoin Addresses (Compressed) and their Public Keys
for key in range(start_range, end_range + 1):
    public_key = private_key_to_public_key(key)
    bitcoin_address = public_key_to_address(public_key, compressed=True)
    public_key = public_key_to_hex(public_key)
    sys.stdout.write("\033c")
    sys.stdout.write("\033[01;33m")
    sys.stdout.write(f"\r[+] Private Key (dec): {key}\n[+] Bitcoin Address (Compressed): {bitcoin_address}\n[+] Public Key: {public_key}" + "\n")
    sys.stdout.flush()


Mobile phones typically run Android or iOS. Python can be run on both platforms, but there are some differences in compatibility..
For Android, you can use the QPython app or Termux to run Python scripts. On iOS, you might need to use apps like Pythonista or Pyto.
It may require a lot of CPU power and memory. Make sure your mobile phone can handle the computational requirements.
You can even translate this script into a mobile app  but I don't see the purpose of it on the phone. Neither the script will work as it should nor the phone.


Lovely. However "def public_key_to_address" behaves not right if "compressed=False". Would you mind taking a look at it?
15  Bitcoin / Development & Technical Discussion / Re: Tool or method for finding public key of Bitcoin address on: January 11, 2024, 01:10:46 PM
Hi guys. Just wandering  if their is a tool, script or method for finding the uncompressed public key of Bitcoin address. I have heard that you can if an addressed has sent funds. I haven't got a clue on how to find this information. Thanks

Check out my simple and fast blockchain parser.

With some clever regex, it should be a walk in the park to list all public keys that have been exposed when spending.
16  Bitcoin / Development & Technical Discussion / Re: How to redeem a "dumb script" native P2WPKH? (Nested P2SH-P2WPKH went well) on: January 09, 2024, 05:49:54 PM
No sigScript allowed when spending... hm. How do I create a witness program for a dumb script for which there is no known public and private key? Is it even possible?
The correct address should be the one provided by vjudeu from Bitcoin Core's decodescript command.

To spend it, you'll basically have to do the same as your previous transaction, but for SegWit:
Code:
01000000								< Version 1
00 < SegWit marker
01 < SegWit flag
01 < amount of input(s)
f052c63f7cc387e85f96c99dc108f53289943f03d3814e0ff5e8f54f8e22d29e < input0 txid
00000000 < input0 index
00 < script length (0 for p2wpkh)
ffffffff < input0 nSequence
01 < amount of output(s)
60EA000000000000 < output0 amount
19 76a914b77e875983ee5851006ec5bd461208ebd11ea1bf88ac < output0 Size scriptPubkey
-------------------------------------------Witness for input 0--------------------------------------------
02 < Number of Stack Items
01 51 < Stack item 0 (Size OP_PUSHNUM_1)
02 7551 < Stack item 1 (Size OP_Drop 1 OP_PUSHNUM_1)
-------------------------------------------Witness for input 0--------------------------------------------
00000000 < nLocktime

Your previous transaction's scriptSig "0x0451027551" is used as Witness.
But in P2WPKH spend, the number of stack items must be indicated followed by those stack items with their respective sizes.

Transactions:
Spent by:   blockstream.info/testnet/tx/1b291f7cf57e827f6e8d1225657264fcee9ad4a8248e769c2788a8196a2bda6b (the example above)
Funded by: blockstream.info/testnet/tx/9ed2228e4ff5e8f50f4e81d3033f948932f508c19dc9965fe887c37c3fc652f0

Excellent, many thanks! This really works.

Amended the code for public address generation, too (now produces the same results as Bitcoin Core):

Code:
def hex_to_hash160(hexdata):
    return hashlib.new('ripemd', hashlib.sha256(bytes.fromhex(hexdata)).digest()).hexdigest()
def hex_to_sha256(hexdata):
    return hashlib.sha256(bytes.fromhex(hexdata)).hexdigest()

the_script = "7551" #feel free to play around
#the_script = bitcoinutils.script.Script(["OP_DROP", "OP_1"]).to_hex() #same result but unnecessary library use

the_script_hash160 = hex_to_hash160(the_script)
the_script_sha256  = hex_to_sha256(the_script)

print('Testnet:')
print(base58.b58encode_check(bytes.fromhex('c4'+ the_script_hash160)).decode())
print(bitcoinutils.bech32.encode('tb', 0, bytes.fromhex(the_script_sha256)))
print()
print('Mainnet:')
print(base58.b58encode_check(bytes.fromhex('05'+ the_script_hash160)).decode())
print(bitcoinutils.bech32.encode('bc', 0, bytes.fromhex(the_script_sha256)))

Code:
Testnet:
2NBgCqkaw8r7s15pamKLogvWurSTFVVf1Kf
tb1qxvvc4xl77e6whhdel7499y5qz7uywfu3u4xxp89etunc4343udys6xzc00

Mainnet:
3L7zn1euXPcWoJC36Biw4yXee6F5ksEYdb
bc1qxvvc4xl77e6whhdel7499y5qz7uywfu3u4xxp89etunc4343udysdw5h4q
17  Bitcoin / Development & Technical Discussion / How to redeem a "dumb script" native P2WPKH? (Nested P2SH-P2WPKH went well) on: January 07, 2024, 04:54:18 PM
I fooled around a bit with "dumb scripts" like so:

Script("OP_DROP", "OP_1") is dumb but valid, will accept any one value in, drop it, and leave just a True on top of the stack. It is simply 0x7151 in hex.

Proven on testnet with (Python 3, import hashlib, base58 and bitcoinutils):

Code:
dumb_script="7551"
dumb_hash = hashlib.new('ripemd', hashlib.sha256(bytes.fromhex(dumb_script)).digest()).hexdigest()
dumb_address = base58.b58encode_check(bytes.fromhex('c4' + dumb_hash)).decode()
print(dumb_address)
#2NAfhvQFw2GR1A1iN8Eke47HqVuDcsW4Uzw

I sent 1 tBTC to 2NAfhvQFw2GR1A1iN8Eke47HqVuDcsW4Uzw, tx id 85fd05407575d31155824f6cdbaae1b544af78158cc643dd4d8f7c7351ef5bfb

To take it back, I created a raw transaction the lazy way using Cointoolkit:

Code:
0100000001fb5bef51737c8f4ddd43c68c1578af44b5e1aadb6c4f825511d375754005fd85000000000451027551ffffffff0118ddf5050000000017a914628cde58e1bcd42efb72b1821ec9fdf49e0f1d498700000000

...and broadcast it successfully in Electrum, got the tBTC back within the same block, tx id 4d7eee43f95a1402128aee4577daf0fd287f8c6aab9893f6aad559ab688cc91f

Comments: Used "OP_1, OP_DROP, OP_1" as the sigScript, which, including length prefixes, is 0x0451027551 - all it took.

I suppose I could have pushed any single number, but I went with OP_1 - and it worked as intended.

So far, so good. But now I wanted to repeat the experiment with native Segwit P2WPKH, so I changed formats while still using the same (dumb) script:

Code:
dumb_script="7551"
dumb_hash = hashlib.new('ripemd', hashlib.sha256(bytes.fromhex(dumb_script)).digest()).hexdigest()
dumb_address = bitcoinutils.bech32.encode('tb',0,memoryview(bytes.fromhex(dumb_hash)).tolist())
print(dumb_address)
#tb1qhudhq5jad3wa26unkzmt0llg4r0wt4euhpwxg7

Sent another 1 tBTC to tb1qhudhq5jad3wa26unkzmt0llg4r0wt4euhpwxg7, tx id 2df1208d368035e6ff923f5a40053792f220b3d87628db62e51a7bcdf0a50a3a

Uh oh. Need help!

No sigScript allowed when spending... hm. How do I create a witness program for a dumb script for which there is no known public and private key? Is it even possible?

Staring into a random raw P2PWKH transaction but fail to see "it."

If possible, there is 1 tBTC in it for you. Take it, and please share how the witness part was created. In case it is impossible, please explain why.

Many thanks!
18  Bitcoin / Development & Technical Discussion / At least 14(!) unique public addresses from one single seed (0 derivation paths) on: December 26, 2023, 11:20:08 PM
Do you agree that a not-uncommon Bitcoin discussion goes along the lines:

"Remember that one private key controls two public addresses, the uncompressed and the compressed." Next, someone smart-ass says, "You're forgetting nested and native segwit - it's four addresses, guys."

What about newer stuff like Taproot? What about crazy things like 1-of-1-multisig? What about older formats like P2K and P2MS that no one has used in years but would still be accepted by the network?

I decided to give it a shot.

The overall goal here is to show how many unique public addresses are actually derived from one single seed, using no "HD Wallet wizardry" with optional additional keyphrases and derivation paths. I decided no input other than the "bad_seed" string is allowed; absolutely no mnemonic phrases or derivations paths from BIPs.

By the way, I call the only input "seed" rather than password/passphrase, and I define a seed to be a 64-character hexadecimal number, such as

Code:
0000000000000000000000000000000000000000000000000000000000000001

because it can then be interpreted as 32 bytes or as a script. (This example wouldn't run as a script, but that's a separate story).

For this project, I've experimented with Python and cross-verified outputs with online services as needed, but I thought my actual code was of limited interest. If you, against all odds, wanna take a look at my scripts, say when.

Back to the title subject. From the seed, I can begin with deriving:

Code:
[[[OUTPUTS]]]

Master root XPRV:
xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21TeqtDeZVxb

Uncompressed public key:                                    0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
Compressed public key:                                      0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
Uncompressed RIPEMD160 a.k.a. HASH160:                      91b24bf9f5288532960ac687abb035127b1d28a5
Compressed RIPEMD160 a.k.a. HASH160:                        751e76e8199196d454941c45d1b3a323f1433bd6


Private keys
~~~~~~~~~~~~

Uncompressed (WIF):                                         5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf
Compressed (WIF):                                           KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn

Nothing special so far, except maybe my XPRV experiment.

But how about this? I've never found anything online that is this extensive (and way too many examples on Github that do bits and pieces of it).

Code:
Public addresses
~~~~~~~~~~~

Public script - rare but valid (P2S)**                      0000000000000000000000000000000000000000000000000000000000000001
Old 1-of-1 multisig uncompressed - rare but valid (P2MS):   51410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b851ae
Old 1-of-1 multisig compressed - rare but valid (P2MS):     51210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179851ae
Uncompressed public key - rare but valid (P2PK):            0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
Compressed public key - rare but valid (P2PK):              0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
Uncompressed legacy public key (P2PKH):                     1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm
Compressed legacy public key (P2PKH):                       1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH
Nested SegWit public address (P2WPKH-P2SH):                 3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN
Nested SegWit script public address (P2SH)**:               3D24NWLpGxY9x8WZYpvowwyNPyZyN7aRNB
New 1-of-1 multisig uncompressed - weird (P2SH):            33RjLdp9usumz3BNqa5PB9umJZjiw7kmjK
New 1-of-1 multisig compressed - weird (P2SH):              3DicS6C8JZm59RsrgXr56iVHzYdQngiehV
Native SegWit public address (P2WPKH):                      bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
Native Segwit public address (P2WSH)**:                     bc1qa3y3dhfgl3xpp4uw9p72tkwv28hp4eeuhl0q334nwvjvh74v30zs5faqjp
Taproot public address (P2TR):                              bc1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sspknck9

[Those marked with ** require the underlying script to be executbale, or the address is unspendable.]

My comments:

I thought I'd take it from the beginning and in chronological order of appearance from 2009 until today.

Pay-to-script (P2S) has, to the best of my knowledge, always been allowed, but there are few examples from the past decade. Paying to the script used in this example but be advised against; it is probably not spendable.

The same goes for early-day multi-signature addresses, for which you had to write the public key(s) in clear text (P2MS). As far as I understand, they are still legal but very scarce. The special case of 1-of-1 was probably never an intended feature - it makes little sense - but they weren't disallowed back then, and they are still accepted today. We can write one each for the uncompressed and the compressed public keys, respectively.

Then, we have the two legacy addresses that reigned for many years (especially the compressed ones).

First among the "3-addresses" is one with many names. I like "nested Segwit" best, but if you prefer P2WPKH-P2SH, I'm cool.

Next in line is "Nested SegWit script public address (P2SH)**" - and I would like you to suggest a better name. The code I use to construct them is:

Code:
base58.b58encode_check(bytes.fromhex('05'+ hashlib.new('ripemd160', hashlib.sha256(bytes.fromhex(bad_seed)).digest()).hexdigest())).decode()

They are, I think, only good if the corresponding script is good (and I strongly suspect "bad_seed" will not pass as a valid Bitcoin Script).

Then we have these weird 1-of-1 multi-sig transactions again. Take a look at this link collection about "Crazy1to1" and good luck getting any wiser! In any case, we can easily create two 1-of-1 multi-sig addresses, one for the uncompressed public key and one for the compressed ditto, and wrap them up in P2SH containers.

Native Segwit, or shall we call them "bc1"-addresses shouldn't surprise anyone. At least not the first. The second one, which I call "(P2WSH)**", is just me using the seed as a script again; hence, 32 instead of 20 bytes and a longer address. In honor of full transparency, I think the address format is correct, but since "bad_seed" isn't a real script, I doubt the address is spendable.

The last one is "just" a regular Taproot. It is also the format that I know the least about. I am struggling with theory in combination with super-enthusiastic Youtubers, but it is still to little avail - I am not sure I get "it" when it comes to Taproot/bc1p and how it changes the world for the better. Is it possible to make Taproot variations without introducing arbitrary extra code? I take pride in that all the above are derived from one single seed and that anyone who reproduces parts or all of them should get the same public addresses.

I am super excited about your feedback! Did I leave anything obvious out? Did I make any mistakes, big or small? In other words, what should I correct? Have others derived more than 14 public addresses from one hardcoded seed in a deterministic and reproducible manner? Should I make public my Python script?

For good measure, iteration 2 yields:

Code:
[[[INPUT]]]]                                                [[[We treat this single input a private key OR a script (HEX)]]]

Bad seed:                                                   0000000000000000000000000000000000000000000000000000000000000002

[[[OUTPUTS]]]

Master root XPRV:
xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21TeqtEE6Pxo

Uncompressed public key:                                    04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a
Compressed public key:                                      02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5
Uncompressed RIPEMD160 a.k.a. HASH160:                      d6c8e828c1eca1bba065e1b83e1dc2a36e387a42
Compressed RIPEMD160 a.k.a. HASH160:                        06afd46bcdfd22ef94ac122aa11f241244a37ecc


Private keys
~~~~~~~~~~~~

Uncompressed (WIF):                                         5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAvUcVfH
Compressed (WIF):                                           KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU74NMTptX4

Public addresses
~~~~~~~~~~~

Public script - rare but valid (P2S)**                      0000000000000000000000000000000000000000000000000000000000000002
Old 1-of-1 multisig uncompressed - rare but valid (P2MS):   514104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a51ae
Old 1-of-1 multisig compressed - rare but valid (P2MS):     512102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee551ae
Uncompressed public key - rare but valid (P2PK):            0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
Compressed public key - rare but valid (P2PK):              0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
Uncompressed legacy public key (P2PKH):                     1LagHJk2FyCV2VzrNHVqg3gYG4TSYwDV4m
Compressed legacy public key (P2PKH):                       1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP
Nested SegWit public address (P2WPKH-P2SH):                 3FWHHE3RVgyv5vYmMrcoRdA25uugWvQbso
Nested SegWit script public address (P2SH)**:               3CNVyupnE7C8FMCv9vPWSpriuVQZVt6Fzh
New 1-of-1 multisig uncompressed - weird (P2SH):            3G1kW5oNxKs2whEgi6cwVy5KEoAqkwuGHr
New 1-of-1 multisig compressed - weird (P2SH):              32KTvowh8quz8eFfxgX98QJUdUGgryn28R
Native SegWit public address (P2WPKH):                      bc1qq6hag67dl53wl99vzg42z8eyzfz2xlkvxechjp
Native Segwit public address (P2WSH)**:                     bc1qjfna8kldsq55zjplrtaz567x3hjlv5cj3t9fhu2xr3ws5wkndmfqhhl956
Taproot public address (P2TR):                              bc1pet7ep3czdu9k4wvdlz2fp5p8x2yp7t6ttyqg2c6cmh0lgeuu9lasmp9hsg

[Those marked with ** require the underlying script to be executable, or the address is unspendable.]
19  Bitcoin / Development & Technical Discussion / Re: Is there a list of public keys on: December 23, 2023, 09:05:16 PM
Fire up Electrum (a portable executable/binary is good enough, regardless of OS), open the console tab and

Code:
getbalance('3ERBHeQ9SENDcsbmZMBTCFvY1LfGiCMEH8')

does what you are looking for. Since Electrum is native Python, it is very "scriptable" too.

That's the good news. The bad news is that it will never work for what you probably want to achieve: scan "all" public addresses, hoping to strike gold.

Please don't hammer the Electrum network with a gazillion pointless requests. If you need 10 or 20 lookups a day, sure. If more than that, do it offline with a recently updated list.




20  Bitcoin / Development & Technical Discussion / Re: In theory,THE most unsafe HD Wallet - [BIP32, BIP39, BIP44, BIP84] - am I close? on: December 23, 2023, 02:41:05 AM
This is interesting. Remember a decade ago and the fuck-up with brain wallets?

People would take SHA256(secret) and use the 32-byte output as a BTC private key. Things got really interesting when someone set secret = "", i.e. an empty string. Its SHA256 is

Code:
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Here is a neat tool to convert 32-byte strings to Bitcoin key pairs. For the empty string hash above, it gives us

Code:
Private key uncompressed (WIF):     5Hvr8Y5y3QZE6cVciRcsRtmXSLbuJcsVXr5JooEVUEwwGpShw71
Private key compressed (WIF):       KwifrGCvMcyEomfJdqfKasVXLqX3rmKrw9c1bZ4YfEZUYoVyHRGv

Legacy address uncompressed:        17DCsohf9Mx9xJxJx5i21VXw8c6xeKVj2d
Legacy address compressed:          151nEUuyFiwrxxqse4wmQfwPvpWACbyBUU
Nested segwit adress:               3FiUrxVsgBuGmTBtfbySRZ4z8kFffzYzTf
Native segwit address:              bc1q8l84lncadllmcrn9wsulzj4e24nnjsv86w0jhh
Taproot address:                    <you know the drill by now>

...and the infamous 50 BTC transaction belongs to one of them.

"What on Earth does any of this have to do with HD Wallets?" is a valid question.

Well, I realized we can easily recreate the madness using a master XPRV and leave out derivation paths.

Others have described the anatomy of XPRV better before me, so allow me to concatenate the bytes according to:

(pseudocode) b"\x04\x88\xad\xe4" + a string of zeros + SHA25&(""), so that we get a 156 characters long hexadecimal string like so

Code:
0488ade4000000000000000000000000000000000000000000000000000000000000000000000000000000000000e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

To verify is as an XPRV and make it human-readable, here is a one-liner that does the trick:

Code:
>>> base58.b58encode_check(binascii.unhexlify('0488ade4000000000000000000000000000000000000000000000000000000000000000000000000000000000000e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')).decode()
>>> 'xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAmp8jihbB12h5EN88J7v7oXcjdCgi9zWxT3Ya8znix4tadVdyM'

Lo and behold, if we run

Code:
xprivkey = "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAmp8jihbB12h5EN88J7v7oXcjdCgi9zWxT3Ya8znix4tadVdyM"
path = ""
hdw = HDWallet(xprivkey, path)
#and so forth

we get... the same private keys and public addresses!

In other words, we have resurrected the decade-old brain wallet madness and shown that the same goes for XPRV.

Bonus:

If we instead use the stupid SHA("") twice in the same string, e.g.

Code:
>>> base58.b58encode_check(binascii.unhexlify('0488ade400000000000000000000e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')).decode()
>>> 'xprv9s21ZrQH143K24sTZGBSPocQZqkzcNhss3CpUdhrJxgdUVyK3iG7QvdpuddRytfq997BhnDGVmrbqFmCEPDQhbAyXDmFfeFJh3AxsfSxCMp'

We get a seemingly new and unique XPRV, but it resolves to the same good old key pairs. If no derivation path is specified, none of these extra bytes matter.

This ultimately leads me to believe that we could scan using either ...01, ...02, ...03 like brainflayer or by feeding it with good old SHA256 that has been out in the wild for a long time.

Additionally, it will be interesting to see whether the derivation path can be the variable that leads us to the discovery of new (old) public keys and their private keys.

Mnemonic phrases and hierarchical deterministic (HD) wallets may be good. I mean, the intention was certainly good. But it is not so good if you can trick non-tech-savvy people into using XPRVs that are accepted by all major wallet software even if they are a cryptographic disaster.
Pages: [1] 2 3 4 5 6 7 8 9 10 »
Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!