#!/usr/bin/env python2 # -*- coding: utf-8 -*- """ Blockstack-client ~~~~~ copyright: (c) 2014-2015 by Halfmoon Labs, Inc. copyright: (c) 2016 by Blockstack.org This file is part of Blockstack-client. Blockstack-client is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Blockstack-client is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Blockstack-client. If not, see . """ import keylib from binascii import hexlify, unhexlify from ..logger import get_logger from ..constants import ( DEFAULT_DUST_FEE, DEFAULT_OP_RETURN_FEE, LENGTH_VALUE_HASH) from ..scripts import ( hash_name, hash256_trunc128, blockstack_script_to_hex, add_magic_bytes, is_name_valid, tx_get_unspents, hash256_trunc128 ) import virtualchain log = get_logger("blockstack-client") def build(name): """ Takes in a name to import. Name must include the namespace ID. Record format: 0 2 3 39 |----|--|-----------------------------| magic op name.ns_id (37 bytes) The transaction itself will have two outputs: * the recipient * the hash of the name's associated data """ if not is_name_valid( name ): raise Exception("Invalid name '%s'" % name) readable_script = "NAME_IMPORT 0x%s" % (hexlify(name)) hex_script = blockstack_script_to_hex(readable_script) packaged_script = add_magic_bytes(hex_script) return packaged_script def make_outputs( data, inputs, recipient_address, sender_address, update_hash_b58, tx_fee): """ Builds the outputs for a name import: * [0] is the OP_RETURN * [1] is the new owner (recipient) * [2] is the update hash * [3] is the change sent to the original owner Raise ValueError if there are not enough inputs to make the transaction """ dust_fee = DEFAULT_OP_RETURN_FEE + (len(inputs) + 3) * DEFAULT_DUST_FEE + tx_fee op_fee = 2 * DEFAULT_DUST_FEE dust_value = DEFAULT_DUST_FEE return [ # main output {"script": virtualchain.make_data_script(str(data)), "value": 0}, # recipient output {"script": virtualchain.make_payment_script(recipient_address), "value": dust_value}, # update hash output {"script": virtualchain.make_payment_script(update_hash_b58), "value": dust_value}, # change output {"script": virtualchain.make_payment_script(sender_address), "value": virtualchain.calculate_change_amount(inputs, op_fee, dust_fee)} ] def make_transaction(name, recipient_address, update_hash, import_addr, blockchain_client, tx_fee=0, safety=True): name = str(name) recipient_address = str(recipient_address) update_hash = str(update_hash) import_addr = str(import_addr) tx_fee = int(tx_fee) assert is_name_valid(name) assert len(update_hash) == LENGTH_VALUE_HASH * 2 nulldata = build(name) # convert update_hash from a hex string so it looks like an address update_hash_b58 = keylib.b58check_encode( unhexlify(update_hash), version_byte=virtualchain.version_byte ) inputs = tx_get_unspents( import_addr, blockchain_client ) if safety: assert len(inputs) > 0 outputs = make_outputs(nulldata, inputs, recipient_address, import_addr, update_hash_b58, tx_fee) return (inputs, outputs) def get_fees( inputs, outputs ): """ Return (dust fee, op fee) totals op fee will be zero dust fee is the sum of the minimum output values """ return ((len(inputs) + 3) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE, 0) def snv_consensus_extras( name_rec, block_id, blockchain_name_data ): """ Given a name record most recently affected by an instance of this operation, find the dict of consensus-affecting fields from the operation that are not already present in the name record. """ ret_op = {} # reconstruct the recipient information ret_op['recipient'] = str(name_rec['sender']) ret_op['recipient_address'] = str(name_rec['address']) # the preorder hash used is the *first* preorder hash calculated in a series of NAME_IMPORTs if name_rec.has_key('preorder_hash'): ret_op['preorder_hash'] = name_rec['preorder_hash'] else: ret_op['preorder_hash'] = hash_name( str(name_rec['name']), name_rec['importer'], ret_op['recipient_address'] ) log.debug("restore preorder hash: %s --> %s (%s, %s, %s)" % (name_rec.get('preorder_hash', "None"), ret_op['preorder_hash'], name_rec['name'], name_rec['importer'], ret_op['recipient_address'])) return ret_op