def get_fees( inputs, outputs ):
    """
    Given a transaction's outputs, look up its fees:
    * the first output should be an OP_RETURN with the transfer info 
    * the second output should be the new owner's address, with a DEFAULT_DUST_FEE
    * the third output should be the change address
    
    Return (dust fees, operation fees) on success 
    Return (None, None) on invalid output listing
    """
    if len(outputs) != 3:
        return (None, None)
    
    # 0: op_return
    if not tx_output_is_op_return( outputs[0] ):
        return (None, None) 
    
    if outputs[0]["value"] != 0:
        return (None, None) 
    
    # 1: transfer address 
    if script_hex_to_address( outputs[1]["script_hex"] ) is None:
        return (None, None)
    
    # 2: change address 
    if script_hex_to_address( outputs[2]["script_hex"] ) is None:
        return (None, None)
    
    dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
    op_fee = DEFAULT_DUST_FEE
    
    return (dust_fee, op_fee)
示例#2
0
def get_fees(inputs, outputs):
    """
    Given a transaction's outputs, look up its fees:
    * the first output should be an OP_RETURN with the transfer info 
    * the second output should be the new owner's address, with a DEFAULT_DUST_FEE
    * the third output should be the change address
    
    Return (dust fees, operation fees) on success 
    Return (None, None) on invalid output listing
    """
    if len(outputs) != 3:
        return (None, None)

    # 0: op_return
    if not tx_output_is_op_return(outputs[0]):
        return (None, None)

    if outputs[0]["value"] != 0:
        return (None, None)

    # 1: transfer address
    if script_hex_to_address(outputs[1]["script_hex"]) is None:
        return (None, None)

    # 2: change address
    if script_hex_to_address(outputs[2]["script_hex"]) is None:
        return (None, None)

    dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
    op_fee = DEFAULT_DUST_FEE

    return (dust_fee, op_fee)
示例#3
0
def get_fees( inputs, outputs ):
    """
    Given a transaction's outputs, look up its fees:
    * the first output must be an OP_RETURN, and it must have a fee of 0.
    * the second output must be the reveal address, and it must have a dust fee
    * the third must be the change address
    * the fourth, if given, must be a burned fee sent to the burn address
    
    Return (dust fees, operation fees) on success 
    Return (None, None) on invalid output listing
    """
    
    dust_fee = 0
    op_fee = 0
    
    if len(outputs) != 3 and len(outputs) != 4:
        log.debug("len(outputs) == %s" % len(outputs))
        return (None, None)
    
    # 0: op_return
    if not tx_output_is_op_return( outputs[0] ):
        log.debug("output[0] is not an OP_RETURN")
        return (None, None) 
   
    # 1: reveal address 
    if script_hex_to_address( outputs[1]["script_hex"] ) is None:
        log.debug("output[1] is not a p2pkh script")
        return (None, None)
    
    # 2: change address 
    if script_hex_to_address( outputs[2]["script_hex"] ) is None:
        log.debug("output[2] is not a p2pkh script")
        return (None, None)
    
    # 3: burn address, if given 
    if len(outputs) == 4:
        
        addr_hash = script_hex_to_address( outputs[3]["script_hex"] )
        if addr_hash is None:
            log.debug("output[3] is not a p2pkh script")
            return (None, None) 
        
        if addr_hash != BLOCKSTACK_BURN_ADDRESS:
            log.debug("output[3] is not the burn address (got %s)" % addr_hash)
            return (None, None)
    
        dust_fee = (len(inputs) + 3) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
        op_fee = outputs[3]["value"]
        
    else:
        dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
    
    return (dust_fee, op_fee)
