async def submitblock(block_hex_or_obj, **kwargs): if isinstance(block_hex_or_obj, str): block_bin = unhexlify(block_hex_or_obj.encode()) # Block mined_block = Block(binary=block_bin[:80]) if mined_block.previous_hash != builder.best_block.hash: return 'PreviousHash don\'t match.' previous_block = builder.get_block(mined_block.previous_hash) mined_block.height = previous_block.height + 1 mined_block.flag = int(kwargs['password']) # tx length storage_flag = int.from_bytes(block_bin[80:81], 'little') if storage_flag < 0xfd: tx_len = storage_flag pos = 81 elif storage_flag == 0xfd: tx_len = int.from_bytes(block_bin[81:83], 'little') pos = 83 elif storage_flag == 0xfe: tx_len = int.from_bytes(block_bin[81:85], 'little') pos = 85 else: # == 0xff tx_len = int.from_bytes(block_bin[81:89], 'little') pos = 89 if F_HEAVY_DEBUG: logging.debug("RpcSubmit block: pos={}, tx_len={}".format( pos, tx_len)) # correct txs while len(block_bin) > pos: tx = TX() tx.b = block_bin tx.deserialize(first_pos=pos, f_raise=False) if tx.version != __chain_version__: return 'tx_ver do not match [{}!={}]'.format( tx.version, __chain_version__) pos += len(tx.b) mined_block.txs.append( tx_builder.get_tx(txhash=tx.hash, default=tx)) # check if tx_len != len(mined_block.txs): return 'Do not match txlen [{}!={}]'.format( tx_len, len(mined_block.txs)) if pos != len(block_bin): return 'Do not match pos [{}!={}]'.format(pos, len(block_bin)) elif isinstance(block_hex_or_obj, Block): mined_block = block_hex_or_obj previous_block = builder.get_block(mined_block.previous_hash) mined_block.height = previous_block.height + 1 mined_block.flag = int(kwargs['password']) else: return 'Unknown input? -> {}'.format(block_hex_or_obj) mined_block.update_pow() if mined_block.pow_check(): confirmed_generating_block(mined_block) return None # accepted else: return 'not satisfied work.'
def put_to_block_stack(r, before_waiter): block_tmp = dict() batch_txs = list() for block_b, block_height, block_flag, txs in r: block = Block(binary=block_b) block.height = block_height block.flag = block_flag for tx_b, tx_signature in txs: tx = TX(binary=tx_b) tx.height = None tx.signature = tx_signature tx_from_database = tx_builder.get_tx(txhash=tx.hash) if tx_from_database: block.txs.append(tx_from_database) else: block.txs.append(tx) block_tmp[block_height] = block batch_txs.extend(block.txs) # check if len(block_tmp) == 0: return None batch_sign_cashe(batch_txs) before_waiter.wait() with write_protect_lock: block_stack.update(block_tmp) return batch_workhash(tuple(block_tmp.values()))
def read_block(self, blockhash): if self.is_batch_thread() and blockhash in self.batch['_block']: b = self.batch['_block'][blockhash] elif is_plyvel: b = self._block.get(blockhash, default=None) else: b = self._block.Get(blockhash, default=None) if b is None: return None b = bytes(b) height, _time, work, b_block, flag, tx_len = struct_block.unpack_from( b) idx = struct_block.size assert len(b) == idx + tx_len, 'Not correct size. [{}={}]'.format( len(b), idx + tx_len) block = Block(binary=b_block) block.height = height block.work_hash = work block.flag = flag # block.txs = [self.read_tx(b[idx+32*i:idx+32*i+32]) for i in range(tx_len//32)] block.txs = [ tx_builder.get_tx(b[idx + 32 * i:idx + 32 * i + 32]) for i in range(tx_len // 32) ] return block
def load_boot_file(): normal_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'boot.dat') extra_path = os.path.join(V.DB_HOME_DIR, 'boot.dat') if os.path.exists(normal_path): with open(normal_path, mode='br') as fp: data = bjson.loads( b64decode(fp.read().replace(b'\n', b'').replace(b'\r', b''))) elif os.path.exists(extra_path): with open(extra_path, mode='br') as fp: data = bjson.loads( b64decode(fp.read().replace(b'\n', b'').replace(b'\r', b''))) else: raise FileNotFoundError('Cannot find boot.dat "{}" or "{}" ?'.format( normal_path, extra_path)) genesis_block = Block(binary=data['block']) genesis_block.flag = C.BLOCK_GENESIS genesis_block.height = 0 for b_tx in data['txs']: tx = TX(binary=b_tx) tx.height = 0 genesis_block.txs.append(tx) connections = data.get('connections', list()) network_ver = data['network_ver'] return genesis_block, network_ver, connections
def make_block_by_node(blockhash): """ create Block by outside node """ r = ask_node(cmd=DirectCmd.BLOCK_BY_HASH, data={'blockhash': blockhash}) if isinstance(r, str): raise BlockChainError('make_block_by_node() failed, by "{}"'.format(hexlify(blockhash).decode(), r)) block = Block(binary=r['block']) block.flag = r['flag'] before_block = builder.get_block(blockhash=block.previous_hash) if before_block is None: raise BlockChainError('Not found BeforeBeforeBlock {}'.format(hexlify(block.previous_hash).decode())) block.height = before_block.height + 1 for tx in r['txs']: _tx = TX(binary=tx['tx']) _tx.height = block.height _tx.signature = tx['sign'] block.txs.append(_tx) return block
def fill_newblock_info(data): new_block = Block(binary=data['block']) logging.debug("Fill newblock={}".format(hexlify(new_block.hash).decode())) proof = TX(binary=data['proof']) new_block.txs.append(proof) new_block.flag = data['block_flag'] proof.signature = data['sign'] # Check the block is correct info if not new_block.pow_check(): raise BlockChainError('Proof of work is not satisfied.') my_block = builder.get_block(new_block.hash) if my_block: raise BlockChainError('Already inserted block {}'.format(my_block)) before_block = builder.get_block(new_block.previous_hash) if before_block is None: logging.debug("Cannot find beforeBlock {}, try to ask outside node." .format(hexlify(new_block.previous_hash).decode())) # not found beforeBlock, need to check other node have the the block new_block.inner_score *= 0.70 # unknown previousBlock, score down before_block = make_block_by_node(blockhash=new_block.previous_hash) if not new_insert_block(before_block, time_check=True): # require time_check, it was generated only a few seconds ago # print([block for block in builder.chain.values()]) raise BlockChainError('Failed insert beforeBlock {}'.format(before_block)) new_height = before_block.height + 1 proof.height = new_height new_block.height = new_height # Append general txs for txhash in data['txs'][1:]: tx = tx_builder.get_tx(txhash) if tx is None: new_block.inner_score *= 0.75 # unknown tx, score down logging.debug("Unknown tx, try to download.") r = ask_node(cmd=DirectCmd.TX_BY_HASH, data={'txhash': txhash}, f_continue_asking=True) if isinstance(r, str): raise BlockChainError('Failed unknown tx download "{}"'.format(r)) tx = TX(binary=r['tx']) tx.signature = r['sign'] check_tx(tx, include_block=None) tx_builder.put_unconfirmed(tx) logging.debug("Success unknown tx download {}".format(tx)) tx.height = new_height new_block.txs.append(tx) return new_block
def fill_newblock_info(data): new_block = Block(binary=data['block']) proof = TX(binary=data['proof']) new_block.txs.append(proof) new_block.flag = data['block_flag'] proof.signature = data['sign'] # Check the block is correct info if not new_block.pow_check(): raise BlockChainError('Proof of work is not satisfied.') if builder.get_block(new_block.hash): raise BlockChainError('Already inserted block.') before_block = builder.get_block(new_block.previous_hash) if before_block is None: raise BlockChainError('Not found beforeBlock {}.'.format( hexlify(new_block.previous_hash).decode())) new_height = before_block.height + 1 proof.height = new_height new_block.height = new_height # Append general txs for txhash in data['txs'][1:]: tx = tx_builder.get_tx(txhash) if tx is None: new_block.inner_score *= 0.75 # unknown tx, score down logging.debug("Unknown tx, try to download.") r = ask_node(cmd=DirectCmd.TX_BY_HASH, data={'txhash': txhash}, f_continue_asking=True) if isinstance(r, str): raise BlockChainError( 'Failed unknown tx download "{}"'.format(r)) tx = TX(binary=r['tx']) tx.signature = r['sign'] check_tx(tx, include_block=None) tx_builder.put_unconfirmed(tx) logging.debug("Success unknown tx download {}".format(tx)) tx.height = new_height new_block.txs.append(tx) return new_block
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