예제 #1
0
def check_tx_create_contract(tx: TX, include_block: Block):
    if len(tx.inputs) == 0 or len(tx.outputs) == 0:
        raise BlockChainError('No inputs or outputs.')
    elif tx.message_type != C.MSG_BYTE:
        raise BlockChainError('create contract tx is bytes msg.')
    elif V.BLOCK_CONTRACT_PREFIX is None:
        raise BlockChainError('Not set contract prefix ?')
    elif V.BLOCK_CONTRACT_PREFIX == V.BLOCK_PREFIX:
        raise BlockChainError('normal prefix same with contract prefix.')
    # GAS量チェック
    estimate_gas = tx.getsize() + C.CONTRACT_CREATE_FEE
    if estimate_gas > tx.gas_amount:
        raise BlockChainError('Insufficient gas [{}>{}]'.format(
            estimate_gas, tx.gas_amount))
    # Contractをデコードできるか
    c_address, c_bin, c_cs = bjson.loads(tx.message)
    binary2contract(c_bin)
    # ContractStorageの初期値チェック
    if c_cs:
        for k, v in c_cs.items():
            if not isinstance(k, bytes) or not isinstance(v, bytes):
                raise BlockChainError('cs format is wrong. {}'.format(c_cs))
    if not is_address(c_address, V.BLOCK_CONTRACT_PREFIX):
        raise BlockChainError('Is not contract address. {}'.format(c_address))
    # 既に登録されていないかチェック
    cs = get_contract_storage(c_address, include_block)
    if cs.version != 0:
        raise BlockChainError('Already created contract. {}'.format(tx))
예제 #2
0
def check_tx_start_contract(start_tx: TX, include_block: Block):
    # 共通チェック
    c_address, c_data, c_args, c_redeem = bjson.loads(start_tx.message)
    if not is_address(c_address, V.BLOCK_CONTRACT_PREFIX):
        raise BlockChainError('Is not contract address. {}'.format(c_address))
    elif not (c_args is None or isinstance(c_args, list)
              or isinstance(c_args, tuple)):
        raise BlockChainError('c_args is {}'.format(type(c_args)))
    elif not is_address(c_redeem, V.BLOCK_PREFIX):
        raise BlockChainError('Is not redeem address. {}'.format(c_redeem))
    elif start_tx.gas_price < V.COIN_MINIMUM_PRICE:
        raise BlockChainError('GasPrice is too low. [{}<{}]'.format(
            start_tx.gas_price, V.COIN_MINIMUM_PRICE))
    elif start_tx.gas_amount < V.CONTRACT_MINIMUM_AMOUNT:
        raise BlockChainError('GasAmount is too low. [{}<{}]'.format(
            start_tx.gas_amount, V.CONTRACT_MINIMUM_AMOUNT))

    # Block内チェック
    if include_block:
        # 同一のStartTXを示すFinishTXが存在しないかチェック
        count = 0
        for finish_tx in include_block.txs:
            if finish_tx.type != C.TX_FINISH_CONTRACT:
                continue
            c_status, c_start_hash, c_diff = bjson.loads(finish_tx.message)
            if c_start_hash != start_tx.hash:
                continue
            count += 1
        if count == 0:
            raise BlockChainError(
                'Not found FinishTX on block. {}'.format(start_tx))
        if count > 1:
            raise BlockChainError(
                'Find some FinishTX on block. {}'.format(count))

    else:
        c_address, c_method, c_args, c_redeem = bjson.loads(start_tx.message)
        get_contract_binary(c_address)
        if P.VALIDATOR_OBJ and im_a_validator(include_block):
            P.VALIDATOR_OBJ.put_unvalidated(start_tx)
            logging.debug("Add validation que {}".format(start_tx))
