Created
July 19, 2020 07:06
-
-
Save buzzkillb/9450011b360705eed39a59a31c8736e0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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