github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/blockchain/transaction_verify.go (about) 1 // Copyright 2017-2018 DERO Project. All rights reserved. 2 // Use of this source code in any form is governed by RESEARCH license. 3 // license can be found in the LICENSE file. 4 // GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8 5 // 6 // 7 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 8 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 9 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 10 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 11 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 12 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 13 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 14 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 15 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 17 package blockchain 18 19 //import "fmt" 20 import "time" 21 22 /*import "bytes" 23 import "encoding/binary" 24 25 import "github.com/romana/rlog" 26 27 */ 28 29 import "sync" 30 import "runtime/debug" 31 32 import "github.com/romana/rlog" 33 import log "github.com/sirupsen/logrus" 34 35 import "github.com/deroproject/derosuite/config" 36 import "github.com/deroproject/derosuite/block" 37 import "github.com/deroproject/derosuite/crypto" 38 import "github.com/deroproject/derosuite/storage" 39 import "github.com/deroproject/derosuite/crypto/ringct" 40 import "github.com/deroproject/derosuite/transaction" 41 42 //import "github.com/deroproject/derosuite/emission" 43 44 // caches x of transactions validity 45 // it is always atomic 46 // the cache is not txhash -> validity mapping 47 // instead it is txhash+expanded ringmembers 48 // if the entry exist, the tx is valid 49 // it stores special hash and first seen time 50 // this can only be used on expanded transactions 51 var transaction_valid_cache sync.Map 52 53 // this go routine continuously scans and cleans up the cache for expired entries 54 func clean_up_valid_cache() { 55 56 for { 57 time.Sleep(3600 * time.Second) 58 current_time := time.Now() 59 60 // track propagation upto 10 minutes 61 transaction_valid_cache.Range(func(k, value interface{}) bool { 62 first_seen := value.(time.Time) 63 if current_time.Sub(first_seen).Round(time.Second).Seconds() > 3600 { 64 transaction_valid_cache.Delete(k) 65 } 66 return true 67 }) 68 69 } 70 } 71 72 /* Coinbase transactions need to verify the amount of coins 73 * */ 74 func (chain *Blockchain) Verify_Transaction_Coinbase(dbtx storage.DBTX, cbl *block.Complete_Block, minertx *transaction.Transaction) (result bool) { 75 76 if !minertx.IsCoinbase() { // transaction is not coinbase, return failed 77 return false 78 } 79 80 // coinbase transactions only have 1 vin, 1 vout 81 if len(minertx.Vin) != 1 { 82 return false 83 } 84 85 if len(minertx.Vout) != 1 { 86 return false 87 } 88 89 public_key := minertx.Vout[0].Target.(transaction.Txout_to_key).Key 90 91 if !public_key.Public_Key_Valid() { // if public_key is not valid ( not a point on the curve reject the TX) 92 logger.WithFields(log.Fields{"txid": minertx.GetHash()}).Warnf("TX public is INVALID %s ", public_key) 93 return false 94 95 } 96 97 // check whether the height mentioned in tx.Vin is equal to block height 98 // this does NOT hold for genesis block so test it differently 99 100 expected_height := chain.Calculate_Height_At_Tips(dbtx, cbl.Bl.Tips) 101 102 if minertx.Vin[0].(transaction.Txin_gen).Height != uint64(expected_height) { 103 logger.Warnf("Rejected Block due to invalid Height actual %d expected %d", minertx.Vin[0].(transaction.Txin_gen).Height, expected_height) 104 return false 105 106 } 107 108 return true 109 } 110 111 // all non miner tx must be non-coinbase tx 112 // each check is placed in a separate block of code, to avoid ambigous code or faulty checks 113 // all check are placed and not within individual functions ( so as we cannot skip a check ) 114 // This function verifies tx fully, means all checks, 115 // if the transaction has passed the check it can be added to mempool, relayed or added to blockchain 116 // the transaction has already been deserialized thats it 117 // 118 func (chain *Blockchain) Verify_Transaction_NonCoinbase(dbtx storage.DBTX, hf_version int64, tx *transaction.Transaction) (result bool) { 119 result = false 120 121 var tx_hash crypto.Hash 122 var tx_serialized []byte // serialized tx 123 defer func() { // safety so if anything wrong happens, verification fails 124 if r := recover(); r != nil { 125 logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("Recovered while Verifying transaction, failed verification, Stack trace below") 126 logger.Warnf("Stack trace \n%s", debug.Stack()) 127 result = false 128 } 129 }() 130 131 tx_hash = tx.GetHash() 132 133 if tx.Version != 2 { 134 return false 135 } 136 137 // make sure atleast 1 vin and 1 vout are there 138 if len(tx.Vin) < 1 || len(tx.Vout) < 1 { 139 logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("Incoming TX does NOT have atleast 1 vin and 1 vout") 140 return false 141 } 142 143 // this means some other checks have failed somewhere else 144 if tx.IsCoinbase() { // transaction coinbase must never come here 145 logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("Coinbase tx in non coinbase path, Please investigate") 146 return false 147 } 148 149 // Vin can be only specific type rest all make the fail case 150 for i := 0; i < len(tx.Vin); i++ { 151 switch tx.Vin[i].(type) { 152 case transaction.Txin_gen: 153 return false // this is for coinbase so fail it 154 case transaction.Txin_to_key: // pass 155 default: 156 return false 157 } 158 } 159 160 if hf_version >= 2 { 161 162 /* // if ever a need comes ups 163 if len(tx.Vin) >= config.MAX_VIN{ 164 rlog.Warnf("Tx %s has more Vins than allowed limit 299 actual %d",tx_hash,len(tx.Vin)) 165 return 166 }*/ 167 168 if len(tx.Vout) >= config.MAX_VOUT { 169 rlog.Warnf("Tx %s has more Vouts than allowed limit 7 actual %d", tx_hash, len(tx.Vout)) 170 return 171 } 172 } 173 174 // Vout can be only specific type rest all make th fail case 175 for i := 0; i < len(tx.Vout); i++ { 176 switch tx.Vout[i].Target.(type) { 177 case transaction.Txout_to_key: // pass 178 179 public_key := tx.Vout[i].Target.(transaction.Txout_to_key).Key 180 181 if !public_key.Public_Key_Valid() { // if public_key is not valid ( not a point on the curve reject the TX) 182 logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("TX public is INVALID %s ", public_key) 183 return false 184 185 } 186 default: 187 return false 188 } 189 } 190 191 // Vout should have amount 0 192 for i := 0; i < len(tx.Vout); i++ { 193 if tx.Vout[i].Amount != 0 { 194 logger.WithFields(log.Fields{"txid": tx_hash, "Amount": tx.Vout[i].Amount}).Warnf("Amount must be zero in ringCT world") 195 return false 196 } 197 } 198 199 // check the mixin , it should be atleast 4 and should be same through out the tx ( all other inputs) 200 // someone did send a mixin of 3 in 12006 block height 201 // atlantis has minimum mixin of 5 202 if hf_version >= 2 { 203 mixin := len(tx.Vin[0].(transaction.Txin_to_key).Key_offsets) 204 205 if mixin < config.MIN_MIXIN { 206 logger.WithFields(log.Fields{"txid": tx_hash, "Mixin": mixin}).Warnf("Mixin cannot be more than %d.", config.MIN_MIXIN) 207 return false 208 } 209 if mixin >= config.MAX_MIXIN { 210 logger.WithFields(log.Fields{"txid": tx_hash, "Mixin": mixin}).Warnf("Mixin cannot be more than %d.", config.MAX_MIXIN) 211 return false 212 } 213 214 for i := 0; i < len(tx.Vin); i++ { 215 if mixin != len(tx.Vin[i].(transaction.Txin_to_key).Key_offsets) { 216 logger.WithFields(log.Fields{"txid": tx_hash, "Mixin": mixin}).Warnf("Mixin must be same for entire TX in ringCT world") 217 return false 218 } 219 } 220 } 221 222 // duplicate ringmembers are not allowed, check them here 223 // just in case protect ourselves as much as we can 224 for i := 0; i < len(tx.Vin); i++ { 225 ring_members := map[uint64]bool{} // create a separate map for each input 226 ring_member := uint64(0) 227 for j := 0; j < len(tx.Vin[i].(transaction.Txin_to_key).Key_offsets); j++ { 228 ring_member += tx.Vin[i].(transaction.Txin_to_key).Key_offsets[j] 229 if _, ok := ring_members[ring_member]; ok { 230 logger.WithFields(log.Fields{"txid": tx_hash, "input_index": i}).Warnf("Duplicate ring member within the TX") 231 return false 232 } 233 ring_members[ring_member] = true // add member to ring member 234 } 235 236 // rlog.Debugf("Ring members for %d %+v", i, ring_members ) 237 } 238 239 // check whether the key image is duplicate within the inputs 240 // NOTE: a block wide key_image duplication is done during block testing but we are still keeping it 241 { 242 kimages := map[crypto.Hash]bool{} 243 for i := 0; i < len(tx.Vin); i++ { 244 if _, ok := kimages[tx.Vin[i].(transaction.Txin_to_key).K_image]; ok { 245 logger.WithFields(log.Fields{ 246 "txid": tx_hash, 247 "kimage": tx.Vin[i].(transaction.Txin_to_key).K_image, 248 }).Warnf("TX using duplicate inputs within the TX") 249 return false 250 } 251 kimages[tx.Vin[i].(transaction.Txin_to_key).K_image] = true // add element to map for next check 252 } 253 } 254 255 // check whether the key image is low order attack, if yes reject it right now 256 for i := 0; i < len(tx.Vin); i++ { 257 k_image := crypto.Key(tx.Vin[i].(transaction.Txin_to_key).K_image) 258 curve_order := crypto.CurveOrder() 259 mult_result := crypto.ScalarMultKey(&k_image, &curve_order) 260 if *mult_result != crypto.Identity { 261 logger.WithFields(log.Fields{ 262 "txid": tx_hash, 263 "kimage": tx.Vin[i].(transaction.Txin_to_key).K_image, 264 "curve_order": curve_order, 265 "mult_result": *mult_result, 266 "identity": crypto.Identity, 267 }).Warnf("TX contains a low order key image attack, but we are already safeguarded") 268 return false 269 } 270 } 271 272 // disallow old transactions with borrowmean signatures 273 if hf_version >= 2 { 274 switch tx.RctSignature.Get_Sig_Type() { 275 case ringct.RCTTypeSimple, ringct.RCTTypeFull: 276 return false 277 } 278 } 279 280 // check whether the TX contains a signature or NOT 281 switch tx.RctSignature.Get_Sig_Type() { 282 case ringct.RCTTypeSimpleBulletproof, ringct.RCTTypeSimple, ringct.RCTTypeFull: // default case, pass through 283 default: 284 logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("TX does NOT contain a ringct signature. It is NOT possible") 285 return false 286 } 287 288 // check tx size for validity 289 if hf_version >= 2 { 290 tx_serialized = tx.Serialize() 291 if len(tx_serialized) >= config.CRYPTONOTE_MAX_TX_SIZE { 292 rlog.Warnf("tx %s rejected Size(%d) is more than allowed(%d)", tx_hash, len(tx.Serialize()), config.CRYPTONOTE_MAX_TX_SIZE) 293 return false 294 } 295 } 296 297 // expand the signature first 298 // whether the inputs are mature and can be used at time is verified while expanding the inputs 299 300 //rlog.Debugf("txverify tx %s hf_version %d", tx_hash, hf_version ) 301 if !chain.Expand_Transaction_v2(dbtx, hf_version, tx) { 302 rlog.Warnf("TX %s inputs could not be expanded or inputs are NOT mature", tx_hash) 303 return false 304 } 305 306 //logger.Infof("Expanded tx %+v", tx.RctSignature) 307 308 // create a temporary hash out of expanded transaction 309 // this feature is very critical and helps the daemon by spreading out the compute load 310 // over the entire time between 2 blocks 311 // this tremendously helps in block propagation times 312 // and make them easy to process just like like small 50 KB blocks 313 314 // each ring member if 64 bytes 315 tmp_buffer := make([]byte, 0, len(tx.Vin)*32+len(tx.Vin)*len(tx.Vin[0].(transaction.Txin_to_key).Key_offsets)*64) 316 317 // build the buffer for special hash 318 // DO NOT skip anything, use full serialized tx, it is used while building keccak hash 319 // use everything from tx expansion etc 320 for i := 0; i < len(tx.Vin); i++ { // append all mlsag sigs 321 tmp_buffer = append(tmp_buffer, tx.RctSignature.MlsagSigs[i].II[0][:]...) 322 } 323 for i := 0; i < len(tx.RctSignature.MixRing); i++ { 324 for j := 0; j < len(tx.RctSignature.MixRing[i]); j++ { 325 tmp_buffer = append(tmp_buffer, tx.RctSignature.MixRing[i][j].Destination[:]...) 326 tmp_buffer = append(tmp_buffer, tx.RctSignature.MixRing[i][j].Mask[:]...) 327 } 328 } 329 330 // 1 less allocation this way 331 special_hash := crypto.Keccak256(tx_serialized, tmp_buffer) 332 333 if _, ok := transaction_valid_cache.Load(special_hash); ok { 334 //logger.Infof("Found in cache %s ",tx_hash) 335 return true 336 } else { 337 //logger.Infof("TX not found in cache %s len %d ",tx_hash, len(tmp_buffer)) 338 } 339 340 // check the ring signature 341 if !tx.RctSignature.Verify() { 342 343 //logger.Infof("tx expanded %+v\n", tx.RctSignature.MixRing) 344 logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("TX RCT Signature failed") 345 return false 346 347 } 348 349 // signature got verified, cache it 350 transaction_valid_cache.Store(special_hash, time.Now()) 351 //logger.Infof("TX validity marked in cache %s ",tx_hash) 352 353 //logger.WithFields(log.Fields{"txid": tx_hash}).Debugf("TX successfully verified") 354 355 return true 356 } 357 358 // double spend check is separate from the core checks ( due to softforks ) 359 func (chain *Blockchain) Verify_Transaction_NonCoinbase_DoubleSpend_Check(dbtx storage.DBTX, tx *transaction.Transaction) (result bool) { 360 result = false 361 362 var tx_hash crypto.Hash 363 defer func() { // safety so if anything wrong happens, verification fails 364 if r := recover(); r != nil { 365 logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("Recovered while Verifying transaction, failed verification, Stack trace below") 366 logger.Warnf("Stack trace \n%s", debug.Stack()) 367 result = false 368 } 369 }() 370 371 tx_hash = tx.GetHash() 372 373 // a similiar block level check is done for double spending attacks within the block itself 374 // check whether the key image is already used or spent earlier ( in blockchain ) 375 for i := 0; i < len(tx.Vin); i++ { 376 k_image := crypto.Key(tx.Vin[i].(transaction.Txin_to_key).K_image) 377 if spent_height, ok := chain.Read_KeyImage_Status(dbtx, crypto.Hash(k_image)); ok { 378 _ = spent_height 379 //rlog.Warnf("Key image is already spent at height %d, attempt to double spend KI %s TXID %s", spent_height,k_image,tx_hash) 380 return false 381 } 382 } 383 return true 384 385 } 386 387 // verify all non coinbase tx, single threaded for double spending on current active chain 388 func (chain *Blockchain) Verify_Block_DoubleSpending(dbtx storage.DBTX, cbl *block.Complete_Block) (result bool) { 389 for i := 0; i < len(cbl.Txs); i++ { 390 if !chain.Verify_Transaction_NonCoinbase_DoubleSpend_Check(dbtx, cbl.Txs[i]) { 391 return false 392 } 393 } 394 395 return true 396 }