예제 #3
0
def signature_check(tx):
    need_cks = set()
    for txhash, txindex in tx.inputs:
        input_tx = tx_builder.get_tx(txhash)
        if input_tx is None:
            raise BlockChainError('Not found input tx {}'.format(hexlify(txhash).decode()))
        address, coin_id, amount = input_tx.outputs[txindex]
        if is_address(address, V.BLOCK_PREFIX):
            need_cks.add(address)  # 通常のアドレスのみ
        else:
            raise BlockChainError('Not common address {} {}.'.format(address, tx))

    signed_cks = get_signed_cks(tx)
    if need_cks != signed_cks:
        raise BlockChainError('Signature verification failed. [{}={}]'.format(need_cks, signed_cks))
예제 #4
0
파일: generate.py 프로젝트: yoosofan/bc4py
def update_unspents_txs():
    global unspents_txs
    c = 50
    while previous_block is None and 0 < c:
        sleep(0.2)
        c -= 1
    previous_height = previous_block.height
    proof_txs = list()
    all_num = 0
    for address, height, txhash, txindex, coin_id, amount in get_unspents_iter(
    ):
        if height is None:
            continue
        if coin_id != 0:
            continue
        if not (previous_height + 1 > height + C.MATURE_HEIGHT):
            continue
        if not is_address(address, prefix=V.BLOCK_PREFIX):
            continue
        if amount < 100000000:
            continue
        if staking_limit < all_num:
            logging.debug("Unspents limit reached, skip by {} limits.".format(
                staking_limit))
            break
        all_num += 1
        proof_tx = TX(
            tx={
                'type': C.TX_POS_REWARD,
                'inputs': [(txhash, txindex)],
                'outputs': [(address, 0, 0)],
                'gas_price': 0,
                'gas_amount': 0,
                'message_type': C.MSG_NONE,
                'message': b''
            })
        proof_tx.height = previous_height + 1
        proof_tx.pos_amount = amount
        proof_txs.append(proof_tx)
    unspents_txs = proof_txs
    return all_num, len(proof_txs)
예제 #5
0
파일: builder.py 프로젝트: kmn/bc4py
    def batch_apply(self, force=False):
        # 無チェックで挿入するから要注意
        if not force and self.cashe_limit > len(self.chain):
            return list()
        # cashe許容量を上回っているので記録
        self.db.batch_create()
        logging.debug("Start batch apply. chain={} force={}".format(len(self.chain), force))
        best_chain = self.best_chain.copy()
        batch_count = self.batch_size
        batched_blocks = list()
        with closing(create_db(V.DB_ACCOUNT_PATH)) as db:
            cur = db.cursor()  # DO NOT USE FOR WRITE!
            try:
                block = None
                while batch_count > 0 and len(best_chain) > 0:
                    batch_count -= 1
                    block = best_chain.pop()  # 古いものから順に
                    batched_blocks.append(block)
                    self.db.write_block(block)  # Block
                    assert len(block.txs) > 0, "found no tx in {}".format(block)
                    for tx in block.txs:
                        self.db.write_tx(tx)  # TX
                        # inputs
                        for index, (txhash, txindex) in enumerate(tx.inputs):
                            # DataBase内でのみのUsedIndexを取得
                            usedindex = self.db.read_usedindex(txhash)
                            if txindex in usedindex:
                                raise BlockBuilderError('Already used index? {}:{}'
                                                        .format(hexlify(txhash).decode(), txindex))
                            usedindex.add(txindex)
                            self.db.write_usedindex(txhash, usedindex)  # UsedIndex update
                            input_tx = tx_builder.get_tx(txhash)
                            address, coin_id, amount = input_tx.outputs[txindex]
                            if config['full_address_index'] or is_address(ck=address, prefix=V.BLOCK_CONTRACT_PREFIX)\
                                    or read_address2user(address=address, cur=cur):
                                # 必要なAddressのみ
                                self.db.write_address_idx(address, txhash, txindex, coin_id, amount, True)
                        # outputs
                        for index, (address, coin_id, amount) in enumerate(tx.outputs):
                            if config['full_address_index'] or is_address(ck=address, prefix=V.BLOCK_CONTRACT_PREFIX) \
                                    or read_address2user(address=address, cur=cur):
                                # 必要なAddressのみ
                                self.db.write_address_idx(address, tx.hash, index, coin_id, amount, False)
                        # TXの種類による追加操作
                        if tx.type == C.TX_GENESIS:
                            pass
                        elif tx.type == C.TX_TRANSFER:
                            pass
                        elif tx.type == C.TX_POW_REWARD:
                            pass
                        elif tx.type == C.TX_POS_REWARD:
                            pass
                        elif tx.type == C.TX_MINT_COIN:
                            mint_id, params, setting = bjson.loads(tx.message)
                            self.db.write_coins(coin_id=mint_id, txhash=tx.hash, params=params, setting=setting)

                        elif tx.type == C.TX_VALIDATOR_EDIT:
                            c_address, new_address, flag, sig_diff = bjson.loads(tx.message)
                            self.db.write_validator(c_address=c_address, new_address=new_address,
                                                    flag=flag, tx=tx, sign_diff=sig_diff)

                        elif tx.type == C.TX_CONCLUDE_CONTRACT:
                            c_address, start_hash, c_storage = bjson.loads(tx.message)
                            start_tx = tx_builder.get_tx(txhash=start_hash)
                            dummy, c_method, redeem_address, c_args = bjson.loads(start_tx.message)
                            self.db.write_contract(c_address=c_address, start_tx=start_tx,
                                                   finish_hash=tx.hash, message=(c_method, c_args, c_storage))

                # block挿入終了
                self.best_chain = best_chain
                self.root_block = block
                self.db.batch_commit()
                self.save_starter()
                # root_blockよりHeightの小さいBlockを消す
                for blockhash, block in self.chain.copy().items():
                    if self.root_block.height >= block.height:
                        del self.chain[blockhash]
                logging.debug("Success batch {} blocks, root={}."
                              .format(len(batched_blocks), self.root_block))
                # アカウントへ反映↓
                user_account.new_batch_apply(batched_blocks)
                return batched_blocks  # [<height=n>, <height=n+1>, .., <height=n+m>]
            except Exception as e:
                self.db.batch_rollback()
                logging.warning("Failed batch block builder. '{}'".format(e), exc_info=True)
                return list()
