Skip to content

Commit dcdd6f0

Browse files
committed
feat: add optional verification of input and output file with separate csv file
1 parent 9eaa720 commit dcdd6f0

File tree

4 files changed

+279
-16
lines changed

4 files changed

+279
-16
lines changed

rollup/missing_header_fields/export-headers-toolkit/cmd/dedup.go

Lines changed: 134 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@ package cmd
22

33
import (
44
"bufio"
5+
"bytes"
56
"crypto/sha256"
67
"encoding/binary"
78
"fmt"
89
"io"
910
"log"
1011
"os"
12+
"strconv"
13+
"strings"
1114

1215
"github.com/spf13/cobra"
1316

17+
"github.com/scroll-tech/go-ethereum/common"
18+
1419
"github.com/scroll-tech/go-ethereum/export-headers-toolkit/types"
1520
)
1621

@@ -38,33 +43,32 @@ The binary layout of the deduplicated file is as follows:
3843
if err != nil {
3944
log.Fatalf("Error reading output flag: %v", err)
4045
}
46+
verifyFile, err := cmd.Flags().GetString("verify")
47+
if err != nil {
48+
log.Fatalf("Error reading verify flag: %v", err)
49+
}
50+
51+
if verifyFile != "" {
52+
verifyInputFile(verifyFile, inputFile)
53+
}
4154

4255
_, seenVanity, _ := runAnalysis(inputFile)
4356
runDedup(inputFile, outputFile, seenVanity)
44-
runSHA256(outputFile)
45-
},
46-
}
4757

48-
func runSHA256(outputFile string) {
49-
f, err := os.Open(outputFile)
50-
defer f.Close()
51-
if err != nil {
52-
log.Fatalf("Error opening file: %v", err)
53-
}
54-
55-
h := sha256.New()
56-
if _, err = io.Copy(h, f); err != nil {
57-
log.Fatalf("Error hashing file: %v", err)
58-
}
58+
if verifyFile != "" {
59+
verifyOutputFile(verifyFile, outputFile)
60+
}
5961

60-
fmt.Printf("Deduplicated headers written to %s with sha256 checksum: %x\n", outputFile, h.Sum(nil))
62+
runSHA256(outputFile)
63+
},
6164
}
6265

6366
func init() {
6467
rootCmd.AddCommand(dedupCmd)
6568

6669
dedupCmd.Flags().String("input", "headers.bin", "headers file")
6770
dedupCmd.Flags().String("output", "headers-dedup.bin", "deduplicated, binary formatted file")
71+
dedupCmd.Flags().String("verify", "", "verify the input and output files with the given .csv file")
6872
}
6973

7074
func runAnalysis(inputFile string) (seenDifficulty map[uint64]int, seenVanity map[[32]byte]bool, seenSealLen map[int]int) {
@@ -118,6 +122,21 @@ func runDedup(inputFile, outputFile string, seenVanity map[[32]byte]bool) {
118122
})
119123
}
120124

