Skip to content

Instantly share code, notes, and snippets.

@cvasqxz
Last active December 12, 2018 04:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cvasqxz/fe5f5bb594fa5623846948f4bfc26f96 to your computer and use it in GitHub Desktop.
Save cvasqxz/fe5f5bb594fa5623846948f4bfc26f96 to your computer and use it in GitHub Desktop.
Generación de dirección MultiSig N-of-M
from binascii import a2b_hex
from hashlib import new
from collections import deque
from os import urandom
from argparse import ArgumentParser
# opcode
OP_CHECKMULTISIG = 'ae'
# Arguments
p = ArgumentParser(description='Creación de dirección MultiSig N-of-M')
p.add_argument('N', help='N: Firmas necesarias', type=int)
p.add_argument('M', help='M: Número de llaves privadas a generar', type=int)
args = p.parse_args()
# SHA256(SHA256(string))
def doubleSHA256(s):
return new('sha256', new('sha256', a2b_hex(s)).digest()).hexdigest()
# RIPEMD160(SHA256(string))
def hash160(s):
return new('ripemd160', new('sha256', a2b_hex(s)).digest()).hexdigest()
# https://github.com/joeblackwaslike/base58chec
def b58check(val):
charset = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
output = b''
p, acc = 1, 0
base = len(charset)
if base == 58:
pad_len = len(val)
val = val.lstrip(b'\0')
pad_len -= len(val)
for char in deque(reversed(val)):
acc += p * char
p = p << 8
while acc:
acc, idx = divmod(acc, base)
output = charset[idx:idx+1] + output
prefix = bytes([charset[0]]) * pad_len
return prefix + output
# https://bitcoin.stackexchange.com/a/59806
def secp256k1(s):
G = (55066263022277343669578718895168534326250603453777594175500187360389116729240,
32670510020758816978083085130507043184471273380659243275938904335757337482424)
N = 115792089237316195423570985008687907852837564279074904382605163141518161494337
P = 2**256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 - 1
sk = int(s, 16)
def add(p, q):
px, py = p
qx, qy = q
if p == q:
lam = (3 * px * px) * pow(2 * py, P - 2, P)
else:
lam = (qy - py) * pow(qx - px, P - 2, P)
rx = lam**2 - px - qx
ry = lam * (px - rx) - py
return rx % P, ry % P
ret = None
for i in range(256):
if sk & (1 << i):
if ret is None:
ret = G
else:
ret = add(ret, G)
G = add(G, G)
return '04' + '{:064x}'.format(ret[0]) + '{:064x}'.format(ret[1])
# create opcode OP_1 to OP_16
def get_OP(n):
if 0 < n <= 16:
return hex(80 + n)[2:]
# create opcode OP_PUSHDATA
def get_hexlen(string):
# fixed-length hex(len(string))
return '{:02x}'.format(int(len(string)/2))
# P2SH address
def get_P2SH_addr(s):
# Chaucha SCRIPT_PREFIX = 50 (0x32)
p2sh_prefix = '32'
# base58check(prefix + sha160(redeemscript) + checksum)
scripthash = hash160(s)
checksum = doubleSHA256(p2sh_prefix + scripthash)[:8]
p2sh_addr = b58check(a2b_hex(p2sh_prefix + scripthash + checksum))
return p2sh_addr.decode('utf-8')
def main():
# N of M multisig Address
N = args.N
M = args.M
print('> GENERATING %s-TO-%s MULTISIG ADDRESS !!' % (N, M))
# create private keys
privkeys = []
for i in range(M):
privkey = new('sha256', urandom(N*M*i)).hexdigest()
privkeys.append(privkey)
# get P2SH redeem script
# (OP_N + OP_PUSHDATA + <pubkeys> + OP_M + OP_CHECKMULTISIG)
redeemScript = get_OP(N)
for i in privkeys:
pubkey = secp256k1(i)
redeemScript += get_hexlen(pubkey) + pubkey
redeemScript += get_OP(M) + OP_CHECKMULTISIG
# get P2SH address
addr = get_P2SH_addr(redeemScript)
# logger
print('> MULTISIG ADDRESS: %s' % addr)
print('> REDEEM SCRIPT: %s' % redeemScript)
for i in range(len(privkeys)):
print('> PRIVKEY #%i: %s' % (i+1, privkeys[i]))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment