Skip to content

Commit

Permalink
Adds support for updating dummy code (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Sapede committed May 31, 2023
1 parent 9f8d9f2 commit 6dffd5d
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 15 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,29 @@ Options:
-o, --base-path Specifies where all generated files are to be stored.
[string] [default: "/tmp/fork-data/"]
```


------------------------

# Tools

## Updating Precompile Code

Each precompile on-chain should contain some dummy code to allow Smart Contracts checking code before calling it to be able to call them.

Verify currently existing precompiles

```typescript
ts-node src/tools/list-precompiles.ts --network moonbeam
```

would output something like:
![Image of precompiles with their dummy code](https://github.com/PureStake/moonbeam-tools/assets/329248/13147092-6ecc-4cfe-ba53-93c40e1ff6a6)

To update the dummy code, you can use the [PrecompileRegistry](https://github.com/PureStake/moonbeam/blob/master/precompiles/precompile-registry/PrecompileRegistry.sol) with a funded account:

```
ts-node src/tools/list-precompiles.ts --network moonbeam --update-dummy-code --private-key $PRIVATE_KEY
```

This will update each missing precompile (or you can use `--address 9` to upgrade only contract at address 9)
126 changes: 111 additions & 15 deletions src/tools/list-precompiles.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { xxhashAsU8a, blake2AsU8a } from "@polkadot/util-crypto";
import { u8aConcat } from "@polkadot/util";
import yargs from "yargs";
import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
import { getApiFor, getViemAccountFor, getViemFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
import { createPublicClient, createWalletClient, encodeFunctionData, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import chalk from "chalk";

const debug = require("debug")("main");
Expand All @@ -15,16 +17,29 @@ const argv = yargs(process.argv.slice(2))
type: "number",
description: "Filter given address",
},
}).argv;
"update-dummy-code": {
type: "boolean",
description: "Updates the dummy contract code",
},
"private-key": {
type: "string",
description: "Private key to use to update the dummy code",
},
})
.strict().argv;

const getPrecompileStorageKey = (address: number) => {
const indexKey = `0x${address.toString(16).padStart(40, "0")}`;
const getAddress = (addressNumber: number): `0x${string}` => {
return `0x${addressNumber.toString(16).padStart(40, "0")}`;
};

const getPrecompileStorageKey = (addressNumber: number) => {
const address = getAddress(addressNumber);
return `0x${Buffer.from(
u8aConcat(
xxhashAsU8a("EVM", 128),
xxhashAsU8a("AccountCodes", 128),
blake2AsU8a(indexKey, 128),
indexKey
blake2AsU8a(address, 128),
address
)
).toString("hex")}`;
};
Expand Down Expand Up @@ -146,26 +161,107 @@ const KNOWN_PRECOMPILES = [
index: 2064,
name: "TreasuryCouncilInstance",
},
{
index: 2065,
name: "Referenda",
},
{
index: 2066,
name: "ConvictionVoting",
},
{
index: 2067,
name: "Preimage",
},
{
index: 2068,
name: "OpenTechCommittee",
},
{
index: 2069,
name: "PrecompileRegistry",
},
];

const main = async () => {
const api = await getApiFor(argv);
if (argv["update-dummy-code"] && !argv["private-key"]) {
console.error("Private key is required to update the dummy code");
process.exit(1);
}
const viem = getViemFor(argv);

const addresses = argv.address ? [argv.address] : KNOWN_PRECOMPILES.map((p) => p.index);

for (const address of addresses) {
const name = KNOWN_PRECOMPILES.find((p) => p.index == address)?.name || "";
const storageKey = getPrecompileStorageKey(address);
const code = (await api.rpc.state.getStorage(storageKey)) as any;
const hasCode = !!code.toHuman();
const precompileCodes: { [key: string]: boolean } = {};
for (const addressNumber of addresses) {
const name = KNOWN_PRECOMPILES.find((p) => p.index == addressNumber)?.name || "";
const storageKey = getPrecompileStorageKey(addressNumber);
const code = (await viem.getBytecode({address: getAddress(addressNumber)}));
const hasCode = !!code;
precompileCodes[addressNumber] = hasCode;
const color = hasCode ? chalk.green : chalk.red;
console.log(
`${color(
`${(name ? `(${name}) ` : "").padStart(26)}${address.toString().padEnd(5)}`
)} [${storageKey}]: ${hasCode ? code.toHex() : "None"}`
`${(name ? `(${name}) ` : "").padStart(26)}${addressNumber.toString().padEnd(5)}`
)} [${storageKey}]: ${hasCode ? code : "None"}`
);
}

if (argv["update-dummy-code"]) {
const abiItem = {
inputs: [{ internalType: "address", name: "a", type: "address" }],
name: "updateAccountCode",
outputs: [],
stateMutability: "nonpayable",
type: "function",
};

const account = privateKeyToAccount(argv["private-key"] as `0x${string}`);
const wallet = getViemAccountFor(argv, account);
let nonce = await viem.getTransactionCount({ address: account.address });
const receipts = (await Promise.all(
addresses.map(async (addressNumber) => {
if (!precompileCodes[addressNumber]) {
try {
const data = encodeFunctionData({
abi: [abiItem],
functionName: "updateAccountCode",
args: [getAddress(addressNumber)],
});
const hash = await wallet.sendTransaction({
chain: null,
account,
to: "0x0000000000000000000000000000000000000815",
data,
nonce: nonce++,
gas: 200000n,
});
console.log(`Updating precompile ${addressNumber}: ${hash}...`);
return {addressNumber, hash};
} catch (err) {
debug(err);
console.log(
`Failed to update precompile ${addressNumber}: ${err.details || err.message || err}}`
);
return null;
}
}
return null;
})
)).filter(data => !!data);
console.log(`Waiting for receipts...${receipts.length}`);
await Promise.all(
receipts.map(async ({hash, addressNumber}) => {
if (!hash) {
return;
}
const receipt = await viem.waitForTransactionReceipt({ hash });
console.log(`|${addressNumber.toString().padStart(5, ' ')}] ${receipt.status} - ${hash} (#${receipt.gasUsed.toString()})`);
})
);
console.log(`Done`);
}
api.disconnect();
(await viem.transport.getSocket()).close();
};

main();

0 comments on commit 6dffd5d

Please sign in to comment.