예제 #1
0
파일: generate.py 프로젝트: volbil/bc4py
def create_mining_block(consensus):
    global mining_address
    # setup mining address for PoW
    with mining_address_lock:
        if mining_address is None:
            if V.MINING_ADDRESS is None:
                with create_db(V.DB_ACCOUNT_PATH) as db:
                    cur = db.cursor()
                    mining_address = generate_new_address_by_userid(
                        C.ANT_UNKNOWN, cur)
                    db.commit()
            else:
                mining_address = V.MINING_ADDRESS
    if unconfirmed_txs is None:
        raise FailedGenerateWarning('unconfirmed_txs is None')
    if previous_block is None:
        raise FailedGenerateWarning('previous_block is None')
    # create proof_tx
    reward = GompertzCurve.calc_block_reward(previous_block.height + 1)
    fees = sum(tx.gas_amount * tx.gas_price for tx in unconfirmed_txs)
    proof_tx = TX.from_dict(
        tx={
            'type': C.TX_POW_REWARD,
            'inputs': list(),
            'outputs': [(mining_address, 0, reward + fees)],
            'gas_price': 0,
            'gas_amount': 0,
            'message_type': C.MSG_NONE,
            'message': b''
        })
    proof_tx.update_time()
    # create mining block
    bits, target = get_bits_by_hash(previous_hash=previous_block.hash,
                                    consensus=consensus)
    mining_block = Block.from_dict(
        block={
            'merkleroot': b'\xff' * 32,
            'time': 0,
            'previous_hash': previous_block.hash,
            'bits': bits,
            'nonce': b'\xff\xff\xff\xff'
        })
    proof_tx.height = previous_block.height + 1
    mining_block.height = proof_tx.height
    mining_block.flag = consensus
    mining_block.bits2target()
    mining_block.txs.append(proof_tx)
    if unconfirmed_txs is None:
        raise FailedGenerateWarning('unconfirmed_txs is None')
    mining_block.txs.extend(unconfirmed_txs)
    mining_block.update_merkleroot()
    mining_block.update_time(proof_tx.time)
    return mining_block
예제 #2
0
def get_submit_data(job: Job, extranonce1: bytes, extranonce2: bytes, nonce: bytes, difficulty: float) \
        -> (Optional[bytes], bytes, bool, bool):
    """check client submit data and generate node submit data"""
    assert len(extranonce1) == 4 and len(extranonce2) == 4
    coinbase = job.coinbase1 + extranonce1 + extranonce2 + job.coinbase2
    coinbase_hash = sha256d_hash(coinbase)
    merkleroot_list = [coinbase_hash] + [tx[0] for tx in job.unconfirmed]
    merkleroot = merkleroot_hash(merkleroot_list)
    block = Block.from_dict({
        'version': job.version,
        'previous_hash': job.previous_hash,
        'merkleroot': merkleroot,
        'time': job.ntime,
        'bits': int.from_bytes(job.bits, 'big'),
        'nonce': nonce,
        # meta
        'height': job.height,
        'flag': job.algorithm,
    })
    # check fulfill target or share
    update_work_hash(block)
    f_mined = block.pow_check()
    f_shared = block.pow_check(int(DEFAULT_TARGET / difficulty))
    log.debug(f"block -> {block.height} {block.hash.hex()}")
    log.debug(f"coinbase -> {coinbase.hex()}")
    log.debug(f"header -> {block.b.hex()}")
    log.debug(f"merkleroot -> {len(merkleroot_list)} {merkleroot.hex()}")
    log.debug(
        f"workhash -> {block.work_hash.hex()} mined:{f_mined} shared:{f_shared}"
    )
    # generate submit data when mined
    if f_mined:
        submit_data = block.b
        tx_len = len(job.unconfirmed) + 1
        if tx_len < 0xfd:
            submit_data += tx_len.to_bytes(1, 'little')
        elif tx_len <= 0xffff:
            submit_data += b'\xfd' + tx_len.to_bytes(2, 'little')
        elif tx_len <= 0xffffffff:
            submit_data += b'\xfe' + tx_len.to_bytes(4, 'little')
        elif tx_len <= 0xffffffffffffffff:  # == 0xff
            submit_data += b'\xff' + tx_len.to_bytes(8, 'little')
        else:
            raise Exception(f"overflowed tx length {tx_len}")
        submit_data += coinbase
        for tx in job.unconfirmed:
            submit_data += tx[1]
    else:
        submit_data = None
    return submit_data, block, f_mined, f_shared
