code.vegaprotocol.io/vega@v0.79.0/core/pow/engine_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package pow 17 18 import ( 19 "context" 20 "errors" 21 "math/rand" 22 "testing" 23 24 "code.vegaprotocol.io/vega/core/blockchain/abci" 25 "code.vegaprotocol.io/vega/core/pow/mocks" 26 "code.vegaprotocol.io/vega/core/txn" 27 "code.vegaprotocol.io/vega/libs/crypto" 28 "code.vegaprotocol.io/vega/libs/num" 29 "code.vegaprotocol.io/vega/logging" 30 31 "github.com/golang/mock/gomock" 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 ) 35 36 func TestSpamPoWNumberOfPastBlocks(t *testing.T) { 37 e := New(logging.NewTestLogger(), NewDefaultConfig()) 38 e.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(200)) 39 require.Equal(t, uint32(200), e.SpamPoWNumberOfPastBlocks()) 40 } 41 42 func TestSpamPoWDifficulty(t *testing.T) { 43 e := New(logging.NewTestLogger(), NewDefaultConfig()) 44 e.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(20)) 45 require.Equal(t, uint32(20), e.SpamPoWDifficulty()) 46 } 47 48 func TestSpamPoWHashFunction(t *testing.T) { 49 e := New(logging.NewTestLogger(), NewDefaultConfig()) 50 e.UpdateSpamPoWHashFunction(context.Background(), "hash4") 51 require.Equal(t, "hash4", e.SpamPoWHashFunction()) 52 } 53 54 func TestSpamPoWNumberOfTxPerBlock(t *testing.T) { 55 e := New(logging.NewTestLogger(), NewDefaultConfig()) 56 e.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(2)) 57 require.Equal(t, uint32(2), e.SpamPoWNumberOfPastBlocks()) 58 } 59 60 func TestSpamPoWIncreasingDifficulty(t *testing.T) { 61 e := New(logging.NewTestLogger(), NewDefaultConfig()) 62 e.UpdateSpamPoWIncreasingDifficulty(context.Background(), num.NewUint(1)) 63 require.Equal(t, true, e.SpamPoWIncreasingDifficulty()) 64 } 65 66 func TestUpdateNumberOfBlocks(t *testing.T) { 67 e := New(logging.NewTestLogger(), NewDefaultConfig()) 68 e.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(5)) 69 require.Equal(t, uint32(5), e.SpamPoWNumberOfPastBlocks()) 70 } 71 72 func TestCheckTx(t *testing.T) { 73 e := New(logging.NewTestLogger(), NewDefaultConfig()) 74 e.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(5)) 75 e.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(20)) 76 e.UpdateSpamPoWHashFunction(context.Background(), crypto.Sha3) 77 e.UpdateSpamPoWNumberOfTxPerBlock(context.Background(), num.NewUint(1)) 78 79 e.currentBlock = 100 80 e.blockHeight[100] = 100 81 e.blockHash[100] = "113EB390CBEB921433BDBA832CCDFD81AC4C77C3748A41B1AF08C96BC6C7BCD9" 82 e.seenTid["49B0DF0954A8C048554B1C65F4F5883C38640D101A11959EB651AE2065A80BBB"] = struct{}{} 83 e.heightToTid[96] = []string{"49B0DF0954A8C048554B1C65F4F5883C38640D101A11959EB651AE2065A80BBB"} 84 85 // seen transction 86 require.Equal(t, errors.New("proof of work tid already used"), e.CheckTx(&testTx{blockHeight: 100, powTxID: "49B0DF0954A8C048554B1C65F4F5883C38640D101A11959EB651AE2065A80BBB"})) 87 88 // incorrect pow 89 require.Equal(t, errors.New("failed to verify proof of work"), e.CheckTx(&testTx{party: crypto.RandomHash(), blockHeight: 100, powTxID: "077723AB0705677EAA704130D403C21352F87A9AF0E9C4C8F85CC13245FEFED7", powNonce: 1})) 90 91 // all good 92 require.NoError(t, e.CheckTx(&testTx{party: crypto.RandomHash(), blockHeight: 100, powTxID: "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4", powNonce: 596})) 93 } 94 95 func TestCheckBlockTx(t *testing.T) { 96 e := New(logging.NewTestLogger(), NewDefaultConfig()) 97 e.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(5)) 98 e.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(20)) 99 e.UpdateSpamPoWHashFunction(context.Background(), crypto.Sha3) 100 e.UpdateSpamPoWNumberOfTxPerBlock(context.Background(), num.NewUint(1)) 101 102 e.currentBlock = 100 103 e.blockHeight[100] = 100 104 e.blockHash[100] = "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4" 105 106 require.Equal(t, 0, len(e.seenTid)) 107 require.Equal(t, 0, len(e.heightToTid)) 108 party := crypto.RandomHash() 109 tx1 := &testTx{party: party, blockHeight: 100, txID: "1", powTxID: "DFE522E234D67E6AE3F017859F898E576B3928EA57310B765398615A0D3FDE2F", powNonce: 424517} // 00000e31f8ac983354f5885d46b7631bc75f69ec82e8f6178bae53db0ab7e054 - 20 110 res1, d1 := e.CheckBlockTx(tx1) 111 require.Equal(t, ValidationResultSuccess, res1) 112 require.Equal(t, 1, len(e.seenTid)) 113 114 // same transaction within the same proposal should fail verification 115 res2, d2 := e.CheckBlockTx(tx1) 116 require.Equal(t, ValidationResultVerificationPowError, res2) 117 118 // another transaction with invalid nonce should fail verification 119 tx2 := &testTx{party: party, blockHeight: 100, txID: "2", powTxID: "DFE522E234D67E6AE3F017859F898E576B3928EA57310B765398615A0D3FDE2F", powNonce: 1} 120 res3, d3 := e.CheckBlockTx(tx2) 121 require.Equal(t, ValidationResultVerificationPowError, res3) 122 123 // old transaction should fail verification 124 tx3 := &testTx{party: party, blockHeight: 50, txID: "3", powTxID: "5B0E1EB96CCAC120E6D824A5F4C4007EABC59573B861BD84B1EF09DFB376DC84", powNonce: 4031737} 125 res4, d4 := e.CheckBlockTx(tx3) 126 require.Equal(t, ValidationResultVerificationPowError, res4) 127 128 // add another transaction not increasing difficulty 129 tx4 := &testTx{party: party, blockHeight: 100, txID: "4", powTxID: "2A1319636230740888C968E4E7610D6DE820E644EEC3C08AA5322A0A022014BD", powNonce: 1421231} // 000009c5043c4e1dd7fe190ece8d3fd83d94c4e2a2b7800456ce5f5a653c9f75 - 20 130 res5, d5 := e.CheckBlockTx(tx4) 131 require.Equal(t, ValidationResultTooManyTx, res5) 132 133 entries := []ValidationEntry{ 134 {ValResult: res1, Difficulty: d1, Tx: tx1}, 135 {ValResult: res2, Difficulty: d2, Tx: tx1}, 136 {ValResult: res3, Difficulty: d3, Tx: tx2}, 137 {ValResult: res4, Difficulty: d4, Tx: tx3}, 138 {ValResult: res5, Difficulty: d5, Tx: tx4}, 139 } 140 e.rollback(entries) 141 require.Equal(t, 0, len(e.seenTid)) 142 require.Equal(t, 0, len(e.heightToTid)) 143 144 res, d := e.CheckBlockTx(tx1) 145 require.Equal(t, ValidationResultSuccess, res) 146 require.Equal(t, 1, len(e.seenTid)) 147 148 valEntry := ValidationEntry{ValResult: res, Difficulty: d, Tx: tx1} 149 e.rollback([]ValidationEntry{valEntry}) 150 e.BeginBlock(101, crypto.RandomHash(), []abci.Tx{tx1}) 151 require.Equal(t, 1, len(e.heightToTid)) 152 require.Equal(t, 1, len(e.seenTid)) 153 require.Equal(t, "DFE522E234D67E6AE3F017859F898E576B3928EA57310B765398615A0D3FDE2F", e.heightToTid[100][0]) 154 } 155 156 func TestMempoolTidRejection(t *testing.T) { 157 e := New(logging.NewTestLogger(), NewDefaultConfig()) 158 e.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(5)) 159 e.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(20)) 160 e.UpdateSpamPoWHashFunction(context.Background(), crypto.Sha3) 161 e.UpdateSpamPoWNumberOfTxPerBlock(context.Background(), num.NewUint(1)) 162 163 party := crypto.RandomHash() 164 e.currentBlock = 100 165 e.blockHeight[100] = 100 166 e.blockHash[100] = "113EB390CBEB921433BDBA832CCDFD81AC4C77C3748A41B1AF08C96BC6C7BCD9" 167 168 tx1 := &testTx{party: party, blockHeight: 100, powTxID: "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4", powNonce: 596} 169 require.NoError(t, e.CheckTx(tx1)) 170 require.Equal(t, 1, len(e.mempoolSeenTid)) 171 172 require.Error(t, e.CheckTx(tx1)) 173 res, d := e.CheckBlockTx(tx1) 174 e.rollback([]ValidationEntry{{Tx: tx1, Difficulty: d, ValResult: res}}) 175 e.BeginBlock(101, crypto.RandomHash(), []abci.Tx{tx1}) 176 e.OnCommit() 177 require.Equal(t, 1, len(e.seenTid)) 178 _, ok := e.seenTid["2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4"] 179 require.True(t, ok) 180 181 require.Equal(t, 0, len(e.mempoolSeenTid)) 182 require.Error(t, e.CheckTx(tx1)) 183 } 184 185 func TestDeliverTxDuplciateNonce(t *testing.T) { 186 e := New(logging.NewTestLogger(), NewDefaultConfig()) 187 e.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(5)) 188 e.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(20)) 189 e.UpdateSpamPoWHashFunction(context.Background(), crypto.Sha3) 190 e.UpdateSpamPoWNumberOfTxPerBlock(context.Background(), num.NewUint(1)) 191 192 e.currentBlock = 100 193 e.blockHeight[100] = 100 194 e.blockHash[100] = "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4" 195 196 require.Equal(t, 0, len(e.seenTid)) 197 require.Equal(t, 0, len(e.heightToTid)) 198 require.Equal(t, 0, len(e.heightToNonceRef)) 199 party := crypto.RandomHash() 200 201 tx := &testTx{ 202 party: party, 203 blockHeight: 100, 204 powTxID: "94A9CB1532011081B013CCD8E6AAA832CAB1CBA603F0C5A093B14C4961E5E7F0", 205 powNonce: 431336, 206 nonce: 12, 207 } 208 209 require.Equal(t, true, e.ProcessProposal([]abci.Tx{tx})) 210 e.BeginBlock(101, crypto.RandomHash(), []abci.Tx{tx}) 211 212 require.Equal(t, 1, len(e.seenTid)) 213 require.Equal(t, 1, len(e.heightToTid)) 214 require.Equal(t, 1, len(e.heightToNonceRef)) 215 require.Equal(t, 1, len(e.heightToNonceRef[100])) 216 217 // now send it in again with a different pow 218 tx.powNonce = 100 219 tx.powTxID = "113EB390CBEB921433BDBA832CCDFD81AC4C77C3748A41B1AF08C96BC6C7BCD9" 220 tx.txID = crypto.RandomHash() 221 222 require.Equal(t, false, e.ProcessProposal([]abci.Tx{tx})) 223 224 // but a different party can used that nonce at that height 225 tx.powTxID = "DC911C0EA95545441F3E1182DD25D973764395A7E75CBDBC086F1C6F7075AED6" 226 tx.powNonce = 523162 227 tx.party = crypto.RandomHash() 228 229 require.Equal(t, true, e.ProcessProposal([]abci.Tx{tx})) 230 e.BeginBlock(101, crypto.RandomHash(), []abci.Tx{tx}) 231 require.Equal(t, 1, len(e.heightToNonceRef)) 232 require.Equal(t, 2, len(e.heightToNonceRef[100])) 233 234 // check the maps are purged when we leave scope 235 e.BeginBlock(105, crypto.RandomHash(), []abci.Tx{}) 236 require.Equal(t, 0, len(e.seenTid)) 237 require.Equal(t, 0, len(e.heightToTid)) 238 require.Equal(t, 0, len(e.heightToNonceRef)) 239 require.Equal(t, 0, len(e.seenNonceRef)) 240 } 241 242 func TestExpectedDifficulty(t *testing.T) { 243 type args struct { 244 spamPowDifficulty uint 245 spamPoWNumberOfTxPerBlock uint 246 seenTx uint 247 } 248 249 tests := []struct { 250 name string 251 args args 252 wantTotal uint 253 wantDifficulty uint 254 }{ 255 { 256 name: "3 transactions", 257 args: args{ 258 spamPowDifficulty: 20, 259 spamPoWNumberOfTxPerBlock: 5, 260 seenTx: 3, 261 }, 262 wantTotal: 60, // 3 * 20 263 wantDifficulty: 20, 264 }, 265 { 266 name: "5 transactions", 267 args: args{ 268 spamPowDifficulty: 20, 269 spamPoWNumberOfTxPerBlock: 5, 270 seenTx: 5, 271 }, 272 wantTotal: 100, // 5 * 20 273 wantDifficulty: 21, 274 }, 275 { 276 name: "6 transactions", 277 args: args{ 278 spamPowDifficulty: 20, 279 spamPoWNumberOfTxPerBlock: 5, 280 seenTx: 6, 281 }, 282 wantTotal: 121, // 5 * 20 + 21 283 wantDifficulty: 21, 284 }, 285 { 286 name: "9 transactions", 287 args: args{ 288 spamPowDifficulty: 20, 289 spamPoWNumberOfTxPerBlock: 5, 290 seenTx: 9, 291 }, 292 wantTotal: 184, // 5 * 20 + 4 * 21 293 wantDifficulty: 21, 294 }, 295 { 296 name: "10 transactions", 297 args: args{ 298 spamPowDifficulty: 20, 299 spamPoWNumberOfTxPerBlock: 5, 300 seenTx: 10, 301 }, 302 wantTotal: 205, // 5 * 20 + 5 * 21 303 wantDifficulty: 22, 304 }, 305 { 306 name: "20 transactions", 307 args: args{ 308 spamPowDifficulty: 20, 309 spamPoWNumberOfTxPerBlock: 5, 310 seenTx: 20, 311 }, 312 wantTotal: 430, // 5 * 20 + 5 * 21 + 5 * 22 + 5 * 23 313 wantDifficulty: 24, 314 }, 315 { 316 name: "22 transactions", 317 args: args{ 318 spamPowDifficulty: 20, 319 spamPoWNumberOfTxPerBlock: 5, 320 seenTx: 22, 321 }, 322 wantTotal: 478, // 5 * 20 + 5 * 21 + 5 * 22 + 5 * 23 + 2 * 24 323 wantDifficulty: 24, 324 }, 325 } 326 327 for _, tt := range tests { 328 t.Run(tt.name, func(t *testing.T) { 329 gotTotal, gotDifficulty := calculateExpectedDifficulty(tt.args.spamPowDifficulty, tt.args.spamPoWNumberOfTxPerBlock, tt.args.seenTx) 330 require.Equal(t, tt.wantTotal, gotTotal) 331 require.Equal(t, tt.wantDifficulty, gotDifficulty) 332 }) 333 } 334 } 335 336 func TestBeginBlock(t *testing.T) { 337 e := New(logging.NewTestLogger(), NewDefaultConfig()) 338 e.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(3)) 339 e.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(20)) 340 e.UpdateSpamPoWHashFunction(context.Background(), crypto.Sha3) 341 e.UpdateSpamPoWNumberOfTxPerBlock(context.Background(), num.NewUint(1)) 342 343 e.BeginBlock(100, "113EB390CBEB921433BDBA832CCDFD81AC4C77C3748A41B1AF08C96BC6C7BCD9", []abci.Tx{}) 344 e.BeginBlock(101, "C692100485479CE9E1815B9E0A66D3596295A04DB42170CB4B61CFAE7332ADD8", []abci.Tx{}) 345 e.BeginBlock(102, "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4", []abci.Tx{}) 346 347 require.Equal(t, uint64(102), e.currentBlock) 348 require.Equal(t, uint64(100), e.blockHeight[100]) 349 require.Equal(t, uint64(101), e.blockHeight[101]) 350 require.Equal(t, uint64(102), e.blockHeight[102]) 351 require.Equal(t, "113EB390CBEB921433BDBA832CCDFD81AC4C77C3748A41B1AF08C96BC6C7BCD9", e.blockHash[100]) 352 require.Equal(t, "C692100485479CE9E1815B9E0A66D3596295A04DB42170CB4B61CFAE7332ADD8", e.blockHash[101]) 353 require.Equal(t, "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4", e.blockHash[102]) 354 355 // now add some transactions for block 102 before it goes off 356 tx1 := &testTx{txID: "1", party: crypto.RandomHash(), blockHeight: 102, powTxID: "94A9CB1532011081B013CCD8E6AAA832CAB1CBA603F0C5A093B14C4961E5E7F0", powNonce: 431336} 357 tx2 := &testTx{txID: "2", party: crypto.RandomHash(), blockHeight: 102, powTxID: "DC911C0EA95545441F3E1182DD25D973764395A7E75CBDBC086F1C6F7075AED6", powNonce: 523162} 358 359 res1, d1 := e.CheckBlockTx(tx1) 360 res2, d2 := e.CheckBlockTx(tx2) 361 require.Equal(t, ValidationResultSuccess, res1) 362 require.Equal(t, ValidationResultSuccess, res2) 363 e.rollback([]ValidationEntry{{Tx: tx1, ValResult: res1, Difficulty: d1}, {Tx: tx2, ValResult: res2, Difficulty: d2}}) 364 require.Equal(t, 0, len(e.seenTid)) 365 require.Equal(t, 0, len(e.heightToTid[100])) 366 367 e.BeginBlock(103, "2E289FB9CEF7234E2C08F34CCD66B330229067CE47E22F76EF0595B3ABA9968F", []abci.Tx{tx1, tx2}) 368 require.Equal(t, uint64(103), e.currentBlock) 369 require.Equal(t, uint64(103), e.blockHeight[103]) 370 require.Equal(t, uint64(101), e.blockHeight[101]) 371 require.Equal(t, uint64(102), e.blockHeight[102]) 372 require.Equal(t, "C692100485479CE9E1815B9E0A66D3596295A04DB42170CB4B61CFAE7332ADD8", e.blockHash[101]) 373 require.Equal(t, "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4", e.blockHash[102]) 374 require.Equal(t, "2E289FB9CEF7234E2C08F34CCD66B330229067CE47E22F76EF0595B3ABA9968F", e.blockHash[103]) 375 require.Equal(t, 2, len(e.seenTid)) 376 require.Equal(t, 2, len(e.seenTx)) 377 require.Equal(t, 2, len(e.heightToTid[102])) 378 require.Equal(t, *d1, e.activeStates[0].blockToPartyState[102][tx1.party].observedDifficulty) 379 require.Equal(t, *d2, e.activeStates[0].blockToPartyState[102][tx2.party].observedDifficulty) 380 require.Equal(t, uint(1), e.activeStates[0].blockToPartyState[102][tx1.party].seenCount) 381 require.Equal(t, uint(1), e.activeStates[0].blockToPartyState[102][tx2.party].seenCount) 382 } 383 384 func TestAllowTransactionsAcrossMultipleBlocks(t *testing.T) { 385 e := New(logging.NewTestLogger(), NewDefaultConfig()) 386 e.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(10)) 387 e.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(20)) 388 e.UpdateSpamPoWHashFunction(context.Background(), crypto.Sha3) 389 e.UpdateSpamPoWNumberOfTxPerBlock(context.Background(), num.NewUint(1)) 390 e.UpdateSpamPoWIncreasingDifficulty(context.Background(), num.NewUint(1)) 391 e.BeginBlock(100, "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4", []abci.Tx{}) 392 393 // test happy days first - 4 transactions with increasing difficulty results in no ban - regardless of the order they come in 394 party := crypto.RandomHash() 395 txs := []*testTx{ 396 {txID: "9", blockHeight: 100, party: party, powTxID: "DFE522E234D67E6AE3F017859F898E576B3928EA57310B765398615A0D3FDE2F", powNonce: 424517}, // 00000e31f8ac983354f5885d46b7631bc75f69ec82e8f6178bae53db0ab7e054 - 20 397 {txID: "10", blockHeight: 100, party: party, powTxID: "5B0E1EB96CCAC120E6D824A5F4C4007EABC59573B861BD84B1EF09DFB376DC84", powNonce: 4031737}, // 000002a98320df372412d7179ca2645b13ff3ecbe660e4a9a743fb423d8aec1f - 22 398 {txID: "11", blockHeight: 100, party: party, powTxID: "94A9CB1532011081B013CCD8E6AAA832CAB1CBA603F0C5A093B14C4961E5E7F0", powNonce: 431336}, // 000001c297318619efd60b9197f89e36fea83ca8d7461cf7b7c78af84e0a3b51 - 23 399 {txID: "8", blockHeight: 100, party: party, powTxID: "2A1319636230740888C968E4E7610D6DE820E644EEC3C08AA5322A0A022014BD", powNonce: 1421231}, // 000009c5043c4e1dd7fe190ece8d3fd83d94c4e2a2b7800456ce5f5a653c9f75 - 20 400 } 401 402 // process the first transaction on block 101 403 e.BeginBlock(101, crypto.RandomHash(), []abci.Tx{}) 404 res, d := e.CheckBlockTx(txs[0]) 405 require.Equal(t, ValidationResultSuccess, res) 406 e.rollback([]ValidationEntry{{ValResult: res, Difficulty: d, Tx: txs[0]}}) 407 408 // process the second transaction on block 102 409 e.BeginBlock(102, crypto.RandomHash(), []abci.Tx{txs[0]}) 410 res, d = e.CheckBlockTx(txs[1]) 411 require.Equal(t, ValidationResultSuccess, res) 412 e.rollback([]ValidationEntry{{ValResult: res, Difficulty: d, Tx: txs[1]}}) 413 414 // process the third transaction on block 103 415 e.BeginBlock(103, crypto.RandomHash(), []abci.Tx{txs[1]}) 416 res, d = e.CheckBlockTx(txs[2]) 417 require.Equal(t, ValidationResultSuccess, res) 418 e.rollback([]ValidationEntry{{ValResult: res, Difficulty: d, Tx: txs[2]}}) 419 420 // process the last transaction on block 104 421 e.BeginBlock(104, crypto.RandomHash(), []abci.Tx{txs[2]}) 422 res, d = e.CheckBlockTx(txs[3]) 423 require.Equal(t, ValidationResultTooManyTx, res) 424 e.rollback([]ValidationEntry{{ValResult: res, Difficulty: d, Tx: txs[3]}}) 425 } 426 427 func TestEdgeCase1(t *testing.T) { 428 ts := mocks.NewMockTimeService(gomock.NewController(t)) 429 e := New(logging.NewTestLogger(), NewDefaultConfig()) 430 e.UpdateSpamPoWDifficulty(context.Background(), num.NewUint(20)) 431 e.UpdateSpamPoWNumberOfPastBlocks(context.Background(), num.NewUint(100)) 432 e.UpdateSpamPoWHashFunction(context.Background(), crypto.Sha3) 433 ts.EXPECT().GetTimeNow().AnyTimes() 434 435 e.BeginBlock(1, "9DF61AC8AD2178E2E2FD2D94E0F07A4B8AA141213179B03C184F8EAD898A9336", []abci.Tx{}) 436 nonce, _, _ := crypto.PoW("9DF61AC8AD2178E2E2FD2D94E0F07A4B8AA141213179B03C184F8EAD898A9336", "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4", 20, "sha3_24_rounds") 437 438 e.BeginBlock(50, "1D5839A6F7BF1CDB681590890E9D50ECFA222C41F57D1F05229ED3DED533F59A", []abci.Tx{&testTx{txID: "5CCCE01E56B9666F39F007BF577F10BB46987CFE1B1BE80AAC1DBBF51F9C45FE", party: "zohar", blockHeight: 1, powTxID: "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4", powNonce: nonce}}) 439 440 // come block 100 441 e.BeginBlock(100, "2D2E4EC3DA3584F3FD4AD1BD1C0700E3C8DFB7BB1C307312AB35F18940836FC4", []abci.Tx{}) 442 443 // block 100 ended, block 101 is being prepared, at this point transactions from block 1 are not valid anymore. 444 // we've cleared the state of the 100th oldest block seen tx (aka block 1) - now with the modified check for distance in verify - checktx should not allow the transaction in 445 require.Error(t, e.CheckTx(&testTx{txID: "5CCCE01E56B9666F39F007BF577F10BB46987CFE1B1BE80AAC1DBBF51F9C45FE", party: "zohar", blockHeight: 2261296, powTxID: "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4", powNonce: nonce})) 446 e.BeginBlock(101, "2D2E4EC3DA3584F3FD4AD1BD1C0700E3C8DFB7BB1C307312AB35F18940836FC4", []abci.Tx{}) 447 448 // verify for fun that we can't get the transaction at this point either. 449 require.Error(t, e.CheckTx(&testTx{txID: "5CCCE01E56B9666F39F007BF577F10BB46987CFE1B1BE80AAC1DBBF51F9C45FE", party: "zohar", blockHeight: 2261296, powTxID: "2E7A16D9EF690F0D2BEED115FBA13BA2AAA16C8F971910AD88C72B9DB010C7D4", powNonce: nonce})) 450 } 451 452 type testTx struct { 453 party string 454 blockHeight uint64 455 powNonce uint64 456 powTxID string 457 txID string 458 nonce uint64 459 } 460 461 func (tx *testTx) TTL() uint64 { return 100 } 462 func (tx *testTx) GetLength() int { return 0 } 463 func (tx *testTx) Unmarshal(interface{}) error { return nil } 464 func (tx *testTx) GetPoWTID() string { return tx.powTxID } 465 func (tx *testTx) GetVersion() uint32 { return 2 } 466 func (tx *testTx) GetPoWNonce() uint64 { return tx.powNonce } 467 468 func (tx *testTx) Signature() []byte { return []byte{} } 469 func (tx *testTx) Payload() []byte { return []byte{} } 470 func (tx *testTx) PubKey() []byte { return []byte{} } 471 func (tx *testTx) PubKeyHex() string { return "" } 472 func (tx *testTx) Party() string { return tx.party } 473 func (tx *testTx) Hash() []byte { return []byte(tx.txID) } 474 func (tx *testTx) Command() txn.Command { return txn.AmendOrderCommand } 475 func (tx *testTx) BlockHeight() uint64 { return tx.blockHeight } 476 func (tx *testTx) GetCmd() interface{} { return nil } 477 func (tx *testTx) Validate() error { return nil } 478 func (tx *testTx) GetNonce() uint64 { 479 if tx.nonce != 0 { 480 return tx.nonce 481 } 482 return rand.Uint64() 483 } 484 485 func Test_ExpectedSpamDifficulty(t *testing.T) { 486 type args struct { 487 spamPowDifficulty uint 488 spamPoWNumberOfTxPerBlock uint 489 seenTx uint 490 observedDifficulty uint 491 increaseDifficulty bool 492 } 493 494 tests := []struct { 495 name string 496 args args 497 isNil bool 498 want uint64 499 }{ 500 { 501 name: "Expected difficulty after 12 txs", 502 args: args{ 503 spamPowDifficulty: 10, 504 spamPoWNumberOfTxPerBlock: 5, 505 seenTx: 12, 506 observedDifficulty: 132, 507 increaseDifficulty: true, 508 }, 509 isNil: false, 510 want: 10, 511 }, 512 { 513 name: "Expected difficulty after 13 txs", 514 args: args{ 515 spamPowDifficulty: 10, 516 spamPoWNumberOfTxPerBlock: 5, 517 seenTx: 13, 518 observedDifficulty: 142, 519 increaseDifficulty: true, 520 }, 521 isNil: false, 522 want: 11, 523 }, 524 { 525 name: "Expected difficulty after 14 txs", 526 args: args{ 527 spamPowDifficulty: 10, 528 spamPoWNumberOfTxPerBlock: 5, 529 seenTx: 14, 530 observedDifficulty: 153, 531 increaseDifficulty: true, 532 }, 533 isNil: false, 534 want: 12, 535 }, 536 { 537 name: "Expected difficulty after 15 txs", 538 args: args{ 539 spamPowDifficulty: 10, 540 spamPoWNumberOfTxPerBlock: 5, 541 seenTx: 15, 542 observedDifficulty: 166, 543 increaseDifficulty: true, 544 }, 545 isNil: false, 546 want: 12, // after 15txs, the difficulty is increased to 13, but we should have 1 extra in the credit from the previous block 547 }, 548 { 549 name: "Expected difficulty after 16 txs", 550 args: args{ 551 spamPowDifficulty: 10, 552 spamPoWNumberOfTxPerBlock: 5, 553 seenTx: 16, 554 observedDifficulty: 178, 555 increaseDifficulty: true, 556 }, 557 isNil: false, 558 want: 13, 559 }, 560 { 561 name: "Expected difficulty after 17 txs", 562 args: args{ 563 spamPowDifficulty: 10, 564 spamPoWNumberOfTxPerBlock: 5, 565 seenTx: 17, 566 observedDifficulty: 193, 567 increaseDifficulty: true, 568 }, 569 isNil: false, 570 want: 11, 571 }, 572 { 573 name: "Expected difficulty when increaseDifficulty is false", 574 args: args{ 575 spamPowDifficulty: 10, 576 spamPoWNumberOfTxPerBlock: 5, 577 seenTx: 17, 578 observedDifficulty: 193, 579 increaseDifficulty: false, 580 }, 581 isNil: true, 582 }, 583 { 584 name: "Expected difficulty when increaseDifficulty is false but fewer seen than allowed in block", 585 args: args{ 586 spamPowDifficulty: 10, 587 spamPoWNumberOfTxPerBlock: 100, 588 seenTx: 1, 589 observedDifficulty: 10, 590 increaseDifficulty: false, 591 }, 592 isNil: false, 593 want: 10, 594 }, 595 } 596 597 for _, tt := range tests { 598 t.Run(tt.name, func(t *testing.T) { 599 got := getMinDifficultyForNextTx( 600 tt.args.spamPowDifficulty, 601 tt.args.spamPoWNumberOfTxPerBlock, 602 tt.args.seenTx, 603 tt.args.observedDifficulty, 604 tt.args.increaseDifficulty) 605 if tt.isNil { 606 assert.Nil(t, got) 607 return 608 } 609 610 assert.Equal(t, tt.want, *got, "getMinDifficultyForNextTx() = %v, want %v", *got, tt.want) 611 }) 612 } 613 }