github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/consensus/payload_test.go (about) 1 package consensus 2 3 import ( 4 "encoding/hex" 5 gio "io" 6 "math/rand" 7 "testing" 8 9 "github.com/nspcc-dev/dbft" 10 "github.com/nspcc-dev/neo-go/internal/random" 11 "github.com/nspcc-dev/neo-go/internal/testserdes" 12 "github.com/nspcc-dev/neo-go/pkg/config/netmode" 13 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 14 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 15 "github.com/nspcc-dev/neo-go/pkg/io" 16 npayload "github.com/nspcc-dev/neo-go/pkg/network/payload" 17 "github.com/nspcc-dev/neo-go/pkg/util" 18 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 ) 22 23 var messageTypes = []messageType{ 24 changeViewType, 25 prepareRequestType, 26 prepareResponseType, 27 commitType, 28 recoveryRequestType, 29 recoveryMessageType, 30 } 31 32 func TestConsensusPayload_Getters(t *testing.T) { 33 var p = &Payload{ 34 Extensible: npayload.Extensible{}, 35 message: message{ 36 Type: prepareRequestType, 37 BlockIndex: 11, 38 ValidatorIndex: 4, 39 ViewNumber: 2, 40 }, 41 } 42 43 assert.EqualValues(t, 4, p.ValidatorIndex()) 44 assert.EqualValues(t, 11, p.Height()) 45 assert.EqualValues(t, 2, p.ViewNumber()) 46 assert.Equal(t, dbft.PrepareRequestType, p.Type()) 47 48 pl := randomMessage(t, prepareRequestType) 49 p.payload = pl 50 require.Equal(t, pl, p.Payload()) 51 require.Equal(t, pl, p.GetPrepareRequest()) 52 53 pl = randomMessage(t, prepareResponseType) 54 p.payload = pl 55 require.Equal(t, pl, p.GetPrepareResponse()) 56 57 pl = randomMessage(t, commitType) 58 p.payload = pl 59 require.Equal(t, pl, p.GetCommit()) 60 61 pl = randomMessage(t, changeViewType) 62 p.payload = pl 63 require.Equal(t, pl, p.GetChangeView()) 64 65 pl = randomMessage(t, recoveryRequestType) 66 p.payload = pl 67 require.Equal(t, pl, p.GetRecoveryRequest()) 68 69 pl = randomMessage(t, recoveryMessageType) 70 p.payload = pl 71 require.Equal(t, pl, p.GetRecoveryMessage()) 72 } 73 74 func TestConsensusPayload_Serializable(t *testing.T) { 75 for _, mt := range messageTypes { 76 p := randomPayload(t, mt) 77 actual := &Payload{Extensible: npayload.Extensible{}, network: netmode.UnitTestNet} 78 data, err := testserdes.EncodeBinary(p) 79 require.NoError(t, err) 80 require.NoError(t, testserdes.DecodeBinary(data, &actual.Extensible)) 81 assert.NoError(t, actual.decodeData()) 82 require.Equal(t, p, actual) 83 } 84 } 85 86 func TestConsensusPayload_DecodeBinaryInvalid(t *testing.T) { 87 // PrepareResponse payload consists of: 88 // - 1-byte message type (PrepareResponse) 89 // - 4-byte block index 90 // - 1-byte validator index 91 // - 1-byte view number 92 // - 32-byte preparation hash 93 const ( 94 typeIndex = 0 95 size = 39 96 ) 97 98 buf := make([]byte, size) 99 expected := message{ 100 Type: prepareResponseType, 101 payload: &prepareResponse{}, 102 } 103 104 // valid payload 105 buf[typeIndex] = byte(prepareResponseType) 106 p := &Payload{Extensible: npayload.Extensible{Data: buf}} 107 require.NoError(t, p.decodeData()) 108 require.Equal(t, expected, p.message) 109 110 // invalid type 111 buf[typeIndex] = 0xFF 112 p = &Payload{Extensible: npayload.Extensible{Data: buf}} 113 require.Error(t, p.decodeData()) 114 115 // invalid length 116 buf[typeIndex] = byte(prepareResponseType) 117 p = &Payload{Extensible: npayload.Extensible{Data: buf[:len(buf)-1]}} 118 require.Error(t, p.decodeData()) 119 } 120 121 func TestCommit_Serializable(t *testing.T) { 122 c := randomMessage(t, commitType) 123 testserdes.EncodeDecodeBinary(t, c, new(commit)) 124 } 125 126 func TestPrepareResponse_Serializable(t *testing.T) { 127 resp := randomMessage(t, prepareResponseType) 128 testserdes.EncodeDecodeBinary(t, resp, new(prepareResponse)) 129 } 130 131 func TestPrepareRequest_Serializable(t *testing.T) { 132 req := randomMessage(t, prepareRequestType) 133 testserdes.EncodeDecodeBinary(t, req, new(prepareRequest)) 134 } 135 136 func TestRecoveryRequest_Serializable(t *testing.T) { 137 req := randomMessage(t, recoveryRequestType) 138 testserdes.EncodeDecodeBinary(t, req, new(recoveryRequest)) 139 } 140 141 func TestRecoveryMessage_Serializable(t *testing.T) { 142 msg := randomMessage(t, recoveryMessageType) 143 testserdes.EncodeDecodeBinary(t, msg, new(recoveryMessage)) 144 } 145 146 func randomPayload(t *testing.T, mt messageType) *Payload { 147 p := &Payload{ 148 message: message{ 149 Type: mt, 150 ValidatorIndex: byte(rand.Uint32()), 151 BlockIndex: rand.Uint32(), 152 ViewNumber: byte(rand.Uint32()), 153 payload: randomMessage(t, mt), 154 }, 155 Extensible: npayload.Extensible{ 156 Witness: transaction.Witness{ 157 InvocationScript: random.Bytes(3), 158 VerificationScript: []byte{byte(opcode.PUSH0)}, 159 }, 160 }, 161 network: netmode.UnitTestNet, 162 } 163 164 if mt == changeViewType { 165 p.payload.(*changeView).newViewNumber = p.ViewNumber() + 1 166 } 167 168 return p 169 } 170 171 func randomMessage(t *testing.T, mt messageType) io.Serializable { 172 switch mt { 173 case changeViewType: 174 return &changeView{ 175 timestamp: rand.Uint64(), 176 } 177 case prepareRequestType: 178 return randomPrepareRequest(t) 179 case prepareResponseType: 180 return &prepareResponse{preparationHash: random.Uint256()} 181 case commitType: 182 var c commit 183 random.Fill(c.signature[:]) 184 return &c 185 case recoveryRequestType: 186 return &recoveryRequest{timestamp: rand.Uint64()} 187 case recoveryMessageType: 188 return randomRecoveryMessage(t) 189 default: 190 require.Fail(t, "invalid type") 191 return nil 192 } 193 } 194 195 func randomPrepareRequest(t *testing.T) *prepareRequest { 196 const txCount = 3 197 198 req := &prepareRequest{ 199 timestamp: rand.Uint64(), 200 transactionHashes: make([]util.Uint256, txCount), 201 } 202 203 for i := 0; i < txCount; i++ { 204 req.transactionHashes[i] = random.Uint256() 205 } 206 207 return req 208 } 209 210 func randomRecoveryMessage(t *testing.T) *recoveryMessage { 211 result := randomMessage(t, prepareRequestType) 212 require.IsType(t, (*prepareRequest)(nil), result) 213 prepReq := result.(*prepareRequest) 214 215 return &recoveryMessage{ 216 preparationPayloads: []*preparationCompact{ 217 { 218 ValidatorIndex: 1, 219 InvocationScript: random.Bytes(10), 220 }, 221 }, 222 commitPayloads: []*commitCompact{ 223 { 224 ViewNumber: 0, 225 ValidatorIndex: 1, 226 Signature: [keys.SignatureLen]byte{1, 2, 3}, 227 InvocationScript: random.Bytes(20), 228 }, 229 { 230 ViewNumber: 0, 231 ValidatorIndex: 2, 232 Signature: [keys.SignatureLen]byte{11, 3, 4, 98}, 233 InvocationScript: random.Bytes(10), 234 }, 235 }, 236 changeViewPayloads: []*changeViewCompact{ 237 { 238 Timestamp: rand.Uint64(), 239 ValidatorIndex: 3, 240 OriginalViewNumber: 3, 241 InvocationScript: random.Bytes(4), 242 }, 243 }, 244 prepareRequest: &message{ 245 Type: prepareRequestType, 246 payload: prepReq, 247 }, 248 } 249 } 250 251 func TestPayload_Sign(t *testing.T) { 252 key, err := keys.NewPrivateKey() 253 require.NoError(t, err) 254 255 priv := &privateKey{key} 256 257 p := randomPayload(t, prepareRequestType) 258 h := priv.PublicKey().GetScriptHash() 259 bc := newTestChain(t, false) 260 _, err = bc.VerifyWitness(h, p, &p.Witness, payloadGasLimit) 261 require.Error(t, err) 262 require.NoError(t, p.Sign(priv)) 263 _, err = bc.VerifyWitness(h, p, &p.Witness, payloadGasLimit) 264 require.NoError(t, err) 265 } 266 267 func TestMessageType_String(t *testing.T) { 268 require.Equal(t, "ChangeView", changeViewType.String()) 269 require.Equal(t, "PrepareRequest", prepareRequestType.String()) 270 require.Equal(t, "PrepareResponse", prepareResponseType.String()) 271 require.Equal(t, "Commit", commitType.String()) 272 require.Equal(t, "RecoveryMessage", recoveryMessageType.String()) 273 require.Equal(t, "RecoveryRequest", recoveryRequestType.String()) 274 require.Equal(t, "UNKNOWN(0xff)", messageType(0xff).String()) 275 } 276 277 func TestPayload_DecodeFromPrivnet(t *testing.T) { 278 hexDump := "0464424654000000000200000018ca088345a4926bcc9a1daccfba0ac4436082a847300200000003003c57d952539c5e0c39a83c0de5744a772c0dcb0e8ccd7c5bba27ef" + 279 "498506cd860cdfd01ad215b251ab64dc64cd544a6f453f3b0128ddc98d95ac15915dbe6f6301420c40b39c9136af3b8186409dec2dbd31d0fd4f3e637b3eeb96d8556b41f8512dd25d91134f62a6c293db089b7e82b7a0fd23bf9a1" + 280 "5ee26c42a5738b913beef74176d290c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee6990b4195440d78" 281 data, err := hex.DecodeString(hexDump) 282 require.NoError(t, err) 283 284 buf := io.NewBinReaderFromBuf(data) 285 p := NewPayload(netmode.PrivNet, false) 286 p.DecodeBinary(buf) 287 require.NoError(t, buf.Err) 288 require.Equal(t, dbft.CommitType, p.Type()) 289 require.Equal(t, uint32(2), p.Height()) 290 require.Equal(t, uint16(3), p.ValidatorIndex()) 291 require.Equal(t, byte(0), p.ViewNumber()) 292 require.NotNil(t, p.message.payload) 293 294 buf.ReadB() 295 require.Equal(t, gio.EOF, buf.Err) 296 }