-
-
Save ymgve/33de2b338f7cfd1b669603c75fdb6f37 to your computer and use it in GitHub Desktop.
import hashlib, os, struct, sys, socket, time | |
N = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fL | |
R = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141L | |
A = 0L | |
B = 7L | |
gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798L | |
gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L | |
b58ab = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" | |
def b58csum(s): | |
return hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4] | |
def b58decode(s, checksum=True): | |
idx = 0 | |
while s[idx] == "1": | |
idx += 1 | |
n = 0 | |
for c in s[idx:]: | |
n = n * 58 + b58ab.index(c) | |
res = long2byte(n) | |
res = idx * "\x00" + res | |
if checksum: | |
res, cs = res[:-4], res[-4:] | |
assert cs == b58csum(res), "base58 checksum failed" | |
return res | |
def b58encode(s, checksum=True): | |
if checksum: | |
s += b58csum(s) | |
idx = 0 | |
while s[idx] == "\x00": | |
idx += 1 | |
n = byte2long(s) | |
res = "" | |
while n > 0: | |
res = b58ab[n % 58] + res | |
n /= 58 | |
return "1" * idx + res | |
def byte2long(s): | |
res = 0 | |
for c in s: | |
res = (res << 8) | ord(c) | |
return res | |
def long2byte(n, sz=None): | |
res = "" | |
while n > 0: | |
res = chr(n & 0xff) + res | |
n >>= 8 | |
if sz is not None: | |
res = res.rjust(sz, "\x00") | |
return res | |
def modinv(x, n): | |
return pow(x, n-2, n) | |
class Point(object): | |
def __init__(self, x, y, inf=False): | |
self.x = x | |
self.y = y | |
self.inf = inf | |
def curve_add(p, q, N): | |
if p.inf: | |
return q | |
if q.inf: | |
return p | |
if p.x == q.x: | |
if p.y == q.y: | |
d1 = (3 * p.x * p.x) % N | |
d2 = (2 * p.y) % N | |
else: | |
return Point(-1, -1, True) | |
else: | |
d1 = (q.y - p.y) % N | |
d2 = (q.x - p.x) % N | |
d2i = modinv(d2, N) | |
d = (d1 * d2i) % N | |
resx = (d * d - p.x - q.x) % N | |
resy = (d * (p.x - resx) - p.y) % N | |
return Point(resx, resy) | |
def scalar_mul(scalar, p, N): | |
t = p | |
res = None | |
while scalar != 0: | |
if scalar & 1 == 1: | |
if res is None: | |
res = t | |
else: | |
res = curve_add(res, t, N) | |
t = curve_add(t, t, N) | |
scalar = scalar >> 1 | |
return res | |
def der_signature(r, s): | |
r = long2byte(r) | |
if ord(r[0]) >= 0x80: | |
r = "\x00" + r | |
s = long2byte(s) | |
if ord(s[0]) >= 0x80: | |
s = "\x00" + s | |
res = "\x02" + chr(len(r)) + r + "\x02" + chr(len(s)) + s | |
return "\x30" + chr(len(res)) + res | |
def signdata(privkey, data): | |
h = hashlib.sha256(hashlib.sha256(data).digest()).digest() | |
z = byte2long(h) | |
r, s = sign(privkey, z) | |
return der_signature(r, s) | |
def sign(privkey, z): | |
while True: | |
k = byte2long(os.urandom(256 / 8)) | |
if k >= 1 and k < R: | |
break | |
p = scalar_mul(k, Point(gx, gy), N) | |
r = p.x % R | |
assert r != 0 | |
ki = modinv(k, R) | |
s = (ki * (z + r * privkey)) % R | |
assert s != 0 | |
if s > (R / 2): | |
s = R - s | |
return r, s | |
def serializepubkey(p, compressed): | |
if compressed: | |
if p.y & 1 == 1: | |
return "\x03" + long2byte(p.x, 32) | |
else: | |
return "\x02" + long2byte(p.x, 32) | |
else: | |
return "\x04" + long2byte(p.x, 32) + long2byte(p.y, 32) | |
def pubkey2h160(p, compressed): | |
s = serializepubkey(p, compressed) | |
s = hashlib.sha256(s).digest() | |
h = hashlib.new("ripemd160") | |
h.update(s) | |
return h.digest() | |
def pubkey2addr(p, compressed): | |
s = pubkey2h160(p, compressed) | |
return b58encode("\x00" + s) | |
def wif2privkey(s): | |
s = b58decode(s) | |
assert s.startswith("\x80") | |
if len(s) == 34 and s[-1] == "\x01": | |
return byte2long(s[1:33]), 1 | |
assert len(s) == 33 | |
return byte2long(s[1:33]), 0 | |
def recv_all(s, length): | |
ret = "" | |
while len(ret) < length: | |
temp = s.recv(length - len(ret)) | |
if len(temp) == 0: | |
raise Exception("Connection reset!") | |
ret += temp | |
return ret | |
class Client(object): | |
def __init__(self, address): | |
self.address = address | |
def connect(self): | |
self.sc = socket.create_connection(self.address) | |
print "connected" | |
def send(self, cmd, msg): | |
magic = struct.pack("<L", 0xf9bc0511) | |
wrapper = magic + cmd.ljust(12, "\x00") + struct.pack("<L", len(msg)) + hashlib.sha256(hashlib.sha256(msg).digest()).digest()[0:4] + msg | |
self.sc.sendall(wrapper) | |
print "sent", repr(cmd) | |
def recv_msg(self): | |
header = recv_all(self.sc, 24) | |
if len(header) != 24: | |
print "INVALID HEADER LENGTH", repr(head) | |
exit() | |
cmd = header[4:16].rstrip("\x00") | |
payloadlen = struct.unpack("<I", header[16:20])[0] | |
payload = recv_all(self.sc, payloadlen) | |
return cmd, payload | |
def maketx(sourcetx, sourceidx, wifkey, targetaddr, numsatoshi, originalsatoshi, compressedtype): | |
sourceprivkey, compressed = wif2privkey(wifkey) | |
if compressedtype in (0, 1): | |
compressed = compressedtype | |
sourcepubkey = scalar_mul(sourceprivkey, Point(gx, gy), N) | |
sourceh160 = pubkey2h160(sourcepubkey, compressed) | |
targeth160 = b58decode(targetaddr)[1:] | |
s = struct.pack("<I", 2) | |
s += chr(1) # one input | |
s += sourcetx.decode("hex")[::-1] # source TX is in little endian order | |
s += struct.pack("<I", sourceidx) # source ID too | |
s += "[[SCRIPT]]" # placeholder for script | |
s += "\xff\xff\xff\xff" # no locktime | |
s += chr(1) # one output | |
s += struct.pack("<Q", numsatoshi) # hope you got this number correctly! | |
s += "\x19\x76\xa9\x14" + targeth160 + "\x88\xac" # standard P2PKH script | |
s += "\x00\x00\x00\x00" # no locktime | |
to_sign = "" | |
to_sign += struct.pack("<I", 2) | |
to_sign += hashlib.sha256(hashlib.sha256(sourcetx.decode("hex")[::-1] + struct.pack("<I", sourceidx)).digest()).digest() | |
to_sign += hashlib.sha256(hashlib.sha256("\xff\xff\xff\xff" ).digest()).digest() | |
to_sign += sourcetx.decode("hex")[::-1] + struct.pack("<I", sourceidx) | |
to_sign += "\x19\x76\xa9\x14" + sourceh160 + "\x88\xac" | |
to_sign += struct.pack("<Q", originalsatoshi) | |
to_sign += "\xff\xff\xff\xff" | |
to_sign += hashlib.sha256(hashlib.sha256(struct.pack("<Q", numsatoshi) + "\x19\x76\xa9\x14" + targeth160 + "\x88\xac").digest()).digest() | |
to_sign += "\x00\x00\x00\x00" | |
to_sign += struct.pack("<I", 0x11) | |
signature = signdata(sourceprivkey, to_sign) + "\x11" | |
serpubkey = serializepubkey(sourcepubkey, compressed) | |
script = chr(len(signature)) + signature + chr(len(serpubkey)) + serpubkey | |
script = chr(len(script)) + script | |
tx = s.replace("[[SCRIPT]]", script) | |
return tx, pubkey2addr(sourcepubkey, compressed) | |
if len(sys.argv) != 8: | |
print "Usage: bcxtest.py <source TXID> <source index> <source WIF private key> <target address> <number of satoshis> <number of satoshis at source output> <compressed key type>" | |
print "example: bcxtest.py 4adc427d330497992710feaa32f85c389ef5106f74e7006878bd14b54500dfff 0 5K2YUVmWfxbmvsNxCsfvArXdGXm7d5DC9pn4yD75k2UaSYgkXTh 1aa5cmqmvQq8YQTEqcTmW7dfBNuFwgdCD 1853 3053 -1" | |
print "compressed key type - use if the 'from' address isn't what you expect it to be" | |
print " -1 parse from WIF private key" | |
print " 0 force uncompressed" | |
print " 1 force compressed" | |
else: | |
sourcetx = sys.argv[1] | |
sourceidx = int(sys.argv[2]) | |
wifkey = sys.argv[3] | |
targetaddr = sys.argv[4] | |
numsatoshi = int(sys.argv[5]) | |
originalsatoshi = int(sys.argv[6]) | |
compressedtype = int(sys.argv[7]) | |
tx, sourceaddr = maketx(sourcetx, sourceidx, wifkey, targetaddr, numsatoshi, originalsatoshi, compressedtype) | |
print "YOU ARE ABOUT TO SEND %.8f BTC (equivalent to %.4f BCX) FROM %s TO %s!" % (numsatoshi / 100000000.0, numsatoshi / 10000.0, sourceaddr, targetaddr) | |
print "!!!EVERYTHING ELSE WILL BE EATEN UP AS FEES! CONTINUE AT YOUR OWN RISK!!!" | |
print "Write 'I understand' to continue" | |
answer = raw_input() | |
assert answer == "I understand" | |
txhash = hashlib.sha256(hashlib.sha256(tx).digest()).digest()[::-1] | |
print "generated transaction", txhash.encode("hex") | |
client = Client(("192.169.227.48", 9003)) | |
client.connect() | |
versionno = 70015 | |
services = 0 | |
localaddr = "\x00" * 8 + "00000000000000000000FFFF".decode("hex") + "\x00" * 6 | |
nonce = os.urandom(8) | |
user_agent = "Scraper" | |
msg = struct.pack("<IQQ", versionno, services, int(time.time())) + localaddr + localaddr + nonce + chr(len(user_agent)) + user_agent + struct.pack("<IB", 0, 0) | |
client.send("version", msg) | |
while True: | |
cmd, payload = client.recv_msg() | |
if cmd == "version": | |
print repr(payload) | |
client.send("verack", "") | |
elif cmd == "ping": | |
client.send("pong", payload) | |
client.send("inv", "\x01" + struct.pack("<I", 1) + txhash) | |
elif cmd == "getdata": | |
if payload == "\x01\x01\x00\x00\x00" + txhash: | |
print "sending txhash, if there is no error response everything probably went well" | |
client.send("tx", tx) | |
else: | |
print repr(cmd) | |
print repr(payload) | |
Great job!
I made a small script to extract the data for your script.
Just edit the script, add your addresses and run it.
Ex:
$ ./fork.py
Valid transactions for 1HTmbaeSZn7faPjxcSeEHJoxgBGMxJYYem: 1
Valid transactions for 1FQ3eFGW4Kek7vFEFqb5e5U9FnH7vzn3xW: 2python bcxtest.py ba3047b53972f93e6ca70f45797fb91fde5114c978f8b1d7e3e25cfbc4d03d12 0 PRIV_KEY_1HTmbaeSZn7faPjxcSeEHJoxgBGMxJYYem YOUR_BCX_ADDRESS 70478906 70479906 -1
python bcxtest.py 069a201c44876fb0cdf37450ead62fce5a2b2f5b49c6cc3d1f0aa436ae7b873a 1 PRIV_KEY_1FQ3eFGW4Kek7vFEFqb5e5U9FnH7vzn3xW YOUR_BCX_ADDRESS 55558079 55559079 -1
python bcxtest.py a0a907d1a57b109a089c7ef26bbe66300f5f63d4446de45ccf7cd62bd2e2ac6f 0 PRIV_KEY_1FQ3eFGW4Kek7vFEFqb5e5U9FnH7vzn3xW YOUR_BCX_ADDRESS 47991234 47992234 -1
Then replace your BCX address and the private key for each tx.
I tested with all my addresses and it worked perfectly. Let me know if there are any bugs.
The script:
#!/usr/bin/env python
import urllib2
import json
block = 498888
# You can either insert your BCX address below or find/replace after running the script
addr_bcx = "YOUR_BCX_ADDRESS"
# Insert your BTC addresses below, one per line
addresses = """
1HTmbaeSZn7faPjxcSeEHJoxgBGMxJYYem
1FQ3eFGW4Kek7vFEFqb5e5U9FnH7vzn3xW
"""
addr_list = addresses.strip().split("\n")
valid = []
for addr in addr_list:
a = urllib2.urlopen("https://blockchain.info/rawaddr/" + addr).read()
txs = json.loads(a)["txs"]
txs_before_fork = [tx for tx in txs if tx.has_key("block_height") and tx["block_height"] <= block]
valid_txs = txs_before_fork[:]
for txid in valid_txs[:]:
for tx in txs_before_fork:
for input_tx in tx["inputs"]:
if input_tx["prev_out"]["tx_index"] == txid["tx_index"] and input_tx["prev_out"]["addr"] == addr:
try:
valid_txs.remove(txid)
except ValueError:
pass # Was probably removed before. Skipping.
print "Valid transactions for " + addr + ": " + str(len(valid_txs))
for tx in valid_txs:
for tx_out in tx["out"]:
if addr == tx_out["addr"]:
valid.append([ tx["hash"], str(tx_out["n"]), "PRIV_KEY_" + addr, addr_bcx, str(tx_out["value"] - 1000), str(tx_out["value"]), "-1" ])
break
print
for v in valid:
print "python bcxtest.py " + " ".join(v)
how to donate to your BTC address