-
Notifications
You must be signed in to change notification settings - Fork 8
/
ee.ts
138 lines (117 loc) · 3.45 KB
/
ee.ts
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
import * as assert from 'assert'
import BN = require('bn.js')
import { keccak256 } from 'ethereumjs-util'
import { encode, decode } from 'rlp'
import { verifyMultiproof, decodeInstructions, flatDecodeInstructions } from './multiproof'
interface TestSuite {
preStateRoot: Buffer
blockData: Buffer
}
// TODO
interface BlockData {}
interface Account {
nonce: BN
balance: BN
stateRoot: Buffer
codeHash: Buffer
}
interface Tx {
toIdx: number
value: BN
nonce: BN
fromIdx: number
}
export function decodeBlockData(blockData: Buffer): any {
const d = decode(blockData)
let keyvals = []
// @ts-ignore
assert(d[3].length === d[4].length)
// @ts-ignore
for (let i = 0; i < d[3].length; i++) {
// @ts-ignore
keyvals.push(encode([d[3][i], d[4][i]]))
// @ts-ignore
}
return {
txes: d[0],
addrs: d[1],
multiproof: {
// @ts-ignore
hashes: d[2],
// @ts-ignore
keyvals: keyvals,
// @ts-ignore
instructions: flatDecodeInstructions(d[5]),
},
}
}
function decodeTx(raw: Buffer[]): Tx {
// TODO: How many addresses possible? is u8 enough for indices?
return {
toIdx: bufToU8(raw[0]),
value: new BN(raw[1]),
nonce: new BN(raw[2]),
fromIdx: bufToU8(raw[3]),
}
}
function bufToU8(b: Buffer): number {
// RLP decoding of 0 is empty buffer
if (b.length === 0) {
return 0
}
return b.readUInt8(0)
}
function decodeAccount(raw: Buffer): Account {
const d = decode(raw)
// @ts-ignore
return { nonce: new BN(d[0]), balance: new BN(d[1]), stateRoot: d[2], codeHash: d[3] }
}
function encodeAccount(acc: Account): Buffer {
return encode([
acc.nonce.toArrayLike(Buffer, 'be'),
acc.balance.toArrayLike(Buffer, 'be'),
acc.stateRoot,
acc.codeHash,
])
}
export function main(testSuite: TestSuite): boolean {
const preStateRoot = testSuite.preStateRoot
const blockData = decodeBlockData(testSuite.blockData)
const addrs = blockData.addrs
const proof = blockData.multiproof
const updatedLeaves = new Array(blockData.multiproof.keyvals.length).fill(undefined)
for (const rawTx of blockData.txes) {
const tx = decodeTx(rawTx)
const fromIdx = tx.fromIdx
const toIdx = tx.toIdx
const encodedUnsignedTx = encode([addrs[toIdx], tx.value, tx.nonce])
const txHash = keccak256(encodedUnsignedTx)
// TODO: assert signature recovered address matches the given address
// const recoveredAddr = ecrecover(txHash, tx.sig)
// assert(addrs[fromIdx].equals(recoveredAddr))
// If `from` or `to` has been modified, use the updated version
let fromLeaf = proof.keyvals[fromIdx]
if (updatedLeaves[fromIdx] !== undefined) {
fromLeaf = updatedLeaves[fromIdx]
}
let toLeaf = proof.keyvals[toIdx]
if (updatedLeaves[toIdx] !== undefined) {
toLeaf = updatedLeaves[toIdx]
}
const decodedFrom = decode(fromLeaf)
const decodedTo = decode(toLeaf)
// @ts-ignore
const fromAcc = decodeAccount(decodedFrom[1])
// @ts-ignore
const toAcc = decodeAccount(decodedTo[1])
assert(fromAcc.balance.gte(tx.value))
assert(fromAcc.nonce.eq(tx.nonce))
fromAcc.balance.isub(tx.value)
toAcc.balance.iadd(tx.value)
fromAcc.nonce.iaddn(1)
updatedLeaves[fromIdx] = encode([decodedFrom[0], encodeAccount(fromAcc)])
updatedLeaves[toIdx] = encode([decodedTo[0], encodeAccount(toAcc)])
}
const keys = addrs.map((a: Buffer) => keccak256(a))
return verifyMultiproof(preStateRoot, blockData.multiproof, keys)
}