github.com/deso-protocol/core@v1.2.9/lib/block_view_test.go (about) 1 package lib 2 3 import ( 4 "fmt" 5 "github.com/dgraph-io/badger/v3" 6 "github.com/stretchr/testify/assert" 7 "github.com/stretchr/testify/require" 8 "testing" 9 10 _ "net/http/pprof" 11 ) 12 13 func _strToPk(t *testing.T, pkStr string) []byte { 14 require := require.New(t) 15 16 pkBytes, _, err := Base58CheckDecode(pkStr) 17 require.NoError(err) 18 19 return pkBytes 20 } 21 22 func getTxnSize(txn MsgDeSoTxn) int64 { 23 bytes, _ := txn.ToBytes(false) 24 return int64(len(bytes)) 25 } 26 27 var ( 28 // Set up some addresses 29 m0Pub = "tBCKY2X1Gbqn95tN1PfsCFLKX6x6h48g5LdHt9T95Wj9Rm6EVKLVpi" 30 m0Priv = "tbc2uXFwv3CJvr5HdLLKpAtLNCtBafvfxLBMbJFCNdLA61cLB7aLq" 31 m0PkBytes, _, _ = Base58CheckDecode(m0Pub) 32 33 m1Pub = "tBCKYGWj36qERG57RKdrnCf6JQad1smGTzeLkj1bfN7UqKwY8SM57a" 34 m1Priv = "tbc2DtxgxPVB6T6sbFqhgNrPqwb7QUYG5ZS7aEXQ3ZxAyG88YAPVy" 35 m1PkBytes, _, _ = Base58CheckDecode(m1Pub) 36 37 m2Pub = "tBCKVNYw7WgG59SGP8EdpR9nyywoMBYa3ChLG4UjCBhvFgd4e7oXNg" 38 m2Priv = "tbc37VGdu4RJ7uJcoGHrDJkr4FZPsVYbyo3dRxdhyQHPNp6jUjbK1" 39 m2PkBytes, _, _ = Base58CheckDecode(m2Pub) 40 41 m3Pub = "tBCKWqMGE7xdz78juDSEsDFYt67CuL9VrTiv627Wj2sLwG6B2fcy7o" 42 m3Priv = "tbc2MkEWaCoVNh5rV4fyAdSmAkLQ9bZLqEMGSLYtoAAxgA1844Y67" 43 m3PkBytes, _, _ = Base58CheckDecode(m3Pub) 44 45 m4Pub = "tBCKWu6nNQa3cUV8QLwRhX9r6NXcNpDuK7xtscwm27zXJ7MxdnmZ3g" 46 m4Priv = "tbc2GmpAmkm8CmMjS9NXiAFZHEDGqxSCCpkvkwnY8oqfZXAXnmtFV" 47 m4PkBytes, _, _ = Base58CheckDecode(m4Pub) 48 49 m5Pub = "tBCKWWAqRR89yCLGEbw2QXK32XZkgEacnrZbdc1KrXk5NzeDvfTr4h" 50 m5Priv = "tbc2w7CpjUTcmtLdAPxb8BwYQ8W66Qn8hDcgLxyHGJWfbuT4RFtjz" 51 m5PkBytes, _, _ = Base58CheckDecode(m5Pub) 52 53 m6Pub = "tBCKX5xzB91EPszJq6Ep4AHf7nKi9BXBFeb7o668N3bryz5deqvCBo" 54 m6Priv = "tbc2hN9pnZVnA8TCtV76tZKt5wfLsHyQ5jo9s7NxRswa1h5Y4Hbgg" 55 m6PkBytes, _, _ = Base58CheckDecode(m6Pub) 56 57 paramUpdaterPub = "tBCKWVdVW6St5R8KkbQYd9uhvwmna4EVAeEKBXRsZLVrCM1JHkEU1G" 58 paramUpdaterPriv = "tbc1jF5hXKspbYUVqkSwyyrs9oSho8yA6vZURvBNLySVESFsRmaGf" 59 paramUpdaterPkBytes, _, _ = Base58CheckDecode(paramUpdaterPub) 60 ) 61 62 func _doBasicTransferWithViewFlush(t *testing.T, chain *Blockchain, db *badger.DB, 63 params *DeSoParams, pkSenderStr string, pkReceiverStr string, privStr string, 64 amountNanos uint64, feeRateNanosPerKB uint64) ( 65 _utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32) { 66 67 assert := assert.New(t) 68 require := require.New(t) 69 _, _ = assert, require 70 71 txn := _assembleBasicTransferTxnFullySigned( 72 t, chain, amountNanos, feeRateNanosPerKB, pkSenderStr, pkReceiverStr, privStr, nil) 73 74 utxoView, err := NewUtxoView(db, params, nil) 75 require.NoError(err) 76 77 // Always use height+1 for validation since it's assumed the transaction will 78 // get mined into the next block. 79 txHash := txn.Hash() 80 blockHeight := chain.blockTip().Height + 1 81 utxoOps, totalInput, totalOutput, fees, err := 82 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 83 require.NoError(err) 84 require.GreaterOrEqual(totalOutput, amountNanos) 85 require.Equal(totalInput, totalOutput+fees) 86 87 require.Equal(len(txn.TxInputs)+len(txn.TxOutputs), len(utxoOps)) 88 for ii := 0; ii < len(txn.TxInputs); ii++ { 89 require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type) 90 } 91 for ii := len(txn.TxInputs); ii < len(txn.TxInputs)+len(txn.TxOutputs); ii++ { 92 require.Equal(OperationTypeAddUtxo, utxoOps[ii].Type) 93 } 94 95 require.NoError(utxoView.FlushToDb()) 96 97 return utxoOps, txn, blockHeight 98 } 99 100 func _registerOrTransferWithTestMeta(testMeta *TestMeta, username string, 101 senderPk string, recipientPk string, senderPriv string, amountToSend uint64) { 102 103 testMeta.expectedSenderBalances = append( 104 testMeta.expectedSenderBalances, _getBalance(testMeta.t, testMeta.chain, nil, senderPk)) 105 106 currentOps, currentTxn, _ := _doBasicTransferWithViewFlush( 107 testMeta.t, testMeta.chain, testMeta.db, testMeta.params, senderPk, recipientPk, 108 senderPriv, amountToSend, 11 /*feerate*/) 109 110 testMeta.txnOps = append(testMeta.txnOps, currentOps) 111 testMeta.txns = append(testMeta.txns, currentTxn) 112 } 113 114 func _updateGlobalParamsEntry(t *testing.T, chain *Blockchain, db *badger.DB, 115 params *DeSoParams, feeRateNanosPerKB uint64, updaterPkBase58Check string, 116 updaterPrivBase58Check string, usdCentsPerBitcoin int64, minimumNetworkFeesNanosPerKB int64, 117 createProfileFeeNanos int64, createNFTFeeNanos int64, maxCopiesPerNFT int64, flushToDb bool) ( 118 _utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) { 119 120 assert := assert.New(t) 121 require := require.New(t) 122 _ = assert 123 _ = require 124 125 updaterPkBytes, _, err := Base58CheckDecode(updaterPkBase58Check) 126 require.NoError(err) 127 128 txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateUpdateGlobalParamsTxn( 129 updaterPkBytes, 130 usdCentsPerBitcoin, 131 createProfileFeeNanos, 132 createNFTFeeNanos, 133 maxCopiesPerNFT, 134 minimumNetworkFeesNanosPerKB, 135 nil, 136 feeRateNanosPerKB, 137 nil, 138 []*DeSoOutput{}) 139 if err != nil { 140 return nil, nil, 0, err 141 } 142 143 require.Equal(totalInputMake, changeAmountMake+feesMake) 144 145 // Sign the transaction now that its inputs are set up. 146 _signTxn(t, txn, updaterPrivBase58Check) 147 148 utxoView, err := NewUtxoView(db, params, nil) 149 require.NoError(err) 150 151 txHash := txn.Hash() 152 // Always use height+1 for validation since it's assumed the transaction will 153 // get mined into the next block. 154 blockHeight := chain.blockTip().Height + 1 155 utxoOps, totalInput, totalOutput, fees, err := 156 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 157 // ConnectTransaction should treat the amount locked as contributing to the 158 // output. 159 if err != nil { 160 return nil, nil, 0, err 161 } 162 require.Equal(totalInput, totalOutput+fees) 163 require.Equal(totalInput, totalInputMake) 164 165 // We should have one SPEND UtxoOperation for each input, one ADD operation 166 // for each output, and one OperationTypePrivateMessage operation at the end. 167 require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps)) 168 for ii := 0; ii < len(txn.TxInputs); ii++ { 169 require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type) 170 } 171 require.Equal( 172 OperationTypeUpdateGlobalParams, utxoOps[len(utxoOps)-1].Type) 173 if flushToDb { 174 require.NoError(utxoView.FlushToDb()) 175 } 176 return utxoOps, txn, blockHeight, nil 177 } 178 179 func _updateGlobalParamsEntryWithTestMeta( 180 testMeta *TestMeta, 181 feeRateNanosPerKB uint64, 182 updaterPkBase58Check string, 183 updaterPrivBase58Check string, 184 USDCentsPerBitcoinExchangeRate int64, 185 minimumNetworkFeeNanosPerKb int64, 186 createProfileFeeNanos int64, 187 createNFTFeeNanos int64, 188 maxCopiesPerNFT int64, 189 ) { 190 191 testMeta.expectedSenderBalances = append( 192 testMeta.expectedSenderBalances, 193 _getBalance(testMeta.t, testMeta.chain, nil, updaterPkBase58Check)) 194 195 currentOps, currentTxn, _, err := _updateGlobalParamsEntry( 196 testMeta.t, testMeta.chain, testMeta.db, testMeta.params, 197 feeRateNanosPerKB, 198 updaterPkBase58Check, 199 updaterPrivBase58Check, 200 int64(InitialUSDCentsPerBitcoinExchangeRate), 201 minimumNetworkFeeNanosPerKb, 202 createProfileFeeNanos, 203 createNFTFeeNanos, 204 maxCopiesPerNFT, 205 true) /*flushToDB*/ 206 require.NoError(testMeta.t, err) 207 testMeta.txnOps = append(testMeta.txnOps, currentOps) 208 testMeta.txns = append(testMeta.txns, currentTxn) 209 } 210 211 type TestMeta struct { 212 t *testing.T 213 chain *Blockchain 214 db *badger.DB 215 params *DeSoParams 216 mempool *DeSoMempool 217 miner *DeSoMiner 218 txnOps [][]*UtxoOperation 219 txns []*MsgDeSoTxn 220 expectedSenderBalances []uint64 221 savedHeight uint32 222 } 223 224 func _rollBackTestMetaTxnsAndFlush(testMeta *TestMeta) { 225 // Roll back all of the above using the utxoOps from each. 226 for ii := 0; ii < len(testMeta.txnOps); ii++ { 227 backwardIter := len(testMeta.txnOps) - 1 - ii 228 currentOps := testMeta.txnOps[backwardIter] 229 currentTxn := testMeta.txns[backwardIter] 230 fmt.Printf( 231 "Disconnecting transaction with type %v index %d (going backwards)\n", 232 currentTxn.TxnMeta.GetTxnType(), backwardIter) 233 234 utxoView, err := NewUtxoView(testMeta.db, testMeta.params, nil) 235 require.NoError(testMeta.t, err) 236 237 currentHash := currentTxn.Hash() 238 err = utxoView.DisconnectTransaction(currentTxn, currentHash, currentOps, testMeta.savedHeight) 239 require.NoError(testMeta.t, err) 240 241 require.NoError(testMeta.t, utxoView.FlushToDb()) 242 243 // After disconnecting, the balances should be restored to what they 244 // were before this transaction was applied. 245 require.Equal( 246 testMeta.t, 247 testMeta.expectedSenderBalances[backwardIter], 248 _getBalance(testMeta.t, testMeta.chain, nil, PkToStringTestnet(currentTxn.PublicKey)), 249 ) 250 } 251 } 252 253 func _applyTestMetaTxnsToMempool(testMeta *TestMeta) { 254 // Apply all the transactions to a mempool object and make sure we don't get any 255 // errors. Verify the balances align as we go. 256 for ii, tx := range testMeta.txns { 257 require.Equal( 258 testMeta.t, 259 testMeta.expectedSenderBalances[ii], 260 _getBalance(testMeta.t, testMeta.chain, testMeta.mempool, PkToStringTestnet(tx.PublicKey))) 261 262 fmt.Printf("Adding txn %d of type %v to mempool\n", ii, tx.TxnMeta.GetTxnType()) 263 264 _, err := testMeta.mempool.ProcessTransaction(tx, false, false, 0, true) 265 require.NoError(testMeta.t, err, "Problem adding transaction %d to mempool: %v", ii, tx) 266 } 267 } 268 269 func _applyTestMetaTxnsToViewAndFlush(testMeta *TestMeta) { 270 // Apply all the transactions to a view and flush the view to the db. 271 utxoView, err := NewUtxoView(testMeta.db, testMeta.params, nil) 272 require.NoError(testMeta.t, err) 273 for ii, txn := range testMeta.txns { 274 fmt.Printf("Adding txn %v of type %v to UtxoView\n", ii, txn.TxnMeta.GetTxnType()) 275 276 // Always use height+1 for validation since it's assumed the transaction will 277 // get mined into the next block. 278 txHash := txn.Hash() 279 blockHeight := testMeta.chain.blockTip().Height + 1 280 _, _, _, _, err = 281 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 282 require.NoError(testMeta.t, err) 283 } 284 // Flush the utxoView after having added all the transactions. 285 require.NoError(testMeta.t, utxoView.FlushToDb()) 286 } 287 288 func _disconnectTestMetaTxnsFromViewAndFlush(testMeta *TestMeta) { 289 // Disonnect the transactions from a single view in the same way as above 290 // i.e. without flushing each time. 291 utxoView, err := NewUtxoView(testMeta.db, testMeta.params, nil) 292 require.NoError(testMeta.t, err) 293 for ii := 0; ii < len(testMeta.txnOps); ii++ { 294 backwardIter := len(testMeta.txnOps) - 1 - ii 295 fmt.Printf("Disconnecting transaction with index %d (going backwards)\n", backwardIter) 296 currentOps := testMeta.txnOps[backwardIter] 297 currentTxn := testMeta.txns[backwardIter] 298 299 currentHash := currentTxn.Hash() 300 err = utxoView.DisconnectTransaction(currentTxn, currentHash, currentOps, testMeta.savedHeight) 301 require.NoError(testMeta.t, err) 302 } 303 require.NoError(testMeta.t, utxoView.FlushToDb()) 304 require.Equal( 305 testMeta.t, 306 testMeta.expectedSenderBalances[0], 307 _getBalance(testMeta.t, testMeta.chain, nil, senderPkString)) 308 } 309 310 func _connectBlockThenDisconnectBlockAndFlush(testMeta *TestMeta) { 311 // all those transactions in it. 312 block, err := testMeta.miner.MineAndProcessSingleBlock(0 /*threadIndex*/, testMeta.mempool) 313 require.NoError(testMeta.t, err) 314 // Add one for the block reward. Now we have a meaty block. 315 require.Equal(testMeta.t, len(testMeta.txnOps)+1, len(block.Txns)) 316 317 // Roll back the block and make sure we don't hit any errors. 318 { 319 utxoView, err := NewUtxoView(testMeta.db, testMeta.params, nil) 320 require.NoError(testMeta.t, err) 321 322 // Fetch the utxo operations for the block we're detaching. We need these 323 // in order to be able to detach the block. 324 hash, err := block.Header.Hash() 325 require.NoError(testMeta.t, err) 326 utxoOps, err := GetUtxoOperationsForBlock(testMeta.db, hash) 327 require.NoError(testMeta.t, err) 328 329 // Compute the hashes for all the transactions. 330 txHashes, err := ComputeTransactionHashes(block.Txns) 331 require.NoError(testMeta.t, err) 332 require.NoError(testMeta.t, utxoView.DisconnectBlock(block, txHashes, utxoOps)) 333 334 // Flushing the view after applying and rolling back should work. 335 require.NoError(testMeta.t, utxoView.FlushToDb()) 336 } 337 } 338 339 func TestUpdateGlobalParams(t *testing.T) { 340 // Set up a blockchain 341 assert := assert.New(t) 342 require := require.New(t) 343 _, _ = assert, require 344 345 chain, params, db := NewLowDifficultyBlockchain() 346 mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/) 347 _, _ = mempool, miner 348 349 // Set the founder equal to the moneyPk 350 params.ParamUpdaterPublicKeys = make(map[PkMapKey]bool) 351 params.ParamUpdaterPublicKeys[MakePkMapKey(MustBase58CheckDecode(moneyPkString))] = true 352 353 // Send money to m0 from moneyPk 354 _, _, _ = _doBasicTransferWithViewFlush( 355 t, chain, db, params, moneyPkString, m0Pub, 356 moneyPrivString, 10*NanosPerUnit /*amount to send*/, 11 /*feerate*/) 357 358 // Should fail when founder key is not equal to moneyPk 359 { 360 newUSDCentsPerBitcoin := int64(27000 * 100) 361 newMinimumNetworkFeeNanosPerKB := int64(100) 362 newCreateProfileFeeNanos := int64(200) 363 newCreateNFTFeeNanos := int64(300) 364 _, _, _, err := _updateGlobalParamsEntry( 365 t, chain, db, params, 100, /*feeRateNanosPerKB*/ 366 m0Pub, 367 m0Priv, 368 newUSDCentsPerBitcoin, 369 newMinimumNetworkFeeNanosPerKB, 370 newCreateProfileFeeNanos, 371 newCreateNFTFeeNanos, 372 -1, /*maxCopiesPerNFT*/ 373 false) 374 require.Error(err) 375 require.Contains(err.Error(), RuleErrorUserNotAuthorizedToUpdateGlobalParams) 376 } 377 378 // Should pass when founder key is equal to moneyPk 379 var updateGlobalParamsTxn *MsgDeSoTxn 380 var err error 381 { 382 newUSDCentsPerBitcoin := int64(270430 * 100) 383 newMinimumNetworkFeeNanosPerKB := int64(191) 384 newCreateProfileFeeNanos := int64(10015) 385 newCreateNFTFeeNanos := int64(14983) 386 newMaxCopiesPerNFT := int64(123) 387 _, updateGlobalParamsTxn, _, err = _updateGlobalParamsEntry( 388 t, chain, db, params, 200, /*feeRateNanosPerKB*/ 389 moneyPkString, 390 moneyPrivString, 391 newUSDCentsPerBitcoin, 392 newMinimumNetworkFeeNanosPerKB, 393 newCreateProfileFeeNanos, 394 newCreateNFTFeeNanos, 395 newMaxCopiesPerNFT, 396 false) 397 require.NoError(err) 398 399 utxoView, err := NewUtxoView(db, params, nil) 400 require.NoError(err) 401 txnSize := getTxnSize(*updateGlobalParamsTxn) 402 blockHeight := chain.blockTip().Height + 1 403 utxoOps, totalInput, totalOutput, fees, err := 404 utxoView.ConnectTransaction(updateGlobalParamsTxn, 405 updateGlobalParamsTxn.Hash(), txnSize, blockHeight, true, /*verifySignature*/ 406 false /*ignoreUtxos*/) 407 require.NoError(err) 408 _, _, _, _ = utxoOps, totalInput, totalOutput, fees 409 require.NoError(utxoView.FlushToDb()) 410 411 // Verify that utxoView and db reflect the new global parmas entry. 412 expectedGlobalParams := GlobalParamsEntry{ 413 USDCentsPerBitcoin: uint64(newUSDCentsPerBitcoin), 414 MinimumNetworkFeeNanosPerKB: uint64(newMinimumNetworkFeeNanosPerKB), 415 CreateProfileFeeNanos: uint64(newCreateProfileFeeNanos), 416 CreateNFTFeeNanos: uint64(newCreateNFTFeeNanos), 417 MaxCopiesPerNFT: 123, 418 } 419 require.Equal(DbGetGlobalParamsEntry(utxoView.Handle), &expectedGlobalParams) 420 421 require.Equal(utxoView.GlobalParamsEntry, &expectedGlobalParams) 422 423 // Check the balance of the updater after this txn 424 require.NotEqual(0, _getBalance(t, chain, nil, moneyPkString)) 425 } 426 427 { 428 429 // Save the prev global params entry so we can check it after disconnect. 430 prevGlobalParams := DbGetGlobalParamsEntry(db) 431 432 newUSDCentsPerBitcoin := int64(270434 * 100) 433 newMinimumNetworkFeeNanosPerKB := int64(131) 434 newCreateProfileFeeNanos := int64(102315) 435 newCreateNFTFeeNanos := int64(3244099) 436 newMaxCopiesPerNFT := int64(555) 437 var utxoOps []*UtxoOperation 438 utxoOps, updateGlobalParamsTxn, _, err = _updateGlobalParamsEntry( 439 t, chain, db, params, 200, /*feeRateNanosPerKB*/ 440 moneyPkString, 441 moneyPrivString, 442 newUSDCentsPerBitcoin, 443 newMinimumNetworkFeeNanosPerKB, 444 newCreateProfileFeeNanos, 445 newCreateNFTFeeNanos, 446 newMaxCopiesPerNFT, /*maxCopiesPerNFT*/ 447 true) 448 require.NoError(err) 449 450 // Verify that the db reflects the new global params entry. 451 expectedGlobalParams := &GlobalParamsEntry{ 452 USDCentsPerBitcoin: uint64(newUSDCentsPerBitcoin), 453 MinimumNetworkFeeNanosPerKB: uint64(newMinimumNetworkFeeNanosPerKB), 454 CreateProfileFeeNanos: uint64(newCreateProfileFeeNanos), 455 CreateNFTFeeNanos: uint64(newCreateNFTFeeNanos), 456 MaxCopiesPerNFT: uint64(newMaxCopiesPerNFT), 457 } 458 459 require.Equal(DbGetGlobalParamsEntry(db), expectedGlobalParams) 460 461 // Now let's do a disconnect and make sure the values reflect the previous entry. 462 utxoView, err := NewUtxoView(db, params, nil) 463 require.NoError(err) 464 blockHeight := chain.blockTip().Height + 1 465 utxoView.DisconnectTransaction( 466 updateGlobalParamsTxn, updateGlobalParamsTxn.Hash(), utxoOps, blockHeight) 467 468 require.NoError(utxoView.FlushToDb()) 469 470 require.Equal(DbGetGlobalParamsEntry(utxoView.Handle), prevGlobalParams) 471 require.Equal(utxoView.GlobalParamsEntry, prevGlobalParams) 472 473 // Check the balance of the updater after this txn 474 require.NotEqual(0, _getBalance(t, chain, nil, moneyPkString)) 475 } 476 } 477 478 func TestBasicTransfer(t *testing.T) { 479 assert := assert.New(t) 480 require := require.New(t) 481 _ = assert 482 _ = require 483 484 chain, params, db := NewLowDifficultyBlockchain() 485 mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/) 486 // Mine two blocks to give the sender some DeSo. 487 _, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 488 require.NoError(err) 489 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 490 require.NoError(err) 491 492 senderPkBytes, _, err := Base58CheckDecode(senderPkString) 493 require.NoError(err) 494 recipientPkBytes, _, err := Base58CheckDecode(recipientPkString) 495 require.NoError(err) 496 497 // A basic transfer whose input public keys differ from the 498 // transaction-level public key should fail. 499 { 500 txn := &MsgDeSoTxn{ 501 // The inputs will be set below. 502 TxInputs: []*DeSoInput{}, 503 TxOutputs: []*DeSoOutput{ 504 { 505 PublicKey: recipientPkBytes, 506 AmountNanos: 1, 507 }, 508 }, 509 PublicKey: senderPkBytes, 510 TxnMeta: &BasicTransferMetadata{}, 511 } 512 513 totalInput, spendAmount, changeAmount, fees, err := 514 chain.AddInputsAndChangeToTransaction(txn, 10, nil) 515 require.NoError(err) 516 require.Equal(totalInput, spendAmount+changeAmount+fees) 517 require.Greater(totalInput, uint64(0)) 518 519 // At this point the txn has inputs for senderPkString. Change 520 // the public key to recipientPkString and sign it with the 521 // recipientPrivString. 522 txn.PublicKey = recipientPkBytes 523 524 _signTxn(t, txn, recipientPrivString) 525 utxoView, _ := NewUtxoView(db, params, nil) 526 txHash := txn.Hash() 527 blockHeight := chain.blockTip().Height + 1 528 _, _, _, _, err = 529 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, 530 true /*verifySignatures*/, false /*ignoreUtxos*/) 531 require.Error(err) 532 require.Contains(err.Error(), RuleErrorInputWithPublicKeyDifferentFromTxnPublicKey) 533 } 534 535 // Just a basic transfer with a bad signature. 536 { 537 txn := &MsgDeSoTxn{ 538 // The inputs will be set below. 539 TxInputs: []*DeSoInput{}, 540 TxOutputs: []*DeSoOutput{ 541 { 542 PublicKey: recipientPkBytes, 543 AmountNanos: 1, 544 }, 545 }, 546 PublicKey: senderPkBytes, 547 TxnMeta: &BasicTransferMetadata{}, 548 } 549 550 totalInput, spendAmount, changeAmount, fees, err := 551 chain.AddInputsAndChangeToTransaction(txn, 10, nil) 552 require.NoError(err) 553 require.Equal(totalInput, spendAmount+changeAmount+fees) 554 require.Greater(totalInput, uint64(0)) 555 556 // Sign the transaction with the recipient's key rather than the 557 // sender's key. 558 _signTxn(t, txn, recipientPrivString) 559 utxoView, _ := NewUtxoView(db, params, nil) 560 txHash := txn.Hash() 561 blockHeight := chain.blockTip().Height + 1 562 _, _, _, _, err = 563 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, 564 true /*verifySignature*/, false /*ignoreUtxos*/) 565 require.Error(err) 566 require.Contains(err.Error(), RuleErrorInvalidTransactionSignature) 567 } 568 569 // A block reward with a bad signature should fail. 570 { 571 txn := &MsgDeSoTxn{ 572 // The inputs will be set below. 573 TxInputs: []*DeSoInput{}, 574 TxOutputs: []*DeSoOutput{ 575 { 576 PublicKey: recipientPkBytes, 577 AmountNanos: 1, 578 }, 579 }, 580 PublicKey: senderPkBytes, 581 TxnMeta: &BlockRewardMetadataa{ 582 ExtraData: []byte{0x00, 0x01}, 583 }, 584 } 585 _signTxn(t, txn, senderPrivString) 586 utxoView, _ := NewUtxoView(db, params, nil) 587 txHash := txn.Hash() 588 blockHeight := chain.blockTip().Height + 1 589 _, _, _, _, err = 590 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, 591 true /*verifySignature*/, false /*ignoreUtxos*/) 592 require.Error(err) 593 require.Contains(err.Error(), RuleErrorBlockRewardTxnNotAllowedToHaveSignature) 594 } 595 596 // A block reward with an input, even if it's signed legitimately, 597 // should fail. 598 { 599 txn := &MsgDeSoTxn{ 600 // The inputs will be set below. 601 TxInputs: []*DeSoInput{}, 602 TxOutputs: []*DeSoOutput{ 603 { 604 PublicKey: recipientPkBytes, 605 AmountNanos: 1, 606 }, 607 }, 608 PublicKey: senderPkBytes, 609 TxnMeta: &BlockRewardMetadataa{ 610 ExtraData: []byte{0x00, 0x01}, 611 }, 612 } 613 614 totalInput, spendAmount, changeAmount, fees, err := 615 chain.AddInputsAndChangeToTransaction(txn, 10, nil) 616 require.NoError(err) 617 require.Equal(totalInput, spendAmount+changeAmount+fees) 618 require.Greater(totalInput, uint64(0)) 619 620 _signTxn(t, txn, senderPrivString) 621 utxoView, _ := NewUtxoView(db, params, nil) 622 txHash := txn.Hash() 623 blockHeight := chain.blockTip().Height + 1 624 _, _, _, _, err = 625 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, 626 true /*verifySignature*/, false /*ignoreUtxos*/) 627 require.Error(err) 628 require.Contains(err.Error(), RuleErrorBlockRewardTxnNotAllowedToHaveInputs) 629 } 630 631 // A block with too much block reward should fail. 632 allowedBlockReward := CalcBlockRewardNanos(chain.blockTip().Height) 633 assert.Equal(int64(allowedBlockReward), int64(1*NanosPerUnit)) 634 blockToMine, _, _, err := miner._getBlockToMine(0 /*threadIndex*/) 635 require.NoError(err) 636 { 637 blockToMine.Txns[0].TxOutputs[0].AmountNanos = allowedBlockReward + 1 638 // One iteration should be sufficient to find us a good block. 639 _, bestNonce, err := FindLowestHash(blockToMine.Header, 10000) 640 require.NoError(err) 641 blockToMine.Header.Nonce = bestNonce 642 643 txHashes, err := ComputeTransactionHashes(blockToMine.Txns) 644 require.NoError(err) 645 utxoView, _ := NewUtxoView(db, params, nil) 646 _, err = utxoView.ConnectBlock(blockToMine, txHashes, true /*verifySignatures*/, nil) 647 require.Error(err) 648 require.Contains(err.Error(), RuleErrorBlockRewardExceedsMaxAllowed) 649 } 650 651 // A block with less than the max block reward should be OK. 652 { 653 blockToMine.Txns[0].TxOutputs[0].AmountNanos = allowedBlockReward - 1 654 // One iteration should be sufficient to find us a good block. 655 _, bestNonce, err := FindLowestHash(blockToMine.Header, 10000) 656 require.NoError(err) 657 blockToMine.Header.Nonce = bestNonce 658 659 txHashes, err := ComputeTransactionHashes(blockToMine.Txns) 660 require.NoError(err) 661 utxoView, _ := NewUtxoView(db, params, nil) 662 _, err = utxoView.ConnectBlock(blockToMine, txHashes, true /*verifySignatures*/, nil) 663 require.NoError(err) 664 } 665 }