<jupyter_start><jupyter_text>Part 7: Build an Encrypted, Decentralized LedgerIn this section, we're going to add a slight extension to the key-value database we made in Part 6 and instead modify it to be a very performant decentralized ledger. At present, you put all of your money into a single bank account at a time. This bank always knows your balance and has the ability to send/receive money on your behalf (perahps even without your permission!!). We're going to design a simple prototype of an (extremely performant) database in which your account is with MULTIPLE banks, but NONE of them can see your balance, and transactions can only occur if all banks work together (they would have to collude to spend on your behalf).Authors:- <NAME> - Twitter: [@iamtrask](https://twitter.com/iamtrask)  Step 1: A Simple LedgerBefore we can attempt to build an encrypted, decentralized ledger, we must first build a simple, decrypted one. This is simple enough. We have a list of starting values, and each transaction changes those values.<jupyter_code>import syft as sy
num_accounts = 10

transactions = list()

initial_balances = sy.ones(num_accounts).long() * 100
transactions.append(initial_balances)

def transact(amt=10,frm=0,to=1):
    record = sy.zeros(num_accounts).long()
    record[frm] = -amt
    record[to] = amt
    transactions.append(record)
    
transact(5,0,1)
transactions<jupyter_output><empty_output><jupyter_text>Getting Your BalanceAnd if you want to calculate the current balance for everyone, you just sum up all the records!<jupyter_code>balance = transactions[0]
for t in transactions[1:]:
    balance = balance + t
balance<jupyter_output><empty_output><jupyter_text>A Bit of Object Orientied OrganizationNow let's put these features into a proper class.<jupyter_code>class SimpleLedger():

    def __init__(self, num_accounts=10, starting_balance=100):
        
        self.transactions = list()
        self.num_accounts = num_accounts
        
        initial_balances = sy.ones(num_accounts).long() * starting_balance
        self.transactions.append(initial_balances)
        
    def transact(self, amt=10,frm=0,to=1):
        record = sy.zeros(self.num_accounts).long()
        record[frm] = -amt
        record[to] = amt
        self.transactions.append(record)
        
    def compute_balances(self):
        balance = self.transactions[0]
        for t in self.transactions[1:]:
            balance = balance + t

        return balance
ledger = SimpleLedger()
ledger.transact(10, 0, 1)
ledger.compute_balances()<jupyter_output><empty_output><jupyter_text>Step 2: Checking Balance ValuesSo our ledger is great, but unfortunately we can transact negative money! We need to be able to deny transactions if the balance for them wasn't available. For this we need two things:- 1) maintain a running list of balances- 2) deny transactions which force balances to become negative<jupyter_code>class SimpleLedger():

    def __init__(self, num_accounts=10, starting_balance=100):
        
        self.transactions = list()
        self.num_accounts = num_accounts
        
        initial_balances = sy.ones(num_accounts).long() * starting_balance
        self.transactions.append(initial_balances)
        
        self.running_balance = self.compute_balances()
        
    def transact(self, amt=10,frm=0,to=1):
        record = sy.zeros(self.num_accounts).long()
        record[frm] = -amt
        record[to] = amt
        
        candidate_balance = self.running_balance + record
        neg_check = 1 - (candidate_balance < 0).sum()
        
        record = record * neg_check
        
        self.transactions.append(record)
        self.running_balance = self.running_balance + record
        
    def compute_balances(self):
        balance = self.transactions[0]
        for t in self.transactions[1:]:
            balance = balance + t
            
        return balance
ledger = SimpleLedger()
# valid transactions work fine
ledger.transact(100, 0, 1)
ledger.compute_balances()
# invalid transactions don't run!
ledger.transact(100, 0, 1)
ledger.compute_balances()<jupyter_output><empty_output><jupyter_text>Step 3: Encrypting / Decentralizing the LedgerAnd now all we need to do is SMPC share all of the tensors involved and we have an encrypted, decentralized ledger!<jupyter_code>class DecentralizedLedger():

    def __init__(self, *owners, num_accounts=10, starting_balance=100):
        
        self.transactions = list()
        self.owners = owners
        self.num_accounts = num_accounts
        
        initial_balances = sy.ones(num_accounts).long() * starting_balance
        initial_balances = initial_balances.share(*self.owners)
        self.transactions.append(initial_balances)
        
        # cache zeros for use in comparison
        self.zeros = initial_balances - initial_balances
        
        self.running_balance = self.compute_balances()
        
    def transact(self, amt=10,frm=0,to=1):
        record = sy.zeros(self.num_accounts).long()
        record[frm] = -amt
        record[to] = amt
        
        record = record.share(*self.owners)
        
        candidate_balance = self.running_balance + record
        neg_check = 1 - (candidate_balance < self.zeros).sum(0).expand(self.num_accounts)
        
        record = record * neg_check
        
        self.transactions.append(record)
        self.running_balance = self.running_balance + record
        
    def compute_balances(self):
        balance = self.transactions[0]
        for t in self.transactions[1:]:
            balance = balance + t
            
        return balance
import syft as sy
hook = sy.TorchHook()

jp_norgan = sy.VirtualWorker(id="jp_norgan")
ubz = sy.VirtualWorker(id="ubz")

ledger = DecentralizedLedger(bob, alice)

# valid transactions work fine
ledger.transact(100, 0, 1)
ledger.compute_balances().get()
# spending too much from an account will cancel the transaction
ledger.transact(100, 0, 1)
ledger.compute_balances().get()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08583264291632146, 'fraction_numerical': 0.013587406793703396, 'mean_word_length': 3.2536997885835097, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 0, '>': 22, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '265500', 'license': 'Apache-2.0', 'n_tokens_mistral': 1856, 'n_tokens_neox': 1783, 'n_words': 517, 'path': 'examples/tutorials/Part 7 - Build an Encrypted, Decentralized Ledger.ipynb', 'repo_name': 'GopikrishnanSasikumar/PySyft', 'size': '6041'}