mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-01-12 22:43:42 +08:00
WIP: move to a state-transition API that doesn't force the state logic
to interface with the blockchain or blockchain services. At the same time, deprecate support for pybitcoin in favor of primitives that now live in virtualchain.
This commit is contained in:
@@ -21,20 +21,24 @@
|
||||
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from pybitcoin import embed_data_in_blockchain, make_op_return_tx, make_op_return_outputs, \
|
||||
make_op_return_script, broadcast_transaction, serialize_transaction, \
|
||||
script_hex_to_address, get_unspents
|
||||
# from blockstack_utxo import get_unspents, broadcast_transaction, analyze_private_key
|
||||
import virtualchain
|
||||
from virtualchain.lib.blockchain.bitcoin import make_op_return_script, \
|
||||
calculate_change_amount, make_pay_to_address_script
|
||||
|
||||
from keylib import ECPrivateKey, ECPublicKey
|
||||
from utilitybelt import is_hex
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
from ..b40 import b40_to_hex, bin_to_b40, is_b40
|
||||
from ..config import *
|
||||
from ..scripts import *
|
||||
from ..blockchain import get_tx_inputs
|
||||
|
||||
# consensus hash fields (none for announcements)
|
||||
FIELDS = []
|
||||
|
||||
def build(message_hash, testset=False):
|
||||
def build(message_hash):
|
||||
"""
|
||||
|
||||
Record format:
|
||||
@@ -53,7 +57,7 @@ def build(message_hash, testset=False):
|
||||
|
||||
readable_script = "ANNOUNCE 0x%s" % (message_hash)
|
||||
hex_script = blockstack_script_to_hex(readable_script)
|
||||
packaged_script = add_magic_bytes(hex_script, testset=testset)
|
||||
packaged_script = add_magic_bytes(hex_script)
|
||||
|
||||
return packaged_script
|
||||
|
||||
@@ -89,6 +93,40 @@ def make_outputs( data, inputs, change_address, pay_fee=True ):
|
||||
]
|
||||
|
||||
|
||||
def state_transition(blockchain_name, message_hash, private_key, user_public_key=None):
|
||||
|
||||
# sanity check
|
||||
pay_fee = True
|
||||
if user_public_key is not None:
|
||||
pay_fee = False
|
||||
|
||||
if user_public_key is None and private_key is None:
|
||||
raise Exception("Missing both public and private key")
|
||||
|
||||
if len(message_hash) != 40:
|
||||
raise Exception("Invalid message hash: not 20 bytes")
|
||||
|
||||
if not is_hex( message_hash ):
|
||||
raise Exception("Invalid message hash: not hex")
|
||||
|
||||
pubk = None
|
||||
|
||||
if user_public_key is not None:
|
||||
# subsidizing
|
||||
pubk = ECPublicKey( user_public_key )
|
||||
|
||||
else:
|
||||
# ordering directly
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_tx_inputs( blockchain_name, from_address )
|
||||
|
||||
nulldata = build(message_hash)
|
||||
outputs = make_outputs( nulldata, inputs, from_address, pay_fee=pay_fee )
|
||||
return inputs, outputs
|
||||
|
||||
|
||||
def broadcast(message_hash, private_key, blockchain_client, testset=False, blockchain_broadcaster=None, user_public_key=None, tx_only=False):
|
||||
|
||||
# sanity check
|
||||
@@ -112,35 +150,30 @@ def broadcast(message_hash, private_key, blockchain_client, testset=False, block
|
||||
if blockchain_broadcaster is None:
|
||||
blockchain_broadcaster = blockchain_client
|
||||
|
||||
from_address = None
|
||||
inputs = None
|
||||
private_key_obj = None
|
||||
|
||||
pubk = None
|
||||
|
||||
if user_public_key is not None:
|
||||
# subsidizing
|
||||
pubk = BitcoinPublicKey( user_public_key )
|
||||
pubk = ECPublicKey( user_public_key )
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
else:
|
||||
# ordering directly
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
|
||||
elif private_key is not None:
|
||||
# ordering directly
|
||||
pubk = BitcoinPrivateKey( private_key ).public_key()
|
||||
public_key = pubk.to_hex()
|
||||
|
||||
private_key_obj, from_address, inputs = analyze_private_key(private_key, blockchain_client)
|
||||
|
||||
nulldata = build(message_hash, testset=testset)
|
||||
outputs = make_outputs( nulldata, inputs, from_address, pay_fee=pay_fee )
|
||||
|
||||
if tx_only:
|
||||
|
||||
unsigned_tx = serialize_transaction( inputs, outputs )
|
||||
unsigned_tx = tx_serialize( inputs, outputs )
|
||||
return {'unsigned_tx': unsigned_tx}
|
||||
|
||||
else:
|
||||
|
||||
signed_tx = tx_serialize_and_sign( inputs, outputs, private_key_obj )
|
||||
signed_tx = tx_serialize_sign( inputs, outputs, private_key )
|
||||
response = broadcast_transaction( signed_tx, blockchain_broadcaster )
|
||||
response.update({'data': nulldata})
|
||||
return response
|
||||
|
||||
@@ -21,11 +21,13 @@
|
||||
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from pybitcoin import embed_data_in_blockchain, \
|
||||
analyze_private_key, serialize_sign_and_broadcast, make_op_return_script, \
|
||||
make_pay_to_address_script, b58check_encode, b58check_decode, serialize_transaction
|
||||
|
||||
from pybitcoin.transactions.outputs import calculate_change_amount
|
||||
# from blockstack_utxo import get_unspents, broadcast_transaction, analyze_private_key
|
||||
import virtualchain
|
||||
from virtualchain.lib.blockchain.bitcoin import make_op_return_script, \
|
||||
calculate_change_amount, make_pay_to_address_script
|
||||
|
||||
from keylib import b58check_encode
|
||||
|
||||
from utilitybelt import is_hex
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
@@ -33,8 +35,8 @@ from ..b40 import b40_to_hex, bin_to_b40, is_b40
|
||||
from ..config import *
|
||||
from ..scripts import *
|
||||
from ..hashing import hash256_trunc128
|
||||
|
||||
from ..nameset import NAMEREC_FIELDS
|
||||
from ..blockchain import get_tx_inputs
|
||||
from ..nameset import NAMEREC_FIELDS, get_namespace_from_name
|
||||
|
||||
# consensus hash fields (ORDER MATTERS!)
|
||||
FIELDS = NAMEREC_FIELDS + [
|
||||
@@ -59,7 +61,8 @@ def get_import_update_hash_from_outputs( outputs, recipient ):
|
||||
ret = None
|
||||
count = 0
|
||||
for output in outputs:
|
||||
|
||||
|
||||
"""
|
||||
output_script = output['scriptPubKey']
|
||||
output_asm = output_script.get('asm')
|
||||
output_hex = output_script.get('hex')
|
||||
@@ -69,6 +72,13 @@ def get_import_update_hash_from_outputs( outputs, recipient ):
|
||||
|
||||
ret = hexlify( b58check_decode( str(output_addresses[0]) ) )
|
||||
break
|
||||
"""
|
||||
if output.type() != "data":
|
||||
continue
|
||||
|
||||
if output.sender_id() != recipient:
|
||||
ret = hexlify(b58check_decode( output.addresses()[0] ))
|
||||
break
|
||||
|
||||
if ret is None:
|
||||
raise Exception("No update hash found")
|
||||
@@ -76,7 +86,7 @@ def get_import_update_hash_from_outputs( outputs, recipient ):
|
||||
return ret
|
||||
|
||||
|
||||
def build(name, testset=False):
|
||||
def build(name):
|
||||
"""
|
||||
Takes in a name to import. Name must include the namespace ID.
|
||||
|
||||
@@ -96,7 +106,7 @@ def build(name, testset=False):
|
||||
|
||||
readable_script = "NAME_IMPORT 0x%s" % (hexlify(name))
|
||||
hex_script = blockstack_script_to_hex(readable_script)
|
||||
packaged_script = add_magic_bytes(hex_script, testset=testset)
|
||||
packaged_script = add_magic_bytes(hex_script)
|
||||
|
||||
return packaged_script
|
||||
|
||||
@@ -133,30 +143,46 @@ def make_outputs( data, inputs, recipient_address, sender_address, update_hash_b
|
||||
]
|
||||
|
||||
|
||||
def state_transition(name, recipient_address, update_hash, private_key):
|
||||
|
||||
blockchain_name = namespace_to_blockchain( get_namespace_from_name( name ) )
|
||||
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
from_address = pubk.address()
|
||||
inputs = get_tx_inputs( blockchain_name, from_address )
|
||||
|
||||
# NAME_IMPORT outputs
|
||||
nulldata = build(name)
|
||||
update_hash_b58 = b58check_encode( unhexlify(update_hash) )
|
||||
outputs = make_outputs(nulldata, inputs, recipient_address, from_address, update_hash_b58, format='hex')
|
||||
|
||||
return inputs, outputs
|
||||
|
||||
|
||||
def broadcast(name, recipient_address, update_hash, private_key, blockchain_client, blockchain_broadcaster=None, tx_only=False, testset=False):
|
||||
|
||||
if blockchain_broadcaster is None:
|
||||
blockchain_broadcaster = blockchain_client
|
||||
|
||||
nulldata = build(name, testset=testset)
|
||||
|
||||
# convert update_hash from a hex string so it looks like an address
|
||||
update_hash_b58 = b58check_encode( unhexlify(update_hash) )
|
||||
|
||||
# get inputs and from address
|
||||
private_key_obj, from_address, inputs = analyze_private_key(private_key, blockchain_client)
|
||||
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
|
||||
# NAME_IMPORT outputs
|
||||
nulldata = build(name, testset=testset)
|
||||
update_hash_b58 = b58check_encode( unhexlify(update_hash) )
|
||||
outputs = make_outputs(nulldata, inputs, recipient_address, from_address, update_hash_b58, format='hex')
|
||||
|
||||
if tx_only:
|
||||
|
||||
unsigned_tx = serialize_transaction( inputs, outputs )
|
||||
unsigned_tx = tx_serialize( inputs, outputs )
|
||||
return {"unsigned_tx": unsigned_tx}
|
||||
|
||||
else:
|
||||
# serialize, sign, and broadcast the tx
|
||||
response = serialize_sign_and_broadcast(inputs, outputs, private_key_obj, blockchain_broadcaster)
|
||||
signed_tx = tx_serialize_sign( inputs, outputs, private_key )
|
||||
response = broadcast_transaction( signed_tx, blockchain_broadcaster )
|
||||
# response = serialize_sign_and_broadcast(inputs, outputs, private_key_obj, blockchain_broadcaster)
|
||||
|
||||
# response = {'success': True }
|
||||
response.update({'data': nulldata})
|
||||
|
||||
@@ -21,11 +21,12 @@
|
||||
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from pybitcoin import embed_data_in_blockchain, serialize_transaction, \
|
||||
analyze_private_key, serialize_sign_and_broadcast, make_op_return_script, \
|
||||
make_pay_to_address_script, b58check_encode, b58check_decode, BlockchainInfoClient, hex_hash160
|
||||
# from blockstack_utxo import get_unspents, broadcast_transaction, analyze_private_key, serialize_sign_and_broadcast
|
||||
import virtualchain
|
||||
from virtualchain.lib.blockchain.bitcoin import make_op_return_script, \
|
||||
calculate_change_amount, make_pay_to_address_script
|
||||
|
||||
from pybitcoin.transactions.outputs import calculate_change_amount
|
||||
from keylib import ECPrivateKey
|
||||
|
||||
from utilitybelt import is_hex
|
||||
from binascii import hexlify, unhexlify
|
||||
@@ -34,6 +35,7 @@ from ..b40 import b40_to_hex, bin_to_b40, is_b40
|
||||
from ..config import *
|
||||
from ..scripts import *
|
||||
from ..hashing import hash_name
|
||||
from ..blockchain import get_tx_inputs
|
||||
|
||||
# consensus hash fields (ORDER MATTERS!)
|
||||
FIELDS = [
|
||||
@@ -117,11 +119,40 @@ def make_outputs( data, inputs, change_addr, fee, pay_fee=True, format='bin' ):
|
||||
"value": calculate_change_amount(inputs, bill, dust_fee)},
|
||||
|
||||
# burn address
|
||||
{"script_hex": make_pay_to_address_script(BLOCKSTORE_BURN_ADDRESS),
|
||||
{"script_hex": make_pay_to_address_script(BLOCKSTACK_BURN_ADDRESS),
|
||||
"value": op_fee}
|
||||
]
|
||||
|
||||
|
||||
def state_transition( namespace_id, register_addr, consensus_hash, private_key, fee ):
|
||||
"""
|
||||
Propagate a namespace.
|
||||
|
||||
Arguments:
|
||||
namespace_id human-readable (i.e. base-40) name of the namespace
|
||||
register_addr the addr of the key that will reveal the namespace (mixed into the preorder to prevent name preimage attack races)
|
||||
private_key the Bitcoin address that created this namespace, and can populate it.
|
||||
"""
|
||||
|
||||
blockchain_name = virtualchain.namespace_to_blocokchain( namespace_id )
|
||||
|
||||
pk = ECPrivateKey( private_key )
|
||||
pubk = pk.public_key()
|
||||
from_address = pubk.address()
|
||||
pubkey_hex = pubk.to_hex()
|
||||
|
||||
inputs = get_tx_outputs( blockchain_name, from_address )
|
||||
|
||||
# build custom outputs here
|
||||
pubkey_hex = pk.public_key().to_hex()
|
||||
script_pubkey = get_script_pubkey( pubkey_hex )
|
||||
|
||||
nulldata = build( namespace_id, script_pubkey, register_addr, consensus_hash )
|
||||
outputs = make_outputs(nulldata, inputs, from_address, fee, format='hex')
|
||||
|
||||
return inputs, outputs
|
||||
|
||||
|
||||
def broadcast( namespace_id, register_addr, consensus_hash, private_key, blockchain_client, fee, pay_fee=True, tx_only=False, testset=False, blockchain_broadcaster=None ):
|
||||
"""
|
||||
Propagate a namespace.
|
||||
@@ -134,26 +165,28 @@ def broadcast( namespace_id, register_addr, consensus_hash, private_key, blockch
|
||||
|
||||
if blockchain_broadcaster is None:
|
||||
blockchain_broadcaster = blockchain_client
|
||||
|
||||
pubkey_hex = BitcoinPrivateKey( private_key ).public_key().to_hex()
|
||||
|
||||
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
|
||||
# build custom outputs here
|
||||
|
||||
pubkey_hex = ECPrivateKey( private_key ).public_key().to_hex()
|
||||
script_pubkey = get_script_pubkey( pubkey_hex )
|
||||
nulldata = build( namespace_id, script_pubkey, register_addr, consensus_hash, testset=testset )
|
||||
|
||||
# get inputs and from address
|
||||
private_key_obj, from_address, inputs = analyze_private_key(private_key, blockchain_client)
|
||||
|
||||
# build custom outputs here
|
||||
outputs = make_outputs(nulldata, inputs, from_address, fee, format='hex')
|
||||
|
||||
if tx_only:
|
||||
|
||||
unsigned_tx = serialize_transaction( inputs, outputs )
|
||||
unsigned_tx = tx_serialize( inputs, outputs )
|
||||
return {"unsigned_tx": unsigned_tx}
|
||||
|
||||
else:
|
||||
# serialize, sign, and broadcast the tx
|
||||
response = serialize_sign_and_broadcast(inputs, outputs, private_key_obj, blockchain_broadcaster)
|
||||
signed_tx = tx_serialize_sign( inputs, outputs, private_key )
|
||||
response = broadcast_transaction( signed_tx, blockchain_broadcaster )
|
||||
# response = serialize_sign_and_broadcast(inputs, outputs, private_key_obj, blockchain_broadcaster)
|
||||
|
||||
# response = {'success': True }
|
||||
response.update({'data': nulldata})
|
||||
|
||||
@@ -21,15 +21,19 @@
|
||||
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from pybitcoin import embed_data_in_blockchain, BlockchainInfoClient, hex_hash160, make_op_return_tx, serialize_transaction, broadcast_transaction, make_op_return_outputs
|
||||
# from blockstack_utxo import get_unspents, broadcast_transaction, analyze_private_key
|
||||
import virtualchain
|
||||
from virtualchain.lib.blockchain.bitcoin import make_op_return_script, \
|
||||
calculate_change_amount, make_pay_to_address_script
|
||||
|
||||
from utilitybelt import is_hex
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
from ..b40 import b40_to_hex, bin_to_b40, is_b40
|
||||
from ..config import *
|
||||
from ..scripts import *
|
||||
from ..blockchain import get_tx_inputs
|
||||
|
||||
import virtualchain
|
||||
log = virtualchain.get_logger("blockstack-server")
|
||||
|
||||
from namespacereveal import FIELDS as NAMESPACE_REVEAL_FIELDS
|
||||
@@ -66,6 +70,20 @@ def build( namespace_id, testset=False ):
|
||||
return packaged_script
|
||||
|
||||
|
||||
def state_transition( namespace_id, private_key ):
|
||||
|
||||
blockchain_name = namespace_to_blockchain( namespace_id )
|
||||
nulldata = build( namespace_id )
|
||||
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
from_address = pubk.address()
|
||||
inputs = get_tx_inputs( blockchain_name, from_address )
|
||||
|
||||
# OP_RETURN outputs
|
||||
outputs = make_op_return_outputs( nulldata, inputs, from_address, fee=DEFAULT_OP_RETURN_FEE, format='hex' )
|
||||
return inputs, outputs
|
||||
|
||||
|
||||
def broadcast( namespace_id, private_key, blockchain_client, testset=False, tx_only=False, blockchain_broadcaster=None ):
|
||||
|
||||
if blockchain_broadcaster is None:
|
||||
@@ -73,15 +91,16 @@ def broadcast( namespace_id, private_key, blockchain_client, testset=False, tx_o
|
||||
|
||||
nulldata = build( namespace_id, testset=testset )
|
||||
|
||||
# get inputs and from address
|
||||
private_key_obj, from_address, inputs = analyze_private_key(private_key, blockchain_client)
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
|
||||
# OP_RETURN outputs
|
||||
outputs = make_op_return_outputs( nulldata, inputs, from_address, fee=DEFAULT_OP_RETURN_FEE, format='hex' )
|
||||
|
||||
if tx_only:
|
||||
|
||||
unsigned_tx = serialize_transaction( inputs, outputs )
|
||||
unsigned_tx = tx_serialize( inputs, outputs )
|
||||
return {'unsigned_tx': signed_tx}
|
||||
|
||||
else:
|
||||
|
||||
@@ -21,11 +21,10 @@
|
||||
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from pybitcoin import embed_data_in_blockchain, serialize_transaction, \
|
||||
analyze_private_key, serialize_sign_and_broadcast, make_op_return_script, \
|
||||
make_pay_to_address_script, b58check_encode, b58check_decode, BlockchainInfoClient, hex_hash160
|
||||
|
||||
from pybitcoin.transactions.outputs import calculate_change_amount
|
||||
# from blockstack_utxo import get_unspents, broadcast_transaction, analyze_private_key
|
||||
import virtualchain
|
||||
from virtualchain.lib.blockchain.bitcoin import make_op_return_script, \
|
||||
calculate_change_amount, make_pay_to_address_script
|
||||
|
||||
from utilitybelt import is_hex
|
||||
from binascii import hexlify, unhexlify
|
||||
@@ -36,8 +35,8 @@ from ..b40 import b40_to_hex, bin_to_b40, is_b40
|
||||
from ..config import *
|
||||
from ..scripts import blockstack_script_to_hex, add_magic_bytes
|
||||
from ..hashing import hash_name
|
||||
from ..blockchain import get_tx_inputs
|
||||
|
||||
import virtualchain
|
||||
log = virtualchain.get_logger("blockstack-log")
|
||||
|
||||
# consensus hash fields (ORDER MATTERS!)
|
||||
@@ -145,7 +144,7 @@ def namespacereveal_sanity_check( namespace_id, version, lifetime, coeff, base,
|
||||
|
||||
# version: 2 bytes
|
||||
# namespace ID: up to 19 bytes
|
||||
def build( namespace_id, version, reveal_addr, lifetime, coeff, base, bucket_exponents, nonalpha_discount, no_vowel_discount, testset=False ):
|
||||
def build( namespace_id, version, reveal_addr, lifetime, coeff, base, bucket_exponents, nonalpha_discount, no_vowel_discount ):
|
||||
"""
|
||||
Record to mark the beginning of a namespace import in the blockchain.
|
||||
This reveals the namespace ID, and encodes the preorder's namespace rules.
|
||||
@@ -196,12 +195,12 @@ def build( namespace_id, version, reveal_addr, lifetime, coeff, base, bucket_exp
|
||||
|
||||
readable_script = "NAMESPACE_REVEAL 0x%s 0x%s 0x%s 0x%s 0x%s 0x%s 0x%s" % (life_hex, coeff_hex, base_hex, bucket_hex, discount_hex, version_hex, namespace_id_hex)
|
||||
hex_script = blockstack_script_to_hex(readable_script)
|
||||
packaged_script = add_magic_bytes(hex_script, testset=testset)
|
||||
packaged_script = add_magic_bytes(hex_script)
|
||||
|
||||
return packaged_script
|
||||
|
||||
|
||||
def make_outputs( data, inputs, reveal_addr, change_addr, format='bin', testset=False ):
|
||||
def make_outputs( data, inputs, reveal_addr, change_addr, format='bin' ):
|
||||
"""
|
||||
Make outputs for a register:
|
||||
[0] OP_RETURN with the name
|
||||
@@ -227,6 +226,33 @@ def make_outputs( data, inputs, reveal_addr, change_addr, format='bin', testset=
|
||||
|
||||
|
||||
|
||||
def state_transition( namespace_id, reveal_addr, lifetime, coeff, base_cost, bucket_exponents, nonalpha_discount, no_vowel_discount, private_key ):
|
||||
"""
|
||||
Propagate a namespace.
|
||||
|
||||
Arguments:
|
||||
namespace_id human-readable (i.e. base-40) name of the namespace
|
||||
reveal_addr address to own this namespace until it is ready
|
||||
lifetime: the number of blocks for which names will be valid (pass a negative value for "infinite")
|
||||
coeff: cost multipler
|
||||
base_cost: the base cost (i.e. cost of a 1-character name), in satoshis
|
||||
bucket_exponents: bucket cost exponents to which to raise the base cost
|
||||
nonalpha_discount: discount multipler for non-alpha-character names
|
||||
no_vowel_discount: discount multipler for no-vowel names
|
||||
"""
|
||||
|
||||
blockchain_name = virtualchain.namespace_to_blockchain(namespace_id)
|
||||
nulldata = build( namespace_id, BLOCKSTACK_VERSION, reveal_addr, lifetime, coeff, base_cost, bucket_exponents, nonalpha_discount, no_vowel_discount )
|
||||
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
from_address = pubk.address()
|
||||
inputs = get_tx_inputs( blockchain_name, from_address )
|
||||
|
||||
# build custom outputs here
|
||||
outputs = make_outputs(nulldata, inputs, reveal_addr, from_address, format='hex')
|
||||
return inputs, outputs
|
||||
|
||||
|
||||
def broadcast( namespace_id, reveal_addr, lifetime, coeff, base_cost, bucket_exponents, nonalpha_discount, no_vowel_discount, private_key, blockchain_client, tx_only=False, blockchain_broadcaster=None, testset=False ):
|
||||
"""
|
||||
Propagate a namespace.
|
||||
@@ -247,19 +273,22 @@ def broadcast( namespace_id, reveal_addr, lifetime, coeff, base_cost, bucket_exp
|
||||
|
||||
nulldata = build( namespace_id, BLOCKSTACK_VERSION, reveal_addr, lifetime, coeff, base_cost, bucket_exponents, nonalpha_discount, no_vowel_discount, testset=testset )
|
||||
|
||||
# get inputs and from address
|
||||
private_key_obj, from_address, inputs = analyze_private_key(private_key, blockchain_client)
|
||||
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
|
||||
# build custom outputs here
|
||||
outputs = make_outputs(nulldata, inputs, reveal_addr, from_address, format='hex')
|
||||
|
||||
if tx_only:
|
||||
unsigned_tx = serialize_transaction( inputs, outputs )
|
||||
unsigned_tx = tx_serialize( inputs, outputs )
|
||||
return {"unsigned_tx": unsigned_tx}
|
||||
|
||||
else:
|
||||
# serialize, sign, and broadcast the tx
|
||||
response = serialize_sign_and_broadcast(inputs, outputs, private_key_obj, blockchain_broadcaster)
|
||||
signed_tx = tx_serialize_sign( inputs, outputs, private_key )
|
||||
response = broadcast_transaction( signed_tx, blockchain_broadcaster )
|
||||
# response = serialize_sign_and_broadcast(inputs, outputs, private_key_obj, blockchain_broadcaster)
|
||||
|
||||
# response = {'success': True }
|
||||
response.update({'data': nulldata})
|
||||
|
||||
@@ -21,14 +21,12 @@
|
||||
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from pybitcoin import embed_data_in_blockchain, serialize_transaction, \
|
||||
analyze_private_key, serialize_sign_and_broadcast, make_op_return_script, \
|
||||
make_pay_to_address_script, b58check_encode, b58check_decode, BlockchainInfoClient, \
|
||||
hex_hash160, bin_hash160, BitcoinPrivateKey, BitcoinPublicKey, script_hex_to_address, get_unspents, \
|
||||
make_op_return_outputs
|
||||
# from blockstack_utxo import get_unspents, broadcast_transaction, analyze_private_key, serialize_sign_and_broadcast
|
||||
import virtualchain
|
||||
from virtualchain.lib.blockchain.bitcoin import tx_serialize, script_hex_to_address, make_op_return_script, \
|
||||
calculate_change_amount, make_pay_to_address_script
|
||||
|
||||
|
||||
from pybitcoin.transactions.outputs import calculate_change_amount
|
||||
from keylib import ECPrivateKey, ECPublicKey
|
||||
from utilitybelt import is_hex
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
@@ -36,6 +34,8 @@ from ..b40 import b40_to_hex, is_b40
|
||||
from ..config import *
|
||||
from ..scripts import *
|
||||
from ..hashing import hash_name
|
||||
from ..blockchain import get_tx_inputs
|
||||
from ..nameset import get_namespace_from_name
|
||||
|
||||
# consensus hash fields (ORDER MATTERS!)
|
||||
FIELDS = [
|
||||
@@ -52,7 +52,7 @@ FIELDS = [
|
||||
'op_fee', # blockstack fee (sent to burn address)
|
||||
]
|
||||
|
||||
def build(name, script_pubkey, register_addr, consensus_hash, name_hash=None, testset=False):
|
||||
def build(name, script_pubkey, register_addr, consensus_hash, name_hash=None):
|
||||
"""
|
||||
Takes a name, including the namespace ID (but not the id: scheme), a script_publickey to prove ownership
|
||||
of the subsequent NAME_REGISTER operation, and the current consensus hash for this block (to prove that the
|
||||
@@ -82,7 +82,7 @@ def build(name, script_pubkey, register_addr, consensus_hash, name_hash=None, te
|
||||
|
||||
script = 'NAME_PREORDER 0x%s 0x%s' % (name_hash, consensus_hash)
|
||||
hex_script = blockstack_script_to_hex(script)
|
||||
packaged_script = add_magic_bytes(hex_script, testset=testset)
|
||||
packaged_script = add_magic_bytes(hex_script)
|
||||
|
||||
return packaged_script
|
||||
|
||||
@@ -111,11 +111,46 @@ def make_outputs( data, inputs, sender_addr, fee, format='bin' ):
|
||||
"value": calculate_change_amount(inputs, bill, dust_fee)},
|
||||
|
||||
# burn address
|
||||
{"script_hex": make_pay_to_address_script(BLOCKSTORE_BURN_ADDRESS),
|
||||
{"script_hex": make_pay_to_address_script(BLOCKSTACK_BURN_ADDRESS),
|
||||
"value": op_fee}
|
||||
]
|
||||
|
||||
|
||||
def state_transition(name, private_key, register_addr, consensus_hash, fee, subsidy_public_key=None):
|
||||
"""
|
||||
Builds and broadcasts a preorder transaction.
|
||||
|
||||
@subsidy_public_key: if given, the public part of the subsidy key
|
||||
"""
|
||||
|
||||
namespace_id = get_namespace_from_name( name )
|
||||
blockchain_name = namespace_to_blockchain( namespace_id )
|
||||
|
||||
# sanity check
|
||||
if subsidy_public_key is None and private_key is None:
|
||||
raise Exception("Missing both client public and private key")
|
||||
|
||||
pubk = None
|
||||
script_pubkey = None # to be mixed into preorder hash
|
||||
|
||||
if subsidy_public_key is not None:
|
||||
# subsidizing
|
||||
pubk = ECPublicKey( subsidy_public_key )
|
||||
script_pubkey = get_script_pubkey( subsidy_public_key )
|
||||
|
||||
else:
|
||||
# ordering directly
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
script_pubkey = get_script_pubkey( public_key )
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_tx_inputs( blockchain_name, from_address )
|
||||
|
||||
nulldata = build( name, script_pubkey, register_addr, consensus_hash, testset=testset)
|
||||
outputs = make_outputs(nulldata, inputs, from_address, fee, format='hex')
|
||||
return inputs, outputs
|
||||
|
||||
|
||||
def broadcast(name, private_key, register_addr, consensus_hash, blockchain_client, fee, blockchain_broadcaster=None, subsidy_public_key=None, tx_only=False, testset=False):
|
||||
"""
|
||||
Builds and broadcasts a preorder transaction.
|
||||
@@ -134,40 +169,35 @@ def broadcast(name, private_key, register_addr, consensus_hash, blockchain_clien
|
||||
if blockchain_broadcaster is None:
|
||||
blockchain_broadcaster = blockchain_client
|
||||
|
||||
from_address = None # change address
|
||||
inputs = None
|
||||
private_key_obj = None
|
||||
pubk = None
|
||||
script_pubkey = None # to be mixed into preorder hash
|
||||
|
||||
if subsidy_public_key is not None:
|
||||
# subsidizing
|
||||
pubk = BitcoinPublicKey( subsidy_public_key )
|
||||
|
||||
from_address = BitcoinPublicKey( subsidy_public_key ).address()
|
||||
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
pubk = ECPublicKey( subsidy_public_key )
|
||||
script_pubkey = get_script_pubkey( subsidy_public_key )
|
||||
|
||||
else:
|
||||
# ordering directly
|
||||
pubk = BitcoinPrivateKey( private_key ).public_key()
|
||||
public_key = pubk.to_hex()
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
script_pubkey = get_script_pubkey( public_key )
|
||||
|
||||
# get inputs and from address using private key
|
||||
private_key_obj, from_address, inputs = analyze_private_key(private_key, blockchain_client)
|
||||
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
|
||||
nulldata = build( name, script_pubkey, register_addr, consensus_hash, testset=testset)
|
||||
outputs = make_outputs(nulldata, inputs, from_address, fee, format='hex')
|
||||
|
||||
if tx_only:
|
||||
|
||||
unsigned_tx = serialize_transaction( inputs, outputs )
|
||||
unsigned_tx = tx_serialize( inputs, outputs )
|
||||
return {"unsigned_tx": unsigned_tx}
|
||||
|
||||
else:
|
||||
# serialize, sign, and broadcast the tx
|
||||
response = serialize_sign_and_broadcast(inputs, outputs, private_key_obj, blockchain_broadcaster)
|
||||
signed_tx = tx_serialize_sign( inputs, outputs, private_key )
|
||||
response = broadcast_transaction( inputs, outputs, blockchain_broadcaster )
|
||||
# response = serialize_sign_and_broadcast(inputs, outputs, private_key_obj, blockchain_broadcaster)
|
||||
response.update({'data': nulldata})
|
||||
return response
|
||||
|
||||
@@ -225,7 +255,7 @@ def get_fees( inputs, outputs ):
|
||||
log.error("outputs[2] has no decipherable burn address")
|
||||
return (None, None)
|
||||
|
||||
if addr_hash != BLOCKSTORE_BURN_ADDRESS:
|
||||
if addr_hash != BLOCKSTACK_BURN_ADDRESS:
|
||||
log.error("outputs[2] is not the burn address")
|
||||
return (None, None)
|
||||
|
||||
|
||||
@@ -21,12 +21,12 @@
|
||||
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from pybitcoin import embed_data_in_blockchain, serialize_transaction, \
|
||||
analyze_private_key, serialize_sign_and_broadcast, make_op_return_script, \
|
||||
make_pay_to_address_script, b58check_encode, b58check_decode, BlockchainInfoClient, hex_hash160, \
|
||||
BitcoinPrivateKey, get_unspents, script_hex_to_address
|
||||
# from blockstack_utxo import get_unspents, broadcast_transaction, analyze_private_key
|
||||
import virtualchain
|
||||
from virtualchain.lib.blockchain.bitcoin import tx_serialize, script_hex_to_address, make_op_return_script, \
|
||||
calculate_change_amount, make_pay_to_address_script
|
||||
|
||||
from pybitcoin.transactions.outputs import calculate_change_amount
|
||||
from keylib import ECPrivateKey, ECPublicKey
|
||||
from utilitybelt import is_hex
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
@@ -34,9 +34,9 @@ from ..b40 import b40_to_hex, bin_to_b40, is_b40
|
||||
from ..config import *
|
||||
from ..scripts import *
|
||||
from ..hashing import hash256_trunc128
|
||||
from ..nameset import NAMEREC_FIELDS
|
||||
from ..nameset import NAMEREC_FIELDS, get_namespace_from_name
|
||||
from ..blockchain import get_tx_inputs
|
||||
|
||||
import virtualchain
|
||||
log = virtualchain.get_logger("blockstack-server")
|
||||
|
||||
# consensus hash fields (ORDER MATTERS!)
|
||||
@@ -61,6 +61,7 @@ def get_registration_recipient_from_outputs( outputs ):
|
||||
ret = None
|
||||
for output in outputs:
|
||||
|
||||
"""
|
||||
output_script = output['scriptPubKey']
|
||||
output_asm = output_script.get('asm')
|
||||
output_hex = output_script.get('hex')
|
||||
@@ -72,6 +73,12 @@ def get_registration_recipient_from_outputs( outputs ):
|
||||
# ret = (output_hex, output_addresses[0])
|
||||
ret = output_hex
|
||||
break
|
||||
"""
|
||||
if output.type() != "data":
|
||||
continue
|
||||
|
||||
ret = output.sender_id()
|
||||
break
|
||||
|
||||
if ret is None:
|
||||
raise Exception("No registration address found")
|
||||
@@ -79,7 +86,7 @@ def get_registration_recipient_from_outputs( outputs ):
|
||||
return ret
|
||||
|
||||
|
||||
def build(name, testset=False):
|
||||
def build(name):
|
||||
"""
|
||||
Takes in the name that was preordered, including the namespace ID (but not the id: scheme)
|
||||
Returns a hex string representing up to LENGTHS['blockchain_id_name'] bytes.
|
||||
@@ -97,7 +104,7 @@ def build(name, testset=False):
|
||||
|
||||
readable_script = "NAME_REGISTRATION 0x%s" % (hexlify(name))
|
||||
hex_script = blockstack_script_to_hex(readable_script)
|
||||
packaged_script = add_magic_bytes(hex_script, testset=testset)
|
||||
packaged_script = add_magic_bytes(hex_script)
|
||||
|
||||
return packaged_script
|
||||
|
||||
@@ -168,12 +175,46 @@ def make_outputs( data, change_inputs, register_addr, change_addr, renewal_fee=N
|
||||
outputs.append(
|
||||
|
||||
# burn address (when renewing)
|
||||
{"script_hex": make_pay_to_address_script(BLOCKSTORE_BURN_ADDRESS),
|
||||
{"script_hex": make_pay_to_address_script(BLOCKSTACK_BURN_ADDRESS),
|
||||
"value": op_fee}
|
||||
)
|
||||
|
||||
return outputs
|
||||
|
||||
|
||||
def state_transition(name, private_key, register_addr, renewal_fee=None, user_public_key=None, subsidy_public_key=None):
|
||||
|
||||
namespace_id = get_namespace_from_name( name )
|
||||
blockchain_name = namespace_to_blockchain( namespace_id )
|
||||
|
||||
if subsidy_public_key is None and private_key is None:
|
||||
raise Exception("Missing both public and private key")
|
||||
|
||||
subsidized_renewal = False
|
||||
|
||||
if subsidy_public_key is not None:
|
||||
# subsidizing
|
||||
|
||||
if user_public_key is not None and renewal_fee is not None:
|
||||
# renewing, and subsidizing the renewal
|
||||
pubk = ECPublicKey( user_public_key )
|
||||
subsidized_renewal = True
|
||||
|
||||
else:
|
||||
# registering or renewing under the subsidy key
|
||||
pubk = ECPublicKey( subsidy_public_key )
|
||||
|
||||
elif private_key is not None:
|
||||
# ordering directly
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
|
||||
from_address = pubk.address()
|
||||
unspents = get_tx_inputs( blockchain_name, from_address )
|
||||
|
||||
nulldata = build(name)
|
||||
outputs = make_outputs(nulldata, change_inputs, register_addr, from_address, renewal_fee=renewal_fee, pay_fee=(not subsidized_renewal), format='hex')
|
||||
return inputs, outputs
|
||||
|
||||
|
||||
def broadcast(name, private_key, register_addr, blockchain_client, renewal_fee=None, blockchain_broadcaster=None, tx_only=False, user_public_key=None, subsidy_public_key=None, testset=False):
|
||||
|
||||
@@ -190,47 +231,42 @@ def broadcast(name, private_key, register_addr, blockchain_client, renewal_fee=N
|
||||
|
||||
if blockchain_broadcaster is None:
|
||||
blockchain_broadcaster = blockchain_client
|
||||
|
||||
from_address = None
|
||||
change_inputs = None
|
||||
private_key_obj = None
|
||||
|
||||
subsidized_renewal = False
|
||||
|
||||
if subsidy_public_key is not None:
|
||||
# subsidizing
|
||||
pubk = BitcoinPublicKey( subsidy_public_key )
|
||||
|
||||
if user_public_key is not None and renewal_fee is not None:
|
||||
# renewing, and subsidizing the renewal
|
||||
from_address = BitcoinPublicKey( user_public_key ).address()
|
||||
pubk = ECPublicKey( user_public_key )
|
||||
subsidized_renewal = True
|
||||
|
||||
else:
|
||||
# registering or renewing under the subsidy key
|
||||
from_address = pubk.address()
|
||||
|
||||
change_inputs = get_unspents( from_address, blockchain_client )
|
||||
pubk = ECPublicKey( subsidy_public_key )
|
||||
|
||||
elif private_key is not None:
|
||||
# ordering directly
|
||||
pubk = BitcoinPrivateKey( private_key ).public_key()
|
||||
public_key = pubk.to_hex()
|
||||
|
||||
# get inputs and from address using private key
|
||||
private_key_obj, from_address, change_inputs = analyze_private_key(private_key, blockchain_client)
|
||||
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
|
||||
from_address = pubk.address()
|
||||
unspents = get_unspents( from_ddress, blockchain_client )
|
||||
|
||||
nulldata = build(name, testset=testset)
|
||||
outputs = make_outputs(nulldata, change_inputs, register_addr, from_address, renewal_fee=renewal_fee, pay_fee=(not subsidized_renewal), format='hex')
|
||||
|
||||
if tx_only:
|
||||
|
||||
unsigned_tx = serialize_transaction( change_inputs, outputs )
|
||||
unsigned_tx = tx_serialize( change_inputs, outputs )
|
||||
return {"unsigned_tx": unsigned_tx}
|
||||
|
||||
else:
|
||||
|
||||
# serialize, sign, and broadcast the tx
|
||||
response = serialize_sign_and_broadcast(change_inputs, outputs, private_key_obj, blockchain_broadcaster)
|
||||
signed_tx = tx_serialize_sign( change_inputs, outputs, private_key )
|
||||
response = broadcast_transaction( signed_tx, blockchain_broadcaster )
|
||||
# response = serialize_sign_and_broadcast(change_inputs, outputs, private_key_obj, blockchain_broadcaster)
|
||||
response.update({'data': nulldata})
|
||||
return response
|
||||
|
||||
@@ -295,7 +331,7 @@ def get_fees( inputs, outputs ):
|
||||
if addr_hash is None:
|
||||
return (None, None)
|
||||
|
||||
if addr_hash != BLOCKSTORE_BURN_PUBKEY_HASH:
|
||||
if addr_hash != BLOCKSTACK_BURN_PUBKEY_HASH:
|
||||
return (None, None)
|
||||
|
||||
dust_fee = (len(inputs) + 3) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE
|
||||
|
||||
@@ -21,23 +21,25 @@
|
||||
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from pybitcoin import embed_data_in_blockchain, make_op_return_tx, make_op_return_outputs, \
|
||||
make_op_return_script, broadcast_transaction, serialize_transaction, \
|
||||
script_hex_to_address, get_unspents
|
||||
# from blockstack_utxo import get_unspents, broadcast_transaction, analyze_private_key
|
||||
import virtualchain
|
||||
from virtualchain.lib.blockchain.bitcoin import tx_serialize, script_hex_to_address, make_op_return_script
|
||||
from utilitybelt import is_hex
|
||||
from binascii import hexlify, unhexlify
|
||||
from keylib import ECPublicKey, ECPrivateKey
|
||||
|
||||
from ..b40 import b40_to_hex, bin_to_b40, is_b40
|
||||
from ..config import *
|
||||
from ..scripts import *
|
||||
|
||||
from ..nameset import NAMEREC_FIELDS
|
||||
from ..blockchain import get_tx_inputs
|
||||
from ..nameset import NAMEREC_FIELDS, get_namespace_from_name
|
||||
|
||||
# consensus hash fields (ORDER MATTERS!)
|
||||
FIELDS = NAMEREC_FIELDS
|
||||
|
||||
|
||||
def build(name, testset=False):
|
||||
def build(name):
|
||||
"""
|
||||
Takes in the name, including the namespace ID (but not the id: scheme)
|
||||
Returns a hex string representing up to LENGTHS['blockchain_id_name'] bytes.
|
||||
@@ -55,7 +57,7 @@ def build(name, testset=False):
|
||||
|
||||
readable_script = "NAME_REVOKE 0x%s" % (hexlify(name))
|
||||
hex_script = blockstack_script_to_hex(readable_script)
|
||||
packaged_script = add_magic_bytes(hex_script, testset=testset)
|
||||
packaged_script = add_magic_bytes(hex_script)
|
||||
|
||||
return packaged_script
|
||||
|
||||
@@ -91,8 +93,40 @@ def make_outputs( data, inputs, change_address, pay_fee=True ):
|
||||
]
|
||||
|
||||
|
||||
def broadcast(name, private_key, blockchain_client, testset=False, blockchain_broadcaster=None, user_public_key=None, tx_only=False):
|
||||
def state_transition(name, private_key, user_public_key=None):
|
||||
|
||||
namespace_id = get_namespace_from_name( name )
|
||||
blockchain_name = namespace_to_blockchain( namespace_id )
|
||||
|
||||
# sanity check
|
||||
pay_fee = True
|
||||
if user_public_key is not None:
|
||||
pay_fee = False
|
||||
|
||||
pubk = None
|
||||
if user_public_key is not None:
|
||||
# subsidizing
|
||||
pubk = ECPublicKey( user_public_key )
|
||||
|
||||
else:
|
||||
# ordering directly
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
|
||||
nulldata = build(name, testset=testset)
|
||||
outputs = make_outputs( nulldata, inputs, from_address, pay_fee=pay_fee )
|
||||
|
||||
return inputs, outputs
|
||||
|
||||
|
||||
|
||||
def broadcast(name, private_key, user_public_key=None):
|
||||
|
||||
namespace_id = get_namespace_from_name( name )
|
||||
blockchain_name = namespace_to_blockchain( namespace_id )
|
||||
|
||||
# sanity check
|
||||
pay_fee = True
|
||||
if user_public_key is not None:
|
||||
@@ -108,30 +142,25 @@ def broadcast(name, private_key, blockchain_client, testset=False, blockchain_br
|
||||
if blockchain_broadcaster is None:
|
||||
blockchain_broadcaster = blockchain_client
|
||||
|
||||
from_address = None
|
||||
inputs = None
|
||||
private_key_obj = None
|
||||
|
||||
|
||||
pubk = None
|
||||
if user_public_key is not None:
|
||||
# subsidizing
|
||||
pubk = BitcoinPublicKey( user_public_key )
|
||||
pubk = ECPublicKey( user_public_key )
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
|
||||
elif private_key is not None:
|
||||
else:
|
||||
# ordering directly
|
||||
pubk = BitcoinPrivateKey( private_key ).public_key()
|
||||
public_key = pubk.to_hex()
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
|
||||
private_key_obj, from_address, inputs = analyze_private_key(private_key, blockchain_client)
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
|
||||
nulldata = build(name, testset=testset)
|
||||
outputs = make_outputs( nulldata, inputs, from_address, pay_fee=pay_fee )
|
||||
|
||||
|
||||
if tx_only:
|
||||
|
||||
unsigned_tx = serialize_transaction( inputs, outputs )
|
||||
unsigned_tx = tx_serialize( inputs, outputs )
|
||||
return {'unsigned_tx': unsigned_tx}
|
||||
|
||||
else:
|
||||
|
||||
@@ -21,20 +21,24 @@
|
||||
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from pybitcoin import embed_data_in_blockchain, serialize_transaction, \
|
||||
analyze_private_key, serialize_sign_and_broadcast, make_op_return_script, \
|
||||
make_pay_to_address_script, BitcoinPrivateKey, BitcoinPublicKey, get_unspents, script_hex_to_address
|
||||
|
||||
from pybitcoin.transactions.outputs import calculate_change_amount
|
||||
from keylib import ECPrivateKey, ECPublicKey
|
||||
from utilitybelt import is_hex
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
from blockstack_utxo import analyze_private_key, get_unspents, broadcast_transaction
|
||||
|
||||
import virtualchain.lib.blockchain.bitcoin as virtualchain_bitcoin
|
||||
from virtualchain_bitcoin import make_pay_to_address_script, \
|
||||
make_op_return_script, script_hex_to_address, calculate_change_amount, \
|
||||
calculate_change_amount, tx_serialize
|
||||
|
||||
from ..b40 import b40_to_hex, bin_to_b40, is_b40
|
||||
from ..config import *
|
||||
from ..scripts import *
|
||||
from ..hashing import hash256_trunc128
|
||||
|
||||
from ..nameset import NAMEREC_FIELDS
|
||||
from ..nameset import NAMEREC_FIELDS, get_namespace_from_name
|
||||
from ..blockchain import get_tx_inputs
|
||||
|
||||
# consensus hash fields (ORDER MATTERS!)
|
||||
FIELDS = NAMEREC_FIELDS + [
|
||||
@@ -56,7 +60,8 @@ def get_transfer_recipient_from_outputs( outputs ):
|
||||
|
||||
ret = None
|
||||
for output in outputs:
|
||||
|
||||
|
||||
"""
|
||||
output_script = output['scriptPubKey']
|
||||
output_asm = output_script.get('asm')
|
||||
output_hex = output_script.get('hex')
|
||||
@@ -66,6 +71,12 @@ def get_transfer_recipient_from_outputs( outputs ):
|
||||
|
||||
ret = output_hex
|
||||
break
|
||||
"""
|
||||
if output.type() != "data":
|
||||
break
|
||||
|
||||
ret = output.sender_id()
|
||||
break
|
||||
|
||||
if ret is None:
|
||||
raise Exception("No recipients found")
|
||||
@@ -90,7 +101,7 @@ def transfer_sanity_check( name, consensus_hash ):
|
||||
return True
|
||||
|
||||
|
||||
def build(name, keepdata, consensus_hash, testset=False):
|
||||
def build(name, keepdata, consensus_hash):
|
||||
"""
|
||||
Takes in a name to transfer. Name must include the namespace ID, but not the scheme.
|
||||
|
||||
@@ -118,7 +129,7 @@ def build(name, keepdata, consensus_hash, testset=False):
|
||||
|
||||
readable_script = 'NAME_TRANSFER 0x%s 0x%s 0x%s' % (disposition_hex, name_hash, consensus_hash)
|
||||
hex_script = blockstack_script_to_hex(readable_script)
|
||||
packaged_script = add_magic_bytes(hex_script, testset=testset)
|
||||
packaged_script = add_magic_bytes(hex_script)
|
||||
|
||||
return packaged_script
|
||||
|
||||
@@ -153,6 +164,37 @@ def make_outputs( data, inputs, new_name_owner_address, change_address, pay_fee=
|
||||
]
|
||||
|
||||
|
||||
def state_transition(name, destination_address, keepdata, consensus_hash, private_key, user_public_key=None):
|
||||
|
||||
namespace_id = get_namespace_from_name(name)
|
||||
blockchain_name = namespace_to_blockchain( namespace_id )
|
||||
|
||||
# sanity check
|
||||
pay_fee = True
|
||||
if user_public_key is not None:
|
||||
pay_fee = False
|
||||
|
||||
if user_public_key is None and private_key is None:
|
||||
raise Exception("Missing both public and private key")
|
||||
|
||||
pubk = None
|
||||
|
||||
if user_public_key is not None:
|
||||
# subsidizing
|
||||
pubk = ECPublicKey( user_public_key )
|
||||
|
||||
else:
|
||||
# ordering directly
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_tx_inputs( blockchain_name, from_address )
|
||||
|
||||
nulldata = build(name, keepdata, consensus_hash )
|
||||
outputs = make_outputs(nulldata, inputs, destination_address, from_address, pay_fee=pay_fee, format='hex')
|
||||
return inputs, outputs
|
||||
|
||||
|
||||
def broadcast(name, destination_address, keepdata, consensus_hash, private_key, blockchain_client, blockchain_broadcaster=None, tx_only=False, user_public_key=None, testset=False):
|
||||
|
||||
# sanity check
|
||||
@@ -169,37 +211,33 @@ def broadcast(name, destination_address, keepdata, consensus_hash, private_key,
|
||||
|
||||
if blockchain_broadcaster is None:
|
||||
blockchain_broadcaster = blockchain_client
|
||||
|
||||
from_address = None
|
||||
inputs = None
|
||||
private_key_obj = None
|
||||
|
||||
pubk = None
|
||||
|
||||
if user_public_key is not None:
|
||||
# subsidizing
|
||||
pubk = BitcoinPublicKey( user_public_key )
|
||||
pubk = ECPublicKey( user_public_key )
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
|
||||
elif private_key is not None:
|
||||
else:
|
||||
# ordering directly
|
||||
pubk = BitcoinPrivateKey( private_key ).public_key()
|
||||
public_key = pubk.to_hex()
|
||||
|
||||
# get inputs and from address using private key
|
||||
private_key_obj, from_address, inputs = analyze_private_key(private_key, blockchain_client)
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
|
||||
nulldata = build(name, keepdata, consensus_hash, testset=testset)
|
||||
outputs = make_outputs(nulldata, inputs, destination_address, from_address, pay_fee=pay_fee, format='hex')
|
||||
|
||||
if tx_only:
|
||||
|
||||
unsigned_tx = serialize_transaction( inputs, outputs )
|
||||
unsigned_tx = tx_serialize( inputs, outputs )
|
||||
return {"unsigned_tx": unsigned_tx}
|
||||
|
||||
else:
|
||||
# serialize, sign, and broadcast the tx
|
||||
response = serialize_sign_and_broadcast(inputs, outputs, private_key_obj, blockchain_broadcaster)
|
||||
signed_tx = tx_serialize_sign( inputs, outputs, private_key )
|
||||
response = broadcast_transaction( signed_tx, blockchain_broadcaster )
|
||||
# response = serialize_sign_and_broadcast(inputs, outputs, private_key_obj, blockchain_broadcaster)
|
||||
response.update({'data': nulldata})
|
||||
return response
|
||||
|
||||
|
||||
@@ -21,10 +21,8 @@
|
||||
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from pybitcoin import embed_data_in_blockchain, make_op_return_tx, BlockchainInfoClient, BitcoinPrivateKey, \
|
||||
BitcoinPublicKey, get_unspents, script_hex_to_address, hex_hash160, broadcast_transaction, serialize_transaction, \
|
||||
make_op_return_outputs, make_op_return_script
|
||||
|
||||
from blockstack_utxo import get_unspents, broadcast_transaction
|
||||
from keylib import ECPrivateKey, ECPublicKey
|
||||
from utilitybelt import is_hex
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
@@ -33,9 +31,13 @@ from ..config import *
|
||||
from ..scripts import *
|
||||
from ..hashing import hash256_trunc128
|
||||
|
||||
from ..nameset import NAMEREC_FIELDS
|
||||
from ..nameset import NAMEREC_FIELDS, get_namespace_from_name
|
||||
from ..blockchain import get_tx_inputs
|
||||
|
||||
import virtualchain
|
||||
from virtualchain import hex_hash160
|
||||
from virtualchain.lib.blockchain.bitcoin import script_hex_to_address, make_op_return_script, tx_serialize, tx_serialize_and_sign, tx_output_is_op_return
|
||||
|
||||
log = virtualchain.get_logger("blockstack-server")
|
||||
|
||||
# consensus hash fields (ORDER MATTERS!)
|
||||
@@ -64,7 +66,7 @@ def update_sanity_test( name, consensus_hash, data_hash ):
|
||||
return True
|
||||
|
||||
|
||||
def build(name, consensus_hash, data_hash=None, testset=False):
|
||||
def build(name, consensus_hash, data_hash=None):
|
||||
"""
|
||||
Takes in the name to update the data for and the data update itself.
|
||||
Name must include the namespace ID, but not the scheme.
|
||||
@@ -84,7 +86,7 @@ def build(name, consensus_hash, data_hash=None, testset=False):
|
||||
|
||||
readable_script = 'NAME_UPDATE 0x%s 0x%s' % (hex_name, data_hash)
|
||||
hex_script = blockstack_script_to_hex(readable_script)
|
||||
packaged_script = add_magic_bytes(hex_script, testset=testset)
|
||||
packaged_script = add_magic_bytes(hex_script)
|
||||
|
||||
return packaged_script
|
||||
|
||||
@@ -120,6 +122,40 @@ def make_outputs( data, inputs, change_address, pay_fee=True ):
|
||||
]
|
||||
|
||||
|
||||
def state_transition(name, data_hash, consensus_hash, private_key, user_public_key=None):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
namespace_id = get_namespace_from_name(name)
|
||||
blockchain_name = namespace_to_blockchain( namespace_id )
|
||||
|
||||
# sanity check
|
||||
pay_fee = True
|
||||
if user_public_key is not None:
|
||||
pay_fee = False
|
||||
|
||||
if user_public_key is None and private_key is None:
|
||||
raise Exception("Missing both public and private key")
|
||||
|
||||
pubk = None
|
||||
if user_public_key is not None:
|
||||
# subsidizing
|
||||
pubk = ECPublicKey( user_public_key )
|
||||
|
||||
else:
|
||||
# ordering directly
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_tx_inputs( blockchain_name, from_address )
|
||||
|
||||
nulldata = build(name, consensus_hash, data_hash=data_hash)
|
||||
outputs = make_outputs( nulldata, inputs, from_address, pay_fee=pay_fee )
|
||||
return inputs, outputs
|
||||
|
||||
|
||||
def broadcast(name, data_hash, consensus_hash, private_key, blockchain_client, blockchain_broadcaster=None, tx_only=False, user_public_key=None, testset=False):
|
||||
"""
|
||||
Write a name update into the blockchain.
|
||||
@@ -141,37 +177,29 @@ def broadcast(name, data_hash, consensus_hash, private_key, blockchain_client, b
|
||||
if blockchain_broadcaster is None:
|
||||
blockchain_broadcaster = blockchain_client
|
||||
|
||||
from_address = None
|
||||
inputs = None
|
||||
private_key_obj = None
|
||||
|
||||
pubk = None
|
||||
if user_public_key is not None:
|
||||
# subsidizing
|
||||
pubk = BitcoinPublicKey( user_public_key )
|
||||
from_address = pubk.address()
|
||||
|
||||
# get inputs from utxo provider
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
pubk = ECPublicKey( user_public_key )
|
||||
|
||||
elif private_key is not None:
|
||||
else:
|
||||
# ordering directly
|
||||
pubk = BitcoinPrivateKey( private_key ).public_key()
|
||||
public_key = pubk.to_hex()
|
||||
|
||||
# get inputs and from address using private key
|
||||
private_key_obj, from_address, inputs = analyze_private_key(private_key, blockchain_client)
|
||||
|
||||
pubk = ECPrivateKey( private_key ).public_key()
|
||||
|
||||
from_address = pubk.address()
|
||||
inputs = get_unspents( from_address, blockchain_client )
|
||||
|
||||
nulldata = build(name, consensus_hash, data_hash=data_hash, testset=testset)
|
||||
outputs = make_outputs( nulldata, inputs, from_address, pay_fee=pay_fee )
|
||||
|
||||
if tx_only:
|
||||
|
||||
unsigned_tx = serialize_transaction( inputs, outputs )
|
||||
unsigned_tx = tx_serialize( inputs, outputs )
|
||||
return {'unsigned_tx': unsigned_tx}
|
||||
|
||||
else:
|
||||
|
||||
signed_tx = tx_serialize_and_sign( inputs, outputs, private_key_obj )
|
||||
signed_tx = tx_serialize_sign( inputs, outputs, private_key )
|
||||
response = broadcast_transaction( signed_tx, blockchain_broadcaster )
|
||||
response.update({'data': nulldata})
|
||||
return response
|
||||
|
||||
Reference in New Issue
Block a user