Skip to content

Instantly share code, notes, and snippets.

@buzzkillb
Created July 19, 2020 07:06
Show Gist options
  • Save buzzkillb/9450011b360705eed39a59a31c8736e0 to your computer and use it in GitHub Desktop.
Save buzzkillb/9450011b360705eed39a59a31c8736e0 to your computer and use it in GitHub Desktop.
import struct
import base58
import hashlib
import ecdsa
import codecs
import binascii
from hashlib import sha256
#For Python 3, tested on stock ubuntu 20.04
#pip3 install each import above
#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
###################################################################################
#A sample to guide someone how to further this along into much more
#Needs work on checking script lengths to check bytes and throw them in, instead of manually putting in
#broadcast still randomly adds 1 byte sometimes to broadcast tx at end
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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment