Merge branch 'master' into gethv1.10.15-merge

This commit is contained in:
Shivam Sharma 2022-02-02 12:58:44 +05:30
commit a6c5473434
68 changed files with 1807 additions and 385 deletions

View file

@ -20,7 +20,7 @@ builds:
- netgo
ldflags:
-s -w
- id: darwin-arm64
main: ./cmd/geth
binary: bor
@ -35,7 +35,7 @@ builds:
- netgo
ldflags:
-s -w
- id: linux-amd64
main: ./cmd/geth
binary: bor
@ -49,7 +49,7 @@ builds:
tags:
- netgo
ldflags:
# We need to build a static binary because we are building in a glibc based system and running in a musl container
# We need to build a static binary because we are building in a glibc based system and running in a musl container
-s -w -extldflags "-static"
- id: linux-arm64
@ -65,7 +65,7 @@ builds:
tags:
- netgo
ldflags:
# We need to build a static binary because we are building in a glibc based system and running in a musl container
# We need to build a static binary because we are building in a glibc based system and running in a musl container
-s -w -extldflags "-static"
nfpms:
@ -112,7 +112,7 @@ dockers:
extra_files:
- builder/files/genesis-mainnet-v1.json
- builder/files/genesis-testnet-v4.json
- image_templates:
- 0xpolygon/{{ .ProjectName }}:{{ .Version }}-arm64
dockerfile: Dockerfile.release

View file

@ -44,10 +44,9 @@ ios:
@echo "Done building."
@echo "Import \"$(GOBIN)/Geth.framework\" to use the library."
test: all
# $(GORUN) build/ci.go test
go test github.com/ethereum/go-ethereum/consensus/bor -v
go test github.com/ethereum/go-ethereum/tests/bor -v
test:
# Skip mobile and cmd tests since they are being deprecated
go test -v $$(go list ./... | grep -v go-ethereum/cmd/)
lint: ## Run linters.
$(GORUN) build/ci.go lint

11
cmd/cli/main.go Normal file
View file

@ -0,0 +1,11 @@
package main
import (
"os"
"github.com/ethereum/go-ethereum/internal/cli"
)
func main() {
os.Exit(cli.Run(os.Args[1:]))
}

View file

@ -214,6 +214,7 @@ type BlockChain struct {
borReceiptsCache *lru.Cache // Cache for the most recent bor receipt receipts per block
stateSyncData []*types.StateSyncData // State sync data
stateSyncFeed event.Feed // State sync feed
chain2HeadFeed event.Feed // Reorg/NewHead/Fork data feed
}
// NewBlockChain returns a fully initialised block chain using information
@ -1368,10 +1369,21 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types
for _, data := range bc.stateSyncData {
bc.stateSyncFeed.Send(StateSyncEvent{Data: data})
}
bc.chain2HeadFeed.Send(Chain2HeadEvent{
Type: Chain2HeadCanonicalEvent,
NewChain: []*types.Block{block},
})
// BOR
}
} else {
bc.chainSideFeed.Send(ChainSideEvent{Block: block})
bc.chain2HeadFeed.Send(Chain2HeadEvent{
Type: Chain2HeadForkEvent,
NewChain: []*types.Block{block},
})
}
return status, nil
}
@ -1455,6 +1467,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
defer func() {
if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
bc.chainHeadFeed.Send(ChainHeadEvent{lastCanon})
bc.chain2HeadFeed.Send(Chain2HeadEvent{
Type: Chain2HeadCanonicalEvent,
NewChain: []*types.Block{lastCanon},
})
}
}()
// Start the parallel header verifier
@ -2065,6 +2082,13 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
}
// Ensure the user sees large reorgs
if len(oldChain) > 0 && len(newChain) > 0 {
bc.chain2HeadFeed.Send(Chain2HeadEvent{
Type: Chain2HeadReorgEvent,
NewChain: newChain,
OldChain: oldChain,
})
logFn := log.Info
msg := "Chain reorg detected"
if len(oldChain) > 63 {

134
core/blockchain_bor_test.go Normal file
View file

@ -0,0 +1,134 @@
package core
import (
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
func TestChain2HeadEvent(t *testing.T) {
var (
db = rawdb.NewMemoryDatabase()
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
gspec = &Genesis{
Config: params.TestChainConfig,
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}},
}
genesis = gspec.MustCommit(db)
signer = types.LatestSigner(gspec.Config)
)
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
defer blockchain.Stop()
chain2HeadCh := make(chan Chain2HeadEvent, 64)
blockchain.SubscribeChain2HeadEvent(chain2HeadCh)
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert chain: %v", err)
}
replacementBlocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, gen *BlockGen) {
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, nil), signer, key1)
if i == 2 {
gen.OffsetTime(-9)
}
if err != nil {
t.Fatalf("failed to create tx: %v", err)
}
gen.AddTx(tx)
})
if _, err := blockchain.InsertChain(replacementBlocks); err != nil {
t.Fatalf("failed to insert chain: %v", err)
}
type eventTest struct {
Type string
Added []common.Hash
Removed []common.Hash
}
readEvent := func(expect *eventTest) {
select {
case ev := <-chain2HeadCh:
if ev.Type != expect.Type {
t.Fatal("Type mismatch")
}
if len(ev.NewChain) != len(expect.Added) {
t.Fatal("Newchain and Added Array Size don't match")
}
if len(ev.OldChain) != len(expect.Removed) {
t.Fatal("Oldchain and Removed Array Size don't match")
}
for j := 0; j < len(ev.OldChain); j++ {
if ev.OldChain[j].Hash() != expect.Removed[j] {
t.Fatal("Oldchain hashes Do Not Match")
}
}
for j := 0; j < len(ev.NewChain); j++ {
if ev.NewChain[j].Hash() != expect.Added[j] {
t.Fatal("Newchain hashes Do Not Match")
}
}
case <-time.After(2 * time.Second):
t.Fatal("timeout")
}
}
// head event
readEvent(&eventTest{
Type: Chain2HeadCanonicalEvent,
Added: []common.Hash{
chain[2].Hash(),
}})
// fork event
readEvent(&eventTest{
Type: Chain2HeadForkEvent,
Added: []common.Hash{
replacementBlocks[0].Hash(),
}})
// fork event
readEvent(&eventTest{
Type: Chain2HeadForkEvent,
Added: []common.Hash{
replacementBlocks[1].Hash(),
}})
// reorg event
//In this event the channel recieves an array of Blocks in NewChain and OldChain
readEvent(&eventTest{
Type: Chain2HeadReorgEvent,
Added: []common.Hash{
replacementBlocks[2].Hash(),
replacementBlocks[1].Hash(),
replacementBlocks[0].Hash(),
},
Removed: []common.Hash{
chain[2].Hash(),
chain[1].Hash(),
chain[0].Hash(),
},
})
// head event
readEvent(&eventTest{
Type: Chain2HeadCanonicalEvent,
Added: []common.Hash{
replacementBlocks[3].Hash(),
}})
}

View file

