Пример #1
0
class BalanceLeaf(object):
    def __init__(self, balance=0):
        self.balance = str(balance)
        # Trading history
        self._tradingHistoryTree = SparseMerkleTree(TREE_DEPTH_TRADING_HISTORY)
        self._tradingHistoryTree.newTree(TradeHistoryLeaf().hash())
        self._tradeHistoryLeafs = {}
        # print("Empty trading tree: " + str(self._tradingHistoryTree._root))

    def hash(self):
        return mimc_hash(
            [int(self.balance),
             int(self._tradingHistoryTree._root)], 1)

    def fromJSON(self, jBalance):
        self.balance = jBalance["balance"]
        # Trading history
        tradeHistoryLeafsDict = jBalance["_tradeHistoryLeafs"]
        for key, val in tradeHistoryLeafsDict.items():
            self._tradeHistoryLeafs[key] = TradeHistoryLeaf(
                val["filled"], val["cancelled"], val["orderID"])
        self._tradingHistoryTree._root = jBalance["_tradingHistoryTree"][
            "_root"]
        self._tradingHistoryTree._db.kv = jBalance["_tradingHistoryTree"][
            "_db"]["kv"]

    def getTradeHistory(self, orderID):
        address = int(orderID) % (2**TREE_DEPTH_TRADING_HISTORY)
        # Make sure the leaf exist in our map
        if not (str(address) in self._tradeHistoryLeafs):
            return TradeHistoryLeaf()
        else:
            return self._tradeHistoryLeafs[str(address)]

    def updateTradeHistory(self, orderID, filled, cancelled, orderIDToStore):
        address = int(orderID) % (2**TREE_DEPTH_TRADING_HISTORY)
        # Make sure the leaf exist in our map
        if not (str(address) in self._tradeHistoryLeafs):
            self._tradeHistoryLeafs[str(address)] = TradeHistoryLeaf(0, 0, 0)

        leafBefore = copy.deepcopy(self._tradeHistoryLeafs[str(address)])
        rootBefore = self._tradingHistoryTree._root
        #print("leafBefore: " + str(leafBefore))
        self._tradeHistoryLeafs[str(address)].filled = str(filled)
        self._tradeHistoryLeafs[str(address)].cancelled = cancelled
        self._tradeHistoryLeafs[str(address)].orderID = int(orderIDToStore)
        leafAfter = copy.deepcopy(self._tradeHistoryLeafs[str(address)])
        #print("leafAfter: " + str(leafAfter))
        proof = self._tradingHistoryTree.createProof(address)
        self._tradingHistoryTree.update(address, leafAfter.hash())
        rootAfter = self._tradingHistoryTree._root

        return TradeHistoryUpdateData(address, proof, rootBefore, rootAfter,
                                      leafBefore, leafAfter)

    def resetTradeHistory(self):
        # Trading history
        self._tradingHistoryTree = SparseMerkleTree(TREE_DEPTH_TRADING_HISTORY)
        self._tradingHistoryTree.newTree(TradeHistoryLeaf().hash())
        self._tradeHistoryLeafs = {}
