adds a dust_included flag to tell the tx serialization code not to add any more dust fees, because those fees are already included in txfee, this prevents double-adds of dust fees

This commit is contained in:
Aaron Blankstein
2017-10-06 16:11:50 -04:00
parent be49f7f410
commit fd62c3bccf
6 changed files with 137 additions and 93 deletions

View File

@@ -78,45 +78,48 @@ def build(name, script_pubkey, register_addr, consensus_hash, name_hash=None):
return packaged_script
def make_outputs( data, inputs, sender_addr, burn_addr, fee, tx_fee, pay_fee=True ):
def make_outputs( data, inputs, sender_addr, burn_addr, fee, tx_fee, pay_fee=True, dust_included=False ):
"""
Make outputs for a name preorder:
[0] OP_RETURN with the name
[0] OP_RETURN with the name
[1] address with the NAME_PREORDER sender's address
[2] pay-to-address with the *burn address* with the fee
Raise ValueError if there are not enough inputs to make the transaction
"""
op_fee = max(fee, DEFAULT_DUST_FEE)
dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee
total_tx_fee = tx_fee
if not dust_included:
total_tx_fee += (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
dust_value = DEFAULT_DUST_FEE
bill = 0
if pay_fee:
bill = op_fee
else:
op_fee = 0
bill = 0
dust_fee = 0
total_tx_fee = 0
return [
# main output
{"script": virtualchain.make_data_script(str(data)),
"value": 0},
# change address (can be subsidy key)
{"script": virtualchain.make_payment_script(str(sender_addr)),
"value": virtualchain.calculate_change_amount(inputs, bill, dust_fee)},
"value": virtualchain.calculate_change_amount(inputs, bill, total_tx_fee)},
# burn address
{"script": virtualchain.make_payment_script(str(burn_addr)),
"value": op_fee}
]
def make_transaction(name, preorder_addr, register_addr, burn_addr, fee, consensus_hash, blockchain_client, tx_fee=0, subsidize=False, safety=True):
def make_transaction(name, preorder_addr, register_addr, burn_addr, fee, consensus_hash,
blockchain_client, tx_fee=0, subsidize=False, safety=True,
dust_included=False):
"""
Builds and broadcasts a preorder transaction.
"""
@@ -134,7 +137,7 @@ def make_transaction(name, preorder_addr, register_addr, burn_addr, fee, consens
inputs = None
private_key_obj = None
script_pubkey = None # to be mixed into preorder hash
pay_fee = True
if subsidize:
pay_fee = False
@@ -143,15 +146,15 @@ def make_transaction(name, preorder_addr, register_addr, burn_addr, fee, consens
inputs = tx_get_unspents( preorder_addr, blockchain_client )
if safety:
assert len(inputs) > 0, "No UTXOs for {}".format(preorder_addr)
script_pubkey = virtualchain.make_payment_script( preorder_addr )
nulldata = build( name, script_pubkey, register_addr, consensus_hash)
outputs = make_outputs(nulldata, inputs, preorder_addr, burn_addr, fee, tx_fee, pay_fee=pay_fee)
outputs = make_outputs(nulldata, inputs, preorder_addr, burn_addr, fee, tx_fee, pay_fee=pay_fee,
dust_included = dust_included)
return (inputs, outputs)
def get_fees( inputs, outputs ):
"""
Given a transaction's outputs, look up its fees:

View File

@@ -93,80 +93,78 @@ def build(name, value_hash=None):
return packaged_script
def make_outputs( data, change_inputs, register_addr, change_addr, tx_fee, renewal_fee=None, burn_address=BLOCKSTACK_BURN_ADDRESS, pay_fee=True):
def make_outputs( data, change_inputs, register_addr, change_addr, tx_fee,
renewal_fee=None, burn_address=BLOCKSTACK_BURN_ADDRESS, pay_fee=True,
dust_included = False ):
"""
Make outputs for a register:
[0] OP_RETURN with the name, and possibly a value hash
[0] OP_RETURN with the name, and possibly a value hash
[1] pay-to-address with the *register_addr*, not the sender's address. If renewing, this is the new owner address.
[2] change address with the NAME_PREORDER or NAME_RENEWAL's subsidizer's sender's address
[3] (OPTIONAL) renewal fee, sent to the burn address
Raise ValueError if there are not enough inputs to make the transaction
"""
dust_fee = None
dust_value = DEFAULT_DUST_FEE
op_fee = None
bill = None
bill = None
if pay_fee:
# sender pays
total_tx_fee = tx_fee
if renewal_fee is not None:
# renewing
dust_fee = (len(change_inputs) + 3) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee
dust_fee = (len(change_inputs) + 3) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
op_fee = max(renewal_fee, DEFAULT_DUST_FEE)
bill = op_fee
else:
# registering
dust_fee = (len(change_inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee
dust_fee = (len(change_inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
op_fee = 0
bill = DEFAULT_DUST_FEE * 2
if not dust_included:
total_tx_fee += dust_fee
else:
# subsidized by another address
bill = 0
total_tx_fee = 0
if renewal_fee is not None:
# renewing
dust_fee = 0
op_fee = max(renewal_fee, DEFAULT_DUST_FEE)
bill = 0
else:
# registering
dust_fee = 0
op_fee = 0
bill = 0
payload = str(data)
outputs = [
# main output
{"script": virtualchain.make_data_script(payload),
"value": 0},
# register/new-owner address
{"script": virtualchain.make_payment_script(str(register_addr)),
"value": dust_value},
# change address (can be the subsidy address)
{"script": virtualchain.make_payment_script(str(change_addr)),
"value": virtualchain.calculate_change_amount(change_inputs, bill, dust_fee)},
"value": virtualchain.calculate_change_amount(change_inputs, bill, total_tx_fee)},
]
if renewal_fee is not None:
outputs.append(
# burn address (when renewing)
{"script": virtualchain.make_payment_script(str(burn_address)),
"value": op_fee}
)
return outputs
def make_transaction(name, preorder_or_owner_addr, register_or_new_owner_addr, blockchain_client, tx_fee=0, burn_address=BLOCKSTACK_BURN_ADDRESS, renewal_fee=None, zonefile_hash=None, subsidize=False, safety=True):
def make_transaction(name, preorder_or_owner_addr, register_or_new_owner_addr, blockchain_client,
tx_fee=0, burn_address=BLOCKSTACK_BURN_ADDRESS, renewal_fee=None,
zonefile_hash=None, subsidize=False, safety=True, dust_included=False):
# register_or_new_owner_addr is the address of the recipient in NAME_PREORDER
# register_or_new_owner_addr is the address of the current name owner in standard NAME_RENEWAL (pre F-day 2017)
# register_or_new_owner_addr is the address of the current or new name owner, in the post-F-day 2017 NAME_RENEWAL
@@ -176,7 +174,7 @@ def make_transaction(name, preorder_or_owner_addr, register_or_new_owner_addr, b
register_or_new_owner_addr = str(register_or_new_owner_addr)
name = str(name)
tx_fee = int(tx_fee)
assert is_name_valid(name)
if renewal_fee is not None:
@@ -184,7 +182,7 @@ def make_transaction(name, preorder_or_owner_addr, register_or_new_owner_addr, b
change_inputs = None
pay_fee = True
change_inputs = tx_get_unspents( preorder_or_owner_addr, blockchain_client )
if safety:
assert len(change_inputs) > 0, "No UTXOs for {}".format(preorder_or_owner_addr)
@@ -199,8 +197,10 @@ def make_transaction(name, preorder_or_owner_addr, register_or_new_owner_addr, b
pay_fee = False
nulldata = build(name, value_hash=zonefile_hash)
outputs = make_outputs(nulldata, change_inputs, register_or_new_owner_addr, preorder_or_owner_addr, tx_fee, burn_address=burn_address, renewal_fee=renewal_fee, pay_fee=pay_fee )
outputs = make_outputs(nulldata, change_inputs, register_or_new_owner_addr, preorder_or_owner_addr, tx_fee,
burn_address=burn_address, renewal_fee=renewal_fee, pay_fee=pay_fee,
dust_included = dust_included)
return (change_inputs, outputs)

View File

@@ -96,24 +96,26 @@ def build(name, keepdata, consensus_hash):
return packaged_script
def make_outputs( data, inputs, new_name_owner_address, change_address, tx_fee=0, pay_fee=True):
def make_outputs( data, inputs, new_name_owner_address, change_address, tx_fee=0, pay_fee=True,
dust_included = False):
"""
Builds the outputs for a name transfer operation.
Raise ValueError if there are not enough inputs to make the transaction
"""
dust_fee = None
op_fee = None
dust_value = DEFAULT_DUST_FEE
if pay_fee:
dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee
total_tx_fee = tx_fee
if not dust_included:
total_tx_fee += (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
op_fee = DEFAULT_DUST_FEE
else:
dust_fee = 0
total_tx_fee = 0
op_fee = 0
return [
# main output
{"script": virtualchain.make_data_script(str(data)),
@@ -123,35 +125,39 @@ def make_outputs( data, inputs, new_name_owner_address, change_address, tx_fee=0
"value": dust_value},
# change output
{"script": virtualchain.make_payment_script(change_address),
"value": virtualchain.calculate_change_amount(inputs, op_fee, dust_fee)}
"value": virtualchain.calculate_change_amount(inputs, op_fee, total_tx_fee)}
]
def make_transaction(name, destination_address, keepdata, consensus_hash, old_owner_addr, blockchain_client, tx_fee=0, subsidize=False, safety=True):
def make_transaction(name, destination_address, keepdata, consensus_hash,
old_owner_addr, blockchain_client, tx_fee=0, subsidize=False, safety=True,
dust_included = False):
name = str(name)
destination_address = str(destination_address)
consensus_hash = str(consensus_hash)
old_owner_addr = str(old_owner_addr)
tx_fee = int(tx_fee)
assert len(consensus_hash) == LENGTH_CONSENSUS_HASH * 2
assert len(consensus_hash) == LENGTH_CONSENSUS_HASH * 2
assert is_name_valid(name)
# sanity check
pay_fee = True
if subsidize:
pay_fee = False
inputs = tx_get_unspents( old_owner_addr, blockchain_client )
if safety:
assert len(inputs) > 0, "No UTXOs for {}".format(old_owner_addr)
nulldata = build(name, keepdata, consensus_hash)
outputs = make_outputs(nulldata, inputs, destination_address, old_owner_addr, tx_fee, pay_fee=pay_fee)
outputs = make_outputs(nulldata, inputs, destination_address,
old_owner_addr, tx_fee, pay_fee=pay_fee,
dust_included = dust_included)
return (inputs, outputs)
def get_fees( inputs, outputs ):
"""

View File

@@ -95,31 +95,33 @@ def make_outputs( data, inputs, change_address, tx_fee, pay_fee=True ):
dust_fee = None
op_fee = None
dust_value = None
dust_value = None
total_tx_fee = tx_fee
if pay_fee:
dust_fee = (len(inputs) + 1) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee
if not dust_included:
total_tx_fee += (len(inputs) + 1) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
op_fee = DEFAULT_DUST_FEE
dust_value = DEFAULT_DUST_FEE
else:
# will be subsidized
dust_fee = 0
total_tx_fee = 0
op_fee = 0
dust_value = 0
return [
# main output
{"script": virtualchain.make_data_script(str(data)),
"value": 0},
# change output
{"script": virtualchain.make_payment_script(change_address),
"value": virtualchain.calculate_change_amount(inputs, op_fee, dust_fee)}
"value": virtualchain.calculate_change_amount(inputs, op_fee, total_tx_fee)}
]
def make_transaction(name, data_hash, consensus_hash, owner_addr, blockchain_client, tx_fee=0, subsidize=False, safety=True):
def make_transaction(name, data_hash, consensus_hash, owner_addr, blockchain_client,
tx_fee=0, subsidize=False, safety=True, dust_included = False):
"""
Write a name update into the blockchain.
Returns a JSON object with 'data' set to the nulldata and 'transaction_hash' set to the transaction hash on success.
@@ -133,8 +135,8 @@ def make_transaction(name, data_hash, consensus_hash, owner_addr, blockchain_cli
assert len(consensus_hash) == LENGTH_CONSENSUS_HASH * 2
assert is_name_valid(name)
# sanity check
# sanity check
pay_fee = True
if subsidize:
pay_fee = False
@@ -144,8 +146,9 @@ def make_transaction(name, data_hash, consensus_hash, owner_addr, blockchain_cli
assert len(inputs) > 0, "No UTXOs for {}".format(owner_addr)
nulldata = build(name, consensus_hash, data_hash=data_hash)
outputs = make_outputs( nulldata, inputs, owner_addr, tx_fee, pay_fee=pay_fee )
outputs = make_outputs( nulldata, inputs, owner_addr, tx_fee, pay_fee=pay_fee,
dust_included = dust_included )
return (inputs, outputs)