Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Remove some zero address limit #1008

Merged
merged 4 commits into from
Mar 6, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/web3-node-js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ jobs:

- run: yarn install
- run: yarn run build
- run: yarn workspace @godwoken-web3/api-server run test tests/db/helpers.test.ts
- run: yarn workspace @godwoken-web3/api-server run test tests/utils
- run: yarn workspace @godwoken-web3/api-server run test tests/base/types
- run: yarn run fmt
- run: yarn run lint
- run: git diff --exit-code
27 changes: 13 additions & 14 deletions web3/packages/api-server/src/base/address.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
import { Hash, HexString, Script, utils } from "@ckb-lumos/base";
import { GodwokenClient } from "@godwoken-web3/godwoken";
import { Store } from "../cache/store";
import { COMPATIBLE_DOCS_URL } from "../methods/constant";
import { gwConfig } from "./index";
import { logger } from "./logger";
import { Uint32 } from "./types/uint";

const ZERO_ETH_ADDRESS = "0x" + "00".repeat(20);

// the eth address vs script hash is not changeable, so we set no expire for cache
const scriptHashCache = new Store(false);

// Only support eth address now!
export class EthRegistryAddress {
private registryId: number = +gwConfig.accounts.ethAddrReg.id;
private registryId: number;
private addressByteSize: number = 20;
public readonly address: HexString;

constructor(address: HexString) {
if (!address.startsWith("0x") || address.length != 42) {
// Using optional registryId when gwConfig not initialized
constructor(
address: HexString,
{ registryId }: { registryId?: number } = {}
) {
if (!address.startsWith("0x") || address.length !== 42) {
throw new Error(`Eth address format error: ${address}`);
}
this.address = address.toLowerCase();

this.registryId = registryId || +gwConfig.accounts.ethAddrReg.id;
}

public serialize(): HexString {
Expand All @@ -35,15 +38,17 @@ export class EthRegistryAddress {

public static Deserialize(hex: HexString): EthRegistryAddress {
const hexWithoutPrefix = hex.slice(2);
// const registryId: number = Uint32.fromLittleEndian(hexWithoutPrefix.slice(0, 8)).getValue();
const registryId: number = Uint32.fromLittleEndian(
hexWithoutPrefix.slice(0, 8)
).getValue();
const addressByteSize: number = Uint32.fromLittleEndian(
hexWithoutPrefix.slice(8, 16)
).getValue();
const address: HexString = hexWithoutPrefix.slice(16);
if (addressByteSize !== 20 || address.length !== 40) {
throw new Error(`Eth address deserialize error: ${hex}`);
}
return new EthRegistryAddress("0x" + address);
return new EthRegistryAddress("0x" + address, { registryId });
}
}

Expand Down Expand Up @@ -88,12 +93,6 @@ export async function ethAddressToAccountId(
return +gwConfig.accounts.polyjuiceCreator.id;
}

if (ethAddress === ZERO_ETH_ADDRESS) {
throw new Error(
`zero address ${ZERO_ETH_ADDRESS} has no valid account_id! more info: ${COMPATIBLE_DOCS_URL}`
);
}

const scriptHash: Hash | undefined = await ethAddressToScriptHash(
ethAddress,
godwokenClient
Expand Down
69 changes: 62 additions & 7 deletions web3/packages/api-server/src/base/gw-config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { utils, HexNumber, Script, HexString } from "@ckb-lumos/base";
import { utils, HexNumber, Script, HexString, Hash } from "@ckb-lumos/base";
import {
BackendInfo,
EoaScript,
Expand All @@ -15,10 +15,12 @@ import {
GodwokenClient,
NodeInfo as GwNodeInfo,
} from "@godwoken-web3/godwoken";
import { CKB_SUDT_ID } from "../methods/constant";
import { CKB_SUDT_ID, ZERO_ETH_ADDRESS } from "../methods/constant";
import { Uint32 } from "./types/uint";
import { snakeToCamel } from "../util";
import { EntryPointContract } from "../gasless/entrypoint";
import { EthRegistryAddress } from "./address";
import { logger } from "./logger";

// source: https://github.com/nervosnetwork/godwoken/commit/d6c98d8f8a199b6ec29bc77c5065c1108220bb0a#diff-c56fda2ca3b1366049c88e633389d9b6faa8366151369fd7314c81f6e389e5c7R5
const BUILTIN_ETH_ADDR_REG_ACCOUNT_ID = 2;
Expand Down Expand Up @@ -51,7 +53,10 @@ export class GwConfig {

const ethAddrReg = await this.fetchEthAddrRegAccount();
const creator = await this.fetchCreatorAccount();
const defaultFrom = await this.fetchDefaultFromAccount();
const defaultFrom = await this.fetchDefaultFromAccount(+ethAddrReg.id);
logger.info(
`Using default from: ${defaultFrom.address}, id: ${defaultFrom.id}`
);

this.iAccounts = {
polyjuiceCreator: creator,
Expand Down Expand Up @@ -227,8 +232,16 @@ export class GwConfig {
return new Account(regIdHex, scriptHash);
}

// Using zero address firstly
// we search the first account id = 3, if it is eoa account, use it, otherwise continue with id + 1;
private async fetchDefaultFromAccount() {
private async fetchDefaultFromAccount(
registryId: number
): Promise<AccountWithAddress> {
const zeroAddressInfo = await zeroAddressAccount(this.rpc, registryId);
if (zeroAddressInfo != null) {
return zeroAddressInfo;
}

const ethAccountLockTypeHash = this.nodeInfo.eoaScripts.find(
(s) => s.eoaType === EoaScriptType.Eth
)?.typeHash;
Expand All @@ -245,7 +258,8 @@ export class GwConfig {

const firstEoaAccount = await findFirstEoaAccountId(
this.rpc,
ethAccountLockTypeHash
ethAccountLockTypeHash,
registryId
);

if (firstEoaAccount == null) {
Expand All @@ -266,10 +280,19 @@ export class Account {
}
}

export class AccountWithAddress extends Account {
address: HexString;

constructor(id: HexNumber, scriptHash: HexString, address: HexString) {
super(id, scriptHash);
this.address = address;
}
}

export interface ConfigAccounts {
polyjuiceCreator: Account;
ethAddrReg: Account;
defaultFrom: Account;
defaultFrom: AccountWithAddress;
}

export interface ConfigBackends {
Expand Down Expand Up @@ -414,9 +437,36 @@ function toApiNodeInfo(nodeInfo: GwNodeInfo): NodeInfo {
return snakeToCamel(nodeInfo, ["code_hash", "hash_type"]);
}

async function zeroAddressAccount(
rpc: GodwokenClient,
registryId: number
): Promise<AccountWithAddress | undefined> {
const registryAddress = new EthRegistryAddress(ZERO_ETH_ADDRESS, {
registryId,
});
const scriptHash: Hash | undefined = await rpc.getScriptHashByRegistryAddress(
registryAddress.serialize()
);
if (scriptHash == null) {
return undefined;
}

const id = await rpc.getAccountIdByScriptHash(scriptHash);
if (id == null) {
return undefined;
}

return {
id: "0x" + id.toString(16),
scriptHash,
address: ZERO_ETH_ADDRESS,
};
}

async function findFirstEoaAccountId(
rpc: GodwokenClient,
ethAccountLockTypeHash: HexString,
registryId: number,
startAccountId: number = 3,
maxTry: number = 20
) {
Expand All @@ -431,7 +481,12 @@ async function findFirstEoaAccountId(
}
if (script.code_hash === ethAccountLockTypeHash) {
const accountIdHex = "0x" + BigInt(id).toString(16);
return new Account(accountIdHex, scriptHash);
const registryAddress = await rpc.getRegistryAddressByScriptHash(
scriptHash,
registryId
);
const address = registryAddress!.address;
return new AccountWithAddress(accountIdHex, scriptHash, address);
}

await asyncSleep(500);
Expand Down
3 changes: 1 addition & 2 deletions web3/packages/api-server/src/methods/modules/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
SUDT_OPERATION_LOG_FLAG,
SUDT_PAY_FEE_LOG_FLAG,
AUTO_CREATE_ACCOUNT_FROM_ID,
ZERO_ETH_ADDRESS,
} from "../constant";
import { Query, universalizeAddress } from "../../db";
import { envConfig } from "../../base/env-config";
Expand Down Expand Up @@ -87,8 +88,6 @@ const Config = require("../../../config/eth.json");
type U32 = number;
type U64 = bigint;

const ZERO_ETH_ADDRESS = "0x" + "00".repeat(20);

type GodwokenBlockParameter = U64 | undefined;

export class Eth {
Expand Down
16 changes: 2 additions & 14 deletions web3/packages/api-server/src/methods/normalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ export class EthNormalizer {
const value = txCallObj.value || "0x0";
const data = txCallObj.data || "0x";
const toAddress = txCallObj.to;
const fromAddress =
txCallObj.from || (await getDefaultFromAddress(this.rpc));
const fromAddress = txCallObj.from || gwConfig.accounts.defaultFrom.address;

// we should set default price to 0 instead of minGasPrice,
// otherwise read operation might fail the balance check.
Expand Down Expand Up @@ -106,7 +105,7 @@ export class EthNormalizer {
const data = txEstimateGasObj.data || "0x";
const toAddress = txEstimateGasObj.to || "0x";
const fromAddress =
txEstimateGasObj.from || (await getDefaultFromAddress(this.rpc));
txEstimateGasObj.from || gwConfig.accounts.defaultFrom.address;
const gasPrice = txEstimateGasObj.gasPrice || "0x0";
const value = txEstimateGasObj.value || "0x0";

Expand Down Expand Up @@ -169,17 +168,6 @@ export class EthNormalizer {
}
}

async function getDefaultFromAddress(rpc: GodwokenClient): Promise<HexString> {
const defaultFromScript = await rpc.getScript(
gwConfig.accounts.defaultFrom.scriptHash
);
if (defaultFromScript == null) {
throw new Error("default from script is null");
}
const defaultFromAddress = "0x" + defaultFromScript.args.slice(2).slice(64);
return defaultFromAddress;
}

export async function getMaxGasByBalance(
rpc: GodwokenClient,
from: HexString,
Expand Down