示例#4
0
def get_fees( inputs, outputs ):
    """
    Given a transaction's outputs, look up its fees:
    * the first output must be an OP_RETURN, and it must have a fee of 0.
    * the second output must be the reveal address, and it must have a dust fee
    * the third must be the change address
    * the fourth, if given, must be a burned fee sent to the burn address
    
    Return (dust fees, operation fees) on success 
    Return (None, None) on invalid output listing
    """
    
    dust_fee = 0
    op_fee = 0
    
    if len(outputs) != 3 and len(outputs) != 4:
        return (None, None)
    
    # 0: op_return
    if not tx_output_is_op_return( outputs[0] ):
        return (None, None) 
    
    if outputs[0]["value"] != 0:
        return (None, None) 
    
    # 1: reveal address 
    if script_hex_to_address( outputs[1]["script_hex"] ) is None:
        return (None, None)
    
    # 2: change address 
    if script_hex_to_address( outputs[2]["script_hex"] ) is None:
        return (None, None)
    
    # 3: burn address, if given 
    if len(outputs) == 4:
        
        addr_hash = script_hex_to_address( outputs[3]["script_hex"] )
        if addr_hash is None:
            return (None, None) 
        
        if addr_hash != BLOCKSTORE_BURN_PUBKEY_HASH:
            return (None, None)
    
        dust_fee = (len(inputs) + 3) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
        op_fee = outputs[3]["value"]
        
    else:
        dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
    
    return (dust_fee, op_fee)
示例#5
0
def get_fees(inputs, outputs):
    """
    Given a transaction's outputs, look up its fees:
    * there should be two outputs: the OP_RETURN and change address
    
    Return (dust fees, operation fees) on success 
    Return (None, None) on invalid output listing
    """
    if len(outputs) != 2:
        return (None, None)

    # 0: op_return
    if not tx_output_is_op_return(outputs[0]):
        return (None, None)

    if outputs[0]["value"] != 0:
        return (None, None)

    # 1: change address
    if script_hex_to_address(outputs[1]["script_hex"]) is None:
        return (None, None)

    dust_fee = (len(inputs) + 1) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
    op_fee = 0

    return (dust_fee, op_fee)
示例#6
0
def get_fees( inputs, outputs ):
    """
    Given a transaction's outputs, look up its fees:
    * there should be two outputs: the OP_RETURN and change address
    
    Return (dust fees, operation fees) on success 
    Return (None, None) on invalid output listing
    """
    if len(outputs) != 2:
        return (None, None)
    
    # 0: op_return
    if not tx_output_is_op_return( outputs[0] ):
        return (None, None) 
    
    if outputs[0]["value"] != 0:
        return (None, None) 
    
    # 1: change address 
    if script_hex_to_address( outputs[1]["script_hex"] ) is None:
        return (None, None)
    
    dust_fee = (len(inputs) + 1) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
    op_fee = 0
    
    return (dust_fee, op_fee)
示例#7
0
def get_fees( inputs, outputs ):
    """
    Given a transaction's outputs, look up its fees:
    * the first output must be an OP_RETURN, and it must have a fee of 0.
    # the second must be the change address
    * the third must be a burn fee to the burn address.
    
    Return (dust fees, operation fees) on success 
    Return (None, None) on invalid output listing
    """
    if len(outputs) != 3:
        log.debug("Expected 3 outputs; got %s" % len(outputs))
        return (None, None)
    
    # 0: op_return
    if not tx_output_is_op_return( outputs[0] ):
        log.debug("outputs[0] is not an OP_RETURN")
        return (None, None) 
    
    if outputs[0]["value"] != 0:
        log.debug("outputs[0] has value %s'" % outputs[0]["value"])
        return (None, None) 
    
    # 1: change address 
    if script_hex_to_address( outputs[1]["script_hex"] ) is None:
        log.error("outputs[1] has no decipherable change address")
        return (None, None)
    
    # 2: burn address 
    addr_hash = script_hex_to_address( outputs[2]["script_hex"] )
    if addr_hash is None:
        log.error("outputs[2] has no decipherable burn address")
        return (None, None) 
    
    if addr_hash != BLOCKSTORE_BURN_ADDRESS:
        log.error("outputs[2] is not the burn address")
        return (None, None)
    
    dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
    op_fee = outputs[2]["value"]
    
    return (dust_fee, op_fee)
