github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/u2u/genesisstore/disk.go (about)

     1  package genesisstore
     2  
     3  import (
     4  	"bytes"
     5  	"compress/gzip"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/status-im/keycard-go/hexutils"
    13  	"github.com/syndtr/goleveldb/leveldb/opt"
    14  	"github.com/unicornultrafoundation/go-helios/common/bigendian"
    15  	"github.com/unicornultrafoundation/go-helios/hash"
    16  	"github.com/unicornultrafoundation/go-u2u/rlp"
    17  
    18  	"github.com/unicornultrafoundation/go-u2u/u2u/genesis"
    19  	"github.com/unicornultrafoundation/go-u2u/u2u/genesisstore/filelog"
    20  	"github.com/unicornultrafoundation/go-u2u/u2u/genesisstore/fileshash"
    21  	"github.com/unicornultrafoundation/go-u2u/u2u/genesisstore/readersmap"
    22  	"github.com/unicornultrafoundation/go-u2u/utils/ioread"
    23  )
    24  
    25  var (
    26  	FileHeader  = hexutils.HexToBytes("641b00ac")
    27  	FileVersion = hexutils.HexToBytes("00020001")
    28  )
    29  
    30  const (
    31  	FilesHashMaxMemUsage = 256 * opt.MiB
    32  	FilesHashPieceSize   = 64 * opt.MiB
    33  )
    34  
    35  type dummyByteReader struct {
    36  	io.Reader
    37  }
    38  
    39  func (r dummyByteReader) ReadByte() (byte, error) {
    40  	b := make([]byte, 1)
    41  	err := ioread.ReadAll(r.Reader, b)
    42  	return b[0], err
    43  }
    44  
    45  func checkFileHeader(reader io.Reader) error {
    46  	headerAndVersion := make([]byte, len(FileHeader)+len(FileVersion))
    47  	err := ioread.ReadAll(reader, headerAndVersion)
    48  	if err != nil {
    49  		return err
    50  	}
    51  	if bytes.Compare(headerAndVersion[:len(FileHeader)], FileHeader) != 0 {
    52  		return errors.New("expected a genesis file, mismatched file header")
    53  	}
    54  	if bytes.Compare(headerAndVersion[len(FileHeader):], FileVersion) != 0 {
    55  		got := hexutils.BytesToHex(headerAndVersion[len(FileHeader):])
    56  		expected := hexutils.BytesToHex(FileVersion)
    57  		return errors.New(fmt.Sprintf("wrong version of genesis file, got=%s, expected=%s", got, expected))
    58  	}
    59  	return nil
    60  }
    61  
    62  type ReadAtSeekerCloser interface {
    63  	io.ReaderAt
    64  	io.Seeker
    65  	io.Closer
    66  }
    67  
    68  type Unit struct {
    69  	UnitName string
    70  	Header   genesis.Header
    71  }
    72  
    73  func OpenGenesisStore(rawReader ReadAtSeekerCloser) (*Store, genesis.Hashes, error) {
    74  	header := genesis.Header{}
    75  	hashes := genesis.Hashes{}
    76  	units := make([]readersmap.Unit, 0, 3)
    77  	offset := int64(0)
    78  	for i := 0; ; i++ {
    79  		// header cannot be long, cap it with 100000 bytes
    80  		headerReader := io.NewSectionReader(rawReader, offset, offset+100000)
    81  		err := checkFileHeader(headerReader)
    82  		if err == io.EOF {
    83  			break
    84  		}
    85  		if err != nil {
    86  			return nil, hashes, err
    87  		}
    88  		unit := Unit{}
    89  		err = rlp.Decode(dummyByteReader{headerReader}, &unit)
    90  		if err != nil {
    91  			return nil, hashes, err
    92  		}
    93  		if i == 0 {
    94  			header = unit.Header
    95  		} else {
    96  			if !header.Equal(unit.Header) {
    97  				return nil, hashes, errors.New("subsequent genesis header doesn't match the first header")
    98  			}
    99  		}
   100  
   101  		var h hash.Hash
   102  		err = ioread.ReadAll(headerReader, h[:])
   103  		if err != nil {
   104  			return nil, hashes, err
   105  		}
   106  		hashes[unit.UnitName] = h
   107  
   108  		var numB [8]byte
   109  		err = ioread.ReadAll(headerReader, numB[:])
   110  		if err != nil {
   111  			return nil, hashes, err
   112  		}
   113  		dataCompressedSize := bigendian.BytesToUint64(numB[:])
   114  
   115  		err = ioread.ReadAll(headerReader, numB[:])
   116  		if err != nil {
   117  			return nil, hashes, err
   118  		}
   119  		uncompressedSize := bigendian.BytesToUint64(numB[:])
   120  
   121  		headerSize, err := headerReader.Seek(0, io.SeekCurrent)
   122  		if err != nil {
   123  			return nil, hashes, err
   124  		}
   125  
   126  		unitReader := io.NewSectionReader(rawReader, offset+headerSize, offset+headerSize+int64(dataCompressedSize))
   127  		offset += headerSize + int64(dataCompressedSize)
   128  
   129  		gzipReader, err := gzip.NewReader(unitReader)
   130  		if err != nil {
   131  			return nil, hashes, err
   132  		}
   133  
   134  		// wrap with a logger
   135  		// human-readable name
   136  		name := unit.UnitName
   137  		scanfName := strings.ReplaceAll(name, "-", "")
   138  		if scanfName[len(scanfName)-1] < '0' || scanfName[len(scanfName)-1] > '9' {
   139  			scanfName += "0"
   140  		}
   141  		var part int
   142  		if _, err := fmt.Sscanf(scanfName, "brs%d", &part); err == nil {
   143  			name = fmt.Sprintf("blocks unit %d", part)
   144  		}
   145  		if _, err := fmt.Sscanf(scanfName, "ers%d", &part); err == nil {
   146  			name = fmt.Sprintf("epochs unit %d", part)
   147  		}
   148  		if _, err := fmt.Sscanf(scanfName, "evm%d", &part); err == nil {
   149  			name = fmt.Sprintf("EVM unit %d", part)
   150  		}
   151  		loggedReader := filelog.Wrap(gzipReader, name, uncompressedSize, time.Minute)
   152  
   153  		units = append(units, readersmap.Unit{
   154  			Name:   unit.UnitName,
   155  			Reader: loggedReader,
   156  		})
   157  	}
   158  
   159  	unitsMap, err := readersmap.Wrap(units)
   160  	if err != nil {
   161  		return nil, hashes, err
   162  	}
   163  
   164  	hashedMap := fileshash.Wrap(unitsMap.Open, FilesHashMaxMemUsage, hashes)
   165  
   166  	return NewStore(hashedMap, header, rawReader.Close), hashes, nil
   167  }