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.

567 lines
20 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 "encoding/binary"
  19. //import log "github.com/sirupsen/logrus"
  20. import "github.com/deroproject/derosuite/block"
  21. import "github.com/deroproject/derosuite/crypto"
  22. import "github.com/deroproject/derosuite/globals"
  23. import "github.com/deroproject/derosuite/transaction"
  24. /* this file implements the only interface which translates comands to/from blockchain to storage layer *
  25. *
  26. *
  27. */
  28. var TOP_ID = []byte("TOP_ID") // stores current TOP, only stores single value
  29. var TX_ID = []byte("TX") // stores transactions
  30. var BLOCK_ID = []byte("BLOCK") // stores blocks
  31. var CHAIN = []byte("CHAIN") // this stores the actual chain, parents keeps child list, starts from genesis block
  32. var HEIGHT_TO_BLOCK_ID = []byte("HEIGHT_TO_BLOCK_ID") // stores block height to block id mapping
  33. var BLOCK_ID_TO_HEIGHT = []byte("BLOCK_ID_TO_HEIGHT") // stores block id to height mapping
  34. var BLOCK_ID_POW = []byte("BLOCK_ID_POW") // stores block_id to pow, this is slow to calculate, do we even need to store it
  35. // all orphan ids are stored here, it id exists its orphan,
  36. // once a block is adden to orphan list it cannot be removed, since we have a better block
  37. var ORPHAN = []byte("ORPHAN")
  38. var ORPHAN_HEIGHT = []byte("ORPHAN_HEIGHT") // height wise orphans are store here
  39. var OO_ID = []byte{0x4} // mapping of incremental numbers to respective transaction ID
  40. var ALTERNATIVE_BLOCKS_ID = []byte{0x5} // each block contains lists of alternative child blocks
  41. var BLOCKCHAIN_UNIVERSE = []byte("BLOCKCHAIN_UNIVERSE") // all block chain data is store in this BLOCKCHAIN_UNIVERSE
  42. // there are only 3 galaxies
  43. var GALAXY_BLOCK = []byte("BLOCK")
  44. var GALAXY_TRANSACTION = []byte("TRANSACTION")
  45. var GALAXY_KEYIMAGE = []byte("KEYIMAGE")
  46. //2 galaxies store inverse mapping
  47. var GALAXY_HEIGHT = []byte("HEIGHT") // height to block id mapping
  48. var GALAXY_OUTPUT_INDEX = []byte("OUTPUT_INDEX") // incremental index over output index
  49. // the unique TXID or block ID becomes the solar system , which is common and saves lot of space
  50. // individual attributes becomes the planets
  51. // individual attributes should be max 1 or 2 chars long, as they will be repeated millions of times and storing a static string millions of times shows foolishness
  52. var PLANET_BLOB = []byte("BLOB") //it shows serialised block
  53. var PLANET_HEIGHT = []byte("HEIGHT") // contains height
  54. var PLANET_PARENT = []byte("PARENT") // parent of block
  55. var PLANET_SIZE = []byte("SIZE") // sum of block + all txs
  56. var PLANET_ALREADY_GENERATED_COINS = []byte("CCOINS") // all coins generated till this block
  57. var PLANET_OUTPUT_INDEX = []byte("OUTPUT_INDEX") // tx outputs indexing starts from here for this block
  58. var PLANET_CUMULATIVE_DIFFICULTY = []byte("CDIFFICULTY")
  59. var PLANET_CHILD = []byte("CHILD")
  60. var PLANET_BASEREWARD = []byte("BASEREWARD") // base reward of a block
  61. var PLANET_TIMESTAMP = []byte("TIMESTAMP")
  62. // this ill only be present if more tahn 1 child exists
  63. var PLANET_CHILDREN = []byte("CHILREN") // children list excludes the main child, so its a multiple of 32
  64. // the TX has the following attributes
  65. var PLANET_TX_BLOB = []byte("BLOB") // contains serialised TX , this attribute is also found in BLOCK where
  66. var PLANET_TX_MINED_IN_BLOCK = []byte("MINERBLOCK") // which block mined this tx, height
  67. var PLANET_TX_SIZE = []byte("SIZE")
  68. // the universe concept is there, as we bring in smart contracts, we will give each of them a universe to play within
  69. // while communicating with external universe
  70. /*
  71. func (chain *Blockchain) Store_Main_Chain(parent_id crypto.Hash, child_id crypto.Hash){
  72. err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE,GALAXY_BLOCK,parent_id[:],PLANET_CHILD, child_id[:] )
  73. _ = err
  74. }
  75. func (chain *Blockchain) Load_Main_Chain(parent_id crypto.Hash) (child_id crypto.Hash ){
  76. var err error
  77. // store OO to TXID automatically
  78. object_data,err = chain.store.LoadObject(BLOCKCHAIN_UNIVERSE,GALAXY_BLOCK,parent_id[:],PLANET_CHILD )
  79. if err != nil {
  80. return child_id,err
  81. }
  82. if len(object_data) == 0 {
  83. return child_id, fmt.Errorf("No Block at such Height %d", Height)
  84. }
  85. if len(object_data) != 32 {
  86. panic("Database corruption, invalid block hash ")
  87. }
  88. copy(child_id[:],object_data[:32])
  89. _ = err
  90. return child_id
  91. }
  92. */
  93. // check whether the block has a child
  94. func (chain *Blockchain) Does_Block_Have_Child(block_id crypto.Hash) bool {
  95. var err error
  96. object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, block_id[:], PLANET_CHILD)
  97. if err != nil || len(object_data) == 0 {
  98. return false
  99. }
  100. if len(object_data) != 32 {
  101. panic("Database corruption, invalid block hash ")
  102. }
  103. return true
  104. }
  105. // load the main child
  106. func (chain *Blockchain) Load_Block_Child(parent_id crypto.Hash) (child_id crypto.Hash) {
  107. if !chain.Does_Block_Have_Child(parent_id) {
  108. panic("Block does not have a child")
  109. }
  110. object_data, _ := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, parent_id[:], PLANET_CHILD)
  111. copy(child_id[:], object_data)
  112. return
  113. }
  114. // changes or set child block of a parent
  115. // there can be only 1 child, rest all are alternatives and stored as
  116. func (chain *Blockchain) Store_Block_Child(parent_id crypto.Hash, child_id crypto.Hash) {
  117. err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, parent_id[:], PLANET_CHILD, child_id[:])
  118. // load block children
  119. _ = err
  120. }
  121. // while store children
  122. func (chain *Blockchain) Store_Block_Children(parent_id crypto.Hash, children []crypto.Hash, exclude_child crypto.Hash) {
  123. var children_bytes []byte
  124. for i := range children {
  125. if children[i] != exclude_child { // exclude main child
  126. children_bytes = append(children_bytes, children[i][:]...)
  127. }
  128. }
  129. err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, parent_id[:], PLANET_CHILDREN, children_bytes)
  130. _ = err
  131. }
  132. func (chain *Blockchain) Load_Block_Children(parent_id crypto.Hash) (children []crypto.Hash) {
  133. var child_hash crypto.Hash
  134. if !chain.Does_Block_Have_Child(parent_id) { // block doesnot have a child, so it cannot have children
  135. return
  136. }
  137. // we are here means parent does have child
  138. children = append(children, chain.Load_Block_Child(parent_id))
  139. // check for children
  140. children_bytes, _ := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, parent_id[:], PLANET_CHILDREN)
  141. if len(children_bytes)%32 != 0 {
  142. panic(fmt.Sprintf("parent does not have child hash in multiples of 32, block_hash %s", parent_id))
  143. }
  144. for i := 0; i < len(children_bytes); i = i + 32 {
  145. copy(child_hash[:], children_bytes[i:i+32])
  146. children = append(children, child_hash)
  147. }
  148. return children
  149. }
  150. // store a tx
  151. // this only occurs when a tx has been mined
  152. // stores a height to show at what height it has been mined
  153. func (chain *Blockchain) Store_TX(tx *transaction.Transaction, Height uint64) {
  154. hash := tx.GetHash()
  155. serialized := tx.Serialize()
  156. err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, hash[:], PLANET_TX_BLOB, serialized)
  157. // store size of tx
  158. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, hash[:], PLANET_TX_SIZE, uint64(len(serialized)))
  159. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, hash[:], PLANET_TX_MINED_IN_BLOCK, Height)
  160. _ = err
  161. }
  162. func (chain *Blockchain) Store_TX_Miner(txhash crypto.Hash, block_id crypto.Hash) {
  163. // store block id which mined this tx
  164. err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, txhash[:], PLANET_TX_MINED_IN_BLOCK, block_id[:])
  165. _ = err
  166. }
  167. func (chain *Blockchain) Load_TX_Size(txhash crypto.Hash) uint64 {
  168. // store block id which mined this tx
  169. size, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, txhash[:], PLANET_TX_SIZE)
  170. if err != nil {
  171. logger.Warnf("Size not stored for tx %s", txhash)
  172. }
  173. return size
  174. }
  175. // load height at which a specific tx was mined
  176. func (chain *Blockchain) Load_TX_Height(txhash crypto.Hash) uint64 {
  177. height, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, txhash[:], PLANET_TX_MINED_IN_BLOCK)
  178. if err != nil {
  179. logger.Warnf("Error while querying height for tx %s\n", txhash)
  180. }
  181. return height
  182. }
  183. // BUG we should be able to delete any arbitrary key
  184. // since a tx mined by one block, can be back in pool after chain reorganises
  185. // TODO the miner tx should be extracted ands stored from somewhere else
  186. // NOTE: before storing a block, its transactions must be stored
  187. func (chain *Blockchain) Store_BL(bl *block.Block) {
  188. // store block height BHID automatically
  189. hash := bl.GetHash()
  190. // we should deserialize the block here
  191. serialized_bytes := bl.Serialize() // we are storing the miner transactions within
  192. err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_BLOB, serialized_bytes)
  193. // get height of parent, add 1 and store it
  194. height := uint64(0)
  195. if hash != globals.Config.Genesis_Block_Hash { // genesis block has no parent
  196. height = chain.Load_Height_for_BL_ID(bl.Prev_Hash)
  197. height++
  198. }
  199. // store new height
  200. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_HEIGHT, height)
  201. // store timestamp
  202. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_TIMESTAMP, bl.Timestamp)
  203. // store parent
  204. chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_PARENT, bl.Prev_Hash[:])
  205. // calculate cumulative difficulty at last block
  206. difficulty_of_current_block := uint64(0)
  207. cumulative_difficulty := uint64(0)
  208. if hash != globals.Config.Genesis_Block_Hash { // genesis block has no parent
  209. cumulative_difficulty = chain.Load_Block_Cumulative_Difficulty(bl.Prev_Hash)
  210. difficulty_of_current_block = chain.Get_Difficulty_At_Block(bl.Prev_Hash)
  211. } else {
  212. cumulative_difficulty = 1 // genesis block cumulative difficulty is 1
  213. }
  214. total_difficulty := cumulative_difficulty + difficulty_of_current_block
  215. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_CUMULATIVE_DIFFICULTY, total_difficulty)
  216. // total size of block = size of miner_tx + size of all transactions in block ( excludind miner tx)
  217. size_of_block := uint64(len(bl.Miner_tx.Serialize()))
  218. for i := 0; i < len(bl.Tx_hashes); i++ {
  219. size_of_tx := chain.Load_TX_Size(bl.Tx_hashes[i])
  220. size_of_block += size_of_tx
  221. }
  222. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_SIZE, size_of_block)
  223. // calculated position of vouts in global indexs
  224. index_pos := uint64(0)
  225. if hash != globals.Config.Genesis_Block_Hash {
  226. // load index pos from last block + add count of vouts from last block
  227. index_pos = chain.Get_Block_Output_Index(bl.Prev_Hash)
  228. vout_count_prev_block := chain.Block_Count_Vout(bl.Prev_Hash)
  229. index_pos += vout_count_prev_block
  230. }
  231. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_OUTPUT_INDEX, index_pos)
  232. //logger.Debugf("height %d output index %d",height, index_pos)
  233. total_fees := uint64(0)
  234. for i := 0; i < len(bl.Tx_hashes); i++ {
  235. tx, _ := chain.Load_TX_FROM_ID(bl.Tx_hashes[i])
  236. total_fees += tx.RctSignature.Get_TX_Fee()
  237. }
  238. total_reward := bl.Miner_tx.Vout[0].Amount
  239. base_reward := total_reward - total_fees
  240. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_BASEREWARD, base_reward)
  241. already_generated_coins := uint64(0)
  242. if hash != globals.Config.Genesis_Block_Hash { // genesis block has no parent
  243. already_generated_coins = chain.Load_Already_Generated_Coins_for_BL_ID(bl.Prev_Hash)
  244. } else {
  245. base_reward = 1000000000000 // trigger the bug to fix coin calculation, see comments in emission
  246. }
  247. already_generated_coins += base_reward
  248. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_ALREADY_GENERATED_COINS, already_generated_coins)
  249. // also extract and store the miner tx separetly, fr direct querying purpose
  250. chain.Store_TX(&bl.Miner_tx, height)
  251. _ = err
  252. }
  253. func (chain *Blockchain) Load_TX_FROM_ID(hash [32]byte) (*transaction.Transaction, error) {
  254. var tx transaction.Transaction
  255. tx_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, hash[:], PLANET_TX_BLOB)
  256. if err != nil {
  257. return nil, err
  258. }
  259. // we should deserialize the block here
  260. err = tx.DeserializeHeader(tx_data)
  261. if err != nil {
  262. logger.Printf("fError deserialiing tx, block id %s len(data) %d data %x\n", hash[:], len(tx_data), tx_data)
  263. return nil, err
  264. }
  265. return &tx, nil
  266. }
  267. func (chain *Blockchain) Load_BL_FROM_ID(hash [32]byte) (*block.Block, error) {
  268. var bl block.Block
  269. block_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_BLOB)
  270. if err != nil {
  271. return nil, err
  272. }
  273. if len(block_data) == 0 {
  274. return nil, fmt.Errorf("Block not found in DB")
  275. }
  276. // we should deserialize the block here
  277. err = bl.Deserialize(block_data)
  278. if err != nil {
  279. logger.Warnf("fError deserialiing block, block id %s len(data) %d data %x\n", hash[:], len(block_data), block_data)
  280. return nil, err
  281. }
  282. return &bl, nil
  283. }
  284. // this will give you a block id at a specific height
  285. func (chain *Blockchain) Load_BL_ID_at_Height(Height uint64) (hash crypto.Hash, err error) {
  286. object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_HEIGHT, PLANET_HEIGHT, itob(Height))
  287. if err != nil {
  288. return hash, err
  289. }
  290. if len(object_data) == 0 {
  291. return hash, fmt.Errorf("No Block at such Height %d", Height)
  292. }
  293. if len(object_data) != 32 {
  294. panic("Database corruption, invalid block hash ")
  295. }
  296. copy(hash[:], object_data[:32])
  297. return hash, nil
  298. }
  299. // this will give you a block id at a specific height
  300. func (chain *Blockchain) Store_BL_ID_at_Height(Height uint64, hash crypto.Hash) {
  301. // store height to block id mapping
  302. chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_HEIGHT, PLANET_HEIGHT, itob(Height), hash[:])
  303. }
  304. func (chain *Blockchain) Load_Height_for_BL_ID(hash crypto.Hash) (Height uint64) {
  305. if hash == ZERO_HASH { // handle special case for genesis
  306. return 0
  307. }
  308. object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_HEIGHT)
  309. if err != nil {
  310. logger.Warnf("Error while querying height for block %s\n", hash)
  311. return
  312. }
  313. if len(object_data) == 0 {
  314. //return hash, fmt.Errorf("No Height for block %x", hash[:])
  315. return
  316. }
  317. if len(object_data) != 8 {
  318. panic("Database corruption, invalid block hash ")
  319. }
  320. Height = binary.BigEndian.Uint64(object_data)
  321. return Height
  322. }
  323. func (chain *Blockchain) Load_Block_Timestamp(hash crypto.Hash) uint64 {
  324. timestamp, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_TIMESTAMP)
  325. if err != nil {
  326. logger.Warnf("Error while querying timestamp for block %s\n", hash)
  327. }
  328. return timestamp
  329. }
  330. func (chain *Blockchain) Load_Block_Cumulative_Difficulty(hash crypto.Hash) uint64 {
  331. cdifficulty, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_CUMULATIVE_DIFFICULTY)
  332. if err != nil {
  333. logger.Panicf("Error while querying cumulative difficulty for block %s\n", hash)
  334. }
  335. return cdifficulty
  336. }
  337. func (chain *Blockchain) Load_Block_Reward(hash crypto.Hash) uint64 {
  338. timestamp, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_BASEREWARD)
  339. if err != nil {
  340. logger.Warnf("Error while querying base_reward for block %s\n", hash)
  341. }
  342. return timestamp
  343. }
  344. func (chain *Blockchain) Load_Already_Generated_Coins_for_BL_ID(hash crypto.Hash) uint64 {
  345. if hash == ZERO_HASH {
  346. return 0
  347. }
  348. timestamp, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_ALREADY_GENERATED_COINS)
  349. if err != nil {
  350. logger.Warnf("Error while querying alreadt generated coins for block %s\n", hash)
  351. }
  352. return timestamp
  353. }
  354. func (chain *Blockchain) Load_Block_Size(hash crypto.Hash) uint64 {
  355. timestamp, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_SIZE)
  356. if err != nil {
  357. logger.Warnf("Error while querying size for block %s\n", hash)
  358. }
  359. return timestamp
  360. }
  361. func (chain *Blockchain) Load_Block_Parent_ID(hash crypto.Hash) crypto.Hash {
  362. var parent_id crypto.Hash
  363. object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_PARENT)
  364. if err != nil || len(object_data) != 32 {
  365. logger.Warnf("Error while querying parent id for block %s\n", hash)
  366. }
  367. copy(parent_id[:], object_data)
  368. return parent_id
  369. }
  370. // store current top id
  371. func (chain *Blockchain) Store_TOP_ID(hash crypto.Hash) {
  372. chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, TOP_ID, TOP_ID, TOP_ID, hash[:])
  373. }
  374. // crash if something is not correct
  375. func (chain *Blockchain) Load_TOP_ID() (hash crypto.Hash) {
  376. object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, TOP_ID, TOP_ID, TOP_ID)
  377. if err != nil {
  378. panic("Backend failure")
  379. }
  380. if len(object_data) == 0 {
  381. panic(fmt.Errorf("most probably Database corruption, No TOP_ID stored "))
  382. }
  383. if len(object_data) != 32 {
  384. panic("Database corruption, invalid block hash ")
  385. }
  386. copy(hash[:], object_data[:32])
  387. return hash
  388. }
  389. // itob returns an 8-byte big endian representation of v.
  390. func itob(v uint64) []byte {
  391. b := make([]byte, 8)
  392. binary.BigEndian.PutUint64(b, uint64(v))
  393. return b
  394. }
  395. // get the position from where indexing must start for this block
  396. // indexing mean vout based index
  397. // cryptonote works by giving each vout a unique index
  398. func (chain *Blockchain) Get_Block_Output_Index(block_id crypto.Hash) uint64 {
  399. if block_id == globals.Config.Genesis_Block_Hash { // genesis block has no output index
  400. return 0 // counting starts from zero
  401. }
  402. index, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, block_id[:], PLANET_OUTPUT_INDEX)
  403. if err != nil {
  404. // TODO this panic must be enabled to catch some bugs
  405. logger.Warnf("Cannot load output index for %s err %s", block_id, err)
  406. return 0
  407. }
  408. return index
  409. }
  410. // store key image to its own galaxy
  411. // a keyimage stored with value 1 == it has been consumed
  412. // a keyimage stored with value 0 == it has not been consumed
  413. // a key image not found in store == it has NOT been consumed
  414. func (chain *Blockchain) Store_KeyImage(hash crypto.Hash, mark bool) {
  415. store_value := byte(0)
  416. if mark {
  417. store_value = byte(1)
  418. }
  419. chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_KEYIMAGE, GALAXY_KEYIMAGE, hash[:], []byte{store_value})
  420. }
  421. // read a key image, whether it's stored with value 1
  422. // a keyimage stored with value 1 == it has been consumed
  423. // a keyimage stored with value 0 == it has not been consumed
  424. // a key image not found in store == it has NOT been consumed
  425. func (chain *Blockchain) Read_KeyImage_Status(hash crypto.Hash) bool {
  426. object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_KEYIMAGE, GALAXY_KEYIMAGE, hash[:])
  427. if err != nil {
  428. return false
  429. }
  430. if len(object_data) == 0 {
  431. return false
  432. }
  433. if len(object_data) != 1 {
  434. panic(fmt.Errorf("probably Database corruption, Wrong data stored in keyimage, expected size 1, actual size %d", len(object_data)))
  435. }
  436. if object_data[0] == 1 {
  437. return true
  438. }
  439. // anything other than value 1 is considered wrong keyimage
  440. return false
  441. }