mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-09 13:33:50 +00:00
feat: get header by block hash
This commit is contained in:
parent
534b83420e
commit
baf8e8aac7
6 changed files with 502 additions and 4 deletions
4
go.mod
4
go.mod
|
|
@ -53,7 +53,7 @@ require (
|
||||||
github.com/mattn/go-sqlite3 v1.14.18
|
github.com/mattn/go-sqlite3 v1.14.18
|
||||||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
|
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
github.com/optimism-java/utp-go v0.0.0-20231225095152-5a9690d82b58
|
github.com/optimism-java/utp-go v0.0.0-20240117090415-3a5aad17f644
|
||||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
|
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
|
||||||
github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7
|
github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7
|
||||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7
|
github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7
|
||||||
|
|
@ -71,7 +71,7 @@ require (
|
||||||
golang.org/x/crypto v0.17.0
|
golang.org/x/crypto v0.17.0
|
||||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
|
||||||
golang.org/x/sync v0.5.0
|
golang.org/x/sync v0.5.0
|
||||||
golang.org/x/sys v0.15.0
|
golang.org/x/sys v0.16.0
|
||||||
golang.org/x/text v0.14.0
|
golang.org/x/text v0.14.0
|
||||||
golang.org/x/time v0.3.0
|
golang.org/x/time v0.3.0
|
||||||
golang.org/x/tools v0.15.0
|
golang.org/x/tools v0.15.0
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -494,6 +494,8 @@ github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsq
|
||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
github.com/optimism-java/utp-go v0.0.0-20231225095152-5a9690d82b58 h1:EZfd3NpJV+CL5vORquJ2O6eAUjkpI7+ge9a9n9HMyYE=
|
github.com/optimism-java/utp-go v0.0.0-20231225095152-5a9690d82b58 h1:EZfd3NpJV+CL5vORquJ2O6eAUjkpI7+ge9a9n9HMyYE=
|
||||||
github.com/optimism-java/utp-go v0.0.0-20231225095152-5a9690d82b58/go.mod h1:DZ0jYzLzt4ZsCmhI/iqYgGFoNx45OfpEoKzXB8HVALQ=
|
github.com/optimism-java/utp-go v0.0.0-20231225095152-5a9690d82b58/go.mod h1:DZ0jYzLzt4ZsCmhI/iqYgGFoNx45OfpEoKzXB8HVALQ=
|
||||||
|
github.com/optimism-java/utp-go v0.0.0-20240117090415-3a5aad17f644 h1:vrYEqCVnDS/Z3lLQa+GBrXRtIHN948TWy+aw04O9dpQ=
|
||||||
|
github.com/optimism-java/utp-go v0.0.0-20240117090415-3a5aad17f644/go.mod h1:DZ0jYzLzt4ZsCmhI/iqYgGFoNx45OfpEoKzXB8HVALQ=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM=
|
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM=
|
||||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
||||||
|
|
@ -797,6 +799,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||||
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,7 @@ func (p *PortalProtocol) setupUDPListening() (*net.UDPConn, error) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: ZAP PRODUCTION LOG
|
// TODO: ZAP PRODUCTION LOG
|
||||||
logger, err := zap.NewDevelopmentConfig().Build()
|
logger, err := zap.NewProductionConfig().Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1355,7 +1355,7 @@ func (p *PortalProtocol) ContentLookup(contentKey []byte) ([]byte, error) {
|
||||||
lookupContext, cancel := context.WithCancel(context.Background())
|
lookupContext, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
resChan := make(chan []byte, 1)
|
resChan := make(chan []byte, 1)
|
||||||
|
defer close(resChan)
|
||||||
newLookup(lookupContext, p.table, p.Self().ID(), func(n *node) ([]*node, error) {
|
newLookup(lookupContext, p.table, p.Self().ID(), func(n *node) ([]*node, error) {
|
||||||
return p.contentLookupWorker(unwrapNode(n), contentKey, resChan)
|
return p.contentLookupWorker(unwrapNode(n), contentKey, resChan)
|
||||||
}).run()
|
}).run()
|
||||||
|
|
@ -1390,6 +1390,26 @@ func (p *PortalProtocol) contentLookupWorker(n *enode.Node, contentKey []byte, r
|
||||||
return wrapedNode, nil
|
return wrapedNode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PortalProtocol) ToContentId(contentKey []byte) []byte {
|
||||||
|
return p.toContentId(contentKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PortalProtocol) InRange(contentId []byte) bool {
|
||||||
|
return inRange(p.Self().ID(), p.nodeRadius, contentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PortalProtocol) Get(contentId []byte) ([]byte, error) {
|
||||||
|
return p.storage.Get(contentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PortalProtocol) Put(contentId []byte, content []byte) error {
|
||||||
|
return p.storage.Put(contentId, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PortalProtocol) GetContent() <-chan *ContentElement {
|
||||||
|
return p.contentQueue
|
||||||
|
}
|
||||||
|
|
||||||
func inRange(nodeId enode.ID, nodeRadius *uint256.Int, contentId []byte) bool {
|
func inRange(nodeId enode.ID, nodeRadius *uint256.Int, contentId []byte) bool {
|
||||||
distance := enode.LogDist(nodeId, enode.ID(contentId))
|
distance := enode.LogDist(nodeId, enode.ID(contentId))
|
||||||
disBig := new(big.Int).SetInt64(int64(distance))
|
disBig := new(big.Int).SetInt64(int64(distance))
|
||||||
|
|
|
||||||
203
portalnetwork/history/history_network.go
Normal file
203
portalnetwork/history/history_network.go
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
package history
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
"github.com/ethereum/go-ethereum/portalnetwork/storage"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContentType byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
BlockHeaderType ContentType = 0x00
|
||||||
|
BlockBodyType ContentType = 0x01
|
||||||
|
ReceiptsType ContentType = 0x02
|
||||||
|
EpochAccumulatorType ContentType = 0x03
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrWithdrawalHashIsNotEqual = errors.New("withdrawals hash is not equal")
|
||||||
|
ErrTxHashIsNotEqual = errors.New("tx hash is not equal")
|
||||||
|
ErrUnclesHashIsNotEqual = errors.New("uncles hash is not equal")
|
||||||
|
ErrReceiptsHashIsNotEqual = errors.New("receipts hash is not equal")
|
||||||
|
ErrContentOutOfRange = errors.New("content out of range")
|
||||||
|
ErrHeaderWithProofIsInvalid = errors.New("header proof is invalid")
|
||||||
|
ErrInvalidBlockHash = errors.New("invalid block hash")
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContentKey struct {
|
||||||
|
selector ContentType
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContentKey(selector ContentType, hash []byte) *ContentKey {
|
||||||
|
return &ContentKey{
|
||||||
|
selector: selector,
|
||||||
|
data: hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ContentKey) encode() []byte {
|
||||||
|
res := make([]byte, 0, len(c.data)+1)
|
||||||
|
res = append(res, byte(c.selector))
|
||||||
|
res = append(res, c.data...)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
type HistoryNetwork struct {
|
||||||
|
portalProtocol *discover.PortalProtocol
|
||||||
|
masterAccumulator *MasterAccumulator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHistoryNetwork(portalProtocol *discover.PortalProtocol, accu *MasterAccumulator) *HistoryNetwork {
|
||||||
|
return &HistoryNetwork{
|
||||||
|
portalProtocol: portalProtocol,
|
||||||
|
masterAccumulator: accu,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HistoryNetwork) Start() error {
|
||||||
|
err := h.portalProtocol.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go h.processContentLoop()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently doing 4 retries on lookups but only when the validation fails.
|
||||||
|
const requestRetries = 4
|
||||||
|
|
||||||
|
func (h *HistoryNetwork) GetBlockHeader(blockHash []byte) (*types.Header, error) {
|
||||||
|
contentKey := newContentKey(BlockHeaderType, blockHash).encode()
|
||||||
|
contentId := h.portalProtocol.ToContentId(contentKey)
|
||||||
|
if !h.portalProtocol.InRange(contentId) {
|
||||||
|
return nil, ErrContentOutOfRange
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := h.portalProtocol.Get(contentId)
|
||||||
|
// other error
|
||||||
|
if err != nil && err != storage.ErrContentNotFound {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// no error
|
||||||
|
if err == nil {
|
||||||
|
blockHeaderWithProof, err := DecodeBlockHeaderWithProof(res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
header := new(types.Header)
|
||||||
|
err = rlp.DecodeBytes(blockHeaderWithProof.Header, header)
|
||||||
|
return header, err
|
||||||
|
}
|
||||||
|
// no content in local storage
|
||||||
|
for retries := 0; retries < requestRetries; retries++ {
|
||||||
|
// TODO log the err and continue
|
||||||
|
content, err := h.portalProtocol.ContentLookup(contentKey)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
headerWithProof, err := DecodeBlockHeaderWithProof(content)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
header, err := ValidateBlockHeaderBytes(headerWithProof.Header, blockHash)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
valid, err := h.verifyHeader(header, *headerWithProof.Proof)
|
||||||
|
if err != nil || !valid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// TODO handle the error
|
||||||
|
_ = h.portalProtocol.Put(contentId, content)
|
||||||
|
return header, nil
|
||||||
|
}
|
||||||
|
return nil, storage.ErrContentNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HistoryNetwork) verifyHeader(header *types.Header, proof BlockHeaderProof) (bool, error) {
|
||||||
|
return h.masterAccumulator.VerifyHeader(*header, proof)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HistoryNetwork) processContentLoop() {
|
||||||
|
contentChan := h.portalProtocol.GetContent()
|
||||||
|
for contentElement := range contentChan {
|
||||||
|
err := h.validateContents(contentElement.ContentKeys, contentElement.Contents)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// TODO gossip the validate content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HistoryNetwork) validateContent(contentKey []byte, content []byte) error {
|
||||||
|
switch ContentType(contentKey[0]) {
|
||||||
|
case BlockHeaderType:
|
||||||
|
headerWithProof, err := DecodeBlockHeaderWithProof(content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
header, err := ValidateBlockHeaderBytes(headerWithProof.Header, contentKey[1:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
valid, err := h.verifyHeader(header, *headerWithProof.Proof)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !valid {
|
||||||
|
return ErrHeaderWithProofIsInvalid
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
case BlockBodyType:
|
||||||
|
// TODO
|
||||||
|
case ReceiptsType:
|
||||||
|
// TODO
|
||||||
|
case EpochAccumulatorType:
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
return errors.New("unknown content type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HistoryNetwork) validateContents(contentKeys [][]byte, contents [][]byte) error {
|
||||||
|
for i, content := range contents {
|
||||||
|
contentKey := contentKeys[i]
|
||||||
|
err := h.validateContent(contentKey, content)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("content validate failed with content key %v", contentKey)
|
||||||
|
}
|
||||||
|
contentId := h.portalProtocol.ToContentId(contentKey)
|
||||||
|
_ = h.portalProtocol.Put(contentId, content)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateBlockHeaderBytes(headerBytes []byte, blockHash []byte) (*types.Header, error) {
|
||||||
|
header := new(types.Header)
|
||||||
|
err := rlp.DecodeBytes(headerBytes, header)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if header.ExcessBlobGas != nil {
|
||||||
|
return nil, errors.New("EIP-4844 not yet implemented")
|
||||||
|
}
|
||||||
|
hash := header.Hash()
|
||||||
|
if !bytes.Equal(hash[:], blockHash) {
|
||||||
|
return nil, ErrInvalidBlockHash
|
||||||
|
}
|
||||||
|
return header, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeBlockHeaderWithProof(content []byte) (*BlockHeaderWithProof, error) {
|
||||||
|
headerWithProof := new(BlockHeaderWithProof)
|
||||||
|
err := headerWithProof.UnmarshalSSZ(content)
|
||||||
|
return headerWithProof, err
|
||||||
|
}
|
||||||
257
portalnetwork/history/history_network_test.go
Normal file
257
portalnetwork/history/history_network_test.go
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
package history
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/discover/portalwire"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
|
"github.com/ethereum/go-ethereum/portalnetwork/storage"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ContentId(contentKey []byte) []byte {
|
||||||
|
digest := sha256.Sum256(contentKey)
|
||||||
|
return digest[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// testcases from https://github.com/ethereum/portal-network-specs/blob/master/content-keys-test-vectors.md
|
||||||
|
func TestContentKey(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
hash string
|
||||||
|
contentKey string
|
||||||
|
contentIdHex string
|
||||||
|
contentIdU256 string
|
||||||
|
selector ContentType
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "block header key",
|
||||||
|
hash: "d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d",
|
||||||
|
contentKey: "00d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d",
|
||||||
|
contentIdHex: "3e86b3767b57402ea72e369ae0496ce47cc15be685bec3b4726b9f316e3895fe",
|
||||||
|
contentIdU256: "28281392725701906550238743427348001871342819822834514257505083923073246729726",
|
||||||
|
selector: BlockHeaderType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "block body key",
|
||||||
|
hash: "d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d",
|
||||||
|
contentKey: "01d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d",
|
||||||
|
contentIdHex: "ebe414854629d60c58ddd5bf60fd72e41760a5f7a463fdcb169f13ee4a26786b",
|
||||||
|
contentIdU256: "106696502175825986237944249828698290888857178633945273402044845898673345165419",
|
||||||
|
selector: BlockBodyType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "receipt key",
|
||||||
|
hash: "d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d",
|
||||||
|
contentKey: "02d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d",
|
||||||
|
contentIdHex: "a888f4aafe9109d495ac4d4774a6277c1ada42035e3da5e10a04cc93247c04a4",
|
||||||
|
contentIdU256: "76230538398907151249589044529104962263309222250374376758768131420767496438948",
|
||||||
|
selector: ReceiptsType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "epoch accumelator key",
|
||||||
|
hash: "e242814b90ed3950e13aac7e56ce116540c71b41d1516605aada26c6c07cc491",
|
||||||
|
contentKey: "03e242814b90ed3950e13aac7e56ce116540c71b41d1516605aada26c6c07cc491",
|
||||||
|
contentIdHex: "9fb2175e76c6989e0fdac3ee10c40d2a81eb176af32e1c16193e3904fe56896e",
|
||||||
|
contentIdU256: "72232402989179419196382321898161638871438419016077939952896528930608027961710",
|
||||||
|
selector: EpochAccumulatorType,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range testCases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
hashByte, err := hex.DecodeString(c.hash)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
contentKey := newContentKey(c.selector, hashByte).encode()
|
||||||
|
hexKey := hex.EncodeToString(contentKey)
|
||||||
|
require.Equal(t, hexKey, c.contentKey)
|
||||||
|
contentId := ContentId(contentKey)
|
||||||
|
require.Equal(t, c.contentIdHex, hex.EncodeToString(contentId))
|
||||||
|
|
||||||
|
bigNum := big.NewInt(0).SetBytes(contentId)
|
||||||
|
u256Format, isOverflow := uint256.FromBig(bigNum)
|
||||||
|
require.False(t, isOverflow)
|
||||||
|
u256Str := fmt.Sprint(u256Format)
|
||||||
|
require.Equal(t, u256Str, c.contentIdU256)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateHeader(t *testing.T) {
|
||||||
|
entrys, err := parseBlockHeaderKeyContent()
|
||||||
|
require.NoError(t, err)
|
||||||
|
historyNetwork, err := genHistoryNetwork(":7891", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, entry := range entrys {
|
||||||
|
err = historyNetwork.validateContent(entry.key, entry.value)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
headerWithProof, err := DecodeBlockHeaderWithProof(entry.value)
|
||||||
|
require.NoError(t, err)
|
||||||
|
// invalid blockhash
|
||||||
|
_, err = ValidateBlockHeaderBytes(headerWithProof.Header, entry.key)
|
||||||
|
require.Equal(t, ErrInvalidBlockHash, err)
|
||||||
|
header, err := ValidateBlockHeaderBytes(headerWithProof.Header, entry.key[1:])
|
||||||
|
require.NoError(t, err)
|
||||||
|
// wrong header number
|
||||||
|
header.Number = big.NewInt(0).Add(header.Number, big.NewInt(122))
|
||||||
|
valid, err := historyNetwork.verifyHeader(header, *headerWithProof.Proof)
|
||||||
|
require.False(t, valid)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetContentByKey(t *testing.T) {
|
||||||
|
historyNetwork1, err := genHistoryNetwork(":7895", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
historyNetwork2, err := genHistoryNetwork(":7896", []*enode.Node{historyNetwork1.portalProtocol.Self()})
|
||||||
|
require.NoError(t, err)
|
||||||
|
// wait node start
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
|
||||||
|
entryMap, err := parseDataForBlock14764013()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
headerEntry := entryMap["header"]
|
||||||
|
|
||||||
|
// test GetBlockHeader
|
||||||
|
// no content
|
||||||
|
header, err := historyNetwork2.GetBlockHeader(headerEntry.key[1:])
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Nil(t, header)
|
||||||
|
|
||||||
|
contentId := historyNetwork1.portalProtocol.ToContentId(headerEntry.key)
|
||||||
|
err = historyNetwork1.portalProtocol.Put(contentId, headerEntry.value)
|
||||||
|
require.NoError(t, err)
|
||||||
|
// get content from historyNetwork1
|
||||||
|
header, err = historyNetwork2.GetBlockHeader(headerEntry.key[1:])
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, header)
|
||||||
|
// get content from local
|
||||||
|
header, err = historyNetwork2.GetBlockHeader(headerEntry.key[1:])
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, header)
|
||||||
|
}
|
||||||
|
|
||||||
|
type contentEntry struct {
|
||||||
|
key []byte
|
||||||
|
value []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBlockHeaderKeyContent() ([]contentEntry, error) {
|
||||||
|
headWithProofBytes, err := os.ReadFile("./testdata/header_with_proofs.json")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
headerMap := make(map[string]map[string]string)
|
||||||
|
|
||||||
|
err = json.Unmarshal(headWithProofBytes, &headerMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res := make([]contentEntry, 0)
|
||||||
|
for _, v := range headerMap {
|
||||||
|
entry := contentEntry{}
|
||||||
|
val := v["value"]
|
||||||
|
bytes, err := hexutil.Decode(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
entry.value = bytes
|
||||||
|
key := v["content_key"]
|
||||||
|
keyBytes, err := hexutil.Decode(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
entry.key = keyBytes
|
||||||
|
res = append(res, entry)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockStorage struct {
|
||||||
|
db map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockStorage) Get(contentId []byte) ([]byte, error) {
|
||||||
|
if content, ok := m.db[string(contentId)]; ok {
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
return nil, storage.ErrContentNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockStorage) Put(contentId []byte, content []byte) error {
|
||||||
|
m.db[string(contentId)] = content
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func genHistoryNetwork(addr string, bootNodes []*enode.Node) (*HistoryNetwork, error) {
|
||||||
|
conf := discover.DefaultPortalProtocolConfig()
|
||||||
|
if addr != "" {
|
||||||
|
conf.ListenAddr = addr
|
||||||
|
}
|
||||||
|
if bootNodes != nil {
|
||||||
|
conf.BootstrapNodes = bootNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
contentQueue := make(chan *discover.ContentElement, 50)
|
||||||
|
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
panic("couldn't generate key: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
portalProtocol, err := discover.NewPortalProtocol(conf, portalwire.HistoryNetwork, key, &MockStorage{db: make(map[string][]byte)}, contentQueue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
accu, err := NewMasterAccumulator()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = portalProtocol.Start()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHistoryNetwork(portalProtocol, &accu), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDataForBlock14764013() (map[string]contentEntry, error) {
|
||||||
|
content, err := os.ReadFile("./testdata/block_14764013.json")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
contentMap := make(map[string]map[string]string)
|
||||||
|
json.Unmarshal(content, &contentMap)
|
||||||
|
res := make(map[string]contentEntry)
|
||||||
|
for key, val := range contentMap {
|
||||||
|
entry := contentEntry{}
|
||||||
|
contentKey := val["content_key"]
|
||||||
|
entry.key, err = hexutil.Decode(contentKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
entry.value, err = hexutil.Decode(val["content_value"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res[key] = entry
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
14
portalnetwork/history/testdata/block_14764013.json
vendored
Normal file
14
portalnetwork/history/testdata/block_14764013.json
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue