github.com/deso-protocol/core@v1.2.9/lib/block_view_profile_test.go (about) 1 package lib 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "github.com/btcsuite/btcd/btcec" 7 "github.com/dgraph-io/badger/v3" 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 "log" 11 "os" 12 "runtime/pprof" 13 "testing" 14 "time" 15 ) 16 17 func _swapIdentity(t *testing.T, chain *Blockchain, db *badger.DB, 18 params *DeSoParams, feeRateNanosPerKB uint64, updaterPkBase58Check string, 19 updaterPrivBase58Check string, fromPublicKey []byte, toPublicKey []byte) ( 20 _utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) { 21 22 assert := assert.New(t) 23 require := require.New(t) 24 _ = assert 25 _ = require 26 27 updaterPkBytes, _, err := Base58CheckDecode(updaterPkBase58Check) 28 require.NoError(err) 29 30 utxoView, err := NewUtxoView(db, params, nil) 31 require.NoError(err) 32 33 txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateSwapIdentityTxn( 34 updaterPkBytes, 35 fromPublicKey, 36 toPublicKey, 37 feeRateNanosPerKB, 38 nil, 39 []*DeSoOutput{}) 40 if err != nil { 41 return nil, nil, 0, err 42 } 43 44 require.Equal(totalInputMake, changeAmountMake+feesMake) 45 46 // Sign the transaction now that its inputs are set up. 47 _signTxn(t, txn, updaterPrivBase58Check) 48 49 txHash := txn.Hash() 50 // Always use height+1 for validation since it's assumed the transaction will 51 // get mined into the next block. 52 blockHeight := chain.blockTip().Height + 1 53 utxoOps, totalInput, totalOutput, fees, err := 54 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 55 // ConnectTransaction should treat the amount locked as contributing to the 56 // output. 57 if err != nil { 58 return nil, nil, 0, err 59 } 60 require.Equal(totalInput, totalOutput+fees) 61 require.Equal(totalInput, totalInputMake) 62 63 // We should have one SPEND UtxoOperation for each input, one ADD operation 64 // for each output, and one OperationTypeSwapIdentity operation at the end. 65 require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps)) 66 for ii := 0; ii < len(txn.TxInputs); ii++ { 67 require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type) 68 } 69 require.Equal(OperationTypeSwapIdentity, utxoOps[len(utxoOps)-1].Type) 70 71 require.NoError(utxoView.FlushToDb()) 72 73 return utxoOps, txn, blockHeight, nil 74 } 75 76 func _updateProfile(t *testing.T, chain *Blockchain, db *badger.DB, 77 params *DeSoParams, feeRateNanosPerKB uint64, updaterPkBase58Check string, 78 updaterPrivBase58Check string, profilePubKey []byte, newUsername string, 79 newDescription string, newProfilePic string, newCreatorBasisPoints uint64, 80 newStakeMultipleBasisPoints uint64, isHidden bool) ( 81 _utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) { 82 83 assert := assert.New(t) 84 require := require.New(t) 85 _ = assert 86 _ = require 87 88 updaterPkBytes, _, err := Base58CheckDecode(updaterPkBase58Check) 89 require.NoError(err) 90 91 utxoView, err := NewUtxoView(db, params, nil) 92 require.NoError(err) 93 94 txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateUpdateProfileTxn( 95 updaterPkBytes, 96 profilePubKey, 97 newUsername, 98 newDescription, 99 newProfilePic, 100 newCreatorBasisPoints, 101 newStakeMultipleBasisPoints, 102 isHidden, 103 0, 104 feeRateNanosPerKB, 105 nil, /*mempool*/ 106 []*DeSoOutput{}) 107 if err != nil { 108 return nil, nil, 0, err 109 } 110 111 require.Equal(totalInputMake, changeAmountMake+feesMake) 112 113 // Sign the transaction now that its inputs are set up. 114 _signTxn(t, txn, updaterPrivBase58Check) 115 116 txHash := txn.Hash() 117 // Always use height+1 for validation since it's assumed the transaction will 118 // get mined into the next block. 119 blockHeight := chain.blockTip().Height + 1 120 utxoOps, totalInput, totalOutput, fees, err := 121 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 122 // ConnectTransaction should treat the amount locked as contributing to the 123 // output. 124 if err != nil { 125 return nil, nil, 0, err 126 } 127 require.Equal(totalInput, totalOutput+fees) 128 require.Equal(totalInput, totalInputMake) 129 130 // We should have one SPEND UtxoOperation for each input, one ADD operation 131 // for each output, and one OperationTypeUpdateProfile operation at the end. 132 require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps)) 133 for ii := 0; ii < len(txn.TxInputs); ii++ { 134 require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type) 135 } 136 require.Equal(OperationTypeUpdateProfile, utxoOps[len(utxoOps)-1].Type) 137 138 require.NoError(utxoView.FlushToDb()) 139 140 return utxoOps, txn, blockHeight, nil 141 } 142 143 func _updateProfileWithTestMeta( 144 testMeta *TestMeta, 145 feeRateNanosPerKB uint64, 146 updaterPkBase58Check string, 147 updaterPrivBase58Check string, 148 profilePubKey []byte, 149 newUsername string, 150 newDescription string, 151 newProfilePic string, 152 newCreatorBasisPoints uint64, 153 newStakeMultipleBasisPoints uint64, 154 isHidden bool) { 155 156 testMeta.expectedSenderBalances = append( 157 testMeta.expectedSenderBalances, _getBalance(testMeta.t, testMeta.chain, nil, updaterPkBase58Check)) 158 159 currentOps, currentTxn, _, err := _updateProfile( 160 testMeta.t, testMeta.chain, testMeta.db, testMeta.params, 161 feeRateNanosPerKB, updaterPkBase58Check, 162 updaterPrivBase58Check, profilePubKey, newUsername, 163 newDescription, newProfilePic, newCreatorBasisPoints, 164 newStakeMultipleBasisPoints, isHidden) 165 166 require.NoError(testMeta.t, err) 167 testMeta.txnOps = append(testMeta.txnOps, currentOps) 168 testMeta.txns = append(testMeta.txns, currentTxn) 169 } 170 171 func _getAuthorizeDerivedKeyMetadata(t *testing.T, ownerPrivateKey *btcec.PrivateKey, 172 params *DeSoParams, expirationBlock uint64, isDeleted bool) (*AuthorizeDerivedKeyMetadata, 173 *btcec.PrivateKey) { 174 require := require.New(t) 175 176 // Generate a random derived key pair 177 derivedPrivateKey, err := btcec.NewPrivateKey(btcec.S256()) 178 require.NoError(err, "_getAuthorizeDerivedKeyMetadata: Error generating a derived key pair") 179 derivedPublicKey := derivedPrivateKey.PubKey().SerializeCompressed() 180 181 // Create access signature 182 expirationBlockByte := EncodeUint64(expirationBlock) 183 accessBytes := append(derivedPublicKey, expirationBlockByte[:]...) 184 accessSignature, err := ownerPrivateKey.Sign(Sha256DoubleHash(accessBytes)[:]) 185 require.NoError(err, "_getAuthorizeDerivedKeyMetadata: Error creating access signature") 186 187 // Determine operation type 188 var operationType AuthorizeDerivedKeyOperationType 189 if isDeleted { 190 operationType = AuthorizeDerivedKeyOperationNotValid 191 } else { 192 operationType = AuthorizeDerivedKeyOperationValid 193 } 194 195 return &AuthorizeDerivedKeyMetadata{ 196 derivedPublicKey, 197 expirationBlock, 198 operationType, 199 accessSignature.Serialize(), 200 }, derivedPrivateKey 201 } 202 203 // Create a new AuthorizeDerivedKey txn and connect it to the utxoView 204 func _doAuthorizeTxn(t *testing.T, chain *Blockchain, db *badger.DB, 205 params *DeSoParams, utxoView *UtxoView, feeRateNanosPerKB uint64, ownerPublicKey []byte, 206 derivedPublicKey []byte, derivedPrivBase58Check string, expirationBlock uint64, 207 accessSignature []byte, deleteKey bool) (_utxoOps []*UtxoOperation, 208 _txn *MsgDeSoTxn, _height uint32, _err error) { 209 210 assert := assert.New(t) 211 require := require.New(t) 212 _ = assert 213 _ = require 214 215 txn, totalInput, changeAmount, fees, err := chain.CreateAuthorizeDerivedKeyTxn( 216 ownerPublicKey, 217 derivedPublicKey, 218 expirationBlock, 219 accessSignature, 220 deleteKey, 221 false, 222 feeRateNanosPerKB, 223 nil, /*mempool*/ 224 []*DeSoOutput{}) 225 if err != nil { 226 return nil, nil, 0, err 227 } 228 229 require.Equal(totalInput, changeAmount+fees) 230 231 // Sign the transaction now that its inputs are set up. 232 // We have to set the solution byte because we're signing 233 // the transaction with derived key on behalf of the owner. 234 _signTxnWithDerivedKey(t, txn, derivedPrivBase58Check) 235 236 txHash := txn.Hash() 237 // Always use height+1 for validation since it's assumed the transaction will 238 // get mined into the next block. 239 blockHeight := chain.blockTip().Height + 1 240 utxoOps, totalInput, totalOutput, fees, err := 241 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 242 // ConnectTransaction should treat the amount locked as contributing to the 243 // output. 244 if err != nil { 245 return nil, nil, 0, err 246 } 247 require.Equal(totalInput, totalOutput+fees) 248 require.Equal(totalInput, totalInput) 249 250 // We should have one SPEND UtxoOperation for each input, one ADD operation 251 // for each output, and one OperationTypeUpdateProfile operation at the end. 252 require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps)) 253 for ii := 0; ii < len(txn.TxInputs); ii++ { 254 require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type) 255 } 256 require.Equal(OperationTypeAuthorizeDerivedKey, utxoOps[len(utxoOps)-1].Type) 257 258 return utxoOps, txn, blockHeight, nil 259 } 260 261 const ( 262 longPic string = "data:image/jpeg;base64,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" 263 shortPic string = "data:image/jpeg;base64,/9j/2wCEAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDIBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIAGQAZAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AOi+MPiX+zdLt9Gt5P392fMmAOCIh0B9if0BrxNroyAhpRv6Fh0X2FbXxF1ldb8bX12pHlKRBHluCqZGfx6/jWBbW5lyyRg++DiolK7IUSSOEhwN2R6nvVyC2LkcE/0qe10+R8blzXVaZojOFIjx9azdzohC5iW2mtx8uAehPatKLSWxnII9TXVw6KExng1P/Z6rwUU/hS5rGygjjpNO8sZ4FQNEEJ6gj1ORXXT2QA+7z7ViahYqqllGWHfFCqClRT2MpoxKBjtzz1BqJ40ByCDjr3xSN5sTZIIweDSS4l+YHBJ4OK05rnPKm4ksUuzGG+XPFetfD2bz9EkQ4PlyHHHYivGJIZUXfjIz1WvWPhg8n2G5D9GKkfhkVcLmUj0DAowPSlxRj3raxJ8gwwM7lpTtBOQpAya6/QtM+1AAcL644rjYlCzDdGQM9+K7zSbp4rcLGwXPGQOawTS1ZpGLbsjfTTrWCRVHLfSui0+ONYhhR9awrC3U7WYkt6k5JrctpFj5POOetYczkzs5VFWNEhFGWprRRyJwaz7jW7FMRyMA5PAwRUaX1i43LdhGHBUmmxxaHXKADg/hVR7cSAg85qV7mOQZRw2TgGgzIrBN4ztJzmsmbLYzJdGRuarHSo0bhQcVfbU7bzmBl6cZ7VG15CzBklUj1zTjdGcmnozO1K1RbMqVAI7gc11Pw5/cFoT1IY4/EVy+uz7LbeMYJAwfeui8GFl1GwccK8cgPPtx/L9K6ou6OGorM9OA4oxUQbgUu4Vpzoz5WfNw05pwZ7kiQliTwARWvpltGHG0dOM1Nf2ksWnslsQshO0cZOT1NP0+M2kUcU/EoGa53K6PQlT5ZXRpxsVGFOCPUcUkus/ZlEfkPLM5+VI13Mx9u3406wcTShW6Vri2jWQMiqG/vY5/OslKzKkrnD3134guWZR4YZkP8bTAn9Kzxp2pGYrNayR8ZG1iQPx//VXpfk3DH5GTHrVC9tZFZS8mT2FVKUbaExjK+pzmg290l6iTzu3PQ9ql8TRzw3EQhkkTcpBZa1rKFUvQRzg0/WYllnUYHHWoXc1emh5rdXj28hMn25oj0dEJBGfpiui0HVtNmZENw+9h8qyrtY/nXSW8eEysan3NRX+mRXkSi4hCgMGDKeQRVpxWpk4tvUyNdIlsmWPsc8/Wul0CZYrvSY1IzlTj6jNYV7bEgRjlTgAjvzWp4es5rjXImOPJtcNz/u4Fb0lzPQ5K/urU9SE6Y+8KPOT++Kyx0pa6Pqxy/WEecrLIVQBN38YOOnGCPzrMlkadC7AiRJCPwqiniYQ37w3J2wySl0Ydu2Ktve2lzdMLZiehcYwAa8/7XkeupKULk9vO8L5HBHeujs73zo8HFc8y/PkCr0GY8HOPpSluXHVHRCVUGUwT71maperEhd2GQP8AOKUTlY8jpWHqUd3OGuI03iJgyp/eINSmVymppJknJlZCOc4p+tTMkD3AU/JgfhXK2fiPWLFpZbuxZIGPyEdV+op9x4suLoxwR2byI4+dsYA/xrTTYhpnWWkmIxwOlOu5VK81gaVcypFHHICGXg/0q7dylk461Fy3Eq3lwi7XzgKcZNdZ4YQmyllIGWfGfXArlFgEiJu/vhiPUYrv9NsmtNNijP3sbjx3Nd+DV5eh5GYytD1LBkwetJ5vvUbRtmk8s162h4V2eOp4fOsRtcLwIGGPU4Iq/dwLaP5q9ScVpatqkXgO2dbzZOJ2byIlHzn1/AVW1WKf7CPtMJimCI7Ie2QD/I1404ckfzPoMPPmkyK2n85s56dBWzbjzOPT1rlbCcrLtJ4JrprOVQ3LY9K5JHowZdSM+YFb7vep5HiA2Lj6elV7oGcYhk2nGc46Vkz2Atzvm1W7Cn7xjVcD9KUdSmzUa1ilVxhSfQnrVN7OKI5CqfUelUxb6bI4K+IXUgdGVRTGtbUZMWuySSHkbEVhn/PvWvIrbiZcaNMh1+hqZ4yI8kc1HY28ocJLcLKvXdtwfxq5dOqIQOnc1l1BvQ0/DOlQ30rTTglbcghR0ZuetdhIMVkeEoDFoombObhy4z/dHA/lWxJ3r1sPHlij5/Fy55vyKjEhqbuNOf71NrrOKx82W+r3PjL4j6fc6iQVnvIkWP8AhSPeMKK9/wDHGh+farqMS/cXy5gP7vZvwP6GvmPQrz+ztfsLw9ILiOQ/QMCa+0lEdxB0DxSL0PIII/wrz170GmenfkqJo+cZozbTnrkHkVpWV3nDAjH1re8b+FZNKuvtEMZazc/I/XYf7p/oa4KZprZyUYhT1FcVSDiepTqJ6o9AtZI5k+8D34OKSW0CtuLkDvn0rh7DUpbZ9xckds10ya1HPCrNKucYIrNLqbqSZo/2bayAOY0Oe5Aqs2lxxtiJFQH0FRpqaKvyuMetQvrlusmA+e5AqvUbLawNACS3PvTYFl1O/hsYsh5X2nH8I7n8BmsK+8QPNKscCk54HufpXovgzw9PpkLX+oLi9mXAjPWJfQ+57+lXRp887I5cTXVOF+p1cMSQQxwxriONQqj0ApkhzUu7ioG6160UeBJ3IGHPSkwPT9Kc/wB7qR9Kb+JrUzPj4H5xX2N4Fu5b7wPotxOQZHtI9xHfAx/SvjgffH1r7A+HP/JPtD/69Ergp/Cz0qnxI6S5giurd4Z41kicYZWGQRXhvivSrXTdcubW3VvKBGAxzjNe7N0rxfx3/wAjRdf8B/kKU/hNaD96xw00aoSFFUpCR0JFX7n7x/CqElefU0Z3w2IzLJ5e0SMB9afbpucAkkfWoj92prX/AFlZ7s06EPiWAQeGYLyJ3WV7wxkhscBNw/WvZPhf4i1DxF4UWfUXWSaF/KEgGCwA6t6mvIPFX/Il2v8A2EG/9FivSvgn/wAidL/18t/IV6+GSS07HjYvXfuelGo24/KpD3pjf0rpOEhfhqbmnP8AeptWQf/Z" 264 otherShortPic string = "data:image/jpeg;base64,/9j/2wCEAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDIBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIAGQAZAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APf6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoopGJVSQpYgcAd6AForyO++JmrHUJCgg05bZyj2dxHuckcEOeD/wB8fma7SDxpDH4Stte1OxurRJ3WNIVQyO5Y4UqBzhu2QDz0rSdKUEpPqZQrRnJxj08jqKKwbTxdp17p9/cwR3ZksB+/tGt2SdTjcAEbBJI6etXptasLU2KXU4gkvmCQRyAhmbaWwR2OAevpWZqaFFc5qfjfRtJv5bS5kuD5Gw3M8UDPFbb/ALvmOOFzViz8TQXfiSfQ2sryC5ihadZJkURyxhgu5SGJIJPcCgDbopnmx/N86/L97np9ar6hqVnpdjPe3twkNvAhkkdudq+uBzQBbopkM0c8SyROHRgCCD1p9ABRRRQAUjbtp2kBscZGRS0UAfP/AIkOqN4guzrccn29WAcW0a+XKv8AAU7gHpzkn7pzjFd34hsvEWo/DywXULaSbUlvbeaWOxTEiRrID9N4XqRgZ6V291o+n3t/a31zaxyXNoSYZGHKZ/n+PQ81exSV+rua1JwlFKMUn1fc8dvNEu7jQPE3m+H9WvVvmT+z2vYlkuxMImXe5z8qLwFPUZPFamt6UNU03wxqk3hm6uPsUyw3tvJbKZzCI2XG3PzLvIOPxr07FGKZkeb2R1HQb3WY7Xwvd3cerSQz2URRViRfLVDHMcny9u3pg8dKmvJ9Sk+IUtxbaPqcSHTH02O6EA8tZjJkPnP3B1z+lehYoxQB4gfCeq3Witaaf4fu7G8j0eaDUpJsAX05KlcHJ8w7lZg3vitbU9Ev/FDeJLj+wrqIz6PAlkt7EEYzp5nTk4YZ46dfevWcUYoA5/wetpHoax2miz6SqsN8M1uISz7RlsA8+mfaugoooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9k=" 265 ) 266 267 func TestUpdateProfile(t *testing.T) { 268 // For testing purposes, we set the fix block height to be 0 for the ParamUpdaterProfileUpdateFixBlockHeight. 269 ParamUpdaterProfileUpdateFixBlockHeight = 0 270 UpdateProfileFixBlockHeight = 0 271 272 assert := assert.New(t) 273 require := require.New(t) 274 _ = assert 275 _ = require 276 277 chain, params, db := NewLowDifficultyBlockchain() 278 mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/) 279 // Make m3 a paramUpdater for this test 280 params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true 281 282 // Mine a few blocks to give the senderPkString some money. 283 _, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 284 require.NoError(err) 285 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 286 require.NoError(err) 287 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 288 require.NoError(err) 289 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 290 require.NoError(err) 291 292 // Setup some convenience functions for the test. 293 txnOps := [][]*UtxoOperation{} 294 txns := []*MsgDeSoTxn{} 295 expectedSenderBalances := []uint64{} 296 297 // We take the block tip to be the blockchain height rather than the 298 // header chain height. 299 savedHeight := chain.blockTip().Height + 1 300 registerOrTransfer := func(username string, 301 senderPk string, recipientPk string, senderPriv string) { 302 303 expectedSenderBalances = append(expectedSenderBalances, _getBalance(t, chain, nil, senderPk)) 304 305 currentOps, currentTxn, _ := _doBasicTransferWithViewFlush( 306 t, chain, db, params, senderPk, recipientPk, 307 senderPriv, 70 /*amount to send*/, 11 /*feerate*/) 308 309 txnOps = append(txnOps, currentOps) 310 txns = append(txns, currentTxn) 311 } 312 313 // Fund all the keys. 314 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 315 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 316 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 317 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 318 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 319 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 320 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 321 registerOrTransfer("", senderPkString, m2Pub, senderPrivString) 322 registerOrTransfer("", senderPkString, m2Pub, senderPrivString) 323 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 324 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 325 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 326 327 // Fund key to test CreateProfile fee 328 registerOrTransfer("", senderPkString, m4Pub, senderPrivString) 329 330 updateProfile := func( 331 feeRateNanosPerKB uint64, updaterPkBase58Check string, 332 updaterPrivBase58Check string, profilePubKey []byte, newUsername string, 333 newDescription string, newProfilePic string, newCreatorBasisPoints uint64, 334 newStakeMultipleBasisPoints uint64, isHidden bool) { 335 336 expectedSenderBalances = append(expectedSenderBalances, _getBalance(t, chain, nil, updaterPkBase58Check)) 337 338 currentOps, currentTxn, _, err := _updateProfile( 339 t, chain, db, params, 340 feeRateNanosPerKB, updaterPkBase58Check, 341 updaterPrivBase58Check, profilePubKey, newUsername, 342 newDescription, newProfilePic, newCreatorBasisPoints, 343 newStakeMultipleBasisPoints, isHidden) 344 345 require.NoError(err) 346 347 txnOps = append(txnOps, currentOps) 348 txns = append(txns, currentTxn) 349 } 350 _, _, _ = m2Priv, m3Priv, updateProfile 351 352 updateGlobalParamsEntry := func( 353 feeRateNanosPerKB uint64, updaterPkBase58Check string, 354 updaterPrivBase58Check string, 355 USDCentsPerBitcoinExchangeRate int64, 356 minimumNetworkFeeNanosPerKb int64, 357 createProfileFeeNanos int64) { 358 359 expectedSenderBalances = append(expectedSenderBalances, _getBalance(t, chain, nil, updaterPkBase58Check)) 360 361 currentOps, currentTxn, _, err := _updateGlobalParamsEntry(t, chain, db, params, 362 feeRateNanosPerKB, 363 updaterPkBase58Check, 364 updaterPrivBase58Check, 365 int64(InitialUSDCentsPerBitcoinExchangeRate), 366 minimumNetworkFeeNanosPerKb, 367 createProfileFeeNanos, 368 0, /*createNFTFeeNanos*/ 369 -1, /*maxCopiesPerNFT*/ 370 true) 371 require.NoError(err) 372 txnOps = append(txnOps, currentOps) 373 txns = append(txns, currentTxn) 374 } 375 376 // =================================================================================== 377 // Do some UpdateProfile transactions 378 // =================================================================================== 379 380 // Zero input txn should fail. 381 { 382 _, _, _, err = _updateProfile( 383 t, chain, db, params, 384 0, /*feeRateNanosPerKB*/ 385 m0Pub, /*updaterPkBase58Check*/ 386 m0Priv, /*updaterPrivBase58Check*/ 387 []byte{}, /*profilePubKey*/ 388 "m0", /*newUsername*/ 389 "I am the m0", /*newDescription*/ 390 shortPic, /*newProfilePic*/ 391 10*100, /*newCreatorBasisPoints*/ 392 2*100*100, /*newStakeMultipleBasisPoints*/ 393 false /*isHidden*/) 394 require.Error(err) 395 require.Contains(err.Error(), RuleErrorTxnMustHaveAtLeastOneInput) 396 } 397 398 // Username too long should fail. 399 { 400 badUsername := string(append([]byte("badUsername: "), 401 RandomBytes(int32(params.MaxUsernameLengthBytes))...)) 402 _, _, _, err = _updateProfile( 403 t, chain, db, params, 404 10, /*feeRateNanosPerKB*/ 405 m0Pub, /*updaterPkBase58Check*/ 406 m0Priv, /*updaterPrivBase58Check*/ 407 []byte{}, /*profilePubKey*/ 408 badUsername, /*newUsername*/ 409 "I am the m0", /*newDescription*/ 410 shortPic, /*newProfilePic*/ 411 10*100, /*newCreatorBasisPoints*/ 412 2*100*100, /*newStakeMultipleBasisPoints*/ 413 false /*isHidden*/) 414 require.Error(err) 415 require.Contains(err.Error(), RuleErrorProfileUsernameTooLong) 416 } 417 418 // Description too long should fail. 419 { 420 badDescription := string(append([]byte("badDescription: "), 421 RandomBytes(int32(params.MaxUserDescriptionLengthBytes))...)) 422 _, _, _, err = _updateProfile( 423 t, chain, db, params, 424 2, /*feeRateNanosPerKB*/ 425 m0Pub, /*updaterPkBase58Check*/ 426 m0Priv, /*updaterPrivBase58Check*/ 427 []byte{}, /*profilePubKey*/ 428 "m0", /*newUsername*/ 429 badDescription, /*newDescription*/ 430 shortPic, /*newProfilePic*/ 431 10*100, /*newCreatorBasisPoints*/ 432 2*100*100, /*newStakeMultipleBasisPoints*/ 433 false /*isHidden*/) 434 require.Error(err) 435 require.Contains(err.Error(), RuleErrorProfileDescriptionTooLong) 436 } 437 438 // Profile pic too long should fail. 439 { 440 _, _, _, err = _updateProfile( 441 t, chain, db, params, 442 1, /*feeRateNanosPerKB*/ 443 m0Pub, /*updaterPkBase58Check*/ 444 m0Priv, /*updaterPrivBase58Check*/ 445 []byte{}, /*profilePubKey*/ 446 "m0", /*newUsername*/ 447 "i am the m0", /*newDescription*/ 448 longPic, /*newProfilePic*/ 449 10*100, /*newCreatorBasisPoints*/ 450 2*100*100, /*newStakeMultipleBasisPoints*/ 451 false /*isHidden*/) 452 require.Error(err) 453 require.Contains(err.Error(), RuleErrorMaxProfilePicSize) 454 } 455 456 // Stake multiple too large should fail long too long should fail. 457 { 458 _, _, _, err = _updateProfile( 459 t, chain, db, params, 460 1, /*feeRateNanosPerKB*/ 461 m0Pub, /*updaterPkBase58Check*/ 462 m0Priv, /*updaterPrivBase58Check*/ 463 []byte{}, /*profilePubKey*/ 464 "m0", /*newUsername*/ 465 "i am the m0", /*newDescription*/ 466 shortPic, /*newProfilePic*/ 467 10*100, /*newCreatorBasisPoints*/ 468 100*100*100, /*newStakeMultipleBasisPoints*/ 469 false /*isHidden*/) 470 require.Error(err) 471 require.Contains(err.Error(), RuleErrorProfileStakeMultipleSize) 472 } 473 474 // Stake multiple too small should fail long too long should fail. 475 { 476 _, _, _, err = _updateProfile( 477 t, chain, db, params, 478 1, /*feeRateNanosPerKB*/ 479 m0Pub, /*updaterPkBase58Check*/ 480 m0Priv, /*updaterPrivBase58Check*/ 481 []byte{}, /*profilePubKey*/ 482 "m0", /*newUsername*/ 483 "i am the m0", /*newDescription*/ 484 shortPic, /*newProfilePic*/ 485 10*100, /*newCreatorBasisPoints*/ 486 .99*100*100, /*newStakeMultipleBasisPoints*/ 487 false /*isHidden*/) 488 require.Error(err) 489 require.Contains(err.Error(), RuleErrorProfileStakeMultipleSize) 490 } 491 492 // Creator percentage too large should fail. 493 { 494 _, _, _, err = _updateProfile( 495 t, chain, db, params, 496 1, /*feeRateNanosPerKB*/ 497 m0Pub, /*updaterPkBase58Check*/ 498 m0Priv, /*updaterPrivBase58Check*/ 499 []byte{}, /*profilePubKey*/ 500 "m0", /*newUsername*/ 501 "i am the m0", /*newDescription*/ 502 shortPic, /*newProfilePic*/ 503 101*100, /*newCreatorBasisPoints*/ 504 1.25*100*100, /*newStakeMultipleBasisPoints*/ 505 false /*isHidden*/) 506 require.Error(err) 507 require.Contains(err.Error(), RuleErrorProfileCreatorPercentageSize) 508 } 509 510 // Invalid profile public key should fail. 511 { 512 _, _, _, err = _updateProfile( 513 t, chain, db, params, 514 1, /*feeRateNanosPerKB*/ 515 m0Pub, /*updaterPkBase58Check*/ 516 m0Priv, /*updaterPrivBase58Check*/ 517 RandomBytes(33), /*profilePubKey*/ 518 "m0", /*newUsername*/ 519 "i am the m0", /*newDescription*/ 520 shortPic, /*newProfilePic*/ 521 10*100, /*newCreatorBasisPoints*/ 522 1.25*100*100, /*newStakeMultipleBasisPoints*/ 523 false /*isHidden*/) 524 require.Error(err) 525 // This returned RuleErrorProfilePubKeyNotAuthorized for me once 526 // "ConnectTransaction: : _connectUpdateProfile: ... RuleErrorProfilePubKeyNotAuthorized" 527 require.Contains(err.Error(), RuleErrorProfileBadPublicKey) 528 } 529 530 // Profile public key that is not authorized should fail. 531 { 532 _, _, _, err = _updateProfile( 533 t, chain, db, params, 534 1, /*feeRateNanosPerKB*/ 535 m0Pub, /*updaterPkBase58Check*/ 536 m0Priv, /*updaterPrivBase58Check*/ 537 m1PkBytes, /*profilePubKey*/ 538 "m0", /*newUsername*/ 539 "i am the m0", /*newDescription*/ 540 shortPic, /*newProfilePic*/ 541 10*100, /*newCreatorBasisPoints*/ 542 1.25*100*100, /*newStakeMultipleBasisPoints*/ 543 false /*isHidden*/) 544 require.Error(err) 545 require.Contains(err.Error(), RuleErrorProfilePubKeyNotAuthorized) 546 } 547 548 // A simple registration should succeed 549 { 550 updateProfile( 551 1, /*feeRateNanosPerKB*/ 552 m0Pub, /*updaterPkBase58Check*/ 553 m0Priv, /*updaterPrivBase58Check*/ 554 []byte{}, /*profilePubKey*/ 555 "m0", /*newUsername*/ 556 "i am the m0", /*newDescription*/ 557 shortPic, /*newProfilePic*/ 558 10*100, /*newCreatorBasisPoints*/ 559 1.25*100*100, /*newStakeMultipleBasisPoints*/ 560 false /*isHidden*/) 561 } 562 563 // Username that does not match our regex should fail 564 { 565 _, _, _, err = _updateProfile( 566 t, chain, db, params, 567 10, /*feeRateNanosPerKB*/ 568 m1Pub, /*updaterPkBase58Check*/ 569 m1Priv, /*updaterPrivBase58Check*/ 570 []byte{}, /*profilePubKey*/ 571 "m0\x00", /*newUsername*/ 572 "i am the m0", /*newDescription*/ 573 shortPic, /*newProfilePic*/ 574 10*100, /*newCreatorBasisPoints*/ 575 1.25*100*100, /*newStakeMultipleBasisPoints*/ 576 false /*isHidden*/) 577 require.Error(err) 578 require.Contains(err.Error(), RuleErrorInvalidUsername) 579 580 _, _, _, err = _updateProfile( 581 t, chain, db, params, 582 10, /*feeRateNanosPerKB*/ 583 m1Pub, /*updaterPkBase58Check*/ 584 m1Priv, /*updaterPrivBase58Check*/ 585 []byte{}, /*profilePubKey*/ 586 "m0 with a space", /*newUsername*/ 587 "i am the m0", /*newDescription*/ 588 shortPic, /*newProfilePic*/ 589 10*100, /*newCreatorBasisPoints*/ 590 1.25*100*100, /*newStakeMultipleBasisPoints*/ 591 false /*isHidden*/) 592 require.Error(err) 593 require.Contains(err.Error(), RuleErrorInvalidUsername) 594 595 _, _, _, err = _updateProfile( 596 t, chain, db, params, 597 10, /*feeRateNanosPerKB*/ 598 m1Pub, /*updaterPkBase58Check*/ 599 m1Priv, /*updaterPrivBase58Check*/ 600 []byte{}, /*profilePubKey*/ 601 "m0TraillingSpace ", /*newUsername*/ 602 "i am the m0", /*newDescription*/ 603 shortPic, /*newProfilePic*/ 604 10*100, /*newCreatorBasisPoints*/ 605 1.25*100*100, /*newStakeMultipleBasisPoints*/ 606 false /*isHidden*/) 607 require.Error(err) 608 require.Contains(err.Error(), RuleErrorInvalidUsername) 609 610 _, _, _, err = _updateProfile( 611 t, chain, db, params, 612 10, /*feeRateNanosPerKB*/ 613 m1Pub, /*updaterPkBase58Check*/ 614 m1Priv, /*updaterPrivBase58Check*/ 615 []byte{}, /*profilePubKey*/ 616 "m0-Hyphen", /*newUsername*/ 617 "i am the m0", /*newDescription*/ 618 shortPic, /*newProfilePic*/ 619 10*100, /*newCreatorBasisPoints*/ 620 1.25*100*100, /*newStakeMultipleBasisPoints*/ 621 false /*isHidden*/) 622 require.Error(err) 623 require.Contains(err.Error(), RuleErrorInvalidUsername) 624 625 _, _, _, err = _updateProfile( 626 t, chain, db, params, 627 10, /*feeRateNanosPerKB*/ 628 m1Pub, /*updaterPkBase58Check*/ 629 m1Priv, /*updaterPrivBase58Check*/ 630 []byte{}, /*profilePubKey*/ 631 " m0SpaceAtBeginning", /*newUsername*/ 632 "i am the m0", /*newDescription*/ 633 shortPic, /*newProfilePic*/ 634 10*100, /*newCreatorBasisPoints*/ 635 1.25*100*100, /*newStakeMultipleBasisPoints*/ 636 false /*isHidden*/) 637 require.Error(err) 638 require.Contains(err.Error(), RuleErrorInvalidUsername) 639 } 640 641 // Trying to take an already-registered username should fail. 642 { 643 _, _, _, err = _updateProfile( 644 t, chain, db, params, 645 1, /*feeRateNanosPerKB*/ 646 m1Pub, /*updaterPkBase58Check*/ 647 m1Priv, /*updaterPrivBase58Check*/ 648 []byte{}, /*profilePubKey*/ 649 "m0", /*newUsername*/ 650 "i am the m0", /*newDescription*/ 651 shortPic, /*newProfilePic*/ 652 10*100, /*newCreatorBasisPoints*/ 653 1.25*100*100, /*newStakeMultipleBasisPoints*/ 654 false /*isHidden*/) 655 require.Error(err) 656 require.Contains(err.Error(), RuleErrorProfileUsernameExists) 657 658 // The username should be case-insensitive so creating a duplicate 659 // with different casing should fail. 660 _, _, _, err = _updateProfile( 661 t, chain, db, params, 662 1, /*feeRateNanosPerKB*/ 663 m1Pub, /*updaterPkBase58Check*/ 664 m1Priv, /*updaterPrivBase58Check*/ 665 []byte{}, /*profilePubKey*/ 666 "M0", /*newUsername*/ 667 "i am the m0", /*newDescription*/ 668 shortPic, /*newProfilePic*/ 669 10*100, /*newCreatorBasisPoints*/ 670 1.25*100*100, /*newStakeMultipleBasisPoints*/ 671 false /*isHidden*/) 672 require.Error(err) 673 require.Contains(err.Error(), RuleErrorProfileUsernameExists) 674 675 // Register m1 and then try to steal the username 676 updateProfile( 677 1, /*feeRateNanosPerKB*/ 678 m1Pub, /*updaterPkBase58Check*/ 679 m1Priv, /*updaterPrivBase58Check*/ 680 []byte{}, /*profilePubKey*/ 681 "m1", /*newUsername*/ 682 "i am the m1", /*newDescription*/ 683 shortPic, /*newProfilePic*/ 684 10*100, /*newCreatorBasisPoints*/ 685 1.25*100*100, /*newStakeMultipleBasisPoints*/ 686 false /*isHidden*/) 687 688 _, _, _, err = _updateProfile( 689 t, chain, db, params, 690 1, /*feeRateNanosPerKB*/ 691 m1Pub, /*updaterPkBase58Check*/ 692 m1Priv, /*updaterPrivBase58Check*/ 693 []byte{}, /*profilePubKey*/ 694 "m0", /*newUsername*/ 695 "i am the m0", /*newDescription*/ 696 shortPic, /*newProfilePic*/ 697 10*100, /*newCreatorBasisPoints*/ 698 1.25*100*100, /*newStakeMultipleBasisPoints*/ 699 false /*isHidden*/) 700 require.Error(err) 701 require.Contains(err.Error(), RuleErrorProfileUsernameExists) 702 703 // The username should be case-insensitive so creating a duplicate 704 // with different casing should fail. 705 _, _, _, err = _updateProfile( 706 t, chain, db, params, 707 1, /*feeRateNanosPerKB*/ 708 m1Pub, /*updaterPkBase58Check*/ 709 m1Priv, /*updaterPrivBase58Check*/ 710 []byte{}, /*profilePubKey*/ 711 "M0", /*newUsername*/ 712 "i am the m0", /*newDescription*/ 713 shortPic, /*newProfilePic*/ 714 10*100, /*newCreatorBasisPoints*/ 715 1.25*100*100, /*newStakeMultipleBasisPoints*/ 716 false /*isHidden*/) 717 require.Error(err) 718 require.Contains(err.Error(), RuleErrorProfileUsernameExists) 719 720 // The username should be case-insensitive so creating a duplicate 721 // with different casing should fail. 722 _, _, _, err = _updateProfile( 723 t, chain, db, params, 724 1, /*feeRateNanosPerKB*/ 725 m0Pub, /*updaterPkBase58Check*/ 726 m0Priv, /*updaterPrivBase58Check*/ 727 []byte{}, /*profilePubKey*/ 728 "M1", /*newUsername*/ 729 "i am the m0", /*newDescription*/ 730 shortPic, /*newProfilePic*/ 731 10*100, /*newCreatorBasisPoints*/ 732 1.25*100*100, /*newStakeMultipleBasisPoints*/ 733 false /*isHidden*/) 734 require.Error(err) 735 require.Contains(err.Error(), RuleErrorProfileUsernameExists) 736 } 737 738 // Register m2 (should succeed) 739 { 740 updateProfile( 741 1, /*feeRateNanosPerKB*/ 742 m2Pub, /*updaterPkBase58Check*/ 743 m2Priv, /*updaterPrivBase58Check*/ 744 []byte{}, /*profilePubKey*/ 745 "m2", /*newUsername*/ 746 "i am the m2", /*newDescription*/ 747 shortPic, /*newProfilePic*/ 748 10*100, /*newCreatorBasisPoints*/ 749 1.25*100*100, /*newStakeMultipleBasisPoints*/ 750 false /*isHidden*/) 751 } 752 753 // Leaving username, description, and pic blank should result in a noop. 754 { 755 updateProfile( 756 10, /*feeRateNanosPerKB*/ 757 m2Pub, /*updaterPkBase58Check*/ 758 m2Priv, /*updaterPrivBase58Check*/ 759 []byte{}, /*profilePubKey*/ 760 "", /*newUsername*/ 761 "", /*newDescription*/ 762 "", /*newProfilePic*/ 763 10*100, /*newCreatorBasisPoints*/ 764 1.25*100*100, /*newStakeMultipleBasisPoints*/ 765 false /*isHidden*/) 766 } 767 768 // An update followed by a reversion should result in no change. 769 { 770 updateProfile( 771 1, /*feeRateNanosPerKB*/ 772 m2Pub, /*updaterPkBase58Check*/ 773 m2Priv, /*updaterPrivBase58Check*/ 774 []byte{}, /*profilePubKey*/ 775 "m2_update", /*newUsername*/ 776 "i am the m2 update", /*newDescription*/ 777 shortPic+"woohoo", /*newProfilePic*/ 778 15*100, /*newCreatorBasisPoints*/ 779 1.7*100*100, /*newStakeMultipleBasisPoints*/ 780 true /*isHidden*/) 781 782 updateProfile( 783 1, /*feeRateNanosPerKB*/ 784 m2Pub, /*updaterPkBase58Check*/ 785 m2Priv, /*updaterPrivBase58Check*/ 786 []byte{}, /*profilePubKey*/ 787 "m2", /*newUsername*/ 788 "i am the m2", /*newDescription*/ 789 shortPic, /*newProfilePic*/ 790 10*100, /*newCreatorBasisPoints*/ 791 1.25*100*100, /*newStakeMultipleBasisPoints*/ 792 false /*isHidden*/) 793 } 794 795 // A normal user updating their profile should succeed. 796 { 797 updateProfile( 798 1, /*feeRateNanosPerKB*/ 799 m1Pub, /*updaterPkBase58Check*/ 800 m1Priv, /*updaterPrivBase58Check*/ 801 []byte{}, /*profilePubKey*/ 802 "m1_updated_by_m1", /*newUsername*/ 803 "m1 updated by m1", /*newDescription*/ 804 otherShortPic, /*newProfilePic*/ 805 12*100, /*newCreatorBasisPoints*/ 806 1.6*100*100, /*newStakeMultipleBasisPoints*/ 807 true /*isHidden*/) 808 } 809 810 // Normal user updating another user's profile should fail. 811 { 812 _, _, _, err = _updateProfile( 813 t, chain, db, params, 814 1, /*feeRateNanosPerKB*/ 815 m1Pub, /*updaterPkBase58Check*/ 816 m1Priv, /*updaterPrivBase58Check*/ 817 m0PkBytes, /*profilePubKey*/ 818 "m0_actually_m1", /*newUsername*/ 819 "i am the m1", /*newDescription*/ 820 shortPic, /*newProfilePic*/ 821 10*100, /*newCreatorBasisPoints*/ 822 1.25*100*100, /*newStakeMultipleBasisPoints*/ 823 false /*isHidden*/) 824 require.Error(err) 825 require.Contains(err.Error(), RuleErrorProfilePubKeyNotAuthorized) 826 } 827 828 // ParamUpdater updating another user's profile should succeed. 829 { 830 updateProfile( 831 1, /*feeRateNanosPerKB*/ 832 m3Pub, /*updaterPkBase58Check*/ 833 m3Priv, /*updaterPrivBase58Check*/ 834 m0PkBytes, /*profilePubKey*/ 835 "m0_paramUpdater", /*newUsername*/ 836 "m0 updated by paramUpdater", /*newDescription*/ 837 otherShortPic, /*newProfilePic*/ 838 11*100, /*newCreatorBasisPoints*/ 839 1.5*100*100, /*newStakeMultipleBasisPoints*/ 840 true /*isHidden*/) 841 } 842 843 // ParamUpdater creating another user's profile should succeed. 844 { 845 updateProfile( 846 1, /*feeRateNanosPerKB*/ 847 m3Pub, /*updaterPkBase58Check*/ 848 m3Priv, /*updaterPrivBase58Check*/ 849 m5PkBytes, /*profilePubKey*/ 850 "m5_paramUpdater", /*newUsername*/ 851 "m5 created by paramUpdater", /*newDescription*/ 852 otherShortPic, /*newProfilePic*/ 853 11*100, /*newCreatorBasisPoints*/ 854 1.5*100*100, /*newStakeMultipleBasisPoints*/ 855 false /*isHidden*/) 856 } 857 858 // Create Profile Fee and Minimum Network Fee tests 859 { 860 // Set the create profile fee to 100 nanos 861 updateGlobalParamsEntry( 862 100, 863 m3Pub, 864 m3Priv, 865 int64(InitialUSDCentsPerBitcoinExchangeRate), 866 0, 867 100) 868 869 // m4 does not have enough to create a profile including fee 870 _, _, _, err = _updateProfile( 871 t, chain, db, params, 872 1, 873 m4Pub, 874 m4Priv, 875 m4PkBytes, 876 "m4_username", 877 "m4 desc", 878 shortPic, 879 11*100, 880 1.5*100*100, 881 false) 882 require.Error(err) 883 require.Contains(err.Error(), RuleErrorCreateProfileTxnOutputExceedsInput) 884 // Reduce the create profile fee, Set minimum network fee to 10 nanos per kb 885 886 updateGlobalParamsEntry( 887 100, 888 m3Pub, 889 m3Priv, 890 int64(InitialUSDCentsPerBitcoinExchangeRate), 891 5, 892 1) 893 894 // Update profile fails as the fee is too low 895 _, _, _, err = _updateProfile( 896 t, chain, db, params, 897 1, 898 m4Pub, 899 m4Priv, 900 m4PkBytes, 901 "m4_username", 902 "m4 description", 903 otherShortPic, 904 11*100, 905 1.5*100*100, 906 false, 907 ) 908 require.Error(err) 909 require.Contains(err.Error(), RuleErrorTxnFeeBelowNetworkMinimum) 910 // Update succeeds because fee is high enough and user has enough to meet fee. 911 updateProfile( 912 10, 913 m4Pub, 914 m4Priv, 915 m4PkBytes, 916 "m4", 917 "m4 description", 918 otherShortPic, 919 11*100, 920 1.5*100*100, 921 false, 922 ) 923 // Reset the create profile fee to 0 nanos (no fee) and set network minimum back to 0. 924 updateGlobalParamsEntry( 925 100, 926 m3Pub, 927 m3Priv, 928 int64(InitialUSDCentsPerBitcoinExchangeRate), 929 0, 930 0) 931 932 } 933 934 // user0 935 // m0Pub, m0_updated_by_paramUpdater, m0 updated by paramUpdater, otherShortPic, 11*100, 1.5*100*100, true 936 // user1 937 // m1Pub, m1_updated_by_m1, m1 updated by m1, otherShortPic, 12*100, 1.6*100*100, true 938 // user2 939 // m2Pub, m2, i am m2, 10*100, 1.25*100*100 940 // user5 941 // m5Pub, m5_paramUpdater, m5 created by paramUpdater, otherShortPic, 11*100, 1.5*100*100, false 942 checkProfilesExist := func() { 943 utxoView, err := NewUtxoView(db, params, nil) 944 require.NoError(err) 945 profileEntriesByPublicKey, _, _, _, err := utxoView.GetAllProfiles(nil) 946 require.NoError(err) 947 // 3 profiles from seed txns 948 require.Equal(8, len(profileEntriesByPublicKey)) 949 { 950 m0Entry, m0Exists := profileEntriesByPublicKey[MakePkMapKey(m0PkBytes)] 951 require.True(m0Exists) 952 require.Equal(string(m0Entry.Username), "m0_paramUpdater") 953 require.Equal(string(m0Entry.Description), "m0 updated by paramUpdater") 954 require.Equal(string(m0Entry.ProfilePic), otherShortPic) 955 require.Equal(int64(m0Entry.CreatorBasisPoints), int64(11*100)) 956 require.True(m0Entry.IsHidden) 957 } 958 { 959 m1Entry, m1Exists := profileEntriesByPublicKey[MakePkMapKey(m1PkBytes)] 960 require.True(m1Exists) 961 require.Equal(string(m1Entry.Username), "m1_updated_by_m1") 962 require.Equal(string(m1Entry.Description), "m1 updated by m1") 963 require.Equal(string(m1Entry.ProfilePic), otherShortPic) 964 require.Equal(int64(m1Entry.CreatorBasisPoints), int64(12*100)) 965 require.True(m1Entry.IsHidden) 966 } 967 { 968 m2Entry, m2Exists := profileEntriesByPublicKey[MakePkMapKey(m2PkBytes)] 969 require.True(m2Exists) 970 require.Equal(string(m2Entry.Username), "m2") 971 require.Equal(string(m2Entry.Description), "i am the m2") 972 require.Equal(string(m2Entry.ProfilePic), shortPic) 973 require.Equal(int64(m2Entry.CreatorBasisPoints), int64(10*100)) 974 require.False(m2Entry.IsHidden) 975 } 976 { 977 m4Entry, m4Exists := profileEntriesByPublicKey[MakePkMapKey(m4PkBytes)] 978 require.True(m4Exists) 979 require.Equal(string(m4Entry.Username), "m4") 980 require.Equal(string(m4Entry.Description), "m4 description") 981 require.Equal(string(m4Entry.ProfilePic), otherShortPic) 982 require.Equal(int64(m4Entry.CreatorBasisPoints), int64(11*100)) 983 require.False(m4Entry.IsHidden) 984 } 985 { 986 m5Entry, m5Exists := profileEntriesByPublicKey[MakePkMapKey(m5PkBytes)] 987 require.True(m5Exists) 988 require.Equal(string(m5Entry.Username), "m5_paramUpdater") 989 require.Equal(string(m5Entry.Description), "m5 created by paramUpdater") 990 require.Equal(string(m5Entry.ProfilePic), otherShortPic) 991 require.Equal(int64(m5Entry.CreatorBasisPoints), int64(11*100)) 992 require.False(m5Entry.IsHidden) 993 } 994 } 995 checkProfilesExist() 996 997 checkProfilesDeleted := func() { 998 utxoView, err := NewUtxoView(db, params, nil) 999 require.NoError(err) 1000 profileEntriesByPublicKey, _, _, _, err := utxoView.GetAllProfiles(nil) 1001 require.NoError(err) 1002 // 3 remain because of the seed txns 1003 require.Equal(3, len(profileEntriesByPublicKey)) 1004 } 1005 1006 // =================================================================================== 1007 // Finish it off with some transactions 1008 // =================================================================================== 1009 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 1010 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 1011 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 1012 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 1013 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 1014 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 1015 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 1016 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 1017 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 1018 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 1019 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 1020 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 1021 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1022 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 1023 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 1024 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1025 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 1026 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1027 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 1028 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1029 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 1030 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 1031 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1032 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1033 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1034 1035 // Roll back all of the above using the utxoOps from each. 1036 for ii := 0; ii < len(txnOps); ii++ { 1037 backwardIter := len(txnOps) - 1 - ii 1038 currentOps := txnOps[backwardIter] 1039 currentTxn := txns[backwardIter] 1040 fmt.Printf("Disconnecting transaction with type %v index %d (going backwards)\n", currentTxn.TxnMeta.GetTxnType(), backwardIter) 1041 1042 utxoView, err := NewUtxoView(db, params, nil) 1043 require.NoError(err) 1044 1045 currentHash := currentTxn.Hash() 1046 err = utxoView.DisconnectTransaction(currentTxn, currentHash, currentOps, savedHeight) 1047 require.NoError(err) 1048 1049 require.NoError(utxoView.FlushToDb()) 1050 1051 // After disconnecting, the balances should be restored to what they 1052 // were before this transaction was applied. 1053 require.Equal(expectedSenderBalances[backwardIter], _getBalance(t, chain, nil, PkToStringTestnet(currentTxn.PublicKey))) 1054 } 1055 1056 // Verify that all the profiles have been deleted. 1057 checkProfilesDeleted() 1058 1059 // Apply all the transactions to a mempool object and make sure we don't get any 1060 // errors. Verify the balances align as we go. 1061 for ii, tx := range txns { 1062 // See comment above on this transaction. 1063 fmt.Printf("Adding txn %d of type %v to mempool\n", ii, tx.TxnMeta.GetTxnType()) 1064 1065 require.Equal(expectedSenderBalances[ii], _getBalance( 1066 t, chain, mempool, PkToStringTestnet(tx.PublicKey))) 1067 1068 _, err := mempool.ProcessTransaction(tx, false, false, 0, true) 1069 require.NoError(err, "Problem adding transaction %d to mempool: %v", ii, tx) 1070 } 1071 1072 // Apply all the transactions to a view and flush the view to the db. 1073 utxoView, err := NewUtxoView(db, params, nil) 1074 require.NoError(err) 1075 for ii, txn := range txns { 1076 fmt.Printf("Adding txn %v of type %v to UtxoView\n", ii, txn.TxnMeta.GetTxnType()) 1077 1078 // Always use height+1 for validation since it's assumed the transaction will 1079 // get mined into the next block. 1080 txHash := txn.Hash() 1081 blockHeight := chain.blockTip().Height + 1 1082 _, _, _, _, err := 1083 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 1084 require.NoError(err) 1085 } 1086 // Flush the utxoView after having added all the transactions. 1087 require.NoError(utxoView.FlushToDb()) 1088 1089 // Verify the profiles exist. 1090 checkProfilesExist() 1091 1092 // Disonnect the transactions from a single view in the same way as above 1093 // i.e. without flushing each time. 1094 utxoView2, err := NewUtxoView(db, params, nil) 1095 require.NoError(err) 1096 for ii := 0; ii < len(txnOps); ii++ { 1097 backwardIter := len(txnOps) - 1 - ii 1098 fmt.Printf("Disconnecting transaction with index %d (going backwards)\n", backwardIter) 1099 currentOps := txnOps[backwardIter] 1100 currentTxn := txns[backwardIter] 1101 1102 currentHash := currentTxn.Hash() 1103 err = utxoView2.DisconnectTransaction(currentTxn, currentHash, currentOps, savedHeight) 1104 require.NoError(err) 1105 } 1106 require.NoError(utxoView2.FlushToDb()) 1107 require.Equal(expectedSenderBalances[0], _getBalance(t, chain, nil, senderPkString)) 1108 1109 // Verify that all the profiles have been deleted. 1110 checkProfilesDeleted() 1111 1112 // All the txns should be in the mempool already so mining a block should put 1113 // all those transactions in it. 1114 block, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 1115 require.NoError(err) 1116 // Add one for the block reward. Now we have a meaty block. 1117 require.Equal(len(txnOps)+1, len(block.Txns)) 1118 1119 // Roll back the block and make sure we don't hit any errors. 1120 { 1121 utxoView, err := NewUtxoView(db, params, nil) 1122 require.NoError(err) 1123 1124 // Fetch the utxo operations for the block we're detaching. We need these 1125 // in order to be able to detach the block. 1126 hash, err := block.Header.Hash() 1127 require.NoError(err) 1128 utxoOps, err := GetUtxoOperationsForBlock(db, hash) 1129 require.NoError(err) 1130 1131 // Compute the hashes for all the transactions. 1132 txHashes, err := ComputeTransactionHashes(block.Txns) 1133 require.NoError(err) 1134 require.NoError(utxoView.DisconnectBlock(block, txHashes, utxoOps)) 1135 1136 // Flushing the view after applying and rolling back should work. 1137 require.NoError(utxoView.FlushToDb()) 1138 } 1139 1140 // Verify that all the profiles have been deleted. 1141 checkProfilesDeleted() 1142 } 1143 1144 func TestSpamUpdateProfile(t *testing.T) { 1145 assert := assert.New(t) 1146 require := require.New(t) 1147 _, _ = assert, require 1148 1149 f, err := os.Create("/tmp/perf") 1150 if err != nil { 1151 log.Fatal(err) 1152 } 1153 pprof.StartCPUProfile(f) 1154 defer pprof.StopCPUProfile() 1155 1156 chain, params, _ := NewLowDifficultyBlockchain() 1157 mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/) 1158 feeRateNanosPerKB := uint64(11) 1159 _, _ = mempool, miner 1160 1161 numTxns := 250 1162 for ii := 0; ii < numTxns; ii++ { 1163 fmt.Println("Creating txns: ", ii) 1164 startTimeCreateTxn := time.Now() 1165 moneyPkBytes, _, _ := Base58CheckDecode(moneyPkString) 1166 txn, _, _, _, err := chain.CreateUpdateProfileTxn( 1167 moneyPkBytes, 1168 nil, 1169 "money", 1170 fmt.Sprintf("this is a new description: %v", ii), 1171 "profile pic", 1172 5000, /*CreatorBasisPoints*/ 1173 12500, /*StakeMultiple*/ 1174 false, /*isHidden*/ 1175 0, 1176 feeRateNanosPerKB, /*feeRateNanosPerKB*/ 1177 mempool, /*mempool*/ 1178 []*DeSoOutput{}) 1179 require.NoError(err) 1180 _signTxn(t, txn, moneyPrivString) 1181 fmt.Printf("Creating txn took: %v seconds\n", time.Since(startTimeCreateTxn).Seconds()) 1182 1183 fmt.Println("Running txns through mempool: ", ii) 1184 startTimeMempoolAdd := time.Now() 1185 mempoolTxsAdded, err := mempool.processTransaction( 1186 txn, true /*allowUnconnectedTxn*/, true /*rateLimit*/, 0, /*peerID*/ 1187 true /*verifySignatures*/) 1188 require.NoError(err) 1189 require.Equal(1, len(mempoolTxsAdded)) 1190 fmt.Printf("Adding to mempool took: %v seconds\n", time.Since(startTimeMempoolAdd).Seconds()) 1191 } 1192 } 1193 1194 func TestSwapIdentityNOOPCreatorCoinBuySimple(t *testing.T) { 1195 1196 creatorCoinTests := []*_CreatorCoinTestData{ 1197 // Create a profile for m0 1198 { 1199 TxnType: TxnTypeUpdateProfile, 1200 UpdaterPublicKeyBase58Check: m0Pub, 1201 UpdaterPrivateKeyBase58Check: m0Priv, 1202 ProfilePublicKeyBase58Check: m0Pub, 1203 ProfileUsername: "m0", 1204 ProfileDescription: "i am m0", 1205 ProfilePic: "m0 profile pic", 1206 ProfileCreatorBasisPoints: 2500, 1207 ProfileIsHidden: false, 1208 1209 SkipChecks: true, 1210 }, 1211 // Have m1 buy some of m0's coins 1212 { 1213 // These are the transaction params 1214 UpdaterPublicKeyBase58Check: m1Pub, 1215 UpdaterPrivateKeyBase58Check: m1Priv, 1216 ProfilePublicKeyBase58Check: m0Pub, 1217 OperationType: CreatorCoinOperationTypeBuy, 1218 DeSoToSellNanos: 1271123456, 1219 CreatorCoinToSellNanos: 0, 1220 DeSoToAddNanos: 0, 1221 MinDeSoExpectedNanos: 0, 1222 MinCreatorCoinExpectedNanos: 0, 1223 1224 // These are the expectations 1225 CoinsInCirculationNanos: 10832150315, 1226 DeSoLockedNanos: 1270996343, 1227 CoinWatermarkNanos: 10832150315, 1228 m0CCBalance: 2708037578, 1229 m0HasPurchased: false, 1230 m1CCBalance: 8124112737, 1231 m1HasPurchased: true, 1232 m2CCBalance: 0, 1233 m2HasPurchased: false, 1234 m0DeSoBalance: 5999999998, 1235 m1DeSoBalance: 4728876542, 1236 m2DeSoBalance: 6000000000, 1237 }, 1238 // Have m2 buy some of m0's coins 1239 { 1240 // These are the transaction params 1241 UpdaterPublicKeyBase58Check: m2Pub, 1242 UpdaterPrivateKeyBase58Check: m2Priv, 1243 ProfilePublicKeyBase58Check: m0Pub, 1244 OperationType: CreatorCoinOperationTypeBuy, 1245 DeSoToSellNanos: 1172373183, 1246 CreatorCoinToSellNanos: 0, 1247 DeSoToAddNanos: 0, 1248 MinDeSoExpectedNanos: 0, 1249 MinCreatorCoinExpectedNanos: 0, 1250 1251 // These are the expectations 1252 CoinsInCirculationNanos: 13468606753, 1253 DeSoLockedNanos: 2443252288, 1254 CoinWatermarkNanos: 13468606753, 1255 m0CCBalance: 3367151687, 1256 m0HasPurchased: false, 1257 m1CCBalance: 8124112737, 1258 m1HasPurchased: true, 1259 m2CCBalance: 1977342329, 1260 m2HasPurchased: true, 1261 m0DeSoBalance: 5999999998, 1262 m1DeSoBalance: 4728876542, 1263 m2DeSoBalance: 4827626815, 1264 }, 1265 // Have m1 sell half of their stake 1266 { 1267 // These are the transaction params 1268 UpdaterPublicKeyBase58Check: m1Pub, 1269 UpdaterPrivateKeyBase58Check: m1Priv, 1270 ProfilePublicKeyBase58Check: m0Pub, 1271 OperationType: CreatorCoinOperationTypeSell, 1272 DeSoToSellNanos: 0, 1273 CreatorCoinToSellNanos: 4123456789, 1274 DeSoToAddNanos: 0, 1275 MinDeSoExpectedNanos: 0, 1276 MinCreatorCoinExpectedNanos: 0, 1277 1278 // These are the expectations 1279 CoinsInCirculationNanos: 9345149964, 1280 DeSoLockedNanos: 816129494, 1281 CoinWatermarkNanos: 13468606753, 1282 m0CCBalance: 3367151687, 1283 m0HasPurchased: false, 1284 m1CCBalance: 4000655948, 1285 m1HasPurchased: true, 1286 m2CCBalance: 1977342329, 1287 m2HasPurchased: true, 1288 m0DeSoBalance: 5999999998, 1289 m1DeSoBalance: 6355836621, 1290 m2DeSoBalance: 4827626815, 1291 m3DeSoBalance: 6000000000, 1292 }, 1293 // Swap m0 and m3 1294 // State after swapping: 1295 // m0Pk: m3 profile 1296 // m3Pk: m0 profile (the one with the profile) 1297 { 1298 TxnType: TxnTypeSwapIdentity, 1299 FromPublicKey: m0PkBytes, 1300 ToPublicKey: m3PkBytes, 1301 // This is the creator whose coins we are testing the balances of. 1302 // Normally it's m0, but because we swapped m0 for m3 we should test 1303 // that one instead. 1304 ProfilePublicKeyBase58Check: m3Pub, 1305 1306 // These are the expectations 1307 CoinsInCirculationNanos: 9345149964, 1308 DeSoLockedNanos: 816129494, 1309 CoinWatermarkNanos: 13468606753, 1310 m0CCBalance: 0, // Should go to zero because it belongs to m3 now 1311 m0HasPurchased: false, 1312 m1CCBalance: 4000655948, 1313 m1HasPurchased: true, 1314 m2CCBalance: 1977342329, 1315 m2HasPurchased: true, 1316 m3CCBalance: 3367151687, 1317 m3HasPurchased: false, 1318 m0DeSoBalance: 5999999998, 1319 m1DeSoBalance: 6355836621, 1320 m2DeSoBalance: 4827626815, 1321 m3DeSoBalance: 6000000000, 1322 }, 1323 // Swap m3 and m1 1324 // State after swapping: 1325 // m0Pk: m3 profile 1326 // m1Pk: m0 profile (the one with the profile) 1327 // m3Pk: m1 profile 1328 { 1329 TxnType: TxnTypeSwapIdentity, 1330 FromPublicKey: m3PkBytes, 1331 ToPublicKey: m1PkBytes, 1332 // This is the creator whose coins we are testing the balances of. 1333 // Normally it's m0, but because we swapped m0 for m3 we should test 1334 // that one instead. 1335 ProfilePublicKeyBase58Check: m1Pub, 1336 1337 // These are the expectations 1338 CoinsInCirculationNanos: 9345149964, 1339 DeSoLockedNanos: 816129494, 1340 CoinWatermarkNanos: 13468606753, 1341 m0CCBalance: 0, // m0 still has zero because they got a dummy profile 1342 m0HasPurchased: false, 1343 m1CCBalance: 3367151687, // This becomes what m3 had a moment ago. 1344 m1HasPurchased: false, 1345 m2CCBalance: 1977342329, 1346 m2HasPurchased: true, 1347 m3CCBalance: 4000655948, // This becomes what m1 had a moment ago. 1348 m3HasPurchased: true, 1349 m0DeSoBalance: 5999999998, 1350 m1DeSoBalance: 6355836621, 1351 m2DeSoBalance: 4827626815, 1352 m3DeSoBalance: 6000000000, 1353 }, 1354 // Swap m0 and m1. Should restore m0's profile to it. 1355 // State after swapping: 1356 // m0Pk: m0 profile (the one with the profile) 1357 // m1Pk: m3 profile 1358 // m3Pk: m1 profile 1359 { 1360 TxnType: TxnTypeSwapIdentity, 1361 FromPublicKey: m0PkBytes, 1362 ToPublicKey: m1PkBytes, 1363 // This is the creator whose coins we are testing the balances of. 1364 // Normally it's m0, but because we swapped m0 for m3 we should test 1365 // that one instead. 1366 ProfilePublicKeyBase58Check: m0Pub, 1367 1368 // These are the expectations 1369 CoinsInCirculationNanos: 9345149964, 1370 DeSoLockedNanos: 816129494, 1371 CoinWatermarkNanos: 13468606753, 1372 m0CCBalance: 3367151687, // m0 should be back to normal 1373 m0HasPurchased: false, 1374 m1CCBalance: 0, // m1 is the zero now since it took the empty profile from m0 1375 m1HasPurchased: false, 1376 m2CCBalance: 1977342329, 1377 m2HasPurchased: true, 1378 m3CCBalance: 4000655948, // This is still what m1 started with. 1379 m3HasPurchased: true, 1380 m0DeSoBalance: 5999999998, 1381 m1DeSoBalance: 6355836621, 1382 m2DeSoBalance: 4827626815, 1383 m3DeSoBalance: 6000000000, 1384 }, 1385 // Swap m1 and m3. Should restore everything back to normal. 1386 // State after swapping: 1387 // m0Pk: m0 profile 1388 // m1Pk: m1 profile (the one with the profile) 1389 // m3Pk: m3 profile 1390 { 1391 TxnType: TxnTypeSwapIdentity, 1392 FromPublicKey: m1PkBytes, 1393 ToPublicKey: m3PkBytes, 1394 // This is the creator whose coins we are testing the balances of. 1395 // Normally it's m0, but because we swapped m0 for m3 we should test 1396 // that one instead. 1397 ProfilePublicKeyBase58Check: m0Pub, 1398 1399 // These are the expectations 1400 CoinsInCirculationNanos: 9345149964, 1401 DeSoLockedNanos: 816129494, 1402 CoinWatermarkNanos: 13468606753, 1403 m0CCBalance: 3367151687, 1404 m0HasPurchased: false, 1405 m1CCBalance: 4000655948, 1406 m1HasPurchased: true, 1407 m2CCBalance: 1977342329, 1408 m2HasPurchased: true, 1409 m3CCBalance: 0, // This goes back to zero as we started with. 1410 m3HasPurchased: false, 1411 m0DeSoBalance: 5999999998, 1412 m1DeSoBalance: 6355836621, 1413 m2DeSoBalance: 4827626815, 1414 m3DeSoBalance: 6000000000, 1415 }, 1416 // Have m2 sell all of their stake 1417 { 1418 // These are the transaction params 1419 UpdaterPublicKeyBase58Check: m2Pub, 1420 UpdaterPrivateKeyBase58Check: m2Priv, 1421 ProfilePublicKeyBase58Check: m0Pub, 1422 OperationType: CreatorCoinOperationTypeSell, 1423 DeSoToSellNanos: 0, 1424 CreatorCoinToSellNanos: 1977342329, 1425 DeSoToAddNanos: 0, 1426 MinDeSoExpectedNanos: 0, 1427 MinCreatorCoinExpectedNanos: 0, 1428 1429 // These are the expectations 1430 CoinsInCirculationNanos: 7367807635, 1431 DeSoLockedNanos: 399958612, 1432 CoinWatermarkNanos: 13468606753, 1433 m0CCBalance: 3367151687, 1434 m0HasPurchased: false, 1435 m1CCBalance: 4000655948, 1436 m1HasPurchased: true, 1437 m2CCBalance: 0, 1438 m2HasPurchased: false, 1439 m0DeSoBalance: 5999999998, 1440 m1DeSoBalance: 6355836621, 1441 m2DeSoBalance: 5243756077, 1442 }, 1443 // Have m1 buy more, m0 should receive 25% of the minted coins as a founders reward 1444 { 1445 // These are the transaction params 1446 UpdaterPublicKeyBase58Check: m1Pub, 1447 UpdaterPrivateKeyBase58Check: m1Priv, 1448 ProfilePublicKeyBase58Check: m0Pub, 1449 OperationType: CreatorCoinOperationTypeBuy, 1450 DeSoToSellNanos: 2123456789, 1451 CreatorCoinToSellNanos: 0, 1452 DeSoToAddNanos: 0, 1453 MinDeSoExpectedNanos: 0, 1454 MinCreatorCoinExpectedNanos: 0, 1455 1456 // These are the expectations 1457 CoinsInCirculationNanos: 13613944261, 1458 DeSoLockedNanos: 2523203055, 1459 CoinWatermarkNanos: 13613944261, 1460 m0CCBalance: 4928685843, 1461 m0HasPurchased: false, 1462 m1CCBalance: 8685258418, 1463 m1HasPurchased: true, 1464 m2CCBalance: 0, 1465 m2HasPurchased: false, 1466 m0DeSoBalance: 5999999998, 1467 m1DeSoBalance: 4232379830, 1468 m2DeSoBalance: 5243756077, 1469 }, 1470 1471 // Have m1 sell the rest of their stake 1472 { 1473 // These are the transaction params 1474 UpdaterPublicKeyBase58Check: m1Pub, 1475 UpdaterPrivateKeyBase58Check: m1Priv, 1476 ProfilePublicKeyBase58Check: m0Pub, 1477 OperationType: CreatorCoinOperationTypeSell, 1478 DeSoToSellNanos: 0, 1479 CreatorCoinToSellNanos: 8685258418, 1480 DeSoToAddNanos: 0, 1481 MinDeSoExpectedNanos: 0, 1482 MinCreatorCoinExpectedNanos: 0, 1483 1484 // These are the expectations 1485 CoinsInCirculationNanos: 4928685843, 1486 DeSoLockedNanos: 119727407, 1487 CoinWatermarkNanos: 13613944261, 1488 m0CCBalance: 4928685843, 1489 m0HasPurchased: false, 1490 m1CCBalance: 0, 1491 m1HasPurchased: false, 1492 m2CCBalance: 0, 1493 m2HasPurchased: false, 1494 m0DeSoBalance: 5999999998, 1495 m1DeSoBalance: 6635615128, 1496 m2DeSoBalance: 5243756077, 1497 }, 1498 1499 // Have m0 sell all of their stake except leave 1 DeSo locked 1500 { 1501 // These are the transaction params 1502 UpdaterPublicKeyBase58Check: m0Pub, 1503 UpdaterPrivateKeyBase58Check: m0Priv, 1504 ProfilePublicKeyBase58Check: m0Pub, 1505 OperationType: CreatorCoinOperationTypeSell, 1506 DeSoToSellNanos: 0, 1507 CreatorCoinToSellNanos: 4925685829, 1508 DeSoToAddNanos: 0, 1509 MinDeSoExpectedNanos: 0, 1510 MinCreatorCoinExpectedNanos: 0, 1511 1512 // These are the expectations 1513 CoinsInCirculationNanos: 3000014, 1514 DeSoLockedNanos: 1, 1515 CoinWatermarkNanos: 13613944261, 1516 m0CCBalance: 3000014, 1517 m0HasPurchased: false, 1518 m1CCBalance: 0, 1519 m1HasPurchased: false, 1520 m2CCBalance: 0, 1521 m2HasPurchased: false, 1522 m0DeSoBalance: 6119715429, 1523 m1DeSoBalance: 6635615128, 1524 m2DeSoBalance: 5243756077, 1525 }, 1526 1527 { 1528 // Have m0 sell all of their remaining stake except for 1 CreatorCoin nano 1529 // This will trigger an autosell. 1530 UpdaterPublicKeyBase58Check: m0Pub, 1531 UpdaterPrivateKeyBase58Check: m0Priv, 1532 ProfilePublicKeyBase58Check: m0Pub, 1533 OperationType: CreatorCoinOperationTypeSell, 1534 DeSoToSellNanos: 0, 1535 CreatorCoinToSellNanos: 3000013, 1536 DeSoToAddNanos: 0, 1537 MinDeSoExpectedNanos: 0, 1538 MinCreatorCoinExpectedNanos: 0, 1539 1540 // These are the expectations 1541 CoinsInCirculationNanos: 0, 1542 DeSoLockedNanos: 0, 1543 CoinWatermarkNanos: 13613944261, 1544 m0CCBalance: 0, 1545 m0HasPurchased: false, 1546 m1CCBalance: 0, 1547 m1HasPurchased: false, 1548 m2CCBalance: 0, 1549 m2HasPurchased: false, 1550 m0DeSoBalance: 6119715427, 1551 m1DeSoBalance: 6635615128, 1552 m2DeSoBalance: 5243756077, 1553 }, 1554 1555 // Have m1 buy a little more, m0 should receieve some 1556 { 1557 // These are the transaction params 1558 UpdaterPublicKeyBase58Check: m1Pub, 1559 UpdaterPrivateKeyBase58Check: m1Priv, 1560 ProfilePublicKeyBase58Check: m0Pub, 1561 OperationType: CreatorCoinOperationTypeBuy, 1562 DeSoToSellNanos: 2123456789, 1563 CreatorCoinToSellNanos: 0, 1564 DeSoToAddNanos: 0, 1565 MinDeSoExpectedNanos: 0, 1566 MinCreatorCoinExpectedNanos: 0, 1567 1568 // These are the expectations 1569 CoinsInCirculationNanos: 12852863707, 1570 DeSoLockedNanos: 2123244443, 1571 CoinWatermarkNanos: 13613944261, 1572 m0CCBalance: 3213215926, 1573 m0HasPurchased: false, 1574 m1CCBalance: 9639647781, 1575 m1HasPurchased: true, 1576 m2CCBalance: 0, 1577 m2HasPurchased: false, 1578 m0DeSoBalance: 6119715427, 1579 m1DeSoBalance: 4512158337, 1580 m2DeSoBalance: 5243756077, 1581 }, 1582 1583 // Have m1 sell their creator coins. m0 should be the only one left with coins. 1584 // Meaning m0 is effectively being left with their founders reward, even after all 1585 // their supporters have sold. 1586 { 1587 // These are the transaction params 1588 UpdaterPublicKeyBase58Check: m1Pub, 1589 UpdaterPrivateKeyBase58Check: m1Priv, 1590 ProfilePublicKeyBase58Check: m0Pub, 1591 OperationType: CreatorCoinOperationTypeSell, 1592 DeSoToSellNanos: 0, 1593 CreatorCoinToSellNanos: 9639647781, 1594 DeSoToAddNanos: 0, 1595 MinDeSoExpectedNanos: 0, 1596 MinCreatorCoinExpectedNanos: 0, 1597 1598 // These are the expectations 1599 CoinsInCirculationNanos: 3213215926, 1600 DeSoLockedNanos: 33175681, 1601 CoinWatermarkNanos: 13613944261, 1602 m0CCBalance: 3213215926, 1603 m0HasPurchased: false, 1604 m1CCBalance: 0, 1605 m1HasPurchased: false, 1606 m2CCBalance: 0, 1607 m2HasPurchased: false, 1608 m0DeSoBalance: 6119715427, 1609 m1DeSoBalance: 6602018090, 1610 m2DeSoBalance: 5243756077, 1611 }, 1612 } 1613 1614 _helpTestCreatorCoinBuySell(t, creatorCoinTests, false) 1615 } 1616 1617 func TestSwapIdentityCreatorCoinBuySimple(t *testing.T) { 1618 // Set up a blockchain 1619 assert := assert.New(t) 1620 require := require.New(t) 1621 _, _ = assert, require 1622 1623 creatorCoinTests := []*_CreatorCoinTestData{ 1624 // Create a profile for m0 1625 { 1626 TxnType: TxnTypeUpdateProfile, 1627 UpdaterPublicKeyBase58Check: m0Pub, 1628 UpdaterPrivateKeyBase58Check: m0Priv, 1629 ProfilePublicKeyBase58Check: m0Pub, 1630 ProfileUsername: "m0", 1631 ProfileDescription: "i am m0", 1632 ProfilePic: "m0 profile pic", 1633 ProfileCreatorBasisPoints: 2500, 1634 ProfileIsHidden: false, 1635 1636 SkipChecks: true, 1637 }, 1638 // Have m1 buy some of m0's coins 1639 { 1640 // These are the transaction params 1641 UpdaterPublicKeyBase58Check: m1Pub, 1642 UpdaterPrivateKeyBase58Check: m1Priv, 1643 ProfilePublicKeyBase58Check: m0Pub, 1644 OperationType: CreatorCoinOperationTypeBuy, 1645 DeSoToSellNanos: 1271123456, 1646 CreatorCoinToSellNanos: 0, 1647 DeSoToAddNanos: 0, 1648 MinDeSoExpectedNanos: 0, 1649 MinCreatorCoinExpectedNanos: 0, 1650 1651 // These are the expectations 1652 CoinsInCirculationNanos: 10832150315, 1653 DeSoLockedNanos: 1270996343, 1654 CoinWatermarkNanos: 10832150315, 1655 m0CCBalance: 2708037578, 1656 m0HasPurchased: false, 1657 m1CCBalance: 8124112737, 1658 m1HasPurchased: true, 1659 m2CCBalance: 0, 1660 m2HasPurchased: false, 1661 m0DeSoBalance: 5999999998, 1662 m1DeSoBalance: 4728876542, 1663 m2DeSoBalance: 6000000000, 1664 }, 1665 // Have m2 buy some of m0's coins 1666 { 1667 // These are the transaction params 1668 UpdaterPublicKeyBase58Check: m2Pub, 1669 UpdaterPrivateKeyBase58Check: m2Priv, 1670 ProfilePublicKeyBase58Check: m0Pub, 1671 OperationType: CreatorCoinOperationTypeBuy, 1672 DeSoToSellNanos: 1172373183, 1673 CreatorCoinToSellNanos: 0, 1674 DeSoToAddNanos: 0, 1675 MinDeSoExpectedNanos: 0, 1676 MinCreatorCoinExpectedNanos: 0, 1677 1678 // These are the expectations 1679 CoinsInCirculationNanos: 13468606753, 1680 DeSoLockedNanos: 2443252288, 1681 CoinWatermarkNanos: 13468606753, 1682 m0CCBalance: 3367151687, 1683 m0HasPurchased: false, 1684 m1CCBalance: 8124112737, 1685 m1HasPurchased: true, 1686 m2CCBalance: 1977342329, 1687 m2HasPurchased: true, 1688 m0DeSoBalance: 5999999998, 1689 m1DeSoBalance: 4728876542, 1690 m2DeSoBalance: 4827626815, 1691 }, 1692 // Have m1 sell half of their stake 1693 { 1694 // These are the transaction params 1695 UpdaterPublicKeyBase58Check: m1Pub, 1696 UpdaterPrivateKeyBase58Check: m1Priv, 1697 ProfilePublicKeyBase58Check: m0Pub, 1698 OperationType: CreatorCoinOperationTypeSell, 1699 DeSoToSellNanos: 0, 1700 CreatorCoinToSellNanos: 4123456789, 1701 DeSoToAddNanos: 0, 1702 MinDeSoExpectedNanos: 0, 1703 MinCreatorCoinExpectedNanos: 0, 1704 1705 // These are the expectations 1706 CoinsInCirculationNanos: 9345149964, 1707 DeSoLockedNanos: 816129494, 1708 CoinWatermarkNanos: 13468606753, 1709 m0CCBalance: 3367151687, 1710 m0HasPurchased: false, 1711 m1CCBalance: 4000655948, 1712 m1HasPurchased: true, 1713 m2CCBalance: 1977342329, 1714 m2HasPurchased: true, 1715 m0DeSoBalance: 5999999998, 1716 m1DeSoBalance: 6355836621, 1717 m2DeSoBalance: 4827626815, 1718 }, 1719 1720 // Swap m0 and m3 1721 { 1722 TxnType: TxnTypeSwapIdentity, 1723 FromPublicKey: m0PkBytes, 1724 ToPublicKey: m3PkBytes, 1725 // This is the creator whose coins we are testing the balances of. 1726 // Normally it's m0, but because we swapped m0 for m3 we should test 1727 // that one instead. 1728 ProfilePublicKeyBase58Check: m3Pub, 1729 1730 // These are the expectations 1731 CoinsInCirculationNanos: 9345149964, 1732 DeSoLockedNanos: 816129494, 1733 CoinWatermarkNanos: 13468606753, 1734 m0CCBalance: 0, // Should go to zero because it belongs to m3 now 1735 m0HasPurchased: false, 1736 m1CCBalance: 4000655948, 1737 m1HasPurchased: true, 1738 m2CCBalance: 1977342329, 1739 m2HasPurchased: true, 1740 m3CCBalance: 3367151687, 1741 m3HasPurchased: false, 1742 m0DeSoBalance: 5999999998, 1743 m1DeSoBalance: 6355836621, 1744 m2DeSoBalance: 4827626815, 1745 }, 1746 1747 // Have m2 sell all of their stake 1748 { 1749 // These are the transaction params 1750 UpdaterPublicKeyBase58Check: m2Pub, 1751 UpdaterPrivateKeyBase58Check: m2Priv, 1752 ProfilePublicKeyBase58Check: m3Pub, 1753 OperationType: CreatorCoinOperationTypeSell, 1754 DeSoToSellNanos: 0, 1755 CreatorCoinToSellNanos: 1977342329, 1756 DeSoToAddNanos: 0, 1757 MinDeSoExpectedNanos: 0, 1758 MinCreatorCoinExpectedNanos: 0, 1759 1760 // These are the expectations 1761 CoinsInCirculationNanos: 7367807635, 1762 DeSoLockedNanos: 399958612, 1763 CoinWatermarkNanos: 13468606753, 1764 m0CCBalance: 0, 1765 m0HasPurchased: false, 1766 m1CCBalance: 4000655948, 1767 m1HasPurchased: true, 1768 m2CCBalance: 0, 1769 m2HasPurchased: false, 1770 m3CCBalance: 3367151687, 1771 m3HasPurchased: false, 1772 m0DeSoBalance: 5999999998, 1773 m1DeSoBalance: 6355836621, 1774 m2DeSoBalance: 5243756077, 1775 }, 1776 // Have m1 buy more, m3 should receieve a founders reward 1777 { 1778 // These are the transaction params 1779 UpdaterPublicKeyBase58Check: m1Pub, 1780 UpdaterPrivateKeyBase58Check: m1Priv, 1781 ProfilePublicKeyBase58Check: m3Pub, 1782 OperationType: CreatorCoinOperationTypeBuy, 1783 DeSoToSellNanos: 2123456789, 1784 CreatorCoinToSellNanos: 0, 1785 DeSoToAddNanos: 0, 1786 MinDeSoExpectedNanos: 0, 1787 MinCreatorCoinExpectedNanos: 0, 1788 1789 // These are the expectations 1790 CoinsInCirculationNanos: 13613944261, 1791 DeSoLockedNanos: 2523203055, 1792 CoinWatermarkNanos: 13613944261, 1793 m0CCBalance: 0, 1794 m0HasPurchased: false, 1795 m1CCBalance: 8685258418, 1796 m1HasPurchased: true, 1797 m2CCBalance: 0, 1798 m2HasPurchased: false, 1799 m3CCBalance: 4928685843, 1800 m3HasPurchased: false, 1801 m0DeSoBalance: 5999999998, 1802 m1DeSoBalance: 4232379830, 1803 m2DeSoBalance: 5243756077, 1804 }, 1805 1806 // Have m1 sell the rest of their stake 1807 { 1808 // These are the transaction params 1809 UpdaterPublicKeyBase58Check: m1Pub, 1810 UpdaterPrivateKeyBase58Check: m1Priv, 1811 ProfilePublicKeyBase58Check: m3Pub, 1812 OperationType: CreatorCoinOperationTypeSell, 1813 DeSoToSellNanos: 0, 1814 CreatorCoinToSellNanos: 8685258418, 1815 DeSoToAddNanos: 0, 1816 MinDeSoExpectedNanos: 0, 1817 MinCreatorCoinExpectedNanos: 0, 1818 1819 // These are the expectations 1820 CoinsInCirculationNanos: 4928685843, 1821 DeSoLockedNanos: 119727407, 1822 CoinWatermarkNanos: 13613944261, 1823 m0CCBalance: 0, 1824 m0HasPurchased: false, 1825 m1CCBalance: 0, 1826 m1HasPurchased: false, 1827 m2CCBalance: 0, 1828 m2HasPurchased: false, 1829 m3CCBalance: 4928685843, 1830 m3HasPurchased: false, 1831 m0DeSoBalance: 5999999998, 1832 m1DeSoBalance: 6635615128, 1833 m2DeSoBalance: 5243756077, 1834 }, 1835 1836 // Have m3 sell all of their stake except leave 1 DeSo locked 1837 { 1838 // These are the transaction params 1839 UpdaterPublicKeyBase58Check: m3Pub, 1840 UpdaterPrivateKeyBase58Check: m3Priv, 1841 ProfilePublicKeyBase58Check: m3Pub, 1842 OperationType: CreatorCoinOperationTypeSell, 1843 DeSoToSellNanos: 0, 1844 CreatorCoinToSellNanos: 4925685829, 1845 DeSoToAddNanos: 0, 1846 MinDeSoExpectedNanos: 0, 1847 MinCreatorCoinExpectedNanos: 0, 1848 1849 // These are the expectations 1850 CoinsInCirculationNanos: 3000014, 1851 DeSoLockedNanos: 1, 1852 CoinWatermarkNanos: 13613944261, 1853 m0CCBalance: 0, 1854 m0HasPurchased: false, 1855 m1CCBalance: 0, 1856 m1HasPurchased: false, 1857 m2CCBalance: 0, 1858 m2HasPurchased: false, 1859 m3CCBalance: 3000014, 1860 m3HasPurchased: false, 1861 m0DeSoBalance: 5999999998, 1862 m1DeSoBalance: 6635615128, 1863 m2DeSoBalance: 5243756077, 1864 m3DeSoBalance: 6119715431, 1865 }, 1866 1867 { 1868 // Have m3 sell all of their remaining stake except for 1 CreatorCoin nano 1869 // This should trigger the CreatorCoinAutoSellThresholdNanos threshold leaving 1870 // m3 with no CreatorCoins. 1871 UpdaterPublicKeyBase58Check: m3Pub, 1872 UpdaterPrivateKeyBase58Check: m3Priv, 1873 ProfilePublicKeyBase58Check: m3Pub, 1874 OperationType: CreatorCoinOperationTypeSell, 1875 DeSoToSellNanos: 0, 1876 CreatorCoinToSellNanos: 3000013, 1877 DeSoToAddNanos: 0, 1878 MinDeSoExpectedNanos: 0, 1879 MinCreatorCoinExpectedNanos: 0, 1880 1881 // These are the expectations 1882 CoinsInCirculationNanos: 0, 1883 DeSoLockedNanos: 0, 1884 CoinWatermarkNanos: 13613944261, 1885 m0CCBalance: 0, 1886 m0HasPurchased: false, 1887 m1CCBalance: 0, 1888 m1HasPurchased: false, 1889 m2CCBalance: 0, 1890 m2HasPurchased: false, 1891 m3CCBalance: 0, 1892 m3HasPurchased: false, 1893 m0DeSoBalance: 5999999998, 1894 m1DeSoBalance: 6635615128, 1895 m2DeSoBalance: 5243756077, 1896 m3DeSoBalance: 6119715429, 1897 }, 1898 1899 // Have m1 buy a little more, m3 should receieve a founders reward 1900 { 1901 // These are the transaction params 1902 UpdaterPublicKeyBase58Check: m1Pub, 1903 UpdaterPrivateKeyBase58Check: m1Priv, 1904 ProfilePublicKeyBase58Check: m3Pub, 1905 OperationType: CreatorCoinOperationTypeBuy, 1906 DeSoToSellNanos: 2123456789, 1907 CreatorCoinToSellNanos: 0, 1908 DeSoToAddNanos: 0, 1909 MinDeSoExpectedNanos: 0, 1910 MinCreatorCoinExpectedNanos: 0, 1911 1912 // These are the expectations 1913 CoinsInCirculationNanos: 12852863707, 1914 DeSoLockedNanos: 2123244443, 1915 CoinWatermarkNanos: 13613944261, 1916 m0CCBalance: 0, 1917 m0HasPurchased: false, 1918 m1CCBalance: 9639647781, 1919 m1HasPurchased: true, 1920 m2CCBalance: 0, 1921 m2HasPurchased: false, 1922 m3CCBalance: 3213215926, 1923 m3HasPurchased: false, 1924 m0DeSoBalance: 5999999998, 1925 m1DeSoBalance: 4512158337, 1926 m2DeSoBalance: 5243756077, 1927 m3DeSoBalance: 6119715429, 1928 }, 1929 1930 // Have m1 sell their creator coins except CreatorCoinAutoSellThresholdNanos - 1. This should 1931 // cause an auto sell and m1 back to zero. 1932 { 1933 // These are the transaction params 1934 UpdaterPublicKeyBase58Check: m1Pub, 1935 UpdaterPrivateKeyBase58Check: m1Priv, 1936 ProfilePublicKeyBase58Check: m3Pub, 1937 OperationType: CreatorCoinOperationTypeSell, 1938 DeSoToSellNanos: 0, 1939 CreatorCoinToSellNanos: 9639647781 - DeSoMainnetParams.CreatorCoinAutoSellThresholdNanos + 1, 1940 DeSoToAddNanos: 0, 1941 MinDeSoExpectedNanos: 0, 1942 MinCreatorCoinExpectedNanos: 0, 1943 1944 // These are the expectations 1945 CoinsInCirculationNanos: 3213215926, 1946 DeSoLockedNanos: 33175681, 1947 CoinWatermarkNanos: 13613944261, 1948 m0CCBalance: 0, 1949 m0HasPurchased: false, 1950 m1CCBalance: 0, 1951 m1HasPurchased: false, 1952 m2CCBalance: 0, 1953 m2HasPurchased: false, 1954 m3CCBalance: 3213215926, 1955 m3HasPurchased: false, 1956 m0DeSoBalance: 5999999998, 1957 m1DeSoBalance: 6602018090, 1958 m2DeSoBalance: 5243756077, 1959 m3DeSoBalance: 6119715429, 1960 }, 1961 } 1962 1963 _helpTestCreatorCoinBuySell(t, creatorCoinTests, false) 1964 } 1965 1966 func TestSwapIdentityFailureCases(t *testing.T) { 1967 assert := assert.New(t) 1968 require := require.New(t) 1969 _, _ = assert, require 1970 1971 // Set up a blockchain 1972 chain, params, db := NewLowDifficultyBlockchain() 1973 mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/) 1974 feeRateNanosPerKB := uint64(11) 1975 _, _ = mempool, miner 1976 1977 // Send money to people from moneyPk 1978 _, _, _ = _doBasicTransferWithViewFlush( 1979 t, chain, db, params, moneyPkString, paramUpdaterPub, 1980 moneyPrivString, 6*NanosPerUnit /*amount to send*/, feeRateNanosPerKB /*feerate*/) 1981 _, _, _ = _doBasicTransferWithViewFlush( 1982 t, chain, db, params, moneyPkString, m0Pub, 1983 moneyPrivString, 6*NanosPerUnit /*amount to send*/, feeRateNanosPerKB /*feerate*/) 1984 _, _, _ = _doBasicTransferWithViewFlush( 1985 t, chain, db, params, moneyPkString, m1Pub, 1986 moneyPrivString, 6*NanosPerUnit /*amount to send*/, feeRateNanosPerKB /*feerate*/) 1987 _, _, _ = _doBasicTransferWithViewFlush( 1988 t, chain, db, params, moneyPkString, m2Pub, 1989 moneyPrivString, 6*NanosPerUnit /*amount to send*/, feeRateNanosPerKB /*feerate*/) 1990 1991 // Create a paramUpdater for this test 1992 params.ParamUpdaterPublicKeys[MakePkMapKey(paramUpdaterPkBytes)] = true 1993 1994 // Swapping identities with a key that is not paramUpdater should fail. 1995 _, _, _, err := _swapIdentity( 1996 t, chain, db, params, feeRateNanosPerKB, 1997 m0Pub, 1998 m0Priv, 1999 m1PkBytes, m2PkBytes) 2000 require.Error(err) 2001 require.Contains(err.Error(), RuleErrorSwapIdentityIsParamUpdaterOnly) 2002 2003 // Swapping identities with a key that is not paramUpdater should fail. 2004 // - Case where the transactor is the from public key 2005 _, _, _, err = _swapIdentity( 2006 t, chain, db, params, feeRateNanosPerKB, 2007 m0Pub, 2008 m0Priv, 2009 m0PkBytes, m2PkBytes) 2010 require.Error(err) 2011 require.Contains(err.Error(), RuleErrorSwapIdentityIsParamUpdaterOnly) 2012 2013 // Swapping identities with a key that is not paramUpdater should fail. 2014 // - Case where the transactor is the to public key 2015 _, _, _, err = _swapIdentity( 2016 t, chain, db, params, feeRateNanosPerKB, 2017 m0Pub, 2018 m0Priv, 2019 m2PkBytes, m0PkBytes) 2020 require.Error(err) 2021 require.Contains(err.Error(), RuleErrorSwapIdentityIsParamUpdaterOnly) 2022 } 2023 2024 func TestSwapIdentityMain(t *testing.T) { 2025 // Set up a blockchain 2026 assert := assert.New(t) 2027 require := require.New(t) 2028 _, _ = assert, require 2029 2030 creatorCoinTests := []*_CreatorCoinTestData{ 2031 // Create a profile for m0 so we can check creator coin balances easily. 2032 { 2033 TxnType: TxnTypeUpdateProfile, 2034 UpdaterPublicKeyBase58Check: m0Pub, 2035 UpdaterPrivateKeyBase58Check: m0Priv, 2036 ProfilePublicKeyBase58Check: m0Pub, 2037 ProfileUsername: "m0", 2038 ProfileDescription: "i am m0", 2039 ProfilePic: "m0 profile pic", 2040 ProfileCreatorBasisPoints: 2500, 2041 ProfileIsHidden: false, 2042 2043 SkipChecks: true, 2044 }, 2045 // Swap m1 and m2, which don't have profiles yet. This should work. 2046 { 2047 TxnType: TxnTypeSwapIdentity, 2048 FromPublicKey: m1PkBytes, 2049 ToPublicKey: m2PkBytes, 2050 // This is the creator whose coins we are testing the balances of. 2051 // Normally it's m0, but because we swapped m0 for m3 we should test 2052 // that one instead. 2053 ProfilePublicKeyBase58Check: m0Pub, 2054 2055 // These are the expectations 2056 CoinsInCirculationNanos: 0, 2057 DeSoLockedNanos: 0, 2058 CoinWatermarkNanos: 0, 2059 m0CCBalance: 0, 2060 m0HasPurchased: false, 2061 m1CCBalance: 0, 2062 m1HasPurchased: false, 2063 m2CCBalance: 0, 2064 m2HasPurchased: false, 2065 m3CCBalance: 0, 2066 m3HasPurchased: false, 2067 m4CCBalance: 0, 2068 m4HasPurchased: false, 2069 m5CCBalance: 0, 2070 m5HasPurchased: false, 2071 m6CCBalance: 0, 2072 m6HasPurchased: false, 2073 m0DeSoBalance: 5999999998, // m0 lost 2 nanos in fees when creating her profile 2074 m1DeSoBalance: 6000000000, 2075 m2DeSoBalance: 6000000000, 2076 m3DeSoBalance: 6000000000, 2077 m4DeSoBalance: 6000000000, 2078 m5DeSoBalance: 6000000000, 2079 m6DeSoBalance: 6000000000, 2080 2081 // Profiles should not exist for either of these keys 2082 ProfilesToCheckPublicKeysBase58Check: []string{m1Pub, m2Pub}, 2083 ProfilesToCheckUsernames: []string{"", ""}, 2084 }, 2085 // Create a profile for m3 2086 { 2087 TxnType: TxnTypeUpdateProfile, 2088 UpdaterPublicKeyBase58Check: m3Pub, 2089 UpdaterPrivateKeyBase58Check: m3Priv, 2090 ProfilePublicKeyBase58Check: m3Pub, 2091 ProfileUsername: "m3", 2092 ProfileDescription: "i am m3", 2093 ProfilePic: "m3 profile pic", 2094 ProfileCreatorBasisPoints: 2500, 2095 ProfileIsHidden: false, 2096 2097 // These are the expectations 2098 CoinsInCirculationNanos: 0, 2099 DeSoLockedNanos: 0, 2100 CoinWatermarkNanos: 0, 2101 m0CCBalance: 0, 2102 m0HasPurchased: false, 2103 m1CCBalance: 0, 2104 m1HasPurchased: false, 2105 m2CCBalance: 0, 2106 m2HasPurchased: false, 2107 m3CCBalance: 0, 2108 m3HasPurchased: false, 2109 m4CCBalance: 0, 2110 m4HasPurchased: false, 2111 m5CCBalance: 0, 2112 m5HasPurchased: false, 2113 m6CCBalance: 0, 2114 m6HasPurchased: false, 2115 m0DeSoBalance: 5999999998, 2116 m1DeSoBalance: 6000000000, 2117 m2DeSoBalance: 6000000000, 2118 m3DeSoBalance: 5999999998, 2119 m4DeSoBalance: 6000000000, 2120 m5DeSoBalance: 6000000000, 2121 m6DeSoBalance: 6000000000, 2122 2123 // Profile check 2124 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m3Pub}, 2125 ProfilesToCheckUsernames: []string{"m0", "m3"}, 2126 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3"}, 2127 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic"}, 2128 }, 2129 // Have m5 buy coins for m3 2130 { 2131 UpdaterPublicKeyBase58Check: m5Pub, 2132 UpdaterPrivateKeyBase58Check: m5Priv, 2133 ProfilePublicKeyBase58Check: m3Pub, 2134 OperationType: CreatorCoinOperationTypeBuy, 2135 DeSoToSellNanos: 1271123456, 2136 CreatorCoinToSellNanos: 0, 2137 DeSoToAddNanos: 0, 2138 MinDeSoExpectedNanos: 0, 2139 MinCreatorCoinExpectedNanos: 0, 2140 2141 // These are the expectations 2142 CoinsInCirculationNanos: 10832150315, 2143 DeSoLockedNanos: 1270996343, 2144 CoinWatermarkNanos: 10832150315, 2145 m0CCBalance: 0, 2146 m0HasPurchased: false, 2147 m1CCBalance: 0, 2148 m1HasPurchased: false, 2149 m2CCBalance: 0, 2150 m2HasPurchased: false, 2151 m3CCBalance: 2708037578, 2152 m3HasPurchased: false, 2153 m4CCBalance: 0, 2154 m4HasPurchased: false, 2155 m5CCBalance: 8124112737, 2156 m5HasPurchased: true, 2157 m6CCBalance: 0, 2158 m6HasPurchased: false, 2159 m0DeSoBalance: 5999999998, 2160 m1DeSoBalance: 6000000000, 2161 m2DeSoBalance: 6000000000, 2162 m3DeSoBalance: 5999999998, 2163 m4DeSoBalance: 6000000000, 2164 m5DeSoBalance: 4728876542, 2165 m6DeSoBalance: 6000000000, 2166 2167 // Profile check 2168 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m3Pub}, 2169 ProfilesToCheckUsernames: []string{"m0", "m3"}, 2170 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3"}, 2171 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic"}, 2172 }, 2173 // Swap m2 and m3. Everything should stay the same except m2 should be the 2174 // creator everyone owns a piece of 2175 { 2176 TxnType: TxnTypeSwapIdentity, 2177 FromPublicKey: m2PkBytes, 2178 ToPublicKey: m3PkBytes, 2179 // This is the creator whose coins we are testing the balances of. 2180 // Normally it's m0, but because we swapped m0 for m3 we should test 2181 // that one instead. 2182 ProfilePublicKeyBase58Check: m2Pub, 2183 2184 CoinsInCirculationNanos: 10832150315, 2185 DeSoLockedNanos: 1270996343, 2186 CoinWatermarkNanos: 10832150315, 2187 m0CCBalance: 0, 2188 m0HasPurchased: false, 2189 m1CCBalance: 0, 2190 m1HasPurchased: false, 2191 m2CCBalance: 2708037578, // The CC balance moves from m3 to m2 2192 m2HasPurchased: false, 2193 m3CCBalance: 0, 2194 m3HasPurchased: false, 2195 m4CCBalance: 0, 2196 m4HasPurchased: false, 2197 m5CCBalance: 8124112737, 2198 m5HasPurchased: true, 2199 m6CCBalance: 0, 2200 m6HasPurchased: false, 2201 m0DeSoBalance: 5999999998, 2202 m1DeSoBalance: 6000000000, 2203 m2DeSoBalance: 6000000000, 2204 m3DeSoBalance: 5999999998, 2205 m4DeSoBalance: 6000000000, 2206 m5DeSoBalance: 4728876542, 2207 m6DeSoBalance: 6000000000, 2208 2209 // Profile check. Note m2 is the public key that owns the profile now. 2210 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m2Pub, m3Pub}, 2211 ProfilesToCheckUsernames: []string{"m0", "m3", ""}, 2212 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", ""}, 2213 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", ""}, 2214 }, 2215 // Create a profile for m3 again. This should work, since m3 lost its 2216 // profile to m2 easrlier 2217 { 2218 TxnType: TxnTypeUpdateProfile, 2219 UpdaterPublicKeyBase58Check: m3Pub, 2220 UpdaterPrivateKeyBase58Check: m3Priv, 2221 ProfilePublicKeyBase58Check: m3Pub, 2222 ProfileUsername: "the_real_m3", 2223 ProfileDescription: "i am the real m3", 2224 ProfilePic: "the real m3 profile pic", 2225 ProfileCreatorBasisPoints: 2500, 2226 ProfileIsHidden: false, 2227 2228 // The CC balances are zero because we're checking against m3 2229 CoinsInCirculationNanos: 0, 2230 DeSoLockedNanos: 0, 2231 CoinWatermarkNanos: 0, 2232 m0CCBalance: 0, 2233 m0HasPurchased: false, 2234 m1CCBalance: 0, 2235 m1HasPurchased: false, 2236 m2CCBalance: 0, 2237 m2HasPurchased: false, 2238 m3CCBalance: 0, 2239 m3HasPurchased: false, 2240 m4CCBalance: 0, 2241 m4HasPurchased: false, 2242 m5CCBalance: 0, 2243 m5HasPurchased: false, 2244 m6CCBalance: 0, 2245 m6HasPurchased: false, 2246 m0DeSoBalance: 5999999998, 2247 m1DeSoBalance: 6000000000, 2248 m2DeSoBalance: 6000000000, 2249 m3DeSoBalance: 5999999996, 2250 m4DeSoBalance: 6000000000, 2251 m5DeSoBalance: 4728876542, 2252 m6DeSoBalance: 6000000000, 2253 2254 // Profile check 2255 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m2Pub, m3Pub}, 2256 ProfilesToCheckUsernames: []string{"m0", "m3", "the_real_m3"}, 2257 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", "i am the real m3"}, 2258 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", "the real m3 profile pic"}, 2259 }, 2260 // Have m6 buy some coins in m3 2261 { 2262 UpdaterPublicKeyBase58Check: m6Pub, 2263 UpdaterPrivateKeyBase58Check: m6Priv, 2264 ProfilePublicKeyBase58Check: m3Pub, 2265 OperationType: CreatorCoinOperationTypeBuy, 2266 DeSoToSellNanos: 2000000001, 2267 CreatorCoinToSellNanos: 0, 2268 DeSoToAddNanos: 0, 2269 MinDeSoExpectedNanos: 0, 2270 MinCreatorCoinExpectedNanos: 0, 2271 2272 // The CC balances are zero because we're checking against m3 2273 CoinsInCirculationNanos: 12598787739, 2274 DeSoLockedNanos: 1999800000, 2275 CoinWatermarkNanos: 12598787739, 2276 m0CCBalance: 0, 2277 m0HasPurchased: false, 2278 m1CCBalance: 0, 2279 m1HasPurchased: false, 2280 m2CCBalance: 0, 2281 m2HasPurchased: false, 2282 m3CCBalance: 3149696934, // m3 has some of its own coins as a founder reward 2283 m3HasPurchased: false, 2284 m4CCBalance: 0, 2285 m4HasPurchased: false, 2286 m5CCBalance: 0, 2287 m5HasPurchased: false, 2288 m6CCBalance: 9449090805, // m6 now owns some m3 2289 m6HasPurchased: true, 2290 m0DeSoBalance: 5999999998, 2291 m1DeSoBalance: 6000000000, 2292 m2DeSoBalance: 6000000000, 2293 m3DeSoBalance: 5999999996, 2294 m4DeSoBalance: 6000000000, 2295 m5DeSoBalance: 4728876542, 2296 m6DeSoBalance: 3999999997, 2297 2298 // Profile check 2299 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m2Pub, m3Pub}, 2300 ProfilesToCheckUsernames: []string{"m0", "m3", "the_real_m3"}, 2301 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", "i am the real m3"}, 2302 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", "the real m3 profile pic"}, 2303 }, 2304 // Have m6 buy a tiny amount of m2. 2305 // This should fail the AutoSellThreshold test if you set it to 2 DeSoToSellNanos 2306 { 2307 UpdaterPublicKeyBase58Check: m6Pub, 2308 UpdaterPrivateKeyBase58Check: m6Priv, 2309 ProfilePublicKeyBase58Check: m2Pub, 2310 OperationType: CreatorCoinOperationTypeBuy, 2311 DeSoToSellNanos: 10, 2312 CreatorCoinToSellNanos: 0, 2313 DeSoToAddNanos: 0, 2314 MinDeSoExpectedNanos: 0, 2315 MinCreatorCoinExpectedNanos: 0, 2316 2317 CoinsInCirculationNanos: 10832150340, 2318 DeSoLockedNanos: 1270996352, 2319 CoinWatermarkNanos: 10832150340, 2320 m0CCBalance: 0, 2321 m0HasPurchased: false, 2322 m1CCBalance: 0, 2323 m1HasPurchased: false, 2324 m2CCBalance: 2708037584, // The CC balance moves from m3 to m2 2325 m2HasPurchased: false, 2326 m3CCBalance: 0, 2327 m3HasPurchased: false, 2328 m4CCBalance: 0, 2329 m4HasPurchased: false, 2330 m5CCBalance: 8124112737, 2331 m5HasPurchased: true, 2332 m6CCBalance: 19, 2333 m6HasPurchased: true, 2334 m0DeSoBalance: 5999999998, 2335 m1DeSoBalance: 6000000000, 2336 m2DeSoBalance: 6000000000, 2337 m3DeSoBalance: 5999999996, 2338 m4DeSoBalance: 6000000000, 2339 m5DeSoBalance: 4728876542, 2340 m6DeSoBalance: 3999999985, 2341 2342 // Profile check 2343 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m2Pub, m3Pub}, 2344 ProfilesToCheckUsernames: []string{"m0", "m3", "the_real_m3"}, 2345 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", "i am the real m3"}, 2346 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", "the real m3 profile pic"}, 2347 }, 2348 // Swap m2 and m3 again. 2349 { 2350 TxnType: TxnTypeSwapIdentity, 2351 FromPublicKey: m2PkBytes, 2352 ToPublicKey: m3PkBytes, 2353 // This is the creator whose coins we are testing the balances of. 2354 // Normally it's m0, but because we swapped m0 for m3 we should test 2355 // that one instead. 2356 ProfilePublicKeyBase58Check: m2Pub, 2357 2358 CoinsInCirculationNanos: 12598787739, 2359 DeSoLockedNanos: 1999800000, 2360 CoinWatermarkNanos: 12598787739, 2361 // This was previously the m3 cap table, but now it's the m2 cap table. 2362 m0CCBalance: 0, 2363 m0HasPurchased: false, 2364 m1CCBalance: 0, 2365 m1HasPurchased: false, 2366 m2CCBalance: 3149696934, 2367 m2HasPurchased: false, 2368 m3CCBalance: 0, 2369 m3HasPurchased: false, 2370 m4CCBalance: 0, 2371 m4HasPurchased: false, 2372 m5CCBalance: 0, 2373 m5HasPurchased: false, 2374 m6CCBalance: 9449090805, // m6 now owns some m2 2375 m6HasPurchased: true, 2376 m0DeSoBalance: 5999999998, 2377 m1DeSoBalance: 6000000000, 2378 m2DeSoBalance: 6000000000, 2379 m3DeSoBalance: 5999999996, 2380 m4DeSoBalance: 6000000000, 2381 m5DeSoBalance: 4728876542, 2382 m6DeSoBalance: 3999999985, 2383 2384 // Profile check. Note m2 is the public key that owns the profile now. 2385 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m3Pub, m2Pub}, 2386 ProfilesToCheckUsernames: []string{"m0", "m3", "the_real_m3"}, 2387 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", "i am the real m3"}, 2388 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", "the real m3 profile pic"}, 2389 }, 2390 // Have m5 buy a small amount of m3 and check the cap table 2391 // If you set the DeSoToSellNanos to 2, this will also fail the autosell threshold. 2392 { 2393 UpdaterPublicKeyBase58Check: m5Pub, 2394 UpdaterPrivateKeyBase58Check: m5Priv, 2395 ProfilePublicKeyBase58Check: m3Pub, 2396 OperationType: CreatorCoinOperationTypeBuy, 2397 DeSoToSellNanos: 10, 2398 CreatorCoinToSellNanos: 0, 2399 DeSoToAddNanos: 0, 2400 MinDeSoExpectedNanos: 0, 2401 MinCreatorCoinExpectedNanos: 0, 2402 2403 CoinsInCirculationNanos: 10832150365, 2404 DeSoLockedNanos: 1270996361, 2405 CoinWatermarkNanos: 10832150365, 2406 m0CCBalance: 0, 2407 m0HasPurchased: false, 2408 m1CCBalance: 0, 2409 m1HasPurchased: false, 2410 m2CCBalance: 0, // The CC balance moves from m3 to m2 2411 m2HasPurchased: false, 2412 m3CCBalance: 2708037590, 2413 m3HasPurchased: false, 2414 m4CCBalance: 0, 2415 m4HasPurchased: false, 2416 m5CCBalance: 8124112756, 2417 m5HasPurchased: true, 2418 m6CCBalance: 19, 2419 m6HasPurchased: true, 2420 m0DeSoBalance: 5999999998, 2421 m1DeSoBalance: 6000000000, 2422 m2DeSoBalance: 6000000000, 2423 m3DeSoBalance: 5999999996, 2424 m4DeSoBalance: 6000000000, 2425 m5DeSoBalance: 4728876530, 2426 m6DeSoBalance: 3999999985, 2427 2428 // Profile check 2429 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m3Pub, m2Pub}, 2430 ProfilesToCheckUsernames: []string{"m0", "m3", "the_real_m3"}, 2431 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", "i am the real m3"}, 2432 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", "the real m3 profile pic"}, 2433 }, 2434 // Swap m3 for m4 and check the m4 cap table. It should be identical to 2435 // the m3 cap table from before. 2436 { 2437 TxnType: TxnTypeSwapIdentity, 2438 FromPublicKey: m3PkBytes, 2439 ToPublicKey: m4PkBytes, 2440 // This is the creator whose coins we are testing the balances of. 2441 // Normally it's m0, but because we swapped m0 for m3 we should test 2442 // that one instead. 2443 ProfilePublicKeyBase58Check: m4Pub, 2444 2445 CoinsInCirculationNanos: 10832150365, 2446 DeSoLockedNanos: 1270996361, 2447 CoinWatermarkNanos: 10832150365, 2448 m0CCBalance: 0, 2449 m0HasPurchased: false, 2450 m1CCBalance: 0, 2451 m1HasPurchased: false, 2452 m2CCBalance: 0, 2453 m2HasPurchased: false, 2454 m3CCBalance: 0, 2455 m3HasPurchased: false, 2456 m4CCBalance: 2708037590, // The m3 balance has moved to m4 now 2457 m4HasPurchased: false, 2458 m5CCBalance: 8124112756, 2459 m5HasPurchased: true, 2460 m6CCBalance: 19, 2461 m6HasPurchased: true, 2462 m0DeSoBalance: 5999999998, 2463 m1DeSoBalance: 6000000000, 2464 m2DeSoBalance: 6000000000, 2465 m3DeSoBalance: 5999999996, 2466 m4DeSoBalance: 6000000000, 2467 m5DeSoBalance: 4728876530, 2468 m6DeSoBalance: 3999999985, 2469 2470 // Profile check 2471 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m4Pub, m2Pub, m3Pub}, 2472 ProfilesToCheckUsernames: []string{"m0", "m3", "the_real_m3", ""}, 2473 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", "i am the real m3", ""}, 2474 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", "the real m3 profile pic", ""}, 2475 }, 2476 // Swap m2 for m3 and check the m3 cap table. It should be identical to 2477 // the m2 cap table from before. 2478 { 2479 TxnType: TxnTypeSwapIdentity, 2480 FromPublicKey: m2PkBytes, 2481 ToPublicKey: m3PkBytes, 2482 // This is the creator whose coins we are testing the balances of. 2483 // Normally it's m0, but because we swapped m0 for m3 we should test 2484 // that one instead. 2485 ProfilePublicKeyBase58Check: m3Pub, 2486 2487 CoinsInCirculationNanos: 12598787739, 2488 DeSoLockedNanos: 1999800000, 2489 CoinWatermarkNanos: 12598787739, 2490 // This was previously the m2 cap table, but now it's the m3 cap table. 2491 m0CCBalance: 0, 2492 m0HasPurchased: false, 2493 m1CCBalance: 0, 2494 m1HasPurchased: false, 2495 m2CCBalance: 0, 2496 m2HasPurchased: false, 2497 m3CCBalance: 3149696934, 2498 m3HasPurchased: false, 2499 m4CCBalance: 0, 2500 m4HasPurchased: false, 2501 m5CCBalance: 0, 2502 m6CCBalance: 9449090805, 2503 m6HasPurchased: true, 2504 m0DeSoBalance: 5999999998, 2505 m1DeSoBalance: 6000000000, 2506 m2DeSoBalance: 6000000000, 2507 m3DeSoBalance: 5999999996, 2508 m4DeSoBalance: 6000000000, 2509 m5DeSoBalance: 4728876530, 2510 m6DeSoBalance: 3999999985, 2511 2512 // Profile check 2513 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m4Pub, m3Pub, m2Pub}, 2514 ProfilesToCheckUsernames: []string{"m0", "m3", "the_real_m3", ""}, 2515 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", "i am the real m3", ""}, 2516 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", "the real m3 profile pic", ""}, 2517 }, 2518 // Swap m4 and m3 and check the m4 cap table. It should be identical to the above. 2519 { 2520 TxnType: TxnTypeSwapIdentity, 2521 FromPublicKey: m3PkBytes, 2522 ToPublicKey: m4PkBytes, 2523 ProfilePublicKeyBase58Check: m4Pub, 2524 2525 CoinsInCirculationNanos: 12598787739, 2526 DeSoLockedNanos: 1999800000, 2527 CoinWatermarkNanos: 12598787739, 2528 // This was previously the m2 cap table, but now it's the m3 cap table. 2529 m0CCBalance: 0, 2530 m0HasPurchased: false, 2531 m1CCBalance: 0, 2532 m1HasPurchased: false, 2533 m2CCBalance: 0, 2534 m2HasPurchased: false, 2535 m3CCBalance: 0, 2536 m3HasPurchased: false, 2537 m4CCBalance: 3149696934, 2538 m4HasPurchased: false, 2539 m5CCBalance: 0, 2540 m5HasPurchased: false, 2541 m6CCBalance: 9449090805, 2542 m6HasPurchased: true, 2543 m0DeSoBalance: 5999999998, 2544 m1DeSoBalance: 6000000000, 2545 m2DeSoBalance: 6000000000, 2546 m3DeSoBalance: 5999999996, 2547 m4DeSoBalance: 6000000000, 2548 m5DeSoBalance: 4728876530, 2549 m6DeSoBalance: 3999999985, 2550 2551 // Profile check 2552 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m3Pub, m4Pub, m2Pub}, 2553 ProfilesToCheckUsernames: []string{"m0", "m3", "the_real_m3", ""}, 2554 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", "i am the real m3", ""}, 2555 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", "the real m3 profile pic", ""}, 2556 }, 2557 // Do a small m3 buy and check that the cap table lines up. 2558 { 2559 UpdaterPublicKeyBase58Check: m5Pub, 2560 UpdaterPrivateKeyBase58Check: m5Priv, 2561 ProfilePublicKeyBase58Check: m3Pub, 2562 OperationType: CreatorCoinOperationTypeBuy, 2563 DeSoToSellNanos: 10, 2564 CreatorCoinToSellNanos: 0, 2565 DeSoToAddNanos: 0, 2566 MinDeSoExpectedNanos: 0, 2567 MinCreatorCoinExpectedNanos: 0, 2568 2569 CoinsInCirculationNanos: 10832150390, 2570 DeSoLockedNanos: 1270996370, 2571 CoinWatermarkNanos: 10832150390, 2572 m0CCBalance: 0, 2573 m0HasPurchased: false, 2574 m1CCBalance: 0, 2575 m1HasPurchased: false, 2576 m2CCBalance: 0, 2577 m2HasPurchased: false, 2578 m3CCBalance: 2708037596, 2579 m3HasPurchased: false, 2580 m4CCBalance: 0, 2581 m4HasPurchased: false, 2582 m5CCBalance: 8124112775, 2583 m5HasPurchased: true, 2584 m6CCBalance: 19, 2585 m6HasPurchased: true, 2586 m0DeSoBalance: 5999999998, 2587 m1DeSoBalance: 6000000000, 2588 m2DeSoBalance: 6000000000, 2589 m3DeSoBalance: 5999999996, 2590 m4DeSoBalance: 6000000000, 2591 m5DeSoBalance: 4728876518, 2592 m6DeSoBalance: 3999999985, 2593 2594 // Profile check 2595 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m3Pub, m4Pub, m2Pub}, 2596 ProfilesToCheckUsernames: []string{"m0", "m3", "the_real_m3", ""}, 2597 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", "i am the real m3", ""}, 2598 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", "the real m3 profile pic", ""}, 2599 }, 2600 // Create a profile for m2 2601 { 2602 TxnType: TxnTypeUpdateProfile, 2603 UpdaterPublicKeyBase58Check: m2Pub, 2604 UpdaterPrivateKeyBase58Check: m2Priv, 2605 ProfilePublicKeyBase58Check: m2Pub, 2606 ProfileUsername: "the_new_m2", 2607 ProfileDescription: "i am the new m2", 2608 ProfilePic: "the new m2 profile pic", 2609 ProfileCreatorBasisPoints: 2500, 2610 ProfileIsHidden: false, 2611 2612 // The CC balances are zero because we're checking against m3 2613 CoinsInCirculationNanos: 0, 2614 DeSoLockedNanos: 0, 2615 CoinWatermarkNanos: 0, 2616 m0CCBalance: 0, 2617 m0HasPurchased: false, 2618 m1CCBalance: 0, 2619 m1HasPurchased: false, 2620 m2CCBalance: 0, 2621 m2HasPurchased: false, 2622 m3CCBalance: 0, 2623 m3HasPurchased: false, 2624 m4CCBalance: 0, 2625 m4HasPurchased: false, 2626 m5CCBalance: 0, 2627 m5HasPurchased: false, 2628 m6CCBalance: 0, 2629 m6HasPurchased: false, 2630 m0DeSoBalance: 5999999998, 2631 m1DeSoBalance: 6000000000, 2632 m2DeSoBalance: 5999999998, 2633 m3DeSoBalance: 5999999996, 2634 m4DeSoBalance: 6000000000, 2635 m5DeSoBalance: 4728876518, 2636 m6DeSoBalance: 3999999985, 2637 2638 // Profile check 2639 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m3Pub, m4Pub, m2Pub}, 2640 ProfilesToCheckUsernames: []string{"m0", "m3", "the_real_m3", "the_new_m2"}, 2641 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", "i am the real m3", "i am the new m2"}, 2642 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", "the real m3 profile pic", "the new m2 profile pic"}, 2643 }, 2644 // Swap m2 and m4 and verify that m4 now has the zeros 2645 { 2646 TxnType: TxnTypeSwapIdentity, 2647 FromPublicKey: m2PkBytes, 2648 ToPublicKey: m4PkBytes, 2649 ProfilePublicKeyBase58Check: m4Pub, 2650 2651 CoinsInCirculationNanos: 0, 2652 DeSoLockedNanos: 0, 2653 CoinWatermarkNanos: 0, 2654 // This was previously the m2 cap table, but now it's the m3 cap table. 2655 m0CCBalance: 0, 2656 m0HasPurchased: false, 2657 m1CCBalance: 0, 2658 m1HasPurchased: false, 2659 m2CCBalance: 0, 2660 m2HasPurchased: false, 2661 m3CCBalance: 0, 2662 m3HasPurchased: false, 2663 m4CCBalance: 0, 2664 m4HasPurchased: false, 2665 m5CCBalance: 0, 2666 m5HasPurchased: false, 2667 m6CCBalance: 0, 2668 m6HasPurchased: false, 2669 m0DeSoBalance: 5999999998, 2670 m1DeSoBalance: 6000000000, 2671 m2DeSoBalance: 5999999998, 2672 m3DeSoBalance: 5999999996, 2673 m4DeSoBalance: 6000000000, 2674 m5DeSoBalance: 4728876518, 2675 m6DeSoBalance: 3999999985, 2676 2677 // Profile check 2678 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m3Pub, m2Pub, m4Pub}, 2679 ProfilesToCheckUsernames: []string{"m0", "m3", "the_real_m3", "the_new_m2"}, 2680 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", "i am the real m3", "i am the new m2"}, 2681 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", "the real m3 profile pic", "the new m2 profile pic"}, 2682 }, 2683 // Do a small m2 buy and make sure that the m4 cap table is what we have 2684 // Setting DeSoToSellNanos to zero will cause an RuleErrorCreatorCoinBuyMustSatisfyAutoSellThresholdNanos 2685 // rule exception. 2686 { 2687 UpdaterPublicKeyBase58Check: m5Pub, 2688 UpdaterPrivateKeyBase58Check: m5Priv, 2689 ProfilePublicKeyBase58Check: m2Pub, 2690 OperationType: CreatorCoinOperationTypeBuy, 2691 DeSoToSellNanos: 10, 2692 CreatorCoinToSellNanos: 0, 2693 DeSoToAddNanos: 0, 2694 MinDeSoExpectedNanos: 0, 2695 MinCreatorCoinExpectedNanos: 0, 2696 2697 CoinsInCirculationNanos: 12598787757, 2698 DeSoLockedNanos: 1999800009, 2699 CoinWatermarkNanos: 12598787757, 2700 m0CCBalance: 0, 2701 m0HasPurchased: false, 2702 m1CCBalance: 0, 2703 m1HasPurchased: false, 2704 m2CCBalance: 3149696938, 2705 m2HasPurchased: false, 2706 m3CCBalance: 0, 2707 m3HasPurchased: false, 2708 m4CCBalance: 0, 2709 m4HasPurchased: false, 2710 m5CCBalance: 14, 2711 m5HasPurchased: true, 2712 m6CCBalance: 9449090805, 2713 m6HasPurchased: true, 2714 m0DeSoBalance: 5999999998, 2715 m1DeSoBalance: 6000000000, 2716 m2DeSoBalance: 5999999998, 2717 m3DeSoBalance: 5999999996, 2718 m4DeSoBalance: 6000000000, 2719 m5DeSoBalance: 4728876506, 2720 m6DeSoBalance: 3999999985, 2721 2722 // Profile check 2723 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m3Pub, m2Pub, m4Pub}, 2724 ProfilesToCheckUsernames: []string{"m0", "m3", "the_real_m3", "the_new_m2"}, 2725 ProfilesToCheckDescriptions: []string{"i am m0", "i am m3", "i am the real m3", "i am the new m2"}, 2726 ProfilesToCheckProfilePic: []string{"m0 profile pic", "m3 profile pic", "the real m3 profile pic", "the new m2 profile pic"}, 2727 }, 2728 } 2729 2730 _helpTestCreatorCoinBuySell(t, creatorCoinTests, false) 2731 } 2732 2733 func TestSwapIdentityWithFollows(t *testing.T) { 2734 // Set up a blockchain 2735 assert := assert.New(t) 2736 require := require.New(t) 2737 _, _ = assert, require 2738 2739 creatorCoinTests := []*_CreatorCoinTestData{ 2740 // Create a profile for m1 2741 { 2742 TxnType: TxnTypeUpdateProfile, 2743 UpdaterPublicKeyBase58Check: m1Pub, 2744 UpdaterPrivateKeyBase58Check: m1Priv, 2745 ProfilePublicKeyBase58Check: m1Pub, 2746 ProfileUsername: "m1", 2747 ProfileDescription: "i am m1", 2748 ProfilePic: "m1 profile pic", 2749 ProfileCreatorBasisPoints: 2500, 2750 ProfileIsHidden: false, 2751 2752 SkipChecks: true, 2753 }, 2754 // Have m0 follow m1 2755 { 2756 TxnType: TxnTypeFollow, 2757 UpdaterPublicKeyBase58Check: m0Pub, 2758 UpdaterPrivateKeyBase58Check: m0Priv, 2759 FollowedPublicKey: m1PkBytes, 2760 IsUnfollow: false, 2761 ProfilePublicKeyBase58Check: m1Pub, 2762 2763 // Profile checks 2764 ProfilesToCheckPublicKeysBase58Check: []string{m1Pub, m0Pub}, 2765 ProfilesToCheckUsernames: []string{"m1", ""}, 2766 ProfilesToCheckDescriptions: []string{"i am m1", ""}, 2767 ProfilesToCheckProfilePic: []string{"m1 profile pic", ""}, 2768 2769 // These are our follow checks 2770 FollowPublicKeysToCheck: []string{m0Pub, m1Pub}, 2771 FollowPublicKeysUserIsFollowing: []map[string]bool{ 2772 {m1Pub: true}, 2773 {}, 2774 }, 2775 FollowPublicKeysFollowingThisUser: []map[string]bool{ 2776 {}, 2777 {m0Pub: true}, 2778 }, 2779 }, 2780 // Swap m0 and m1 2781 { 2782 TxnType: TxnTypeSwapIdentity, 2783 FromPublicKey: m0PkBytes, 2784 ToPublicKey: m1PkBytes, 2785 ProfilePublicKeyBase58Check: m0Pub, 2786 2787 // Profile checks 2788 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m1Pub}, 2789 ProfilesToCheckUsernames: []string{"m1", ""}, 2790 ProfilesToCheckDescriptions: []string{"i am m1", ""}, 2791 ProfilesToCheckProfilePic: []string{"m1 profile pic", ""}, 2792 2793 // Follow checks 2794 // The whole thing should be reversed now. Instead of m1 following 2795 // m0, it should be m0 following m1. 2796 FollowPublicKeysToCheck: []string{m0Pub, m1Pub}, 2797 FollowPublicKeysUserIsFollowing: []map[string]bool{ 2798 {}, 2799 {m0Pub: true}, 2800 }, 2801 FollowPublicKeysFollowingThisUser: []map[string]bool{ 2802 {m1Pub: true}, 2803 {}, 2804 }, 2805 }, 2806 // Swap m1 and m2. m2 should now be the one following m0 2807 { 2808 TxnType: TxnTypeSwapIdentity, 2809 FromPublicKey: m1PkBytes, 2810 ToPublicKey: m2PkBytes, 2811 ProfilePublicKeyBase58Check: m0Pub, 2812 2813 // Profile checks 2814 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m1Pub, m2Pub}, 2815 ProfilesToCheckUsernames: []string{"m1", "", ""}, 2816 ProfilesToCheckDescriptions: []string{"i am m1", "", ""}, 2817 ProfilesToCheckProfilePic: []string{"m1 profile pic", "", ""}, 2818 2819 // Follow checks 2820 // The whole thing should be reversed now. Instead of m1 following 2821 // m0, it should be m0 following m1. 2822 FollowPublicKeysToCheck: []string{m0Pub, m1Pub, m2Pub}, 2823 FollowPublicKeysUserIsFollowing: []map[string]bool{ 2824 {}, 2825 {}, 2826 {m0Pub: true}, 2827 }, 2828 FollowPublicKeysFollowingThisUser: []map[string]bool{ 2829 {m2Pub: true}, 2830 {}, 2831 {}, 2832 }, 2833 }, 2834 // Give m1 a new profile 2835 { 2836 TxnType: TxnTypeUpdateProfile, 2837 UpdaterPublicKeyBase58Check: m1Pub, 2838 UpdaterPrivateKeyBase58Check: m1Priv, 2839 ProfilePublicKeyBase58Check: m1Pub, 2840 ProfileUsername: "new_m1", 2841 ProfileDescription: "i am new m1", 2842 ProfilePic: "new m1 profile pic", 2843 ProfileCreatorBasisPoints: 2500, 2844 ProfileIsHidden: false, 2845 2846 // Profile checks 2847 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m1Pub, m2Pub}, 2848 ProfilesToCheckUsernames: []string{"m1", "new_m1", ""}, 2849 ProfilesToCheckDescriptions: []string{"i am m1", "i am new m1", ""}, 2850 ProfilesToCheckProfilePic: []string{"m1 profile pic", "new m1 profile pic", ""}, 2851 }, 2852 // Have m2 follow m1 2853 { 2854 TxnType: TxnTypeFollow, 2855 UpdaterPublicKeyBase58Check: m2Pub, 2856 UpdaterPrivateKeyBase58Check: m2Priv, 2857 FollowedPublicKey: m1PkBytes, 2858 IsUnfollow: false, 2859 ProfilePublicKeyBase58Check: m1Pub, 2860 2861 // Profile checks 2862 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m1Pub, m2Pub}, 2863 ProfilesToCheckUsernames: []string{"m1", "new_m1", ""}, 2864 ProfilesToCheckDescriptions: []string{"i am m1", "i am new m1", ""}, 2865 ProfilesToCheckProfilePic: []string{"m1 profile pic", "new m1 profile pic", ""}, 2866 2867 // Follow checks 2868 // The whole thing should be reversed now. Instead of m1 following 2869 // m0, it should be m0 following m1. 2870 FollowPublicKeysToCheck: []string{m0Pub, m1Pub, m2Pub}, 2871 FollowPublicKeysUserIsFollowing: []map[string]bool{ 2872 {}, 2873 {}, 2874 {m0Pub: true, m1Pub: true}, 2875 }, 2876 FollowPublicKeysFollowingThisUser: []map[string]bool{ 2877 {m2Pub: true}, 2878 {m2Pub: true}, 2879 {}, 2880 }, 2881 }, 2882 // Have m0 follow m1 2883 { 2884 TxnType: TxnTypeFollow, 2885 UpdaterPublicKeyBase58Check: m0Pub, 2886 UpdaterPrivateKeyBase58Check: m0Priv, 2887 FollowedPublicKey: m1PkBytes, 2888 IsUnfollow: false, 2889 ProfilePublicKeyBase58Check: m1Pub, 2890 2891 // Profile checks 2892 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m1Pub, m2Pub}, 2893 ProfilesToCheckUsernames: []string{"m1", "new_m1", ""}, 2894 ProfilesToCheckDescriptions: []string{"i am m1", "i am new m1", ""}, 2895 ProfilesToCheckProfilePic: []string{"m1 profile pic", "new m1 profile pic", ""}, 2896 2897 // Follow checks 2898 // The whole thing should be reversed now. Instead of m1 following 2899 // m0, it should be m0 following m1. 2900 FollowPublicKeysToCheck: []string{m0Pub, m1Pub, m2Pub}, 2901 FollowPublicKeysUserIsFollowing: []map[string]bool{ 2902 {m1Pub: true}, 2903 {}, 2904 {m0Pub: true, m1Pub: true}, 2905 }, 2906 FollowPublicKeysFollowingThisUser: []map[string]bool{ 2907 {m2Pub: true}, 2908 {m2Pub: true, m0Pub: true}, 2909 {}, 2910 }, 2911 }, 2912 // Have m1 follow m0 2913 { 2914 TxnType: TxnTypeFollow, 2915 UpdaterPublicKeyBase58Check: m1Pub, 2916 UpdaterPrivateKeyBase58Check: m1Priv, 2917 FollowedPublicKey: m0PkBytes, 2918 IsUnfollow: false, 2919 ProfilePublicKeyBase58Check: m1Pub, 2920 2921 // Profile checks 2922 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m1Pub, m2Pub}, 2923 ProfilesToCheckUsernames: []string{"m1", "new_m1", ""}, 2924 ProfilesToCheckDescriptions: []string{"i am m1", "i am new m1", ""}, 2925 ProfilesToCheckProfilePic: []string{"m1 profile pic", "new m1 profile pic", ""}, 2926 2927 // Follow checks 2928 // The whole thing should be reversed now. Instead of m1 following 2929 // m0, it should be m0 following m1. 2930 FollowPublicKeysToCheck: []string{m0Pub, m1Pub, m2Pub}, 2931 FollowPublicKeysUserIsFollowing: []map[string]bool{ 2932 {m1Pub: true}, 2933 {m0Pub: true}, 2934 {m0Pub: true, m1Pub: true}, 2935 }, 2936 FollowPublicKeysFollowingThisUser: []map[string]bool{ 2937 {m2Pub: true, m1Pub: true}, 2938 {m2Pub: true, m0Pub: true}, 2939 {}, 2940 }, 2941 }, 2942 // Swap m1 and m2. m2 should now inherit m1's follows 2943 // This is a tricky one... 2944 { 2945 TxnType: TxnTypeSwapIdentity, 2946 FromPublicKey: m1PkBytes, 2947 ToPublicKey: m2PkBytes, 2948 ProfilePublicKeyBase58Check: m0Pub, 2949 2950 // Profile checks 2951 ProfilesToCheckPublicKeysBase58Check: []string{m0Pub, m2Pub, m1Pub}, 2952 ProfilesToCheckUsernames: []string{"m1", "new_m1", ""}, 2953 ProfilesToCheckDescriptions: []string{"i am m1", "i am new m1", ""}, 2954 ProfilesToCheckProfilePic: []string{"m1 profile pic", "new m1 profile pic", ""}, 2955 2956 // Follow checks 2957 // The whole thing should be reversed now. Instead of m1 following 2958 // m0, it should be m0 following m1. 2959 FollowPublicKeysToCheck: []string{m0Pub, m1Pub, m2Pub}, 2960 FollowPublicKeysUserIsFollowing: []map[string]bool{ 2961 {m2Pub: true}, 2962 {m0Pub: true, m2Pub: true}, 2963 {m0Pub: true}, 2964 }, 2965 FollowPublicKeysFollowingThisUser: []map[string]bool{ 2966 {m1Pub: true, m2Pub: true}, 2967 {}, 2968 {m1Pub: true, m0Pub: true}, 2969 }, 2970 }, 2971 // Swap m2 and m0. 2972 { 2973 TxnType: TxnTypeSwapIdentity, 2974 FromPublicKey: m2PkBytes, 2975 ToPublicKey: m0PkBytes, 2976 ProfilePublicKeyBase58Check: m0Pub, 2977 2978 // Profile checks 2979 ProfilesToCheckPublicKeysBase58Check: []string{m2Pub, m0Pub, m1Pub}, 2980 ProfilesToCheckUsernames: []string{"m1", "new_m1", ""}, 2981 ProfilesToCheckDescriptions: []string{"i am m1", "i am new m1", ""}, 2982 ProfilesToCheckProfilePic: []string{"m1 profile pic", "new m1 profile pic", ""}, 2983 2984 // Follow checks 2985 // The whole thing should be reversed now. Instead of m1 following 2986 // m0, it should be m0 following m1. 2987 FollowPublicKeysToCheck: []string{m2Pub, m1Pub, m0Pub}, 2988 FollowPublicKeysUserIsFollowing: []map[string]bool{ 2989 {m0Pub: true}, 2990 {m2Pub: true, m0Pub: true}, 2991 {m2Pub: true}, 2992 }, 2993 FollowPublicKeysFollowingThisUser: []map[string]bool{ 2994 {m1Pub: true, m0Pub: true}, 2995 {}, 2996 {m1Pub: true, m2Pub: true}, 2997 }, 2998 }, 2999 } 3000 3001 _helpTestCreatorCoinBuySell(t, creatorCoinTests, false) 3002 } 3003 3004 func TestUpdateProfileChangeBack(t *testing.T) { 3005 assert := assert.New(t) 3006 require := require.New(t) 3007 _ = assert 3008 _ = require 3009 3010 // This test fails non-deterministically so we wrap it in a loop to make it 3011 // not flake. 3012 for ii := 0; ii < 10; ii++ { 3013 chain, params, db := NewLowDifficultyBlockchain() 3014 mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/) 3015 // Make m3 a paramUpdater for this test 3016 params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true 3017 3018 // Mine a few blocks to give the senderPkString some money. 3019 _, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 3020 require.NoError(err) 3021 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 3022 require.NoError(err) 3023 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 3024 require.NoError(err) 3025 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 3026 require.NoError(err) 3027 3028 _, _, _ = _doBasicTransferWithViewFlush( 3029 t, chain, db, params, moneyPkString, m0Pub, 3030 moneyPrivString, 10000 /*amount to send*/, 11 /*feerate*/) 3031 _, _, _ = _doBasicTransferWithViewFlush( 3032 t, chain, db, params, moneyPkString, m1Pub, 3033 moneyPrivString, 10000 /*amount to send*/, 11 /*feerate*/) 3034 _, _, _ = _doBasicTransferWithViewFlush( 3035 t, chain, db, params, moneyPkString, m2Pub, 3036 moneyPrivString, 10000 /*amount to send*/, 11 /*feerate*/) 3037 _, _, _ = _doBasicTransferWithViewFlush( 3038 t, chain, db, params, moneyPkString, m3Pub, 3039 moneyPrivString, 10000 /*amount to send*/, 11 /*feerate*/) 3040 3041 // m0 takes m0 3042 { 3043 txn, _, _, _, err := chain.CreateUpdateProfileTxn( 3044 m0PkBytes, 3045 m0PkBytes, 3046 "m0", 3047 "", 3048 "", 3049 0, 3050 20000, 3051 false, 3052 0, 3053 100, 3054 mempool, /*mempool*/ 3055 []*DeSoOutput{}) 3056 require.NoError(err) 3057 3058 // Sign the transaction now that its inputs are set up. 3059 _signTxn(t, txn, m0Priv) 3060 require.NoError(err) 3061 mempoolTxsAdded, err := mempool.processTransaction( 3062 txn, true /*allowUnconnectedTxn*/, false /*rateLimit*/, 0, /*peerID*/ 3063 true /*verifySignatures*/) 3064 require.NoError(err) 3065 require.Equal(1, len(mempoolTxsAdded)) 3066 } 3067 { 3068 txn, _, _, _, err := chain.CreateUpdateProfileTxn( 3069 m1PkBytes, 3070 m1PkBytes, 3071 "m1", 3072 "", 3073 "", 3074 0, 3075 20000, 3076 false, 3077 0, 3078 100, 3079 mempool, /*mempool*/ 3080 []*DeSoOutput{}) 3081 require.NoError(err) 3082 3083 // Sign the transaction now that its inputs are set up. 3084 _signTxn(t, txn, m1Priv) 3085 require.NoError(err) 3086 mempoolTxsAdded, err := mempool.processTransaction( 3087 txn, true /*allowUnconnectedTxn*/, false /*rateLimit*/, 0, /*peerID*/ 3088 true /*verifySignatures*/) 3089 require.NoError(err) 3090 require.Equal(1, len(mempoolTxsAdded)) 3091 } 3092 // Write to db 3093 block, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 3094 require.NoError(err) 3095 // one for the block reward, two for the new profiles 3096 require.Equal(1+2, len(block.Txns)) 3097 3098 // m1 takes m2 3099 { 3100 txn, _, _, _, err := chain.CreateUpdateProfileTxn( 3101 m1PkBytes, 3102 m1PkBytes, 3103 "m2", 3104 "", 3105 "", 3106 0, 3107 20000, 3108 false, 3109 0, 3110 100, 3111 mempool, /*mempool*/ 3112 []*DeSoOutput{}) 3113 require.NoError(err) 3114 3115 // Sign the transaction now that its inputs are set up. 3116 _signTxn(t, txn, m1Priv) 3117 require.NoError(err) 3118 mempoolTxsAdded, err := mempool.processTransaction( 3119 txn, true /*allowUnconnectedTxn*/, false /*rateLimit*/, 0, /*peerID*/ 3120 true /*verifySignatures*/) 3121 require.NoError(err) 3122 require.Equal(1, len(mempoolTxsAdded)) 3123 } 3124 // m0 takes m1 3125 { 3126 txn, _, _, _, err := chain.CreateUpdateProfileTxn( 3127 m0PkBytes, 3128 m0PkBytes, 3129 "m1", 3130 "", 3131 "", 3132 0, 3133 20000, 3134 false, 3135 0, 3136 100, 3137 mempool, /*mempool*/ 3138 []*DeSoOutput{}) 3139 require.NoError(err) 3140 3141 // Sign the transaction now that its inputs are set up. 3142 _signTxn(t, txn, m0Priv) 3143 require.NoError(err) 3144 3145 // This ensure that the read-only version of the utxoView accurately reflects the current set of profile names taken. 3146 utxoViewCopy, err := mempool.universalUtxoView.CopyUtxoView() 3147 require.NoError(err) 3148 txnSize := getTxnSize(*txn) 3149 _, _, _, _, err = utxoViewCopy.ConnectTransaction(txn, txn.Hash(), txnSize, chain.blockTip().Height+1, false, false) 3150 require.NoError(err) 3151 3152 mempoolTxsAdded, err := mempool.processTransaction( 3153 txn, true /*allowUnconnectedTxn*/, false /*rateLimit*/, 0, /*peerID*/ 3154 true /*verifySignatures*/) 3155 require.NoError(err) 3156 require.Equal(1, len(mempoolTxsAdded)) 3157 } 3158 // m1 takes m0 3159 { 3160 txn, _, _, _, err := chain.CreateUpdateProfileTxn( 3161 m1PkBytes, 3162 m1PkBytes, 3163 "m0", 3164 "", 3165 "", 3166 0, 3167 20000, 3168 false, 3169 0, 3170 100, 3171 mempool, /*mempool*/ 3172 []*DeSoOutput{}) 3173 require.NoError(err) 3174 3175 // Sign the transaction now that its inputs are set up. 3176 _signTxn(t, txn, m1Priv) 3177 require.NoError(err) 3178 mempoolTxsAdded, err := mempool.processTransaction( 3179 txn, true /*allowUnconnectedTxn*/, false /*rateLimit*/, 0, /*peerID*/ 3180 true /*verifySignatures*/) 3181 require.NoError(err) 3182 require.Equal(1, len(mempoolTxsAdded)) 3183 } 3184 // Write to db 3185 block, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 3186 require.NoError(err) 3187 // one for the block reward, three for the new txns 3188 require.Equal(1+3, len(block.Txns)) 3189 3190 // m2 takes m0 should fail 3191 { 3192 txn, _, _, _, err := chain.CreateUpdateProfileTxn( 3193 m2PkBytes, 3194 m2PkBytes, 3195 "m0", 3196 "", 3197 "", 3198 0, 3199 20000, 3200 false, 3201 0, 3202 100, 3203 mempool, /*mempool*/ 3204 []*DeSoOutput{}) 3205 require.NoError(err) 3206 3207 // Sign the transaction now that its inputs are set up. 3208 _signTxn(t, txn, m2Priv) 3209 require.NoError(err) 3210 mempoolTxsAdded, err := mempool.processTransaction( 3211 txn, true /*allowUnconnectedTxn*/, false /*rateLimit*/, 0, /*peerID*/ 3212 true /*verifySignatures*/) 3213 require.Error(err) 3214 require.Equal(0, len(mempoolTxsAdded)) 3215 } 3216 // m3 takes m0 should fail 3217 { 3218 txn, _, _, _, err := chain.CreateUpdateProfileTxn( 3219 m3PkBytes, 3220 m3PkBytes, 3221 "m0", 3222 "", 3223 "", 3224 0, 3225 20000, 3226 false, 3227 0, 3228 100, 3229 mempool, /*mempool*/ 3230 []*DeSoOutput{}) 3231 require.NoError(err) 3232 3233 // Sign the transaction now that its inputs are set up. 3234 _signTxn(t, txn, m3Priv) 3235 require.NoError(err) 3236 mempoolTxsAdded, err := mempool.processTransaction( 3237 txn, true /*allowUnconnectedTxn*/, false /*rateLimit*/, 0, /*peerID*/ 3238 true /*verifySignatures*/) 3239 require.Error(err) 3240 require.Equal(0, len(mempoolTxsAdded)) 3241 } 3242 } 3243 } 3244 3245 func TestAuthorizeDerivedKeyBasic(t *testing.T) { 3246 NFTTransferOrBurnAndDerivedKeysBlockHeight = uint32(0) 3247 3248 assert := assert.New(t) 3249 require := require.New(t) 3250 _ = assert 3251 _ = require 3252 3253 chain, params, db := NewLowDifficultyBlockchain() 3254 mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/) 3255 3256 // Mine two blocks to give the sender some DeSo. 3257 _, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 3258 require.NoError(err) 3259 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 3260 require.NoError(err) 3261 3262 senderPkBytes, _, err := Base58CheckDecode(senderPkString) 3263 require.NoError(err) 3264 senderPrivBytes, _, err := Base58CheckDecode(senderPrivString) 3265 require.NoError(err) 3266 recipientPkBytes, _, err := Base58CheckDecode(recipientPkString) 3267 require.NoError(err) 3268 3269 // Get AuthorizeDerivedKey txn metadata with expiration at block 6 3270 senderPriv, _ := btcec.PrivKeyFromBytes(btcec.S256(), senderPrivBytes) 3271 authTxnMeta, derivedPriv := _getAuthorizeDerivedKeyMetadata(t, senderPriv, params, 6, false) 3272 derivedPrivBase58Check := Base58CheckEncode(derivedPriv.Serialize(), true, params) 3273 derivedPkBytes := derivedPriv.PubKey().SerializeCompressed() 3274 fmt.Println("Derived public key:", hex.EncodeToString(derivedPkBytes)) 3275 3276 // We create this inline function for attempting a basic transfer. 3277 // This helps us test that the DeSoChain recognizes a derived key. 3278 _basicTransfer := func(senderPk []byte, recipientPk []byte, signerPriv string, utxoView *UtxoView, 3279 mempool *DeSoMempool, isSignerSender bool) ([]*UtxoOperation, *MsgDeSoTxn, error) { 3280 3281 txn := &MsgDeSoTxn{ 3282 // The inputs will be set below. 3283 TxInputs: []*DeSoInput{}, 3284 TxOutputs: []*DeSoOutput{ 3285 { 3286 PublicKey: recipientPk, 3287 AmountNanos: 1, 3288 }, 3289 }, 3290 PublicKey: senderPk, 3291 TxnMeta: &BasicTransferMetadata{}, 3292 ExtraData: make(map[string][]byte), 3293 } 3294 3295 totalInput, spendAmount, changeAmount, fees, err := 3296 chain.AddInputsAndChangeToTransaction(txn, 10, mempool) 3297 require.NoError(err) 3298 require.Equal(totalInput, spendAmount+changeAmount+fees) 3299 require.Greater(totalInput, uint64(0)) 3300 3301 if isSignerSender { 3302 // Sign the transaction with the provided derived key 3303 _signTxn(t, txn, signerPriv) 3304 } else { 3305 // Sign the transaction with the provided derived key 3306 _signTxnWithDerivedKey(t, txn, signerPriv) 3307 } 3308 3309 // Get utxoView if it doesn't exist 3310 if mempool != nil { 3311 utxoView, err = mempool.GetAugmentedUniversalView() 3312 require.NoError(err) 3313 } 3314 if utxoView == nil { 3315 utxoView, err = NewUtxoView(db, params, nil) 3316 require.NoError(err) 3317 } 3318 3319 txHash := txn.Hash() 3320 blockHeight := chain.blockTip().Height + 1 3321 utxoOps, _, _, _, err := 3322 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, 3323 true /*verifySignature*/, false /*ignoreUtxos*/) 3324 return utxoOps, txn, err 3325 } 3326 3327 // Verify that the balance and expiration block in the db match expectation. 3328 _verifyTest := func(derivedPublicKey []byte, expirationBlockExpected uint64, 3329 balanceExpected uint64, operationTypeExpected AuthorizeDerivedKeyOperationType, mempool *DeSoMempool) { 3330 // Verify that expiration block was persisted in the db or is in mempool utxoView 3331 if mempool == nil { 3332 derivedKeyEntry := DBGetOwnerToDerivedKeyMapping(db, *NewPublicKey(senderPkBytes), *NewPublicKey(derivedPublicKey)) 3333 // If we removed the derivedKeyEntry from utxoView altogether, it'll be nil. 3334 // To pass the tests, we initialize it to a default struct. 3335 if derivedKeyEntry == nil { 3336 derivedKeyEntry = &DerivedKeyEntry{*NewPublicKey(senderPkBytes), *NewPublicKey(derivedPublicKey), 0, AuthorizeDerivedKeyOperationValid, false} 3337 } 3338 assert.Equal(derivedKeyEntry.ExpirationBlock, expirationBlockExpected) 3339 assert.Equal(derivedKeyEntry.OperationType, operationTypeExpected) 3340 } else { 3341 utxoView, err := mempool.GetAugmentedUniversalView() 3342 require.NoError(err) 3343 derivedKeyEntry := utxoView._getDerivedKeyMappingForOwner(senderPkBytes, derivedPublicKey) 3344 // If we removed the derivedKeyEntry from utxoView altogether, it'll be nil. 3345 // To pass the tests, we initialize it to a default struct. 3346 if derivedKeyEntry == nil { 3347 derivedKeyEntry = &DerivedKeyEntry{*NewPublicKey(senderPkBytes), *NewPublicKey(derivedPublicKey), 0, AuthorizeDerivedKeyOperationValid, false} 3348 } 3349 assert.Equal(derivedKeyEntry.ExpirationBlock, expirationBlockExpected) 3350 assert.Equal(derivedKeyEntry.OperationType, operationTypeExpected) 3351 } 3352 3353 // Verify that the balance of recipient is equal to expected balance 3354 assert.Equal(_getBalance(t, chain, mempool, recipientPkString), balanceExpected) 3355 } 3356 3357 // We will use these to keep track of added utxo ops and txns 3358 testUtxoOps := [][]*UtxoOperation{} 3359 testTxns := []*MsgDeSoTxn{} 3360 3361 // Just for the sake of consistency, we run the _basicTransfer on unauthorized 3362 // derived key. It should fail since blockchain hasn't seen this key yet. 3363 { 3364 utxoView, err := NewUtxoView(db, params, nil) 3365 require.NoError(err) 3366 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 3367 derivedPrivBase58Check, utxoView, nil, false) 3368 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 3369 3370 _verifyTest(authTxnMeta.DerivedPublicKey, 0, 0, AuthorizeDerivedKeyOperationValid, nil) 3371 fmt.Println("Failed basic transfer signed with unauthorized derived key") 3372 } 3373 // Attempt sending an AuthorizeDerivedKey txn signed with an invalid private key. 3374 // This must fail because the txn has to be signed either by owner or derived key. 3375 { 3376 utxoView, err := NewUtxoView(db, params, nil) 3377 require.NoError(err) 3378 randomPrivateKey, err := btcec.NewPrivateKey(btcec.S256()) 3379 require.NoError(err) 3380 randomPrivBase58Check := Base58CheckEncode(randomPrivateKey.Serialize(), true, params) 3381 _, _, _, err = _doAuthorizeTxn( 3382 t, 3383 chain, 3384 db, 3385 params, 3386 utxoView, 3387 10, 3388 senderPkBytes, 3389 authTxnMeta.DerivedPublicKey, 3390 randomPrivBase58Check, 3391 authTxnMeta.ExpirationBlock, 3392 authTxnMeta.AccessSignature, 3393 false) 3394 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 3395 3396 _verifyTest(authTxnMeta.DerivedPublicKey, 0, 0, AuthorizeDerivedKeyOperationValid, nil) 3397 fmt.Println("Failed connecting AuthorizeDerivedKey txn signed with an unauthorized private key.") 3398 } 3399 // Attempt sending an AuthorizeDerivedKey txn where access signature is signed with 3400 // an invalid private key. This must fail. 3401 { 3402 utxoView, err := NewUtxoView(db, params, nil) 3403 require.NoError(err) 3404 randomPrivateKey, err := btcec.NewPrivateKey(btcec.S256()) 3405 require.NoError(err) 3406 expirationBlockByte := UintToBuf(authTxnMeta.ExpirationBlock) 3407 accessBytes := append(authTxnMeta.DerivedPublicKey, expirationBlockByte[:]...) 3408 accessSignatureRandom, err := randomPrivateKey.Sign(Sha256DoubleHash(accessBytes)[:]) 3409 require.NoError(err) 3410 _, _, _, err = _doAuthorizeTxn( 3411 t, 3412 chain, 3413 db, 3414 params, 3415 utxoView, 3416 10, 3417 senderPkBytes, 3418 authTxnMeta.DerivedPublicKey, 3419 derivedPrivBase58Check, 3420 authTxnMeta.ExpirationBlock, 3421 accessSignatureRandom.Serialize(), 3422 false) 3423 require.Error(err) 3424 3425 _verifyTest(authTxnMeta.DerivedPublicKey, 0, 0, AuthorizeDerivedKeyOperationValid, nil) 3426 fmt.Println("Failed connecting AuthorizeDerivedKey txn signed with an invalid access signature.") 3427 } 3428 // Check basic transfer signed with still unauthorized derived key. 3429 // Should fail. 3430 { 3431 utxoView, err := NewUtxoView(db, params, nil) 3432 require.NoError(err) 3433 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 3434 derivedPrivBase58Check, utxoView, nil, false) 3435 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 3436 3437 _verifyTest(authTxnMeta.DerivedPublicKey, 0, 0, AuthorizeDerivedKeyOperationValid, nil) 3438 fmt.Println("Failed basic transfer signed with unauthorized derived key") 3439 } 3440 // Now attempt to send the same transaction but signed with the correct derived key. 3441 // This must pass. The new derived key will be flushed to the db here. 3442 { 3443 utxoView, err := NewUtxoView(db, params, nil) 3444 require.NoError(err) 3445 utxoOps, txn, _, err := _doAuthorizeTxn( 3446 t, 3447 chain, 3448 db, 3449 params, 3450 utxoView, 3451 10, 3452 senderPkBytes, 3453 authTxnMeta.DerivedPublicKey, 3454 derivedPrivBase58Check, 3455 authTxnMeta.ExpirationBlock, 3456 authTxnMeta.AccessSignature, 3457 false) 3458 require.NoError(err) 3459 require.NoError(utxoView.FlushToDb()) 3460 3461 testUtxoOps = append(testUtxoOps, utxoOps) 3462 testTxns = append(testTxns, txn) 3463 3464 // Verify that expiration block was persisted in the db 3465 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 0, AuthorizeDerivedKeyOperationValid, nil) 3466 fmt.Println("Passed connecting AuthorizeDerivedKey txn signed with an authorized private key. Flushed to Db.") 3467 } 3468 // Check basic transfer signed by the owner key. 3469 // Should succeed. Flush to db. 3470 { 3471 utxoView, err := NewUtxoView(db, params, nil) 3472 require.NoError(err) 3473 utxoOps, txn, err := _basicTransfer(senderPkBytes, recipientPkBytes, 3474 senderPrivString, utxoView, nil, true) 3475 require.NoError(err) 3476 require.NoError(utxoView.FlushToDb()) 3477 testUtxoOps = append(testUtxoOps, utxoOps) 3478 testTxns = append(testTxns, txn) 3479 3480 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 1, AuthorizeDerivedKeyOperationValid, nil) 3481 fmt.Println("Passed basic transfer signed with owner key. Flushed to Db.") 3482 } 3483 // Check basic transfer signed with now authorized derived key. 3484 // Should succeed. Flush to db. 3485 { 3486 utxoView, err := NewUtxoView(db, params, nil) 3487 require.NoError(err) 3488 utxoOps, txn, err := _basicTransfer(senderPkBytes, recipientPkBytes, 3489 derivedPrivBase58Check, utxoView, nil, false) 3490 require.NoError(err) 3491 require.NoError(utxoView.FlushToDb()) 3492 testUtxoOps = append(testUtxoOps, utxoOps) 3493 testTxns = append(testTxns, txn) 3494 3495 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 2, AuthorizeDerivedKeyOperationValid, nil) 3496 fmt.Println("Passed basic transfer signed with authorized derived key. Flushed to Db.") 3497 } 3498 // Check basic transfer signed with a random key. 3499 // Should fail. Well... theoretically, it could pass in a distant future. 3500 { 3501 // Generate a random key pair 3502 randomPrivateKey, err := btcec.NewPrivateKey(btcec.S256()) 3503 require.NoError(err) 3504 randomPrivBase58Check := Base58CheckEncode(randomPrivateKey.Serialize(), true, params) 3505 utxoView, err := NewUtxoView(db, params, nil) 3506 require.NoError(err) 3507 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 3508 randomPrivBase58Check, utxoView, nil, false) 3509 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 3510 3511 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 2, AuthorizeDerivedKeyOperationValid, nil) 3512 fmt.Println("Fail basic transfer signed with random key.") 3513 } 3514 // Try disconnecting all transactions so that key is deauthorized. 3515 // Should succeed. 3516 { 3517 for iterIndex := range testTxns { 3518 testIndex := len(testTxns) - 1 - iterIndex 3519 currentTxn := testTxns[testIndex] 3520 currentUtxoOps := testUtxoOps[testIndex] 3521 fmt.Println("currentTxn.String()", currentTxn.String()) 3522 3523 // Disconnect the transaction 3524 utxoView, err := NewUtxoView(db, params, nil) 3525 require.NoError(err) 3526 blockHeight := chain.blockTip().Height + 1 3527 fmt.Printf("Disconnecting test index: %v\n", testIndex) 3528 require.NoError(utxoView.DisconnectTransaction( 3529 currentTxn, currentTxn.Hash(), currentUtxoOps, blockHeight)) 3530 fmt.Printf("Disconnected test index: %v\n", testIndex) 3531 3532 require.NoErrorf(utxoView.FlushToDb(), "SimpleDisconnect: Index: %v", testIndex) 3533 } 3534 3535 _verifyTest(authTxnMeta.DerivedPublicKey, 0, 0, AuthorizeDerivedKeyOperationValid, nil) 3536 fmt.Println("Passed disconnecting all txns. Flushed to Db.") 3537 } 3538 // After disconnecting, check basic transfer signed with unauthorized derived key. 3539 // Should fail. 3540 { 3541 utxoView, err := NewUtxoView(db, params, nil) 3542 require.NoError(err) 3543 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 3544 derivedPrivBase58Check, utxoView, nil, false) 3545 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 3546 3547 _verifyTest(authTxnMeta.DerivedPublicKey, 0, 0, AuthorizeDerivedKeyOperationValid, nil) 3548 fmt.Println("Failed basic transfer signed with unauthorized derived key after disconnecting") 3549 } 3550 // Connect all txns to a single UtxoView flushing only at the end. 3551 { 3552 // Create a new UtxoView 3553 utxoView, err := NewUtxoView(db, params, nil) 3554 require.NoError(err) 3555 for testIndex, txn := range testTxns { 3556 fmt.Printf("Applying test index: %v\n", testIndex) 3557 blockHeight := chain.blockTip().Height + 1 3558 txnSize := getTxnSize(*txn) 3559 _, _, _, _, err := 3560 utxoView.ConnectTransaction( 3561 txn, txn.Hash(), txnSize, blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 3562 require.NoError(err) 3563 } 3564 3565 // Now flush at the end. 3566 require.NoError(utxoView.FlushToDb()) 3567 3568 // Verify that expiration block and balance was persisted in the db 3569 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 2, AuthorizeDerivedKeyOperationValid, nil) 3570 fmt.Println("Passed re-connecting all txn to a single utxoView") 3571 } 3572 // Check basic transfer signed with a random key. 3573 // Should fail. 3574 { 3575 // Generate a random key pair 3576 randomPrivateKey, err := btcec.NewPrivateKey(btcec.S256()) 3577 require.NoError(err) 3578 randomPrivBase58Check := Base58CheckEncode(randomPrivateKey.Serialize(), true, params) 3579 utxoView, err := NewUtxoView(db, params, nil) 3580 require.NoError(err) 3581 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 3582 randomPrivBase58Check, utxoView, nil, false) 3583 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 3584 3585 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 2, AuthorizeDerivedKeyOperationValid, nil) 3586 fmt.Println("Fail basic transfer signed with random key.") 3587 } 3588 // Disconnect all txns on a single UtxoView flushing only at the end 3589 { 3590 // Create a new UtxoView 3591 utxoView, err := NewUtxoView(db, params, nil) 3592 require.NoError(err) 3593 for iterIndex := range testTxns { 3594 testIndex := len(testTxns) - 1 - iterIndex 3595 blockHeight := chain.blockTip().Height + 1 3596 fmt.Printf("Disconnecting test index: %v\n", testIndex) 3597 txn := testTxns[testIndex] 3598 require.NoError(utxoView.DisconnectTransaction( 3599 txn, txn.Hash(), testUtxoOps[testIndex], blockHeight)) 3600 } 3601 3602 // Now flush at the end. 3603 require.NoError(utxoView.FlushToDb()) 3604 3605 // Verify that expiration block and balance was persisted in the db 3606 _verifyTest(authTxnMeta.DerivedPublicKey, 0, 0, AuthorizeDerivedKeyOperationValid, nil) 3607 fmt.Println("Passed disconnecting all txn on a single utxoView") 3608 } 3609 // Connect transactions to a single mempool, should pass. 3610 { 3611 for ii, currentTxn := range testTxns { 3612 mempoolTxsAdded, err := mempool.processTransaction( 3613 currentTxn, true /*allowUnconnectedTxn*/, true /*rateLimit*/, 0, /*peerID*/ 3614 true /*verifySignatures*/) 3615 require.NoErrorf(err, "mempool index %v", ii) 3616 require.Equal(1, len(mempoolTxsAdded)) 3617 } 3618 3619 // This will check the expiration block and balances according to the mempool augmented utxoView. 3620 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 2, AuthorizeDerivedKeyOperationValid, mempool) 3621 fmt.Println("Passed connecting all txn to the mempool") 3622 } 3623 // Check basic transfer signed with a random key, when passing mempool. 3624 // Should fail. 3625 { 3626 // Generate a random key pair 3627 randomPrivateKey, err := btcec.NewPrivateKey(btcec.S256()) 3628 require.NoError(err) 3629 randomPrivBase58Check := Base58CheckEncode(randomPrivateKey.Serialize(), true, params) 3630 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 3631 randomPrivBase58Check, nil, mempool, false) 3632 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 3633 3634 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 2, AuthorizeDerivedKeyOperationValid, mempool) 3635 fmt.Println("Fail basic transfer signed with random key with mempool.") 3636 } 3637 // Remove all the transactions from the mempool. Should pass. 3638 { 3639 for _, burnTxn := range testTxns { 3640 mempool.inefficientRemoveTransaction(burnTxn) 3641 } 3642 // This will check the expiration block and balances according to the mempool augmented utxoView. 3643 _verifyTest(authTxnMeta.DerivedPublicKey, 0, 0, AuthorizeDerivedKeyOperationValid, mempool) 3644 fmt.Println("Passed removing all txn from the mempool.") 3645 } 3646 // After disconnecting, check basic transfer signed with unauthorized derived key. 3647 // Should fail. 3648 { 3649 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 3650 derivedPrivBase58Check, nil, mempool, false) 3651 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 3652 3653 _verifyTest(authTxnMeta.DerivedPublicKey, 0, 0, AuthorizeDerivedKeyOperationValid, mempool) 3654 fmt.Println("Failed basic transfer signed with unauthorized derived key after disconnecting") 3655 } 3656 // Re-connect transactions to a single mempool, should pass. 3657 { 3658 for ii, currentTxn := range testTxns { 3659 mempoolTxsAdded, err := mempool.processTransaction( 3660 currentTxn, true /*allowUnconnectedTxn*/, true /*rateLimit*/, 0, /*peerID*/ 3661 true /*verifySignatures*/) 3662 require.NoErrorf(err, "mempool index %v", ii) 3663 require.Equal(1, len(mempoolTxsAdded)) 3664 } 3665 3666 // This will check the expiration block and balances according to the mempool augmented utxoView. 3667 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 2, AuthorizeDerivedKeyOperationValid, mempool) 3668 fmt.Println("Passed connecting all txn to the mempool.") 3669 } 3670 // We will be adding some blocks so we define an array to keep track of them. 3671 testBlocks := []*MsgDeSoBlock{} 3672 // Mine a block with all the mempool transactions. 3673 { 3674 // All the txns should be in the mempool already so mining a block should put 3675 // all those transactions in it. 3676 addedBlock, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 3677 require.NoError(err) 3678 testBlocks = append(testBlocks, addedBlock) 3679 } 3680 // Reset testUtxoOps and testTxns so we can test more transactions 3681 testUtxoOps = [][]*UtxoOperation{} 3682 testTxns = []*MsgDeSoTxn{} 3683 // Check basic transfer signed by the owner key. 3684 // Should succeed. Flush to db. 3685 { 3686 utxoView, err := NewUtxoView(db, params, nil) 3687 require.NoError(err) 3688 utxoOps, txn, err := _basicTransfer(senderPkBytes, recipientPkBytes, 3689 senderPrivString, utxoView, nil, true) 3690 require.NoError(err) 3691 require.NoError(utxoView.FlushToDb()) 3692 testUtxoOps = append(testUtxoOps, utxoOps) 3693 testTxns = append(testTxns, txn) 3694 3695 fmt.Println("Passed basic transfer signed with owner key. Flushed to Db.") 3696 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 3, AuthorizeDerivedKeyOperationValid, nil) 3697 } 3698 // Check basic transfer signed with authorized derived key. Now the auth txn is persisted in the db. 3699 // Should succeed. Flush to db. 3700 { 3701 utxoView, err := NewUtxoView(db, params, nil) 3702 require.NoError(err) 3703 utxoOps, txn, err := _basicTransfer(senderPkBytes, recipientPkBytes, 3704 derivedPrivBase58Check, utxoView, nil, false) 3705 require.NoError(err) 3706 require.NoError(utxoView.FlushToDb()) 3707 testUtxoOps = append(testUtxoOps, utxoOps) 3708 testTxns = append(testTxns, txn) 3709 3710 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 4, AuthorizeDerivedKeyOperationValid, nil) 3711 fmt.Println("Passed basic transfer signed with authorized derived key. Flushed to Db.") 3712 } 3713 // Check basic transfer signed with a random key. 3714 // Should fail. 3715 { 3716 // Generate a random key pair 3717 randomPrivateKey, err := btcec.NewPrivateKey(btcec.S256()) 3718 require.NoError(err) 3719 randomPrivBase58Check := Base58CheckEncode(randomPrivateKey.Serialize(), true, params) 3720 utxoView, err := NewUtxoView(db, params, nil) 3721 require.NoError(err) 3722 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 3723 randomPrivBase58Check, utxoView, nil, false) 3724 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 3725 3726 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 4, AuthorizeDerivedKeyOperationValid, nil) 3727 fmt.Println("Fail basic transfer signed with random key.") 3728 } 3729 // Try disconnecting all transactions. Should succeed. 3730 { 3731 for iterIndex := range testTxns { 3732 testIndex := len(testTxns) - 1 - iterIndex 3733 currentTxn := testTxns[testIndex] 3734 currentUtxoOps := testUtxoOps[testIndex] 3735 fmt.Println("currentTxn.String()", currentTxn.String()) 3736 3737 // Disconnect the transaction 3738 utxoView, err := NewUtxoView(db, params, nil) 3739 require.NoError(err) 3740 blockHeight := chain.blockTip().Height + 1 3741 fmt.Printf("Disconnecting test index: %v\n", testIndex) 3742 require.NoError(utxoView.DisconnectTransaction( 3743 currentTxn, currentTxn.Hash(), currentUtxoOps, blockHeight)) 3744 fmt.Printf("Disconnected test index: %v\n", testIndex) 3745 3746 require.NoErrorf(utxoView.FlushToDb(), "SimpleDisconnect: Index: %v", testIndex) 3747 } 3748 3749 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 2, AuthorizeDerivedKeyOperationValid, nil) 3750 fmt.Println("Passed disconnecting all txns. Flushed to Db.") 3751 } 3752 // Mine a few more blocks so that the authorization should expire 3753 { 3754 for i := uint64(chain.blockTip().Height); i < authTxnMeta.ExpirationBlock; i++ { 3755 addedBlock, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 3756 require.NoError(err) 3757 testBlocks = append(testBlocks, addedBlock) 3758 } 3759 fmt.Println("Added a few more blocks.") 3760 } 3761 // Check basic transfer signed by the owner key. 3762 // Should succeed. 3763 { 3764 utxoView, err := NewUtxoView(db, params, nil) 3765 require.NoError(err) 3766 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 3767 senderPrivString, utxoView, nil, true) 3768 require.NoError(err) 3769 3770 // We're not persisting in the db so balance should remain at 2. 3771 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 2, AuthorizeDerivedKeyOperationValid, nil) 3772 fmt.Println("Passed basic transfer signed with owner key.") 3773 } 3774 // Check basic transfer signed with expired authorized derived key. 3775 // Should fail. 3776 { 3777 utxoView, err := NewUtxoView(db, params, nil) 3778 require.NoError(err) 3779 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 3780 derivedPrivBase58Check, utxoView, nil, false) 3781 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 3782 3783 _verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 2, AuthorizeDerivedKeyOperationValid, nil) 3784 fmt.Println("Failed a txn signed with an expired derived key.") 3785 } 3786 3787 // Reset testUtxoOps and testTxns so we can test more transactions 3788 testUtxoOps = [][]*UtxoOperation{} 3789 testTxns = []*MsgDeSoTxn{} 3790 // Get another AuthorizeDerivedKey txn metadata with expiration at block 10 3791 // We will try to de-authorize this key with a txn before it expires. 3792 authTxnMetaDeAuth, derivedDeAuthPriv := _getAuthorizeDerivedKeyMetadata(t, senderPriv, params, 10, false) 3793 derivedPrivDeAuthBase58Check := Base58CheckEncode(derivedDeAuthPriv.Serialize(), true, params) 3794 derivedDeAuthPkBytes := derivedDeAuthPriv.PubKey().SerializeCompressed() 3795 fmt.Println("Derived public key:", hex.EncodeToString(derivedDeAuthPkBytes)) 3796 // Send an authorize transaction signed with the correct derived key. 3797 // This must pass. 3798 { 3799 utxoView, err := NewUtxoView(db, params, nil) 3800 require.NoError(err) 3801 utxoOps, txn, _, err := _doAuthorizeTxn( 3802 t, 3803 chain, 3804 db, 3805 params, 3806 utxoView, 3807 10, 3808 senderPkBytes, 3809 authTxnMetaDeAuth.DerivedPublicKey, 3810 derivedPrivDeAuthBase58Check, 3811 authTxnMetaDeAuth.ExpirationBlock, 3812 authTxnMetaDeAuth.AccessSignature, 3813 false) 3814 require.NoError(err) 3815 testUtxoOps = append(testUtxoOps, utxoOps) 3816 testTxns = append(testTxns, txn) 3817 3818 // Verify that expiration block was persisted in the db 3819 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, 0, 2, AuthorizeDerivedKeyOperationValid, nil) 3820 fmt.Println("Passed connecting AuthorizeDerivedKey txn signed with an authorized private key.") 3821 } 3822 // Re-connect transactions to a single mempool, should pass. 3823 { 3824 for ii, currentTxn := range testTxns { 3825 mempoolTxsAdded, err := mempool.processTransaction( 3826 currentTxn, true /*allowUnconnectedTxn*/, true /*rateLimit*/, 0, /*peerID*/ 3827 true /*verifySignatures*/) 3828 require.NoErrorf(err, "mempool index %v", ii) 3829 require.Equal(1, len(mempoolTxsAdded)) 3830 } 3831 3832 // This will check the expiration block and balances according to the mempool augmented utxoView. 3833 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 2, AuthorizeDerivedKeyOperationValid, mempool) 3834 fmt.Println("Passed connecting all txn to the mempool.") 3835 } 3836 // Mine a block so that mempool gets flushed to db 3837 { 3838 addedBlock, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 3839 require.NoError(err) 3840 testBlocks = append(testBlocks, addedBlock) 3841 fmt.Println("Added a block.") 3842 } 3843 // Reset testUtxoOps and testTxns so we can test more transactions 3844 testUtxoOps = [][]*UtxoOperation{} 3845 testTxns = []*MsgDeSoTxn{} 3846 // Check basic transfer signed with new authorized derived key. 3847 // Sanity check. Should pass. We're not flushing to the db yet. 3848 { 3849 utxoView, err := NewUtxoView(db, params, nil) 3850 require.NoError(err) 3851 utxoOps, txn, err := _basicTransfer(senderPkBytes, recipientPkBytes, 3852 derivedPrivDeAuthBase58Check, utxoView, nil, false) 3853 require.NoError(err) 3854 require.NoError(utxoView.FlushToDb()) 3855 testUtxoOps = append(testUtxoOps, utxoOps) 3856 testTxns = append(testTxns, txn) 3857 3858 // We're persisting to the db so balance should change to 3. 3859 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 3, AuthorizeDerivedKeyOperationValid, nil) 3860 fmt.Println("Passed basic transfer signed with derived key.") 3861 } 3862 // Send a de-authorize transaction signed with a derived key. 3863 // Doesn't matter if it's signed by the owner or not, once a isDeleted 3864 // txn appears, the key should be forever expired. This must pass. 3865 { 3866 utxoView, err := NewUtxoView(db, params, nil) 3867 require.NoError(err) 3868 utxoOps, txn, _, err := _doAuthorizeTxn( 3869 t, 3870 chain, 3871 db, 3872 params, 3873 utxoView, 3874 10, 3875 senderPkBytes, 3876 authTxnMetaDeAuth.DerivedPublicKey, 3877 derivedPrivDeAuthBase58Check, 3878 10, 3879 authTxnMetaDeAuth.AccessSignature, 3880 true) 3881 require.NoError(err) 3882 require.NoError(utxoView.FlushToDb()) 3883 testUtxoOps = append(testUtxoOps, utxoOps) 3884 testTxns = append(testTxns, txn) 3885 // Verify the expiration block in the db 3886 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 3, AuthorizeDerivedKeyOperationNotValid, nil) 3887 fmt.Println("Passed connecting AuthorizeDerivedKey txn with isDeleted signed with an authorized private key.") 3888 } 3889 // Check basic transfer signed with new authorized derived key. 3890 // Now that key has been de-authorized this must fail. 3891 { 3892 utxoView, err := NewUtxoView(db, params, nil) 3893 require.NoError(err) 3894 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 3895 derivedPrivDeAuthBase58Check, utxoView, nil, false) 3896 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 3897 3898 // Since this should fail, balance wouldn't change. 3899 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 3, AuthorizeDerivedKeyOperationNotValid, nil) 3900 fmt.Println("Failed basic transfer signed with de-authorized derived key.") 3901 } 3902 // Sanity check basic transfer signed by the owner key. 3903 // Should succeed. 3904 { 3905 utxoView, err := NewUtxoView(db, params, nil) 3906 require.NoError(err) 3907 utxoOps, txn, err := _basicTransfer(senderPkBytes, recipientPkBytes, 3908 senderPrivString, utxoView, nil, true) 3909 require.NoError(err) 3910 require.NoError(utxoView.FlushToDb()) 3911 testUtxoOps = append(testUtxoOps, utxoOps) 3912 testTxns = append(testTxns, txn) 3913 3914 // Balance should change to 4 3915 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 4, AuthorizeDerivedKeyOperationNotValid, nil) 3916 fmt.Println("Passed basic transfer signed with owner key.") 3917 } 3918 // Send an authorize transaction signed with a derived key. 3919 // Since we've already deleted this derived key, this must fail. 3920 { 3921 utxoView, err := NewUtxoView(db, params, nil) 3922 require.NoError(err) 3923 _, _, _, err = _doAuthorizeTxn( 3924 t, 3925 chain, 3926 db, 3927 params, 3928 utxoView, 3929 10, 3930 senderPkBytes, 3931 authTxnMetaDeAuth.DerivedPublicKey, 3932 derivedPrivDeAuthBase58Check, 3933 10, 3934 authTxnMetaDeAuth.AccessSignature, 3935 false) 3936 require.Contains(err.Error(), RuleErrorAuthorizeDerivedKeyDeletedDerivedPublicKey) 3937 3938 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 4, AuthorizeDerivedKeyOperationNotValid, nil) 3939 fmt.Println("Failed connecting AuthorizeDerivedKey txn with de-authorized private key.") 3940 } 3941 // Try disconnecting all transactions. Should succeed. 3942 { 3943 for iterIndex := range testTxns { 3944 testIndex := len(testTxns) - 1 - iterIndex 3945 currentTxn := testTxns[testIndex] 3946 currentUtxoOps := testUtxoOps[testIndex] 3947 fmt.Println("currentTxn.String()", currentTxn.String()) 3948 3949 // Disconnect the transaction 3950 utxoView, err := NewUtxoView(db, params, nil) 3951 require.NoError(err) 3952 blockHeight := chain.blockTip().Height + 1 3953 fmt.Printf("Disconnecting test index: %v\n", testIndex) 3954 require.NoError(utxoView.DisconnectTransaction( 3955 currentTxn, currentTxn.Hash(), currentUtxoOps, blockHeight)) 3956 fmt.Printf("Disconnected test index: %v\n", testIndex) 3957 3958 require.NoErrorf(utxoView.FlushToDb(), "SimpleDisconnect: Index: %v", testIndex) 3959 } 3960 3961 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 2, AuthorizeDerivedKeyOperationValid, nil) 3962 fmt.Println("Passed disconnecting all txns. Flushed to Db.") 3963 } 3964 // Connect transactions to a single mempool, should pass. 3965 { 3966 for ii, currentTxn := range testTxns { 3967 mempoolTxsAdded, err := mempool.processTransaction( 3968 currentTxn, true /*allowUnconnectedTxn*/, true /*rateLimit*/, 0, /*peerID*/ 3969 true /*verifySignatures*/) 3970 require.NoErrorf(err, "mempool index %v", ii) 3971 require.Equal(1, len(mempoolTxsAdded)) 3972 } 3973 3974 // This will check the expiration block and balances according to the mempool augmented utxoView. 3975 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 4, AuthorizeDerivedKeyOperationNotValid, mempool) 3976 fmt.Println("Passed connecting all txn to the mempool") 3977 } 3978 // Check adding basic transfer to mempool signed with new authorized derived key. 3979 // Now that key has been de-authorized this must fail. 3980 { 3981 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 3982 derivedPrivDeAuthBase58Check, nil, mempool, false) 3983 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 3984 3985 // Since this should fail, balance wouldn't change. 3986 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 4, AuthorizeDerivedKeyOperationNotValid, mempool) 3987 fmt.Println("Failed basic transfer signed with de-authorized derived key in mempool.") 3988 } 3989 // Attempt re-authorizing a previously de-authorized derived key. 3990 // Since we've already deleted this derived key, this must fail. 3991 { 3992 utxoView, err := mempool.GetAugmentedUniversalView() 3993 require.NoError(err) 3994 _, _, _, err = _doAuthorizeTxn( 3995 t, 3996 chain, 3997 db, 3998 params, 3999 utxoView, 4000 10, 4001 senderPkBytes, 4002 authTxnMetaDeAuth.DerivedPublicKey, 4003 derivedPrivDeAuthBase58Check, 4004 10, 4005 authTxnMetaDeAuth.AccessSignature, 4006 false) 4007 require.Contains(err.Error(), RuleErrorAuthorizeDerivedKeyDeletedDerivedPublicKey) 4008 4009 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 4, AuthorizeDerivedKeyOperationNotValid, mempool) 4010 fmt.Println("Failed connecting AuthorizeDerivedKey txn with de-authorized private key.") 4011 } 4012 // Mine a block so that mempool gets flushed to db 4013 { 4014 addedBlock, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 4015 require.NoError(err) 4016 testBlocks = append(testBlocks, addedBlock) 4017 fmt.Println("Added a block.") 4018 } 4019 // Check adding basic transfer signed with new authorized derived key. 4020 // Now that key has been de-authorized this must fail. 4021 { 4022 utxoView, err := NewUtxoView(db, params, nil) 4023 require.NoError(err) 4024 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 4025 derivedPrivDeAuthBase58Check, utxoView, nil, false) 4026 require.Contains(err.Error(), RuleErrorDerivedKeyNotAuthorized) 4027 4028 // Since this should fail, balance wouldn't change. 4029 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 4, AuthorizeDerivedKeyOperationNotValid, nil) 4030 fmt.Println("Failed basic transfer signed with de-authorized derived key.") 4031 } 4032 // Attempt re-authorizing a previously de-authorized derived key. 4033 // Since we've already deleted this derived key, this must fail. 4034 { 4035 utxoView, err := NewUtxoView(db, params, nil) 4036 require.NoError(err) 4037 _, _, _, err = _doAuthorizeTxn( 4038 t, 4039 chain, 4040 db, 4041 params, 4042 utxoView, 4043 10, 4044 senderPkBytes, 4045 authTxnMetaDeAuth.DerivedPublicKey, 4046 derivedPrivDeAuthBase58Check, 4047 10, 4048 authTxnMetaDeAuth.AccessSignature, 4049 false) 4050 require.Contains(err.Error(), RuleErrorAuthorizeDerivedKeyDeletedDerivedPublicKey) 4051 4052 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 4, AuthorizeDerivedKeyOperationNotValid, nil) 4053 fmt.Println("Failed connecting AuthorizeDerivedKey txn with de-authorized private key.") 4054 } 4055 // Sanity check basic transfer signed by the owner key. 4056 // Should succeed. 4057 { 4058 utxoView, err := NewUtxoView(db, params, nil) 4059 require.NoError(err) 4060 _, _, err = _basicTransfer(senderPkBytes, recipientPkBytes, 4061 senderPrivString, utxoView, nil, true) 4062 require.NoError(err) 4063 4064 // Balance should change to 4 4065 _verifyTest(authTxnMetaDeAuth.DerivedPublicKey, authTxnMetaDeAuth.ExpirationBlock, 4, AuthorizeDerivedKeyOperationNotValid, nil) 4066 fmt.Println("Passed basic transfer signed with owner key.") 4067 } 4068 // Roll back the blocks and make sure we don't hit any errors. 4069 disconnectSingleBlock := func(blockToDisconnect *MsgDeSoBlock, utxoView *UtxoView) { 4070 // Fetch the utxo operations for the block we're detaching. We need these 4071 // in order to be able to detach the block. 4072 hash, err := blockToDisconnect.Header.Hash() 4073 require.NoError(err) 4074 utxoOps, err := GetUtxoOperationsForBlock(db, hash) 4075 require.NoError(err) 4076 4077 // Compute the hashes for all the transactions. 4078 txHashes, err := ComputeTransactionHashes(blockToDisconnect.Txns) 4079 require.NoError(err) 4080 require.NoError(utxoView.DisconnectBlock(blockToDisconnect, txHashes, utxoOps)) 4081 } 4082 { 4083 utxoView, err := NewUtxoView(db, params, nil) 4084 require.NoError(err) 4085 4086 for iterIndex := range testBlocks { 4087 testIndex := len(testBlocks) - 1 - iterIndex 4088 testBlock := testBlocks[testIndex] 4089 disconnectSingleBlock(testBlock, utxoView) 4090 } 4091 4092 // Flushing the view after applying and rolling back should work. 4093 require.NoError(utxoView.FlushToDb()) 4094 fmt.Println("Successfully rolled back the blocks.") 4095 } 4096 4097 // After we rolled back the blocks, db should reset 4098 _verifyTest(authTxnMeta.DerivedPublicKey, 0, 0, AuthorizeDerivedKeyOperationValid, nil) 4099 fmt.Println("Successfuly run TestAuthorizeDerivedKeyBasic()") 4100 }