mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-03-28 23:58:29 +08:00
374 lines
14 KiB
Python
374 lines
14 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from __future__ import print_function
|
|
|
|
"""
|
|
Blockstack-client
|
|
~~~~~
|
|
copyright: (c) 2014 by Halfmoon Labs, Inc.
|
|
copyright: (c) 2015 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 <http://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import virtualchain
|
|
from .version import __version__, __version_major__, __version_minor__, __version_patch__
|
|
|
|
BLOCKSTACK_TEST = os.environ.get('BLOCKSTACK_TEST', None)
|
|
BLOCKSTACK_TEST_NODEBUG = os.environ.get('BLOCKSTACK_TEST_NODEBUG', None)
|
|
BLOCKSTACK_DEBUG = os.environ.get('BLOCKSTACK_DEBUG', None)
|
|
BLOCKSTACK_TEST_FIRST_BLOCK = os.environ.get('BLOCKSTACK_TEST_FIRST_BLOCK', None)
|
|
|
|
DEBUG = False
|
|
if BLOCKSTACK_TEST is not None and BLOCKSTACK_TEST_NODEBUG is None:
|
|
DEBUG = True
|
|
|
|
if BLOCKSTACK_DEBUG is not None:
|
|
DEBUG = True
|
|
|
|
if os.environ.get("DISABLE_CLIENT_DEBUG") == "1":
|
|
DEBUG = False
|
|
|
|
TX_MIN_CONFIRMATIONS = 6
|
|
if os.environ.get("BLOCKSTACK_TEST", None) is not None:
|
|
# test environment
|
|
TX_MIN_CONFIRMATIONS = 0
|
|
|
|
VERSION = __version__
|
|
SERIES_VERSION = "{}.{}.{}".format(__version_major__, __version_minor__, __version_patch__)
|
|
|
|
DEFAULT_BLOCKSTACKD_PORT = 6264 # blockstackd port
|
|
DEFAULT_BLOCKSTACKD_SERVER = 'node.blockstack.org'
|
|
|
|
DEFAULT_API_PORT = 6270 # RPC endpoint port
|
|
|
|
# initialize to default settings
|
|
BLOCKSTACKD_SERVER = DEFAULT_BLOCKSTACKD_SERVER
|
|
BLOCKSTACKD_PORT = DEFAULT_BLOCKSTACKD_PORT
|
|
WALLET_PASSWORD_LENGTH = 8
|
|
WALLET_DECRYPT_MAX_TRIES = 5
|
|
WALLET_DECRYPT_BACKOFF_RESET = 3600
|
|
|
|
BLOCKSTACK_DEFAULT_STORAGE_DRIVERS = 'disk,blockstack_resolver,blockstack_server,http,dht'
|
|
|
|
# storage drivers that must successfully acknowledge each write
|
|
BLOCKSTACK_REQUIRED_STORAGE_DRIVERS_WRITE = 'disk,blockstack_server,dht'
|
|
|
|
DEFAULT_TIMEOUT = 30 # in secs
|
|
|
|
""" transaction fee configs
|
|
"""
|
|
|
|
DEFAULT_OP_RETURN_FEE = 10000
|
|
DEFAULT_DUST_FEE = 5500
|
|
DEFAULT_OP_RETURN_VALUE = 0
|
|
DEFAULT_FEE_PER_KB = 10000
|
|
|
|
|
|
""" magic bytes configs
|
|
"""
|
|
|
|
MAGIC_BYTES = 'id'
|
|
|
|
# borrowed from Blockstack
|
|
FIRST_BLOCK_MAINNET = 373601
|
|
|
|
if BLOCKSTACK_TEST and BLOCKSTACK_TEST_FIRST_BLOCK:
|
|
FIRST_BLOCK_MAINNET = int(BLOCKSTACK_TEST_FIRST_BLOCK)
|
|
print('TEST ACTIVE: FIRST_BLOCK_MAINNET = {}'.format(FIRST_BLOCK_MAINNET))
|
|
|
|
FIRST_BLOCK_TIME_UTC = 1441737751
|
|
|
|
TX_MIN_CONFIRMATIONS = 6
|
|
if BLOCKSTACK_TEST:
|
|
# test environment
|
|
TX_MIN_CONFIRMATIONS = 0
|
|
print('TEST ACTIVE: TX_MIN_CONFIRMATIONS = {}'.format(TX_MIN_CONFIRMATIONS))
|
|
|
|
# borrowed from Blockstack
|
|
# Opcodes
|
|
ANNOUNCE = '#'
|
|
NAME_PREORDER = '?'
|
|
NAME_REGISTRATION = ':'
|
|
NAME_UPDATE = '+'
|
|
NAME_TRANSFER = '>'
|
|
NAME_RENEWAL = NAME_REGISTRATION
|
|
NAME_REVOKE = '~'
|
|
NAME_IMPORT = ';'
|
|
NAMESPACE_PREORDER = '*'
|
|
NAMESPACE_REVEAL = '&'
|
|
NAMESPACE_READY = '!'
|
|
|
|
# extra bytes affecting a transfer
|
|
TRANSFER_KEEP_DATA = '>'
|
|
TRANSFER_REMOVE_DATA = '~'
|
|
|
|
# borrowed from Blockstack Core
|
|
# these never change, so it's fine to duplicate them here
|
|
NAME_OPCODES = {
|
|
'NAME_PREORDER': NAME_PREORDER,
|
|
'NAME_REGISTRATION': NAME_REGISTRATION,
|
|
'NAME_UPDATE': NAME_UPDATE,
|
|
'NAME_TRANSFER': NAME_TRANSFER,
|
|
'NAME_RENEWAL': NAME_REGISTRATION,
|
|
'NAME_IMPORT': NAME_IMPORT,
|
|
'NAME_REVOKE': NAME_REVOKE,
|
|
'NAMESPACE_PREORDER': NAMESPACE_PREORDER,
|
|
'NAMESPACE_REVEAL': NAMESPACE_REVEAL,
|
|
'NAMESPACE_READY': NAMESPACE_READY,
|
|
'ANNOUNCE': ANNOUNCE
|
|
}
|
|
|
|
# borrowed from Blockstack Core
|
|
# these never change, so it's fine to duplicate them here
|
|
OPCODE_NAMES = {
|
|
NAME_PREORDER: 'NAME_PREORDER',
|
|
NAME_REGISTRATION: 'NAME_REGISTRATION',
|
|
NAME_UPDATE: 'NAME_UPDATE',
|
|
NAME_TRANSFER: 'NAME_TRANSFER',
|
|
NAME_RENEWAL: 'NAME_REGISTRATION',
|
|
NAME_REVOKE: 'NAME_REVOKE',
|
|
NAME_IMPORT: 'NAME_IMPORT',
|
|
NAMESPACE_PREORDER: 'NAMESPACE_PREORDER',
|
|
NAMESPACE_REVEAL: 'NAMESPACE_REVEAL',
|
|
NAMESPACE_READY: 'NAMESPACE_READY',
|
|
ANNOUNCE: 'ANNOUNCE'
|
|
}
|
|
|
|
# borrowed from Blockstack Core; needed by SNV
|
|
# these never change, so it's fine to duplicate them here
|
|
NAMEREC_FIELDS = [
|
|
'name', # the name itself
|
|
'value_hash', # the hash of the name's associated profile
|
|
'sender', # the scriptPubKey hex that owns this name (identifies ownership)
|
|
'sender_pubkey', # (OPTIONAL) the public key
|
|
'address', # the address of the sender
|
|
'block_number', # the block number when this name record was created (preordered for the first time)
|
|
'preorder_block_number', # the block number when this name was last preordered
|
|
'first_registered', # the block number when this name was registered by the current owner
|
|
'last_renewed', # the block number when this name was renewed by the current owner
|
|
'revoked', # whether or not the name is revoked
|
|
|
|
'op', # byte sequence describing the last operation to affect this name
|
|
'txid', # the ID of the last transaction to affect this name
|
|
'vtxindex', # the index in the block of the transaction.
|
|
'op_fee', # the value of the last Blockstack-specific burn fee paid for this name (i.e. from preorder or renew)
|
|
|
|
'importer', # (OPTIONAL) if this name was imported, this is the importer's scriptPubKey hex
|
|
'importer_address', # (OPTIONAL) if this name was imported, this is the importer's address
|
|
]
|
|
|
|
# borrowed from Blockstack Core; needed by SNV
|
|
# these never change, so it's fine to duplicate them here
|
|
NAMESPACE_FIELDS = [
|
|
'namespace_id', # human-readable namespace ID
|
|
'namespace_id_hash', # hash(namespace_id,sender,reveal_addr) from the preorder (binds this namespace to its preorder)
|
|
'version', # namespace rules version
|
|
'sender', # the scriptPubKey hex script that identifies the preorderer
|
|
'sender_pubkey', # if sender is a p2pkh script, this is the public key
|
|
'address', # address of the sender, from the scriptPubKey
|
|
'recipient', # the scriptPubKey hex script that identifies the revealer.
|
|
'recipient_address', # the address of the revealer
|
|
'block_number', # block number at which this namespace was preordered
|
|
'reveal_block', # block number at which this namespace was revealed
|
|
'op', # byte code identifying this operation to Blockstack
|
|
'txid', # transaction ID at which this namespace was revealed
|
|
'vtxindex', # the index in the block where the tx occurs
|
|
'lifetime', # how long names last in this namespace (in number of blocks)
|
|
'coeff', # constant multiplicative coefficient on a name's price
|
|
'base', # exponential base of a name's price
|
|
'buckets', # array that maps name length to the exponent to which to raise 'base' to
|
|
'nonalpha_discount', # multiplicative coefficient that drops a name's price if it has non-alpha characters
|
|
'no_vowel_discount', # multiplicative coefficient that drops a name's price if it has no vowels
|
|
]
|
|
|
|
# borrowed from Blockstack Core; needed by SNV
|
|
# these never change, so it's fine to duplicate them here
|
|
OPFIELDS = {
|
|
NAME_IMPORT: NAMEREC_FIELDS + [
|
|
'recipient', # scriptPubKey hex that identifies the name recipient
|
|
'recipient_address' # address of the recipient
|
|
],
|
|
NAMESPACE_PREORDER: [
|
|
'namespace_id_hash', # hash(namespace_id,sender,reveal_addr)
|
|
'consensus_hash', # consensus hash at the time issued
|
|
'op', # bytecode describing the operation (not necessarily 1 byte)
|
|
'op_fee', # fee paid for the namespace to the burn address
|
|
'txid', # transaction ID
|
|
'vtxindex', # the index in the block where the tx occurs
|
|
'block_number', # block number at which this transaction occurred
|
|
'sender', # scriptPubKey hex from the principal that issued this preorder (identifies the preorderer)
|
|
'sender_pubkey', # if sender is a p2pkh script, this is the public key
|
|
'address', # address from the scriptPubKey
|
|
],
|
|
NAMESPACE_REVEAL: NAMESPACE_FIELDS,
|
|
NAMESPACE_READY: NAMESPACE_FIELDS + [
|
|
'ready_block', # block number at which the namespace was readied
|
|
],
|
|
NAME_PREORDER: [
|
|
'preorder_hash', # hash(name,sender,register_addr)
|
|
'consensus_hash', # consensus hash at time of send
|
|
'sender', # scriptPubKey hex that identifies the principal that issued the preorder
|
|
'sender_pubkey', # if sender is a pubkeyhash script, then this is the public key. Otherwise, this is empty.
|
|
'address', # address from the sender's scriptPubKey
|
|
'block_number', # block number at which this name was preordered for the first time
|
|
|
|
'op', # blockstack bytestring describing the operation
|
|
'txid', # transaction ID
|
|
'vtxindex', # the index in the block where the tx occurs
|
|
'op_fee', # blockstack fee (sent to burn address)
|
|
],
|
|
NAME_REGISTRATION: NAMEREC_FIELDS + [
|
|
'recipient', # scriptPubKey hex script that identifies the principal to own this name
|
|
'recipient_address' # principal's address from the scriptPubKey in the transaction
|
|
],
|
|
NAME_REVOKE: NAMEREC_FIELDS,
|
|
NAME_TRANSFER: NAMEREC_FIELDS + [
|
|
'name_hash128', # hash(name)
|
|
'consensus_hash', # consensus hash when this operation was sent
|
|
'keep_data' # whether or not to keep the profile data associated with the name when transferred
|
|
],
|
|
NAME_UPDATE: NAMEREC_FIELDS + [
|
|
'name_consensus_hash', # hash(name,consensus_hash)
|
|
'consensus_hash' # consensus hash when this update was sent
|
|
]
|
|
}
|
|
|
|
|
|
# a few contants borrowed from Blockstack Core
|
|
LENGTH_VALUE_HASH = 20
|
|
LENGTH_CONSENSUS_HASH = 16
|
|
LENGTH_MAX_NAME = 37 # maximum name length
|
|
LENGTH_MAX_NAMESPACE_ID = 19 # maximum namespace length
|
|
|
|
# namespace version
|
|
BLOCKSTACK_VERSION = 1
|
|
NAME_SCHEME = MAGIC_BYTES + NAME_REGISTRATION
|
|
|
|
# burn address for fees (the address of public key
|
|
# 0x0000000000000000000000000000000000000000)
|
|
BLOCKSTACK_BURN_PUBKEY_HASH = '0000000000000000000000000000000000000000'
|
|
BLOCKSTACK_BURN_ADDRESS = virtualchain.hex_hash160_to_address(BLOCKSTACK_BURN_PUBKEY_HASH) # '1111111111111111111114oLvT2'
|
|
|
|
# borrowed from Blockstack Core
|
|
# never changes, so safe to duplicate to avoid gratuitous imports
|
|
MAXIMUM_NAMES_PER_ADDRESS = 25
|
|
|
|
MAX_RPC_LEN = 1024 * 1024 * 1024
|
|
|
|
RPC_MAX_ZONEFILE_LEN = 4096 # 4KB
|
|
RPC_MAX_PROFILE_LEN = 1024000 # 1MB
|
|
|
|
CONFIG_FILENAME = 'client.ini'
|
|
WALLET_FILENAME = 'wallet.json'
|
|
|
|
CONFIG_PATH = os.environ.get('BLOCKSTACK_CLIENT_CONFIG')
|
|
|
|
if not BLOCKSTACK_TEST:
|
|
# production
|
|
if CONFIG_PATH is None:
|
|
# default
|
|
CONFIG_DIR = os.path.expanduser("~/.blockstack")
|
|
CONFIG_PATH = os.path.join(CONFIG_DIR, CONFIG_FILENAME)
|
|
|
|
else:
|
|
# env value
|
|
CONFIG_DIR = os.path.dirname(CONFIG_PATH)
|
|
|
|
else:
|
|
# testing
|
|
assert CONFIG_PATH, 'BLOCKSTACK_CLIENT_CONFIG not set'
|
|
|
|
CONFIG_DIR = os.path.dirname(CONFIG_PATH)
|
|
print('TEST ACTIVE: CONFIG_PATH = {}'.format(CONFIG_PATH))
|
|
|
|
WALLET_PATH = os.path.join(CONFIG_DIR, 'wallet.json')
|
|
DEFAULT_QUEUE_PATH = os.path.join(CONFIG_DIR, 'queues.db')
|
|
|
|
METADATA_DIRNAME = 'metadata'
|
|
APP_ACCOUNT_DIRNAME = 'accounts'
|
|
USER_DIRNAME = 'accounts'
|
|
DATASTORE_DIRNAME = 'datastores'
|
|
LOCAL_PRIVKEY_INDEX_NAME = 'local_privkey.idx'
|
|
|
|
BLOCKCHAIN_ID_MAGIC = 'id'
|
|
|
|
USER_ZONEFILE_TTL = 3600 # cache lifetime for a user's zonefile
|
|
|
|
SLEEP_INTERVAL = 20 # in seconds
|
|
TX_EXPIRED_INTERVAL = 10 # if a tx is not picked up by x blocks
|
|
PREORDER_CONFIRMATIONS = 6
|
|
PREORDER_MAX_CONFIRMATIONS = 130 # no. of blocks after which preorder should be removed
|
|
TX_CONFIRMATIONS_NEEDED = 10
|
|
MAX_TX_CONFIRMATIONS = 130
|
|
QUEUE_LENGTH_TO_MONITOR = 50
|
|
MINIMUM_BALANCE = 0.002
|
|
DEFAULT_POLL_INTERVAL = 300
|
|
|
|
# approximate transaction sizes, for when the user has no balance.
|
|
# over-estimations, to avoid stalled registrations.
|
|
APPROX_PREORDER_TX_LEN = 620
|
|
APPROX_REGISTER_TX_LEN = 620
|
|
APPROX_UPDATE_TX_LEN = 1240
|
|
APPROX_TRANSFER_TX_LEN = 1240
|
|
APPROX_RENEWAL_TX_LEN = 1240
|
|
APPROX_REVOKE_TX_LEN = 1240
|
|
|
|
# for estimating tx lengths, when we can't generate a transaction.
|
|
APPROX_TX_OVERHEAD_LEN = 12
|
|
APPROX_TX_IN_P2PKH_LEN = 150
|
|
APPROX_TX_OUT_P2PKH_LEN = 40
|
|
APPROX_TX_IN_P2SH_LEN = 300
|
|
APPROX_TX_OUT_P2SH_LEN = 40
|
|
|
|
# hardened children indexes
|
|
ACCOUNT_SIGNING_KEY_INDEX = 0
|
|
DATASTORE_SIGNING_KEY_INDEX = 0
|
|
|
|
# session lifetime
|
|
DEFAULT_SESSION_LIFETIME = 3600 * 24 * 7 # 1 week
|
|
|
|
# epoch dates
|
|
EPOCH_1_END_BLOCK = 436650
|
|
|
|
# epoch dates for the test environment
|
|
NUM_EPOCHS = 2
|
|
for i in range(1, NUM_EPOCHS + 1):
|
|
# epoch lengths can be altered by the test framework, for ease of tests
|
|
blockstack_epoch_end_block = os.environ.get('BLOCKSTACK_EPOCH_{}_END_BLOCK'.format(i), None)
|
|
if blockstack_epoch_end_block is not None and BLOCKSTACK_TEST is not None:
|
|
exec('EPOCH_{}_END_BLOCK = int({})'.format(i, blockstack_epoch_end_block))
|
|
if DEBUG:
|
|
print('Envar: EPOCH_{}_END_BLOCK = {}'.format(i, eval('EPOCH_{}_END_BLOCK'.format(i))))
|
|
|
|
del i
|
|
|
|
EPOCH_HEIGHT_MINIMUM = EPOCH_1_END_BLOCK + 1
|
|
|
|
DEFAULT_BLOCKCHAIN_READER = 'blockcypher'
|
|
DEFAULT_BLOCKCHAIN_WRITER = 'blockcypher'
|
|
|
|
SUPPORTED_UTXO_PROMPT_MESSAGES = {
|
|
'blockcypher': 'Please enter your Blockcypher API token.',
|
|
'blockchain_info': 'Please enter your blockchain.info API token.',
|
|
'bitcoind_utxo': 'Please enter your fully-indexed bitcoind node information.',
|
|
'blockstack_utxo': 'Please enter your Blockstack server info.',
|
|
}
|
|
|
|
|