예제 #6
0
def check_tx_validator_edit(tx: TX, include_block: Block):
    # common check
    if not (len(tx.inputs) > 0 and len(tx.inputs) > 0):
        raise BlockChainError('No inputs or outputs.')
    elif tx.message_type != C.MSG_BYTE:
        raise BlockChainError('validator_edit_tx is bytes msg.')
    elif V.BLOCK_CONTRACT_PREFIX is None:
        raise BlockChainError('Not set contract prefix ?')
    elif V.BLOCK_CONTRACT_PREFIX == V.BLOCK_PREFIX:
        raise BlockChainError('normal prefix same with contract prefix.')
    # message
    try:
        c_address, new_address, flag, sig_diff = bjson.loads(tx.message)
    except Exception as e:
        raise BlockChainError('BjsonError: {}'.format(e))
    # inputs/outputs address check
    for txhash, txindex in tx.inputs:
        input_tx = tx_builder.get_tx(txhash)
        if input_tx is None:
            raise BlockChainError('Not found input tx.')
        address, coin_id, amount = input_tx.outputs[txindex]
        if address != c_address:
            raise BlockChainError('1 Not contract address. {}'.format(address))
    for address, coin_id, amount in tx.outputs:
        if address != c_address:
            raise BlockChainError('2 Not contract address. {}'.format(address))
    # check new_address
    v_before = get_validator_object(c_address=c_address, best_block=include_block, stop_txhash=tx.hash)
    if new_address:
        if not is_address(ck=new_address, prefix=V.BLOCK_PREFIX):
            raise BlockChainError('new_address is normal prefix.')
        elif flag == F_NOP:
            raise BlockChainError('input new_address, but NOP.')
    if v_before.index == -1:
        # create validator for the first time
        if new_address is None:
            raise BlockChainError('Not setup new_address.')
        elif flag != F_ADD:
            raise BlockChainError('Need to add new_address flag.')
        elif sig_diff != 1:
            raise BlockChainError('sig_diff is 1.')
    else:
        # edit already created validator
        next_validator_num = len(v_before.validators)  # Note: Add/Remove after
        next_require_num = v_before.require + sig_diff
        if flag == F_ADD:
            if new_address is None:
                raise BlockChainError('Not setup new_address.')
            elif new_address in v_before.validators:
                raise BlockChainError('Already included new_address.')
            next_validator_num += 1
        elif flag == F_REMOVE:
            if new_address is None:
                raise BlockChainError('Not setup new_address.')
            elif new_address not in v_before.validators:
                raise BlockChainError('Not include new_address.')
            elif len(v_before.validators) < 2:
                raise BlockChainError('validator is now only {}.'.format(len(v_before.validators)))
            next_validator_num -= 1
        elif flag == F_NOP:
            if new_address is not None:
                raise BlockChainError('Input new_address?')
        else:
            raise BlockChainError('unknown flag {}.'.format(flag))
        # sig_diff check
        if not (0 < next_require_num <= next_validator_num):
            raise BlockChainError('sig_diff check failed, 0 < {} <= {}.'
                                  .format(next_require_num, next_validator_num))
    contract_required_gas_check(tx=tx, v=v_before, extra_gas=C.VALIDATOR_EDIT_GAS)
    contract_signature_check(extra_tx=tx, v=v_before, include_block=include_block)
