-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.mjs
147 lines (120 loc) · 3.89 KB
/
index.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import fs from 'fs';
import axios from 'axios';
import { ethers } from 'ethers';
import pkg from 'csvtojson';
const { csv } = pkg;
import sleep from 'sleep-promise';
const etherscan_api_key = JSON.parse(fs.readFileSync('secrets.json'))['ETHERSCAN_API_KEY'];
const ethers_provider = ethers.getDefaultProvider('homestead', {
etherscan: etherscan_api_key
});
async function getPubkey(address, wait=false) {
console.log(`Searching for first tx for address ${address}`);
let tx_res = await axios.get('https://api.etherscan.io/api' +
'?module=account' +
'&action=txlist' +
`&address=${address}` +
'&startblock=0' +
'&endblock=99999999' +
`&apikey=${etherscan_api_key}`);
if (wait) {
await sleep(200);
}
let txes = tx_res['data']['result'];
let from_txes = txes.filter(tx => tx.from.toLowerCase() == address.toLowerCase());
let tx_hash = from_txes[0]['hash'];
console.log(`Finding signature for tx ${tx_hash}`);
let tx = await ethers_provider.getTransaction(tx_hash);
if (wait) {
await sleep(200);
}
// NOTE: taken from static data for now
let expandedSig = {
r: tx.r,
s: tx.s,
v: tx.v
}
let txData;
switch (tx.type) {
case 0:
txData = {
gasPrice: tx.gasPrice,
gasLimit: tx.gasLimit,
value: tx.value,
nonce: tx.nonce,
data: tx.data,
chainId: tx.chainId, // NOTE: always mainnet
to: tx.to
};
break;
case 2: // 1559
txData = {
gasLimit: tx.gasLimit,
value: tx.value,
nonce: tx.nonce,
data: tx.data,
chainId: tx.chainId,
to: tx.to,
type: 2,
maxFeePerGas: tx.maxFeePerGas,
maxPriorityFeePerGas: tx.maxPriorityFeePerGas
}
break;
default:
// NOTE: if this is an issue, should try other txes
console.log(`unsupported tx found for ${address}`);
return null;
}
let sig = ethers.utils.joinSignature(expandedSig)
let rsTx = await ethers.utils.resolveProperties(txData);
let raw = ethers.utils.serializeTransaction(rsTx) // returns RLP encoded tx
let msgHash = ethers.utils.keccak256(raw) // as specified by ECDSA
let msgBytes = ethers.utils.arrayify(msgHash) // create binary hash
let pubkey = ethers.utils.recoverPublicKey(msgBytes, sig)
console.log(`retrieved pubkey: ${pubkey}`);
let recoveredAddress = ethers.utils.computeAddress(pubkey);
if (recoveredAddress.toLowerCase() != address.toLowerCase()) {
throw 'recovered address differs from original!'
}
return pubkey
}
async function continueBuildingAddressPubkeyCSV() {
const allAddresses = new Set(
Object.values(
JSON.parse(fs.readFileSync('data/devconAddresses.json'))
).flat()
);
let rows = await csv().fromFile('output/addressPubkeys.csv');
const finishedAddresses = new Set(
rows.map(r => r['address'])
)
const addressesToProcess = [...new Set(
[...allAddresses].filter(a => !finishedAddresses.has(a))
)];
console.log(`remaining addresses: ${addressesToProcess.length}`);
for (let a of addressesToProcess) {
try {
const pubkey = await getPubkey(a, true);
fs.appendFileSync('output/addressPubkeys.csv', `${a},${pubkey}\n`);
}
catch (e) {
console.log(`failed on address ${a}: ${e}`);
continue;
}
}
}
async function checkPubkeys() {
let rows = await csv().fromFile('output/addressPubkeys.csv');
let success = true;
for (let row of rows) {
let address = row['address']
let pubkey = row['pubkey']
let recoveredAddress = ethers.utils.computeAddress(pubkey);
if (address.toLowerCase() != recoveredAddress.toLowerCase()) {
console.log(`wrong pubkey ${pubkey} for address ${address}`);
success = false
}
}
return success;
}
await continueBuildingAddressPubkeyCSV();