@ -376,6 +376,11 @@ func (bc *BlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Su
return bc.scope.Track(bc.chainHeadFeed.Subscribe(ch))
}
// SubscribeChain2HeadEvent registers a subscription of ChainHeadEvent. ()
func (bc *BlockChain) SubscribeChain2HeadEvent(ch chan<- Chain2HeadEvent) event.Subscription {
return bc.scope.Track(bc.chain2HeadFeed.Subscribe(ch))
}
// SubscribeChainSideEvent registers a subscription of ChainSideEvent.
func (bc *BlockChain) SubscribeChainSideEvent(ch chan<- ChainSideEvent) event.Subscription {
return bc.scope.Track(bc.chainSideFeed.Subscribe(ch))

View file

@ -8,3 +8,16 @@ import (
type StateSyncEvent struct {
Data *types.StateSyncData
}
var (
Chain2HeadReorgEvent = "reorg"
Chain2HeadCanonicalEvent = "head"
Chain2HeadForkEvent = "fork"
)
// For tracking reorgs related information
type Chain2HeadEvent struct {
NewChain []*types.Block
OldChain []*types.Block
Type string
}

View file

@ -152,8 +152,10 @@ func newFreezer(datadir string, namespace string, readonly bool, maxTableSize ui
// This way they don't have to sync again from block 0 and still be compatible
// for block logs for future blocks. Note that already synced nodes
// won't have past block logs. Newly synced node will have all the data.
if err := freezer.tables[freezerBorReceiptTable].Fill(freezer.tables[freezerHeaderTable].items); err != nil {
return nil, err
if _, ok := freezer.tables[freezerBorReceiptTable]; ok {
if err := freezer.tables[freezerBorReceiptTable].Fill(freezer.tables[freezerHeaderTable].items); err != nil {
return nil, err
}
}
// Truncate all tables to common length.

View file

@ -55,6 +55,7 @@ func TestStateProcessorErrors(t *testing.T) {
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
Ethash: new(params.EthashConfig),
Bor: &params.BorConfig{BurntContract: map[string]string{"0": "0x000000000000000000000000000000000000dead"}},
}
signer = types.LatestSigner(config)
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")

View file

@ -31,4 +31,6 @@
- [```status```](./status.md)
- [```chain watch```](./chain_watch.md)
- [```version```](./version.md)

3
docs/cli/chain_watch.md Normal file
View file

@ -0,0 +1,3 @@
# Chain watch
The ```chain watch``` command is used to view the chainHead, reorg and fork events in real-time.

View file

@ -67,3 +67,8 @@ func (b *EthAPIBackend) GetBorBlockTransactionWithBlockHash(ctx context.Context,
func (b *EthAPIBackend) SubscribeStateSyncEvent(ch chan<- core.StateSyncEvent) event.Subscription {
return b.eth.BlockChain().SubscribeStateSyncEvent(ch)
}
// SubscribeChain2HeadEvent subscribes to reorg/head/fork event
func (b *EthAPIBackend) SubscribeChain2HeadEvent(ch chan<- core.Chain2HeadEvent) event.Subscription {
return b.eth.BlockChain().SubscribeChain2HeadEvent(ch)
}

View file

@ -254,7 +254,9 @@ func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, et
engine.(*ethash.Ethash).SetThreads(-1) // Disable CPU mining
}
// If Matic bor consensus is requested, set it up
if chainConfig.Bor != nil {
// In order to pass the ethereum transaction tests, we need to set the burn contract which is in the bor config
// Then, bor != nil will also be enabled for ethash and clique. Only enable Bor for real if there is a validator contract present.
if chainConfig.Bor != nil && chainConfig.Bor.ValidatorContract != "" {
return bor.New(chainConfig, db, blockchainAPI, ethConfig.HeimdallURL, ethConfig.WithoutHeimdall)
}
// Otherwise assume proof-of-work

View file

@ -35,7 +35,7 @@ import (
const sampleNumber = 3 // Number of transactions sampled in a block
var (
DefaultMaxPrice = big.NewInt(500 * params.GWei)
DefaultMaxPrice = big.NewInt(5000 * params.GWei)
DefaultIgnorePrice = big.NewInt(2 * params.Wei)
)

View file

@ -256,6 +256,8 @@ func generateTestChain() []*types.Block {
}
func TestEthClient(t *testing.T) {
t.Skip("bor due to burn contract")
backend, chain := newTestBackend(t)
client, _ := backend.Attach()
defer backend.Close()

View file

@ -89,6 +89,8 @@ func generateTestChain() (*core.Genesis, []*types.Block) {
}
func TestGethClient(t *testing.T) {
t.Skip("bor due to burn contract")
backend, _ := newTestBackend(t)
client, err := backend.Attach()
if err != nil {

View file

@ -57,6 +57,9 @@ const (
txChanSize = 4096
// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
chainHeadChanSize = 10
// chain2HeadChanSize is the size of channel listening to Chain2HeadEvent.
chain2HeadChanSize = 10
)
// backend encompasses the bare-minimum functionality needed for ethstats reporting
@ -68,6 +71,9 @@ type backend interface {
GetTd(ctx context.Context, hash common.Hash) *big.Int
Stats() (pending int, queued int)
SyncProgress() ethereum.SyncProgress
// Bor
SubscribeChain2HeadEvent(ch chan<- core.Chain2HeadEvent) event.Subscription
}
// fullNodeBackend encompasses the functionality necessary for a full node
@ -96,6 +102,9 @@ type Service struct {
headSub event.Subscription
txSub event.Subscription
//bor related sub
chain2headSub event.Subscription
}
// connWrapper is a wrapper to prevent concurrent-write or concurrent-read on the
@ -195,7 +204,9 @@ func (s *Service) Start() error {
s.headSub = s.backend.SubscribeChainHeadEvent(chainHeadCh)
txEventCh := make(chan core.NewTxsEvent, txChanSize)
s.txSub = s.backend.SubscribeNewTxsEvent(txEventCh)
go s.loop(chainHeadCh, txEventCh)
chain2HeadCh := make(chan core.Chain2HeadEvent, chain2HeadChanSize)
s.chain2headSub = s.backend.SubscribeChain2HeadEvent(chain2HeadCh)
go s.loop(chainHeadCh, chain2HeadCh, txEventCh)
log.Info("Stats daemon started")
return nil
@ -211,12 +222,13 @@ func (s *Service) Stop() error {
// loop keeps trying to connect to the netstats server, reporting chain events
// until termination.
func (s *Service) loop(chainHeadCh chan core.ChainHeadEvent, txEventCh chan core.NewTxsEvent) {
func (s *Service) loop(chainHeadCh chan core.ChainHeadEvent, chain2HeadCh chan core.Chain2HeadEvent, txEventCh chan core.NewTxsEvent) {
// Start a goroutine that exhausts the subscriptions to avoid events piling up
var (
quitCh = make(chan struct{})
headCh = make(chan *types.Block, 1)
txCh = make(chan struct{}, 1)
quitCh = make(chan struct{})
headCh = make(chan *types.Block, 1)
txCh = make(chan struct{}, 1)
head2Ch = make(chan core.Chain2HeadEvent, 100)
)
go func() {
var lastTx mclock.AbsTime
@ -231,6 +243,13 @@ func (s *Service) loop(chainHeadCh chan core.ChainHeadEvent, txEventCh chan core
default:
}
// Notify of chain2head events, but drop if too frequent
case chain2head := <-chain2HeadCh:
select {
case head2Ch <- chain2head:
default:
}
// Notify of new transaction events, but drop if too frequent
case <-txEventCh:
if time.Duration(mclock.Now()-lastTx) < time.Second {
@ -333,6 +352,12 @@ func (s *Service) loop(chainHeadCh chan core.ChainHeadEvent, txEventCh chan core
if err = s.reportPending(conn); err != nil {
log.Warn("Post-block transaction stats report failed", "err", err)
}
case chain2head := <-head2Ch:
if err = s.reportChain2Head(conn, &chain2head); err != nil {
log.Warn("Reorg stats report failed", "err", err)
}
case <-txCh:
if err = s.reportPending(conn); err != nil {
log.Warn("Transaction stats report failed", "err", err)
@ -750,6 +775,49 @@ func (s *Service) reportPending(conn *connWrapper) error {
return conn.WriteJSON(report)
}
type blockStub struct {
Hash string `json:"hash"`
Number uint64 `json:"number"`
ParentHash string `json:"parent_hash"`
}
func createStub(b *types.Block) *blockStub {
s := &blockStub{
Hash: b.Hash().String(),
ParentHash: b.ParentHash().String(),
Number: b.NumberU64(),
}
return s
}
type ChainHeadEvent struct {
NewChain []*blockStub `json:"added"`
OldChain []*blockStub `json:"removed"`
Type string `json:"type"`
}
// reportChain2Head checks for reorg and sends current head to stats server.
func (s *Service) reportChain2Head(conn *connWrapper, chain2HeadData *core.Chain2HeadEvent) error {
chainHeadEvent := ChainHeadEvent{
Type: chain2HeadData.Type,
}
for _, block := range chain2HeadData.NewChain {
chainHeadEvent.NewChain = append(chainHeadEvent.NewChain, createStub(block))
}
for _, block := range chain2HeadData.OldChain {
chainHeadEvent.OldChain = append(chainHeadEvent.OldChain, createStub(block))
}
stats := map[string]interface{}{
"id": s.node,
"event": chainHeadEvent,
}
report := map[string][]interface{}{
"emit": {"headEvent", stats},
}
return conn.WriteJSON(report)
}
// nodeStats is the information to report about the local node.
type nodeStats struct {
Active bool `json:"active"`

1
go.mod
View file

@ -53,6 +53,7 @@ require (
github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-isatty v0.0.12
github.com/mitchellh/cli v1.1.2
github.com/mitchellh/go-grpc-net-conn v0.0.0-20200427190222-eb030e4876f0 // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416

4
go.sum
View file

@ -33,7 +33,6 @@ github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSW
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
@ -375,6 +374,8 @@ github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw=
github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-grpc-net-conn v0.0.0-20200427190222-eb030e4876f0 h1:oZuel4h7224ILBLg2SlTxdaMYXDyqcVfL4Cg1PJQHZs=
github.com/mitchellh/go-grpc-net-conn v0.0.0-20200427190222-eb030e4876f0/go.mod h1:ZCzL0JMR6qfm7VrDC8HGwVtPA8D2Ijc/edUSBw58x94=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
@ -732,6 +733,7 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=

View file

@ -1,4 +1,4 @@
package main
package cli
import "github.com/mitchellh/cli"

View file

@ -1,11 +1,11 @@
package main
package cli
import (
"fmt"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
)
type AccountImportCommand struct {

View file

@ -1,10 +1,10 @@
package main
package cli
import (
"fmt"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
)
type AccountListCommand struct {

View file

@ -1,9 +1,9 @@
package main
package cli
import (
"fmt"
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
)
type AccountNewCommand struct {

View file

@ -1,4 +1,4 @@
package main
package cli
import (
"github.com/mitchellh/cli"

View file

@ -1,12 +1,12 @@
package main
package cli
import (
"context"
"fmt"
"strconv"
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/command/server/proto"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
)
// ChainSetHeadCommand is the command to group the peers commands

View file

@ -0,0 +1,89 @@
package cli
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
)
// ChainWatchCommand is the command to group the peers commands
type ChainWatchCommand struct {
*Meta2
}
// Help implements the cli.Command interface
func (c *ChainWatchCommand) Help() string {
return `Usage: bor chain watch
This command is used to view the chainHead, reorg and fork events in real-time`
}
func (c *ChainWatchCommand) Flags() *flagset.Flagset {
flags := c.NewFlagSet("chain watch")
return flags
}
// Synopsis implements the cli.Command interface
func (c *ChainWatchCommand) Synopsis() string {
return "Watch the chainHead, reorg and fork events in real-time"
}
// Run implements the cli.Command interface
func (c *ChainWatchCommand) Run(args []string) int {
flags := c.Flags()
if err := flags.Parse(args); err != nil {
c.UI.Error(err.Error())
return 1
}
borClt, err := c.BorConn()
if err != nil {
c.UI.Error(err.Error())
return 1
}
sub, err := borClt.ChainWatch(context.Background(), &proto.ChainWatchRequest{})
if err != nil {
c.UI.Error(err.Error())
return 1
}
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)
go func() {
<-signalCh
sub.CloseSend()
}()
for {
msg, err := sub.Recv()
if err != nil {
// if err == EOF if finished on the other side
c.UI.Output(err.Error())
break
}
c.UI.Output(formatHeadEvent(msg))
}
return 0
}
func formatHeadEvent(msg *proto.ChainWatchResponse) string {
var out string
if msg.Type == core.Chain2HeadCanonicalEvent {
out = fmt.Sprintf("Block Added : %v", msg.Newchain)
} else if msg.Type == core.Chain2HeadForkEvent {
out = fmt.Sprintf("New Fork Block : %v", msg.Newchain)
} else if msg.Type == core.Chain2HeadReorgEvent {
out = fmt.Sprintf("Reorg Detected \nAdded : %v \nRemoved : %v", msg.Newchain, msg.Oldchain)
}
return out
}

View file

@ -1,23 +1,19 @@
package main
package cli
import (
"fmt"
"os"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/command/server"
"github.com/ethereum/go-ethereum/command/server/proto"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
"github.com/ethereum/go-ethereum/internal/cli/server"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
"github.com/ethereum/go-ethereum/node"
"github.com/mitchellh/cli"
"github.com/ryanuber/columnize"
"google.golang.org/grpc"
)
func main() {
os.Exit(Run(os.Args[1:]))
}
func Run(args []string) int {
commands := commands()
@ -69,6 +65,11 @@ func commands() map[string]cli.CommandFactory {
UI: ui,
}, nil
},
"chain watch": func() (cli.Command, error) {
return &ChainWatchCommand{
Meta2: meta2,
}, nil
},
"chain sethead": func() (cli.Command, error) {
return &ChainSetHeadCommand{
Meta2: meta2,

View file

@ -1,4 +1,4 @@
package main
package cli
// Based on https://github.com/hashicorp/nomad/blob/main/command/operator_debug.go
@ -6,7 +6,6 @@ import (
"archive/tar"
"compress/gzip"
"context"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
@ -17,8 +16,12 @@ import (
"syscall"
"time"
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/command/server/proto"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
"github.com/golang/protobuf/jsonpb"
gproto "github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/empty"
grpc_net_conn "github.com/mitchellh/go-grpc-net-conn"
)
type DebugCommand struct {
@ -118,16 +121,35 @@ func (d *DebugCommand) Run(args []string) int {
req.Type = proto.PprofRequest_LOOKUP
req.Profile = profile
}
resp, err := clt.Pprof(ctx, req)
stream, err := clt.Pprof(ctx, req)
if err != nil {
return err
}
// write file
raw, err := hex.DecodeString(resp.Payload)
// wait for open request
msg, err := stream.Recv()
if err != nil {
return err
}
if err := ioutil.WriteFile(filepath.Join(tmp, filename+".prof"), raw, 0755); err != nil {
if _, ok := msg.Event.(*proto.PprofResponse_Open_); !ok {
return fmt.Errorf("expected open message")
}
// create the stream
conn := &grpc_net_conn.Conn{
Stream: stream,
Response: &proto.PprofResponse_Input{},
Decode: grpc_net_conn.SimpleDecoder(func(msg gproto.Message) *[]byte {
return &msg.(*proto.PprofResponse_Input).Data
}),
}
file, err := os.OpenFile(filepath.Join(tmp, filename+".prof"), os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return err
}
defer file.Close()
if _, err := io.Copy(file, conn); err != nil {
return err
}
return nil
@ -148,6 +170,25 @@ func (d *DebugCommand) Run(args []string) int {
}
}
// append the status
{
statusResp, err := clt.Status(ctx, &empty.Empty{})
if err != nil {
d.UI.Output(fmt.Sprintf("Failed to get status: %v", err))
return 1
}
m := jsonpb.Marshaler{}
data, err := m.MarshalToString(statusResp)
if err != nil {
d.UI.Output(err.Error())
return 1
}
if err := ioutil.WriteFile(filepath.Join(tmp, "status.json"), []byte(data), 0644); err != nil {
d.UI.Output(fmt.Sprintf("Failed to write status: %v", err))
return 1
}
}
// Exit before archive if output directory was specified
if d.output != "" {
d.UI.Output(fmt.Sprintf("Created debug directory: %s", tmp))

View file

@ -1,4 +1,4 @@
package main
package cli
import (
"github.com/mitchellh/cli"

View file

@ -1,10 +1,10 @@
package main
package cli
import (
"context"
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/command/server/proto"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
)
// PeersAddCommand is the command to group the peers commands

View file

@ -1,12 +1,12 @@
package main
package cli
import (
"context"
"fmt"
"strings"
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/command/server/proto"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
)
// PeersListCommand is the command to group the peers commands

View file

@ -1,10 +1,10 @@
package main
package cli
import (
"context"
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/command/server/proto"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
)
// PeersRemoveCommand is the command to group the peers commands

View file

@ -1,12 +1,12 @@
package main
package cli
import (
"context"
"fmt"
"strings"
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/command/server/proto"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
)
// PeersStatusCommand is the command to group the peers commands

View file

@ -0,0 +1,47 @@
package chains
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
// GetDeveloperChain returns the developer mode configs.
func GetDeveloperChain(period uint64, faucet common.Address) *Chain {
// Override the default period to the user requested one
config := *params.AllCliqueProtocolChanges
config.Clique = &params.CliqueConfig{
Period: period,
Epoch: config.Clique.Epoch,
}
// Assemble and return the chain having genesis with the
// precompiles and faucet pre-funded
return &Chain{
Hash: common.Hash{},
NetworkId: 1337,
Genesis: &core.Genesis{
Config: &config,
ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...),
GasLimit: 11500000,
BaseFee: big.NewInt(params.InitialBaseFee),
Difficulty: big.NewInt(1),
Alloc: map[common.Address]core.GenesisAccount{
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
common.BytesToAddress([]byte{3}): {Balance: big.NewInt(1)}, // RIPEMD
common.BytesToAddress([]byte{4}): {Balance: big.NewInt(1)}, // Identity
common.BytesToAddress([]byte{5}): {Balance: big.NewInt(1)}, // ModExp
common.BytesToAddress([]byte{6}): {Balance: big.NewInt(1)}, // ECAdd
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))},
},
},
Bootnodes: []string{},
}
}

View file

@ -14,12 +14,14 @@ import (
godebug "runtime/debug"
"github.com/ethereum/go-ethereum/command/server/chains"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/internal/cli/server/chains"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
@ -91,6 +93,9 @@ type Config struct {
// GRPC has the grpc server related settings
GRPC *GRPCConfig
// Developer has the developer mode related settings
Developer *DeveloperConfig
}
type P2PConfig struct {
@ -365,6 +370,14 @@ type AccountsConfig struct {
UseLightweightKDF bool `hcl:"use-lightweight-kdf,optional"`
}
type DeveloperConfig struct {
// Enabled enables the developer mode
Enabled bool `hcl:"dev,optional"`
// Period is the block period to use in developer mode
Period uint64 `hcl:"period,optional"`
}
func DefaultConfig() *Config {
return &Config{
Chain: "mainnet",
@ -486,6 +499,10 @@ func DefaultConfig() *Config {
GRPC: &GRPCConfig{
Addr: ":3131",
},
Developer: &DeveloperConfig{
Enabled: false,
Period: 0,
},
}
}
@ -573,6 +590,9 @@ func readConfigFile(path string) (*Config, error) {
}
func (c *Config) loadChain() error {
if c.Developer.Enabled {
return nil
}
chain, ok := chains.GetChain(c.Chain)
if !ok {
return fmt.Errorf("chain '%s' not found", c.Chain)
@ -585,7 +605,7 @@ func (c *Config) loadChain() error {
}
// depending on the chain we have different cache values
if c.Chain != "mainnet" {
if c.Chain == "mainnet" {
c.Cache.Cache = 4096
} else {
c.Cache.Cache = 1024
@ -593,14 +613,19 @@ func (c *Config) loadChain() error {
return nil
}
func (c *Config) buildEth() (*ethconfig.Config, error) {
func (c *Config) buildEth(stack *node.Node) (*ethconfig.Config, error) {
dbHandles, err := makeDatabaseHandles()
if err != nil {
return nil, err
}
n := ethconfig.Defaults
n.NetworkId = c.chain.NetworkId
n.Genesis = c.chain.Genesis
// only update for non-developer mode as we don't yet
// have the chain object for it.
if !c.Developer.Enabled {
n.NetworkId = c.chain.NetworkId
n.Genesis = c.chain.Genesis
}
n.HeimdallURL = c.Heimdall.URL
n.WithoutHeimdall = c.Heimdall.Without
@ -640,6 +665,55 @@ func (c *Config) buildEth() (*ethconfig.Config, error) {
}
}
// update for developer mode
if c.Developer.Enabled {
// Get a keystore
var ks *keystore.KeyStore
if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 {
ks = keystores[0].(*keystore.KeyStore)
}
// Create new developer account or reuse existing one
var (
developer accounts.Account
passphrase string
err error
)
// etherbase has been set above, configuring the miner address from command line flags.
if n.Miner.Etherbase != (common.Address{}) {
developer = accounts.Account{Address: n.Miner.Etherbase}
} else if accs := ks.Accounts(); len(accs) > 0 {
developer = ks.Accounts()[0]
} else {
developer, err = ks.NewAccount(passphrase)
if err != nil {
return nil, fmt.Errorf("failed to create developer account: %v", err)
}
}
if err := ks.Unlock(developer, passphrase); err != nil {
return nil, fmt.Errorf("failed to unlock developer account: %v", err)
}
log.Info("Using developer account", "address", developer.Address)
// get developer mode chain config
c.chain = chains.GetDeveloperChain(c.Developer.Period, developer.Address)
// update the parameters
n.NetworkId = c.chain.NetworkId
n.Genesis = c.chain.Genesis
// Update cache
c.Cache.Cache = 1024
// Update sync mode
c.SyncMode = "full"
// update miner gas price
if n.Miner.GasPrice == nil {
n.Miner.GasPrice = big.NewInt(1)
}
}
// discovery (this params should be in node.Config)
{
n.EthDiscoveryURLs = c.P2P.Discovery.DNS
@ -786,6 +860,17 @@ func (c *Config) buildNode() (*node.Config, error) {
GraphQLVirtualHosts: c.JsonRPC.VHost,
}
// dev mode
if c.Developer.Enabled {
cfg.UseLightweightKDF = true
// disable p2p networking
c.P2P.NoDiscover = true
cfg.P2P.ListenAddr = ""
cfg.P2P.NoDial = true
cfg.P2P.DiscoveryV5 = false
}
// enable jsonrpc endpoints
{
if c.JsonRPC.Http.Enabled {
@ -804,23 +889,26 @@ func (c *Config) buildNode() (*node.Config, error) {
}
cfg.P2P.NAT = natif
// Discovery
// if no bootnodes are defined, use the ones from the chain file.
bootnodes := c.P2P.Discovery.Bootnodes
if len(bootnodes) == 0 {
bootnodes = c.chain.Bootnodes
}
if cfg.P2P.BootstrapNodes, err = parseBootnodes(bootnodes); err != nil {
return nil, err
}
if cfg.P2P.BootstrapNodesV5, err = parseBootnodes(c.P2P.Discovery.BootnodesV5); err != nil {
return nil, err
}
if cfg.P2P.StaticNodes, err = parseBootnodes(c.P2P.Discovery.StaticNodes); err != nil {
return nil, err
}
if cfg.P2P.TrustedNodes, err = parseBootnodes(c.P2P.Discovery.TrustedNodes); err != nil {
return nil, err
// only check for non-developer modes
if !c.Developer.Enabled {
// Discovery
// if no bootnodes are defined, use the ones from the chain file.
bootnodes := c.P2P.Discovery.Bootnodes
if len(bootnodes) == 0 {
bootnodes = c.chain.Bootnodes
}
if cfg.P2P.BootstrapNodes, err = parseBootnodes(bootnodes); err != nil {
return nil, err
}
if cfg.P2P.BootstrapNodesV5, err = parseBootnodes(c.P2P.Discovery.BootnodesV5); err != nil {
return nil, err
}
if cfg.P2P.StaticNodes, err = parseBootnodes(c.P2P.Discovery.StaticNodes); err != nil {
return nil, err
}
if cfg.P2P.TrustedNodes, err = parseBootnodes(c.P2P.Discovery.TrustedNodes); err != nil {
return nil, err
}
}
if c.P2P.NoDiscover {

View file

@ -16,7 +16,7 @@ func TestConfigDefault(t *testing.T) {
_, err := config.buildNode()
assert.NoError(t, err)
_, err = config.buildEth()
_, err = config.buildEth(nil)
assert.NoError(t, err)
}

View file

@ -1,7 +1,7 @@
package server
import (
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
)
func (c *Command) Flags() *flagset.Flagset {
@ -461,5 +461,17 @@ func (c *Command) Flags() *flagset.Flagset {
Usage: "Address and port to bind the GRPC server",
Value: &c.cliConfig.GRPC.Addr,
})
// developer
f.BoolFlag(&flagset.BoolFlag{
Name: "dev",
Usage: "Enable developer mode with ephemeral proof-of-authority network and a pre-funded developer account, mining enabled",
Value: &c.cliConfig.Developer.Enabled,
})
f.Uint64Flag(&flagset.Uint64Flag{
Name: "dev.period",
Usage: "Block period to use in developer mode (0 = mine only if transaction pending)",
Value: &c.cliConfig.Developer.Period,
})
return f
}

View file

@ -7,7 +7,7 @@ import "google/protobuf/empty.proto";
option go_package = "/command/server/proto";
service Bor {
rpc Pprof(PprofRequest) returns (PprofResponse);
rpc Pprof(PprofRequest) returns (stream PprofResponse);
rpc PeersAdd(PeersAddRequest) returns (PeersAddResponse);
@ -20,8 +20,24 @@ service Bor {
rpc ChainSetHead(ChainSetHeadRequest) returns (ChainSetHeadResponse);
rpc Status(google.protobuf.Empty) returns (StatusResponse);
rpc ChainWatch(ChainWatchRequest) returns (stream ChainWatchResponse);
}
message ChainWatchRequest {
}
message ChainWatchResponse {
repeated BlockStub oldchain = 1;
repeated BlockStub newchain = 2;
string type = 3;
}
message BlockStub {
string hash = 1;
uint64 number = 2;
}
message PeersAddRequest {
string enode = 1;
@ -77,6 +93,13 @@ message StatusResponse {
int64 numPeers = 3;
string syncMode = 4;
Syncing syncing = 5;
repeated Fork forks = 6;
message Fork {
string name = 1;
int64 block = 2;
bool disabled = 3;
}
message Syncing {
int64 startingBlock = 1;
@ -105,6 +128,18 @@ message PprofRequest {
}
message PprofResponse {
string payload = 1;
map<string, string> headers = 2;
oneof event {
Open open = 1;
Input input = 2;
google.protobuf.Empty eof = 3;
}
message Open {
map<string, string> headers = 1;
int64 size = 2;
}
message Input {
bytes data = 1;
}
}

View file

@ -1,13 +1,17 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.17.3
// source: command/server/proto/server.proto
package proto
import (
context "context"
empty "github.com/golang/protobuf/ptypes/empty"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
emptypb "google.golang.org/protobuf/types/known/emptypb"
)
// This is a compile-time assertion to ensure that this generated file
@ -19,13 +23,14 @@ const _ = grpc.SupportPackageIsVersion7
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type BorClient interface {
Pprof(ctx context.Context, in *PprofRequest, opts ...grpc.CallOption) (*PprofResponse, error)
Pprof(ctx context.Context, in *PprofRequest, opts ...grpc.CallOption) (Bor_PprofClient, error)
PeersAdd(ctx context.Context, in *PeersAddRequest, opts ...grpc.CallOption) (*PeersAddResponse, error)
PeersRemove(ctx context.Context, in *PeersRemoveRequest, opts ...grpc.CallOption) (*PeersRemoveResponse, error)
PeersList(ctx context.Context, in *PeersListRequest, opts ...grpc.CallOption) (*PeersListResponse, error)
PeersStatus(ctx context.Context, in *PeersStatusRequest, opts ...grpc.CallOption) (*PeersStatusResponse, error)
ChainSetHead(ctx context.Context, in *ChainSetHeadRequest, opts ...grpc.CallOption) (*ChainSetHeadResponse, error)
Status(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*StatusResponse, error)
Status(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*StatusResponse, error)
ChainWatch(ctx context.Context, in *ChainWatchRequest, opts ...grpc.CallOption) (Bor_ChainWatchClient, error)
}
type borClient struct {
@ -36,13 +41,36 @@ func NewBorClient(cc grpc.ClientConnInterface) BorClient {
return &borClient{cc}
}
func (c *borClient) Pprof(ctx context.Context, in *PprofRequest, opts ...grpc.CallOption) (*PprofResponse, error) {
out := new(PprofResponse)
err := c.cc.Invoke(ctx, "/proto.Bor/Pprof", in, out, opts...)
func (c *borClient) Pprof(ctx context.Context, in *PprofRequest, opts ...grpc.CallOption) (Bor_PprofClient, error) {
stream, err := c.cc.NewStream(ctx, &Bor_ServiceDesc.Streams[0], "/proto.Bor/Pprof", opts...)
if err != nil {
return nil, err
}
return out, nil
x := &borPprofClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type Bor_PprofClient interface {
Recv() (*PprofResponse, error)
grpc.ClientStream
}
type borPprofClient struct {
grpc.ClientStream
}
func (x *borPprofClient) Recv() (*PprofResponse, error) {
m := new(PprofResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *borClient) PeersAdd(ctx context.Context, in *PeersAddRequest, opts ...grpc.CallOption) (*PeersAddResponse, error) {
@ -90,7 +118,7 @@ func (c *borClient) ChainSetHead(ctx context.Context, in *ChainSetHeadRequest, o
return out, nil
}
func (c *borClient) Status(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*StatusResponse, error) {
func (c *borClient) Status(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*StatusResponse, error) {
out := new(StatusResponse)
err := c.cc.Invoke(ctx, "/proto.Bor/Status", in, out, opts...)
if err != nil {
@ -99,17 +127,50 @@ func (c *borClient) Status(ctx context.Context, in *empty.Empty, opts ...grpc.Ca
return out, nil
}
func (c *borClient) ChainWatch(ctx context.Context, in *ChainWatchRequest, opts ...grpc.CallOption) (Bor_ChainWatchClient, error) {
stream, err := c.cc.NewStream(ctx, &Bor_ServiceDesc.Streams[1], "/proto.Bor/ChainWatch", opts...)
if err != nil {
return nil, err
}
x := &borChainWatchClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type Bor_ChainWatchClient interface {
Recv() (*ChainWatchResponse, error)
grpc.ClientStream
}
type borChainWatchClient struct {
grpc.ClientStream
}
func (x *borChainWatchClient) Recv() (*ChainWatchResponse, error) {
m := new(ChainWatchResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// BorServer is the server API for Bor service.
// All implementations must embed UnimplementedBorServer
// for forward compatibility
type BorServer interface {
Pprof(context.Context, *PprofRequest) (*PprofResponse, error)
Pprof(*PprofRequest, Bor_PprofServer) error
PeersAdd(context.Context, *PeersAddRequest) (*PeersAddResponse, error)
PeersRemove(context.Context, *PeersRemoveRequest) (*PeersRemoveResponse, error)
PeersList(context.Context, *PeersListRequest) (*PeersListResponse, error)
PeersStatus(context.Context, *PeersStatusRequest) (*PeersStatusResponse, error)
ChainSetHead(context.Context, *ChainSetHeadRequest) (*ChainSetHeadResponse, error)
Status(context.Context, *empty.Empty) (*StatusResponse, error)
Status(context.Context, *emptypb.Empty) (*StatusResponse, error)
ChainWatch(*ChainWatchRequest, Bor_ChainWatchServer) error
mustEmbedUnimplementedBorServer()
}
@ -117,8 +178,8 @@ type BorServer interface {
type UnimplementedBorServer struct {
}
func (UnimplementedBorServer) Pprof(context.Context, *PprofRequest) (*PprofResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Pprof not implemented")
func (UnimplementedBorServer) Pprof(*PprofRequest, Bor_PprofServer) error {
return status.Errorf(codes.Unimplemented, "method Pprof not implemented")
}
func (UnimplementedBorServer) PeersAdd(context.Context, *PeersAddRequest) (*PeersAddResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PeersAdd not implemented")
@ -135,9 +196,12 @@ func (UnimplementedBorServer) PeersStatus(context.Context, *PeersStatusRequest)
func (UnimplementedBorServer) ChainSetHead(context.Context, *ChainSetHeadRequest) (*ChainSetHeadResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ChainSetHead not implemented")
}
func (UnimplementedBorServer) Status(context.Context, *empty.Empty) (*StatusResponse, error) {
func (UnimplementedBorServer) Status(context.Context, *emptypb.Empty) (*StatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Status not implemented")
}
func (UnimplementedBorServer) ChainWatch(*ChainWatchRequest, Bor_ChainWatchServer) error {
return status.Errorf(codes.Unimplemented, "method ChainWatch not implemented")
}
func (UnimplementedBorServer) mustEmbedUnimplementedBorServer() {}
// UnsafeBorServer may be embedded to opt out of forward compatibility for this service.
@ -151,22 +215,25 @@ func RegisterBorServer(s grpc.ServiceRegistrar, srv BorServer) {
s.RegisterService(&Bor_ServiceDesc, srv)
}
func _Bor_Pprof_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PprofRequest)
if err := dec(in); err != nil {
return nil, err
func _Bor_Pprof_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(PprofRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
if interceptor == nil {
return srv.(BorServer).Pprof(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.Bor/Pprof",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BorServer).Pprof(ctx, req.(*PprofRequest))
}
return interceptor(ctx, in, info, handler)
return srv.(BorServer).Pprof(m, &borPprofServer{stream})
}
type Bor_PprofServer interface {
Send(*PprofResponse) error
grpc.ServerStream
}
type borPprofServer struct {
grpc.ServerStream
}
func (x *borPprofServer) Send(m *PprofResponse) error {
return x.ServerStream.SendMsg(m)
}
func _Bor_PeersAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
@ -260,7 +327,7 @@ func _Bor_ChainSetHead_Handler(srv interface{}, ctx context.Context, dec func(in
}
func _Bor_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(empty.Empty)
in := new(emptypb.Empty)
if err := dec(in); err != nil {
return nil, err
}
@ -272,11 +339,32 @@ func _Bor_Status_Handler(srv interface{}, ctx context.Context, dec func(interfac
FullMethod: "/proto.Bor/Status",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BorServer).Status(ctx, req.(*empty.Empty))
return srv.(BorServer).Status(ctx, req.(*emptypb.Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Bor_ChainWatch_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(ChainWatchRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(BorServer).ChainWatch(m, &borChainWatchServer{stream})
}
type Bor_ChainWatchServer interface {
Send(*ChainWatchResponse) error
grpc.ServerStream
}
type borChainWatchServer struct {
grpc.ServerStream
}
func (x *borChainWatchServer) Send(m *ChainWatchResponse) error {
return x.ServerStream.SendMsg(m)
}
// Bor_ServiceDesc is the grpc.ServiceDesc for Bor service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -284,10 +372,6 @@ var Bor_ServiceDesc = grpc.ServiceDesc{
ServiceName: "proto.Bor",
HandlerType: (*BorServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Pprof",
Handler: _Bor_Pprof_Handler,
},
{
MethodName: "PeersAdd",
Handler: _Bor_PeersAdd_Handler,
@ -313,6 +397,17 @@ var Bor_ServiceDesc = grpc.ServiceDesc{
Handler: _Bor_Status_Handler,
},
},
Streams: []grpc.StreamDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "Pprof",
Handler: _Bor_Pprof_Handler,
ServerStreams: true,
},
{
StreamName: "ChainWatch",
Handler: _Bor_ChainWatch_Handler,
ServerStreams: true,
},
},
Metadata: "command/server/proto/server.proto",
}

View file

@ -11,11 +11,11 @@ import (
"time"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/command/server/proto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethstats"
"github.com/ethereum/go-ethereum/graphql"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/metrics/influxdb"
@ -69,11 +69,22 @@ func NewServer(config *Config) (*Server, error) {
}
srv.node = stack
// setup account manager (only keystore)
{
keydir := stack.KeyStoreDir()
n, p := keystore.StandardScryptN, keystore.StandardScryptP
if config.Accounts.UseLightweightKDF {
n, p = keystore.LightScryptN, keystore.LightScryptP
}
stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p))
}
// register the ethereum backend
ethCfg, err := config.buildEth()
ethCfg, err := config.buildEth(stack)
if err != nil {
return nil, err
}
backend, err := eth.New(stack, ethCfg)
if err != nil {
return nil, err
@ -97,18 +108,8 @@ func NewServer(config *Config) (*Server, error) {
}
}
// setup account manager (only keystore)
{
keydir := stack.KeyStoreDir()
n, p := keystore.StandardScryptN, keystore.StandardScryptP
if config.Accounts.UseLightweightKDF {
n, p = keystore.LightScryptN, keystore.LightScryptP
}
stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p))
}
// sealing (if enabled)
if config.Sealer.Enabled {
// sealing (if enabled) or in dev mode
if config.Sealer.Enabled || config.Developer.Enabled {
if err := backend.StartMining(1); err != nil {
return nil, err
}

View file

@ -0,0 +1,41 @@
package server
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestServer_DeveloperMode(t *testing.T) {
// get the default config
config := DefaultConfig()
// enable developer mode
config.Developer.Enabled = true
config.Developer.Period = 2 // block time
// start the server
server, err1 := NewServer(config)
if err1 != nil {
t.Fatalf("failed to start server: %v", err1)
}
// record the initial block number
blockNumber := server.backend.BlockChain().CurrentBlock().Header().Number.Int64()
var i int64 = 0
for i = 0; i < 10; i++ {
// We expect the node to mine blocks every `config.Developer.Period` time period
time.Sleep(time.Duration(config.Developer.Period) * time.Second)
currBlock := server.backend.BlockChain().CurrentBlock().Header().Number.Int64()
expected := blockNumber + i + 1
if res := assert.Equal(t, currBlock, expected); res == false {
break
}
}
// stop the server
server.Stop()
}

View file

@ -2,23 +2,28 @@ package server
import (
"context"
"encoding/hex"
"fmt"
"math/big"
"reflect"
"strings"
"github.com/ethereum/go-ethereum/command/server/pprof"
"github.com/ethereum/go-ethereum/command/server/proto"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/cli/server/pprof"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
gproto "github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/empty"
grpc_net_conn "github.com/mitchellh/go-grpc-net-conn"
)
func (s *Server) Pprof(ctx context.Context, req *proto.PprofRequest) (*proto.PprofResponse, error) {
func (s *Server) Pprof(req *proto.PprofRequest, stream proto.Bor_PprofServer) error {
var payload []byte
var headers map[string]string
var err error
ctx := context.Background()
switch req.Type {
case proto.PprofRequest_CPU:
payload, headers, err = pprof.CPUProfile(ctx, int(req.Seconds))
@ -28,14 +33,42 @@ func (s *Server) Pprof(ctx context.Context, req *proto.PprofRequest) (*proto.Ppr
payload, headers, err = pprof.Profile(req.Profile, 0, 0)
}
if err != nil {
return nil, err
return err
}
resp := &proto.PprofResponse{
Payload: hex.EncodeToString(payload),
Headers: headers,
// open the stream and send the headers
err = stream.Send(&proto.PprofResponse{
Event: &proto.PprofResponse_Open_{
Open: &proto.PprofResponse_Open{
Headers: headers,
Size: int64(len(payload)),
},
},
})
if err != nil {
return err
}
return resp, nil
// Wrap our conn around the response.
conn := &grpc_net_conn.Conn{
Stream: stream,
Request: &proto.PprofResponse_Input{},
Encode: grpc_net_conn.SimpleEncoder(func(msg gproto.Message) *[]byte {
return &msg.(*proto.PprofResponse_Input).Data
}),
}
if _, err := conn.Write(payload); err != nil {
return err
}
// send the eof
err = stream.Send(&proto.PprofResponse{
Event: &proto.PprofResponse_Eof{},
})
if err != nil {
return err
}
return nil
}
func (s *Server) PeersAdd(ctx context.Context, req *proto.PeersAddRequest) (*proto.PeersAddResponse, error) {
@ -124,6 +157,7 @@ func (s *Server) Status(ctx context.Context, _ *empty.Empty) (*proto.StatusRespo
HighestBlock: int64(syncProgress.HighestBlock),
CurrentBlock: int64(syncProgress.CurrentBlock),
},
Forks: gatherForks(s.config.chain.Genesis.Config, s.config.chain.Genesis.Config.Bor),
}
return resp, nil
}
@ -134,3 +168,90 @@ func headerToProtoHeader(h *types.Header) *proto.Header {
Number: h.Number.Uint64(),
}
}
var bigIntT = reflect.TypeOf(new(big.Int)).Kind()
// gatherForks gathers all the fork numbers via reflection
func gatherForks(configList ...interface{}) []*proto.StatusResponse_Fork {
var forks []*proto.StatusResponse_Fork
for _, config := range configList {
kind := reflect.TypeOf(config)
for kind.Kind() == reflect.Ptr {
kind = kind.Elem()
}
skip := "DAOForkBlock"
conf := reflect.ValueOf(config).Elem()
for i := 0; i < kind.NumField(); i++ {
// Fetch the next field and skip non-fork rules
field := kind.Field(i)
if strings.Contains(field.Name, skip) {
continue
}
if !strings.HasSuffix(field.Name, "Block") {
continue
}
fork := &proto.StatusResponse_Fork{
Name: strings.TrimSuffix(field.Name, "Block"),
}
val := conf.Field(i)
switch field.Type.Kind() {
case bigIntT:
rule := val.Interface().(*big.Int)
if rule != nil {
fork.Block = rule.Int64()
} else {
fork.Disabled = true
}
case reflect.Uint64:
fork.Block = int64(val.Uint())
default:
continue
}
forks = append(forks, fork)
}
}
return forks
}
func convertBlockToBlockStub(blocks []*types.Block) []*proto.BlockStub {
var blockStubs []*proto.BlockStub
for _, block := range blocks {
blockStub := &proto.BlockStub{
Hash: block.Hash().String(),
Number: block.NumberU64(),
}
blockStubs = append(blockStubs, blockStub)
}
return blockStubs
}
func (s *Server) ChainWatch(req *proto.ChainWatchRequest, reply proto.Bor_ChainWatchServer) error {
chain2HeadChanSize := 10
chain2HeadCh := make(chan core.Chain2HeadEvent, chain2HeadChanSize)
headSub := s.backend.APIBackend.SubscribeChain2HeadEvent(chain2HeadCh)
defer headSub.Unsubscribe()
for {
msg := <-chain2HeadCh
err := reply.Send(&proto.ChainWatchResponse{Type: msg.Type,
Newchain: convertBlockToBlockStub(msg.NewChain),
Oldchain: convertBlockToBlockStub(msg.OldChain),
})
if err != nil {
return err
}
}
}

View file

@ -0,0 +1,43 @@
package server
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
"github.com/stretchr/testify/assert"
)
func TestGatherBlocks(t *testing.T) {
type c struct {
ABlock *big.Int
BBlock *big.Int
}
type d struct {
DBlock uint64
}
val := &c{
BBlock: new(big.Int).SetInt64(1),
}
val2 := &d{
DBlock: 10,
}
expect := []*proto.StatusResponse_Fork{
{
Name: "A",
Disabled: true,
},
{
Name: "B",
Block: 1,
},
{
Name: "D",
Block: 10,
},
}
res := gatherForks(val, val2)
assert.Equal(t, res, expect)
}

View file

@ -1,11 +1,11 @@
package main
package cli
import (
"context"
"fmt"
"strings"
"github.com/ethereum/go-ethereum/command/server/proto"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
"github.com/golang/protobuf/ptypes/empty"
)
@ -57,6 +57,13 @@ func printStatus(status *proto.StatusResponse) string {
fmt.Sprintf("Number|%d", h.Number),
})
}
forks := make([]string, len(status.Forks)+1)
forks[0] = "Name|Block|Enabled"
for i, d := range status.Forks {
forks[i+1] = fmt.Sprintf("%s|%d|%v", d.Name, d.Block, !d.Disabled)
}
full := []string{
"General",
formatKV([]string{
@ -73,6 +80,8 @@ func printStatus(status *proto.StatusResponse) string {
fmt.Sprintf("Highest block|%d", status.Syncing.HighestBlock),
fmt.Sprintf("Starting block|%d", status.Syncing.StartingBlock),
}),
"\nForks",
formatList(forks),
}
return strings.Join(full, "\n")
}

View file

@ -1,4 +1,4 @@
package main
package cli
import (
"github.com/ethereum/go-ethereum/params"

View file

@ -645,7 +645,7 @@ func (s *PublicBlockChainAPI) GetTransactionReceiptsByBlock(ctx context.Context,
}
if len(txs) != len(receipts) {
return nil, fmt.Errorf("txs length doesn't equal to receipts' length", len(txs), len(receipts))
return nil, fmt.Errorf("txs length %d doesn't equal to receipts' length %d", len(txs), len(receipts))
}
txReceipts := make([]map[string]interface{}, 0, len(txs))

View file

@ -98,6 +98,7 @@ type Backend interface {
GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error)
GetBorBlockTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
GetBorBlockTransactionWithBlockHash(ctx context.Context, txHash common.Hash, blockHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
SubscribeChain2HeadEvent(ch chan<- core.Chain2HeadEvent) event.Subscription
ChainConfig() *params.ChainConfig
Engine() consensus.Engine

View file

@ -17,3 +17,8 @@ func (b *LesApiBackend) GetRootHash(ctx context.Context, starBlockNr uint64, end
func (b *LesApiBackend) SubscribeStateSyncEvent(ch chan<- core.StateSyncEvent) event.Subscription {
return b.eth.blockchain.SubscribeStateSyncEvent(ch)
}
// SubscribeChain2HeadEvent subscribe head/fork/reorg events.
func (b *LesApiBackend) SubscribeChain2HeadEvent(ch chan<- core.Chain2HeadEvent) event.Subscription {
return b.eth.BlockChain().SubscribeChain2HeadEvent(ch)
}

View file

@ -73,6 +73,9 @@ type LightChain struct {
running int32 // whether LightChain is running or stopped
procInterrupt int32 // interrupts chain insert
disableCheckFreq int32 // disables header verification
// Bor
chain2HeadFeed event.Feed
}
// NewLightChain returns a fully initialised light chain using information
@ -597,6 +600,11 @@ func (lc *LightChain) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent)
return lc.scope.Track(new(event.Feed).Subscribe(ch))
}
// SubscribeChain2HeadEvent registers a subscription of Reorg/head/fork events.
func (lc *LightChain) SubscribeChain2HeadEvent(ch chan<- core.Chain2HeadEvent) event.Subscription {
return lc.scope.Track(lc.chain2HeadFeed.Subscribe(ch))
}
// DisableCheckFreq disables header validation. This is used for ultralight mode.
func (lc *LightChain) DisableCheckFreq() {
atomic.StoreInt32(&lc.disableCheckFreq, 1)

View file

@ -155,6 +155,8 @@ public class AndroidTest extends InstrumentationTestCase {
//
// This method has been adapted from golang.org/x/mobile/bind/java/seq_test.go/runTest
func TestAndroid(t *testing.T) {
t.Skip("Bor: We do not test this")
// Skip tests on Windows altogether
if runtime.GOOS == "windows" {
t.Skip("cannot test Android bindings on Windows, skipping")

View file

@ -391,16 +391,16 @@ var (
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil, nil}
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil, &BorConfig{BurntContract: map[string]string{"0": "0x000000000000000000000000000000000000dead"}}}
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Clique consensus.
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil}
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, &BorConfig{BurntContract: map[string]string{"0": "0x000000000000000000000000000000000000dead"}}}
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil, nil}
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil, &BorConfig{BurntContract: map[string]string{"0": "0x000000000000000000000000000000000000dead"}}}
TestRules = TestChainConfig.Rules(new(big.Int))
)

15
scripts/tools-protobuf.sh Normal file → Executable file
View file

@ -1,5 +1,16 @@
curl -LO https://github.com/protocolbuffers/protobuf/releases/download/v3.12.0/protoc-3.12.0-linux-x86_64.zip
sudo unzip protoc-3.12.0-linux-x86_64.zip -d /usr/local/bin
#!/bin/bash
# Install protobuf
PROTOC_ZIP=protoc-3.12.0-linux-x86_64.zip
curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v3.12.0/$PROTOC_ZIP
sudo unzip -o $PROTOC_ZIP -d /usr/local bin/protoc
sudo unzip -o $PROTOC_ZIP -d /usr/local 'include/*'
rm -f $PROTOC_ZIP
# Change permissions to use the binary
sudo chmod 755 -R /usr/local/bin/protoc
sudo chmod 755 -R /usr/local/include
# Install golang extensions (DO NOT CHANGE THE VERSIONS)
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.25.0
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1

@ -1 +0,0 @@
Subproject commit 092a8834dc445e683103689d6f0e75a5d380a190