github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/raft/minter_test.go (about)

     1  package raft
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/coreos/etcd/raft/raftpb"
    11  	mapset "github.com/deckarep/golang-set"
    12  	"github.com/kisexp/xdchain/common"
    13  	"github.com/kisexp/xdchain/common/hexutil"
    14  	"github.com/kisexp/xdchain/core/types"
    15  	"github.com/kisexp/xdchain/crypto"
    16  	"github.com/kisexp/xdchain/node"
    17  	"github.com/kisexp/xdchain/p2p"
    18  	"github.com/kisexp/xdchain/p2p/enode"
    19  	"github.com/kisexp/xdchain/rlp"
    20  )
    21  
    22  const TEST_URL = "enode://3d9ca5956b38557aba991e31cf510d4df641dce9cc26bfeb7de082f0c07abb6ede3a58410c8f249dabeecee4ad3979929ac4c7c496ad20b8cfdd061b7401b4f5@127.0.0.1:21003?discport=0&raftport=50404"
    23  
    24  func TestSignHeader(t *testing.T) {
    25  	//create only what we need to test the seal
    26  	var testRaftId uint16 = 5
    27  	config := &node.Config{Name: "unit-test", DataDir: ""}
    28  	nodeKey := config.NodeKey()
    29  
    30  	raftProtocolManager := &ProtocolManager{raftId: testRaftId}
    31  	raftService := &RaftService{nodeKey: nodeKey, raftProtocolManager: raftProtocolManager}
    32  	minter := minter{eth: raftService}
    33  
    34  	//create some fake header to sign
    35  	fakeParentHash := common.HexToHash("0xc2c1dc1be8054808c69e06137429899d")
    36  
    37  	header := &types.Header{
    38  		ParentHash: fakeParentHash,
    39  		Number:     big.NewInt(1),
    40  		Difficulty: big.NewInt(1),
    41  		GasLimit:   uint64(0),
    42  		GasUsed:    uint64(0),
    43  		Coinbase:   minter.coinbase,
    44  		Time:       uint64(time.Now().UnixNano()),
    45  	}
    46  
    47  	headerHash := header.Hash()
    48  	extraDataBytes := minter.buildExtraSeal(headerHash)
    49  	var seal *extraSeal
    50  	err := rlp.DecodeBytes(extraDataBytes[:], &seal)
    51  	if err != nil {
    52  		t.Fatalf("Unable to decode seal: %s", err.Error())
    53  	}
    54  
    55  	// Check raftId
    56  	sealRaftId, err := hexutil.DecodeUint64("0x" + string(seal.RaftId)) //add the 0x prefix
    57  	if err != nil {
    58  		t.Errorf("Unable to get RaftId: %s", err.Error())
    59  	}
    60  	if sealRaftId != uint64(testRaftId) {
    61  		t.Errorf("RaftID does not match. Expected: %d, Actual: %d", testRaftId, sealRaftId)
    62  	}
    63  
    64  	//Identify who signed it
    65  	sig := seal.Signature
    66  	pubKey, err := crypto.SigToPub(headerHash.Bytes(), sig)
    67  	if err != nil {
    68  		t.Fatalf("Unable to get public key from signature: %s", err.Error())
    69  	}
    70  
    71  	//Compare derived public key to original public key
    72  	if pubKey.X.Cmp(nodeKey.X) != 0 {
    73  		t.Errorf("Signature incorrect!")
    74  	}
    75  
    76  }
    77  
    78  func TestAddLearner_whenTypical(t *testing.T) {
    79  
    80  	raftService := newTestRaftService(t, 1, []uint64{1}, []uint64{})
    81  
    82  	propPeer := func() {
    83  		raftid, err := raftService.raftProtocolManager.ProposeNewPeer(TEST_URL, true)
    84  		if err != nil {
    85  			t.Errorf("propose new peer failed %v\n", err)
    86  		}
    87  		if raftid != raftService.raftProtocolManager.raftId+1 {
    88  			t.Errorf("1. wrong raft id. expected %d got %d\n", raftService.raftProtocolManager.raftId+1, raftid)
    89  		}
    90  	}
    91  	go propPeer()
    92  	select {
    93  	case confChange := <-raftService.raftProtocolManager.confChangeProposalC:
    94  		if confChange.Type != raftpb.ConfChangeAddLearnerNode {
    95  			t.Errorf("expected ConfChangeAddLearnerNode but got %s", confChange.Type.String())
    96  		}
    97  		if uint16(confChange.NodeID) != raftService.raftProtocolManager.raftId+1 {
    98  			t.Errorf("2. wrong raft id. expected %d got %d\n", raftService.raftProtocolManager.raftId+1, uint16(confChange.NodeID))
    99  		}
   100  	case <-time.After(time.Millisecond * 200):
   101  		t.Errorf("add learner conf change not received")
   102  	}
   103  }
   104  
   105  func TestPromoteLearnerToPeer_whenTypical(t *testing.T) {
   106  	learnerRaftId := uint16(3)
   107  	raftService := newTestRaftService(t, 2, []uint64{2}, []uint64{uint64(learnerRaftId)})
   108  	promoteToPeer := func() {
   109  		ok, err := raftService.raftProtocolManager.PromoteToPeer(learnerRaftId)
   110  		if err != nil || !ok {
   111  			t.Errorf("promote learner to peer failed %v\n", err)
   112  		}
   113  	}
   114  	go promoteToPeer()
   115  	select {
   116  	case confChange := <-raftService.raftProtocolManager.confChangeProposalC:
   117  		if confChange.Type != raftpb.ConfChangeAddNode {
   118  			t.Errorf("expected ConfChangeAddNode but got %s", confChange.Type.String())
   119  		}
   120  		if uint16(confChange.NodeID) != learnerRaftId {
   121  			t.Errorf("2. wrong raft id. expected %d got %d\n", learnerRaftId, uint16(confChange.NodeID))
   122  		}
   123  	case <-time.After(time.Millisecond * 200):
   124  		t.Errorf("add learner conf change not received")
   125  	}
   126  }
   127  
   128  func TestAddLearnerOrPeer_fromLearner(t *testing.T) {
   129  
   130  	raftService := newTestRaftService(t, 3, []uint64{2}, []uint64{3})
   131  
   132  	_, err := raftService.raftProtocolManager.ProposeNewPeer(TEST_URL, true)
   133  
   134  	if err == nil {
   135  		t.Errorf("learner should not be allowed to add learner or peer")
   136  	}
   137  
   138  	if err != nil && !strings.Contains(err.Error(), "learner node can't add peer or learner") {
   139  		t.Errorf("expect error message: propose new peer failed, got: %v\n", err)
   140  	}
   141  
   142  	_, err = raftService.raftProtocolManager.ProposeNewPeer(TEST_URL, false)
   143  
   144  	if err == nil {
   145  		t.Errorf("learner should not be allowed to add learner or peer")
   146  	}
   147  
   148  	if err != nil && !strings.Contains(err.Error(), "learner node can't add peer or learner") {
   149  		t.Errorf("expect error message: propose new peer failed, got: %v\n", err)
   150  	}
   151  
   152  }
   153  
   154  func TestPromoteLearnerToPeer_fromLearner(t *testing.T) {
   155  	learnerRaftId := uint16(3)
   156  	raftService := newTestRaftService(t, 2, []uint64{1}, []uint64{2, uint64(learnerRaftId)})
   157  
   158  	_, err := raftService.raftProtocolManager.PromoteToPeer(learnerRaftId)
   159  
   160  	if err == nil {
   161  		t.Errorf("learner should not be allowed to promote to peer")
   162  	}
   163  
   164  	if err != nil && !strings.Contains(err.Error(), "learner node can't promote to peer") {
   165  		t.Errorf("expect error message: propose new peer failed, got: %v\n", err)
   166  	}
   167  
   168  }
   169  
   170  func enodeId(id string, ip string, raftPort int) string {
   171  	return fmt.Sprintf("enode://%s@%s?discport=0&raftport=%d", id, ip, raftPort)
   172  }
   173  
   174  func peerList(url string) (error, []*enode.Node) {
   175  	var nodes []*enode.Node
   176  	node, err := enode.ParseV4(url)
   177  	if err != nil {
   178  		return fmt.Errorf("Node URL %s: %v\n", url, err), nil
   179  	}
   180  	nodes = append(nodes, node)
   181  	return nil, nodes
   182  }
   183  
   184  func newTestRaftService(t *testing.T, raftId uint16, nodes []uint64, learners []uint64) *RaftService {
   185  	//create only what we need to test add learner node
   186  	config := &node.Config{Name: "unit-test", DataDir: ""}
   187  	// This will create a new node key, which is needed to set a stub p2p.Server and avoid `nil pointer dereference` when testing.
   188  	nodeKey := config.NodeKey()
   189  	mockp2pConfig := p2p.Config{Name: "unit-test", ListenAddr: "30303", PrivateKey: nodeKey}
   190  	mockp2p := &p2p.Server{Config: mockp2pConfig}
   191  
   192  	enodeIdStr := fmt.Sprintf("%x", crypto.FromECDSAPub(&nodeKey.PublicKey)[1:])
   193  	url := enodeId(enodeIdStr, "127.0.0.1:21001", 50401)
   194  	err, peers := peerList(url)
   195  	if err != nil {
   196  		t.Errorf("getting peers failed %v", err)
   197  	}
   198  	raftProtocolManager := &ProtocolManager{
   199  		raftId:              raftId,
   200  		bootstrapNodes:      peers,
   201  		confChangeProposalC: make(chan raftpb.ConfChange),
   202  		removedPeers:        mapset.NewSet(),
   203  		confState:           raftpb.ConfState{Nodes: nodes, Learners: learners},
   204  		p2pServer:           mockp2p,
   205  	}
   206  	raftService := &RaftService{nodeKey: nodeKey, raftProtocolManager: raftProtocolManager}
   207  	return raftService
   208  }