예제 #7
0
def send_many(sender,
              send_pairs,
              cur,
              fee_coin_id=0,
              gas_price=None,
              msg_type=C.MSG_NONE,
              msg_body=b'',
              f_balance_check=True,
              retention=10800):
    assert isinstance(sender, int), 'Sender is user id.'
    assert 0 < len(send_pairs), 'Empty send_pairs.'
    # send_pairs check
    movements = UserCoins()
    outputs = list()
    coins = CoinObject()
    for address, coin_id, amount in send_pairs:
        assert isinstance(
            address,
            str) and len(address) == 40, 'Recipient is 40 letter string.'
        assert isinstance(coin_id, int) and isinstance(
            amount, int), 'CoinID, amount is int.'
        assert is_address(
            address, V.BLOCK_PREFIX), 'Not correct format {}'.format(address)
        coins[coin_id] += amount
        outputs.append((address, coin_id, amount))
    movements[sender] -= coins
    movements[C.ANT_OUTSIDE] += coins
    # tx
    now = int(time.time() - V.BLOCK_GENESIS_TIME)
    tx = TX(
        tx={
            'version': __chain_version__,
            'type': C.TX_TRANSFER,
            'time': now,
            'deadline': now + retention,
            'inputs': list(),
            'outputs': outputs,
            'gas_price': gas_price or V.COIN_MINIMUM_PRICE,
            'gas_amount': 1,
            'message_type': msg_type,
            'message': msg_body
        })
    tx.gas_amount = tx.getsize()
    # fill unspents
    input_address = fill_inputs_outputs(tx, cur, fee_coin_id, additional_fee=0)
    # account check
    fee_coins = CoinObject(coin_id=fee_coin_id,
                           amount=tx.gas_price * tx.gas_amount)
    if f_balance_check:
        # 残高が十分にあるかチェック
        send_coins = CoinObject()
        for address, coin_id, amount in send_pairs:
            send_coins[coin_id] += amount
        check_enough_amount(sender, send_coins, fee_coins)
    if sender in (C.ANT_OUTSIDE, C.ANT_RESERVED):
        # 内部アカウントは不可
        raise BlockChainError('Not allowed inner account.')
    # replace dummy address
    replace_redeem_dummy_address(tx, cur)
    # setup signature
    tx.serialize()
    setup_signature(tx, input_address)
    movements[sender] -= fee_coins
    movements[C.ANT_OUTSIDE] += fee_coins
    insert_log(movements, cur, tx.type, tx.time, tx.hash)
    return tx
