Skip to content

Commit

Permalink
implement trust protocol and verify node
Browse files Browse the repository at this point in the history
  • Loading branch information
keefel committed Jan 26, 2022
1 parent 3f266bf commit 885aeb9
Show file tree
Hide file tree
Showing 19 changed files with 785 additions and 13 deletions.
15 changes: 15 additions & 0 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,21 @@ func (a *Address) UnmarshalGraphQL(input interface{}) error {
return err
}

// AddressSlice is used for sort
type AddressSlice []Address

func (s AddressSlice) Len() int {
return len(s)
}

func (s AddressSlice) Less(i, j int) bool {
return s[i].Hex() < s[j].Hex()
}

func (s AddressSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

// UnprefixedAddress allows marshaling an Address without 0x prefix.
type UnprefixedAddress Address

Expand Down
114 changes: 114 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"sync/atomic"
"time"

"golang.org/x/crypto/sha3"

lru "github.com/hashicorp/golang-lru"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -1687,6 +1689,12 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
diffLayer.Receipts = receipts
diffLayer.BlockHash = block.Hash()
diffLayer.Number = block.NumberU64()

sort.Sort(types.DiffCodeSlice(diffLayer.Codes))
sort.Sort(common.AddressSlice(diffLayer.Destructs))
sort.Sort(types.DiffAccountSlice(diffLayer.Accounts))
sort.Sort(types.DiffStorageSlice(diffLayer.Storages))

bc.cacheDiffLayer(diffLayer)
}
triedb := bc.stateCache.TrieDB()
Expand Down Expand Up @@ -3026,3 +3034,109 @@ func EnablePersistDiff(limit uint64) BlockChainOption {
return chain
}
}

func (bc *BlockChain) GetRootByDiffHash(blockNumber uint64, blockHash common.Hash, diffHash common.Hash) (*types.VerifyResult, error) {
var res types.VerifyResult
res.BlockNumber = blockNumber
res.BlockHash = blockHash

if blockNumber > bc.CurrentHeader().Number.Uint64()+11 {
res.Status = types.StatusBlockTooNew
return &res, nil
} else if blockNumber > bc.CurrentHeader().Number.Uint64() {
res.Status = types.StatusBlockNewer
return &res, nil
}

diff := bc.GetTrustedDiffLayer(blockHash)
if diff != nil {
if diff.DiffHash == (common.Hash{}) {
hash, err := GetTrustedDiffHash(diff)
if err != nil {
res.Status = types.StatusUnexpectedError
return &res, err
}

diff.DiffHash = hash
}

if diffHash != diff.DiffHash {
res.Status = types.StatusDiffHashMismatch
return &res, nil
}

header := bc.GetHeaderByHash(blockHash)
if header == nil {
res.Status = types.StatusUnexpectedError
return &res, fmt.Errorf("unexpected error, header not found")
}
res.Status = types.StatusFullVerified
res.Root = header.Root
return &res, nil
}

header := bc.GetHeaderByHash(blockHash)
if header == nil {
if blockNumber > bc.CurrentHeader().Number.Uint64()-11 {
res.Status = types.StatusPossibleFork
return &res, nil
}

res.Status = types.StatusImpossibleFork
return &res, nil
}

res.Status = types.StatusUntrustedVerified
res.Root = header.Root
return &res, nil
}

func (bc *BlockChain) GetTrustedDiffLayer(blockHash common.Hash) *types.DiffLayer {
var diff *types.DiffLayer
if cached, ok := bc.diffLayerCache.Get(blockHash); ok {
diff = cached.(*types.DiffLayer)
return diff
}

diffStore := bc.db.DiffStore()
if diffStore != nil {
diff = rawdb.ReadDiffLayer(diffStore, blockHash)
}
return diff
}

