Skip to content

Commit 8f206d3

Browse files
committed
cache polish
1 parent df76528 commit 8f206d3

File tree

2 files changed

+53
-145
lines changed

2 files changed

+53
-145
lines changed

packages/eth-providers/src/base-provider.ts

Lines changed: 46 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,22 @@ import {
1414
} from '@ethersproject/abstract-provider';
1515
import { AcalaEvmTX, checkSignatureType, parseTransaction } from '@acala-network/eth-transactions';
1616
import { AccessList, accessListify } from 'ethers/lib/utils';
17-
import { AccountId, H160, Header } from '@polkadot/types/interfaces';
1817
import { ApiPromise } from '@polkadot/api';
1918
import { AsyncAction } from 'rxjs/internal/scheduler/AsyncAction';
2019
import { AsyncScheduler } from 'rxjs/internal/scheduler/AsyncScheduler';
2120
import { BigNumber, BigNumberish, Wallet } from 'ethers';
2221
import { Deferrable, defineReadOnly, resolveProperties } from '@ethersproject/properties';
23-
import { EvmAccountInfo, EvmContractInfo } from '@acala-network/types/interfaces';
2422
import { Formatter } from '@ethersproject/providers';
25-
import { FrameSystemAccountInfo } from '@polkadot/types/lookup';
23+
import { Header } from '@polkadot/types/interfaces';
2624
import { ISubmittableResult } from '@polkadot/types/types';
2725
import { Logger } from '@ethersproject/logger';
26+
import { ModuleEvmModuleAccountInfo } from '@polkadot/types/lookup';
2827
import { Network } from '@ethersproject/networks';
2928
import { Observable, ReplaySubject, Subscription, firstValueFrom, throwError } from 'rxjs';
30-
import { Option, decorateStorage, unwrapStorageType } from '@polkadot/types';
31-
import { Storage } from '@polkadot/types/metadata/decorate/types';
3229
import { SubmittableExtrinsic } from '@polkadot/api/types';
33-
import { VersionedRegistry } from '@polkadot/api/base/types';
34-
import { createHeaderExtended } from '@polkadot/api-derive';
3530
import { filter, first, timeout } from 'rxjs/operators';
3631
import { getAddress } from '@ethersproject/address';
3732
import { hexDataLength, hexValue, hexZeroPad, hexlify, isHexString, joinSignature } from '@ethersproject/bytes';
38-
import { isNull, u8aToHex, u8aToU8a } from '@polkadot/util';
3933
import BN from 'bn.js';
4034
import LRUCache from 'lru-cache';
4135