Пример #2
0
class BalanceLeaf(object):
    def __init__(self, balance=0, weightAMM=0):
        self.balance = str(balance)
        self.weightAMM = str(weightAMM)
        # Storage
        self._storageTree = SparseMerkleTree(BINARY_TREE_DEPTH_STORAGE // 2, 4)
        self._storageTree.newTree(StorageLeaf().hash())
        self._storageLeafs = {}
        #print("Empty storage tree: " + str(self._storageTree._root))

    def hash(self):
        #print("balance: " + self.balance)
        temp = [
            int(self.balance),
            int(self.weightAMM),
            int(self._storageTree._root)
        ]
        #print(temp)
        return poseidon(temp, poseidonParamsBalance)

    def fromJSON(self, jBalance):
        self.balance = jBalance["balance"]
        self.weightAMM = jBalance["weightAMM"]
        # Storage
        storageLeafsDict = jBalance["_storageLeafs"]
        for key, val in storageLeafsDict.items():
            self._storageLeafs[key] = StorageLeaf(val["data"],
                                                  val["storageID"])
        self._storageTree._root = jBalance["_storageTree"]["_root"]
        self._storageTree._db.kv = jBalance["_storageTree"]["_db"]["kv"]

    def getStorage(self, storageID):
        address = int(storageID) % (2**BINARY_TREE_DEPTH_STORAGE)
        # Make sure the leaf exist in our map
        if not (str(address) in self._storageLeafs):
            return StorageLeaf()
        else:
            return self._storageLeafs[str(address)]

    def updateStorage(self, storageID, data):
        address = int(storageID) % (2**BINARY_TREE_DEPTH_STORAGE)
        # Make sure the leaf exist in our map
        if not (str(address) in self._storageLeafs):
            self._storageLeafs[str(address)] = StorageLeaf(0, 0)

        leafBefore = copy.deepcopy(self._storageLeafs[str(address)])
        rootBefore = self._storageTree._root
        #print("leafBefore: " + str(leafBefore))
        self._storageLeafs[str(address)].data = str(data)
        self._storageLeafs[str(address)].storageID = str(storageID)
        leafAfter = copy.deepcopy(self._storageLeafs[str(address)])
        #print("leafAfter: " + str(leafAfter))
        proof = self._storageTree.createProof(address)
        self._storageTree.update(address, leafAfter.hash())
        rootAfter = self._storageTree._root

        return StorageUpdateData(storageID, proof, rootBefore, rootAfter,
                                 leafBefore, leafAfter)
Пример #3
0
class State(object):
    def __init__(self, exchangeID):
        self.exchangeID = int(exchangeID)
        # Accounts
        self._accountsTree = SparseMerkleTree(TREE_DEPTH_ACCOUNTS // 2, 4)
        self._accountsTree.newTree(getDefaultAccount().hash())
        self._accounts = {}
        self._accounts[str(0)] = getDefaultAccount()
        # print("Empty accounts tree: " + str(hex(self._accountsTree._root)))

    def load(self, filename):
        with open(filename) as f:
            data = json.load(f)
            self.exchangeID = int(data["exchangeID"])
            # Accounts
            accountLeafsDict = data["accounts_values"]
            for key, val in accountLeafsDict.items():
                account = getDefaultAccount()
                account.fromJSON(val)
                self._accounts[key] = account
            self._accountsTree._root = data["accounts_root"]
            self._accountsTree._db.kv = data["accounts_tree"]

    def save(self, filename):
        with open(filename, "w") as file:
            file.write(
                json.dumps(
                    {
                        "exchangeID": self.exchangeID,
                        "accounts_values": self._accounts,
                        "accounts_root": self._accountsTree._root,
                        "accounts_tree": self._accountsTree._db.kv,
                    },
                    default=lambda o: o.__dict__,
                    sort_keys=True,
                    indent=4))

    def calculateFees(self, amountB, feeBips, protocolFeeBips, rebateBips):
        protocolFee = (amountB * protocolFeeBips) // 100000
        fee = (amountB * feeBips) // 10000
        rebate = (amountB * rebateBips) // 10000
        return (fee, protocolFee, rebate)

    def getMaxFillAmounts(self, order):
        account = self.getAccount(order.accountID)
        tradeHistory = account.getBalanceLeaf(order.tokenS).getTradeHistory(
            int(order.orderID))

        # Trade history trimming
        bNew = tradeHistory.orderID < order.orderID
        bTrim = not (tradeHistory.orderID <= order.orderID)
        filled = 0 if bNew else int(tradeHistory.filled)
        cancelledToStore = 0 if bNew else int(tradeHistory.cancelled)
        cancelled = 1 if bTrim else cancelledToStore
        orderIDToStore = int(order.orderID) if bNew else tradeHistory.orderID
        """
        print("bNew: " + str(bNew))
        print("bTrim: " + str(bTrim))
        print("filled: " + str(filled))
        print("cancelledToStore: " + str(cancelledToStore))
        print("cancelled: " + str(cancelled))
        print("orderIDToStore: " + str(orderIDToStore))
        """

        # Scale the order
        balanceS = int(account.getBalance(order.tokenS))

        limit = int(order.amountB) if order.buy else int(order.amountS)
        filledLimited = limit if limit < filled else filled
        remainingBeforeCancelled = limit - filledLimited
        remaining = 0 if cancelled else remainingBeforeCancelled
        remainingS_buy = remaining * int(order.amountS) // int(order.amountB)
        remainingS = remainingS_buy if order.buy else remaining
        fillAmountS = balanceS if balanceS < remainingS else remainingS
        fillAmountB = fillAmountS * int(order.amountB) // int(order.amountS)
        return (Fill(fillAmountS,
                     fillAmountB), filled, cancelledToStore, orderIDToStore)

    def match(self, takerOrder, takerFill, makerOrder, makerFill):
        if takerFill.B < makerFill.S:
            makerFill.S = takerFill.B
            makerFill.B = takerFill.B * int(makerOrder.amountB) // int(
                makerOrder.amountS)
        else:
            takerFill.S = makerFill.S * int(takerOrder.amountS) // int(
                takerOrder.amountB)
            takerFill.B = makerFill.S

        spread = takerFill.S - makerFill.B
        matchable = makerFill.B <= takerFill.S

        return (spread, matchable)

    def settleRing(self, context, ring):
        #print("State update ring: ")

        (fillA, filled_A, cancelledToStore_A,
         orderIDToStore_A) = self.getMaxFillAmounts(ring.orderA)
        (fillB, filled_B, cancelledToStore_B,
         orderIDToStore_B) = self.getMaxFillAmounts(ring.orderB)
        '''
        print("fillA.S: " + str(fillA.S))
        print("fillA.B: " + str(fillA.B))
        print("fillB.S: " + str(fillB.S))
        print("fillB.B: " + str(fillB.B))
        print("-------------")
        '''

        if ring.orderA.buy:
            (spread, matchable) = self.match(ring.orderA, fillA, ring.orderB,
                                             fillB)
            fillA.S = fillB.B
        else:
            (spread, matchable) = self.match(ring.orderB, fillB, ring.orderA,
                                             fillA)
            fillA.B = fillB.S

        # Check valid
        ring.orderA.checkValid(context, ring.orderA, fillA.S, fillA.B)
        ring.orderB.checkValid(context, ring.orderB, fillB.S, fillB.B)
        ring.valid = matchable and ring.orderA.valid and ring.orderB.valid
        #print("ring.orderA.valid " + str(ring.orderA.valid))
        #print("ring.orderB.valid " + str(ring.orderB.valid))
        if ring.valid == False:
            #print("ring.valid false: ")
            fillA.S = 0
            fillA.B = 0
            fillB.S = 0
            fillB.B = 0

        # Saved in ring for tests
        ring.fFillS_A = toFloat(fillA.S, Float24Encoding)
        ring.fFillS_B = toFloat(fillB.S, Float24Encoding)

        fillA.S = roundToFloatValue(fillA.S, Float24Encoding)
        fillB.S = roundToFloatValue(fillB.S, Float24Encoding)
        fillA.B = fillB.S
        fillB.B = fillA.S
        '''
        print("fillA.S: " + str(fillA.S))
        print("fillA.B: " + str(fillA.B))
        print("fillB.S: " + str(fillB.S))
        print("fillB.B: " + str(fillB.B))
        print("spread: " + str(spread))
        '''

        # Copy the initial merkle root
        accountsMerkleRoot = self._accountsTree._root

        (fee_A, protocolFee_A,
         rebate_A) = self.calculateFees(fillA.B, ring.orderA.feeBips,
                                        context.protocolTakerFeeBips,
                                        ring.orderA.rebateBips)

        (fee_B, protocolFee_B,
         rebate_B) = self.calculateFees(fillB.B, ring.orderB.feeBips,
                                        context.protocolMakerFeeBips,
                                        ring.orderB.rebateBips)
        '''
        print("fee_A: " + str(fee_A))
        print("protocolFee_A: " + str(protocolFee_A))
        print("rebate_A: " + str(rebate_A))

        print("fee_B: " + str(fee_B))
        print("protocolFee_B: " + str(protocolFee_B))
        print("rebate_B: " + str(rebate_B))
        '''

        # Update balances A
        accountA = self.getAccount(ring.orderA.accountID)

        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(ring.orderA.accountID))
        proof = self._accountsTree.createProof(ring.orderA.accountID)

        (balanceUpdateS_A,
         tradeHistoryUpdate_A) = accountA.updateBalanceAndTradeHistory(
             ring.orderA.tokenS, ring.orderA.orderID, -fillA.S,
             filled_A + (fillA.B if ring.orderA.buy else fillA.S),
             cancelledToStore_A, orderIDToStore_A)
        balanceUpdateB_A = accountA.updateBalance(ring.orderA.tokenB,
                                                  fillA.B - fee_A + rebate_A)

        self.updateAccountTree(ring.orderA.accountID)
        accountAfter = copyAccountInfo(self.getAccount(ring.orderA.accountID))
        rootAfter = self._accountsTree._root
        accountUpdate_A = AccountUpdateData(ring.orderA.accountID, proof,
                                            rootBefore, rootAfter,
                                            accountBefore, accountAfter)
        ###

        # Update balances B
        accountB = self.getAccount(ring.orderB.accountID)

        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(ring.orderB.accountID))
        proof = self._accountsTree.createProof(ring.orderB.accountID)

        (balanceUpdateS_B,
         tradeHistoryUpdate_B) = accountB.updateBalanceAndTradeHistory(
             ring.orderB.tokenS, ring.orderB.orderID, -fillB.S,
             filled_B + (fillB.B if ring.orderB.buy else fillB.S),
             cancelledToStore_B, orderIDToStore_B)
        balanceUpdateB_B = accountB.updateBalance(ring.orderB.tokenB,
                                                  fillB.B - fee_B + rebate_B)

        self.updateAccountTree(ring.orderB.accountID)
        accountAfter = copyAccountInfo(self.getAccount(ring.orderB.accountID))
        rootAfter = self._accountsTree._root
        accountUpdate_B = AccountUpdateData(ring.orderB.accountID, proof,
                                            rootBefore, rootAfter,
                                            accountBefore, accountAfter)
        ###

        # Protocol fee payment
        balanceUpdateA_P = self.getAccount(0).updateBalance(
            ring.orderA.tokenB, protocolFee_A)
        balanceUpdateB_P = self.getAccount(0).updateBalance(
            ring.orderB.tokenB, protocolFee_B)
        ###

        # Operator payment
        balanceDeltaA_O = fee_A - protocolFee_A - rebate_A
        balanceDeltaB_O = fee_B - protocolFee_B - rebate_B
        # The Merkle tree update is done after all rings are settled

        return RingSettlement(ring, accountsMerkleRoot, tradeHistoryUpdate_A,
                              tradeHistoryUpdate_B, balanceUpdateS_A,
                              balanceUpdateB_A, accountUpdate_A,
                              balanceUpdateS_B, balanceUpdateB_B,
                              accountUpdate_B, balanceUpdateA_P,
                              balanceUpdateB_P, balanceDeltaA_O,
                              balanceDeltaB_O)

    def deposit(self, accountID, secretKey, publicKeyX, publicKeyY, token,
                amount):
        # Copy the initial merkle root
        rootBefore = self._accountsTree._root

        if not (str(accountID) in self._accounts):
            accountBefore = copyAccountInfo(getDefaultAccount())
        else:
            accountBefore = copyAccountInfo(self.getAccount(accountID))

        proof = self._accountsTree.createProof(accountID)

        # Create the account if necessary
        if not (str(accountID) in self._accounts):
            self._accounts[str(accountID)] = Account(
                secretKey, Point(publicKeyX, publicKeyY))

        account = self.getAccount(accountID)
        balanceUpdate = account.updateBalance(token, amount)

        # Update keys
        account.secretKey = str(secretKey)
        account.publicKeyX = str(publicKeyX)
        account.publicKeyY = str(publicKeyY)

        self._accountsTree.update(accountID, account.hash())

        accountAfter = copyAccountInfo(account)

        rootAfter = self._accountsTree._root

        accountUpdate = AccountUpdateData(accountID, proof, rootBefore,
                                          rootAfter, accountBefore,
                                          accountAfter)
        return Deposit(amount, balanceUpdate, accountUpdate)

    def getAccount(self, accountID):
        # Make sure the leaf exist in our map
        if not (str(accountID) in self._accounts):
            print("Account doesn't exist: " + str(accountID))
        return self._accounts[str(accountID)]

    def onchainWithdraw(self, exchangeID, accountID, tokenID, amountRequested,
                        shutdown):
        # When a withdrawal is done before the deposit (account creation) we shouldn't
        # do anything. Just leave everything as it is.
        if str(accountID) in self._accounts:
            # Calculate amount withdrawn
            balance = int(self.getAccount(accountID).getBalance(tokenID))
            uAmountMin = int(amountRequested) if (
                int(amountRequested) < balance) else balance

            # Withdraw the complete balance in shutdown
            uAmount = balance if shutdown else uAmountMin

            fAmount = toFloat(uAmount, Float28Encoding)
            amount = fromFloat(fAmount, Float28Encoding)

            # Make sure no 'dust' remains after a withdrawal in shutdown
            amountToSubtract = uAmount if shutdown else amount

            # Update account
            rootBefore = self._accountsTree._root
            accountBefore = copyAccountInfo(self.getAccount(accountID))
            proof = self._accountsTree.createProof(accountID)

            balanceUpdate = self.getAccount(accountID).updateBalance(
                tokenID, -amountToSubtract, shutdown)
            if shutdown:
                self.getAccount(accountID).publicKeyX = str(0)
                self.getAccount(accountID).publicKeyY = str(0)
                self.getAccount(accountID).nonce = 0

            self.updateAccountTree(accountID)
            accountAfter = copyAccountInfo(self.getAccount(accountID))
            rootAfter = self._accountsTree._root
            accountUpdate = AccountUpdateData(accountID, proof, rootBefore,
                                              rootAfter, accountBefore,
                                              accountAfter)
            ###
        else:
            # Dummy update
            fAmount = 0

            rootBefore = self._accountsTree._root
            accountBefore = copyAccountInfo(getDefaultAccount())
            proof = self._accountsTree.createProof(accountID)

            balanceUpdate = getDefaultAccount().updateBalance(
                tokenID, 0, shutdown)

            accountAfter = copyAccountInfo(getDefaultAccount())
            rootAfter = self._accountsTree._root
            accountUpdate = AccountUpdateData(accountID, proof, rootBefore,
                                              rootAfter, accountBefore,
                                              accountAfter)
            ###

        withdrawal = OnchainWithdrawal(amountRequested, balanceUpdate,
                                       accountUpdate, accountID, tokenID,
                                       fAmount)
        return withdrawal

    def offchainWithdraw(self, exchangeID, accountID, tokenID, amountRequested,
                         operatorAccountID, feeTokenID, fee, label):
        feeValue = roundToFloatValue(fee, Float16Encoding)

        # Update account
        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(accountID))
        nonce = accountBefore.nonce
        proof = self._accountsTree.createProof(accountID)

        balanceUpdateF_A = self.getAccount(accountID).updateBalance(
            feeTokenID, -feeValue)

        balance = int(self.getAccount(accountID).getBalance(tokenID))
        uAmountWithdrawn = int(amountRequested) if (
            int(amountRequested) < balance) else balance

        fAmountWithdrawn = toFloat(uAmountWithdrawn, Float28Encoding)
        amountWithdrawn = fromFloat(fAmountWithdrawn, Float28Encoding)

        balanceUpdateW_A = self.getAccount(accountID).updateBalance(
            tokenID, -amountWithdrawn)
        self.getAccount(accountID).nonce += 1

        self.updateAccountTree(accountID)
        accountAfter = copyAccountInfo(self.getAccount(accountID))
        rootAfter = self._accountsTree._root
        accountUpdate_A = AccountUpdateData(accountID, proof, rootBefore,
                                            rootAfter, accountBefore,
                                            accountAfter)
        ###

        # Operator payment
        # This is done after all withdrawals are processed

        withdrawal = OffchainWithdrawal(exchangeID, accountID, tokenID,
                                        amountRequested, fAmountWithdrawn,
                                        feeTokenID, fee, label,
                                        balanceUpdateF_A, balanceUpdateW_A,
                                        accountUpdate_A, None, nonce)
        return withdrawal

    def cancelOrder(self, exchangeID, accountID, orderTokenID, orderID,
                    operatorAccountID, feeTokenID, fee, label):

        feeValue = roundToFloatValue(fee, Float16Encoding)

        # Update account
        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(accountID))
        nonce = accountBefore.nonce
        proof = self._accountsTree.createProof(accountID)

        (balanceUpdateT_A,
         tradeHistoryUpdate_A) = self.getAccount(accountID).cancelOrder(
             orderTokenID, orderID)
        balanceUpdateF_A = self.getAccount(accountID).updateBalance(
            feeTokenID, -feeValue)
        self.getAccount(accountID).nonce += 1

        self.updateAccountTree(accountID)
        accountAfter = copyAccountInfo(self.getAccount(accountID))
        rootAfter = self._accountsTree._root
        accountUpdate_A = AccountUpdateData(accountID, proof, rootBefore,
                                            rootAfter, accountBefore,
                                            accountAfter)
        ###

        # Operator payment
        # This is done after all cancellations are processed

        cancellation = Cancellation(exchangeID, accountID, orderTokenID,
                                    orderID, feeTokenID, fee, label, nonce,
                                    tradeHistoryUpdate_A, balanceUpdateT_A,
                                    balanceUpdateF_A, accountUpdate_A, None)
        return cancellation

    def createWithdrawProof(self, exchangeID, accountID, tokenID):
        account = copyAccountInfo(self.getAccount(accountID))
        balance = copyBalanceInfo(
            self.getAccount(accountID)._balancesLeafs[str(tokenID)])
        accountProof = self._accountsTree.createProof(accountID)
        balanceProof = self.getAccount(accountID)._balancesTree.createProof(
            tokenID)

        return WithdrawProof(exchangeID, accountID, tokenID, account, balance,
                             self.getRoot(), accountProof, balanceProof)

    def updateAccountTree(self, accountID):
        self._accountsTree.update(accountID, self.getAccount(accountID).hash())

    def getRoot(self):
        return self._accountsTree._root
