Skip to content

Commit

Permalink
[network] support static node (#280)
Browse files Browse the repository at this point in the history
# Description

this PR add static node feature, static nodes configured for normal node
not remove on runtime. unlike bootnode, node connections to static nodes
is keep alive, if the connection is disconnected, node will actively try
to reconnect static node (management connections is not affected by this
PR)

command change

1. command `dogechain genesis` add `--staticnode` Flag
2. genesis file add `staticnodes` field
3. command `dogechain peers add` add `--static` Flag, dynamically add
static nodes

genesis file simple:

```
{
.......
    "bootnodes": [
        "/ip4/10.5.0.15/tcp/1478/p2p/16Uiu2HAm7oonKfext8YgFCAMNWZKuXP2dpvnamCrN2VxjqTS54y3"
    ],
    "staticnodes": [
        "/ip4/10.5.0.15/tcp/1478/p2p/16Uiu2HAm7oonKfext8YgFCAMNWZKuXP2dpvnamCrN2VxjqTS54y3"
    ]
}
```    

# Changes include

- [x] New feature (non-breaking change that adds functionality)

## Testing

- [x] I have tested this code with the official test suite
- [x] I have tested this code manually

### Manual tests

1. create static nodes genesis file (1 boot node, 4 validator node,
validator node configured boot node as a static node)
2. start up dogechain node
3. stop rpc node
4. check validator node is try reconnect rpc node
  • Loading branch information
0xcb9ff9 authored Jan 12, 2023
1 parent 09c4a7c commit 24ab44e
Show file tree
Hide file tree
Showing 31 changed files with 583 additions and 222 deletions.
9 changes: 5 additions & 4 deletions chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ var (

// Chain is the blockchain chain configuration
type Chain struct {
Name string `json:"name"`
Genesis *Genesis `json:"genesis"`
Params *Params `json:"params"`
Bootnodes []string `json:"bootnodes,omitempty"`
Name string `json:"name"`
Genesis *Genesis `json:"genesis"`
Params *Params `json:"params"`
Bootnodes []string `json:"bootnodes,omitempty"`
Staticnodes []string `json:"staticnodes,omitempty"`
}

// Genesis specifies the header fields, state of a genesis block
Expand Down
5 changes: 3 additions & 2 deletions command/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const (

IgnoreDiscoverCIDRFlag = "ignore-discover-cidr"

BootnodeFlag = "bootnode"
LogLevelFlag = "log-level"
BootnodeFlag = "bootnode"
StaticnodeFlag = "staticnode"
LogLevelFlag = "log-level"
)
7 changes: 7 additions & 0 deletions command/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ func setFlags(cmd *cobra.Command) {
"multiAddr URL for p2p discovery bootstrap. This flag can be used multiple times",
)

cmd.Flags().StringArrayVar(
&params.staticnodes,
command.StaticnodeFlag,
[]string{},
"multiAddr URL for p2p static nodes. This flag can be used multiple times",
)

// IBFT Validators
{
cmd.Flags().StringVar(
Expand Down
4 changes: 3 additions & 1 deletion command/genesis/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type genesisParams struct {
validatorPrefixPath string
premine []string
bootnodes []string
staticnodes []string
ibftValidators []types.Address

ibftValidatorsRaw []string
Expand Down Expand Up @@ -251,7 +252,8 @@ func (p *genesisParams) initGenesisConfig() error {
Forks: chain.AllForksEnabled,
Engine: p.consensusEngineConfig,
},
Bootnodes: p.bootnodes,
Bootnodes: p.bootnodes,
Staticnodes: p.staticnodes,
}

// Predeploy ValidatorSet smart contract if needed
Expand Down
11 changes: 7 additions & 4 deletions command/peers/add/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ var (
)

const (
addrFlag = "addr"
addrFlag = "addr"
staticFlag = "static"
)

type addParams struct {
isStatic bool
peerAddresses []string

systemClient proto.SystemClient
Expand Down Expand Up @@ -60,7 +62,7 @@ func (p *addParams) initSystemClient(grpcAddress string) error {

func (p *addParams) addPeers() {
for _, address := range p.peerAddresses {
if addErr := p.addPeer(address); addErr != nil {
if addErr := p.addPeer(address, p.isStatic); addErr != nil {
p.addErrors = append(p.addErrors, addErr.Error())

continue
Expand All @@ -70,11 +72,12 @@ func (p *addParams) addPeers() {
}
}

func (p *addParams) addPeer(peerAddress string) error {
func (p *addParams) addPeer(peerAddress string, static bool) error {
if _, err := p.systemClient.PeersAdd(
context.Background(),
&proto.PeersAddRequest{
Id: peerAddress,
Id: peerAddress,
Static: static,
},
); err != nil {
return err
Expand Down
7 changes: 7 additions & 0 deletions command/peers/add/peers_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ func setFlags(cmd *cobra.Command) {
[]string{},
"the libp2p addresses of the peers",
)

cmd.Flags().BoolVar(
&params.isStatic,
staticFlag,
false,
"add the peers as static peers",
)
}

func runPreRun(_ *cobra.Command, _ []string) error {
Expand Down
18 changes: 17 additions & 1 deletion e2e/broadcast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,32 @@ func TestBroadcast(t *testing.T) {
numNodes int
// Number of nodes that connects to left node
numConnectedNodes int
// static peers
static bool
}{
{
name: "tx should not reach to last node",
numNodes: 10,
numConnectedNodes: 5,
static: false,
},
{
name: "tx should reach to last node",
numNodes: 10,
numConnectedNodes: 10,
static: false,
},
{
name: "tx should not reach to last node (static node)",
numNodes: 10,
numConnectedNodes: 5,
static: true,
},
{
name: "tx should reach to last node (static node)",
numNodes: 10,
numConnectedNodes: 10,
static: true,
},
}

Expand All @@ -46,7 +62,7 @@ func TestBroadcast(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
srvs := framework.NewTestServers(t, tt.numNodes, conf)
framework.MultiJoinSerial(t, srvs[0:tt.numConnectedNodes])
framework.MultiJoinSerial(t, tt.static, srvs[0:tt.numConnectedNodes])

// Check the connections
for i, srv := range srvs {
Expand Down
19 changes: 18 additions & 1 deletion e2e/discovery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,32 @@ func TestDiscovery(t *testing.T) {
numNodes int
// Number of nodes that connects to left node as default
numInitConnectNodes int
// static peers
static bool
}{
{
name: "first 4 nodes should know each other",
numNodes: 5,
numInitConnectNodes: 4,
static: false,
},
{
name: "all should know each other",
numNodes: 5,
numInitConnectNodes: 5,
static: false,
},
{
name: "first 4 nodes should know each other (static node)",
numNodes: 5,
numInitConnectNodes: 4,
static: true,
},
{
name: "all should know each other (static node)",
numNodes: 5,
numInitConnectNodes: 5,
static: true,
},
}

Expand All @@ -53,7 +69,8 @@ func TestDiscovery(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_, err := srv.Operator().PeersAdd(ctx, &proto.PeersAddRequest{
Id: dest,
Id: dest,
Static: tt.static,
})
if err != nil {
t.Fatal(err)
Expand Down
6 changes: 6 additions & 0 deletions e2e/framework/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type TestServerConfig struct {
DevStakers []types.Address
Consensus ConsensusType // Consensus MechanismType
Bootnodes []string // Bootnode Addresses
Staticnodes []string // Staticnode Addresses
PriceLimit *uint64 // Minimum gas price limit to enforce for acceptance into the pool
DevInterval int // Dev consensus update interval [s]
EpochSize uint64 // The epoch size in blocks for the IBFT layer
Expand Down Expand Up @@ -141,6 +142,11 @@ func (t *TestServerConfig) SetBootnodes(bootnodes []string) {
t.Bootnodes = bootnodes
}

// SetStaticnodes sets bootnodes
func (t *TestServerConfig) SetStaticnodes(staticnodes []string) {
t.Staticnodes = staticnodes
}

// SetPriceLimit sets the gas price limit
func (t *TestServerConfig) SetPriceLimit(priceLimit *uint64) {
t.PriceLimit = priceLimit
Expand Down
9 changes: 5 additions & 4 deletions e2e/framework/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func EcrecoverFromBlockhash(hash types.Hash, signature []byte) (types.Address, e
return crypto.PubKeyToAddress(pubKey), nil
}

func MultiJoinSerial(t *testing.T, srvs []*TestServer) {
func MultiJoinSerial(t *testing.T, static bool, srvs []*TestServer) {
t.Helper()

dials := []*TestServer{}
Expand All @@ -130,10 +130,10 @@ func MultiJoinSerial(t *testing.T, srvs []*TestServer) {
srv, dst := srvs[i], srvs[i+1]
dials = append(dials, srv, dst)
}
MultiJoin(t, dials...)
MultiJoin(t, static, dials...)
}

func MultiJoin(t *testing.T, srvs ...*TestServer) {
func MultiJoin(t *testing.T, static bool, srvs ...*TestServer) {
t.Helper()

if len(srvs)%2 != 0 {
Expand All @@ -157,7 +157,8 @@ func MultiJoin(t *testing.T, srvs ...*TestServer) {

dstAddr := strings.Split(dstStatus.P2PAddr, ",")[0]
_, err = srcClient.PeersAdd(context.Background(), &proto.PeersAddRequest{
Id: dstAddr,
Id: dstAddr,
Static: static,
})

errCh <- err
Expand Down
9 changes: 9 additions & 0 deletions e2e/framework/ibft.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func NewIBFTServersManager(
t *testing.T,
numNodes int,
ibftDirPrefix string,
static bool,
callback IBFTServerConfigCallback,
) *IBFTServersManager {
t.Helper()
Expand All @@ -41,6 +42,7 @@ func NewIBFTServersManager(
})

bootnodes := make([]string, 0, numNodes)
staticnodes := make([]string, 0, numNodes)
genesisValidators := make([]string, 0, numNodes)

for i := 0; i < numNodes; i++ {
Expand All @@ -60,11 +62,18 @@ func NewIBFTServersManager(

srvs = append(srvs, srv)
bootnodes = append(bootnodes, libp2pAddr)

if static {
staticnodes = append(staticnodes, libp2pAddr)
}

genesisValidators = append(genesisValidators, res.Address)
}

srv := srvs[0]
srv.Config.SetBootnodes(bootnodes)
// Set static nodes
srv.Config.SetStaticnodes(staticnodes)
// Set genesis staking balance for genesis validators
for i, v := range genesisValidators {
addr := types.StringToAddress(v)
Expand Down
4 changes: 4 additions & 0 deletions e2e/framework/testserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,10 @@ func (t *TestServer) GenerateGenesis() error {
args = append(args, "--bootnode", bootnode)
}

for _, staticnode := range t.Config.Staticnodes {
args = append(args, "--staticnode", staticnode)
}

// Make sure the correct mechanism is selected
if t.Config.IsPos {
args = append(args, "--pos")
Expand Down
18 changes: 18 additions & 0 deletions e2e/ibft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func TestIbft_Transfer(t *testing.T) {
t,
IBFTMinNodes,
IBFTDirPrefix,
false,
func(i int, config *framework.TestServerConfig) {
config.Premine(senderAddr, framework.EthToWei(10))
config.SetSeal(true)
Expand Down Expand Up @@ -58,16 +59,31 @@ func TestIbft_TransactionFeeRecipient(t *testing.T) {
name string
contractCall bool
txAmount *big.Int
static bool
}{
{
name: "transfer transaction",
contractCall: false,
txAmount: framework.EthToWei(1),
static: false,
},
{
name: "contract function execution",
contractCall: true,
txAmount: big.NewInt(0),
static: false,
},
{
name: "transfer transaction (static node)",
contractCall: false,
txAmount: framework.EthToWei(1),
static: true,
},
{
name: "contract function execution (static node)",
contractCall: true,
txAmount: big.NewInt(0),
static: true,
},
}

Expand All @@ -80,9 +96,11 @@ func TestIbft_TransactionFeeRecipient(t *testing.T) {
t,
IBFTMinNodes,
IBFTDirPrefix,
tc.static,
func(i int, config *framework.TestServerConfig) {
config.Premine(senderAddr, framework.EthToWei(10))
config.SetSeal(true)
config.SetBlockTime(1)
})

ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
Expand Down
5 changes: 4 additions & 1 deletion e2e/syncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,17 @@ func TestClusterBlockSync(t *testing.T) {
ibftManager := framework.NewIBFTServersManager(
t,
IBFTMinNodes+numNonValidators,
IBFTDirPrefix, func(i int, config *framework.TestServerConfig) {
IBFTDirPrefix,
false,
func(i int, config *framework.TestServerConfig) {
if i >= IBFTMinNodes {
// Other nodes should not be in the validator set
dirPrefix := "dogechain-non-validator-"
config.SetIBFTDirPrefix(dirPrefix)
config.SetIBFTDir(fmt.Sprintf("%s%d", dirPrefix, i))
}
config.SetSeal(i < IBFTMinNodes)
config.SetBlockTime(1)
})

startContext, startCancelFn := context.WithTimeout(context.Background(), time.Minute)
Expand Down
3 changes: 3 additions & 0 deletions e2e/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func TestSignedTransaction(t *testing.T) {
t,
IBFTMinNodes,
IBFTDirPrefix,
false,
func(i int, config *framework.TestServerConfig) {
config.Premine(senderAddr, preminedAmount)
config.SetSeal(true)
Expand Down Expand Up @@ -511,10 +512,12 @@ func Test_TransactionIBFTLoop(t *testing.T) {
t,
IBFTMinNodes,
IBFTDirPrefix,
false,
func(i int, config *framework.TestServerConfig) {
config.Premine(sender, defaultBalance)
config.SetSeal(true)
config.SetBlockLimit(20000000)
config.SetBlockTime(1)
})

ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
Expand Down
1 change: 1 addition & 0 deletions e2e/txpool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ func TestTxPool_TransactionCoalescing(t *testing.T) {
t,
1,
IBFTDirPrefix,
false,
func(i int, config *framework.TestServerConfig) {
config.SetIBFTPoS(true)
config.SetValidatorSetOwner(fakeAddr)
Expand Down
Loading

0 comments on commit 24ab44e

Please sign in to comment.