@@ -44,7 +38,6 @@ import {
4438
BLOCK_GAS_LIMIT,
4539
BLOCK_STORAGE_LIMIT,
4640
CACHE_SIZE_WARNING,
47-
DUMMY_ADDRESS,
4841
DUMMY_BLOCK_NONCE,
4942
DUMMY_LOGS_BLOOM,
5043
EMPTY_HEX_STRING,
@@ -78,6 +71,7 @@ import {
7871
filterLogByTopics,
7972
getAllReceiptsAtBlock,
8073
getHealthResult,
74+
getTimestamp,
8175
getTransactionRequest,
8276
hexlifyRpcResult,
8377
isEvmExtrinsic,
@@ -309,8 +303,7 @@ export abstract class BaseProvider extends AbstractProvider {
309303
readonly localMode: boolean;
310304
readonly verbose: boolean;
311305
readonly maxBlockCacheSize: number;
312-
readonly storages: WeakMap<VersionedRegistry<'promise'>, Storage> = new WeakMap();
313-
readonly storageCache: LRUCache<string, Uint8Array | null>;
306+
readonly queryCache: LRUCache<string, any>;
314307
readonly blockCache: BlockCache;
315308
readonly finalizedBlockHashes: MaxSizeSet;
316309

@@ -347,7 +340,7 @@ export abstract class BaseProvider extends AbstractProvider {
347340
this.localMode = localMode;
348341
this.verbose = verbose;
349342
this.maxBlockCacheSize = maxBlockCacheSize;
350-
this.storageCache = new LRUCache({ max: storageCacheSize });
343+
this.queryCache = new LRUCache({ max: storageCacheSize });
351344
this.blockCache = new BlockCache(this.maxBlockCacheSize);
352345
this.finalizedBlockHashes = new MaxSizeSet(this.maxBlockCacheSize);
353346

@@ -523,67 +516,6 @@ export abstract class BaseProvider extends AbstractProvider {
523516
defineReadOnly(this, '_api', api);
524517
};
525518

526-
queryStorage = async <T = any>(
527-
module: `${string}.${string}`,
528-
args: any[],
529-
_blockTag?: BlockTag | Promise<BlockTag> | Eip1898BlockTag
530-
): Promise<T> => {
531-
const blockTag = await this._ensureSafeModeBlockTagFinalization(await parseBlockTag(_blockTag));
532-
const blockHash = await this._getBlockHash(blockTag);
533-
534-
const registry = await this.api.getBlockRegistry(u8aToU8a(blockHash));
535-
536-
if (!this.storages.get(registry)) {
537-
const storage = decorateStorage(
538-
registry.registry,
539-
registry.metadata.asLatest,
540-
registry.metadata.version,
541-
);
542-
this.storages.set(registry, storage);
543-
}
544-
545-
const storage = this.storages.get(registry)!;
546-
547-
const [section, method] = module.split('.');
548-
549-
const entry = storage[section][method];
550-
const key = entry(...args);
551-
552-
const outputType = unwrapStorageType(
553-
registry.registry,
554-
entry.meta.type,
555-
entry.meta.modifier.isOptional,
556-
);
557-
558-
const cacheKey = `${module}-${blockHash}-${args.join(',')}`;
559-
const cached = this.storageCache.get(cacheKey);
560-
561-
let input: Uint8Array | null = null;
562-
563-
if (cached) {
564-
input = cached;
565-
} else {
566-
const value: any = await this.api.rpc.state.getStorage(key, blockHash);
567-
568-
const isEmpty = isNull(value);
569-
570-
// we convert to Uint8Array since it maps to the raw encoding, all
571-
// data will be correctly encoded (incl. numbers, excl. :code)
572-
input = isEmpty
573-
? null
574-
: u8aToU8a(entry.meta.modifier.isOptional ? value.toU8a() : value.isSome ? value.unwrap().toU8a() : null);
575-
576-
this.storageCache.set(cacheKey, input);
577-
}
578-
579-
const result = registry.registry.createTypeUnsafe(outputType, [input], {
580-
blockHash,
581-
isPedantic: !entry.meta.modifier.isOptional,
582-
});
583-
584-
return result as any as T;
585-
};
586-
587519
get api(): ApiPromise {
588520
return this._api ?? logger.throwError('the api needs to be set', Logger.errors.UNKNOWN_ERROR);
589521
}
@@ -672,19 +604,15 @@ export abstract class BaseProvider extends AbstractProvider {
672604
const blockHash = header.hash.toHex();
673605
const blockNumber = header.number.toNumber();
674606

675-
const [block, validators, now, receiptsFromSubql] = await Promise.all([
607+
const [block, headerExtended, timestamp, receiptsFromSubql] = await Promise.all([
676608
this.api.rpc.chain.getBlock(blockHash),
677-
this.api.query.session ? this.queryStorage('session.validators', [], blockHash) : ([] as any),
678-
this.queryStorage('timestamp.now', [], blockHash),
609+
this.api.derive.chain.getHeader(blockHash),
610+
getTimestamp(this.api, blockHash),
679611
this.subql?.getAllReceiptsAtBlock(blockHash),
680612
]);
681613

682-
const headerExtended = createHeaderExtended(header.registry, header, validators);
683-
684614
// blockscout need `toLowerCase`
685-
const author = headerExtended.author
686-
? (await this.getEvmAddress(headerExtended.author.toString())).toLowerCase()
687-
: DUMMY_ADDRESS;
615+
const author = (await this.getEvmAddress(headerExtended.author.toString())).toLowerCase();
688616

689617
let receipts: TransactionReceipt[];
690618
if (receiptsFromSubql?.length) {
@@ -709,7 +637,7 @@ export abstract class BaseProvider extends AbstractProvider {
709637
number: blockNumber,
710638
stateRoot: headerExtended.stateRoot.toHex(),
711639
transactionsRoot: headerExtended.extrinsicsRoot.toHex(),
712-
timestamp: Math.floor(now.toNumber() / 1000),
640+
timestamp: Math.floor(timestamp / 1000),
713641
nonce: DUMMY_BLOCK_NONCE,
714642
mixHash: ZERO_BLOCK_HASH,
715643
difficulty: ZERO,
@@ -750,11 +678,8 @@ export abstract class BaseProvider extends AbstractProvider {
750678

751679
const substrateAddress = await this.getSubstrateAddress(address, blockHash);
752680

753-
const accountInfo = await this.queryStorage<FrameSystemAccountInfo>(
754-
'system.account',
755-
[substrateAddress],
756-
blockHash
757-
);
681+
const apiAt = await this.api.at(blockHash);
682+
const accountInfo = await apiAt.query.system.account(substrateAddress);
758683

759684
return nativeToEthDecimal(accountInfo.data.free.toBigInt());
760685
};
@@ -779,9 +704,7 @@ export abstract class BaseProvider extends AbstractProvider {
779704
}
780705

781706
const accountInfo = await this.queryAccountInfo(addressOrName, blockTag);
782-
const minedNonce = accountInfo.isNone
783-
? 0
784-
: accountInfo.unwrap().nonce.toNumber();
707+
const minedNonce = accountInfo?.nonce?.toNumber?.() ?? 0;
785708

786709
return minedNonce + pendingNonce;
787710
};
@@ -791,23 +714,14 @@ export abstract class BaseProvider extends AbstractProvider {
791714
_blockTag?: BlockTag | Promise<BlockTag> | Eip1898BlockTag
792715
): Promise<string> => {
793716
const blockTag = await this._ensureSafeModeBlockTagFinalization(await parseBlockTag(_blockTag));
717+
const blockHash = await this._getBlockHash(blockTag);
794718

795-
const [address, blockHash] = await Promise.all([
796-
addressOrName,
797-
this._getBlockHash(blockTag),
798-
]);
799-
800-
const contractInfo = await this.queryContractInfo(address, blockHash);
801-
802-
if (contractInfo.isNone) {
803-
return '0x';
804-
}
805-
806-
const codeHash = contractInfo.unwrap().codeHash;
807-
808-
const api = blockHash ? await this.api.at(blockHash) : this.api;
719+
const accountInfo = await this.queryAccountInfo(addressOrName, blockHash);
720+
const contractInfo = accountInfo?.contractInfo.unwrapOr(null);
721+
if (!contractInfo) { return '0x'; }
809722

810-
const code = await api.query.evm.codes(codeHash);
723+
const apiAt = await this.api.at(blockHash);
724+
const code = await apiAt.query.evm.codes(contractInfo.codeHash);
811725

812726
return code.toHex();
813727
};
@@ -904,7 +818,8 @@ export abstract class BaseProvider extends AbstractProvider {
904818
Promise.resolve(position).then(hexValue),
905819
]);
906820

907-
const code = await this.queryStorage('evm.accountStorages', [address, hexZeroPad(resolvedPosition, 32)], blockHash);
821+
const apiAt = await this.api.at(blockHash);
822+
const code = await apiAt.query.evm.accountStorages(address, hexZeroPad(resolvedPosition, 32));
908823

909824
return code.toHex();
910825
};
@@ -1185,51 +1100,39 @@ export abstract class BaseProvider extends AbstractProvider {
11851100
};
11861101
};
11871102

1188-
getSubstrateAddress = async (addressOrName: string, blockTag?: BlockTag): Promise<string> => {
1189-
const [address, blockHash] = await Promise.all([
1190-
addressOrName,
1191-
this._getBlockHash(blockTag),
1192-
]);
1193-
1194-
const substrateAccount = await this.queryStorage<Option<AccountId>>('evmAccounts.accounts', [address], blockHash);
1103+
getSubstrateAddress = async (address: string, blockTag?: BlockTag): Promise<string> => {
1104+
const blockHash = await this._getBlockHash(blockTag);
1105+
const apiAt = await this.api.at(blockHash);
1106+
const substrateAccount = await apiAt.query.evmAccounts.accounts(address);
11951107

1196-
return substrateAccount.isEmpty ? computeDefaultSubstrateAddress(address) : substrateAccount.toString();
1108+
return substrateAccount.isEmpty
1109+
? computeDefaultSubstrateAddress(address)
1110+
: substrateAccount.toString();
11971111
};
11981112

11991113
getEvmAddress = async (substrateAddress: string, blockTag?: BlockTag): Promise<string> => {
12001114
const blockHash = await this._getBlockHash(blockTag);
1201-
const evmAddress = await this.queryStorage<Option<H160>>('evmAccounts.evmAddresses', [substrateAddress], blockHash);
1115+
const apiAt = await this.api.at(blockHash);
1116+
const evmAddress = await apiAt.query.evmAccounts.evmAddresses(substrateAddress);
12021117

12031118
return getAddress(evmAddress.isEmpty ? computeDefaultEvmAddress(substrateAddress) : evmAddress.toString());
12041119
};
12051120

12061121
queryAccountInfo = async (
12071122
addressOrName: string | Promise<string>,
12081123
_blockTag?: BlockTag | Promise<BlockTag> | Eip1898BlockTag
1209-
): Promise<Option<EvmAccountInfo>> => {
1124+
): Promise<ModuleEvmModuleAccountInfo | null> => {
12101125
const blockTag = await this._ensureSafeModeBlockTagFinalization(await parseBlockTag(_blockTag));
12111126

12121127
const [address, blockHash] = await Promise.all([
12131128
addressOrName,
12141129
this._getBlockHash(blockTag),
12151130
]);
12161131

1217-
const accountInfo = await this.queryStorage<Option<EvmAccountInfo>>('evm.accounts', [address], blockHash);
1132+
const apiAt = await this.api.at(blockHash);
1133+
const accountInfo = await apiAt.query.evm.accounts(address);
12181134

1219-
return accountInfo;
1220-
};
1221-
1222-
queryContractInfo = async (
1223-
addressOrName: string | Promise<string>,
1224-
blockTag?: BlockTag | Promise<BlockTag>
1225-
): Promise<Option<EvmContractInfo>> => {
1226-
const accountInfo = await this.queryAccountInfo(addressOrName, blockTag);
1227-
1228-
if (accountInfo.isNone) {
1229-
return this.api.createType<Option<EvmContractInfo>>('Option<EvmContractInfo>', null);
1230-
}
1231-
1232-
return accountInfo.unwrap().contractInfo;
1135+
return accountInfo.unwrapOr(null);
12331136
};
12341137

12351138
_getSubstrateGasParams = (ethTx: Partial<AcalaEvmTX>): {
@@ -1287,7 +1190,7 @@ export abstract class BaseProvider extends AbstractProvider {
12871190
storageLimit: storageLimit.toBigInt(),
12881191
tip: 0n,
12891192
};
1290-
} catch (error) {
1193+
} catch {
12911194
// v2
12921195
v2 = true;
12931196

@@ -1471,7 +1374,8 @@ export abstract class BaseProvider extends AbstractProvider {
14711374
result.blockNumber = startBlock;
14721375
result.blockHash = startBlockHash;
14731376

1474-
result.timestamp = Math.floor((await this.queryStorage('timestamp.now', [], result.blockHash)).toNumber() / 1000);
1377+
const timestamp = await getTimestamp(this.api, result.blockHash);
1378+
result.timestamp = Math.floor(timestamp / 1000);
14751379

14761380
result.wait = async (confirms?: number, timeoutMs?: number) => {
14771381
if (confirms === null || confirms === undefined) {
@@ -1561,26 +1465,23 @@ export abstract class BaseProvider extends AbstractProvider {
15611465
}
15621466

15631467
const isFinalized = blockNumber.lte(await this.finalizedBlockNumber);
1564-
const cacheKey = `blockHash-${blockNumber.toString()}`;
1468+
const cacheKey = `blockHash-${blockNumber.toHexString()}`;
15651469

15661470
if (isFinalized) {
1567-
const cached = this.storageCache.get(cacheKey);
1471+
const cached = this.queryCache.get(cacheKey);
15681472
if (cached) {
1569-
return u8aToHex(cached);
1473+
return cached;
15701474
}
15711475
}
15721476

1573-
const _blockHash = await this.api.rpc.chain.getBlockHash(blockNumber.toBigInt());
1574-
1575-
if (_blockHash.isEmpty) {
1576-
//@ts-ignore
1577-
return logger.throwError('header not found', PROVIDER_ERRORS.HEADER_NOT_FOUND, { blockNumber });
1578-
}
1579-
1580-
const blockHash = _blockHash.toHex();
1477+
// TODO: test header not found should throw
1478+
const blockHash = (await this.api.rpc.chain.getBlockHash(blockNumber.toBigInt())).toHex();
1479+
// if (_blockHash.isEmpty) {
1480+
// return logger.throwError('header not found', Logger.errors.CALL_EXCEPTION, { blockNumber });
1481+
// }
15811482

15821483
if (isFinalized) {
1583-
this.storageCache.set(cacheKey, _blockHash.toU8a());
1484+
this.queryCache.set(cacheKey, blockHash);
15841485
}
15851486

15861487
return blockHash;

packages/eth-providers/src/utils/utils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { AnyFunction } from '@polkadot/types/types';
2+
import { ApiPromise } from '@polkadot/api';
23
import { BigNumber, BigNumberish } from '@ethersproject/bignumber';
34
import { Extrinsic } from '@polkadot/types/interfaces';
45
import { FrameSystemEventRecord } from '@polkadot/types/lookup';
@@ -411,3 +412,9 @@ export const toBN = (bigNumberis: BigNumberish = 0): BN => {
411412
// eslint-disable-next-line @typescript-eslint/no-explicit-any
412413
return new BN(bigNumberis as any);
413414
};
415+
416+
export const getTimestamp = async (api: ApiPromise, blockHash: string): Promise<number> => {
417+
const apiAt = await api.at(blockHash);
418+
const timestamp = await apiAt.query.timestamp.now();
419+
return timestamp.toNumber();
420+
};

0 commit comments

Comments
 (0)