Пример #4
0
class Account(object):
    def __init__(self, secretKey, publicKey):
        self.secretKey = str(secretKey)
        self.publicKeyX = str(publicKey.x)
        self.publicKeyY = str(publicKey.y)
        self.nonce = 0
        # Balances
        self._balancesTree = SparseMerkleTree(TREE_DEPTH_TOKENS // 2, 4)
        self._balancesTree.newTree(BalanceLeaf().hash())
        self._balancesLeafs = {}
        #print("Empty balances tree: " + str(self._balancesTree._root))

    def hash(self):
        return poseidon([
            int(self.publicKeyX),
            int(self.publicKeyY),
            int(self.nonce),
            int(self._balancesTree._root)
        ], poseidonParamsAccount)

    def fromJSON(self, jAccount):
        self.secretKey = jAccount["secretKey"]
        self.publicKeyX = jAccount["publicKeyX"]
        self.publicKeyY = jAccount["publicKeyY"]
        self.nonce = int(jAccount["nonce"])
        # Balances
        balancesLeafsDict = jAccount["_balancesLeafs"]
        for key, val in balancesLeafsDict.items():
            balanceLeaf = BalanceLeaf()
            balanceLeaf.fromJSON(val)
            self._balancesLeafs[key] = balanceLeaf
        self._balancesTree._root = jAccount["_balancesTree"]["_root"]
        self._balancesTree._db.kv = jAccount["_balancesTree"]["_db"]["kv"]

    def getBalanceLeaf(self, address):
        # Make sure the leaf exist in our map
        if not (str(address) in self._balancesLeafs):
            return BalanceLeaf()
        else:
            return self._balancesLeafs[str(address)]

    def getBalance(self, address):
        return self.getBalanceLeaf(address).balance

    def updateBalance(self, tokenID, amount, shutdown=False):
        # Make sure the leaf exist in our map
        if not (str(tokenID) in self._balancesLeafs):
            self._balancesLeafs[str(tokenID)] = BalanceLeaf()

        balancesBefore = copyBalanceInfo(self._balancesLeafs[str(tokenID)])
        rootBefore = self._balancesTree._root

        self._balancesLeafs[str(tokenID)].balance = str(
            int(self._balancesLeafs[str(tokenID)].balance) + amount)
        if int(self._balancesLeafs[str(tokenID)].balance) > MAX_AMOUNT:
            self._balancesLeafs[str(tokenID)].balance = str(MAX_AMOUNT)
        if shutdown:
            self._balancesLeafs[str(tokenID)].resetTradeHistory()

        balancesAfter = copyBalanceInfo(self._balancesLeafs[str(tokenID)])
        proof = self._balancesTree.createProof(tokenID)
        self._balancesTree.update(tokenID,
                                  self._balancesLeafs[str(tokenID)].hash())
        rootAfter = self._balancesTree._root

        return BalanceUpdateData(tokenID, proof, rootBefore, rootAfter,
                                 balancesBefore, balancesAfter)

    def updateBalanceAndTradeHistory(self, tokenID, orderID, amount, filled,
                                     cancelledToStore, orderIDToStore):
        # Make sure the leaf exist in our map
        if not (str(tokenID) in self._balancesLeafs):
            self._balancesLeafs[str(tokenID)] = BalanceLeaf()

        balancesBefore = copyBalanceInfo(self._balancesLeafs[str(tokenID)])
        rootBefore = self._balancesTree._root

        # Update filled amounts
        tradeHistoryUpdate = self._balancesLeafs[str(
            tokenID)].updateTradeHistory(orderID, filled, cancelledToStore,
                                         orderIDToStore)
        self._balancesLeafs[str(tokenID)].balance = str(
            int(self._balancesLeafs[str(tokenID)].balance) + amount)
        if int(self._balancesLeafs[str(tokenID)].balance) > MAX_AMOUNT:
            self._balancesLeafs[str(tokenID)].balance = str(MAX_AMOUNT)

        balancesAfter = copyBalanceInfo(self._balancesLeafs[str(tokenID)])
        proof = self._balancesTree.createProof(tokenID)
        self._balancesTree.update(tokenID,
                                  self._balancesLeafs[str(tokenID)].hash())
        rootAfter = self._balancesTree._root

        return (BalanceUpdateData(tokenID, proof, rootBefore, rootAfter,
                                  balancesBefore,
                                  balancesAfter), tradeHistoryUpdate)

    def cancelOrder(self, tokenID, orderID):
        # Make sure the leaf exist in our map
        if not (str(tokenID) in self._balancesLeafs):
            self._balancesLeafs[str(tokenID)] = BalanceLeaf()

        balancesBefore = copyBalanceInfo(self._balancesLeafs[str(tokenID)])
        rootBefore = self._balancesTree._root

        # Update cancelled state
        tradeHistory = self._balancesLeafs[str(tokenID)].getTradeHistory(
            orderID)
        filled = int(tradeHistory.filled)
        if int(tradeHistory.orderID) < orderID:
            filled = 0
        tradeHistoryUpdate = self._balancesLeafs[str(
            tokenID)].updateTradeHistory(orderID, filled, 1, orderID)

        balancesAfter = copyBalanceInfo(self._balancesLeafs[str(tokenID)])
        proof = self._balancesTree.createProof(tokenID)
        self._balancesTree.update(tokenID,
                                  self._balancesLeafs[str(tokenID)].hash())
        rootAfter = self._balancesTree._root

        return (BalanceUpdateData(tokenID, proof, rootBefore, rootAfter,
                                  balancesBefore,
                                  balancesAfter), tradeHistoryUpdate)
Пример #5
0
class State(object):
    def __init__(self, exchangeID):
        self.exchangeID = int(exchangeID)
        # Accounts
        self._accountsTree = SparseMerkleTree(BINARY_TREE_DEPTH_ACCOUNTS // 2, 4)
        self._accountsTree.newTree(getDefaultAccount().hash())
        self._accounts = {}
        self._accounts[str(0)] = getDefaultAccount()
        self._accounts[str(1)] = getDefaultAccount()
        # print("Empty accounts tree: " + str(hex(self._accountsTree._root)))

    def load(self, filename):
        with open(filename) as f:
            data = json.load(f)
            self.exchangeID = int(data["exchangeID"])
            # Accounts
            accountLeafsDict = data["accounts_values"]
            for key, val in accountLeafsDict.items():
                account = getDefaultAccount()
                account.fromJSON(val)
                self._accounts[key] = account
            self._accountsTree._root = data["accounts_root"]
            self._accountsTree._db.kv = data["accounts_tree"]

    def save(self, filename):
        with open(filename, "w") as file:
            file.write(json.dumps(
                {
                    "exchangeID": self.exchangeID,
                    "accounts_values": self._accounts,
                    "accounts_root": self._accountsTree._root,
                    "accounts_tree": self._accountsTree._db.kv,
                }, default=lambda o: o.__dict__, sort_keys=True, indent=4))

    def calculateFees(self, amountB, feeBips, protocolFeeBips):
        protocolFee = (amountB * protocolFeeBips) // 100000
        fee = (amountB * feeBips) // 10000
        return (fee, protocolFee)

    def getData(self, accountID, tokenID, storageID):
        account = self.getAccount(accountID)
        storage = account.getBalanceLeaf(tokenID).getStorage(int(storageID))

        # Storage trimming
        numSlots = (2 ** BINARY_TREE_DEPTH_STORAGE)
        leafStorageID = storage.storageID if int(storage.storageID) > 0 else int(storageID) % numSlots
        filled = int(storage.data) if (int(storageID) == int(leafStorageID)) else 0

        return filled

    def getMaxFill(self, order, filled, balanceLimit):
        account = self.getAccount(order.accountID)

        # Scale the order
        balanceS = int(account.getBalance(order.tokenS)) if balanceLimit else int(order.amountS)

        limit = int(order.amountB) if order.fillAmountBorS else int(order.amountS)
        filledLimited = limit if limit < filled else filled
        remaining = limit - filledLimited
        remainingS_buy = remaining * int(order.amountS) // int(order.amountB)
        remainingS = remainingS_buy if order.fillAmountBorS else remaining
        fillAmountS = balanceS if balanceS < remainingS else remainingS
        fillAmountB = fillAmountS * int(order.amountB) // int(order.amountS)
        return Fill(fillAmountS, fillAmountB)

    def match(self, takerOrder, takerFill, makerOrder, makerFill):
        if takerFill.B < makerFill.S:
            makerFill.S = takerFill.B
            makerFill.B = takerFill.B * int(makerOrder.amountB) // int(makerOrder.amountS)
        else:
            takerFill.S = makerFill.S * int(takerOrder.amountS) // int(takerOrder.amountB)
            takerFill.B = makerFill.S

        spread = takerFill.S - makerFill.B
        matchable = makerFill.B <= takerFill.S

        return (spread, matchable)

    def executeTransaction(self, context, txInput):
        newState = GeneralObject()
        newState.signatureA = None
        newState.signatureB = None
        # Tokens
        newState.TXV_BALANCE_A_S_ADDRESS = None
        newState.TXV_BALANCE_A_S_ADDRESS = None
        # A
        newState.TXV_ACCOUNT_A_ADDRESS = None
        newState.TXV_ACCOUNT_A_OWNER = None
        newState.TXV_ACCOUNT_A_PUBKEY_X = None
        newState.TXV_ACCOUNT_A_PUBKEY_Y = None
        newState.TXV_ACCOUNT_A_NONCE = None
        newState.TXV_ACCOUNT_A_FEEBIPSAMM = None
        newState.TXV_BALANCE_A_S_ADDRESS = None
        newState.TXV_BALANCE_A_S_BALANCE = None
        newState.TXV_BALANCE_A_S_WEIGHT = None
        newState.TXV_BALANCE_A_B_BALANCE = None
        newState.TXV_STORAGE_A_ADDRESS = None
        newState.TXV_STORAGE_A_DATA = None
        newState.TXV_STORAGE_A_STORAGEID = None
        # B
        newState.TXV_ACCOUNT_B_ADDRESS = None
        newState.TXV_ACCOUNT_B_OWNER = None
        newState.TXV_ACCOUNT_B_PUBKEY_X = None
        newState.TXV_ACCOUNT_B_PUBKEY_Y = None
        newState.TXV_ACCOUNT_B_NONCE = None
        newState.TXV_BALANCE_B_S_ADDRESS = None
        newState.TXV_BALANCE_B_S_BALANCE = None
        newState.TXV_BALANCE_B_B_BALANCE = None
        newState.TXV_STORAGE_B_ADDRESS = None
        newState.TXV_STORAGE_B_DATA = None
        newState.TXV_STORAGE_B_STORAGEID = None
        # Operator
        newState.balanceDeltaA_O = None
        newState.balanceDeltaB_O = None
        # Protocol fees
        newState.balanceDeltaA_P = None
        newState.balanceDeltaB_P = None

        if txInput.txType == "Noop":

            # Nothing to do
            pass

        elif txInput.txType == "SpotTrade":

            ring = txInput

            # Amount filled in the trade history
            filled_A = self.getData(ring.orderA.accountID, ring.orderA.tokenS, ring.orderA.storageID)
            filled_B = self.getData(ring.orderB.accountID, ring.orderB.tokenS, ring.orderB.storageID)

            # Simple matching logic
            fillA = self.getMaxFill(ring.orderA, filled_A, True)
            fillB = self.getMaxFill(ring.orderB, filled_B, True)
            '''
            print("fillA.S: " + str(fillA.S))
            print("fillA.B: " + str(fillA.B))
            print("fillB.S: " + str(fillB.S))
            print("fillB.B: " + str(fillB.B))
            print("-------------")
            '''
            if ring.orderA.fillAmountBorS:
                (spread, matchable) = self.match(ring.orderA, fillA, ring.orderB, fillB)
                fillA.S = fillB.B
            else:
                (spread, matchable) = self.match(ring.orderB, fillB, ring.orderA, fillA)
                fillA.B = fillB.S

            # Check valid
            ring.orderA.checkValid(context, ring.orderA, fillA.S, fillA.B)
            ring.orderB.checkValid(context, ring.orderB, fillB.S, fillB.B)
            ring.valid = matchable and ring.orderA.valid and ring.orderB.valid
            #print("ring.orderA.valid " + str(ring.orderA.valid))
            #print("ring.orderB.valid " + str(ring.orderB.valid))
            #if ring.valid == False:
                #print("ring.valid false: ")
                #fillA.S = 0
                #fillA.B = 0
                #fillB.S = 0
                #fillB.B = 0

            # Saved in ring for tests
            ring.fFillS_A = toFloat(fillA.S, Float24Encoding)
            ring.fFillS_B = toFloat(fillB.S, Float24Encoding)

            fillA.S = roundToFloatValue(fillA.S, Float24Encoding)
            fillB.S = roundToFloatValue(fillB.S, Float24Encoding)
            fillA.B = fillB.S
            fillB.B = fillA.S

            '''
            print("fillA.S: " + str(fillA.S))
            print("fillA.B: " + str(fillA.B))
            print("fillB.S: " + str(fillB.S))
            print("fillB.B: " + str(fillB.B))
            print("spread: " + str(spread))
            '''

            (fee_A, protocolFee_A) = self.calculateFees(
                fillA.B,
                ring.orderA.feeBips,
                context.protocolTakerFeeBips
            )

            (fee_B, protocolFee_B) = self.calculateFees(
                fillB.B,
                ring.orderB.feeBips,
                context.protocolMakerFeeBips
            )

            '''
            print("fee_A: " + str(fee_A))
            print("protocolFee_A: " + str(protocolFee_A))

            print("fee_B: " + str(fee_B))
            print("protocolFee_B: " + str(protocolFee_B))
            '''

            newState.signatureA = ring.orderA.signature
            newState.signatureB = ring.orderB.signature

            newState.TXV_ACCOUNT_A_ADDRESS = ring.orderA.accountID
            accountA = self.getAccount(ring.orderA.accountID)

            newState.TXV_BALANCE_A_S_ADDRESS = ring.orderA.tokenS
            newState.TXV_BALANCE_A_S_BALANCE = -fillA.S

            newState.TXV_BALANCE_B_S_ADDRESS = ring.orderA.tokenB
            newState.TXV_BALANCE_A_B_BALANCE = fillA.B - fee_A

            newState.TXV_STORAGE_A_ADDRESS = ring.orderA.storageID
            newState.TXV_STORAGE_A_DATA = filled_A + (fillA.B if ring.orderA.fillAmountBorS else fillA.S)
            newState.TXV_STORAGE_A_STORAGEID = ring.orderA.storageID


            newState.TXV_ACCOUNT_B_ADDRESS = ring.orderB.accountID
            accountB = self.getAccount(ring.orderB.accountID)

            newState.TXV_BALANCE_B_S_ADDRESS = ring.orderB.tokenS
            newState.TXV_BALANCE_B_S_BALANCE = -fillB.S

            newState.TXV_BALANCE_A_S_ADDRESS = ring.orderB.tokenB
            newState.TXV_BALANCE_B_B_BALANCE = fillB.B - fee_B

            newState.TXV_STORAGE_B_ADDRESS = ring.orderB.storageID
            newState.TXV_STORAGE_B_DATA = filled_B + (fillB.B if ring.orderB.fillAmountBorS else fillB.S)
            newState.TXV_STORAGE_B_STORAGEID = ring.orderB.storageID

            newState.balanceDeltaA_O = fee_A - protocolFee_A
            newState.balanceDeltaB_O = fee_B - protocolFee_B

            newState.balanceDeltaA_P = protocolFee_A
            newState.balanceDeltaB_P = protocolFee_B

        elif txInput.txType == "Transfer":

            storageData = self.getData(txInput.fromAccountID, txInput.tokenID, txInput.storageID)

            transferAmount = roundToFloatValue(int(txInput.amount), Float24Encoding)
            feeValue = roundToFloatValue(int(txInput.fee), Float16Encoding)

            newState.signatureA = txInput.signature
            newState.signatureB = txInput.dualSignature

            newState.TXV_ACCOUNT_A_ADDRESS = txInput.fromAccountID
            accountA = self.getAccount(newState.TXV_ACCOUNT_A_ADDRESS)

            newState.TXV_BALANCE_A_S_ADDRESS = txInput.tokenID
            newState.TXV_BALANCE_A_S_BALANCE = -transferAmount

            newState.TXV_BALANCE_B_S_ADDRESS = txInput.feeTokenID
            newState.TXV_BALANCE_A_B_BALANCE = -feeValue

            newState.TXV_ACCOUNT_B_ADDRESS = txInput.toAccountID
            accountB = self.getAccount(newState.TXV_ACCOUNT_B_ADDRESS)
            newState.TXV_ACCOUNT_B_OWNER = txInput.to

            newState.TXV_BALANCE_A_S_ADDRESS = txInput.tokenID
            newState.TXV_BALANCE_B_B_BALANCE = transferAmount

            newState.TXV_STORAGE_A_ADDRESS = txInput.storageID
            newState.TXV_STORAGE_A_DATA = 1
            newState.TXV_STORAGE_A_STORAGEID = txInput.storageID

            if txInput.type != 0:
                context.numConditionalTransactions = context.numConditionalTransactions + 1

            newState.balanceDeltaA_O = feeValue

            # For tests (used to set the DA data)
            txInput.toNewAccount = True if accountB.owner == str(0) else False

        elif txInput.txType == "Withdraw":

            ## calculate how much can be withdrawn
            account = self.getAccount(txInput.accountID)
            if int(txInput.type) == 2:
                # Full balance with intrest
                balanceLeaf = account.getBalanceLeaf(txInput.tokenID)
                txInput.amount = str(balanceLeaf.balance)
            elif int(txInput.type) == 3:
                txInput.amount = str(0)


            # Protocol fee withdrawals are handled a bit differently
            # as the balance needs to be withdrawn from the already opened protocol pool account
            isProtocolfeeWithdrawal = int(txInput.accountID) == 0

            feeValue = roundToFloatValue(int(txInput.fee), Float16Encoding)

            newState.signatureA = txInput.signature

            newState.TXV_ACCOUNT_A_ADDRESS = 1 if isProtocolfeeWithdrawal else txInput.accountID
            accountA = self.getAccount(newState.TXV_ACCOUNT_A_ADDRESS)

            newState.TXV_BALANCE_A_S_ADDRESS = txInput.tokenID
            newState.TXV_BALANCE_A_S_BALANCE = 0 if isProtocolfeeWithdrawal else -int(txInput.amount)

            newState.TXV_BALANCE_B_S_ADDRESS = txInput.feeTokenID
            newState.TXV_BALANCE_A_B_BALANCE = -feeValue

            newState.TXV_STORAGE_A_ADDRESS = txInput.storageID
            if int(txInput.type) == 0 or int(txInput.type) == 1:
                newState.TXV_STORAGE_A_DATA = 1
                newState.TXV_STORAGE_A_STORAGEID = txInput.storageID
            if not isProtocolfeeWithdrawal and int(txInput.type) == 2:
                newState.TXV_BALANCE_A_S_WEIGHT = 0

            newState.balanceDeltaA_O = feeValue

            newState.balanceDeltaB_P = -int(txInput.amount) if isProtocolfeeWithdrawal else 0

            context.numConditionalTransactions = context.numConditionalTransactions + 1

        elif txInput.txType == "Deposit":

            newState.TXV_ACCOUNT_A_ADDRESS = txInput.accountID
            newState.TXV_ACCOUNT_A_OWNER = txInput.owner

            newState.TXV_BALANCE_A_S_ADDRESS = txInput.tokenID
            newState.TXV_BALANCE_A_S_BALANCE = txInput.amount

            context.numConditionalTransactions = context.numConditionalTransactions + 1

        elif txInput.txType == "AccountUpdate":

            feeValue = roundToFloatValue(int(txInput.fee), Float16Encoding)

            newState.TXV_ACCOUNT_A_ADDRESS = txInput.accountID
            accountA = self.getAccount(newState.TXV_ACCOUNT_A_ADDRESS)

            newState.TXV_ACCOUNT_A_PUBKEY_X = txInput.publicKeyX
            newState.TXV_ACCOUNT_A_PUBKEY_Y = txInput.publicKeyY
            newState.TXV_ACCOUNT_A_NONCE = 1

            newState.TXV_BALANCE_A_S_ADDRESS = txInput.feeTokenID
            newState.TXV_BALANCE_A_S_BALANCE = -feeValue

            newState.balanceDeltaB_O = feeValue

            newState.signatureA = txInput.signature

            if txInput.type != 0:
                context.numConditionalTransactions = context.numConditionalTransactions + 1

        elif txInput.txType == "AmmUpdate":

            # Cache the balance for tests
            account = self.getAccount(txInput.accountID)
            balanceLeaf = account.getBalanceLeaf(txInput.tokenID)
            txInput.balance = str(balanceLeaf.balance)

            newState.TXV_ACCOUNT_A_ADDRESS = txInput.accountID
            newState.TXV_BALANCE_A_S_ADDRESS = txInput.tokenID

            newState.TXV_ACCOUNT_A_NONCE = 1
            newState.TXV_ACCOUNT_A_FEEBIPSAMM = txInput.feeBips
            newState.TXV_BALANCE_A_S_WEIGHT = txInput.tokenWeight

            context.numConditionalTransactions = context.numConditionalTransactions + 1


        # Tokens default values
        newState.TXV_BALANCE_A_S_ADDRESS = setValue(newState.TXV_BALANCE_A_S_ADDRESS, 0)
        newState.TXV_BALANCE_B_S_ADDRESS = setValue(newState.TXV_BALANCE_B_S_ADDRESS, 0)

        # A default values
        newState.TXV_ACCOUNT_A_ADDRESS = setValue(newState.TXV_ACCOUNT_A_ADDRESS, 1)
        accountA = self.getAccount(newState.TXV_ACCOUNT_A_ADDRESS)
        newState.TXV_ACCOUNT_A_OWNER = setValue(newState.TXV_ACCOUNT_A_OWNER, accountA.owner)
        newState.TXV_ACCOUNT_A_PUBKEY_X = setValue(newState.TXV_ACCOUNT_A_PUBKEY_X, accountA.publicKeyX)
        newState.TXV_ACCOUNT_A_PUBKEY_Y = setValue(newState.TXV_ACCOUNT_A_PUBKEY_Y, accountA.publicKeyY)
        newState.TXV_ACCOUNT_A_NONCE = setValue(newState.TXV_ACCOUNT_A_NONCE, 0)
        newState.TXV_ACCOUNT_A_FEEBIPSAMM = setValue(newState.TXV_ACCOUNT_A_FEEBIPSAMM, accountA.feeBipsAMM)

        balanceLeafA_S = accountA.getBalanceLeaf(newState.TXV_BALANCE_A_S_ADDRESS)
        newState.TXV_BALANCE_A_S_BALANCE = setValue(newState.TXV_BALANCE_A_S_BALANCE, 0)
        newState.TXV_BALANCE_A_S_WEIGHT = setValue(newState.TXV_BALANCE_A_S_WEIGHT, balanceLeafA_S.weightAMM)

        newState.TXV_BALANCE_A_B_BALANCE = setValue(newState.TXV_BALANCE_A_B_BALANCE, 0)

        newState.TXV_STORAGE_A_ADDRESS = setValue(newState.TXV_STORAGE_A_ADDRESS, 0)
        storageA = balanceLeafA_S.getStorage(newState.TXV_STORAGE_A_ADDRESS)
        newState.TXV_STORAGE_A_DATA = setValue(newState.TXV_STORAGE_A_DATA, storageA.data)
        newState.TXV_STORAGE_A_STORAGEID = setValue(newState.TXV_STORAGE_A_STORAGEID, storageA.storageID)

        # Operator default values
        newState.balanceDeltaA_O = setValue(newState.balanceDeltaA_O, 0)
        newState.balanceDeltaB_O = setValue(newState.balanceDeltaB_O, 0)

        # Protocol fees default values
        newState.balanceDeltaA_P = setValue(newState.balanceDeltaA_P, 0)
        newState.balanceDeltaB_P = setValue(newState.balanceDeltaB_P, 0)


        # Copy the initial merkle root
        accountsMerkleRoot = self._accountsTree._root

        # Update A
        accountA = self.getAccount(newState.TXV_ACCOUNT_A_ADDRESS)

        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(newState.TXV_ACCOUNT_A_ADDRESS))
        proof = self._accountsTree.createProof(newState.TXV_ACCOUNT_A_ADDRESS)

        (balanceUpdateS_A, storageUpdate_A) = accountA.updateBalanceAndStorage(
            newState.TXV_BALANCE_A_S_ADDRESS,
            newState.TXV_STORAGE_A_STORAGEID,
            newState.TXV_STORAGE_A_DATA,
            newState.TXV_BALANCE_A_S_BALANCE,
            newState.TXV_BALANCE_A_S_WEIGHT
        )
        balanceUpdateB_A = accountA.updateBalance(
            newState.TXV_BALANCE_B_S_ADDRESS,
            newState.TXV_BALANCE_A_B_BALANCE
        )

        accountA.owner = newState.TXV_ACCOUNT_A_OWNER
        accountA.publicKeyX = newState.TXV_ACCOUNT_A_PUBKEY_X
        accountA.publicKeyY = newState.TXV_ACCOUNT_A_PUBKEY_Y
        accountA.nonce = accountA.nonce + newState.TXV_ACCOUNT_A_NONCE
        accountA.feeBipsAMM = newState.TXV_ACCOUNT_A_FEEBIPSAMM

        self.updateAccountTree(newState.TXV_ACCOUNT_A_ADDRESS)
        accountAfter = copyAccountInfo(self.getAccount(newState.TXV_ACCOUNT_A_ADDRESS))
        rootAfter = self._accountsTree._root
        accountUpdate_A = AccountUpdateData(newState.TXV_ACCOUNT_A_ADDRESS, proof, rootBefore, rootAfter, accountBefore, accountAfter)
        ###

        # B default values
        newState.TXV_ACCOUNT_B_ADDRESS = setValue(newState.TXV_ACCOUNT_B_ADDRESS, 1)
        accountB = self.getAccount(newState.TXV_ACCOUNT_B_ADDRESS)
        newState.TXV_ACCOUNT_B_OWNER = setValue(newState.TXV_ACCOUNT_B_OWNER, accountB.owner)
        newState.TXV_ACCOUNT_B_PUBKEY_X = setValue(newState.TXV_ACCOUNT_B_PUBKEY_X, accountB.publicKeyX)
        newState.TXV_ACCOUNT_B_PUBKEY_Y = setValue(newState.TXV_ACCOUNT_B_PUBKEY_Y, accountB.publicKeyY)
        newState.TXV_ACCOUNT_B_NONCE = setValue(newState.TXV_ACCOUNT_B_NONCE, 0)

        balanceLeafB_S = accountB.getBalanceLeaf(newState.TXV_BALANCE_B_S_ADDRESS)
        newState.TXV_BALANCE_B_S_BALANCE = setValue(newState.TXV_BALANCE_B_S_BALANCE, 0)

        newState.TXV_BALANCE_B_B_BALANCE = setValue(newState.TXV_BALANCE_B_B_BALANCE, 0)

        newState.TXV_STORAGE_B_ADDRESS = setValue(newState.TXV_STORAGE_B_ADDRESS, 0)
        storageB = balanceLeafB_S.getStorage(newState.TXV_STORAGE_B_ADDRESS)
        newState.TXV_STORAGE_B_DATA = setValue(newState.TXV_STORAGE_B_DATA, storageB.data)
        newState.TXV_STORAGE_B_STORAGEID = setValue(newState.TXV_STORAGE_B_STORAGEID, storageB.storageID)

        # Update B
        accountB = self.getAccount(newState.TXV_ACCOUNT_B_ADDRESS)

        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(newState.TXV_ACCOUNT_B_ADDRESS))
        proof = self._accountsTree.createProof(newState.TXV_ACCOUNT_B_ADDRESS)

        (balanceUpdateS_B, storageUpdate_B) = accountB.updateBalanceAndStorage(
            newState.TXV_BALANCE_B_S_ADDRESS,
            newState.TXV_STORAGE_B_STORAGEID,
            newState.TXV_STORAGE_B_DATA,
            newState.TXV_BALANCE_B_S_BALANCE
        )
        balanceUpdateB_B = accountB.updateBalance(
            newState.TXV_BALANCE_A_S_ADDRESS,
            newState.TXV_BALANCE_B_B_BALANCE
        )

        accountB.owner = newState.TXV_ACCOUNT_B_OWNER
        accountB.publicKeyX = newState.TXV_ACCOUNT_B_PUBKEY_X
        accountB.publicKeyY = newState.TXV_ACCOUNT_B_PUBKEY_Y
        accountB.nonce = accountB.nonce + newState.TXV_ACCOUNT_B_NONCE

        self.updateAccountTree(newState.TXV_ACCOUNT_B_ADDRESS)
        accountAfter = copyAccountInfo(self.getAccount(newState.TXV_ACCOUNT_B_ADDRESS))
        rootAfter = self._accountsTree._root
        accountUpdate_B = AccountUpdateData(newState.TXV_ACCOUNT_B_ADDRESS, proof, rootBefore, rootAfter, accountBefore, accountAfter)
        ###

        # Update balances Operator
        accountO = self.getAccount(context.operatorAccountID)

        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(context.operatorAccountID))
        proof = self._accountsTree.createProof(context.operatorAccountID)

        balanceUpdateB_O = accountO.updateBalance(
            newState.TXV_BALANCE_A_S_ADDRESS,
            newState.balanceDeltaB_O
        )
        balanceUpdateA_O = accountO.updateBalance(
            newState.TXV_BALANCE_B_S_ADDRESS,
            newState.balanceDeltaA_O
        )

        self.updateAccountTree(context.operatorAccountID)
        accountAfter = copyAccountInfo(self.getAccount(context.operatorAccountID))
        rootAfter = self._accountsTree._root
        accountUpdate_O = AccountUpdateData(context.operatorAccountID, proof, rootBefore, rootAfter, accountBefore, accountAfter)
        ###

        # Protocol fee payment
        balanceUpdateB_P = self.getAccount(0).updateBalance(newState.TXV_BALANCE_A_S_ADDRESS, newState.balanceDeltaB_P)
        balanceUpdateA_P = self.getAccount(0).updateBalance(newState.TXV_BALANCE_B_S_ADDRESS, newState.balanceDeltaA_P)
        ###

        witness = Witness(newState.signatureA, newState.signatureB,
                          accountsMerkleRoot,
                          storageUpdate_A, storageUpdate_B,
                          balanceUpdateS_A, balanceUpdateB_A, accountUpdate_A,
                          balanceUpdateS_B, balanceUpdateB_B, accountUpdate_B,
                          balanceUpdateA_O, balanceUpdateB_O, accountUpdate_O,
                          balanceUpdateA_P, balanceUpdateB_P)

        return TxWitness(witness, txInput)

    def getAccount(self, accountID):
        # Make sure the leaf exist in our map
        if not(str(accountID) in self._accounts):
            # print("Account doesn't exist: " + str(accountID))
            self._accounts[str(accountID)] = Account(0, Point(0, 0))
        return self._accounts[str(accountID)]

    def updateAccountTree(self, accountID):
        self._accountsTree.update(accountID, self.getAccount(accountID).hash())

    def getRoot(self):
        return self._accountsTree._root
