def run(self, *args, **kwargs): LOG.info('Running Spellbook Script: %s' % os.path.splitext(os.path.basename(__file__))[0]) if 'message' not in self.json: LOG.error('key "message" not found in http POST request') return message = self.json['message'] if not valid_op_return(message=message): LOG.error( 'Can not create Notary request: message is not valid to put in a OP_RETURN output: %s' % message) return # Use the number of times the trigger has been triggered as a identifier for the request and as the index of the address in the hot wallet request_id = self.triggered + 1 # Get the address to receive payment request_address = get_address_from_wallet(account=BIP44_ACCOUNT, index=request_id) # Create a new action to send a custom transaction with the OP_RETURN data action_id = 'Notary-send-tx_%s' % request_id action = get_action(action_id=action_id, action_type=ActionType.SENDTRANSACTION) action.transaction_type = TransactionType.SEND2SINGLE action.wallet_type = WALLET_TYPE action.bip44_account = BIP44_ACCOUNT action.bip44_index = request_id action.receiving_address = get_address_from_wallet( account=BIP44_ACCOUNT, index=0 ) # always use the first address in the account to receive the transactions action.op_return_data = message action.save() # Create a new trigger that activates when payment is received invoice_paid_trigger_id = 'Notary-payment_%s' % request_id trigger = get_trigger(trigger_id=invoice_paid_trigger_id, trigger_type=TriggerType.BALANCE) trigger.address = request_address trigger.amount = NOTARY_COST trigger.actions = [action_id] trigger.status = 'Active' trigger.self_destruct = int(time.time()) + REQUEST_TIMEOUT trigger.destruct_actions = True trigger.save() self.http_response = { 'request_id': request_id, 'address': request_address, 'value': NOTARY_COST, 'timeout': int(time.time()) + REQUEST_TIMEOUT }
def test_sign_message_with_a_message_of_256_chars(self): address = get_address_from_wallet(account=0, index=0) message = ''.join(['a' for _ in range(255)]) private_key = get_private_key_from_wallet(account=0, index=0)[address] signature = sign_message(message, private_key) print('Signature:', signature) assert verify_message(address=address, message=message, signature=signature)
def run(self): LOG.info('Running Spellbook Script: %s' % os.path.splitext(os.path.basename(__file__))[0]) trigger = get_trigger(trigger_id=self.trigger_id) LOG.info('Lottery winner is determined at block height %s' % trigger.block_height) # Double check the block at the specified height has been mined already (even though the trigger should not activate before the specified blockheight + number of confirmations) block_data = block_by_height(height=trigger.block_height) if 'block' in block_data: block_hash = block_data['block']['hash'] else: LOG.error( 'Unable to get the block hash of block %s to determine the winner of the lottery' % trigger.block_height) return LOG.info('Picking the winner with block %s (%s)' % (trigger.block_height, block_hash)) action_id = 'Lottery-payout' if action_id not in get_actions(): LOG.error('Can not modify action %s: action not found' % action_id) return action = get_action(action_id=action_id) lottery_address = get_address_from_wallet(account=action.bip44_account, index=action.bip44_index) LOG.info('Getting SIL from lottery address %s' % lottery_address) random_address_data = random_address_from_sil( address=lottery_address, sil_block_height=trigger.block_height, rng_block_height=trigger.block_height) LOG.info('random_address_data: %s' % random_address_data) if 'chosen_address' not in random_address_data: LOG.error('Failed to get winning address') return winning_address = random_address_data['chosen_address'] LOG.info('Winning address: %s' % winning_address) LOG.info('Distribution: %s' % random_address_data['distribution']) LOG.info('Configuring action Lottery-payout') action.receiving_address = winning_address # Also set the amount to send in the payout, just in case someone sends a transaction to the lottery after the winner has been picked # Todo this (how to get the balance at a specific time?) action.save() LOG.info('Action Lottery-payout is configured') self.attach_action('Lottery-payout')
def test_sign_message_with_addresses_from_hot_wallet(self, index): account = 0 address = get_address_from_wallet(account=account, index=index) private_key = get_private_key_from_wallet(account=account, index=index)[address] message = 'This is a test message' print('Address:', address) print('Message:', message) signature = sign_message(message, private_key) print('Signature:', signature) assert verify_message(address=address, message=message, signature=signature)
def configure(self, **config): """ Configure the action with given config settings :param config: A dict containing the configuration settings - config['fee_address'] : An address to send the spellbook fee to - config['fee_percentage'] : The percentage to calculate the spellbook fee - config['wallet_type'] : The type of wallet (Single or BIP44) - config['sending_address'] : The address that will be sending the transaction - config['bip44_account'] : An account number of a BIP44 wallet - config['bip44_index'] : An index number of a BIP44 account - config['receiving_address'] : The address to receive the transaction - config['receiving_xpub'] : The xpub key to derive the receiving addresses from - config['amount'] : The amount to send - config['minimum_amount'] : The minimum amount that needs to be available - config['registration_address'] : An address used for the registration of a SIL, LBL, LRL or LSL - config['registration_block_height'] : An block height used for the registration of a SIL - config['registration_xpub'] : An xpub key used for the registration of a LBL, LRL or LSL - config['distribution'] : A dict containing a distribution (each address should be a key in the dict with the value being the share) """ super(SendTransactionAction, self).configure(**config) if 'fee_address' in config and valid_address(config['fee_address']): self.fee_address = config['fee_address'] if 'fee_percentage' in config and valid_percentage( config['fee_percentage']): self.fee_percentage = config['fee_percentage'] if 'wallet_type' in config and config['wallet_type'] in [ 'Single', 'BIP44' ]: self.wallet_type = config['wallet_type'] if 'sending_address' in config and valid_address( config['sending_address']): self.sending_address = config['sending_address'] if 'bip44_account' in config: self.bip44_account = config['bip44_account'] if 'bip44_index' in config: self.bip44_index = config['bip44_index'] if 'receiving_address' in config and valid_address( config['receiving_address']): self.receiving_address = config['receiving_address'] if 'receiving_xpub' in config and valid_xpub(config['receiving_xpub']): self.receiving_xpub = config['receiving_xpub'] if 'amount' in config and valid_amount(config['amount']): self.amount = config['amount'] if 'minimum_amount' in config and valid_amount( config['minimum_amount']): self.minimum_amount = config['minimum_amount'] if 'op_return_data' in config and valid_op_return( config['op_return_data']): self.op_return_data = config['op_return_data'] if 'change_address' in config and valid_address( config['change_address']): self.receiving_address = config['change_address'] if 'transaction_type' in config and valid_transaction_type( config['transaction_type']): self.transaction_type = config['transaction_type'] if 'minimum_output_value' in config and valid_amount( config['minimum_output_value']): self.minimum_output_value = config['minimum_output_value'] if 'registration_address' in config and valid_address( config['registration_address']): self.registration_address = config['registration_address'] if 'registration_block_height' in config and valid_block_height( config['registration_block_height']): self.registration_block_height = config[ 'registration_block_height'] if 'registration_xpub' in config and valid_xpub( config['registration_xpub']): self.registration_xpub = config['registration_xpub'] if 'distribution' in config and valid_distribution( config['distribution']): self.distribution = config['distribution'] if 'tx_fee_type' in config and config['tx_fee_type'] in [ 'High', 'Medium', 'Low', 'Fixed' ]: self.tx_fee_type = config['tx_fee_type'] if 'tx_fee' in config and valid_amount( config['tx_fee']) and self.tx_fee_type == 'Fixed': self.tx_fee = config['tx_fee'] if 'utxo_confirmations' in config and valid_amount( config['utxo_confirmations']): self.utxo_confirmations = config['utxo_confirmations'] if 'private_key' in config and valid_private_key( private_key=config['private_key']): self.private_key = config['private_key'] # fill in the address in case of a BIP44 hot wallet if self.wallet_type == 'BIP44': self.sending_address = get_address_from_wallet( self.bip44_account, self.bip44_index)
print('Starting Spellbook integration test: SendTransaction action') print('----------------------------------------------\n') # Clean up actions if necessary clean_up_actions(action_ids=['integrationtest_action_SendTransaction_REAL']) ######################################################################################################### # SendTransaction actions ######################################################################################################### action_name = 'integrationtest_action_SendTransaction_REAL' wallet_type = 'BIP44' bip44_account = 0 bip44_index = 0 fee_address = get_address_from_wallet(account=0, index=1) fee_percentage = 1.0 minimum_amount = 100000 # 100 BTC receiving_address = get_address_from_wallet(account=0, index=2) op_return_data = 'A test op return message' # -------------------------------------------------------------------------------------------------------- print('Creating test action: SendTransaction') response = spellbook_call('save_action', action_name, '-t=SendTransaction', '-fa=%s' % fee_address, '-fp=%s' % fee_percentage, '-wt=%s' % wallet_type, '-ba=%s' % bip44_account, '-bi=%s' % bip44_index, '-ma=%s' % minimum_amount, '-ra=%s' % receiving_address,
from helpers.triggerhelpers import TriggerType from helpers.actionhelpers import ActionType from action.transactiontype import TransactionType from data.data import latest_block, balance ########################################################################################################## # Lottery parameters ########################################################################################################## # Set the account and index from the hot wallet to use wallet_type = 'BIP44' bip44_account = 0 bip44_index = 0 lottery_address = get_address_from_wallet(account=bip44_account, index=bip44_index) # Set the lottery fee lottery_fee_percentage = 1.0 lottery_fee_address = get_address_from_wallet(account=bip44_account, index=bip44_index + 1) # Set when the lottery picks the winner (offset by the current block_height) block_height_offset = 10 # Set when the lottery winner is paid, by number of confirmations after the block that picks the winner # This is to avoid a situation where the block that picks the winner happens to become orphaned. confirmations = 6 ##########################################################################################################
response = spellbook_call('get_trigger_config', trigger_name) assert response['trigger_id'] == trigger_name assert response['trigger_type'] == trigger_type print( '--------------------------------------------------------------------------------------------------------' ) for trigger_type in trigger_types: print( '--------------------------------------------------------------------------------------------------------' ) print('updating trigger of type: %s' % trigger_type) trigger_name = 'test_trigger_%s' % trigger_type address = get_address_from_wallet(0, 3) amount = 1000000 block_height = 480000 timestamp = int(time.time()) + 10 # 10 seconds in the future response = spellbook_call('save_trigger', trigger_name, '-t=%s' % trigger_type, '-a=%s' % address, '-b=%s' % block_height, '-am=%s' % amount, '-ts=%s' % timestamp) assert response is None response = spellbook_call('get_trigger_config', trigger_name) assert response['trigger_id'] == trigger_name assert response['trigger_type'] == trigger_type if trigger_type in ['Balance', 'Received', 'Sent']:
import sys import requests from helpers.configurationhelpers import get_host, get_port from helpers.hotwallethelpers import get_address_from_wallet voucher = 'ABC123' address = get_address_from_wallet(account=0, index=1) url = 'http://{host}:{port}/api/RedeemVoucher'.format(host=get_host(), port=get_port()) data = {'voucher': voucher, 'address': address} print('Making new Voucher request') try: r = requests.post(url, json=data) print(r.text) except Exception as ex: print('POST %s failed: %s' % (url, ex), file=sys.stderr) sys.exit(1)
print('Starting Spellbook integration test: SendTransaction action') print('----------------------------------------------\n') # Clean up actions if necessary clean_up_actions(action_ids=['integrationtest_action_SendTransaction']) ######################################################################################################### # SendTransaction actions ######################################################################################################### action_name = 'integrationtest_action_SendTransaction' wallet_type = 'BIP44' bip44_account = 0 bip44_index = 0 fee_address = get_address_from_wallet(account=0, index=1) fee_percentage = 1.0 minimum_amount = 10000000000 # 100 BTC receiving_address = get_address_from_wallet(account=0, index=2) op_return_data = 'A test op return message' # -------------------------------------------------------------------------------------------------------- print('Creating test action: SendTransaction') response = spellbook_call('save_action', action_name, '-t=SendTransaction', '-fa=%s' % fee_address, '-fp=%s' % fee_percentage, '-wt=%s' % wallet_type, '-ba=%s' % bip44_account, '-bi=%s' % bip44_index, '-ma=%s' % minimum_amount, '-ra=%s' % receiving_address, '-or=%s' % op_return_data)
'-st=Active') assert response is None response = spellbook_call('get_trigger_config', trigger_name) assert response['trigger_type'] == trigger_type assert response['triggered'] == 0 assert response['multi'] is False print('Checking SignedMessage trigger, should not activate') response = spellbook_call('check_triggers', trigger_name) assert response is None account = 0 index = 0 address = get_address_from_wallet(account=account, index=index) private_key = get_private_key_from_wallet(account=account, index=index)[address] message = 'test message' signature = sign_message(message=message, private_key=private_key) assert verify_message(address=address, message=message, signature=signature) print('%s %s' % (signature, len(signature))) print('Sending a signed message') response = spellbook_call('send_signed_message', trigger_name, address, message, signature) assert response is None response = spellbook_call('get_trigger_config', trigger_name)
def run(self): LOG.info('Running Spellbook Script: %s' % os.path.splitext(os.path.basename(__file__))[0]) if self.json is not None: if 'seller_id' not in self.json: LOG.error( 'Payment request json does not contain the seller id') return if 'amount_fiat' not in self.json: LOG.error( 'Payment request json does not contain the fiat amount!') return if 'currency' not in self.json: LOG.error( 'Payment request json does not contain the fiat currency!') return elif self.json['currency'] not in ['EUR', 'USD']: LOG.error( 'Payment processor currently only supports EUR or USD as currency!' ) return # Create a new payment request payment_request = PaymentRequest() payment_request.seller_id = self.json['seller_id'] payment_request.amount_fiat = self.json['amount_fiat'] payment_request.currency = self.json['currency'] payment_request.note = self.json[ 'note'] if 'note' in self.json else None # Use the number of times the trigger has been triggered as the index in the hot wallet account payment_request.address = get_address_from_wallet( account=ACCOUNT, index=self.triggered) # Get the current BTC price from bitcoinaverage url = 'https://apiv2.bitcoinaverage.com/indices/global/ticker/BTC{currency}'.format( currency=payment_request.currency) LOG.info('Retrieving BTC%s price from bitcoinaverage.com' % payment_request.currency) LOG.info('GET %s' % url) try: r = requests.get(url=url) price_data = r.json() except Exception as ex: LOG.error( 'Unable to retrieve BTC price from bitcoinaverage.com: %s' % ex) self.http_response = { 'error': 'Unable to convert %s amount to BTC amount' % payment_request.currency } return payment_request.price_btc = price_data['last'] payment_request.price_timestamp = price_data['timestamp'] if payment_request.price_btc == 0: LOG.error('BTC price can not be 0!') self.http_response = { 'error': 'Unable to convert %s amount to BTC amount' % payment_request.currency } return payment_request.amount_btc = int(payment_request.amount_fiat / payment_request.price_btc * 1e8) LOG.info('Created new payment request: %s' % payment_request.payment_request_id) LOG.info('Fiat Amount: %s %s' % (payment_request.amount_fiat, payment_request.currency)) LOG.info('BTC Amount: %s (price %s %s/BTC @ %s)' % (payment_request.amount_btc, payment_request.price_btc, payment_request.currency, datetime.fromtimestamp(payment_request.price_timestamp ).strftime('%Y-%m-%d %H:%M:%S'))) LOG.info('Note: %s' % payment_request.note) LOG.info('Address: %s' % payment_request.address) payment_request.save() # Set the HTTP response with the payment request details self.http_response = payment_request.json_encodable() # Create a trigger to monitor the balance of the address (This is a fallback in case the listener doesn't pick up the transaction) # The script will then check the number of confirmations of the transaction if one is received trigger = get_trigger( trigger_id=payment_request.payment_request_id, trigger_type=TriggerType.BALANCE) trigger.address = payment_request.address trigger.amount = payment_request.amount_btc trigger.script = os.path.join('PaymentProcessor', 'PaymentProcessorPaymentStatus.py') trigger.data = { 'payment_request_id': payment_request.payment_request_id } trigger.self_destruct = int(time.time()) + REQUEST_TIMEOUT trigger.status = 'Active' trigger.multi = True trigger.save() # Spawn up a separate process to listen for the payment transaction url = 'http://%s:%s/spellbook/triggers/PaymentProcessorTransactionReceived/post' % ( get_host(), get_port()) notify_program = os.path.join('helpers', 'notify_transaction.py') command = r'%s %s %s #txid#' % (notify_program, url, payment_request.payment_request_id) # Construct the command for the listener so that it listens for any receiving transactions on the address and executes the notify_transaction program when # a transaction is detected and stop the listener if no tx happens within the timeout period. listener_program = os.path.join('listeners', 'transaction_listener.py') run_command = r'%s --address=%s --timeout=%s --exit --receive --command="%s"' % ( listener_program, payment_request.address, LISTENER_TIMEOUT, command) # If we are configured for testnet we must also add the --testnet flag to the listener if get_use_testnet(): run_command += ' --testnet' action = get_action(action_id='start_listener', action_type=ActionType.SPAWNPROCESS) action.run_command = run_command # Run the action immediately instead of saving it, so we are not creating new actions with each request action.run()
from action.transactiontype import TransactionType ########################################################################################################## # Dividends parameters ########################################################################################################## # Set the account and index from the hot wallet to use wallet_type = 'BIP44' bip44_account = 0 bip44_index_investors = 0 bip44_index_dividends = 1 bip44_index_fee = 2 # Set the address where investors must send their investment to so they can receive a dividend in the future investors_address = get_address_from_wallet(account=bip44_account, index=bip44_index_investors) # If you want to lock the investors' shares at a specific moment in time, uncomment the next line and also in the action # investors_block_height = 500000 # Set the address to receive funds which will be distributed amongst the investors as dividends dividends_address = get_address_from_wallet(account=bip44_account, index=bip44_index_dividends) # Set the required amount the dividends address needs to receive before sending dividends # if a transaction with a lower value than this is received, nothing will happen until another transaction is received that makes the total above this value amount = 10000 # Set the dividends fee, in this case we will charge a 1 percent fee each time dividends are given out dividends_fee_percentage = 1.0 dividends_fee_address = get_address_from_wallet(account=bip44_account, index=bip44_index_fee)
from helpers.jsonhelpers import save_to_json_file from validators.validators import valid_distribution from action.transactiontype import TransactionType ########################################################################################################## # Splitter parameters ########################################################################################################## # Set the account and index from the hot wallet to use wallet_type = 'BIP44' bip44_account = 0 bip44_index = 0 # Set the address to receive funds splitter_address = get_address_from_wallet(account=bip44_account, index=bip44_index) # Set the required amount the splitter address needs to receive before sending a splitter transaction # if a transaction with a lower value than this is received, nothing will happen until another transaction is received that makes the total above this value amount = 10000 # Set the splitter fee, uncomment here and in the action if you want to charge a fee for the service # splitter_fee_percentage = 1.0 # splitter_fee_address = get_address_from_wallet(account=bip44_account, index=bip44_index + 1) # Set the distribution for the splitter # This example will distribute the funds in the splitter address over the next 4 addresses in the wallet # keys must be valid addresses, the values can be any integer, it is handy to make the sum of the values equal to 100 but that is not really necessary # each address will receive a portion relative to its share and the total of all shares. # Keep in mind each output value of the transaction must be at least the minimum output value (specified in the configuration file) distribution = {