From b69faf69209ce31dd92e6d84d37ad52b2df07b87 Mon Sep 17 00:00:00 2001
From: Jack Robison <jackrobison@lbry.io>
Date: Mon, 24 May 2021 12:35:26 -0400
Subject: [PATCH] bid ordered resolve (WIP)

---
 lbry/wallet/server/db/__init__.py |  2 +-
 lbry/wallet/server/db/prefixes.py | 72 ++++++++++++++++++++++++++++++-
 lbry/wallet/server/leveldb.py     | 17 ++++----
 3 files changed, 81 insertions(+), 10 deletions(-)

diff --git a/lbry/wallet/server/db/__init__.py b/lbry/wallet/server/db/__init__.py
index 2a47fc5c5..befa3f3a2 100644
--- a/lbry/wallet/server/db/__init__.py
+++ b/lbry/wallet/server/db/__init__.py
@@ -12,7 +12,7 @@ class DB_PREFIXES(enum.Enum):
     channel_to_claim = b'J'
 
     claim_short_id_prefix = b'F'
-    # claim_effective_amount_prefix = b'D'
+    claim_effective_amount_prefix = b'D'
     claim_expiration = b'O'
 
     claim_takeover = b'P'
diff --git a/lbry/wallet/server/db/prefixes.py b/lbry/wallet/server/db/prefixes.py
index c33b1371a..a766068cb 100644
--- a/lbry/wallet/server/db/prefixes.py
+++ b/lbry/wallet/server/db/prefixes.py
@@ -182,6 +182,17 @@ class ActiveAmountValue(typing.NamedTuple):
     amount: int
 
 
+class EffectiveAmountKey(typing.NamedTuple):
+    name: str
+    effective_amount: int
+    tx_num: int
+    position: int
+
+
+class EffectiveAmountValue(typing.NamedTuple):
+    claim_hash: bytes
+
+
 class ActiveAmountPrefixRow(PrefixRow):
     prefix = DB_PREFIXES.active_amount.value
     key_struct = struct.Struct(b'>20sBLLH')
@@ -296,6 +307,11 @@ def shortid_key_helper(struct_fmt):
     return wrapper
 
 
+def shortid_key_partial_claim_helper(name: str, partial_claim_hash: bytes):
+    assert len(partial_claim_hash) <= 20
+    return length_encoded_name(name) + partial_claim_hash
+
+
 class ClaimShortIDPrefixRow(PrefixRow):
     prefix = DB_PREFIXES.claim_short_id_prefix.value
     key_struct = struct.Struct(b'>20sLH')
@@ -303,7 +319,7 @@ class ClaimShortIDPrefixRow(PrefixRow):
     key_part_lambdas = [
         lambda: b'',
         length_encoded_name,
-        shortid_key_helper(b'>20s'),
+        shortid_key_partial_claim_helper,
         shortid_key_helper(b'>20sL'),
         shortid_key_helper(b'>20sLH'),
     ]
@@ -608,6 +624,58 @@ class ActivatedPrefixRow(PrefixRow):
                cls.pack_value(height, claim_hash, name)
 
 