Пример #6
0
class Account(object):
    def __init__(self, owner, publicKey):
        self.owner = str(owner)
        self.publicKeyX = str(publicKey.x)
        self.publicKeyY = str(publicKey.y)
        self.nonce = 0
        self.feeBipsAMM = 0
        # Balances
        self._balancesTree = SparseMerkleTree(BINARY_TREE_DEPTH_TOKENS // 2, 4)
        self._balancesTree.newTree(BalanceLeaf().hash())
        self._balancesLeafs = {}
        # print("Empty balances tree: " + str(self._balancesTree._root))

    def hash(self):
        return poseidon([int(self.owner), int(self.publicKeyX), int(self.publicKeyY), int(self.nonce), int(self.feeBipsAMM), int(self._balancesTree._root)], poseidonParamsAccount)

    def fromJSON(self, jAccount):
        self.owner = jAccount["owner"]
        self.publicKeyX = jAccount["publicKeyX"]
        self.publicKeyY = jAccount["publicKeyY"]
        self.nonce = int(jAccount["nonce"])
        self.feeBipsAMM = int(jAccount["feeBipsAMM"])
        # Balances
        balancesLeafsDict = jAccount["_balancesLeafs"]
        for key, val in balancesLeafsDict.items():
            balanceLeaf = BalanceLeaf()
            balanceLeaf.fromJSON(val)
            self._balancesLeafs[key] = balanceLeaf
        self._balancesTree._root = jAccount["_balancesTree"]["_root"]
        self._balancesTree._db.kv = jAccount["_balancesTree"]["_db"]["kv"]

    def getBalanceLeaf(self, address):
        # Make sure the leaf exist in our map
        if not(str(address) in self._balancesLeafs):
            return BalanceLeaf()
        else:
            return self._balancesLeafs[str(address)]

    def getBalance(self, address):
        return self.getBalanceLeaf(address).balance

    def updateBalance(self, tokenID, deltaBalance):
        # Make sure the leaf exists in our map
        if not(str(tokenID) in self._balancesLeafs):
            self._balancesLeafs[str(tokenID)] = BalanceLeaf()

        balancesBefore = copyBalanceInfo(self._balancesLeafs[str(tokenID)])
        rootBefore = self._balancesTree._root

        self._balancesLeafs[str(tokenID)].balance = str(int(self._balancesLeafs[str(tokenID)].balance) + int(deltaBalance))

        balancesAfter = copyBalanceInfo(self._balancesLeafs[str(tokenID)])
        proof = self._balancesTree.createProof(tokenID)
        self._balancesTree.update(tokenID, self._balancesLeafs[str(tokenID)].hash())
        rootAfter = self._balancesTree._root

        return BalanceUpdateData(tokenID, proof,
                                 rootBefore, rootAfter,
                                 balancesBefore, balancesAfter)

    def updateBalanceAndStorage(self, tokenID, storageID, filled, delta_balance, weight = None):
        # Make sure the leaf exist in our map
        if not(str(tokenID) in self._balancesLeafs):
            self._balancesLeafs[str(tokenID)] = BalanceLeaf()

        balancesBefore = copyBalanceInfo(self._balancesLeafs[str(tokenID)])
        rootBefore = self._balancesTree._root

        # Update filled amounts
        storageUpdate = self._balancesLeafs[str(tokenID)].updateStorage(storageID, filled)
        self._balancesLeafs[str(tokenID)].balance = str(int(self._balancesLeafs[str(tokenID)].balance) + int(delta_balance))
        if weight is not None:
            self._balancesLeafs[str(tokenID)].weightAMM = str(weight)

        #print("str(delta_balance): " + str(delta_balance))
        #print("endBalance: " + self._balancesLeafs[str(tokenID)].balance)

        balancesAfter = copyBalanceInfo(self._balancesLeafs[str(tokenID)])
        proof = self._balancesTree.createProof(tokenID)
        self._balancesTree.update(tokenID, self._balancesLeafs[str(tokenID)].hash())
        rootAfter = self._balancesTree._root

        return (BalanceUpdateData(tokenID, proof,
                                 rootBefore, rootAfter,
                                 balancesBefore, balancesAfter),
                storageUpdate)
Пример #7
0
class State(object):
    def __init__(self, realmID):
        self.realmID = int(realmID)
        # Accounts
        self._accountsTree = SparseMerkleTree(TREE_DEPTH_ACCOUNTS)
        self._accountsTree.newTree(getDefaultAccount().hash())
        self._accounts = {}
        self._accounts[str(0)] = getDefaultAccount()
        # print("Empty accounts tree: " + str(hex(self._accountsTree._root)))

    def load(self, filename):
        with open(filename) as f:
            data = json.load(f)
            self.realmID = int(data["realmID"])
            # Accounts
            accountLeafsDict = data["accounts_values"]
            for key, val in accountLeafsDict.items():
                account = getDefaultAccount()
                account.fromJSON(val)
                self._accounts[key] = account
            self._accountsTree._root = data["accounts_root"]
            self._accountsTree._db.kv = data["accounts_tree"]

    def save(self, filename):
        with open(filename, "w") as file:
            file.write(
                json.dumps(
                    {
                        "realmID": self.realmID,
                        "accounts_values": self._accounts,
                        "accounts_root": self._accountsTree._root,
                        "accounts_tree": self._accountsTree._db.kv,
                    },
                    default=lambda o: o.__dict__,
                    sort_keys=True,
                    indent=4))

    def calculateFees(self, fee, walletSplitPercentage, waiveFeePercentage):
        walletFee = (fee * walletSplitPercentage) // 100
        matchingFee = fee - walletFee

        matchingFeeAfterWaiving = (matchingFee * waiveFeePercentage) // 100

        return (walletFee, matchingFeeAfterWaiving)

    def getMaxFillAmounts(self, order):
        account = self.getAccount(order.accountID)
        tradeHistory = account.getBalanceLeaf(order.tokenS).getTradeHistory(
            int(order.orderID))
        order.tradeHistoryFilled = str(tradeHistory.filled)
        order.tradeHistoryCancelled = int(tradeHistory.cancelled)
        order.tradeHistoryOrderID = int(tradeHistory.orderID)
        order.nonce = int(account.nonce)
        order.balanceS = str(account.getBalance(order.tokenS))
        order.balanceB = str(account.getBalance(order.tokenB))
        order.balanceF = str(account.getBalance(order.tokenF))

        # Trade history trimming
        bNew = tradeHistory.orderID < order.orderID
        bTrim = not (tradeHistory.orderID <= order.orderID)
        filled = 0 if bNew else int(tradeHistory.filled)
        cancelledToStore = 0 if bNew else int(tradeHistory.cancelled)
        cancelled = 1 if bTrim else cancelledToStore
        orderIDToStore = int(order.orderID) if bNew else tradeHistory.orderID
        """
        print("bNew: " + str(bNew))
        print("bTrim: " + str(bTrim))
        print("filled: " + str(filled))
        print("cancelledToStore: " + str(cancelledToStore))
        print("cancelled: " + str(cancelled))
        print("orderIDToStore: " + str(orderIDToStore))
        """

        # Scale the order
        balanceS = int(account.getBalance(order.tokenS))
        balanceF = int(account.getBalance(order.tokenF))
        remainingS = int(order.amountS) - filled
        if cancelled == 1:
            remainingS = 0
        fillAmountS = balanceS if (balanceS < remainingS) else remainingS

        # Check how much fee needs to be paid. We limit fillAmountS to how much
        # fee the order owner can pay.
        fillAmountF = int(order.amountF) * fillAmountS // int(order.amountS)

        if order.tokenF == order.tokenS and balanceS < fillAmountS + fillAmountF:
            # Equally divide the available tokens between fillAmountS and fillAmountF
            fillAmountS = balanceS * int(
                order.amountS) // (int(order.amountS) + int(order.amountF))

        if order.tokenF != order.tokenS and balanceF < fillAmountF:
            # Scale down fillAmountS so the available fillAmountF is sufficient
            fillAmountS = balanceF * int(order.amountS) // int(order.amountF)

        if order.tokenF == order.tokenB and int(order.amountF) <= int(
                order.amountB):
            # No rebalancing (because of insufficient balanceF) is ever necessary when amountF <= amountB
            fillAmountS = balanceS if (balanceS < remainingS) else remainingS

        fillAmountB = (fillAmountS * int(order.amountB)) // int(order.amountS)
        return (fillAmountS, fillAmountB, filled, cancelledToStore,
                orderIDToStore)

    def settleRing(self, context, ring):
        #print("State update ring: ")

        (fillAmountS_A, fillAmountB_A, filled_A, cancelledToStore_A,
         orderIDToStore_A) = self.getMaxFillAmounts(ring.orderA)
        (fillAmountS_B, fillAmountB_B, filled_B, cancelledToStore_B,
         orderIDToStore_B) = self.getMaxFillAmounts(ring.orderB)
        '''
        print("fillAmountS_A: " + str(fillAmountS_A))
        print("fillAmountB_A: " + str(fillAmountB_A))
        print("fillAmountS_B: " + str(fillAmountS_B))
        print("fillAmountB_B: " + str(fillAmountB_B))
        print("-------------")
        '''

        if fillAmountB_A < fillAmountS_B:
            fillAmountS_B = fillAmountB_A
            fillAmountB_B = (fillAmountS_B * int(ring.orderB.amountB)) // int(
                ring.orderB.amountS)
        else:
            fillAmountB_A = fillAmountS_B
            fillAmountS_A = (fillAmountB_A * int(ring.orderA.amountS)) // int(
                ring.orderA.amountB)

        fillAmountF_A = (int(ring.orderA.amountF) * fillAmountS_A) // int(
            ring.orderA.amountS)
        fillAmountF_B = (int(ring.orderB.amountF) * fillAmountS_B) // int(
            ring.orderB.amountS)

        margin = fillAmountS_A - fillAmountB_B

        # matchable
        ring.valid = True
        if fillAmountS_A < fillAmountB_B:
            ring.valid = False

        # self-trading
        totalFee = fillAmountF_A + fillAmountF_B
        if ring.orderA.accountID == ring.orderB.accountID and ring.orderA.tokenF == ring.orderB.tokenF and int(
                ring.orderA.balanceF) < totalFee:
            ring.valid = False

        ring.orderA.checkValid(context, fillAmountS_A, fillAmountB_A)
        ring.orderB.checkValid(context, fillAmountS_B, fillAmountB_B)
        ring.valid = ring.valid and ring.orderA.valid and ring.orderB.valid

        #print("ring.orderA.valid " + str(ring.orderA.valid))
        #print("ring.orderB.valid " + str(ring.orderB.valid))

        if ring.valid == False:
            #print("ring.valid false: ")
            fillAmountS_A = 0
            fillAmountB_A = 0
            fillAmountF_A = 0
            fillAmountS_B = 0
            fillAmountB_B = 0
            fillAmountF_B = 0
            margin = 0

        ring.fillS_A = str(fillAmountS_A)
        ring.fillB_A = str(fillAmountB_A)
        ring.fillF_A = str(fillAmountF_A)

        ring.fillS_B = str(fillAmountS_B)
        ring.fillB_B = str(fillAmountB_B)
        ring.fillF_B = str(fillAmountF_B)

        ring.margin = str(margin)
        '''
        print("fillAmountS_A: " + str(fillAmountS_A))
        print("fillAmountB_A: " + str(fillAmountB_A))
        print("fillAmountF_A: " + str(fillAmountF_A))
        print("fillAmountS_B: " + str(fillAmountS_B))
        print("fillAmountB_B: " + str(fillAmountB_B))
        print("fillAmountF_B: " + str(fillAmountF_B))
        print("margin: " + str(margin))
        '''

        # Copy the initial merkle root
        accountsMerkleRoot = self._accountsTree._root

        (walletFee_A,
         matchingFee_A) = self.calculateFees(int(ring.fillF_A),
                                             ring.orderA.walletSplitPercentage,
                                             ring.orderA.waiveFeePercentage)

        (walletFee_B,
         matchingFee_B) = self.calculateFees(int(ring.fillF_B),
                                             ring.orderB.walletSplitPercentage,
                                             ring.orderB.waiveFeePercentage)

        # Update balances A
        accountA = self.getAccount(ring.orderA.accountID)

        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(ring.orderA.accountID))
        proof = self._accountsTree.createProof(ring.orderA.accountID)

        (balanceUpdateS_A,
         tradeHistoryUpdate_A) = accountA.updateBalanceAndTradeHistory(
             ring.orderA.tokenS, ring.orderA.orderID, -int(ring.fillS_A),
             filled_A + int(ring.fillS_A), cancelledToStore_A,
             orderIDToStore_A)
        balanceUpdateB_A = accountA.updateBalance(ring.orderA.tokenB,
                                                  int(ring.fillB_A))
        balanceUpdateF_A = accountA.updateBalance(
            ring.orderA.tokenF, -(walletFee_A + matchingFee_A))

        self.updateAccountTree(ring.orderA.accountID)
        accountAfter = copyAccountInfo(self.getAccount(ring.orderA.accountID))
        rootAfter = self._accountsTree._root
        accountUpdate_A = AccountUpdateData(ring.orderA.accountID, proof,
                                            rootBefore, rootAfter,
                                            accountBefore, accountAfter)
        ###

        # Update balances B
        accountB = self.getAccount(ring.orderB.accountID)

        ring.orderB.balanceS = str(accountB.getBalance(ring.orderB.tokenS))
        ring.orderB.balanceB = str(accountB.getBalance(ring.orderB.tokenB))
        ring.orderB.balanceF = str(accountB.getBalance(ring.orderB.tokenF))

        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(ring.orderB.accountID))
        proof = self._accountsTree.createProof(ring.orderB.accountID)

        (balanceUpdateS_B,
         tradeHistoryUpdate_B) = accountB.updateBalanceAndTradeHistory(
             ring.orderB.tokenS, ring.orderB.orderID, -int(ring.fillS_B),
             filled_B + int(ring.fillS_B), cancelledToStore_B,
             orderIDToStore_B)
        balanceUpdateB_B = accountB.updateBalance(ring.orderB.tokenB,
                                                  int(ring.fillB_B))
        balanceUpdateF_B = accountB.updateBalance(
            ring.orderB.tokenF, -(walletFee_B + matchingFee_B))

        self.updateAccountTree(ring.orderB.accountID)
        accountAfter = copyAccountInfo(self.getAccount(ring.orderB.accountID))
        rootAfter = self._accountsTree._root
        accountUpdate_B = AccountUpdateData(ring.orderB.accountID, proof,
                                            rootBefore, rootAfter,
                                            accountBefore, accountAfter)
        ###

        # Update wallet A
        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(
            self.getAccount(ring.orderA.walletAccountID))
        proof = self._accountsTree.createProof(ring.orderA.walletAccountID)

        balanceUpdateA_W = self.getAccount(
            ring.orderA.walletAccountID).updateBalance(ring.orderA.tokenF,
                                                       walletFee_A)

        self.updateAccountTree(ring.orderA.walletAccountID)
        accountAfter = copyAccountInfo(
            self.getAccount(ring.orderA.walletAccountID))
        rootAfter = self._accountsTree._root
        accountUpdateA_W = AccountUpdateData(ring.orderA.walletAccountID,
                                             proof, rootBefore, rootAfter,
                                             accountBefore, accountAfter)
        ###

        # Update wallet B
        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(
            self.getAccount(ring.orderB.walletAccountID))
        proof = self._accountsTree.createProof(ring.orderB.walletAccountID)

        balanceUpdateB_W = self.getAccount(
            ring.orderB.walletAccountID).updateBalance(ring.orderB.tokenF,
                                                       walletFee_B)

        self.updateAccountTree(ring.orderB.walletAccountID)
        accountAfter = copyAccountInfo(
            self.getAccount(ring.orderB.walletAccountID))
        rootAfter = self._accountsTree._root
        accountUpdateB_W = AccountUpdateData(ring.orderB.walletAccountID,
                                             proof, rootBefore, rootAfter,
                                             accountBefore, accountAfter)
        ###

        # Update feeRecipient
        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(
            self.getAccount(ring.feeRecipientAccountID))
        proof = self._accountsTree.createProof(ring.feeRecipientAccountID)

        balanceUpdateA_F = self.getAccount(
            ring.feeRecipientAccountID).updateBalance(ring.orderA.tokenF,
                                                      matchingFee_A)
        balanceUpdateB_F = self.getAccount(
            ring.feeRecipientAccountID).updateBalance(ring.orderB.tokenF,
                                                      matchingFee_B)

        self.updateAccountTree(ring.feeRecipientAccountID)
        accountAfter = copyAccountInfo(
            self.getAccount(ring.feeRecipientAccountID))
        rootAfter = self._accountsTree._root
        accountUpdate_F = AccountUpdateData(ring.feeRecipientAccountID, proof,
                                            rootBefore, rootAfter,
                                            accountBefore, accountAfter)

        # Update ringmatcher
        accountM = self.getAccount(ring.minerAccountID)

        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(ring.minerAccountID))
        proof = self._accountsTree.createProof(ring.minerAccountID)

        balanceUpdateM_M = accountM.updateBalance(ring.orderA.tokenS,
                                                  int(ring.margin))
        balanceUpdateO_M = accountM.updateBalance(ring.tokenID, -int(ring.fee))
        accountM.nonce += 1

        self.updateAccountTree(ring.minerAccountID)
        accountAfter = copyAccountInfo(self.getAccount(ring.minerAccountID))
        rootAfter = self._accountsTree._root
        accountUpdate_M = AccountUpdateData(ring.minerAccountID, proof,
                                            rootBefore, rootAfter,
                                            accountBefore, accountAfter)
        ###

        # Operator payment
        balanceUpdateF_O = self.getAccount(
            context.operatorAccountID).updateBalance(ring.tokenID,
                                                     int(ring.fee))
        ###

        return RingSettlement(
            ring, accountsMerkleRoot, tradeHistoryUpdate_A,
            tradeHistoryUpdate_B, balanceUpdateS_A, balanceUpdateB_A,
            balanceUpdateF_A, accountUpdate_A, balanceUpdateS_B,
            balanceUpdateB_B, balanceUpdateF_B, accountUpdate_B,
            balanceUpdateA_W, accountUpdateA_W, balanceUpdateB_W,
            accountUpdateB_W, balanceUpdateA_F, balanceUpdateB_F,
            accountUpdate_F, balanceUpdateM_M, balanceUpdateO_M,
            accountUpdate_M, balanceUpdateF_O, walletFee_A, matchingFee_A,
            walletFee_B, matchingFee_B)

    def deposit(self, accountID, secretKey, publicKeyX, publicKeyY, token,
                amount):
        # Copy the initial merkle root
        rootBefore = self._accountsTree._root

        if not (str(accountID) in self._accounts):
            accountBefore = copyAccountInfo(getDefaultAccount())
        else:
            accountBefore = copyAccountInfo(self.getAccount(accountID))

        proof = self._accountsTree.createProof(accountID)

        # Create the account if necessary
        if not (str(accountID) in self._accounts):
            self._accounts[str(accountID)] = Account(
                secretKey, Point(publicKeyX, publicKeyY))

        account = self.getAccount(accountID)
        balanceUpdate = account.updateBalance(token, amount)

        # Update keys
        account.secretKey = str(secretKey)
        account.publicKeyX = str(publicKeyX)
        account.publicKeyY = str(publicKeyY)

        self._accountsTree.update(accountID, account.hash())

        accountAfter = copyAccountInfo(account)

        rootAfter = self._accountsTree._root

        accountUpdate = AccountUpdateData(accountID, proof, rootBefore,
                                          rootAfter, accountBefore,
                                          accountAfter)
        return Deposit(amount, balanceUpdate, accountUpdate)

    def getAccount(self, accountID):
        # Make sure the leaf exist in our map
        if not (str(accountID) in self._accounts):
            print("Account doesn't exist: " + str(accountID))
        return self._accounts[str(accountID)]

    def onchainWithdraw(self, realmID, accountID, tokenID, amountRequested,
                        shutdown):
        # When a withdrawal is done before the deposit (account creation) we shouldn't
        # do anything. Just leave everything as it is.
        if str(accountID) in self._accounts:
            # Calculate amount withdrawn
            balance = int(self.getAccount(accountID).getBalance(tokenID))
            amount = int(amountRequested) if (
                int(amountRequested) < balance) else balance

            # Update account
            rootBefore = self._accountsTree._root
            accountBefore = copyAccountInfo(self.getAccount(accountID))
            proof = self._accountsTree.createProof(accountID)

            balanceUpdate = self.getAccount(accountID).updateBalance(
                tokenID, -amount, shutdown)
            if shutdown:
                self.getAccount(accountID).publicKeyX = str(0)
                self.getAccount(accountID).publicKeyY = str(0)
                self.getAccount(accountID).nonce = 0

            self.updateAccountTree(accountID)
            accountAfter = copyAccountInfo(self.getAccount(accountID))
            rootAfter = self._accountsTree._root
            accountUpdate = AccountUpdateData(accountID, proof, rootBefore,
                                              rootAfter, accountBefore,
                                              accountAfter)
            ###
        else:
            # Dummy update
            amount = 0

            rootBefore = self._accountsTree._root
            accountBefore = copyAccountInfo(getDefaultAccount())
            proof = self._accountsTree.createProof(accountID)

            balanceUpdate = getDefaultAccount().updateBalance(
                tokenID, 0, shutdown)

            accountAfter = copyAccountInfo(getDefaultAccount())
            rootAfter = self._accountsTree._root
            accountUpdate = AccountUpdateData(accountID, proof, rootBefore,
                                              rootAfter, accountBefore,
                                              accountAfter)
            ###

        withdrawal = OnchainWithdrawal(amountRequested, balanceUpdate,
                                       accountUpdate, accountID, tokenID,
                                       amount)
        return withdrawal

    def offchainWithdraw(self, realmID, accountID, tokenID, amountRequested,
                         operatorAccountID, walletAccountID, feeTokenID, fee,
                         walletSplitPercentage):

        feeToWallet = int(fee) * walletSplitPercentage // 100
        feeToOperator = int(fee) - feeToWallet

        # Update account
        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(accountID))
        nonce = accountBefore.nonce
        proof = self._accountsTree.createProof(accountID)

        balanceUpdateF_A = self.getAccount(accountID).updateBalance(
            feeTokenID, -fee)

        balance = int(self.getAccount(accountID).getBalance(tokenID))
        amountWithdrawn = int(amountRequested) if (
            int(amountRequested) < balance) else balance

        balanceUpdateW_A = self.getAccount(accountID).updateBalance(
            tokenID, -amountWithdrawn)
        self.getAccount(accountID).nonce += 1

        self.updateAccountTree(accountID)
        accountAfter = copyAccountInfo(self.getAccount(accountID))
        rootAfter = self._accountsTree._root
        accountUpdate_A = AccountUpdateData(accountID, proof, rootBefore,
                                            rootAfter, accountBefore,
                                            accountAfter)
        ###

        # Update wallet
        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(walletAccountID))
        proof = self._accountsTree.createProof(walletAccountID)

        balanceUpdateF_W = self.getAccount(walletAccountID).updateBalance(
            feeTokenID, feeToWallet)

        self.updateAccountTree(walletAccountID)
        accountAfter = copyAccountInfo(self.getAccount(walletAccountID))
        rootAfter = self._accountsTree._root
        accountUpdate_W = AccountUpdateData(walletAccountID, proof, rootBefore,
                                            rootAfter, accountBefore,
                                            accountAfter)
        ###

        # Operator payment
        balanceUpdateF_O = self.getAccount(operatorAccountID).updateBalance(
            feeTokenID, feeToOperator)

        account = self.getAccount(accountID)
        withdrawal = OffchainWithdrawal(
            realmID, accountID, tokenID, amountRequested, amountWithdrawn,
            walletAccountID, feeTokenID, fee, walletSplitPercentage,
            balanceUpdateF_A, balanceUpdateW_A, accountUpdate_A,
            balanceUpdateF_W, accountUpdate_W, balanceUpdateF_O, nonce)
        withdrawal.sign(FQ(int(account.secretKey)))
        return withdrawal

    def cancelOrder(self, realmID, accountID, orderTokenID, orderID,
                    walletAccountID, operatorAccountID, feeTokenID, fee,
                    walletSplitPercentage):

        feeToWallet = int(fee) * walletSplitPercentage // 100
        feeToOperator = int(fee) - feeToWallet

        # Update account
        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(accountID))
        nonce = accountBefore.nonce
        proof = self._accountsTree.createProof(accountID)

        (balanceUpdateT_A,
         tradeHistoryUpdate_A) = self.getAccount(accountID).cancelOrder(
             orderTokenID, orderID)
        balanceUpdateF_A = self.getAccount(accountID).updateBalance(
            feeTokenID, -fee)
        self.getAccount(accountID).nonce += 1

        self.updateAccountTree(accountID)
        accountAfter = copyAccountInfo(self.getAccount(accountID))
        rootAfter = self._accountsTree._root
        accountUpdate_A = AccountUpdateData(accountID, proof, rootBefore,
                                            rootAfter, accountBefore,
                                            accountAfter)
        ###

        # Update wallet
        rootBefore = self._accountsTree._root
        accountBefore = copyAccountInfo(self.getAccount(walletAccountID))
        proof = self._accountsTree.createProof(walletAccountID)

        balanceUpdateF_W = self.getAccount(walletAccountID).updateBalance(
            feeTokenID, feeToWallet)

        self.updateAccountTree(walletAccountID)
        accountAfter = copyAccountInfo(self.getAccount(walletAccountID))
        rootAfter = self._accountsTree._root
        accountUpdate_W = AccountUpdateData(walletAccountID, proof, rootBefore,
                                            rootAfter, accountBefore,
                                            accountAfter)
        ###

        # Operator payment
        balanceUpdateF_O = self.getAccount(operatorAccountID).updateBalance(
            feeTokenID, feeToOperator)

        account = self.getAccount(accountID)
        walletAccount = self.getAccount(walletAccountID)
        cancellation = Cancellation(
            Point(int(account.publicKeyX), int(account.publicKeyY)),
            Point(int(walletAccount.publicKeyX),
                  int(walletAccount.publicKeyY)), realmID, accountID,
            orderTokenID, orderID, walletAccountID, operatorAccountID,
            feeTokenID, fee, walletSplitPercentage, nonce,
            tradeHistoryUpdate_A, balanceUpdateT_A, balanceUpdateF_A,
            accountUpdate_A, balanceUpdateF_W, accountUpdate_W,
            balanceUpdateF_O)
        cancellation.sign(FQ(int(account.secretKey)))
        return cancellation

    def createWithdrawProof(self, realmID, accountID, tokenID):
        account = copyAccountInfo(self.getAccount(accountID))
        balance = copyBalanceInfo(
            self.getAccount(accountID)._balancesLeafs[str(tokenID)])
        accountProof = self._accountsTree.createProof(accountID)
        balanceProof = self.getAccount(accountID)._balancesTree.createProof(
            tokenID)

        return WithdrawProof(realmID, accountID, tokenID, account, balance,
                             self.getRoot(), accountProof, balanceProof)

    def updateAccountTree(self, accountID):
        self._accountsTree.update(accountID, self.getAccount(accountID).hash())

    def getRoot(self):
        return self._accountsTree._root