Skip to content

Commit d2bc3aa

Browse files
committed
Add type AccountIdOrderable to make AccountId comparable
`AccountId` could already be compared (using `Ord`/`PartialOrd`) but we had to re-compute the `BigInteger256` values every time `PartialOrd::partial_cmp` was called. This commit only make the comparaison (`Ord`/`PartialOrd`) faster. Remove `Ord` & `PartialOrd` implementation for `AccountId`.
1 parent 8c8098e commit d2bc3aa

File tree

2 files changed

+111
-21
lines changed

2 files changed

+111
-21
lines changed

ledger/src/account/account.rs

Lines changed: 101 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -750,39 +750,124 @@ impl ZkAppAccount {
750750
}
751751
}
752752

753+
/// An `AccountId` implementing `Ord` & `PartialOrd`, reproducing OCaml ordering.
754+
///
755+
/// We compare them using `BigInteger256`, not `Fp`.
756+
/// This is a different type than `AccountId` because we want to keep the
757+
/// `BigInteger256` values around, without re-computing them every time
758+
/// `<Self as PartialOrd>::partial_cmp` is called.
759+
///
760+
/// So far this is used only when `AccountId` are used as keys in BTreeMap, in zkapp application.
753761
#[derive(Clone, Eq)]
754-
pub struct AccountId {
755-
pub public_key: CompressedPubKey,
756-
pub token_id: TokenId,
762+
pub struct AccountIdOrderable {
763+
// Keep the values as `BigInteger256`. This avoid re-computing them
764+
// every time `<Self as PartialOrd>::partial_cmp` is called
765+
bigint_public_key_x: BigInteger256,
766+
bigint_public_key_is_odd: bool,
767+
bigint_token_id: BigInteger256,
768+
// We keep the original values, to convert back into `AccountId` without computing
769+
public_key: CompressedPubKey,
770+
token_id: TokenId,
757771
}
758772

759-
impl Ord for AccountId {
773+
impl PartialEq for AccountIdOrderable {
774+
fn eq(&self, other: &Self) -> bool {
775+
let Self {
776+
bigint_public_key_x: self_x,
777+
bigint_public_key_is_odd: self_is_odd,
778+
bigint_token_id: self_token_id,
779+
public_key: _,
780+
token_id: _,
781+
} = self;
782+
let Self {
783+
bigint_public_key_x: other_x,
784+
bigint_public_key_is_odd: other_is_odd,
785+
bigint_token_id: other_token_id,
786+
public_key: _,
787+
token_id: _,
788+
} = other;
789+
790+
self_x == other_x && self_is_odd == other_is_odd && self_token_id == other_token_id
791+
}
792+
}
793+
impl Ord for AccountIdOrderable {
760794
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
761795
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Equal)
762796
}
763797
}
764-
765-
impl PartialOrd for AccountId {
798+
impl PartialOrd for AccountIdOrderable {
799+
/// Ignore `Self::public_key` and `Self::token_id`
800+
/// We only use their bigint representations
766801
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
767-
let self_pk: BigInteger256 = self.public_key.x.into();
768-
let other_pk: BigInteger256 = other.public_key.x.into();
769-
match self_pk.partial_cmp(&other_pk) {
770-
Some(core::cmp::Ordering::Equal) | None => {}
802+
let Self {
803+
bigint_public_key_x: self_x,
804+
bigint_public_key_is_odd: self_is_odd,
805+
bigint_token_id: self_token_id,
806+
public_key: _,
807+
token_id: _,
808+
} = self;
809+
810+
let Self {
811+
bigint_public_key_x: other_x,
812+
bigint_public_key_is_odd: other_is_odd,
813+
bigint_token_id: other_token_id,
814+
public_key: _,
815+
token_id: _,
816+
} = other;
817+
818+
match self_x.partial_cmp(other_x) {
819+
None | Some(core::cmp::Ordering::Equal) => {}
771820
ord => return ord,
772821
}
773-
774-
match self.public_key.is_odd.partial_cmp(&other.public_key.is_odd) {
775-
Some(core::cmp::Ordering::Equal) | None => {}
822+
match self_is_odd.partial_cmp(other_is_odd) {
823+
None | Some(core::cmp::Ordering::Equal) => {}
776824
ord => return ord,
777825
}
826+
self_token_id.partial_cmp(other_token_id)
827+
}
828+
}
829+
830+
impl From<AccountId> for AccountIdOrderable {
831+
fn from(value: AccountId) -> Self {
832+
let AccountId {
833+
public_key,
834+
token_id,
835+
} = value;
836+
let CompressedPubKey { x, is_odd } = &public_key;
837+
838+
Self {
839+
bigint_public_key_x: (*x).into(),
840+
bigint_public_key_is_odd: *is_odd,
841+
bigint_token_id: token_id.0.into(),
842+
public_key,
843+
token_id,
844+
}
845+
}
846+
}
778847

779-
let self_token_id: BigInteger256 = self.token_id.0.into();
780-
let other_token_id: BigInteger256 = other.token_id.0.into();
848+
impl From<AccountIdOrderable> for AccountId {
849+
fn from(value: AccountIdOrderable) -> Self {
850+
let AccountIdOrderable {
851+
bigint_public_key_x: _,
852+
bigint_public_key_is_odd: _,
853+
bigint_token_id: _,
854+
public_key,
855+
token_id,
856+
} = value;
781857

782-
self_token_id.partial_cmp(&other_token_id)
858+
Self {
859+
public_key,
860+
token_id,
861+
}
783862
}
784863
}
785864

865+
#[derive(Clone, Eq)]
866+
pub struct AccountId {
867+
pub public_key: CompressedPubKey,
868+
pub token_id: TokenId,
869+
}
870+
786871
impl ToInputs for AccountId {
787872
fn to_inputs(&self, inputs: &mut Inputs) {
788873
let Self {

ledger/src/scan_state/transaction_logic.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::scan_state::transaction_logic::transaction_partially_applied::FullyAp
1313
use crate::scan_state::transaction_logic::zkapp_command::MaybeWithStatus;
1414
use crate::scan_state::zkapp_logic;
1515
use crate::transaction_pool::VerificationKeyWire;
16-
use crate::{hash_with_kimchi, BaseLedger, ControlTag, Inputs};
16+
use crate::{hash_with_kimchi, AccountIdOrderable, BaseLedger, ControlTag, Inputs};
1717
use crate::{
1818
scan_state::transaction_logic::transaction_applied::{CommandApplied, Varying},
1919
sparse_ledger::{LedgerIntf, SparseLedger},
@@ -5933,14 +5933,14 @@ where
59335933
{
59345934
let perform = |eff: Eff<L>| Env::perform(eff);
59355935

5936-
let original_account_states: Vec<_> = {
5936+
let original_account_states: Vec<(AccountId, Option<_>)> = {
59375937
// get the original states of all the accounts in each pass.
59385938
// If an account updated in the first pass is referenced in account
59395939
// updates, then retain the value before first pass application*)
59405940

59415941
let accounts_referenced = c.command.accounts_referenced();
59425942

5943-
let mut account_states = BTreeMap::<AccountId, Option<_>>::new();
5943+
let mut account_states = BTreeMap::<AccountIdOrderable, Option<_>>::new();
59445944

59455945
let referenced = accounts_referenced.into_iter().map(|id| {
59465946
let location = {
@@ -5957,12 +5957,17 @@ where
59575957
.for_each(|(id, acc_opt)| {
59585958
use std::collections::btree_map::Entry::Vacant;
59595959

5960-
if let Vacant(entry) = account_states.entry(id) {
5960+
let id_with_order: AccountIdOrderable = id.into();
5961+
if let Vacant(entry) = account_states.entry(id_with_order) {
59615962
entry.insert(acc_opt);
59625963
};
59635964
});
59645965

5965-
account_states.into_iter().collect()
5966+
account_states
5967+
.into_iter()
5968+
// Convert back the `AccountIdOrder` into `AccountId`, now that they are sorted
5969+
.map(|(id, account): (AccountIdOrderable, Option<_>)| (id.into(), account))
5970+
.collect()
59665971
};
59675972

59685973
let mut account_states_after_fee_payer = {

0 commit comments

Comments
 (0)