func GetTrustedDiffHash(d *types.DiffLayer) (common.Hash, error) {
diff := &types.ExtDiffLayer{
BlockHash: d.BlockHash,
Receipts: make([]*types.ReceiptForStorage, 0),
Number: d.Number,
Codes: d.Codes,
Destructs: d.Destructs,
Accounts: d.Accounts,
Storages: d.Storages,
}

for index, account := range diff.Accounts {
full, err := snapshot.FullAccount(account.Blob)
if err != nil {
return common.Hash{}, fmt.Errorf("decode full account error: %v", err)
}
// set account root to empty root
diff.Accounts[index].Blob = snapshot.SlimAccountRLP(full.Nonce, full.Balance, common.Hash{}, full.CodeHash)
}

rawData, err := rlp.EncodeToBytes(diff)
if err != nil {
return common.Hash{}, fmt.Errorf("encode new diff error: %v", err)
}

hasher := sha3.NewLegacyKeccak256()
_, err = hasher.Write(rawData)
if err != nil {
return common.Hash{}, fmt.Errorf("hasher write error: %v", err)
}

var hash common.Hash
hasher.Sum(hash[:0])
return hash, nil
}
7 changes: 7 additions & 0 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,13 @@ func (s *StateObject) updateTrie(db Database) Trie {

// UpdateRoot sets the trie root to the current root hash of
func (s *StateObject) updateRoot(db Database) {
// If node runs in no trie mode, set root to empty.
defer func() {
if db.NoTries() {
s.data.Root = common.Hash{}
}
}()

// If nothing changed, don't bother with hashing anything
if s.updateTrie(db) == nil {
return
Expand Down
6 changes: 5 additions & 1 deletion core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -1442,8 +1442,12 @@ func (s *StateDB) SnapToDiffLayer() ([]common.Address, []types.DiffAccount, []ty
for accountHash, storage := range s.snapStorage {
keys := make([]string, 0, len(storage))
values := make([][]byte, 0, len(storage))
for k, v := range storage {
for k, _ := range storage {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
v := storage[k]
values = append(values, v)
}
storages = append(storages, types.DiffStorage{
Expand Down
89 changes: 86 additions & 3 deletions core/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,40 @@ var (
EmptyUncleHash = rlpHash([]*Header(nil))
)

type VerifyStatus struct {
Code uint16
Msg string
}

var (
// StatusVerified means the processing of request going as expected and found the root correctly.
StatusVerified = VerifyStatus{Code: 0x100}
StatusFullVerified = VerifyStatus{Code: 0x101, Msg: "state root full verified"}
StatusUntrustedVerified = VerifyStatus{Code: 0x102, Msg: "state root untrusted verified, because of difflayer not found"}

// StatusFailed means the request has something wrong.
StatusFailed = VerifyStatus{Code: 0x200}
StatusDiffHashMismatch = VerifyStatus{Code: 0x201, Msg: "verify failed because of blockhash mismatch with diffhash"}
StatusImpossibleFork = VerifyStatus{Code: 0x202, Msg: "verify failed because of impossible fork detected"}

// StatusUncertain means verify node can't give a certain result of the request.
StatusUncertain = VerifyStatus{Code: 0x300}
StatusBlockTooNew = VerifyStatus{Code: 0x301, Msg: "can’t verify because of block number larger than current height more than 11"}
StatusBlockNewer = VerifyStatus{Code: 0x302, Msg: "can’t verify because of block number larger than current height"}
StatusPossibleFork = VerifyStatus{Code: 0x303, Msg: "can’t verify because of possible fork detected"}
StatusRequestTooBusy = VerifyStatus{Code: 0x304, Msg: "can’t verify because of request too busy"}

// StatusUnexpectedError is unexpected internal error.
StatusUnexpectedError = VerifyStatus{Code: 0x400, Msg: "can’t verify because of unexpected internal error"}
)

type VerifyResult struct {
Status VerifyStatus
BlockNumber uint64
BlockHash common.Hash
Root common.Hash
}

// A BlockNonce is a 64-bit hash which proves (combined with the
// mix-hash) that a sufficient amount of computation has been carried
// out on a block.
Expand Down Expand Up @@ -383,7 +417,7 @@ type DiffLayer struct {
DiffHash common.Hash
}

type extDiffLayer struct {
type ExtDiffLayer struct {
BlockHash common.Hash
Number uint64
Receipts []*ReceiptForStorage // Receipts are duplicated stored to simplify the logic
Expand All @@ -395,7 +429,7 @@ type extDiffLayer struct {

// DecodeRLP decodes the Ethereum
func (d *DiffLayer) DecodeRLP(s *rlp.Stream) error {
var ed extDiffLayer
var ed ExtDiffLayer
if err := s.Decode(&ed); err != nil {
return err
}
Expand All @@ -415,7 +449,7 @@ func (d *DiffLayer) EncodeRLP(w io.Writer) error {
for i, receipt := range d.Receipts {
storageReceipts[i] = (*ReceiptForStorage)(receipt)
}
return rlp.Encode(w, extDiffLayer{
return rlp.Encode(w, ExtDiffLayer{
BlockHash: d.BlockHash,
Number: d.Number,
Receipts: storageReceipts,
Expand Down Expand Up @@ -443,17 +477,66 @@ type DiffCode struct {
Code []byte
}

// DiffCodeSlice is used for sort
type DiffCodeSlice []DiffCode

func (s DiffCodeSlice) Len() int {
return len(s)
}

func (s DiffCodeSlice) Less(i, j int) bool {
return s[i].Hash.Hex() < s[j].Hash.Hex()
}

func (s DiffCodeSlice) Swap(i, j int) {
s[i].Hash, s[j].Hash = s[j].Hash, s[i].Hash
s[i].Code, s[j].Code = s[j].Code, s[i].Code
}

type DiffAccount struct {
Account common.Address
Blob []byte
}

// DiffAccountSlice is used for sort
type DiffAccountSlice []DiffAccount

func (s DiffAccountSlice) Len() int {
return len(s)
}

func (s DiffAccountSlice) Less(i, j int) bool {
return s[i].Account.Hex() < s[j].Account.Hex()
}

func (s DiffAccountSlice) Swap(i, j int) {
s[i].Account, s[j].Account = s[j].Account, s[i].Account
s[i].Blob, s[j].Blob = s[j].Blob, s[i].Blob
}

type DiffStorage struct {
Account common.Address
Keys []string
Vals [][]byte
}

// DiffStorageSlice is used for sort
type DiffStorageSlice []DiffStorage

func (s DiffStorageSlice) Len() int {
return len(s)
}

func (s DiffStorageSlice) Less(i, j int) bool {
return s[i].Account.Hex() < s[j].Account.Hex()
}

func (s DiffStorageSlice) Swap(i, j int) {
s[i].Account, s[j].Account = s[j].Account, s[i].Account
s[i].Keys, s[j].Keys = s[j].Keys, s[i].Keys
s[i].Vals, s[j].Vals = s[j].Vals, s[i].Vals
}

type DiffAccountsInTx struct {
TxHash common.Hash
Accounts map[common.Address]*big.Int
Expand Down
3 changes: 3 additions & 0 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"sync/atomic"
"time"

"github.com/ethereum/go-ethereum/eth/protocols/trust"

"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
Expand Down Expand Up @@ -551,6 +553,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
}
// diff protocol can still open without snap protocol
protos = append(protos, diff.MakeProtocols((*diffHandler)(s.handler), s.snapDialCandidates)...)
protos = append(protos, trust.MakeProtocols((*trustHandler)(s.handler), s.snapDialCandidates)...)
return protos
}

Expand Down
24 changes: 23 additions & 1 deletion eth/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"sync/atomic"
"time"

"github.com/ethereum/go-ethereum/eth/protocols/trust"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid"
Expand Down Expand Up @@ -269,6 +271,11 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
peer.Log().Error("Diff extension barrier failed", "err", err)
return err
}
trust, err := h.peers.waitTrustExtension(peer)
if err != nil {
peer.Log().Error("Trust extension barrier failed", "err", err)
return err
}
// TODO(karalabe): Not sure why this is needed
if !h.chainSync.handlePeerEvent(peer) {
return p2p.DiscQuitting
Expand Down Expand Up @@ -309,7 +316,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
peer.Log().Debug("Ethereum peer connected", "name", peer.Name())

// Register the peer locally
if err := h.peers.registerPeer(peer, snap, diff); err != nil {
if err := h.peers.registerPeer(peer, snap, diff, trust); err != nil {
peer.Log().Error("Ethereum peer registration failed", "err", err)
return err
}
Expand Down Expand Up @@ -395,6 +402,21 @@ func (h *handler) runDiffExtension(peer *diff.Peer, handler diff.Handler) error
return handler(peer)
}

// runTrustExtension registers a `trust` peer into the joint eth/trust peerset and
// starts handling inbound messages. As `trust` is only a satellite protocol to
// `eth`, all subsystem registrations and lifecycle management will be done by
// the main `eth` handler to prevent strange races.
func (h *handler) runTrustExtension(peer *trust.Peer, handler trust.Handler) error {
h.peerWG.Add(1)
defer h.peerWG.Done()

if err := h.peers.registerTrustExtension(peer); err != nil {
peer.Log().Error("Trust extension registration failed", "err", err)
return err
}
return handler(peer)
}

// removePeer unregisters a peer from the downloader and fetchers, removes it from
// the set of tracked peers and closes the network connection to it.
func (h *handler) removePeer(id string) {
Expand Down
Loading

0 comments on commit 885aeb9

Please sign in to comment.