Jump to content
buzzkillb

Bash Client to Electrumx Server: Welcome to the Beginning

Recommended Posts

304

459 posts
1183 BF$

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

image.png.ebff74b830d1ea33a3beb830d8b8d3cb.png

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

image.thumb.png.4117e19d11f34076761567b2f2af354d.png

#DUP HASH160
begin="76A914"
echo $begin
#EQUALVERIFY CHECKSIG
end="88AC"
echo $end

Then decodeBase58 of our Denarius address, works on bitcoin obviously.

image.png.34861a731fa03c35069e0f28f8a47bbb.png

echo "decodeBase58"
decoded="$(decodeBase58 DCMRvR6MUppPgP8vrMKuni4FL5de8SjicG)"

Then remove 00 bytes from the front of this. The #?? removes 2 characters from the left of a string.

image.png.5bb321862717851130ba60e8a893659f.png

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.

image.png.fd9636e8ef934953cd178abcc8a021d2.png

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.

  • Like 3

If you enjoy my content please consider donating to the Denarius creator - https://www.patreon.com/carsenk
Join Denarius Discord - https://discord.gg/JQEmXwb

Share this post


Link to post
Share on other sites
304

459 posts
1183 BF$

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)

 


If you enjoy my content please consider donating to the Denarius creator - https://www.patreon.com/carsenk
Join Denarius Discord - https://discord.gg/JQEmXwb

Share this post


Link to post
Share on other sites
304

459 posts
1183 BF$

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)

 


If you enjoy my content please consider donating to the Denarius creator - https://www.patreon.com/carsenk
Join Denarius Discord - https://discord.gg/JQEmXwb

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...