125+
func runSHA256(outputFile string) {
126+
f, err := os.Open(outputFile)
127+
defer f.Close()
128+
if err != nil {
129+
log.Fatalf("Error opening file: %v", err)
130+
}
131+
132+
h := sha256.New()
133+
if _, err = io.Copy(h, f); err != nil {
134+
log.Fatalf("Error hashing file: %v", err)
135+
}
136+
137+
fmt.Printf("Deduplicated headers written to %s with sha256 checksum: %x\n", outputFile, h.Sum(nil))
138+
}
139+
121140
type headerReader struct {
122141
file *os.File
123142
reader *bufio.Reader
@@ -175,3 +194,103 @@ func (h *headerReader) read(callback func(header *types.Header)) {
175194
func (h *headerReader) close() {
176195
h.file.Close()
177196
}
197+
198+
type csvHeaderReader struct {
199+
file *os.File
200+
reader *bufio.Reader
201+
}
202+
203+
func newCSVHeaderReader(verifyFile string) *csvHeaderReader {
204+
f, err := os.Open(verifyFile)
205+
if err != nil {
206+
log.Fatalf("Error opening verify file: %v", err)
207+
}
208+
209+
h := &csvHeaderReader{
210+
file: f,
211+
reader: bufio.NewReader(f),
212+
}
213+
214+
return h
215+
}
216+
217+
func (h *csvHeaderReader) readNext() *types.Header {
218+
line, err := h.reader.ReadString('\n')
219+
if err != nil {
220+
if err == io.EOF {
221+
return nil
222+
}
223+
log.Fatalf("Error reading line: %v", err)
224+
}
225+
226+
s := strings.Split(line, ",")
227+
extraString := strings.Split(s[2], "\n")
228+
229+
num, err := strconv.ParseUint(s[0], 10, 64)
230+
if err != nil {
231+
log.Fatalf("Error parsing block number: %v", err)
232+
}
233+
difficulty, err := strconv.ParseUint(s[1], 10, 64)
234+
if err != nil {
235+
log.Fatalf("Error parsing difficulty: %v", err)
236+
}
237+
extra := common.FromHex(extraString[0])
238+
239+
header := types.NewHeader(num, difficulty, extra)
240+
return header
241+
}
242+
243+
func (h *csvHeaderReader) close() {
244+
h.file.Close()
245+
}
246+
247+
func verifyInputFile(verifyFile, inputFile string) {
248+
csvReader := newCSVHeaderReader(verifyFile)
249+
defer csvReader.close()
250+
251+
binaryReader := newHeaderReader(inputFile)
252+
defer binaryReader.close()
253+
254+
binaryReader.read(func(header *types.Header) {
255+
csvHeader := csvReader.readNext()
256+
257+
if !csvHeader.Equal(header) {
258+
log.Fatalf("Header mismatch: %v != %v", csvHeader, header)
259+
}
260+
})
261+
262+
log.Printf("All headers match in %s and %s\n", verifyFile, inputFile)
263+
}
264+
265+
func verifyOutputFile(verifyFile, outputFile string) {
266+
csvReader := newCSVHeaderReader(verifyFile)
267+
defer csvReader.close()
268+
269+
dedupReader, err := NewReader(outputFile)
270+
if err != nil {
271+
log.Fatalf("Error opening dedup file: %v", err)
272+
}
273+
defer dedupReader.Close()
274+
275+
for {
276+
header := csvReader.readNext()
277+
if header == nil {
278+
if _, _, err = dedupReader.ReadNext(); err == nil {
279+
log.Fatalf("Expected EOF, got more headers")
280+
}
281+
break
282+
}
283+
284+
difficulty, extraData, err := dedupReader.Read(header.Number)
285+
if err != nil {
286+
log.Fatalf("Error reading header: %v", err)
287+
}
288+
289+
if header.Difficulty != difficulty {
290+
log.Fatalf("Difficulty mismatch: headerNum %d: %d != %d", header.Number, header.Difficulty, difficulty)
291+
}
292+
if !bytes.Equal(header.ExtraData, extraData) {
293+
log.Fatalf("ExtraData mismatch: headerNum %d: %x != %x", header.Number, header.ExtraData, extraData)
294+
}
295+
}
296+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package cmd
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"fmt"
7+
"io"
8+
"os"
9+
)
10+
11+
// TODO: instead of duplicating this file, missing_header_fields.Reader should be used in toolkit
12+
13+
type missingHeader struct {
14+
headerNum uint64
15+
difficulty uint64
16+
extraData []byte
17+
}
18+
19+
type Reader struct {
20+
file *os.File
21+
reader *bufio.Reader
22+
sortedVanities map[int][32]byte
23+
lastReadHeader *missingHeader
24+
}
25+
26+
func NewReader(filePath string) (*Reader, error) {
27+
f, err := os.Open(filePath)
28+
if err != nil {
29+
return nil, fmt.Errorf("failed to open file: %v", err)
30+
}
31+
32+
r := &Reader{
33+
file: f,
34+
reader: bufio.NewReader(f),
35+
}
36+
37+
// read the count of unique vanities
38+
vanityCount, err := r.reader.ReadByte()
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
// read the unique vanities
44+
r.sortedVanities = make(map[int][32]byte)
45+
for i := uint8(0); i < vanityCount; i++ {
46+
var vanity [32]byte
47+
if _, err = r.reader.Read(vanity[:]); err != nil {
48+
return nil, err
49+
}
50+
r.sortedVanities[int(i)] = vanity
51+
}
52+
53+
return r, nil
54+
}
55+
56+
func (r *Reader) Read(headerNum uint64) (difficulty uint64, extraData []byte, err error) {
57+
if r.lastReadHeader == nil {
58+
if _, _, err = r.ReadNext(); err != nil {
59+
return 0, nil, err
60+
}
61+
}
62+
63+
if headerNum > r.lastReadHeader.headerNum {
64+
// skip the headers until the requested header number
65+
for i := r.lastReadHeader.headerNum; i < headerNum; i++ {
66+
if _, _, err = r.ReadNext(); err != nil {
67+
return 0, nil, err
68+
}
69+
}
70+
}
71+
72+
if headerNum == r.lastReadHeader.headerNum {
73+
return r.lastReadHeader.difficulty, r.lastReadHeader.extraData, nil
74+
}
75+
76+
// headerNum < r.lastReadHeader.headerNum is not supported
77+
return 0, nil, fmt.Errorf("requested header %d below last read header number %d", headerNum, r.lastReadHeader.headerNum)
78+
}
79+
80+
func (r *Reader) ReadNext() (difficulty uint64, extraData []byte, err error) {
81+
// read the bitmask
82+
bitmaskByte, err := r.reader.ReadByte()
83+
if err != nil {
84+
return 0, nil, fmt.Errorf("failed to read bitmask: %v", err)
85+
}
86+
87+
bits := newBitMaskFromByte(bitmaskByte)
88+
89+
seal := make([]byte, bits.sealLen())
90+
91+
if _, err = io.ReadFull(r.reader, seal); err != nil {
92+
return 0, nil, fmt.Errorf("failed to read seal: %v", err)
93+
}
94+
95+
// construct the extraData field
96+
vanity := r.sortedVanities[bits.vanityIndex()]
97+
var b bytes.Buffer
98+
b.Write(vanity[:])
99+
b.Write(seal)
100+
101+
// we don't have the header number, so we'll just increment the last read header number
102+
// we assume that the headers are written in order, starting from 0
103+
if r.lastReadHeader == nil {
104+
r.lastReadHeader = &missingHeader{
105+
headerNum: 0,
106+
difficulty: uint64(bits.difficulty()),
107+
extraData: b.Bytes(),
108+
}
109+
} else {
110+
r.lastReadHeader.headerNum++
111+
r.lastReadHeader.difficulty = uint64(bits.difficulty())
112+
r.lastReadHeader.extraData = b.Bytes()
113+
}
114+
115+
return difficulty, b.Bytes(), nil
116+
}
117+
118+
func (r *Reader) Close() error {
119+
return r.file.Close()
120+
}

rollup/missing_header_fields/export-headers-toolkit/cmd/missing_header_writer.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ type bitMask struct {
115115
b uint8
116116
}
117117

118+
func newBitMaskFromByte(b uint8) bitMask {
119+
return bitMask{b}
120+
}
121+
118122
func newBitMask(vanityIndex int, difficulty int, sealLen int) bitMask {
119123
b := uint8(0)
120124

rollup/missing_header_fields/export-headers-toolkit/types/header.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package types
33
import (
44
"encoding/binary"
55
"fmt"
6+
7+
"github.com/scroll-tech/go-ethereum/common"
68
)
79

810
const HeaderSizeSerialized = 2
@@ -23,7 +25,25 @@ func NewHeader(number, difficulty uint64, extraData []byte) *Header {
2325
}
2426

2527
func (h *Header) String() string {
26-
return fmt.Sprintf("%d,%d,0x%x\n", h.Number, h.Difficulty, h.ExtraData)
28+
return fmt.Sprintf("%d,%d,%s\n", h.Number, h.Difficulty, common.Bytes2Hex(h.ExtraData))
29+
}
30+
31+
func (h *Header) Equal(other *Header) bool {
32+
if h.Number != other.Number {
33+
return false
34+
}
35+
if h.Difficulty != other.Difficulty {
36+
return false
37+
}
38+
if len(h.ExtraData) != len(other.ExtraData) {
39+
return false
40+
}
41+
for i, b := range h.ExtraData {
42+
if b != other.ExtraData[i] {
43+
return false
44+
}
45+
}
46+
return true
2747
}
2848

2949
// Bytes returns the byte representation of the header including the initial 2 bytes for the size.

0 commit comments

Comments
 (0)