You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

300 lines
11 KiB

  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. package blockchain
  17. //import "fmt"
  18. //import "time"
  19. /*import "bytes"
  20. import "encoding/binary"
  21. import "github.com/romana/rlog"
  22. */
  23. //import "github.com/romana/rlog"
  24. import "runtime/debug"
  25. import log "github.com/sirupsen/logrus"
  26. import "github.com/deroproject/derosuite/block"
  27. import "github.com/deroproject/derosuite/crypto"
  28. import "github.com/deroproject/derosuite/globals"
  29. import "github.com/deroproject/derosuite/crypto/ringct"
  30. import "github.com/deroproject/derosuite/transaction"
  31. import "github.com/deroproject/derosuite/emission"
  32. /* This function verifies tx fully, means all checks,
  33. * if the transaction has passed the check it can be added to mempool, relayed or added to blockchain
  34. * the transaction has already been deserialized thats it
  35. * */
  36. func (chain *Blockchain) Verify_Transaction(tx *transaction.Transaction) (result bool) {
  37. return false
  38. }
  39. /* Coinbase transactions need to verify the amount of coins
  40. * */
  41. func (chain *Blockchain) Verify_Transaction_Coinbase(cbl *block.Complete_Block, minertx *transaction.Transaction) (result bool) {
  42. if !minertx.IsCoinbase() { // transaction is not coinbase, return failed
  43. return false
  44. }
  45. // coinbase transactions only have 1 vin, 1 vout
  46. if len(minertx.Vin) != 1 {
  47. return false
  48. }
  49. if len(minertx.Vout) != 1 {
  50. return false
  51. }
  52. // check whether the height mentioned in tx.Vin is equal to block height
  53. // this does NOT hold for genesis block so test it differently
  54. expected_height := chain.Load_Height_for_BL_ID(cbl.Bl.Prev_Hash)
  55. expected_height++
  56. if cbl.Bl.GetHash() == globals.Config.Genesis_Block_Hash {
  57. expected_height = 0
  58. }
  59. if minertx.Vin[0].(transaction.Txin_gen).Height != expected_height {
  60. logger.Warnf(" Rejected Height %d should be %d", minertx.Vin[0].(transaction.Txin_gen).Height, chain.Load_Height_for_BL_ID(cbl.Bl.Prev_Hash))
  61. return false
  62. }
  63. // verify coins amount ( minied amount ) + the fees colllected which is sum of all tx included in this block
  64. // and whether it has been calculated correctly
  65. total_fees := uint64(0)
  66. for i := 0; i < len(cbl.Txs); i++ {
  67. total_fees += cbl.Txs[i].RctSignature.Get_TX_Fee()
  68. }
  69. total_reward := minertx.Vout[0].Amount
  70. base_reward := total_reward - total_fees
  71. // size of block = size of miner_tx + size of all non coinbase tx
  72. sizeofblock := uint64(0)
  73. sizeofblock += uint64(len(cbl.Bl.Miner_tx.Serialize()))
  74. // logger.Infof("size of block %d sizeof miner tx %d", sizeofblock, len(cbl.Bl.Miner_tx.Serialize()))
  75. for i := 0; i < len(cbl.Txs); i++ {
  76. sizeofblock += uint64(len(cbl.Txs[i].Serialize()))
  77. // logger.Infof("size of tx i %d tx size %d total size %d", i, uint64(len(cbl.Txs[i].Serialize())) ,sizeofblock)
  78. }
  79. median_block_size := chain.Get_Median_BlockSize_At_Block(cbl.Bl.Prev_Hash)
  80. already_generated_coins := chain.Load_Already_Generated_Coins_for_BL_ID(cbl.Bl.Prev_Hash)
  81. base_reward_calculated := emission.GetBlockReward(median_block_size, sizeofblock, already_generated_coins, 6, 0)
  82. if base_reward != base_reward_calculated {
  83. logger.Warnf("Base reward %d should be %d", base_reward, base_reward_calculated)
  84. logger.Warnf("median_block_size %d block_size %d already already_generated_coins %d", median_block_size, sizeofblock,
  85. already_generated_coins)
  86. return false
  87. }
  88. /*
  89. for i := sizeofblock-4000; i < (sizeofblock+4000);i++{
  90. // now we need to verify whether base reward is okay
  91. //base_reward_calculated := emission.GetBlockReward(median_block_size,sizeofblock,already_generated_coins,6,0)
  92. base_reward_calculated := emission.GetBlockReward(median_block_size,i,already_generated_coins,6,0)
  93. if base_reward == base_reward_calculated {
  94. logger.Warnf("Base reward %d should be %d size %d", base_reward, base_reward_calculated,i)
  95. }
  96. }
  97. */
  98. return true
  99. }
  100. // all non miner tx must be non-coinbase tx
  101. // each check is placed in a separate block of code, to avoid ambigous code or faulty checks
  102. // all check are placed and not within individual functions ( so as we cannot skip a check )
  103. func (chain *Blockchain) Verify_Transaction_NonCoinbase(tx *transaction.Transaction) (result bool) {
  104. result = false
  105. var tx_hash crypto.Hash
  106. defer func() { // safety so if anything wrong happens, verification fails
  107. if r := recover(); r != nil {
  108. logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("Recovered while Verifying transaction, failed verification, Stack trace below")
  109. logger.Warnf("Stack trace \n%s", debug.Stack())
  110. result = false
  111. }
  112. }()
  113. tx_hash = tx.GetHash()
  114. if tx.Version != 2 {
  115. return false
  116. }
  117. // make sure atleast 1 vin and 1 vout are there
  118. if len(tx.Vin) < 1 || len(tx.Vout) < 1 {
  119. logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("Incoming TX does NOT have atleast 1 vin and 1 vout")
  120. return false
  121. }
  122. // this means some other checks have failed somewhere else
  123. if tx.IsCoinbase() { // transaction coinbase must never come here
  124. logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("Coinbase tx in non coinbase path, Please investigate")
  125. return false
  126. }
  127. // Vin can be only specific type rest all make the fail case
  128. for i := 0; i < len(tx.Vin); i++ {
  129. switch tx.Vin[i].(type) {
  130. case transaction.Txin_gen:
  131. return false // this is for coinbase so fail it
  132. case transaction.Txin_to_key: // pass
  133. default:
  134. return false
  135. }
  136. }
  137. // Vout can be only specific type rest all make th fail case
  138. for i := 0; i < len(tx.Vout); i++ {
  139. switch tx.Vout[i].Target.(type) {
  140. case transaction.Txout_to_key: // pass
  141. default:
  142. return false
  143. }
  144. }
  145. // Vout should have amount 0
  146. for i := 0; i < len(tx.Vout); i++ {
  147. if tx.Vout[i].Amount != 0 {
  148. logger.WithFields(log.Fields{"txid": tx_hash, "Amount": tx.Vout[i].Amount}).Warnf("Amount must be zero in ringCT world")
  149. return false
  150. }
  151. }
  152. // just some extra logs for testing purposes
  153. if len(tx.Vin) >= 3 {
  154. logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("tx has more than 3 inputs")
  155. }
  156. if len(tx.Vout) >= 3 {
  157. logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("tx has more than 3 outputs")
  158. }
  159. // check the mixin , it should be atleast 4 and should be same through out the tx ( all other inputs)
  160. // someone did send a mixin of 3 in 12006 block height
  161. mixin := len(tx.Vin[0].(transaction.Txin_to_key).Key_offsets)
  162. if mixin < 3 {
  163. logger.WithFields(log.Fields{"txid": tx_hash, "Mixin": mixin}).Warnf("Mixin must be atleast 3 in ringCT world")
  164. return false
  165. }
  166. for i := 0; i < len(tx.Vin); i++ {
  167. if mixin != len(tx.Vin[i].(transaction.Txin_to_key).Key_offsets) {
  168. logger.WithFields(log.Fields{"txid": tx_hash, "Mixin": mixin}).Warnf("Mixin must be same for entire TX in ringCT world")
  169. return false
  170. }
  171. }
  172. // duplicate ringmembers are not allowed, check them here
  173. // just in case protect ourselves as much as we can
  174. for i := 0; i < len(tx.Vin); i++ {
  175. ring_members := map[uint64]bool{} // create a separate map for each input
  176. ring_member := uint64(0)
  177. for j := 0; j < len(tx.Vin[i].(transaction.Txin_to_key).Key_offsets); j++ {
  178. ring_member += tx.Vin[i].(transaction.Txin_to_key).Key_offsets[j]
  179. if _, ok := ring_members[ring_member]; ok {
  180. logger.WithFields(log.Fields{"txid": tx_hash, "input_index": i}).Warnf("Duplicate ring member within the TX")
  181. return false
  182. }
  183. ring_members[ring_member] = true // add member to ring member
  184. }
  185. }
  186. // check whether the key image is duplicate within the inputs
  187. // NOTE: a block wide key_image duplication is done during block testing but we are still keeping it
  188. {
  189. kimages := map[crypto.Hash]bool{}
  190. for i := 0; i < len(tx.Vin); i++ {
  191. if _, ok := kimages[tx.Vin[i].(transaction.Txin_to_key).K_image]; ok {
  192. logger.WithFields(log.Fields{
  193. "txid": tx_hash,
  194. "kimage": tx.Vin[i].(transaction.Txin_to_key).K_image,
  195. }).Warnf("TX using duplicate inputs within the TX")
  196. return false
  197. }
  198. kimages[tx.Vin[i].(transaction.Txin_to_key).K_image] = true // add element to map for next check
  199. }
  200. }
  201. // check whether the key image is low order attack, if yes reject it right now
  202. for i := 0; i < len(tx.Vin); i++ {
  203. k_image := ringct.Key(tx.Vin[i].(transaction.Txin_to_key).K_image)
  204. curve_order := ringct.CurveOrder()
  205. mult_result := ringct.ScalarMultKey(&k_image, &curve_order)
  206. if *mult_result != ringct.Identity {
  207. logger.WithFields(log.Fields{
  208. "txid": tx_hash,
  209. "kimage": tx.Vin[i].(transaction.Txin_to_key).K_image,
  210. "curve_order": curve_order,
  211. "mult_result": *mult_result,
  212. "identity": ringct.Identity,
  213. }).Warnf("TX contains a low order key image attack, but we are already safeguarded")
  214. return false
  215. }
  216. }
  217. // a similiar block level check is done for double spending attacks within the block itself
  218. // check whether the key image is already used or spent earlier ( in blockchain )
  219. /*
  220. for i := 0; i < len(tx.Vin); i++ {
  221. k_image := ringct.Key(tx.Vin[i].(transaction.Txin_to_key).K_image)
  222. if chain.Read_KeyImage_Status(crypto.Hash(k_image)) {
  223. logger.WithFields(log.Fields{
  224. "txid": tx_hash,
  225. "kimage": k_image,
  226. }).Warnf("Key image is already spent, attempt to double spend ")
  227. return false
  228. }
  229. }
  230. */
  231. // check whether the TX contains a signature or NOT
  232. switch tx.RctSignature.Get_Sig_Type() {
  233. case ringct.RCTTypeSimple, ringct.RCTTypeFull: // default case, pass through
  234. default:
  235. logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("TX does NOT contain a ringct signature. It is NOT possible")
  236. return false
  237. }
  238. // expand the signature first
  239. // whether the inputs are mature and can be used at time is verified while expanding the inputs
  240. if !chain.Expand_Transaction_v2(tx) {
  241. logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("TX inputs could not be expanded or inputs are NOT mature")
  242. return false
  243. }
  244. // check the ring signature
  245. if !tx.RctSignature.Verify() {
  246. logger.WithFields(log.Fields{"txid": tx_hash}).Warnf("TX RCT Signature failed")
  247. return false
  248. }
  249. logger.WithFields(log.Fields{"txid": tx_hash}).Debugf("TX successfully verified")
  250. return true
  251. }