예제 #3
0
def create_genesis_block(mining_supply,
                         block_span,
                         hrp='pycon',
                         digit_number=8,
                         minimum_price=100,
                         consensus=None,
                         genesis_msg="blockchain for python",
                         premine=None):
    """
    Height0のGenesisBlockを作成する
    :param mining_supply: PoW/POS合わせた全採掘量、プリマインを除く
    :param block_span: Blockの採掘間隔(Sec)
    :param hrp: human readable part
    :param digit_number: コインの分解能
    :param minimum_price: 最小gas_price
    :param consensus: 採掘アルゴ {consensus: ratio(0~100), ..}
    :param genesis_msg: GenesisMessage
    :param premine: プリマイン [(address, coin_id, amount), ...]
    """

    # default: Yescript9割, Stake1割の分配
    consensus = consensus or {C.BLOCK_X16S_POW: 100}
    if sum(consensus.values()) != 100:
        raise BlockChainError('sum of consensus values is 100 [!={}]'.format(sum(consensus.values())))
    elif not isinstance(sum(consensus.values()), int):
        raise BlockChainError('value is int only')
    elif not (0 < min(consensus.values()) <= 100):
        raise BlockChainError('out of range {}'.format(min(consensus.values())))
    elif not (0 < max(consensus.values()) <= 100):
        raise BlockChainError('out of range {}'.format(min(consensus.values())))
    all_consensus = {
        C.BLOCK_COIN_POS, C.BLOCK_CAP_POS, C.BLOCK_FLK_POS, C.BLOCK_YES_POW, C.BLOCK_X11_POW, C.BLOCK_HMQ_POW,
        C.BLOCK_LTC_POW, C.BLOCK_X16S_POW
    }
    if len(set(consensus.keys()) - all_consensus) > 0:
        raise BlockChainError('Not found all_consensus number {}'.format(set(consensus.keys()) - all_consensus))
    elif len(set(consensus.keys()) & all_consensus) == 0:
        raise BlockChainError('No usable consensus found {}'.format(set(consensus.keys()) & all_consensus))
    elif not (0 < len(hrp) < 5):
        raise BlockChainError('hrp is too long hrp={}'.format(hrp))
    elif 'dummy' in hrp or '1' in hrp:
        raise BlockChainError('Not allowed  include "dummy" and "1" str {}'.format(hrp))

    # params
    assert isinstance(minimum_price, int), 'minimum_price is INT'
    genesis_time = int(time())
    # BLockChainの設定TX
    params = {
        'hrp': hrp,
        'genesis_time': genesis_time,  # GenesisBlockの採掘時間
        'mining_supply': mining_supply,  # 全採掘量
        'block_span': block_span,  # ブロックの採掘間隔
        'digit_number': digit_number,  # 小数点以下の桁数
        'minimum_price': minimum_price,
        'contract_minimum_amount': pow(10, digit_number),
        'consensus': consensus,  # Block承認のアルゴリズム
    }
    V.BLOCK_GENESIS_TIME = genesis_time
    # first tx
    first_tx = TX.from_dict(
        tx={
            'type': C.TX_GENESIS,
            'time': 0,
            'deadline': 10800,
            'gas_price': 0,
            'gas_amount': 0,
            'message_type': C.MSG_PLAIN,
            'message': genesis_msg.encode()
        })
    first_tx.height = 0
    # premine
    premine_txs = list()
    for index, chunk in enumerate(chunked(premine or list(), 255)):
        tx = TX.from_dict(tx={
            'type': C.TX_TRANSFER,
            'time': 0,
            'deadline': 10800,
            'outputs': chunk,
            'gas_price': 0,
            'gas_amount': 0
        })
        tx.height = 0
        premine_txs.append(tx)
    # height0のBlock生成
    genesis_block = Block.from_dict(block={
        'merkleroot': b'\x00' * 32,
        'time': 0,
        'previous_hash': b'\xff' * 32,
        'bits': MAX_BITS,
        'nonce': b'\xff' * 4
    })
    # block params
    genesis_block.height = 0
    genesis_block.flag = C.BLOCK_GENESIS
    # block body
    genesis_block.txs.append(first_tx)
    genesis_block.txs.extend(premine_txs)
    genesis_block.bits2target()
    genesis_block.target2diff()
    genesis_block.update_merkleroot()
    genesis_block.serialize()
    return genesis_block, params
