def create_mining_block(consensus): global mining_address # create proof_tx mining_address = mining_address or V.MINING_ADDRESS or new_key() 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( tx={ 'type': C.TX_POW_REWARD, 'inputs': list(), 'outputs': [(mining_address, 0, reward + fees)], 'gas_price': 0, 'gas_amount': 0, 'message_type': C.MSG_PLAIN if V.MINING_MESSAGE else C.MSG_NONE, 'message': V.MINING_MESSAGE if V.MINING_MESSAGE else b'' }) proof_tx.update_time() # create mining block bits, target = get_bits_by_hash(previous_hash=previous_block.hash, consensus=consensus) mining_block = Block( 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) mining_block.txs.extend(unconfirmed_txs) mining_block.update_merkleroot() mining_block.update_time(proof_tx.time) return mining_block
def proof_of_stake(self): global staking_limit limit_deque = deque(maxlen=10) self.event_close.set() while self.event_close.is_set(): # 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: logging.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_POS) reward = GompertzCurve.calc_block_reward(previous_block.height + 1) staking_block = Block( block={ '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_POS staking_block.bits2target() staking_block.txs.append(None) # Dummy proof tx 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: logging.debug("Reset by \"nothing params found\"") sleep(1) break elif previous_block.hash != staking_block.previous_hash: logging.debug("Reset by \"Don't match previous_hash\"") sleep(1) break elif not proof_tx.pos_check( previous_hash=previous_block.hash, pos_target_hash=staking_block.target_hash): continue else: # Staked yay!! proof_tx.height = staking_block.height proof_tx.signature = [ message2signature(proof_tx.b, proof_tx.outputs[0][0]) ] staking_block.txs[0] = proof_tx # Fit block size while staking_block.getsize() > C.SIZE_BLOCK_LIMIT: tx = staking_block.txs.pop() if tx.type == C.TX_FINISH_CONTRACT: staking_block.txs.pop() staking_block.update_time(proof_tx.time) staking_block.update_merkleroot() 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: logging.info("Staking... margin={}% limit={}".format( round(remain * 100, 1), staking_limit)) self.hashrate = (calculate_nam, time()) sleep(max(0.0, remain)) logging.info("Close signal")
def create_genesis_block(all_supply, block_span, prefix=b'\x98', contract_prefix=b'\x12', digit_number=8, minimum_price=100, consensus=None, premine=None): """ Height0のGenesisBlockを作成する :param all_supply: PoW/POS合わせた全採掘量、プリマインを除く :param block_span: Blockの採掘間隔(Sec) :param prefix: 一般アドレスの頭文字、b'\x98'=N :param contract_prefix: コントラクトの頭文字、b'\x98'=C :param digit_number: コインの分解能 :param minimum_price: 最小gas_price :param consensus: 採掘アルゴ {consensus: ratio(0~100), ..} :param premine: プリマイン [(address, coin_id, amount), ...] """ # default: Yescript9割, Stake1割の分配 consensus = consensus or {C.BLOCK_YES_POW: 90, C.BLOCK_POS: 10} 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_POS, C.BLOCK_YES_POW, C.BLOCK_X11_POW, C.BLOCK_HMQ_POW, C.BLOCK_LTC_POW, C.BLOCK_X16_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)) # params assert isinstance(minimum_price, int), 'minimum_price is INT' genesis_time = int(time.time()) # premine premine_txs = list() for index, chunk in enumerate(chunked(premine or list(), 256)): tx = TX( tx={ 'version': __chain_version__, 'type': C.TX_TRANSFER, 'time': 0, 'deadline': 10800, 'inputs': list(), 'outputs': chunk, 'gas_price': 0, 'gas_amount': 0, 'message_type': C.MSG_PLAIN, 'message': 'Premine {}'.format(index).encode() }) tx.height = 0 premine_txs.append(tx) # validator V.BLOCK_GENESIS_TIME = int(time.time()) with closing(create_db(V.DB_ACCOUNT_PATH)) as db: ck = create_new_user_keypair(C.ANT_CONTRACT, db.cursor()) db.commit() c_address = convert_address(ck, contract_prefix) c_bin = contract2binary(Contract) c_cs = { ck.encode(): b'\x00\x00\x00\x00', b'\x00' + b'\x00\x00\x00\x00': b'\x01' } # TODO: 初期値どうする? validator_tx = TX( tx={ 'version': __chain_version__, 'type': C.TX_CREATE_CONTRACT, 'time': 0, 'deadline': 10800, 'inputs': list(), 'outputs': list(), 'gas_price': 0, 'gas_amount': 0, 'message_type': C.MSG_BYTE, 'message': bjson.dumps((c_address, c_bin, c_cs), compress=False) }) validator_tx.height = 0 params = { 'prefix': prefix, # CompressedKey prefix 'contract_prefix': contract_prefix, # ContractKey prefix 'validator_address': c_address, 'genesis_time': genesis_time, # GenesisBlockの採掘時間 'all_supply': all_supply, # 全採掘量 'block_span': block_span, # ブロックの採掘間隔 'digit_number': digit_number, # 小数点以下の桁数 'minimum_price': minimum_price, 'contract_minimum_amount': pow(10, digit_number), 'consensus': consensus } # Block承認のアルゴリズム # BLockChainの設定TX setting_tx = TX( tx={ 'version': __chain_version__, 'type': C.TX_GENESIS, 'time': 0, 'deadline': 10800, 'inputs': list(), 'outputs': list(), 'gas_price': 0, 'gas_amount': 0, 'message_type': C.MSG_BYTE, 'message': bjson.dumps(params, compress=False) }) setting_tx.height = 0 # height0のBlock生成 genesis_block = Block( 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(setting_tx) genesis_block.txs.append(validator_tx) genesis_block.txs.extend(premine_txs) genesis_block.bits2target() genesis_block.target2diff() genesis_block.update_merkleroot() genesis_block.serialize() return genesis_block