# -*- coding: utf-8 -*-
import random
from .ecc import VerifyingKey,build_message
from .datatype import Pointer,Vin,Vout,UTXO,Tx,Block,get_merkle_root_of_txs
from .params import Params
from .consensus import mine,caculate_target
from .logger import logger
from .wallet import Wallet
from .script import LittleMachine
class Peer(object):
def __init__(self,coords = None):
self.coords = coords
self.network = None
self.txs = []
self.candidate_block_txs = []
self.candidate_block = None
self.blockchain = []
self.orphan_block = []
self.utxo_set = {}
self.mem_pool = {}
self.orphan_pool = {}
self.pid = None
self.fee = Params.FIX_FEE_PER_TX
self.tx_choice_method = 'whole'
self.current_tx = None
self.machine = LittleMachine()
self.generate_wallet()
self._is_block_candidate_created = False
self._is_current_tx_created = False
self._is_current_tx_sent = False
self._delayed_tx = None
self._delayed_block = None
self._utxos_from_vins = None
self._pointers_from_vouts = None
self._utxos_from_vouts = None
self._txs_removed = None
############################################################
# peer as wallet
############################################################
"""
Generate wallet
"""
def generate_wallet(self):
self.wallet = Wallet()
@property
def sk(self):
return self.wallet.keys[-1].sk.to_string() if self.wallet.keys else None
@property
def pk(self):
return self.wallet.keys[-1].pk.to_string() if self.wallet.keys else None
@property
def addr(self):
return self.wallet.addrs[-1] if self.wallet.addrs else None
@property
def key_base_len(self):
return len(self.sk)
"""
your bank balance
"""
def get_balance(self):
utxos = self.get_utxo()
return sum(utxo.vout.value for utxo in utxos)
"""
your output
"""
def get_utxo(self):
return [utxo for utxo in self.utxo_set.values()
if (utxo.vout.to_addr in self.wallet.addrs) and utxo.unspent]
def get_unconfirmed_utxo(self):
utxos = self.get_utxo()
return [utxo for utxo in utxos if not utxo.confirmed]
def get_confirmed_utxo(self):
utxos = self.get_utxo()
return [utxo for utxo in utxos if utxo.confirmed]
def set_fee(self,value):
self.fee = value
def get_fee(self):
return self.fee
def calculate_fees(self,txs):
fee = 0
if txs:
for tx in txs:
fee += tx.fee
return fee
return 0
def get_block_subsidy(self):
return Params.FIX_BLOCK_SUBSIDY
"""
create a transaction
"""
def create_transaction(self,to_addr,
value,
tx_type = "normal"):
if tx_type == 'normal':
outputs = create_normal_tx(self,to_addr,value)
if outputs:
tx_in,tx_out,fee = outputs
self.current_tx = Tx(tx_in,tx_out,fee = fee,nlocktime = 0)
self.txs.append(self.current_tx)
self._is_current_tx_created = True
logger.info('{0}(pid={1}) created a transaction'.format(self,self.pid))
return True
return False
"""
if this is a recorder peer, build rewards for self after won
"""
def create_coinbase(self,value):
return Tx.create_coinbase(self.wallet.addrs[-1],value = value)
############################################################
# peer as route
############################################################
"""
broadcast a transaction
"""
def send_transaction(self):
if not self.txs:
return False
if self._is_current_tx_created:
sign_utxo_from_tx(self.utxo_set,self.current_tx)
add_tx_to_mem_pool(self,self.current_tx)
self._is_current_tx_created = False
self._is_current_tx_sent = True
logger.info("{0}(pid={1}) sent a transaction to network".format(self,self.pid))
return True
return False
def recieve_transaction(self,tx):
if tx and (tx not in self.mem_pool):
if self.verify_transaction(self,tx,self.mem_pool):
add_tx_to_mem_pool(self,tx)
return True
return False
def broadcast_transaction(self,tx = None):
if not self._is_current_tx_sent:
self.send_transaction()
self._is_current_tx_created = False
self._is_current_tx_sent = False
tx = tx or self.current_tx
if tx:
peers = self.network.peers[:]
peers.remove(self)
number = broadcast_tx(peers,tx)
self.current_tx = None
logger.info("{0}(pid={1})'s transaction verified by {2} peers".format(self,self.pid,number))
return number
return 0
"""
broadcast a transaction
"""
def broadcast_block(self,block):
peers = self.network.peers[:]
peers.remove(self)
number = broadcast_winner_block(peers,block)
logger.info('{0} received by {1} peers'.format(block,number))
def locate_block(self,block_hash):
return locate_block_by_hash(self,block_hash)
def recieve_block(self,block):
if not self.verify_block(block):
return False
return try_to_add_block(self,block)
"""
verify a transaction
"""
def verify_transaction(self,tx,pool={}):
if tx in self.txs:
return True
return verify_tx(self,tx,pool)
"""
verify a block
"""
def verify_block(self,block):
if self._delayed_tx:
fill_mem_pool(self)
if self.orphan_pool:
check_orphan_tx_from_pool(self)
pool = get_unknown_txs_from_block(self.mem_pool,block.txs)
if block == self.candidate_block:
return True
if verify_winner_block(self,block,pool):
return True
else:
return False
"""
peer links to p2p network
"""
def login(self):
assert self in self.network.off_peers,(
"This peer does not connect to network or online"
)
repeat_log_in(self,self.network)
self.update_blockchain()
"""
peer logs out
"""
def logout(self):
assert self in self.network.peers,(
"This peer does not connect to network"
)
log_out(self,self.network)
def update_blockchain(self,other):
return update_chain(self,other)
def update_mem_pool(self,other):
if other._delayed_tx:
fill_mem_pool(other)
return update_pool(self,other.mem_pool)
def update_utxo_set(self,other):
self.utxo_set.update(other.utxo_set)
############################################################
# peer as recorder
############################################################
"""
if u r a consensus peer, u have create a candid