def import_json( self, json_text ): try: importTxInfo = json.loads( json_text ) except: raise RuntimeError( "Invalid Input", "The input doesn't parse to valid json." ) try: if 'tx' not in importTxInfo: raise ValueError( "No transaction found in json" ) redeemScriptCanidate = None try: self.tx = bitcoin.deserialize( importTxInfo['tx'] ) sigScript = self.tx['ins'][0]['script'] if sigScript != "": sigScript = bitcoin.deserialize_script( sigScript ) redeemScriptCanidate = sigScript[-1] except Exception as e: raise ValueError( "tx could not be deserialized:" + str( e ) ) if 'input' in importTxInfo and 'redeemScript' in importTxInfo['input']: if redeemScriptCanidate is not None and redeemScriptCanidate.lower() != importTxInfo['input']['redeemScript'].lower(): raise ValueError( "redeemScript given in info doesn't match redeemScript from partial signature" ) redeemScriptCanidate = importTxInfo['input']['redeemScript'] if redeemScriptCanidate is not None: self.pubs, self.neededSigs = KeyHelper.parse_redeem_script( redeemScriptCanidate ) self.redeemScript = redeemScriptCanidate else: raise ValueError( "No redeemScript can be located." ) except ValueError as e: raise RuntimeError( "Invalid Input", str( e ) )
def tx_script_to_asm( script_hex ): """ Decode a script into assembler """ if len(script_hex) == 0: return "" try: script_array = bitcoin.deserialize_script( script_hex ) except: log.error("Failed to convert '%s' to assembler" % script_hex) raise script_tokens = [] for token in script_array: if token is None: token = 0 token_name = None if type(token) in [int,long]: token_name = OPCODE_NAMES.get(token, None) if token_name is None: token_name = str(token) else: token_name = token script_tokens.append(token_name) return " ".join(script_tokens)
def extract_sigs( self, tx ): sigScript = tx['ins'][0]['script'] if sigScript == "": return [] sigScript = bitcoin.deserialize_script( sigScript ) return sigScript[1:-1]
def add_signature(self, nick, sigb64): if nick not in self.nonrespondants: debug('add_signature => nick=' + nick + ' not in nonrespondants ' + str(self.nonrespondants)) return sig = base64.b64decode(sigb64).encode('hex') inserted_sig = False txhex = btc.serialize(self.latest_tx) #batch retrieval of utxo data utxo = {} ctr = 0 for index, ins in enumerate(self.latest_tx['ins']): utxo_for_checking = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) if ins['script'] != '' or utxo_for_checking in self.input_utxos.keys(): continue utxo[ctr] = [index, utxo_for_checking] ctr += 1 utxo_data = common.bc_interface.query_utxo_set([x[1] for x in utxo.values()]) #insert signatures for i,u in utxo.iteritems(): if utxo_data[i] == None: continue sig_good = btc.verify_tx_input(txhex, u[0], utxo_data[i]['script'], *btc.deserialize_script(sig)) if sig_good: debug('found good sig at index=%d' % (u[0])) self.latest_tx['ins'][u[0]]['script'] = sig inserted_sig = True #check if maker has sent everything possible self.utxos[nick].remove(u[1]) if len(self.utxos[nick]) == 0: debug('nick = ' + nick + ' sent all sigs, removing from nonrespondant list') self.nonrespondants.remove(nick) break if not inserted_sig: debug('signature did not match anything in the tx') #TODO what if the signature doesnt match anything # nothing really to do except drop it, carry on and wonder why the # other guy sent a failed signature tx_signed = True for ins in self.latest_tx['ins']: if ins['script'] == '': tx_signed = False if not tx_signed: return self.end_timeout_thread = True self.all_responded = True with self.timeout_lock: self.timeout_lock.notify() debug('all makers have sent their signatures') for index, ins in enumerate(self.latest_tx['ins']): #remove placeholders if ins['script'] == 'deadbeef': ins['script'] = '' if self.finishcallback != None: self.finishcallback(self)
def decode(cls, vout, n, network): hex_script = vout['script'] script = [0 if x == None else x for x in deserialize_script(hex_script)] data = {'hs': hex_script, 'd': vout, 'n': n, 's': script, 'p': {'pub': 0x00 if network == "main" else 0x6F, 'p2sh': 0x05 if network == "main" else 0xC4}} return VOUTDecoder.__decode(data)
def parse_redeem_script( script ): try: elements = bitcoin.deserialize_script( script ) if elements[-1] != 174: raise ValueError( "no OP_CHECKMULTISIG found in redeemScript" ) if elements[-2] < elements[0]: raise ValueError( "redeemscript asks for more sigs than supplies keys" ) return elements[1:(1+elements[-2])], elements[0] except Exception as e: raise ValueError( "redeemScript not in valid format: " + str(e) )
def dataReceived(self, data): m = deserialize(data) if m["message"]["command"] == "verack": """Complete the handshake if we received both version and verack""" self.timeouts["verack"].cancel() del self.timeouts["verack"] if "version" not in self.timeouts: self.on_handshake_complete() elif m["message"]["command"] == "version": """Send the verack back""" # TODO: make sure this node uses NODE_NETWORK (and maybe NODE_BLOOM in the future) self.timeouts["version"].cancel() del self.timeouts["version"] self.transport.write(serialize(message(verack(), self.params))) if "verack" not in self.timeouts: self.on_handshake_complete() elif m["message"]["command"] == "inv": """Run through our callbacks to see if we are waiting on any of these inventory items""" inventory = deserialize(m["message"]["payload"], "inv") for item in inventory["inv"]: if item[1] in self.callbacks: self.callbacks[item[1]](item[1]) del self.callbacks[item[1]] elif item[0] == "TX": self.send_message(message(get_data(item[0], item[1]), self.params)) print "Peer %s:%s announced new %s %s" % (self.transport.getPeer().host, self.transport.getPeer().port, item[0], item[1]) elif m["message"]["command"] == "getdata": """Serve the data from inventory if we have it""" data_request = deserialize(m["message"]["payload"], "getdata") for item in data_request["getdata"]: if item[1] in self.inventory and item[0] == "TX": transaction = tx(self.inventory[item[1]]) self.send_message(message(transaction, self.params)) elif m["message"]["command"] == "tx": """Parse to check the script_pubkey data element against our subscriptions""" t = deserialize(m["message"]["payload"], "tx") for out in t['tx']['outs']: script = bitcoin.deserialize_script(out['script']) data_element = script[2] if len(script) == 5 else script[1] if data_element in self.callbacks: self.callbacks[data_element](t) else: print "Received message %s from %s:%s" % (m["message"]["command"], self.transport.getPeer().host, self.transport.getPeer().port)
def add_signature(self, sigb64): sig = base64.b64decode(sigb64).encode('hex') inserted_sig = False txhex = btc.serialize(self.latest_tx) #batch retrieval of utxo data utxo = {} ctr = 0 for index, ins in enumerate(self.latest_tx['ins']): utxo_for_checking = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) if ins['script'] != '' or utxo_for_checking in self.input_utxos.keys(): continue utxo[ctr] = [index, utxo_for_checking] ctr += 1 utxo_data = common.bc_interface.query_utxo_set([x[1] for x in utxo.values()]) #insert signatures for i,u in utxo.iteritems(): if utxo_data[i] == None: continue sig_good = btc.verify_tx_input(txhex, u[0], utxo_data[i]['script'], *btc.deserialize_script(sig)) if sig_good: debug('found good sig at index=%d' % (u[0])) self.latest_tx['ins'][u[0]]['script'] = sig inserted_sig = True break if not inserted_sig: debug('signature did not match anything in the tx') #TODO what if the signature doesnt match anything # nothing really to do except drop it, carry on and wonder why the # other guy sent a failed signature tx_signed = True for ins in self.latest_tx['ins']: if ins['script'] == '': tx_signed = False if not tx_signed: return debug('the entire tx is signed, ready to pushtx()') txhex = btc.serialize(self.latest_tx) debug('\n' + txhex) #TODO send to a random maker or push myself #self.msgchan.push_tx(self.active_orders.keys()[0], txhex) self.txid = common.bc_interface.pushtx(txhex) debug('pushed tx ' + str(self.txid)) if self.txid == None: debug('unable to pushtx') if self.finishcallback != None: self.finishcallback(self)
def segwit_strip_script_separator(script, index=0): if index == None: return script OP_CODESEPARATOR = 171 def get_pos(script, index): i = 0 for x in range(0, index): try: i = script.index(OP_CODESEPARATOR, i) except ValueError: return i return i + 1 deserialized_script = deserialize_script(str(script)) pos = get_pos(deserialized_script, index) return serialize_script(deserialized_script[pos:])
def parse_multisig_scriptsig( scriptsig_hex ): """ Given a scriptsig (as hex), extract the signatures. Return list of signatures on success Return None on error """ try: script_parts = bitcoin.deserialize_script( scriptsig_hex ) except: if os.environ.get("BLOCKSTACK_TEST") == "1": traceback.print_exc() return None # sanity check return script_parts
def tx_output_parse_scriptPubKey( scriptpubkey ): """ Given the hex representation of a scriptPubKey, turn it into a nice, easy-to-read dict like what bitcoind would give us. """ script_tokens = bitcoin.deserialize_script( scriptpubkey ) script_type = None reqSigs = None addresses = [] if scriptpubkey.startswith("76a914") and scriptpubkey.endswith("88ac") and len(scriptpubkey) == 50: script_type = "pubkeyhash" reqSigs = 1 addresses = [ script_hex_to_address(scriptpubkey) ] elif scriptpubkey.startswith("a914") and scriptpubkey.endswith("87") and len(scriptpubkey) == 46: script_type = "scripthash" reqsigs = 1 addresses = [ script_hex_to_address(scriptpubkey) ] elif script_tokens[-1] == OPCODE_VALUES["OP_CHECKMULTISIG"]: script_type = "multisig" elif script_tokens[0] == OPCODE_VALUES["OP_RETURN"] and len(script_tokens) == 2: script_type = "nulldata" elif len(script_tokens) == 2 and script_tokens[-1] == OPCODE_VALUES["OP_CHECKSIG"]: script_type = "pubkey" reqSigs = 1 else: script_type = "nonstandard" ret = { "asm": tx_script_to_asm(scriptpubkey), "hex": scriptpubkey, "type": script_type } if addresses is not None: ret['addresses'] = addresses if reqSigs is not None: ret['reqSigs'] = reqSigs return ret
def parse_multisig_redeemscript( redeem_script_hex ): """ Given a redeem script (as hex), extract multisig information. Return m, list of public keys on success Return (None, None) """ script_parts = [] redeem_script_hex = str(redeem_script_hex) try: script_parts = bitcoin.deserialize_script( redeem_script_hex ) except: if os.environ.get("BLOCKSTACK_TEST") == "1": traceback.print_exc() print >> sys.stderr, "Invalid redeem script %s" % redeem_script_hex return None, None try: assert len(script_parts) > 2 assert script_parts[-1] == opcodes.OP_CHECKMULTISIG script_parts.pop(-1) # get n n = script_parts.pop(-1) pubkeys = [] # get m m = script_parts.pop(0) for i in xrange(0, n): pubk = script_parts.pop(0) # must be a public key BitcoinPublicKey(pubk) pubkeys.append(pubk) assert len(script_parts) == 0, "script_parts = %s" % script_parts return (m, pubkeys) except Exception, e: if os.environ.get("BLOCKSTACK_TEST") == "1": traceback.print_exc() print >> sys.stderr, "Invalid redeem script %s (parses to %s)" % (redeem_script_hex, script_parts) return (None, None)
def decode_multisig_script(script): scriptSig = bitcoin.deserialize_script(script) redeemScript = hexlify(scriptSig[-1]) (M, N, pubkeys) = decode_redeem_script(redeemScript) sigs = [] for sig in scriptSig[1:-1]: sigs.append(hexlify(sig)) return (sigs, pubkeys, redeemScript, M, N)
def add_signature(self, sigb64): sig = base64.b64decode(sigb64).encode('hex') inserted_sig = False tx = btc.serialize(self.latest_tx) for index, ins in enumerate(self.latest_tx['ins']): if ins['script'] != '': continue utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) utxo_data = common.bc_interface.query_utxo_set(utxo) if utxo_data[0] == None: continue sig_good = btc.verify_tx_input(tx, index, utxo_data[0]['script'], *btc.deserialize_script(sig)) if sig_good: debug('found good sig at index=%d' % (index)) ins['script'] = sig inserted_sig = True break if not inserted_sig: debug('signature did not match anything in the tx') #TODO what if the signature doesnt match anything # nothing really to do except drop it, carry on and wonder why the # other guy sent a failed signature tx_signed = True for ins in self.latest_tx['ins']: if ins['script'] == '': tx_signed = False if not tx_signed: return debug('the entire tx is signed, ready to pushtx()') txhex = btc.serialize(self.latest_tx) debug('\n' + txhex) #TODO send to a random maker or push myself #self.msgchan.push_tx(self.active_orders.keys()[0], txhex) txid = common.bc_interface.pushtx(txhex) debug('pushed tx ' + str(txid)) if self.finishcallback != None: self.finishcallback(self)
def _decode_script(cls, vin): ds = [0 if x == None else x for x in deserialize_script(vin['script'])] asm = '' for i, x in enumerate(ds): try: y = int(x, 16) if isinstance(x, str) else x if 256 > y > 128 and y not in range(139, 176): asm += '{}'.format(128 - y) else: asm += '{}'.format(SCRIPTS[y] if len(ds) > 1 else int().from_bytes(binascii.unhexlify(y), byteorder='little')) except: to_int = int().from_bytes(binascii.unhexlify(x), byteorder="little") # FIXME - Sometimes bytes wrap into hex, sometimes into dec asm += str(to_int if to_int < 1418797546 else x) if i < len(ds)-1: asm += ' ' return {'txid': vin['outpoint']['hash'], 'vout': vin['outpoint']['index'], 'scriptSig': {'hex': vin['script'], 'asm': asm }, 'sequence': vin['sequence']}
def test_donation_address(setup_donations, amount): wallets = make_wallets(1, wallet_structures=[[1,1,1,0,0]], mean_amt=0.5) wallet = wallets[0]['wallet'] jm_single().bc_interface.sync_wallet(wallet) #make a rdp from a simple privkey rdp_priv = "\x01"*32 reusable_donation_pubkey = binascii.hexlify(secp256k1.PrivateKey( privkey=rdp_priv, raw=True, ctx=btc.ctx).pubkey.serialize()) dest_addr, sign_k = donation_address(reusable_donation_pubkey) print dest_addr jm_single().bc_interface.rpc('importaddress', [dest_addr, '', False]) ins_full = wallet.unspent total = sum(x['value'] for x in ins_full.values()) ins = ins_full.keys() output_addr = wallet.get_new_addr(1, 1) fee_est = 10000 outs = [{'value': amount, 'address': dest_addr}, {'value': total - amount - fee_est, 'address': output_addr}] tx = btc.mktx(ins, outs) de_tx = btc.deserialize(tx) for index, ins in enumerate(de_tx['ins']): utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) addr = ins_full[utxo]['address'] priv = wallet.get_key_from_addr(addr) priv = binascii.unhexlify(priv) usenonce = binascii.unhexlify(sign_k) if index == 0 else None if index == 0: log.debug("Applying rdp to input: " + str(ins)) tx = btc.sign(tx, index, priv, usenonce=usenonce) #pushtx returns False on any error push_succeed = jm_single().bc_interface.pushtx(tx) if push_succeed: log.debug(btc.txhash(tx)) else: assert False #Role of receiver: regenerate the destination private key, #and address, from the nonce of the first input; check it has #received the coins. detx = btc.deserialize(tx) first_utxo_script = detx['ins'][0]['script'] sig, pub = btc.deserialize_script(first_utxo_script) log.debug(sig) sig = binascii.unhexlify(sig) kGlen = ord(sig[3]) kG = sig[4:4+kGlen] log.debug(binascii.hexlify(kG)) if kG[0] == "\x00": kG = kG[1:] #H(rdp private key * K) + rdp should be ==> dest addr #Open issue: re-introduce recovery without ECC shenanigans #Just cheat by trying both signs for pubkey coerced_kG_1 = "02" + binascii.hexlify(kG) coerced_kG_2 = "03" + binascii.hexlify(kG) for coerc in [coerced_kG_1, coerced_kG_2]: c = btc.sha256(btc.multiply(binascii.hexlify(rdp_priv), coerc, True)) pub_check = btc.add_pubkeys([reusable_donation_pubkey, btc.privtopub(c+'01', True)], True) addr_check = btc.pubtoaddr(pub_check, get_p2pk_vbyte()) log.debug("Found checked address: " + addr_check) if addr_check == dest_addr: time.sleep(3) received = jm_single().bc_interface.get_received_by_addr( [dest_addr], None)['data'][0]['balance'] assert received == amount return assert False
def handle_block( self, message_header, block ): """ Got a block. * validate it * load its transactions * ask for each transaction's sender transaction """ if self.have_all_block_data(): self.loop_exit() return block_hash = block.calculate_hash() # is this a solicited block? if block_hash not in self.block_info.keys(): log.error("Ignoring unsolicited block %s" % block_hash) return header = self.block_info[block_hash]['header'] height = self.block_info[block_hash]['height'] log.debug("handle block %s (%s)" % (height, block_hash)) # does this block's transaction hashes match the merkle root? tx_hashes = [block.txns[i].calculate_hash() for i in xrange(0, len(block.txns))] mr = pybitcoin.MerkleTree( tx_hashes ).root() if mr != header['merkle_root']: log.error("Merkle root of %s (%s) mismatch: expected %s, got %s" % (block_hash, height, header['merkle_root'], mr)) return nulldata_txs = [] relindex = 0 for txindex in xrange(0, len(block.txns)): txdata = self.parse_tx( block.txns[txindex], header, block_hash, txindex ) # if there is no nulldata output, then we don't care about this one. has_nulldata = False nulldata_payload = None for outp in txdata['vout']: if outp['scriptPubKey']['type'] == 'nulldata': has_nulldata = True nulldata_payload = bitcoin.deserialize_script(outp['scriptPubKey']['hex'])[1] if type(nulldata_payload) not in [str, unicode]: # this is a malformed OP_RETURN, where the varint that should follow OP_RETURN doesn't have the data behind it. # just take the data after the varint, no matter what it is (i.e. "6a52" will be "") nulldata_payload = outp['scriptPubKey']['hex'][4:] # count all txs processed self.num_txs_processed += 1 if not has_nulldata: continue # remember nulldata txdata['nulldata'] = nulldata_payload # calculate total output (part of fee; will be debited when we discover the senders) txdata['fee'] -= sum( int(out['value'] * 10**8) for out in txdata['vout'] ) # remember the relative tx index (i.e. the ith nulldata tx) txdata['relindex'] = relindex # do we actually want this? if self.tx_filter is not None: if not self.tx_filter( txdata ): continue # yup, we want it! relindex += 1 nulldata_txs.append( txdata ) self.block_info[block_hash]['txns'] = nulldata_txs self.block_info[block_hash]['num_txns'] = len(block.txns) self.block_info[block_hash]['num_senders'] = 0 # get each input's transaction sender_txhashes = [] for txn in self.block_info[block_hash]['txns']: for i in xrange(0, len(txn['vin'])): # record information about the transaction # that created this input (so we can go find # it later). inp = txn['vin'][i] sender_txid = inp['txid'] inp_sender_outp = inp['vout'] if str(sender_txid) not in sender_txhashes: sender_txhashes.append( str(sender_txid) ) sinfo = self.make_sender_info( block_hash, txn, i ) if not self.sender_info.has_key(sender_txid): # map outpoint for this input to the tx info self.sender_info[sender_txid] = {} # sinfo is the information from the output in # the sender-tx that funded inp self.sender_info[sender_txid][inp_sender_outp] = sinfo # update accounting... self.num_blocks_received += 1 self.block_info[block_hash]['handled'] = True log.debug("Request %s nulldata sender TXs" % len(sender_txhashes)) if self.have_all_block_data(): self.loop_exit() return
def add_signature(self, nick, sigb64): if nick not in self.nonrespondants: log.debug( ('add_signature => nick={} ' 'not in nonrespondants {}').format(nick, self.nonrespondants)) return sig = base64.b64decode(sigb64).encode('hex') inserted_sig = False txhex = btc.serialize(self.latest_tx) # batch retrieval of utxo data utxo = {} ctr = 0 for index, ins in enumerate(self.latest_tx['ins']): utxo_for_checking = ins['outpoint']['hash'] + ':' + str( ins['outpoint']['index']) if (ins['script'] != '' or utxo_for_checking in self.input_utxos.keys()): continue utxo[ctr] = [index, utxo_for_checking] ctr += 1 utxo_data = jm_single().bc_interface.query_utxo_set( [x[1] for x in utxo.values()]) # insert signatures for i, u in utxo.iteritems(): if utxo_data[i] is None: continue sig_good = btc.verify_tx_input(txhex, u[0], utxo_data[i]['script'], *btc.deserialize_script(sig)) if sig_good: log.debug('found good sig at index=%d' % (u[0])) self.latest_tx['ins'][u[0]]['script'] = sig inserted_sig = True # check if maker has sent everything possible self.utxos[nick].remove(u[1]) if len(self.utxos[nick]) == 0: log.info(('nick = {} sent all sigs, removing from ' 'nonrespondant list').format(nick)) self.nonrespondants.remove(nick) break if not inserted_sig: log.debug('signature did not match anything in the tx') # TODO what if the signature doesnt match anything # nothing really to do except drop it, carry on and wonder why the # other guy sent a failed signature tx_signed = True for ins in self.latest_tx['ins']: if ins['script'] == '': tx_signed = False if not tx_signed: return self.end_timeout_thread = True self.all_responded = True with self.timeout_lock: self.timeout_lock.notify() log.info('all makers have sent their signatures') for index, ins in enumerate(self.latest_tx['ins']): # remove placeholders if ins['script'] == 'deadbeef': ins['script'] = '' if self.finishcallback is not None: self.finishcallback(self)