示例#8
0
def get_fees(inputs, outputs):
    """
    Given a transaction's outputs, look up its fees:
    * the first output must be an OP_RETURN, and it must have a fee of 0.
    # the second must be the change address
    * the third must be a burn fee to the burn address.
    
    Return (dust fees, operation fees) on success 
    Return (None, None) on invalid output listing
    """
    if len(outputs) != 3:
        log.debug("Expected 3 outputs; got %s" % len(outputs))
        return (None, None)

    # 0: op_return
    if not tx_output_is_op_return(outputs[0]):
        log.debug("outputs[0] is not an OP_RETURN")
        return (None, None)

    if outputs[0]["value"] != 0:
        log.debug("outputs[0] has value %s'" % outputs[0]["value"])
        return (None, None)

    # 1: change address
    if script_hex_to_address(outputs[1]["script_hex"]) is None:
        log.error("outputs[1] has no decipherable change address")
        return (None, None)

    # 2: burn address
    addr_hash = script_hex_to_address(outputs[2]["script_hex"])
    if addr_hash is None:
        log.error("outputs[2] has no decipherable burn address")
        return (None, None)

    if addr_hash != BLOCKSTORE_BURN_ADDRESS:
        log.error("outputs[2] is not the burn address")
        return (None, None)

    dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
    op_fee = outputs[2]["value"]

    return (dust_fee, op_fee)
def script_hex_to_address( script_hex ):
    """
    Examine a scriptPubkey and extract an address.
    """
    if script_hex.startswith("76a914") and script_hex.endswith("88ac") and len(script_hex) == 50:
        # p2pkh script
        return pybitcoin.script_hex_to_address( script_hex, version_byte=version_byte )

    elif script_hex.startswith("a914") and script_hex.endswith("87") and len(script_hex) == 46:
        # p2sh script
        return bitcoin.script_to_address( script_hex, vbyte=multisig_version_byte )

    else:
        raise ValueError("Nonstandard script %s" % script_hex)
def script_hex_address(script_hex):
    """
    Examine a scriptPubkey and extract an address
    """
    if script_hex.startswith("76a914") and script_hex.endswith("88ac") and len(
            script_hex) == 50:
        # p2pkh script
        return pybitcoin.script_hex_to_address(script_hex,
                                               version_byte=version_byte)

    elif script_hex.startswith("a914") and script_hex.endswith("87") and len(
            script_hex) == 46:
        # p2sh script
        return bitcoin.script_to_address(script_hex,
                                         vbyte=multisig_version_byte)

    else:
        raise ValueError("Nonstandard script %s" % script_hex)