예제 #4
0
파일: generate.py 프로젝트: volbil/bc4py
    def proof_of_capacity(self):
        dir_path: str = self.config.get('path',
                                        os.path.join(V.DB_HOME_DIR, 'plots'))
        while self.f_enable:
            # check start mining
            if previous_block is None or unconfirmed_txs is None:
                sleep(0.1)
                continue
            if not os.path.exists(dir_path):
                sleep(30)
                continue
            s = time()
            previous_hash = previous_block.hash
            height = previous_block.height + 1
            block_time = int(s - V.BLOCK_GENESIS_TIME)
            bits, target = get_bits_by_hash(previous_hash=previous_hash,
                                            consensus=C.BLOCK_CAP_POS)
            reward = GompertzCurve.calc_block_reward(height)

            # start staking by capacity
            count = 0
            for file_name in os.listdir(dir_path):
                m = optimize_file_name_re.match(file_name)
                if m is None:
                    continue
                count += int(m.group(3)) - int(m.group(2))
            if count < 1:
                log.debug("not found plot file, wait 60 sec")
                sleep(60)
                continue

            # let's seek files
            nonce, work_hash, address = multi_seek(dir=dir_path,
                                                   previous_hash=previous_hash,
                                                   target=target.to_bytes(
                                                       32, 'little'),
                                                   time=block_time,
                                                   worker=os.cpu_count())
            if work_hash is None:
                # return failed => (None, None, err-msg)
                if int(s) % 300 == 0:
                    log.debug("PoC mining info by \"{}\"".format(address))
            else:
                # return success => (nonce, workhash, address)
                if previous_block is None or unconfirmed_txs is None:
                    continue
                if previous_block.hash != previous_hash:
                    continue
                # Staked by capacity yay!!
                total_fee = sum(tx.gas_price * tx.gas_amount
                                for tx in unconfirmed_txs)
                staked_block = Block.from_dict(
                    block={
                        'version': 0,  # always 0
                        'previous_hash': previous_hash,
                        'merkleroot': b'\x00' * 32,
                        'time': 0,
                        'bits': bits,
                        'nonce': nonce,
                        'height': height,
                        'flag': C.BLOCK_CAP_POS
                    })
                staked_proof_tx = TX.from_dict(
                    tx={
                        'type': C.TX_POS_REWARD,
                        'time': block_time,
                        'deadline': block_time + 10800,
                        'gas_price': 0,
                        'gas_amount': 0,
                        'outputs': [(address, 0, reward + total_fee)]
                    })
                staked_block.txs.append(staked_proof_tx)
                staked_block.txs.extend(unconfirmed_txs)
                while staked_block.size > C.SIZE_BLOCK_LIMIT:
                    staked_block.txs.pop()
                staked_block.update_time(staked_proof_tx.time)
                staked_block.update_merkleroot()
                staked_block.work_hash = work_hash
                signature = sign_message_by_address(raw=staked_block.b,
                                                    address=address)
                staked_proof_tx.signature.append(signature)
                confirmed_generating_block(staked_block)

            # finish all
            used_time = time() - s
            self.hashrate = (count, time())
            sleep(max(1 - used_time, 0))
        log.info("Close signal")
예제 #5
0
파일: generate.py 프로젝트: volbil/bc4py
 def proof_of_stake(self):
     global staking_limit
     limit_deque = deque(maxlen=10)
     while self.f_enable:
         # check start mining
         if previous_block is None or unconfirmed_txs is None or unspents_txs is None:
             sleep(0.1)
             continue
         if len(unspents_txs) == 0:
             log.info("No unspents for staking, wait 180s")
             sleep(180)
             continue
         start = time()
         # create staking block
         bits, target = get_bits_by_hash(previous_hash=previous_block.hash,
                                         consensus=C.BLOCK_COIN_POS)
         reward = GompertzCurve.calc_block_reward(previous_block.height + 1)
         staking_block = Block.from_dict(
             block={
                 'version': 0,  # always 0
                 'merkleroot': b'\xff' * 32,
                 'time': 0,
                 'previous_hash': previous_block.hash,
                 'bits': bits,
                 'nonce': b'\xff\xff\xff\xff'
             })
         staking_block.height = previous_block.height + 1
         staking_block.flag = C.BLOCK_COIN_POS
         staking_block.bits2target()
         staking_block.txs.append(None)  # Dummy proof tx
         if unconfirmed_txs is None:
             raise FailedGenerateWarning('unconfirmed_txs is None')
         staking_block.txs.extend(unconfirmed_txs)
         calculate_nam = 0
         for proof_tx in unspents_txs.copy():
             address = proof_tx.outputs[0][0]
             proof_tx.outputs[0] = (address, 0,
                                    proof_tx.pos_amount + reward)
             proof_tx.update_time()
             calculate_nam += 1
             # next check block
             if previous_block is None or unconfirmed_txs is None or unspents_txs is None:
                 log.debug("Reset by \"nothing params found\"")
                 sleep(1)
                 break
             elif previous_block.hash != staking_block.previous_hash:
                 log.debug("Reset by \"Don't match previous_hash\"")
                 sleep(1)
                 break
             elif not stake_coin_check(
                     tx=proof_tx,
                     previous_hash=previous_block.hash,
                     target_hash=staking_block.target_hash):
                 continue
             else:
                 # Staked yay!!
                 proof_tx.height = staking_block.height
                 staking_block.txs[0] = proof_tx
                 # Fit block size
                 while staking_block.size > C.SIZE_BLOCK_LIMIT:
                     staking_block.txs.pop()
                 staking_block.update_time(proof_tx.time)
                 staking_block.update_merkleroot()
                 signature = sign_message_by_address(raw=staking_block.b,
                                                     address=address)
                 proof_tx.signature.append(signature)
                 confirmed_generating_block(staking_block)
                 break
         else:
             # check time
             used = time() - start
             remain = 1.0 - used
             max_limit = max(50, int(calculate_nam / max(0.0001, used)))
             limit_deque.append(int(max_limit * self.power_limit))
             staking_limit = sum(limit_deque) // len(limit_deque)
             if int(time()) % 90 == 0:
                 log.info("Staking... margin={}% limit={}".format(
                     round(remain * 100, 1), staking_limit))
             self.hashrate = (calculate_nam, time())
             sleep(max(0.0, remain + random() - 0.5))
     log.info("Close signal")