def transaction_checking(grid: MultiCircuit, transactions: Transactions, agent_id_to_grid_id, dt=1): """ Function that checks the transactions with electrical computations :param grid: GridCal grid Object :param transactions: Transactions object :param agent_id_to_grid_id: dictionary to relate the agent's id with the grid object's id :param dt: market interval in hours :return: """ # declare the final transactions list final_transactions = Transactions() for transaction in transactions: hash = transaction_mine(grid, transaction, agent_id_to_grid_id, dt=dt) # if there are no errors if hash is not None: # modify the transaction, adding a hash based on the voltage transaction.hash = hash # store the transaction final_transactions.append(transaction) else: # since the transactions are sorted, if a critical state is found the rest are curtailed return final_transactions # return the approved transactions return final_transactions
def make_rows(self): import dateutil def get_increments(ledger, account): ld = ledger[ledger.idx].get_date(posting=ledger_account_name, return_string=False) if ledger.idx < len(ledger) else dateutil.parser.parse("2099/01/01") ad = account[account.idx].get_date(posting=ledger_account_name, return_string=False) if account.idx < len(account) else dateutil.parser.parse("2099/01/01") lbump = abump = False if ledger.idx == len(ledger): lbump = True if account.idx == len(account): abump = True if ledger.idx < len(ledger) and account.idx < len(account) and self.total(ledger[ledger.idx])==self.total(account[account.idx]): abump = lbump = True if ld <= ad: lbump = True if ld >= ad: abump = True ledger.bump = False if ledger.idx < len(ledger): ledger.bump = lbump account.bump = False if account.idx < len(account): account.bump = abump uncleared = Transactions() ret = "" ledger_account_name = c['banks'][self.account.bank_name]['accounts'][self.account.name]['ledger-account'].lower() while self.ledger.idx < len(self.ledger) or self.account.idx < len(self.account): if self.ledger.idx < len(self.ledger): if (self.ledger[self.ledger.idx]['state'] != "cleared" and not "cleared" in [p['state'] for p in self.ledger[self.ledger.idx]['postings'] if p['account_name'].lower().startswith(ledger_account_name)]): uncleared.append(self.ledger[self.ledger.idx]) self.ledger.idx += 1 continue get_increments(self.ledger, self.account) if self.ledger.bump: self.ledger.total.append(self.ledger.total[-1] + self.total(self.ledger[self.ledger.idx])) if self.account.bump: self.account.total.append(self.account.total[-1] + self.total(self.account[self.account.idx])) if True: # make table row line = "<tr><td align='right'>" if self.ledger.bump: line += '{0}<br />' line += "</td><td>" if self.account.bump: line += '{1}<br />' line += "</td></tr>" ret += line.format(self.ledger[self.ledger.idx] if self.ledger.idx<len(self.ledger) else "", self.account[self.account.idx] if self.account.idx < len(self.account) else "") if self.ledger.total[-1] == self.account.total[-1]: ret += ("<tr><td align='right'><font color='green'>{0}</font><br /></td><td><font color='green'>{1}</font></td></tr>". format(self.ledger.total[-1], self.account.total[-1])) else: if self.ledger.bump == self.account.bump == True: ret += ("<tr><td align='right'><font color='blue'>{0}</font><br /></td><td><font color='blue'>{1}</font></td></tr>". format(self.ledger.total[-1], self.account.total[-1])) else: ret += "<tr><td align='right'>{0}<br /></td><td>{1}</td></tr>".format(self.ledger.total[-1], self.account.total[-1]) if self.ledger.bump: self.ledger.idx += 1 if self.account.bump: self.account.idx += 1 # TODO: print the uncleared transactions in an intelligent way at the end. #if len(self.ledger) >= self.ledger.idx and len(self.account) >= self.account.idx: # if len(uncleared) > 0: # for tx in uncleared: # print "<tr><td align='right'>{0}</td></tr>".format(tx) return ret
class Blockchain: def __init__(self, agent_id_to_grid_id, fname='Grid.xlsx', dt=1): self.current_transactions = Transactions() self.chain = [] self.nodes = set() self.agent_id_to_grid_id = agent_id_to_grid_id self.actors_group = ActorsGroup() self.market = Market(actors_group=self.actors_group) self.dt = dt self.grid = MultiCircuit() self.grid.load_file(fname) # Create the genesis block self.new_block(previous_hash='1', proof=100) def register_node(self, address): """ Add a new node to the list of nodes :param address: Address of node. Eg. 'http://192.168.0.5:5000' """ parsed_url = urlparse(address) if parsed_url.netloc: self.nodes.add(parsed_url.netloc) elif parsed_url.path: # Accepts an URL without scheme like '192.168.0.5:5000'. self.nodes.add(parsed_url.path) else: raise ValueError('Invalid URL') def valid_chain(self, chain): """ Determine if a given blockchain is valid :param chain: A blockchain :return: True if valid, False if not """ last_block = chain[0] current_index = 1 while current_index < len(chain): block = chain[current_index] print(f'{last_block}') print(f'{block}') print("\n-----------\n") # Check that the hash of the block is correct last_block_hash = self.hash(last_block) if block['previous_hash'] != last_block_hash: return False # Check that the Proof of Work is correct if not self.valid_proof(last_block['proof'], block['proof'], last_block_hash): return False last_block = block current_index += 1 return True def resolve_conflicts(self): """ This is our consensus algorithm, it resolves conflicts by replacing our chain with the longest one in the network. :return: True if our chain was replaced, False if not """ neighbours = self.nodes new_chain = None # We're only looking for chains longer than ours max_length = len(self.chain) # Grab and verify the chains from all the nodes in our network for node in neighbours: response = requests.get(f'http://{node}/chain') if response.status_code == 200: length = response.json()['length'] chain = response.json()['chain'] # Check if the length is longer and the chain is valid if length > max_length and self.valid_chain(chain): max_length = length new_chain = chain # Replace our chain if we discovered a new, valid chain longer than ours if new_chain: self.chain = new_chain return True return False def new_block(self, proof, previous_hash): """ Create a new Block in the Blockchain :param proof: The proof given by the Proof of Work algorithm :param previous_hash: Hash of previous Block :return: New Block """ block = { 'index': len(self.chain) + 1, 'timestamp': time(), 'transactions': self.current_transactions, 'proof': proof, 'previous_hash': previous_hash or self.hash(self.chain[-1]), } # Reset the current list of transactions self.current_transactions = [] self.chain.append(block) return block def new_transaction(self, sender, recipient, energy_amount, price, bid_type, electric_hash): """ Creates a new transaction to go into the next mined Block :param sender: Address of the Sender :param recipient: Address of the Recipient :param amount: Amount :return: The index of the Block that will hold this transaction """ tr = Transaction(bid_id=len(self.current_transactions), # this may be repeated but it is only used to print seller_id=sender, buyer_id=recipient, energy_amount=energy_amount, price=price, bid_type=bid_type, bid_hash=electric_hash) # self.current_transactions.append({ # 'sender': sender, # 'recipient': recipient, # 'energy_amount': energy_amount, # 'price': price, # 'bid_type': bid_type, # 'electric_hash': electric_hash # }) self.current_transactions.append(tr) return self.last_block['index'] + 1 @property def last_block(self): return self.chain[-1] @staticmethod def hash(block): """ Creates a SHA-256 hash of a Block :param block: Block """ # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes # block_string = json.dumps(block, sort_keys=True).encode() block_string = str(block['index']) + \ str(block['timestamp']) + \ str(block['proof']) + \ str(block['previous_hash']) + \ str([j.hash for j in block['transactions']]) # block_string = block. # block_string = block.to_json() return hashlib.sha256(block_string.encode('UTF-8')).hexdigest() @staticmethod def valid_proof( proof ): """ Validates the Proof :param proof: <int> Current Proof :return: <bool> True if correct, False if not. """ return proof != None
def bid_matching(self): self.compute() prob = pulp.LpProblem("DC optimal power flow", pulp.LpMinimize) ni = len(self.demand_bids) nj = len(self.generation_bids) alpha = np.empty((ni, nj), dtype=object) # declare alpha for i, j in product(range(ni), range(nj)): alpha[i, j] = pulp.LpVariable('alpha_' + str(i) + '_' + str(j), 0, 1) gen_slack = np.empty(nj, dtype=object) for j in range(nj): gen_slack[j] = pulp.LpVariable('gen_slack_' + str(j)) # objective function f = 0 for i, j in product(range(ni), range(nj)): f += self.demand_bids[i].energy_mw * alpha[ i, j] * self.generation_bids[j].price prob += f #+ sum(gen_slack) # for j in range(nj): d_alpha = 0 for i in range(ni): d_alpha += self.demand_bids[i].energy_mw * alpha[i, j] prob += (d_alpha <= self.generation_bids[j].energy_mw ) #+ gen_slack[j]) # sum of alpha per demand contract must be one for i in range(ni): prob += (sum(alpha[i, :]) == 1.0) # solve prob.solve() prob.writeLP('problem.lp') prob.writeMPS('problem.mps') print("Status:", pulp.LpStatus[prob.status], prob.status) # ------------------------------------------------------------------------------------------------------------- # Generate the transactions transactions = Transactions() # problem solved for i, j in product(range(ni), range(nj)): id_gen = self.generation_bids[j].id id_demnd = self.demand_bids[i].id demand = self.demand_bids[i].energy_mw price = self.generation_bids[j].price val = alpha[i, j].value() if val != 0: print(id_demnd, 'buys from', id_gen, 'alpha', val) tr = Transaction(bid_id=str(id_gen) + '_' + str(id_demnd), seller_id=id_gen, buyer_id=id_demnd, energy_amount=val * demand, price=price, bid_type=self.demand_bids[i].bid_type) transactions.append(tr) return sorted(transactions, key=lambda x: x.bid_type, reverse=False)