示例#11
0
def btc_decoderawtransaction_compat( tx_hex ):
    """
    Implementation of bitcoind's decoderawtransaction
    JSONRPC method.  Tries to be faithful enough to
    bitcoind for virtualchain's sake.

    Does NOT handle coinbase transactions
    """

    inputs, outputs, locktime, version = tx_deserialize( tx_hex )
    txid = make_txid( tx_hex )

    vin = []
    vout = []

    for inp in inputs:
        vin_inp = {
            "txid": inp['transaction_hash'],
            "vout": inp['output_index'],
        }

        if inp.has_key("script_sig"):
            scriptsig_hex = inp['script_sig']
            scriptsig_asm = btc_decoderawtransaction_script_hex_to_asm( scriptsig_hex )

            vin_inp['scriptSig'] = {
                'asm': scriptsig_asm,
                'hex': scriptsig_hex
            }

        if inp.has_key("sequence"):

            vin_inp['sequence'] = inp['sequence']

        vin.append( vin_inp )

    for i in xrange( 0, len(outputs) ):

        out = outputs[i]
        script_type = btc_decoderawtransaction_get_script_type( out['script_hex'] )
        addresses = []

        if script_type == "pubkeyhash":
            addresses.append( pybitcoin.script_hex_to_address( out['script_hex'] ) )

        elif script_type == "pubkey":
            pubkey = btc_decoderawtransaction_get_pubkey_from_script( out['script_hex'] )
            addr = pybitcoin.BitcoinPublicKey( pubkey ).address()
            addresses.append( addr )

        elif script_type == "scripthash":
            script_hash = btc_decoderawtransaction_get_script_hash_from_script( out['script_hex'] ) 
            addr = pybitcoin.b58check_encode( binascii.unhexlify( script_hash ), version_byte=5 )
            addresses.append( addr )

        vout_out = {
            "value": float(out['value']) / 10e7,
            "mock_bitcoind_value_satoshi": out['value'],  # NOTE: extra
            "n": i,
            "scriptPubKey": {
                'asm': btc_decoderawtransaction_script_hex_to_asm( out['script_hex'] ),
                'hex': out['script_hex'],
                "type": script_type
            },
        }

        if script_type in ["pubkeyhash", "pubkey", "scripthash"]:
            vout_out['scriptPubKey']['reqSigs'] = 1

        if len(addresses) > 0:
            vout_out['scriptPubKey']['addresses'] = addresses

        vout.append( vout_out )

    tx_decoded = {
        "txid": txid,
        "version": version,
        "locktime": locktime,
        "vin": vin,
        "vout": vout
    }

    return tx_decoded
示例#12
0
def db_parse( block_id, opcode, data, senders, inputs, outputs, fee, db_state=None ):
   """
   (required by virtualchain state engine)
   
   Parse a blockstore operation from a transaction's nulldata (data) and a list of outputs, as well as 
   optionally the list of transaction's senders and the total fee paid.
   
   Return a parsed operation, and will also optionally have:
   * "sender": the first (primary) sender's script_pubkey.
   * "address": the sender's bitcoin address
   * "fee": the total fee paid for this record.
   * "recipient": the first non-OP_RETURN output's script_pubkey.
   * "sender_pubkey": the sender's public key (hex string), if this is a p2pkh transaction

   Return None on error
   
   NOTE: the transactions that our tools put have a single sender, and a single output address.
   This is assumed by this code.
   """

   sender = None 
   recipient = None
   recipient_address = None
   import_update_hash = None
   address = None
   sender_pubkey_hex = None
   
   if len(senders) == 0:
      raise Exception("No senders for (%s, %s)" % (opcode, hexlify(data)))
  
   # the first sender is always the first non-nulldata output script hex, and by construction
   # of Blockstore, this is always the principal that issued the operation.
   if 'script_pubkey' not in senders[0].keys():
      raise Exception("No script_pubkey in sender of (%s, %s)" % (opcode, hexlify(data)))
   
   if 'addresses' not in senders[0].keys():
      log.error("No addresses in sender of (%s, %s)" % (opcode, hexlify(data)))
      return None
   
   if len(senders[0]['addresses']) != 1:
      log.error("Multisig transactions are unsupported for (%s, %s)" % (opcode, hexlify(data)))
      return None
   
   sender = str(senders[0]['script_pubkey'])
   address = str(senders[0]['addresses'][0])

   if str(senders[0]['script_type']) == 'pubkeyhash':
      sender_pubkey_hex = get_public_key_hex_from_tx( inputs, address )
   
   if sender_pubkey_hex is None:
      log.warning("No public key found for (%s, %s)" % (opcode, hexlify(data)))
   
   op_fee = get_burn_fee_from_outputs( outputs )
   
   if opcode in [NAME_REGISTRATION, NAMESPACE_REVEAL]:
      # these operations have a designated recipient that is *not* the sender
      try:
         recipient = get_registration_recipient_from_outputs( outputs )
         recipient_address = pybitcoin.script_hex_to_address( recipient )
      except Exception, e:
         log.exception(e)
         raise Exception("No registration address for (%s, %s)" % (opcode, hexlify(data)))
