buzzkillb Posted May 15, 2020 Report Share Posted May 15, 2020 Been wondering how to make use of the electrumx server, but in bash. First I needed to connect to the electrumx server. #!/bin/bash echo "get block header" (echo '{"method" : "blockchain.block.header", "params": ["1"], "id": "msg_id"}'; sleep 1) | ncat --ssl electrumx1.denarius.pro 50002 This outputs. block header {"jsonrpc": "2.0", "result": "06000000cd8f82c4c28201fd89def0dba541d66432bff9c1bb16fc1c6201dabb5d0d0000e4abd1522f390c5a2b83add7c8d29b875e1e0691dc43267f0406e9ddfea329c76ab74159ffff0f1e000d8425", "id": "msg_id"} So now to get a balance. The thing with electrumx server is that you need a scripthash to call any address functions. But I don't have that. All I have is a bash terminal and a Denarius address. So what do I do? Line by line dissect how people create an address and then work backwards to the scripthash. I need this image, but down to up. source: https://learnmeabitcoin.com/guide/p2pkh This is not so easy as I can't find anyone doing this backwards in bash. But I found lots of posts how to go forwards. Here is how to get it before converting to big endian. #!/bin/bash . denarius.sh #DUP HASH160 begin="76A914" echo $begin #EQUALVERIFY CHECKSIG end="88AC" echo $end echo "decodeBase58" decoded="$(decodeBase58 DCMRvR6MUppPgP8vrMKuni4FL5de8SjicG)" echo "Remove 00 bytes" echo ${decoded#??} removefront=$(echo "${decoded#??}") echo $removefront echo "Remove checksum" removeback=$(echo "${removefront%????????}") echo "95 characters base58" echo $removeback echo "now what?" echo $begin$removeback$end echo "convert to scripthash" echo -n $begin$removeback$end | xxd -r -p | sha256sum | cut -d' ' -f1 scripthash=$(echo -n $begin$removeback$end | xxd -r -p | sha256sum | cut -d' ' -f1) echo $scripthash Will also need grondilu bitcoin-bash-tools. https://github.com/grondilu/bitcoin-bash-tools Get the bitcoin.sh and throw that into the same directory you are going to test this out in, for obvious reasons I renamed bitcoin.sh to denarius.sh. First I am assuming a standard P2PKH address with OP_DUP OP_HASH160 hashedpublickey OP_EQUALVERIFY OP_CHECKSIG #DUP HASH160 begin="76A914" echo $begin #EQUALVERIFY CHECKSIG end="88AC" echo $end Then decodeBase58 of our Denarius address, works on bitcoin obviously. echo "decodeBase58" decoded="$(decodeBase58 DCMRvR6MUppPgP8vrMKuni4FL5de8SjicG)" Then remove 00 bytes from the front of this. The #?? removes 2 characters from the left of a string. echo "Remove 00 bytes" echo ${decoded#??} removefront=$(echo "${decoded#??}") echo $removefront Now remove the checksum. The %???????? removes 8 characters from the right of the string. echo "Remove checksum" removeback=$(echo "${removefront%????????}") echo "95 characters base58" echo $removeback Now what? How to convert to scripthash? We can echo the the uncompressed public key, kind of. echo "now what?" echo $begin$removeback$end echo "convert to scripthash" sha256 this in proper format, using xxd and then sha256 that. echo -n $begin$removeback$end | xxd -r -p | sha256sum | cut -d' ' -f1 scripthash=$(echo -n $begin$removeback$end | xxd -r -p | sha256sum | cut -d' ' -f1) echo $scripthash At the end from that address I get. 3735fe8239322122222f8a8f42d9b6545bc517e2086116e57953fdc9cc7f6115 For now I have a separate function I found to convert to big endian, since electrumx server wants it this way. source: https://electrumx.readthedocs.io/en/latest/protocol-basics.html#script-hashes #!/bin/bash #echo 6191c3b590bfcfa0475e877c302da1e323497acf3b42c08d8fa28e364edf018b | ./bigendian.sh #8b01df4e368ea28f8dc0423bcf7a4923e3a12d307c875e47a0cfbf90b5c39161 # check stdin if [ -t 0 ]; then exit; fi v=`cat /dev/stdin` i=${#v} while [ $i -gt 0 ] do i=$[$i-2] echo -n ${v:$i:2} done echo I run this like echo 3735fe8239322122222f8a8f42d9b6545bc517e2086116e57953fdc9cc7f6115 | ./bigendian.sh and get the scripthash electrumx server wants 15617fccc9fd5379e5166108e217c55b54b6d9428f8a2f222221323982fe3537 Like bash magic we can use that scripthash to talk to the electrumx server to get a balance. #!/bin/bash echo "get balance" (echo '{"method" : "blockchain.scripthash.get_balance", "params": ["15617fccc9fd5379e5166108e217c55b54b6d9428f8a2f222221323982fe3537"], "id": "msg_id"}'; sleep 1) | ncat --ssl electrumx1.denarius.pro 50002 And we get the balance. get balance {"jsonrpc": "2.0", "result": {"confirmed": 125740854, "unconfirmed": 0}, "id": "msg_id"} And a large step forward in creating a basic terminal wallet for any device that can use bash, ncat and has port 50002 open. 3 Quote Link to comment Share on other sites More sharing options...
manosv Posted May 15, 2020 Report Share Posted May 15, 2020 You are becoming the Bash god !!!. Very nice work. 2 1 Quote Link to comment Share on other sites More sharing options...
Ghost Posted May 24, 2020 Report Share Posted May 24, 2020 Really awesome job @buzzkillb - I hope to see more of things like this! The powerful potential of ElectrumX is awesome. 1 Quote Founder of BlockForums.org - PM me for any help - Join our Discord Server: https://discord.gg/UPpQy3n Link to comment Share on other sites More sharing options...
C5YA Posted May 24, 2020 Report Share Posted May 24, 2020 very nice work and thanks thanks again for the education. 2 Quote Link to comment Share on other sites More sharing options...
buzzkillb Posted July 18, 2020 Author Report Share Posted July 18, 2020 How I did this for python3, trying to learn python. import os import ecdsa import hashlib import base58 import binascii import codecs import struct denariusAddress= "DDD6SzCwXSEcTPHmNwEQX6xbUs2Rf3svNX" print("D Addy:" + denariusAddress) #base58decode denarius address addrToBytes = base58.b58decode(denariusAddress) print(addrToBytes) decodedToHex = addrToBytes.hex() print(decodedToHex) #remove prefix removeZeroBytes = 2 decodedToHexnoPrefix = decodedToHex[removeZeroBytes:] print(decodedToHexnoPrefix) #remove checksum removeChecksum = 40 decodedNoPrefixnoChecksum = decodedToHexnoPrefix[:removeChecksum] print(decodedNoPrefixnoChecksum) #Add OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG opDup = "76" opHash160 = "A9" opsBuffer = "14" opEqualVerify = "88" opChecksig = "AC" preparedtoHash = opDup + opHash160 + opsBuffer + decodedNoPrefixnoChecksum + opEqualVerify + opChecksig print(preparedtoHash.upper()) hashedKey = codecs.decode(preparedtoHash.upper(), 'hex') s = hashlib.new('sha256', hashedKey).digest() r = hashlib.new('ripemd160', s).digest() convertBigEndian = (codecs.encode(s, 'hex').decode("utf-8")) print(convertBigEndian) scriptHash = codecs.encode(codecs.decode(convertBigEndian, 'hex')[::-1], 'hex').decode() print(scriptHash) then throw that last value into something like this import socket import json from time import sleep port = 50001 host = 'electrumx1.denarius.pro' content = { "method": "blockchain.scripthash.get_balance", "params": ["1fbfac24c0ed8084288904ba34eb891f1feaec146065cf7c08209167235ca3dc"], "id": 0 } def electrumx(host, port, content): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, port)) sock.sendall(json.dumps(content).encode('utf-8')+b'\n') sleep(0.5) sock.shutdown(socket.SHUT_WR) res = "" while True: data = sock.recv(1024) if (not data): break res += data.decode() print(res) sock.close() electrumx(host, port, content) Quote Link to comment Share on other sites More sharing options...
buzzkillb Posted July 21, 2020 Author Report Share Posted July 21, 2020 Still learning python 3, this will sign a bitcoin tx based on a sample I found. Want to convert this next to sign a Denarius tx, and work out some of the forced variables I put in here. The idea is to not pull any bitcoin modules in. import struct import base58 import hashlib import ecdsa import codecs import binascii from hashlib import sha256 #References #https://klmoney.wordpress.com/bitcoin-dissecting-transactions-part-2-building-a-transaction-by-hand/ #http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html #https://github.com/shirriff/bitcoin-code #https://bitcoin.stackexchange.com/questions/3374/how-to-redeem-a-basic-tx ################################################################################### #Needs work on checking script lengths to check bytes and throw them in, instead of manually putting in def double_hash(num): first_hash = hashlib.sha256(binascii.unhexlify(num)).hexdigest() #print(first_hash, "first hash") second_hash = hashlib.sha256(binascii.unhexlify(first_hash)).hexdigest() return second_hash #set this up, construction message to be signed #Add four-byte version field version = "01000000" #One-byte variant specifying the number of inputs number_of_inputs = "01" #32-byte hash of the transaction from which we want to redeem an output previous_tx_hash = "416e9b4555180aaa0c417067a46607bc58c96f0131b2f41f7d0fb665eab03a7e" #vout 1 would be -> 01000000 #The index of the previous Output must be a 4 byte entry in little endian format. v_out = "00000000" #one-byte variant which denotes the length of the scriptSig (0x19 = 25 bytes) script_length = "19" #temporary scriptSig which, again, is the scriptPubKey of the output we want to redeem scriptpubkey = "76a91499b1ebcfc11a13df5161aba8160460fe1601d54188ac" #four-byte field denoting the sequence sequence = "ffffffff" #one-byte varint containing the number of outputs in our new transaction number_of_outputs = "01" #8-byte field (64 bit integer) containing the amount we want to redeem from the specified output #100000 denariis, or example 0.00100000 amount_to_send = 20000 amount_to_send_bytes = (hex(amount_to_send)[2:].zfill(16)) print(amount_to_send_bytes) amount_to_send_bytes_flipped = codecs.encode(codecs.decode(amount_to_send_bytes, 'hex')[::-1], 'hex').decode() print(amount_to_send_bytes_flipped) #The Script Length field is Ox19 (25 bytes) which is the length in bytes of the scriptPubKey above script_length = "19" print(script_length) #output script script_pub_key = "76a914e81d742e2c3c7acd4c29de090fc2c4d4120b2bf888ac" #four-byte "lock time" field lock_time = "00000000" #For normal transactions, the SigHash code will be 1 for SIGHASH_ALL. #This signature hash type means that the signature includes all the Inputs and Outputs minus the scriptSig. #The SigHash code is padded to four bytes and entered in little endian format sighash_code = "01000000" complete_tx_message = ( version + number_of_inputs + previous_tx_hash + v_out + script_length + scriptpubkey + sequence + number_of_outputs + amount_to_send_bytes_flipped + script_length + script_pub_key + lock_time + sighash_code ) print("Complete rawtx to Sign") print(complete_tx_message) #double hash complete_tx_message - 2 methods both in hex double_hash_complete_tx_message = double_hash(complete_tx_message) print("double hashed") print(double_hash_complete_tx_message) #or header_complete_tx_message = bytes.fromhex(complete_tx_message) print("double hashed another method") print(sha256(sha256(header_complete_tx_message).digest()).digest().hex()) #derive signature #The private key associated with the previous Output’s address, in hex format privkey = '3cd0560f5b27591916c643a0b7aa69d03839380a738d2e912990dcc573715d2c' print("private key") print(privkey) privateKey_bytes = bytes.fromhex(privkey) print("private key in bytes") print(privateKey_bytes) hash_2 = bytes.fromhex(double_hash_complete_tx_message) print("double hashed in bytes") print(hash_2) sk = ecdsa.SigningKey.from_string(privateKey_bytes, curve=ecdsa.SECP256k1) print(sk) verifying_key = sk.get_verifying_key() print(verifying_key) public_key = bytes.fromhex("04") + verifying_key.to_string() print("public key") print(public_key) signature = sk.sign_digest(hash_2, sigencode = ecdsa.util.sigencode_der_canonize) print("Signature") print(signature) signer_convert = binascii.hexlify(signature) print("signer in hex bytes") print(signer_convert) signer = (signer_convert.decode("utf-8")) print("Signer in Hex") print(signer) #The PUSHDATA opcode 0x47 (or decimal 71) is the number of bytes that will be pushed onto the stack. This includes the one byte sigHash code. pushdata_op = "47" sighash_code = "01" pushdata_op_second = "21" #Recent transactions use compressed public keys as in this example. #Compressed public keys are 32 bytes with a one byte prefix of 02 or 03. #Uncompressed public keys are 64 bytes plus a prefix of 04. pubkey_compressed = "03bf350d2821375158a608b51e3e898e507fe47f2d2e8c774de4a9a7edecf74eda" ####GET THIS SOMEHOW sighash_script_length = "6a" scriptsig = ( pushdata_op + signer + sighash_code + pushdata_op_second + pubkey_compressed ) print("scriptSig") print(scriptsig) prepare_to_broadcast = ( version + number_of_inputs + previous_tx_hash + v_out + sighash_script_length + scriptsig + sequence + number_of_outputs + amount_to_send_bytes_flipped + script_length + script_pub_key + lock_time ) print("Broadcast") print(prepare_to_broadcast) print(version) print(number_of_inputs) print(previous_tx_hash) print(v_out) print(sighash_script_length) print(scriptsig) print(sequence) print(number_of_outputs) print(amount_to_send_bytes_flipped) print(script_length) print(script_pub_key) print(lock_time) Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.