ferranbt / fastssz

Fast Ethereum2.0 SSZ encoder/decoder
MIT License
73 stars 44 forks source link

ssz HashTreeRoot is different in window 386 environment #140

Closed fearlessfe closed 3 months ago

fearlessfe commented 6 months ago

In window 386 environment, the result of HashTreeRoot is different with amd64(both window or linux)

the sample code and the data file is below.

when build CGO_ENABLED=0 GOOS=windows GOARCH=386 go build main.go, the epochRoot is different epoch.txt

package main

import (
    "encoding/hex"
    "fmt"

    ssz "github.com/ferranbt/fastssz"
)

//go:embed epoch.txt
var epochAccuHex string

func main() {
    epochAccuBytes, err := hex.DecodeString(epochAccuHex)
    if err != nil {
        panic(err)
    }
    epochAccu, err := DecodeEpochAccumulator(epochAccuBytes)
    if err != nil {
        panic(err)
    }
    epochRoot, err := epochAccu.HashTreeRoot()
    if err != nil {
        panic(err)
    }
        // differ in window 386
    fmt.Println("epochRoot:" + hex.EncodeToString(epochRoot[:]))
}

func DecodeEpochAccumulator(data []byte) (*EpochAccumulator, error) {
    epochAccu := new(EpochAccumulator)
    err := epochAccu.UnmarshalSSZ(data)
    return epochAccu, err
}

type EpochAccumulator struct {
    HeaderRecords [][]byte `ssz-size:"8192,64"`
}

// MarshalSSZ ssz marshals the EpochAccumulator object
func (e *EpochAccumulator) MarshalSSZ() ([]byte, error) {
    return ssz.MarshalSSZ(e)
}

// MarshalSSZTo ssz marshals the EpochAccumulator object to a target array
func (e *EpochAccumulator) MarshalSSZTo(buf []byte) (dst []byte, err error) {
    dst = buf

    // Field (0) 'HeaderRecords'
    if size := len(e.HeaderRecords); size != 8192 {
        err = ssz.ErrVectorLengthFn("EpochAccumulator.HeaderRecords", size, 8192)
        return
    }
    for ii := 0; ii < 8192; ii++ {
        if size := len(e.HeaderRecords[ii]); size != 64 {
            err = ssz.ErrBytesLengthFn("EpochAccumulator.HeaderRecords[ii]", size, 64)
            return
        }
        dst = append(dst, e.HeaderRecords[ii]...)
    }

    return
}

// UnmarshalSSZ ssz unmarshals the EpochAccumulator object
func (e *EpochAccumulator) UnmarshalSSZ(buf []byte) error {
    var err error
    size := uint64(len(buf))
    if size != 524288 {
        return ssz.ErrSize
    }

    // Field (0) 'HeaderRecords'
    e.HeaderRecords = make([][]byte, 8192)
    for ii := 0; ii < 8192; ii++ {
        if cap(e.HeaderRecords[ii]) == 0 {
            e.HeaderRecords[ii] = make([]byte, 0, len(buf[0:524288][ii*64:(ii+1)*64]))
        }
        e.HeaderRecords[ii] = append(e.HeaderRecords[ii], buf[0:524288][ii*64:(ii+1)*64]...)
    }

    return err
}

// SizeSSZ returns the ssz encoded size in bytes for the EpochAccumulator object
func (e *EpochAccumulator) SizeSSZ() (size int) {
    size = 524288
    return
}

// HashTreeRoot ssz hashes the EpochAccumulator object
func (e *EpochAccumulator) HashTreeRoot() ([32]byte, error) {
    return ssz.HashWithDefaultHasher(e)
}

// HashTreeRootWith ssz hashes the EpochAccumulator object with a hasher
func (e *EpochAccumulator) HashTreeRootWith(hh ssz.HashWalker) (err error) {
    indx := hh.Index()

    // Field (0) 'HeaderRecords'
    {
        if size := len(e.HeaderRecords); size != 8192 {
            err = ssz.ErrVectorLengthFn("EpochAccumulator.HeaderRecords", size, 8192)
            return
        }
        subIndx := hh.Index()
        for _, i := range e.HeaderRecords {
            if len(i) != 64 {
                err = ssz.ErrBytesLength
                return
            }
            hh.PutBytes(i)
        }
        hh.Merkleize(subIndx)
    }

    hh.Merkleize(indx)
    return
}

// GetTree ssz hashes the EpochAccumulator object
func (e *EpochAccumulator) GetTree() (*ssz.Node, error) {
    return ssz.ProofTree(e)
}
ferranbt commented 6 months ago

Hello. I do not have a windows machine to test this on my side. Is there any way you could give me more info on the issue? Is it a matter of the hash protocol or byte manipulation of the hasher library? You could print the values at this line, it is where the core part of the hashing happens.

fearlessfe commented 6 months ago

I print the input image

the output is quite different, here is the result of linux amd64 image

next is the output of window 386 image

GrapeBaBa commented 6 months ago

@ferranbt Any suggestion for further investigation?

ferranbt commented 6 months ago

It is not easy to figure out what is wrong with only that output. Please try to reproduce the issue with an example with less data (i.e. accumulator array with a couple of entries only).

GrapeBaBa commented 6 months ago

It is not easy to figure out what is wrong with only that output. Please try to reproduce the issue with an example with less data (i.e. accumulator array with a couple of entries only).

@fearlessfe can you help?

ferranbt commented 3 months ago

Closing due to inactivity.