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)
Exemple #3
0
    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)
Exemple #6
0
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

##########################################################################################################
Exemple #8
0
    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)
Exemple #10
0
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)
Exemple #11
0
                          '-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)
Exemple #12
0
    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()
Exemple #13
0
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)
Exemple #14
0
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 = {