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.

313 lines
7.6 KiB

  1. package blockchain
  2. import "fmt"
  3. import "bytes"
  4. import "encoding/hex"
  5. import "encoding/binary"
  6. import "github.com/romana/rlog"
  7. import "github.com/deroproject/derosuite/crypto"
  8. import "github.com/deroproject/derosuite/config"
  9. import "github.com/deroproject/derosuite/cryptonight"
  10. // these are defined in file
  11. //https://github.com/monero-project/monero/src/cryptonote_basic/cryptonote_basic.h
  12. type Block_Header struct {
  13. Major_Version uint32 `json:"major_version"`
  14. Minor_Version uint32 `json:"minor_version"`
  15. Timestamp uint64 `json:"timestamp"`
  16. Prev_Hash crypto.Hash `json:"prev_id"`
  17. Nonce uint32 `json:"nonce"`
  18. }
  19. type Block struct {
  20. Block_Header
  21. Miner_tx Transaction `json:"miner_tx"`
  22. Merkle_Root crypto.Hash `json:"-"`
  23. Tx_hashes []crypto.Hash `json:"tx_hashes"`
  24. treehash crypto.Hash
  25. }
  26. // see spec here https://cryptonote.org/cns/cns003.txt
  27. // this function gets the block identifier hash
  28. func (bl *Block) GetHash() (hash crypto.Hash) {
  29. buf := make([]byte, binary.MaxVarintLen64)
  30. long_header := bl.GetBlockWork()
  31. length := uint64(len(long_header))
  32. n := binary.PutUvarint(buf, length) //
  33. buf = buf[:n]
  34. block_id_blob := append(buf, long_header...)
  35. // keccak hash of this above blob, gives the block id
  36. hash2 := crypto.Keccak256(block_id_blob)
  37. return crypto.Hash(hash2)
  38. }
  39. // converts a block, into a getwork style work, ready for either submitting the block
  40. // or doing Pow Calculations
  41. func (bl *Block) GetBlockWork() []byte {
  42. buf := make([]byte, binary.MaxVarintLen64)
  43. header := bl.SerializeHeader()
  44. tx_treehash := bl.GetTreeHash() // treehash of all transactions
  45. // length of all transactions
  46. n := binary.PutUvarint(buf, uint64(len(bl.Tx_hashes)+1)) // +1 for miner TX
  47. buf = buf[:n]
  48. long_header := append(header, tx_treehash[:]...)
  49. long_header = append(long_header, buf...)
  50. return long_header
  51. }
  52. // Get PoW hash , this is very slow function
  53. func (bl *Block) GetPoWHash() (hash crypto.Hash) {
  54. long_header := bl.GetBlockWork()
  55. rlog.Tracef(9, "longheader %x\n", long_header)
  56. tmphash := cryptonight.SlowHash(long_header)
  57. copy(hash[:], tmphash[:32])
  58. return
  59. }
  60. // Reward is, total amount in the miner tx - fees
  61. func (bl *Block) GetReward() uint64 {
  62. total_amount := bl.Miner_tx.Vout[0].Amount
  63. total_fees := uint64(0)
  64. // load all the TX and get the fees, since we are in a post rct world
  65. // extract the fees from the rct sig
  66. return total_amount - total_fees
  67. }
  68. // serialize block header
  69. func (bl *Block) SerializeHeader() []byte {
  70. var serialised bytes.Buffer
  71. buf := make([]byte, binary.MaxVarintLen64)
  72. n := binary.PutUvarint(buf, uint64(bl.Major_Version))
  73. serialised.Write(buf[:n])
  74. n = binary.PutUvarint(buf, uint64(bl.Minor_Version))
  75. serialised.Write(buf[:n])
  76. n = binary.PutUvarint(buf, bl.Timestamp)
  77. serialised.Write(buf[:n])
  78. serialised.Write(bl.Prev_Hash[:32]) // write previous ID
  79. binary.LittleEndian.PutUint32(buf[0:8], bl.Nonce) // check whether it needs to be big endian
  80. serialised.Write(buf[:4])
  81. return serialised.Bytes()
  82. }
  83. // serialize entire block ( block_header + miner_tx + tx_list )
  84. func (bl *Block) Serialize() []byte {
  85. var serialized bytes.Buffer
  86. buf := make([]byte, binary.MaxVarintLen64)
  87. header := bl.SerializeHeader()
  88. serialized.Write(header)
  89. // miner tx should always be coinbase
  90. minex_tx := bl.Miner_tx.Serialize()
  91. serialized.Write(minex_tx)
  92. //fmt.Printf("serializing tx hashes %d\n", len(bl.Tx_hashes))
  93. n := binary.PutUvarint(buf, uint64(len(bl.Tx_hashes)))
  94. serialized.Write(buf[:n])
  95. for _, hash := range bl.Tx_hashes {
  96. serialized.Write(hash[:])
  97. }
  98. return serialized.Bytes()
  99. }
  100. // get block transactions tree hash
  101. func (bl *Block) GetTreeHash() (hash crypto.Hash) {
  102. var hash_list []crypto.Hash
  103. hash_list = append(hash_list, bl.Miner_tx.GetHash())
  104. // add all the remaining hashes
  105. for i := range bl.Tx_hashes {
  106. hash_list = append(hash_list, bl.Tx_hashes[i])
  107. }
  108. return TreeHash(hash_list)
  109. }
  110. // input is the list of transactions hashes
  111. func TreeHash(hashes []crypto.Hash) (hash crypto.Hash) {
  112. switch len(hashes) {
  113. case 0:
  114. panic("Treehash cannot have 0 transactions, atleast miner tx will be present")
  115. case 1:
  116. copy(hash[:], hashes[0][:32])
  117. case 2:
  118. var buf []byte
  119. for i := 0; i < len(hashes); i++ {
  120. buf = append(buf, hashes[i][:32]...)
  121. }
  122. tmp_hash := crypto.Keccak256(buf)
  123. copy(hash[:], tmp_hash[:32])
  124. default:
  125. count := uint64(len(hashes))
  126. cnt := tree_hash_cnt(count)
  127. //fmt.Printf("cnt %d count %d\n",cnt, count)
  128. ints := make([]byte, 32*cnt, 32*cnt)
  129. hashes_buf := make([]byte, 32*count, 32*count)
  130. for i := uint64(0); i < count; i++ {
  131. copy(hashes_buf[i*32:], hashes[i][:32]) // copy hashes 1 by 1
  132. }
  133. for i := uint64(0); i < ((2 * cnt) - count); i++ {
  134. copy(ints[i*32:], hashes[i][:32]) // copy hashes 1 by 1
  135. }
  136. i := ((2 * cnt) - count)
  137. j := ((2 * cnt) - count)
  138. for ; j < cnt; i, j = i+2, j+1 {
  139. hash := crypto.Keccak256(hashes_buf[i*32 : (i*32)+64]) // find hash of 64 bytes
  140. copy(ints[j*32:], hash[:32])
  141. }
  142. if i != count {
  143. panic("please fix tree hash")
  144. }
  145. for cnt > 2 {
  146. cnt = cnt >> 1
  147. i = 0
  148. j = 0
  149. for ; j < cnt; i, j = i+2, j+1 {
  150. hash := crypto.Keccak256(ints[i*32 : (i*32)+64]) // find hash of 64 bytes
  151. copy(ints[j*32:], hash[:32])
  152. }
  153. }
  154. hash = crypto.Hash(crypto.Keccak256(ints[0:64])) // find hash of 64 bytes
  155. }
  156. return
  157. }
  158. // see crypto/tree-hash.c
  159. // this function has a naughty history
  160. func tree_hash_cnt(count uint64) uint64 {
  161. pow := uint64(2)
  162. for pow < count {
  163. pow = pow << 1
  164. }
  165. return pow >> 1
  166. }
  167. func (bl *Block) Deserialize(buf []byte) (err error) {
  168. done := 0
  169. var tmp uint64
  170. defer func() {
  171. if r := recover(); r != nil {
  172. logger.Warnf("Panic while deserialising block, block hex_dump below to make a testcase/debug\n")
  173. logger.Warnf("%s", hex.EncodeToString(buf))
  174. err = fmt.Errorf("Invalid Block")
  175. return
  176. }
  177. }()
  178. tmp, done = binary.Uvarint(buf)
  179. if done <= 0 {
  180. return fmt.Errorf("Invalid Version in Block\n")
  181. }
  182. buf = buf[done:]
  183. bl.Major_Version = uint32(tmp)
  184. if uint64(bl.Major_Version) != tmp {
  185. return fmt.Errorf("Invalid Block major version")
  186. }
  187. tmp, done = binary.Uvarint(buf)
  188. if done <= 0 {
  189. return fmt.Errorf("Invalid minor Version in Block\n")
  190. }
  191. buf = buf[done:]
  192. bl.Minor_Version = uint32(tmp)
  193. if uint64(bl.Minor_Version) != tmp {
  194. return fmt.Errorf("Invalid Block minor version")
  195. }
  196. bl.Timestamp, done = binary.Uvarint(buf)
  197. if done <= 0 {
  198. return fmt.Errorf("Invalid Timestamp in Block\n")
  199. }
  200. buf = buf[done:]
  201. copy(bl.Prev_Hash[:], buf[:32]) // hash is always 32 byte
  202. buf = buf[32:]
  203. bl.Nonce = binary.LittleEndian.Uint32(buf)
  204. buf = buf[4:] // nonce is always 4 bytes
  205. // read and parse transaction
  206. err = bl.Miner_tx.DeserializeHeader(buf)
  207. if err != nil {
  208. return fmt.Errorf("Cannot parse miner TX %x", buf)
  209. }
  210. // if tx was parse, make sure it's coin base
  211. if len(bl.Miner_tx.Vin) != 1 || bl.Miner_tx.Vin[0].(Txin_gen).Height > config.MAX_CHAIN_HEIGHT {
  212. // serialize transaction again to get the tx size, so as parsing could continue
  213. return fmt.Errorf("Invalid Miner TX")
  214. }
  215. miner_tx_serialized_size := bl.Miner_tx.Serialize()
  216. buf = buf[len(miner_tx_serialized_size):]
  217. //fmt.Printf("miner tx %x\n", miner_tx_serialized_size)
  218. // read number of transactions
  219. tx_count, done := binary.Uvarint(buf)
  220. if done <= 0 {
  221. return fmt.Errorf("Invalid Tx count in Block\n")
  222. }
  223. buf = buf[done:]
  224. // remember first tx is merkle root
  225. for i := uint64(0); i < tx_count; i++ {
  226. //fmt.Printf("Parsing transaction hash %d tx_count %d\n", i, tx_count)
  227. var h crypto.Hash
  228. copy(h[:], buf[:32])
  229. buf = buf[32:]
  230. bl.Tx_hashes = append(bl.Tx_hashes, h)
  231. }
  232. //fmt.Printf("%d member in tx hashes \n",len(bl.Tx_hashes))
  233. return
  234. }