github.com/baptiste-b-pegasys/quorum/v22@v22.4.2/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/ethereum/go-ethereum/common" 13 "github.com/ethereum/go-ethereum/common/hexutil" 14 "github.com/ethereum/go-ethereum/core/types" 15 "github.com/ethereum/go-ethereum/crypto" 16 "github.com/ethereum/go-ethereum/node" 17 "github.com/ethereum/go-ethereum/p2p" 18 "github.com/ethereum/go-ethereum/p2p/enode" 19 "github.com/ethereum/go-ethereum/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 }