Replace public keys with those compressed
I don’t ask for crypto. M◉⃤|<𓊖༒𓊖 Takemiya 武宮誠 (Sora.org & SORAMITSU), [2025/01/08 19:58] How many bytes used per account stored in memory in wsv?
S. Sato, [2025/01/09 0:20] Approximately 2.2GiB resident memory difference is observed for 2^20 (1Mi) account-asset pairs, which means 2200~2300B for a pair. Code (https://github.com/s8sato/iroha/commit/17bfacd8d2e0436b050251546b387cde076a39e7)
Assumptions:
- No difference except in the world state: compared in the initial state of 0 blocks
- Every account accompanies one asset
I might investigate further to be more precise
S. Sato, [2025/01/09 0:47] We might have to store multihash instead of PublicKey as well as to dedup ids
I don’t ask for crypto. M◉⃤|<𓊖༒𓊖 Takemiya 武宮誠 (Sora.org & SORAMITSU), [2025/01/09 1:12] Do we store compressed public keys?
S. Sato, [2025/01/09 1:28] I guess no, it consumes 8+296 bytes while e.g. ed25519 multihash is essentially 32 bytes
I don’t ask for crypto. M◉⃤|<𓊖༒𓊖 Takemiya 武宮誠 (Sora.org & SORAMITSU), [2025/01/09 14:18] We can compress the public key I guess as we just need a bit showing if y is even or odd and not the whole y coord
In addition to PublicKey size and duplicate IDs between keys and values, it would be a major factor in memory usage that mv::storage::Storage.revert holds a snapshot to handle soft forks
I don’t ask for crypto. M◉⃤|<𓊖༒𓊖 Takemiya 武宮誠 (Sora.org & SORAMITSU), [2025/03/13 23:51] Regarding addresses and keys, we should use compressed public keys and generate addresses with checksums and network bytes, so there are a few tasks associated with account structure.
I don’t ask for crypto. M◉⃤|<𓊖༒𓊖 Takemiya 武宮誠 (Sora.org & SORAMITSU), [2025/03/13 23:57] By compressed, I mean that on the eliptic curve, you don't need to store the entire y value, just the x + a bit that shows if it is positive y or negative y (though it could also depend on the curve used).
I don’t ask for crypto. M◉⃤|<𓊖༒𓊖 Takemiya 武宮誠 (Sora.org & SORAMITSU), [2025/03/13 23:58] Like in secp256k1 used by bitcoin:
I don’t ask for crypto. M◉⃤|<𓊖༒𓊖 Takemiya 武宮誠 (Sora.org & SORAMITSU), [2025/03/13 23:58]
I don’t ask for crypto. M◉⃤|<𓊖༒𓊖 Takemiya 武宮誠 (Sora.org & SORAMITSU), [2025/03/13 23:58] the curve is mirrored on the y axis, so if you know the x value and you know if y is positive or negative, then you can calculate y, so bitcoin doesn't store the full public keys anymore, just the "compressed" keys
I don’t ask for crypto. M◉⃤|<𓊖༒𓊖 Takemiya 武宮誠 (Sora.org & SORAMITSU), [2025/03/13 23:59] It saves a lot of space actually, but at the cost of some computation of course (but it is pretty simple math, at least for secp256k1. Not sure about ed25519 or other arbirtrary curves we may support)
So currently Account struct consumes ~344 bytes, and when stored in World it takes ~1006 bytes per account. (In World it takes more because of key/value duplication, and because of concread/BTreeMap internal structure)
Potential memory optimizations:
- Optimize
PublicKeystruct size:- Store
PublicKeyin compact form (#5445) - Inline
algorithminPublicKeyCompact(#5452) - ? Make size of
PublicKey40 bytes, first two variants (Ed25519 and Secp256k1) should be stored inline (without heap), other two variants (BlsNormal and BlsSmall) will be stored on heap. - ? Use Arc for
PublicKeyCompact::algorithm_and_payload - ? Use custom
ConstVecimplementation forPublicKeyCompact::algorithm_and_payload(there is no need to store capacity, we can get it from algorithm) - ? Introduce map
World::public_keysof type something likeMap<PublicKey, u64>and use those u64 id in all other maps likeWorld::accounts
- Store
- Remove key/values duplication in
World::accountsand similar maps (#5463)
Analysis of Account and PublicKey structs:
// total 344
struct Account {
// total 320
id: AccountId,
// self 24
metadata: Metadata,
}
// self 24 + heap 296 = total 320
struct AccountId {
// self 16
domain: DomainId,
// self 8 + heap 296
signatory: PublicKey,
}
// self 8 + heap 296
struct PublicKey(Box<PublicKeyInner>);
// 296
enum PublicKeyInner {
// 192 (encoded - 32)
Ed25519(ed25519::PublicKey),
// 104 (encoded - 33)
Secp256k1(secp256k1::PublicKey),
// 144 (encoded - 48)
BlsNormal(bls::BlsNormalPublicKey),
// 288 (encoded - 96)
BlsSmall(bls::BlsSmallPublicKey),
}
Copying Sato comment from #5445 for reference:
Great—please also revisit these prior discussions:
- Combining keys and values only at the interface: Redundant identifiers in storage values #5034
- Separating primary keys and foreign keys, treating them as reference counters: Data integrity in
World#4672
Overview of memory usage after optimizations:
- Structs in
World. This is similar to usage of real Iroha.Account— 336 bytes per accountAsset— 422 bytes per assetNft— 406 bytes per nft
- Struct as is. This is just for comparison. In
Worldstructs consume more because they are stored in BTreeMap, where keys are stored multiple times in intermediate nodes.Account— 108 bytesAsset— 132 bytesNft— 174 bytes