Skip to content

Commit dae8fc9

Browse files
authored
Set account head when creating a new wallet (#291)
1 parent 6db9162 commit dae8fc9

File tree

8 files changed

+301
-18
lines changed

8 files changed

+301
-18
lines changed

main/api/accounts/handleCreateAccount.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
1-
import { AccountFormat } from "@ironfish/sdk";
1+
import { AccountFormat, CreateAccountRequest } from "@ironfish/sdk";
22

33
import { manager } from "../manager";
44

5-
export async function handleCreateAccount({ name }: { name: string }) {
5+
export async function handleCreateAccount({
6+
name,
7+
createdAt,
8+
head,
9+
}: CreateAccountRequest) {
610
const ironfish = await manager.getIronfish();
711
const rpcClient = await ironfish.rpcClient();
812

913
const createResponse = await rpcClient.wallet.createAccount({
1014
name,
15+
createdAt,
16+
head,
1117
});
1218

1319
const exportResponse = await rpcClient.wallet.exportAccount({

main/api/accounts/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,21 @@ export const accountRouter = t.router({
3939
.input(
4040
z.object({
4141
name: z.string(),
42+
createdAt: z.number().optional(),
43+
head: z
44+
.object({
45+
sequence: z.number(),
46+
hash: z.string(),
47+
})
48+
.optional(),
4249
}),
4350
)
4451
.mutation(async (opts) => {
4552
return handleCreateAccount(opts.input);
4653
}),
54+
getExternalChainHead: t.procedure.query(async () =>
55+
manager.getExternalChainHead(),
56+
),
4757
importAccount: t.procedure
4858
.input(handleImportAccountInputs)
4959
.mutation(async (opts) => {
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { GENESIS_BLOCK_SEQUENCE } from "@ironfish/sdk";
2+
import axios from "axios";
3+
4+
const MAINNET_API_URL = `https://api.ironfish.network`;
5+
const TESTNET_API_URL = `https://testnet.api.ironfish.network`;
6+
const CONFIRMATION_BLOCKS = 20;
7+
8+
type Block = { sequence: number; hash: string };
9+
10+
// Try to get a head sequence to use for new account createdAt fields if the chain is not yet synced
11+
export async function getExternalChainHead(
12+
networkId: number,
13+
): Promise<Block | undefined> {
14+
const fromAPI = await getChainHeadWithConfirmation(networkId).catch(
15+
() => undefined,
16+
);
17+
18+
return fromAPI || getDefaultHead(networkId);
19+
}
20+
21+
async function getChainHeadWithConfirmation(
22+
networkId: number,
23+
): Promise<Block | undefined> {
24+
const headFromAPI = await getChainHeadFromAPI(networkId).catch(
25+
() => undefined,
26+
);
27+
if (!headFromAPI) {
28+
return undefined;
29+
}
30+
31+
const sequence = Math.max(
32+
GENESIS_BLOCK_SEQUENCE,
33+
headFromAPI.sequence - CONFIRMATION_BLOCKS,
34+
);
35+
return getBlockFromAPI(networkId, sequence).catch(() => undefined);
36+
}
37+
38+
// Local block sequences in case network is unreachable
39+
function getDefaultHead(networkId: number): Block | undefined {
40+
switch (networkId) {
41+
case 0:
42+
return {
43+
sequence: 740000,
44+
hash: "00000419de21cb51eeea7b002c89e630f0b326525e99a6595b4c87442fd7bfa5",
45+
};
46+
case 1:
47+
return {
48+
sequence: 806900,
49+
hash: "0000000000016a6993058e7459fff9f1413f14c09483612d511e5e86894803b7",
50+
};
51+
default:
52+
return undefined;
53+
}
54+
}
55+
56+
async function getChainHeadFromAPI(networkId: number): Promise<Block> {
57+
const apiURL = getAPIUrl(networkId);
58+
if (!apiURL) {
59+
throw new Error(
60+
`Manifest url for the snapshots are not available for network ID ${networkId}`,
61+
);
62+
}
63+
64+
const headBlock = (
65+
await axios.get<{ sequence: number; hash: string }>(
66+
`${apiURL}/blocks/head`,
67+
{
68+
timeout: 5000,
69+
},
70+
)
71+
).data;
72+
73+
return {
74+
sequence: headBlock.sequence,
75+
hash: headBlock.hash,
76+
};
77+
}
78+
79+
async function getBlockFromAPI(
80+
networkId: number,
81+
sequence: number,
82+
): Promise<Block | undefined> {
83+
const apiURL = getAPIUrl(networkId);
84+
if (!apiURL) {
85+
throw new Error(
86+
`Manifest url for the snapshots are not available for network ID ${networkId}`,
87+
);
88+
}
89+
90+
const head = (
91+
await axios.get<{ sequence: number; hash: string }>(
92+
`${apiURL}/blocks/find?sequence=${sequence}&with_transactions=false`,
93+
{
94+
timeout: 5000,
95+
},
96+
)
97+
).data;
98+
99+
return {
100+
sequence: head.sequence,
101+
hash: head.hash,
102+
};
103+
}
104+
105+
const getAPIUrl = (networkId: number): string | null => {
106+
switch (networkId) {
107+
case 0:
108+
return TESTNET_API_URL;
109+
case 1:
110+
return MAINNET_API_URL;
111+
default:
112+
return null;
113+
}
114+
};

main/api/manager.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { RpcClient } from "@ironfish/sdk";
22

3+
import { getExternalChainHead } from "./accounts/utils/getExternalChainHead";
34
import { Ironfish } from "./ironfish/Ironfish";
45
import { userSettingsStore } from "../stores/userSettingsStore";
56

@@ -11,6 +12,7 @@ export type InitialState =
1112

1213
export class Manager {
1314
private _ironfish: Ironfish | null = null;
15+
private _externalChainHead?: { sequence: number; hash: string };
1416

1517
async getIronfish(): Promise<Ironfish> {
1618
if (this._ironfish) return this._ironfish;
@@ -23,6 +25,13 @@ export class Manager {
2325
return this._ironfish;
2426
}
2527

28+
async getExternalChainHead(): Promise<
29+
{ sequence: number; hash: string } | undefined
30+
> {
31+
const networkId = await userSettingsStore.getSetting("networkId");
32+
return getExternalChainHead(networkId);
33+
}
34+
2635
async shouldDownloadSnapshot(): Promise<boolean> {
2736
const ironfish = await this.getIronfish();
2837
const rpcClient = await ironfish.rpcClient();

package-lock.json

Lines changed: 135 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
},
2323
"license": "MPL-2.0",
2424
"dependencies": {
25-
"@ironfish/sdk": "2.8.1",
25+
"@ironfish/sdk": "2.9.0",
2626
"@ironfish/rust-nodejs": "2.7.0",
2727
"@ledgerhq/errors": "6.19.1",
2828
"@ledgerhq/hw-transport-node-hid": "6.29.5",

0 commit comments

Comments
 (0)