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.

455 lines
15 KiB

  1. package blockchain
  2. import "fmt"
  3. import "encoding/binary"
  4. //import log "github.com/sirupsen/logrus"
  5. import "github.com/deroproject/derosuite/crypto"
  6. import "github.com/deroproject/derosuite/globals"
  7. /* this file implements the only interface which translates comands to/from blockchain to storage layer *
  8. *
  9. *
  10. */
  11. var TOP_ID = []byte("TOP_ID") // stores current TOP, only stores single value
  12. var TX_ID = []byte("TX") // stores transactions
  13. var BLOCK_ID = []byte("BLOCK") // stores blocks
  14. var CHAIN = []byte("CHAIN") // this stores the actual chain, parents keeps child list, starts from genesis block
  15. var HEIGHT_TO_BLOCK_ID = []byte("HEIGHT_TO_BLOCK_ID") // stores block height to block id mapping
  16. var BLOCK_ID_TO_HEIGHT = []byte("BLOCK_ID_TO_HEIGHT") // stores block id to height mapping
  17. 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
  18. // all orphan ids are stored here, it id exists its orphan,
  19. // once a block is adden to orphan list it cannot be removed, since we have a better block
  20. var ORPHAN = []byte("ORPHAN")
  21. var ORPHAN_HEIGHT = []byte("ORPHAN_HEIGHT") // height wise orphans are store here
  22. var OO_ID = []byte{0x4} // mapping of incremental numbers to respective transaction ID
  23. var ALTERNATIVE_BLOCKS_ID = []byte{0x5} // each block contains lists of alternative child blocks
  24. var BLOCKCHAIN_UNIVERSE = []byte("BLOCKCHAIN_UNIVERSE") // all block chain data is store in this BLOCKCHAIN_UNIVERSE
  25. // there are only 3 galaxies
  26. var GALAXY_BLOCK = []byte("BLOCK")
  27. var GALAXY_TRANSACTION = []byte("TRANSACTION")
  28. var GALAXY_KEYIMAGE = []byte("KEYIMAGE")
  29. //2 galaxies store inverse mapping
  30. var GALAXY_HEIGHT = []byte("HEIGHT") // height to block id mapping
  31. var GALAXY_OUTPUT_INDEX = []byte("OUTPUT_INDEX") // incremental index over output index
  32. // the unique TXID or block ID becomes the solar system , which is common and saves lot of space
  33. // individual attributes becomes the planets
  34. // 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
  35. var PLANET_BLOB = []byte("BLOB") //it shows serialised block
  36. var PLANET_HEIGHT = []byte("HEIGHT") // contains height
  37. var PLANET_PARENT = []byte("PARENT") // parent of block
  38. var PLANET_SIZE = []byte("SIZE") // sum of block + all txs
  39. var PLANET_ALREADY_GENERATED_COINS = []byte("CCOINS") // all coins generated till this block
  40. var PLANET_OUTPUT_INDEX = []byte("OUTPUT_INDEX") // tx outputs indexing starts from here for this block
  41. var PLANET_CUMULATIVE_DIFFICULTY = []byte("CDIFFICULTY")
  42. var PLANET_CHILD = []byte("CHILD")
  43. //var PLANET_ORPHAN = []byte("ORPHAN")
  44. var PLANET_TIMESTAMP = []byte("TIMESTAMP")
  45. // this ill only be present if more tahn 1 child exists
  46. var PLANET_CHILDREN = []byte("CHILREN") // children list excludes the main child, so its a multiple of 32
  47. // the TX has the following attributes
  48. var PLANET_TX_BLOB = []byte("BLOB") // contains serialised TX , this attribute is also found in BLOCK where
  49. var PLANET_TX_MINED_IN_BLOCK = []byte("MINERBLOCK") // which block mined this tx
  50. var PLANET_TX_SIZE = []byte("SIZE")
  51. // the universe concept is there, as we bring in smart contracts, we will give each of them a universe to play within
  52. // while communicating with external universe
  53. /*
  54. func (chain *Blockchain) Store_Main_Chain(parent_id crypto.Hash, child_id crypto.Hash){
  55. err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE,GALAXY_BLOCK,parent_id[:],PLANET_CHILD, child_id[:] )
  56. _ = err
  57. }
  58. func (chain *Blockchain) Load_Main_Chain(parent_id crypto.Hash) (child_id crypto.Hash ){
  59. var err error
  60. // store OO to TXID automatically
  61. object_data,err = chain.store.LoadObject(BLOCKCHAIN_UNIVERSE,GALAXY_BLOCK,parent_id[:],PLANET_CHILD )
  62. if err != nil {
  63. return child_id,err
  64. }
  65. if len(object_data) == 0 {
  66. return child_id, fmt.Errorf("No Block at such Height %d", Height)
  67. }
  68. if len(object_data) != 32 {
  69. panic("Database corruption, invalid block hash ")
  70. }
  71. copy(child_id[:],object_data[:32])
  72. _ = err
  73. return child_id
  74. }
  75. */
  76. // check whether the block has a child
  77. func (chain *Blockchain) Does_Block_Have_Child(block_id crypto.Hash) bool {
  78. var err error
  79. object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, block_id[:], PLANET_CHILD)
  80. if err != nil || len(object_data) == 0 {
  81. return false
  82. }
  83. if len(object_data) != 32 {
  84. panic("Database corruption, invalid block hash ")
  85. }
  86. return true
  87. }
  88. // load the main child
  89. func (chain *Blockchain) Load_Block_Child(parent_id crypto.Hash) (child_id crypto.Hash) {
  90. if !chain.Does_Block_Have_Child(parent_id) {
  91. panic("Block does not have a child")
  92. }
  93. object_data, _ := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, parent_id[:], PLANET_CHILD)
  94. copy(child_id[:], object_data)
  95. return
  96. }
  97. // changes or set child block of a parent
  98. // there can be only 1 child, rest all are alternatives and stored as
  99. func (chain *Blockchain) Store_Block_Child(parent_id crypto.Hash, child_id crypto.Hash) {
  100. err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, parent_id[:], PLANET_CHILD, child_id[:])
  101. // load block children
  102. _ = err
  103. }
  104. // while store children
  105. func (chain *Blockchain) Store_Block_Children(parent_id crypto.Hash, children []crypto.Hash, exclude_child crypto.Hash) {
  106. var children_bytes []byte
  107. for i := range children {
  108. if children[i] != exclude_child { // exclude main child
  109. children_bytes = append(children_bytes, children[i][:]...)
  110. }
  111. }
  112. err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, parent_id[:], PLANET_CHILDREN, children_bytes)
  113. _ = err
  114. }
  115. func (chain *Blockchain) Load_Block_Children(parent_id crypto.Hash) (children []crypto.Hash) {
  116. var child_hash crypto.Hash
  117. if !chain.Does_Block_Have_Child(parent_id) { // block doesnot have a child, so it cannot have children
  118. return
  119. }
  120. // we are here means parent does have child
  121. children = append(children, chain.Load_Block_Child(parent_id))
  122. // check for children
  123. children_bytes, _ := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, parent_id[:], PLANET_CHILDREN)
  124. if len(children_bytes)%32 != 0 {
  125. panic(fmt.Sprintf("parent does not have child hash in multiples of 32, block_hash %x", parent_id))
  126. }
  127. for i := 0; i < len(children_bytes); i = i + 32 {
  128. copy(child_hash[:], children_bytes[i:i+32])
  129. children = append(children, child_hash)
  130. }
  131. return children
  132. }
  133. // store a tx
  134. // this only occurs when a tx has been mined
  135. func (chain *Blockchain) Store_TX(tx *Transaction) {
  136. hash := tx.GetHash()
  137. serialized := tx.Serialize()
  138. err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, hash[:], PLANET_TX_BLOB, serialized)
  139. // store size of tx
  140. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, hash[:], PLANET_TX_SIZE, uint64(len(serialized)))
  141. _ = err
  142. }
  143. func (chain *Blockchain) Store_TX_Miner(txhash crypto.Hash, block_id crypto.Hash) {
  144. // store block id which mined this tx
  145. err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, txhash[:], PLANET_TX_MINED_IN_BLOCK, block_id[:])
  146. _ = err
  147. }
  148. func (chain *Blockchain) Load_TX_Size(txhash crypto.Hash) uint64 {
  149. // store block id which mined this tx
  150. size, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, txhash[:], PLANET_TX_SIZE)
  151. if err != nil {
  152. logger.Warnf("Size not stored for tx %x", txhash)
  153. }
  154. return size
  155. }
  156. // BUG we should be able to delete any arbitrary key
  157. // since a tx mined by one block, can be back in pool after chain reorganises
  158. // TODO the miner tx should be extracted ands stored from somewhere else
  159. // NOTE: before storing a block, its transactions must be stored
  160. func (chain *Blockchain) Store_BL(bl *Block) {
  161. // store block height BHID automatically
  162. hash := bl.GetHash()
  163. // we should deserialize the block here
  164. serialized_bytes := bl.Serialize() // we are storing the miner transactions within
  165. err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_BLOB, serialized_bytes)
  166. // get height of parent, add 1 and store it
  167. height := uint64(0)
  168. if hash != globals.Config.Genesis_Block_Hash { // genesis block has no parent
  169. height = chain.Load_Height_for_BL_ID(bl.Prev_Hash)
  170. height++
  171. }
  172. // store new height
  173. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_HEIGHT, height)
  174. // store timestamp
  175. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_TIMESTAMP, bl.Timestamp)
  176. // store parent
  177. chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_PARENT, bl.Prev_Hash[:])
  178. // calculate cumulative difficulty at last block
  179. difficulty_of_current_block := uint64(0)
  180. cumulative_difficulty := uint64(0)
  181. if hash != globals.Config.Genesis_Block_Hash { // genesis block has no parent
  182. cumulative_difficulty = chain.Load_Block_Cumulative_Difficulty(bl.Prev_Hash)
  183. difficulty_of_current_block = chain.Get_Difficulty_At_Block(bl.Prev_Hash)
  184. } else {
  185. cumulative_difficulty = 1 // genesis block cumulative difficulty is 1
  186. }
  187. total_difficulty := cumulative_difficulty + difficulty_of_current_block
  188. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_CUMULATIVE_DIFFICULTY, total_difficulty)
  189. // total size of block = size of block + size of all transactions in block ( excludind miner tx)
  190. size_of_block := uint64(len(serialized_bytes))
  191. for i := 0; i < len(bl.Tx_hashes); i++ {
  192. size_of_tx := chain.Load_TX_Size(bl.Tx_hashes[i])
  193. size_of_block += size_of_tx
  194. }
  195. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_SIZE, size_of_block)
  196. // calculated position of vouts in global index
  197. /* TODO below code has been disabled and should be enabled for extensive testing
  198. index_pos := uint64(0)
  199. if hash != globals.Config.Genesis_Block_Hash {
  200. // load index pos from last block + add count of vouts from last block
  201. index_pos = chain.Get_Block_Output_Index(bl.Prev_Hash)
  202. vout_count_prev_block := chain.Block_Count_Vout(bl.Prev_Hash)
  203. index_pos += vout_count_prev_block
  204. }
  205. chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_OUTPUT_INDEX, index_pos)
  206. logger.Debugf("height %d output index %d\n",height, index_pos)
  207. */
  208. // TODO calculate total coins emitted till this block
  209. // also extract and store the miner tx separetly, fr direct querying purpose
  210. chain.Store_TX(&bl.Miner_tx)
  211. _ = err
  212. }
  213. func (chain *Blockchain) Load_TX_FROM_ID(hash [32]byte) (*Transaction, error) {
  214. var tx Transaction
  215. tx_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, hash[:], PLANET_TX_BLOB)
  216. if err != nil {
  217. return nil, err
  218. }
  219. // we should deserialize the block here
  220. err = tx.DeserializeHeader(tx_data)
  221. if err != nil {
  222. logger.Printf("fError deserialiing tx, block id %x len(data) %d data %x\n", hash[:], len(tx_data), tx_data)
  223. return nil, err
  224. }
  225. return &tx, nil
  226. }
  227. func (chain *Blockchain) Load_TX_FROM_OO(Offset uint64) {
  228. }
  229. func (chain *Blockchain) Load_BL_FROM_ID(hash [32]byte) (*Block, error) {
  230. var bl Block
  231. block_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_BLOB)
  232. if err != nil {
  233. return nil, err
  234. }
  235. if len(block_data) == 0 {
  236. return nil, fmt.Errorf("Block not found in DB")
  237. }
  238. // we should deserialize the block here
  239. err = bl.Deserialize(block_data)
  240. if err != nil {
  241. logger.Warnf("fError deserialiing block, block id %x len(data) %d data %x\n", hash[:], len(block_data), block_data)
  242. return nil, err
  243. }
  244. return &bl, nil
  245. }
  246. // this will give you a block id at a specific height
  247. func (chain *Blockchain) Load_BL_ID_at_Height(Height uint64) (hash crypto.Hash, err error) {
  248. object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_HEIGHT, PLANET_HEIGHT, itob(Height))
  249. if err != nil {
  250. return hash, err
  251. }
  252. if len(object_data) == 0 {
  253. return hash, fmt.Errorf("No Block at such Height %d", Height)
  254. }
  255. if len(object_data) != 32 {
  256. panic("Database corruption, invalid block hash ")
  257. }
  258. copy(hash[:], object_data[:32])
  259. return hash, nil
  260. }
  261. // this will give you a block id at a specific height
  262. func (chain *Blockchain) Store_BL_ID_at_Height(Height uint64, hash crypto.Hash) {
  263. // store height to block id mapping
  264. chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_HEIGHT, PLANET_HEIGHT, itob(Height), hash[:])
  265. }
  266. func (chain *Blockchain) Load_Height_for_BL_ID(hash crypto.Hash) (Height uint64) {
  267. object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_HEIGHT)
  268. if err != nil {
  269. logger.Warnf("Error while querying height for block %x\n", hash)
  270. return
  271. }
  272. if len(object_data) == 0 {
  273. //return hash, fmt.Errorf("No Height for block %x", hash[:])
  274. return
  275. }
  276. if len(object_data) != 8 {
  277. panic("Database corruption, invalid block hash ")
  278. }
  279. Height = binary.BigEndian.Uint64(object_data)
  280. return Height
  281. }
  282. func (chain *Blockchain) Load_Block_Timestamp(hash crypto.Hash) uint64 {
  283. timestamp, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_TIMESTAMP)
  284. if err != nil {
  285. logger.Fatalf("Error while querying timestamp for block %x\n", hash)
  286. panic("Error while querying timestamp for block")
  287. }
  288. return timestamp
  289. }
  290. func (chain *Blockchain) Load_Block_Cumulative_Difficulty(hash crypto.Hash) uint64 {
  291. cdifficulty, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_CUMULATIVE_DIFFICULTY)
  292. if err != nil {
  293. logger.Panicf("Error while querying cumulative difficulty for block %x\n", hash)
  294. }
  295. return cdifficulty
  296. }
  297. func (chain *Blockchain) Load_Block_Parent_ID(hash crypto.Hash) crypto.Hash {
  298. var parent_id crypto.Hash
  299. object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_PARENT)
  300. if err != nil || len(object_data) != 32 {
  301. logger.Panicf("Error while querying parent id for block %x\n", hash)
  302. }
  303. copy(parent_id[:], object_data)
  304. return parent_id
  305. }
  306. // store current top id
  307. func (chain *Blockchain) Store_TOP_ID(hash crypto.Hash) {
  308. chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, TOP_ID, TOP_ID, TOP_ID, hash[:])
  309. }
  310. func (chain *Blockchain) Load_TOP_ID() (hash crypto.Hash) {
  311. object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, TOP_ID, TOP_ID, TOP_ID)
  312. if err != nil {
  313. panic("Backend failure")
  314. }
  315. if len(object_data) == 0 {
  316. panic(fmt.Errorf("most probably Database corruption, No TOP_ID stored "))
  317. }
  318. if len(object_data) != 32 {
  319. panic("Database corruption, invalid block hash ")
  320. }
  321. copy(hash[:], object_data[:32])
  322. return hash
  323. }
  324. // itob returns an 8-byte big endian representation of v.
  325. func itob(v uint64) []byte {
  326. b := make([]byte, 8)
  327. binary.BigEndian.PutUint64(b, uint64(v))
  328. return b
  329. }
  330. // get the position from where indexing must start for this block
  331. // indexing mean vout based index
  332. // cryptonote works by giving each vout a unique index
  333. func (chain *Blockchain)Get_Block_Output_Index(block_id crypto.Hash) uint64 {
  334. if block_id == globals.Config.Genesis_Block_Hash { // genesis block has no output index
  335. return 0 ; // counting starts from zero
  336. }
  337. index, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, block_id[:], PLANET_OUTPUT_INDEX)
  338. if err != nil {
  339. // TODO this panic must be enabled to catch some bugs
  340. panic(fmt.Errorf("Cannot load output index for %x err %s", block_id, err))
  341. return 0
  342. }
  343. return index
  344. }