示例#13
0
 
 if opcode in [NAME_REGISTRATION, NAMESPACE_REVEAL]:
    # these operations have a designated recipient that is *not* the sender
    try:
       recipient = get_registration_recipient_from_outputs( outputs )
       recipient_address = pybitcoin.script_hex_to_address( recipient )
    except Exception, e:
       log.exception(e)
       raise Exception("No registration address for (%s, %s)" % (opcode, hexlify(data)))
   
 
 if opcode in [NAME_IMPORT, NAME_TRANSFER]:
    # these operations have a designated recipient that is *not* the sender
    try:
       recipient = get_transfer_recipient_from_outputs( outputs )
       recipient_address = pybitcoin.script_hex_to_address( recipient )
    except Exception, e:
       log.exception(e)
       raise Exception("No recipient for (%s, %s)" % (opcode, hexlify(data)))
    
    
 if opcode in [NAME_IMPORT]:
    # this operation has an update hash embedded as a phony recipient 
    try:
       import_update_hash = get_import_update_hash_from_outputs( outputs, recipient )
    except Exception, e:
       log.exception(e)
       raise Exception("No update hash for (%s, %s)" % (opcode, hexlify(data)))
   
       
 op = parse_blockstore_op_data(opcode, data, sender, recipient=recipient, recipient_address=recipient_address, import_update_hash=import_update_hash )
示例#14
0
def db_parse(block_id,
             opcode,
             data,
             senders,
             inputs,
             outputs,
             fee,
             db_state=None):
    """
   (required by virtualchain state engine)
   
   Parse a blockstore operation from a transaction's nulldata (data) and a list of outputs, as well as 
   optionally the list of transaction's senders and the total fee paid.
   
   Return a parsed operation, and will also optionally have:
   * "sender": the first (primary) sender's script_pubkey.
   * "address": the sender's bitcoin address
   * "fee": the total fee paid for this record.
   * "recipient": the first non-OP_RETURN output's script_pubkey.
   * "sender_pubkey": the sender's public key (hex string), if this is a p2pkh transaction

   Return None on error
   
   NOTE: the transactions that our tools put have a single sender, and a single output address.
   This is assumed by this code.
   """

    sender = None
    recipient = None
    recipient_address = None
    import_update_hash = None
    address = None
    sender_pubkey_hex = None

    if len(senders) == 0:
        raise Exception("No senders for (%s, %s)" % (opcode, hexlify(data)))

    # the first sender is always the first non-nulldata output script hex, and by construction
    # of Blockstore, this is always the principal that issued the operation.
    if 'script_pubkey' not in senders[0].keys():
        raise Exception("No script_pubkey in sender of (%s, %s)" %
                        (opcode, hexlify(data)))

    if 'addresses' not in senders[0].keys():
        log.error("No addresses in sender of (%s, %s)" %
                  (opcode, hexlify(data)))
        return None

    if len(senders[0]['addresses']) != 1:
        log.error("Multisig transactions are unsupported for (%s, %s)" %
                  (opcode, hexlify(data)))
        return None

    sender = str(senders[0]['script_pubkey'])
    address = str(senders[0]['addresses'][0])

    if str(senders[0]['script_type']) == 'pubkeyhash':
        sender_pubkey_hex = get_public_key_hex_from_tx(inputs, address)

    if sender_pubkey_hex is None:
        log.warning("No public key found for (%s, %s)" %
                    (opcode, hexlify(data)))

    op_fee = get_burn_fee_from_outputs(outputs)

    if opcode in [NAME_REGISTRATION, NAMESPACE_REVEAL]:
        # these operations have a designated recipient that is *not* the sender
        try:
            recipient = get_registration_recipient_from_outputs(outputs)
            recipient_address = pybitcoin.script_hex_to_address(recipient)
        except Exception, e:
            log.exception(e)
            raise Exception("No registration address for (%s, %s)" %
                            (opcode, hexlify(data)))