예제 #8
0
    def send(self, from_id, to_address, mosaics, msg=b'', only_check=True, balance_check=True, encrypted=False,
             db=None):
        if db is None:
            db = self.db
        assert self.sk is not None, 'You need sk if you use \"send\"'
        to_address = to_address.replace('-', '')
        if to_address == self.ck:
            raise AccountError("You send to and receive to same address.")
        elif not is_address(to_address):
            raise AccountError('Not correct address format.')

        for mosaic in mosaics:
            self._check_expire_mosaic(mosaic, db)
        if encrypted:
            to_pk = self.nem.get_account_info(ck=to_address)['account']['publicKey']
            if to_pk is None:
                raise AccountError('You send encrypt msg to Account that have never send before.')
            # Generally cannot convert CK to PK.
            msg = encrypt(self.sk, to_pk, msg)
            msg_type = 2
        else:
            msg_type = 1
        fee = self.nem.estimate_levy_fee(mosaics)
        fee = DictMath.add(fee, self.nem.estimate_msg_fee(msg))
        fee = DictMath.add(fee, self.nem.estimate_send_fee(mosaics))
        tx_dict = self.nem.mosaic_transfer(self.pk, to_address, mosaics, msg, msg_type)
        tb = TransactionBuilder()
        tx_hex = tb.encode(tx_dict)
        sign_raw = sign(msg=unhexlify(tx_hex.encode()), sk=self.sk, pk=self.pk)
        sign_hex = hexlify(sign_raw).decode()
        if only_check:
            balance = self.balance(from_id)
            need_amount = DictMath.add(fee, mosaics)
            send_ok = DictMath.all_plus_amount(DictMath.sub(balance, need_amount))
            # only_check=False return sending info, NOT send
            return fee, send_ok, tx_dict, tx_hex, sign_hex
        else:
            with self.transaction:
                self.refresh(db=db)
                with db as conn:
                    balance = self.balance(from_id)
                    need_amount = DictMath.add(fee, mosaics)
                    if balance_check and not DictMath.all_plus_amount(DictMath.sub(balance, need_amount)):
                        need = {m: a for m, a in DictMath.sub(balance, need_amount).items() if a < 0}
                        raise AccountError('Not enough balance on ID:%d, %s' % (from_id, need))
                    tx_hash = self.nem.transaction_announce(tx_hex, sign_hex)
                    outgoing_many = list()
                    for mosaic in need_amount:
                        # height, time is None
                        amount = need_amount[mosaic]
                        value = self.get_value(mosaic, amount, db=db)
                        price = self.get_price(mosaic, db=db)
                        outgoing_many.append((
                            unhexlify(tx_hash.encode()), None, from_id, mosaic, amount, value, price, None
                        ))
                    conn.executemany("""
                    INSERT INTO `outgoing_table` VALUES (?, ?, ?, ?, ?, ?, ?, ?)
                    """, outgoing_many)
                    conn.commit()
                threading.Thread(target=self._send, name='Wait',
                                 args=(tx_hash,), daemon=False).start()
            return tx_hash
예제 #9
0
import time

from nem_ed25519.key import secret_key, public_key, get_address, is_address

start = time.time()
result = list()
COUNT = 10
for i in range(COUNT):
    sk = secret_key()
    pk = public_key(sk)
    ck = get_address(pk)
    if not is_address(ck):
        raise Exception('not correct key')
    result.append((sk, pk, ck))
print((time.time() - start) * 1000 // COUNT, "mS/create_pair")

print("\ntry check")
sk = '78f8932df54d22319a16dc4940c269205ae0946f98d38ef30aea488a47426153'
pk = '77041bfb4b6afebc31aaab7b02d68e577fe069524b3c661c804b42ef381f717b'
ck = 'NBOGOGSENUPBFMAPTGHVI4UIAQPVSNKJLWUVHBED'
assert pk == public_key(sk), 'Not correct sk'
assert ck == get_address(pk), 'Not correct pk'
print("all ok.")