github.com/deso-protocol/core@v1.2.9/lib/block_view_message_test.go (about) 1 package lib 2 3 import ( 4 "fmt" 5 "github.com/btcsuite/btcd/btcec" 6 "github.com/dgraph-io/badger/v3" 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 "testing" 10 "time" 11 ) 12 13 func _privateMessage(t *testing.T, chain *Blockchain, db *badger.DB, 14 params *DeSoParams, feeRateNanosPerKB uint64, senderPkBase58Check string, 15 recipientPkBase58Check string, 16 senderPrivBase58Check string, unencryptedMessageText string, tstampNanos uint64) ( 17 _utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) { 18 19 assert := assert.New(t) 20 require := require.New(t) 21 _ = assert 22 _ = require 23 24 senderPkBytes, _, err := Base58CheckDecode(senderPkBase58Check) 25 require.NoError(err) 26 27 recipientPkBytes, _, err := Base58CheckDecode(recipientPkBase58Check) 28 require.NoError(err) 29 30 utxoView, err := NewUtxoView(db, params, nil) 31 require.NoError(err) 32 33 txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreatePrivateMessageTxn( 34 senderPkBytes, recipientPkBytes, unencryptedMessageText, "", 35 tstampNanos, feeRateNanosPerKB, nil, []*DeSoOutput{}) 36 if err != nil { 37 return nil, nil, 0, err 38 } 39 40 require.Equal(totalInputMake, changeAmountMake+feesMake) 41 42 // Sign the transaction now that its inputs are set up. 43 _signTxn(t, txn, senderPrivBase58Check) 44 45 txHash := txn.Hash() 46 // Always use height+1 for validation since it's assumed the transaction will 47 // get mined into the next block. 48 blockHeight := chain.blockTip().Height + 1 49 utxoOps, totalInput, totalOutput, fees, err := 50 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 51 // ConnectTransaction should treat the amount locked as contributing to the 52 // output. 53 if err != nil { 54 return nil, nil, 0, err 55 } 56 require.Equal(totalInput, totalOutput+fees) 57 require.Equal(totalInput, totalInputMake) 58 59 // We should have one SPEND UtxoOperation for each input, one ADD operation 60 // for each output, and one OperationTypePrivateMessage operation at the end. 61 require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps)) 62 for ii := 0; ii < len(txn.TxInputs); ii++ { 63 require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type) 64 } 65 require.Equal(OperationTypePrivateMessage, utxoOps[len(utxoOps)-1].Type) 66 67 require.NoError(utxoView.FlushToDb()) 68 69 return utxoOps, txn, blockHeight, nil 70 } 71 72 func TestPrivateMessage(t *testing.T) { 73 assert := assert.New(t) 74 require := require.New(t) 75 _ = assert 76 _ = require 77 78 chain, params, db := NewLowDifficultyBlockchain() 79 mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/) 80 81 // Mine a few blocks to give the senderPkString some money. 82 _, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 83 require.NoError(err) 84 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 85 require.NoError(err) 86 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 87 require.NoError(err) 88 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 89 require.NoError(err) 90 91 // Setup some convenience functions for the test. 92 txnOps := [][]*UtxoOperation{} 93 txns := []*MsgDeSoTxn{} 94 expectedSenderBalances := []uint64{} 95 expectedRecipientBalances := []uint64{} 96 97 // We take the block tip to be the blockchain height rather than the 98 // header chain height. 99 savedHeight := chain.blockTip().Height + 1 100 registerOrTransfer := func(username string, 101 senderPk string, recipientPk string, senderPriv string) { 102 103 expectedSenderBalances = append(expectedSenderBalances, _getBalance(t, chain, nil, senderPkString)) 104 expectedRecipientBalances = append(expectedRecipientBalances, _getBalance(t, chain, nil, recipientPkString)) 105 106 currentOps, currentTxn, _ := _doBasicTransferWithViewFlush( 107 t, chain, db, params, senderPk, recipientPk, 108 senderPriv, 7 /*amount to send*/, 11 /*feerate*/) 109 110 txnOps = append(txnOps, currentOps) 111 txns = append(txns, currentTxn) 112 } 113 114 // Fund all the keys. 115 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 116 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 117 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 118 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 119 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 120 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 121 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 122 registerOrTransfer("", senderPkString, m2Pub, senderPrivString) 123 registerOrTransfer("", senderPkString, m2Pub, senderPrivString) 124 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 125 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 126 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 127 128 privateMessage := func( 129 senderPkBase58Check string, recipientPkBase58Check string, 130 senderPrivBase58Check string, unencryptedMessageText string, tstampNanos uint64, 131 feeRateNanosPerKB uint64) { 132 133 expectedSenderBalances = append(expectedSenderBalances, _getBalance(t, chain, nil, senderPkString)) 134 expectedRecipientBalances = append(expectedRecipientBalances, _getBalance(t, chain, nil, recipientPkString)) 135 136 currentOps, currentTxn, _, err := _privateMessage( 137 t, chain, db, params, feeRateNanosPerKB, senderPkBase58Check, 138 recipientPkBase58Check, senderPrivBase58Check, unencryptedMessageText, tstampNanos) 139 require.NoError(err) 140 141 txnOps = append(txnOps, currentOps) 142 txns = append(txns, currentTxn) 143 } 144 145 // =================================================================================== 146 // Do some PrivateMessage transactions 147 // =================================================================================== 148 tstamp1 := uint64(time.Now().UnixNano()) 149 message1 := string(append([]byte("message1: "), RandomBytes(100)...)) 150 tstamp2 := uint64(time.Now().UnixNano()) 151 message2 := string(append([]byte("message2: "), RandomBytes(100)...)) 152 tstamp3 := uint64(time.Now().UnixNano()) 153 message3 := string(append([]byte("message3: "), RandomBytes(100)...)) 154 tstamp4 := uint64(time.Now().UnixNano()) 155 message4 := string(append([]byte("message4: "), RandomBytes(100)...)) 156 message5 := string(append([]byte("message5: "), RandomBytes(100)...)) 157 158 // Message where the sender is the recipient should fail. 159 _, _, _, err = _privateMessage( 160 t, chain, db, params, 10 /*feeRateNanosPerKB*/, m0Pub, 161 m0Pub, m0Priv, "test" /*unencryptedMessageText*/, tstamp1) 162 require.Error(err) 163 require.Contains(err.Error(), RuleErrorPrivateMessageSenderPublicKeyEqualsRecipientPublicKey) 164 165 // Message with length too long should fail. 166 badMessage := string(append([]byte("badMessage: "), 167 RandomBytes(int32(params.MaxPrivateMessageLengthBytes))...)) 168 _, _, _, err = _privateMessage( 169 t, chain, db, params, 0 /*feeRateNanosPerKB*/, m0Pub, 170 m1Pub, m0Priv, badMessage /*unencryptedMessageText*/, tstamp1) 171 require.Error(err) 172 require.Contains(err.Error(), RuleErrorPrivateMessageEncryptedTextLengthExceedsMax) 173 174 // Zero tstamp should fail. 175 _, _, _, err = _privateMessage( 176 t, chain, db, params, 0 /*feeRateNanosPerKB*/, m0Pub, 177 m1Pub, m0Priv, message1 /*unencryptedMessageText*/, 0) 178 require.Error(err) 179 require.Contains(err.Error(), RuleErrorPrivateMessageTstampIsZero) 180 181 // m0 -> m1: message1, tstamp1 182 privateMessage( 183 m0Pub, m1Pub, m0Priv, message1, tstamp1, 0 /*feeRateNanosPerKB*/) 184 185 // Duplicating (m0, tstamp1) should fail. 186 _, _, _, err = _privateMessage( 187 t, chain, db, params, 0 /*feeRateNanosPerKB*/, m0Pub, 188 m1Pub, m0Priv, message1 /*unencryptedMessageText*/, tstamp1) 189 require.Error(err) 190 require.Contains(err.Error(), RuleErrorPrivateMessageExistsWithSenderPublicKeyTstampTuple) 191 192 // Duplicating (m1, tstamp1) should fail. 193 _, _, _, err = _privateMessage( 194 t, chain, db, params, 0 /*feeRateNanosPerKB*/, m1Pub, 195 m0Pub, m1Priv, message1 /*unencryptedMessageText*/, tstamp1) 196 require.Error(err) 197 require.Contains(err.Error(), RuleErrorPrivateMessageExistsWithSenderPublicKeyTstampTuple) 198 199 // Duplicating (m0, tstamp1) with a different sender should still fail. 200 _, _, _, err = _privateMessage( 201 t, chain, db, params, 0 /*feeRateNanosPerKB*/, m2Pub, 202 m0Pub, m2Priv, message1 /*unencryptedMessageText*/, tstamp1) 203 require.Error(err) 204 require.Contains(err.Error(), RuleErrorPrivateMessageExistsWithRecipientPublicKeyTstampTuple) 205 206 // Duplicating (m1, tstamp1) with a different sender should still fail. 207 _, _, _, err = _privateMessage( 208 t, chain, db, params, 0 /*feeRateNanosPerKB*/, m2Pub, 209 m1Pub, m2Priv, message1 /*unencryptedMessageText*/, tstamp1) 210 require.Error(err) 211 require.Contains(err.Error(), RuleErrorPrivateMessageExistsWithRecipientPublicKeyTstampTuple) 212 213 // m2 -> m1: message2, tstamp2 214 privateMessage( 215 m2Pub, m1Pub, m2Priv, message2, tstamp2, 10 /*feeRateNanosPerKB*/) 216 217 // m3 -> m1: message3, tstamp3 218 privateMessage( 219 m3Pub, m1Pub, m3Priv, message3, tstamp3, 10 /*feeRateNanosPerKB*/) 220 221 // m2 -> m1: message4Str, tstamp4 222 privateMessage( 223 m1Pub, m2Pub, m1Priv, message4, tstamp4, 10 /*feeRateNanosPerKB*/) 224 225 // m2 -> m3: message5Str, tstamp1 226 // Using tstamp1 should be OK since the message is between two new users. 227 privateMessage( 228 m2Pub, m3Pub, m2Priv, message5, tstamp1, 10 /*feeRateNanosPerKB*/) 229 230 // Verify that the messages are as we expect them in the db. 231 // 1: m0 m1 232 // 2: m2 m1 233 // 3: m3 m1 234 // 4: m1 m2 235 // 5: m2 m3 236 // => m0: 1 237 // m1: 4 238 // m2: 3 239 // m3: 2 240 { 241 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m0Pub)) 242 require.NoError(err) 243 require.Equal(1, len(messages)) 244 messageEntry := messages[0] 245 require.Equal(messageEntry.SenderPublicKey, _strToPk(t, m0Pub)) 246 require.Equal(messageEntry.RecipientPublicKey, _strToPk(t, m1Pub)) 247 require.Equal(messageEntry.TstampNanos, tstamp1) 248 require.Equal(messageEntry.isDeleted, false) 249 priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), _strToPk(t, m1Priv)) 250 decryptedBytes, err := DecryptBytesWithPrivateKey(messageEntry.EncryptedText, priv.ToECDSA()) 251 require.NoError(err) 252 require.Equal(message1, string(decryptedBytes)) 253 } 254 { 255 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m1Pub)) 256 require.NoError(err) 257 require.Equal(4, len(messages)) 258 } 259 { 260 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m2Pub)) 261 require.NoError(err) 262 require.Equal(3, len(messages)) 263 } 264 { 265 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m3Pub)) 266 require.NoError(err) 267 require.Equal(2, len(messages)) 268 } 269 270 // =================================================================================== 271 // Finish it off with some transactions 272 // =================================================================================== 273 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 274 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 275 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 276 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 277 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 278 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 279 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 280 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 281 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 282 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 283 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 284 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 285 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 286 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 287 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 288 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 289 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 290 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 291 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 292 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 293 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 294 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 295 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 296 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 297 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 298 299 // Roll back all of the above using the utxoOps from each. 300 for ii := 0; ii < len(txnOps); ii++ { 301 backwardIter := len(txnOps) - 1 - ii 302 currentOps := txnOps[backwardIter] 303 currentTxn := txns[backwardIter] 304 fmt.Printf("Disconnecting transaction with type %v index %d (going backwards)\n", currentTxn.TxnMeta.GetTxnType(), backwardIter) 305 306 utxoView, err := NewUtxoView(db, params, nil) 307 require.NoError(err) 308 309 currentHash := currentTxn.Hash() 310 err = utxoView.DisconnectTransaction(currentTxn, currentHash, currentOps, savedHeight) 311 require.NoError(err) 312 313 require.NoError(utxoView.FlushToDb()) 314 315 // After disconnecting, the balances should be restored to what they 316 // were before this transaction was applied. 317 require.Equal(expectedSenderBalances[backwardIter], _getBalance(t, chain, nil, senderPkString)) 318 require.Equal(expectedRecipientBalances[backwardIter], _getBalance(t, chain, nil, recipientPkString)) 319 } 320 321 // Verify that all the messages have been deleted. 322 { 323 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m0Pub)) 324 require.NoError(err) 325 require.Equal(0, len(messages)) 326 } 327 { 328 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m1Pub)) 329 require.NoError(err) 330 require.Equal(0, len(messages)) 331 } 332 { 333 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m2Pub)) 334 require.NoError(err) 335 require.Equal(0, len(messages)) 336 } 337 { 338 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m3Pub)) 339 require.NoError(err) 340 require.Equal(0, len(messages)) 341 } 342 343 // Apply all the transactions to a mempool object and make sure we don't get any 344 // errors. Verify the balances align as we go. 345 for ii, tx := range txns { 346 // See comment above on this transaction. 347 fmt.Printf("Adding txn %d of type %v to mempool\n", ii, tx.TxnMeta.GetTxnType()) 348 349 require.Equal(expectedSenderBalances[ii], _getBalance(t, chain, mempool, senderPkString)) 350 require.Equal(expectedRecipientBalances[ii], _getBalance(t, chain, mempool, recipientPkString)) 351 352 acceptedTxns, err := mempool.ProcessTransaction(tx, false, false, 0, true) 353 require.NoError(err, "Problem adding transaction %d to mempool: %v", ii, tx) 354 require.Equal(1, len(acceptedTxns)) 355 } 356 357 // Apply all the transactions to a view and flush the view to the db. 358 utxoView, err := NewUtxoView(db, params, nil) 359 require.NoError(err) 360 for ii, txn := range txns { 361 fmt.Printf("Adding txn %v of type %v to UtxoView\n", ii, txn.TxnMeta.GetTxnType()) 362 363 // Always use height+1 for validation since it's assumed the transaction will 364 // get mined into the next block. 365 txHash := txn.Hash() 366 blockHeight := chain.blockTip().Height + 1 367 _, _, _, _, err := 368 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 369 require.NoError(err) 370 } 371 // Flush the utxoView after having added all the transactions. 372 require.NoError(utxoView.FlushToDb()) 373 374 // Disonnect the transactions from a single view in the same way as above 375 // i.e. without flushing each time. 376 utxoView2, err := NewUtxoView(db, params, nil) 377 require.NoError(err) 378 for ii := 0; ii < len(txnOps); ii++ { 379 backwardIter := len(txnOps) - 1 - ii 380 fmt.Printf("Disconnecting transaction with index %d (going backwards)\n", backwardIter) 381 currentOps := txnOps[backwardIter] 382 currentTxn := txns[backwardIter] 383 384 currentHash := currentTxn.Hash() 385 err = utxoView2.DisconnectTransaction(currentTxn, currentHash, currentOps, savedHeight) 386 require.NoError(err) 387 } 388 require.NoError(utxoView2.FlushToDb()) 389 require.Equal(expectedSenderBalances[0], _getBalance(t, chain, nil, senderPkString)) 390 require.Equal(expectedRecipientBalances[0], _getBalance(t, chain, nil, recipientPkString)) 391 392 // Verify that all the messages have been deleted. 393 { 394 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m0Pub)) 395 require.NoError(err) 396 require.Equal(0, len(messages)) 397 } 398 { 399 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m1Pub)) 400 require.NoError(err) 401 require.Equal(0, len(messages)) 402 } 403 { 404 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m2Pub)) 405 require.NoError(err) 406 require.Equal(0, len(messages)) 407 } 408 { 409 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m3Pub)) 410 require.NoError(err) 411 require.Equal(0, len(messages)) 412 } 413 414 // Try and estimate the fees in a situation where the last block contains just a 415 // block reward. 416 { 417 // Fee should just equal the min passed in because the block has so few transactions. 418 require.Equal(int64(0), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 0))) 419 require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 7))) 420 require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(0, 7))) 421 require.Equal(int64(0), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 0))) 422 require.Equal(int64(1), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 1))) 423 require.Equal(int64(1), int64(chain.EstimateDefaultFeeRateNanosPerKB(0, 1))) 424 } 425 426 // All the txns should be in the mempool already so mining a block should put 427 // all those transactions in it. 428 block, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 429 require.NoError(err) 430 // Add one for the block reward. Now we have a meaty block. 431 require.Equal(len(txnOps)+1, len(block.Txns)) 432 // Estimate the transaction fees of the tip block in various ways. 433 { 434 // Threshold above what's in the block should return the default fee at all times. 435 require.Equal(int64(0), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 0))) 436 require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 7))) 437 // Threshold below what's in the block should return the max of the median 438 // and the minfee. This means with a low minfee the value returned should be 439 // higher. And with a high minfee the value returned should be equal to the 440 // fee. 441 require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(0, 7))) 442 require.Equal(int64(4), int64(chain.EstimateDefaultFeeRateNanosPerKB(0, 0))) 443 require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(.01, 7))) 444 require.Equal(int64(4), int64(chain.EstimateDefaultFeeRateNanosPerKB(.01, 1))) 445 } 446 447 // Roll back the block and make sure we don't hit any errors. 448 { 449 utxoView, err := NewUtxoView(db, params, nil) 450 require.NoError(err) 451 452 // Fetch the utxo operations for the block we're detaching. We need these 453 // in order to be able to detach the block. 454 hash, err := block.Header.Hash() 455 require.NoError(err) 456 utxoOps, err := GetUtxoOperationsForBlock(db, hash) 457 require.NoError(err) 458 459 // Compute the hashes for all the transactions. 460 txHashes, err := ComputeTransactionHashes(block.Txns) 461 require.NoError(err) 462 require.NoError(utxoView.DisconnectBlock(block, txHashes, utxoOps)) 463 464 // Flushing the view after applying and rolling back should work. 465 require.NoError(utxoView.FlushToDb()) 466 } 467 468 // Verify that all the messages have been deleted. 469 { 470 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m0Pub)) 471 require.NoError(err) 472 require.Equal(0, len(messages)) 473 } 474 { 475 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m1Pub)) 476 require.NoError(err) 477 require.Equal(0, len(messages)) 478 } 479 { 480 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m2Pub)) 481 require.NoError(err) 482 require.Equal(0, len(messages)) 483 } 484 { 485 messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m3Pub)) 486 require.NoError(err) 487 require.Equal(0, len(messages)) 488 } 489 }