github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/consensus/istanbul/backend/backend_test.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package backend 18 19 import ( 20 "bytes" 21 "crypto/ecdsa" 22 "math/big" 23 "sort" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/electroneum/electroneum-sc/common" 29 "github.com/electroneum/electroneum-sc/consensus/istanbul" 30 istanbulcommon "github.com/electroneum/electroneum-sc/consensus/istanbul/common" 31 "github.com/electroneum/electroneum-sc/consensus/istanbul/validator" 32 "github.com/electroneum/electroneum-sc/core/types" 33 "github.com/electroneum/electroneum-sc/crypto" 34 ) 35 36 func TestSign(t *testing.T) { 37 b := newBackend() 38 defer b.Stop() 39 data := []byte("Here is a string....") 40 sig, err := b.Sign(data) 41 if err != nil { 42 t.Errorf("error mismatch: have %v, want nil", err) 43 } 44 //Check signature recover 45 hashData := crypto.Keccak256(data) 46 pubkey, _ := crypto.Ecrecover(hashData, sig) 47 var signer common.Address 48 copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) 49 if signer != getAddress() { 50 t.Errorf("address mismatch: have %v, want %s", signer.Hex(), getAddress().Hex()) 51 } 52 } 53 54 func TestCheckSignature(t *testing.T) { 55 key, _ := generatePrivateKey() 56 data := []byte("Here is a string....") 57 hashData := crypto.Keccak256(data) 58 sig, _ := crypto.Sign(hashData, key) 59 b := newBackend() 60 defer b.Stop() 61 a := getAddress() 62 err := b.CheckSignature(data, a, sig) 63 if err != nil { 64 t.Errorf("error mismatch: have %v, want nil", err) 65 } 66 a = getInvalidAddress() 67 err = b.CheckSignature(data, a, sig) 68 if err != istanbulcommon.ErrInvalidSignature { 69 t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidSignature) 70 } 71 } 72 73 func TestCheckValidatorSignature(t *testing.T) { 74 vset, keys := newTestValidatorSet(5) 75 76 // 1. Positive test: sign with validator's key should succeed 77 data := []byte("dummy data") 78 hashData := crypto.Keccak256(data) 79 for i, k := range keys { 80 // Sign 81 sig, err := crypto.Sign(hashData, k) 82 if err != nil { 83 t.Errorf("error mismatch: have %v, want nil", err) 84 } 85 // CheckValidatorSignature should succeed 86 addr, err := istanbul.CheckValidatorSignature(vset, data, sig) 87 if err != nil { 88 t.Errorf("error mismatch: have %v, want nil", err) 89 } 90 validator := vset.GetByIndex(uint64(i)) 91 if addr != validator.Address() { 92 t.Errorf("validator address mismatch: have %v, want %v", addr, validator.Address()) 93 } 94 } 95 96 // 2. Negative test: sign with any key other than validator's key should return error 97 key, err := crypto.GenerateKey() 98 if err != nil { 99 t.Errorf("error mismatch: have %v, want nil", err) 100 } 101 // Sign 102 sig, err := crypto.Sign(hashData, key) 103 if err != nil { 104 t.Errorf("error mismatch: have %v, want nil", err) 105 } 106 107 // CheckValidatorSignature should return ErrUnauthorizedAddress 108 addr, err := istanbul.CheckValidatorSignature(vset, data, sig) 109 if err != istanbul.ErrUnauthorizedAddress { 110 t.Errorf("error mismatch: have %v, want %v", err, istanbul.ErrUnauthorizedAddress) 111 } 112 emptyAddr := common.Address{} 113 if addr != emptyAddr { 114 t.Errorf("address mismatch: have %v, want %v", addr, emptyAddr) 115 } 116 } 117 118 func TestCommit(t *testing.T) { 119 backend := newBackend() 120 defer backend.Stop() 121 122 commitCh := make(chan *types.Block) 123 // Case: it's a proposer, so the backend.commit will receive channel result from backend.Commit function 124 testCases := []struct { 125 expectedErr error 126 expectedSignature [][]byte 127 expectedBlock func() *types.Block 128 }{ 129 { 130 // normal case 131 nil, 132 [][]byte{append([]byte{1}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraSeal-1)...)}, 133 func() *types.Block { 134 chain, engine := newBlockChain(1) 135 block := makeBlockWithoutSeal(chain, engine, chain.Genesis(), true) 136 return updateQBFTBlock(block, engine.Address()) 137 }, 138 }, 139 { 140 // invalid signature 141 istanbulcommon.ErrInvalidCommittedSeals, 142 nil, 143 func() *types.Block { 144 chain, engine := newBlockChain(1) 145 block := makeBlockWithoutSeal(chain, engine, chain.Genesis(), true) 146 return updateQBFTBlock(block, engine.Address()) 147 }, 148 }, 149 } 150 151 for _, test := range testCases { 152 expBlock := test.expectedBlock() 153 go func() { 154 result := <-backend.commitCh 155 commitCh <- result 156 }() 157 158 backend.proposedBlockHash = expBlock.Hash() 159 if err := backend.Commit(expBlock, test.expectedSignature, big.NewInt(0)); err != nil { 160 if err != test.expectedErr { 161 t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) 162 } 163 } 164 165 if test.expectedErr == nil { 166 // to avoid race condition is occurred by goroutine 167 select { 168 case result := <-commitCh: 169 if result.Hash() != expBlock.Hash() { 170 t.Errorf("hash mismatch: have %v, want %v", result.Hash(), expBlock.Hash()) 171 } 172 case <-time.After(10 * time.Second): 173 t.Fatal("timeout") 174 } 175 } 176 } 177 } 178 179 func TestGetProposer(t *testing.T) { 180 chain, engine := newBlockChain(1) 181 defer engine.Stop() 182 block := makeBlock(chain, engine, chain.Genesis()) 183 chain.InsertChain(types.Blocks{block}) 184 expected := engine.GetProposer(1) 185 actual := engine.Address() 186 if actual != expected { 187 t.Errorf("proposer mismatch: have %v, want %v", actual.Hex(), expected.Hex()) 188 } 189 } 190 191 /** 192 * SimpleBackend 193 * Private key: bb047e5940b6d83354d9432db7c449ac8fca2248008aaa7271369880f9f11cc1 194 * Public key: 04a2bfb0f7da9e1b9c0c64e14f87e8fb82eb0144e97c25fe3a977a921041a50976984d18257d2495e7bfd3d4b280220217f429287d25ecdf2b0d7c0f7aae9aa624 195 * Address: 0x70524d664ffe731100208a0154e556f9bb679ae6 196 */ 197 func getAddress() common.Address { 198 return common.HexToAddress("0x70524d664ffe731100208a0154e556f9bb679ae6") 199 } 200 201 func getInvalidAddress() common.Address { 202 return common.HexToAddress("0x9535b2e7faaba5288511d89341d94a38063a349b") 203 } 204 205 func generatePrivateKey() (*ecdsa.PrivateKey, error) { 206 key := "bb047e5940b6d83354d9432db7c449ac8fca2248008aaa7271369880f9f11cc1" 207 return crypto.HexToECDSA(key) 208 } 209 210 func newTestValidatorSet(n int) (istanbul.ValidatorSet, []*ecdsa.PrivateKey) { 211 // generate validators 212 keys := make(Keys, n) 213 addrs := make([]common.Address, n) 214 for i := 0; i < n; i++ { 215 privateKey, _ := crypto.GenerateKey() 216 keys[i] = privateKey 217 addrs[i] = crypto.PubkeyToAddress(privateKey.PublicKey) 218 } 219 vset := validator.NewSet(addrs, istanbul.NewRoundRobinProposerPolicy()) 220 sort.Sort(keys) //Keys need to be sorted by its public key address 221 return vset, keys 222 } 223 224 type Keys []*ecdsa.PrivateKey 225 226 func (slice Keys) Len() int { 227 return len(slice) 228 } 229 230 func (slice Keys) Less(i, j int) bool { 231 return strings.Compare(crypto.PubkeyToAddress(slice[i].PublicKey).String(), crypto.PubkeyToAddress(slice[j].PublicKey).String()) < 0 232 } 233 234 func (slice Keys) Swap(i, j int) { 235 slice[i], slice[j] = slice[j], slice[i] 236 } 237 238 func newBackend() (b *Backend) { 239 _, b = newBlockChain(1) 240 key, _ := generatePrivateKey() 241 b.privateKey = key 242 return 243 }