示例#15
0
    if opcode in [NAME_REGISTRATION, NAMESPACE_REVEAL]:
        # these operations have a designated recipient that is *not* the sender
        try:
            recipient = get_registration_recipient_from_outputs(outputs)
            recipient_address = pybitcoin.script_hex_to_address(recipient)
        except Exception, e:
            log.exception(e)
            raise Exception("No registration address for (%s, %s)" %
                            (opcode, hexlify(data)))

    if opcode in [NAME_IMPORT, NAME_TRANSFER]:
        # these operations have a designated recipient that is *not* the sender
        try:
            recipient = get_transfer_recipient_from_outputs(outputs)
            recipient_address = pybitcoin.script_hex_to_address(recipient)
        except Exception, e:
            log.exception(e)
            raise Exception("No recipient for (%s, %s)" %
                            (opcode, hexlify(data)))

    if opcode in [NAME_IMPORT]:
        # this operation has an update hash embedded as a phony recipient
        try:
            import_update_hash = get_import_update_hash_from_outputs(
                outputs, recipient)
        except Exception, e:
            log.exception(e)
            raise Exception("No update hash for (%s, %s)" %
                            (opcode, hexlify(data)))
示例#16
0
def btc_decoderawtransaction_compat( tx_hex ):
    """
    Implementation of bitcoind's decoderawtransaction
    JSONRPC method.  Tries to be faithful enough to
    bitcoind for virtualchain's sake.

    Does NOT handle coinbase transactions
    """

    inputs, outputs, locktime, version = tx_deserialize( tx_hex )
    txid = make_txid( tx_hex )

    vin = []
    vout = []

    for inp in inputs:
        vin_inp = {
            "txid": inp['transaction_hash'],
            "vout": inp['output_index'],
        }

        if inp.has_key("script_sig"):
            scriptsig_hex = inp['script_sig']
            scriptsig_asm = btc_decoderawtransaction_script_hex_to_asm( scriptsig_hex )

            vin_inp['scriptSig'] = {
                'asm': scriptsig_asm,
                'hex': scriptsig_hex
            }

        if inp.has_key("sequence"):

            vin_inp['sequence'] = inp['sequence']

        vin.append( vin_inp )

    for i in xrange( 0, len(outputs) ):

        out = outputs[i]
        script_type = btc_decoderawtransaction_get_script_type( out['script_hex'] )
        addresses = []

        if script_type == "pubkeyhash":
            addresses.append( pybitcoin.script_hex_to_address( out['script_hex'] ) )

        elif script_type == "pubkey":
            pubkey = btc_decoderawtransaction_get_pubkey_from_script( out['script_hex'] )
            addr = pybitcoin.BitcoinPublicKey( pubkey ).address()
            addresses.append( addr )

        elif script_type == "scripthash":
            script_hash = btc_decoderawtransaction_get_script_hash_from_script( out['script_hex'] ) 
            addr = pybitcoin.b58check_encode( binascii.unhexlify( script_hash ), version_byte=5 )
            addresses.append( addr )

        vout_out = {
            "value": float(out['value']) / 10e7,
            "mock_bitcoind_value_satoshi": out['value'],  # NOTE: extra
            "n": i,
            "scriptPubKey": {
                'asm': btc_decoderawtransaction_script_hex_to_asm( out['script_hex'] ),
                'hex': out['script_hex'],
                "type": script_type
            },
        }

        if script_type in ["pubkeyhash", "pubkey", "scripthash"]:
            vout_out['scriptPubKey']['reqSigs'] = 1

        if len(addresses) > 0:
            vout_out['scriptPubKey']['addresses'] = addresses

        vout.append( vout_out )

    tx_decoded = {
        "txid": txid,
        "version": version,
        "locktime": locktime,
        "vin": vin,
        "vout": vout
    }

    return tx_decoded