+def effective_amount_helper(struct_fmt):
+    packer = struct.Struct(struct_fmt).pack
+
+    def wrapper(name, *args):
+        if not args:
+            return length_encoded_name(name)
+        if len(args) == 1:
+            return length_encoded_name(name) + packer(0xffffffffffffffff - args[0])
+        return length_encoded_name(name) + packer(0xffffffffffffffff - args[0], *args[1:])
+
+    return wrapper
+
+
+class EffectiveAmountPrefixRow(PrefixRow):
+    prefix = DB_PREFIXES.claim_effective_amount_prefix.value
+    key_struct = struct.Struct(b'>QLH')
+    value_struct = struct.Struct(b'>20s')
+    key_part_lambdas = [
+        lambda: b'',
+        length_encoded_name,
+        shortid_key_helper(b'>Q'),
+        shortid_key_helper(b'>QL'),
+        shortid_key_helper(b'>QLH'),
+    ]
+
+    @classmethod
+    def pack_key(cls, name: str, effective_amount: int, tx_num: int, position: int):
+        return cls.prefix + length_encoded_name(name) + cls.key_struct.pack(
+                    0xffffffffffffffff - effective_amount, tx_num, position
+        )
+
+    @classmethod
+    def unpack_key(cls, key: bytes) -> EffectiveAmountKey:
+        assert key[:1] == cls.prefix
+        name_len = int.from_bytes(key[1:3], byteorder='big')
+        name = key[3:3 + name_len].decode()
+        ones_comp_effective_amount, tx_num, position = cls.key_struct.unpack(key[3 + name_len:])
+        return EffectiveAmountKey(name, 0xffffffffffffffff - ones_comp_effective_amount, tx_num, position)
+
+    @classmethod
+    def unpack_value(cls, data: bytes) -> EffectiveAmountValue:
+        return EffectiveAmountValue(*super().unpack_value(data))
+
+    @classmethod
+    def pack_value(cls, claim_hash: bytes) -> bytes:
+        return super().pack_value(claim_hash)
+
+    @classmethod
+    def pack_item(cls, name: str, effective_amount: int, tx_num: int, position: int, claim_hash: bytes):
+        return cls.pack_key(name, effective_amount, tx_num, position), cls.pack_value(claim_hash)
+
+
 class Prefixes:
     claim_to_support = ClaimToSupportPrefixRow
     support_to_claim = SupportToClaimPrefixRow
@@ -626,4 +694,6 @@ class Prefixes:
     activated = ActivatedPrefixRow
     active_amount = ActiveAmountPrefixRow
 
+    effective_amount = EffectiveAmountPrefixRow
+
     # undo_claimtrie = b'M'
diff --git a/lbry/wallet/server/leveldb.py b/lbry/wallet/server/leveldb.py
index 469e6af75..54987335f 100644
--- a/lbry/wallet/server/leveldb.py
+++ b/lbry/wallet/server/leveldb.py
@@ -264,13 +264,12 @@ class LevelDB:
             print("resolved controlling", controlling.claim_hash.hex())
             return self._fs_get_claim_by_hash(controlling.claim_hash)
 
-        encoded_name = length_encoded_name(normalized_name)
         amount_order = max(int(amount_order or 1), 1)
 
         if claim_id:
             # resolve by partial/complete claim id
             short_claim_hash = bytes.fromhex(claim_id)
-            prefix = DB_PREFIXES.claim_short_id_prefix.value + encoded_name + short_claim_hash
+            prefix = Prefixes.claim_short_id.pack_partial_key(normalized_name, short_claim_hash)
             for k, v in self.db.iterator(prefix=prefix):
                 key = Prefixes.claim_short_id.unpack_key(k)
                 claim_txo = Prefixes.claim_short_id.unpack_value(v)
@@ -281,15 +280,17 @@ class LevelDB:
             return
 
         # resolve by amount ordering, 1 indexed
-        for idx, (k, v) in enumerate(self.db.iterator(
-                prefix=DB_PREFIXES.claim_effective_amount_prefix.value + encoded_name)):
+        prefix = Prefixes.effective_amount.pack_partial_key(normalized_name)
+        for idx, (k, v) in enumerate(self.db.iterator(prefix=prefix)):
             if amount_order > idx + 1:
                 continue
-            key = Prefixes.claim_effective_amount.unpack_key(k)
-            claim_val = Prefixes.claim_effective_amount.unpack_value(v)
+            key = Prefixes.effective_amount.unpack_key(k)
+            claim_val = Prefixes.effective_amount.unpack_value(v)
+            claim_txo = self.get_claim_txo(claim_val.claim_hash)
+            activation = self.get_activation(key.tx_num, key.position)
             return self._prepare_resolve_result(
-                key.tx_num, key.position, claim_val.claim_hash, key.name, claim_val.root_tx_num,
-                claim_val.root_position, claim_val.activation
+                key.tx_num, key.position, claim_val.claim_hash, key.name, claim_txo[1].root_tx_num,
+                claim_txo[1].root_position, activation
             )
         return