github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/block/block.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 block
    18  
    19  import "fmt"
    20  
    21  //import "sort"
    22  import "bytes"
    23  import "runtime/debug"
    24  import "encoding/hex"
    25  import "encoding/binary"
    26  
    27  import "github.com/ebfe/keccak"
    28  import "github.com/romana/rlog"
    29  
    30  import "github.com/deroproject/derosuite/crypto"
    31  
    32  //import "github.com/deroproject/derosuite/config"
    33  import "github.com/deroproject/derosuite/astrobwt"
    34  import "github.com/deroproject/derosuite/cryptonight"
    35  import "github.com/deroproject/derosuite/transaction"
    36  
    37  // these are defined  in file
    38  //https://github.com/monero-project/monero/src/cryptonote_basic/cryptonote_basic.h
    39  type Block_Header struct {
    40  	Major_Version uint64                  `json:"major_version"`
    41  	Minor_Version uint64                  `json:"minor_version"`
    42  	Timestamp     uint64                  `json:"timestamp"`
    43  	Nonce         uint32                  `json:"nonce"` // TODO make nonce 32 byte array for infinite work capacity
    44  	ExtraNonce    [32]byte                `json:"-"`
    45  	Miner_TX      transaction.Transaction `json:"miner_tx"`
    46  }
    47  
    48  type Block struct {
    49  	Block_Header
    50  	Proof     [32]byte      `json:"-"` // Reserved for future
    51  	Tips      []crypto.Hash `json:"tips"`
    52  	Tx_hashes []crypto.Hash `json:"tx_hashes"`
    53  }
    54  
    55  // we process incoming blocks in this format
    56  type Complete_Block struct {
    57  	Bl  *Block
    58  	Txs []*transaction.Transaction
    59  }
    60  
    61  // see spec here https://cryptonote.org/cns/cns003.txt
    62  // this function gets the block identifier hash
    63  // this has been simplified and varint length has been removed
    64  func (bl *Block) GetHash() (hash crypto.Hash) {
    65  	long_header := bl.GetBlockWork()
    66  
    67  	// keccak hash of this above blob, gives the block id
    68  	return crypto.Keccak256(long_header)
    69  }
    70  
    71  // converts a block, into a getwork style work, ready for either submitting the block
    72  // or doing Pow Calculations
    73  func (bl *Block) GetBlockWork() []byte {
    74  
    75  	var buf []byte // bitcoin/litecoin getworks are 80 bytes
    76  	var scratch [8]byte
    77  
    78  	buf = append(buf, []byte{byte(bl.Major_Version), byte(bl.Minor_Version), 0, 0, 0, 0, 0}...) // 0 first 7 bytes are version in little endia format
    79  
    80  	binary.LittleEndian.PutUint32(buf[2:6], uint32(bl.Timestamp))
    81  	header_hash := crypto.Keccak256(bl.getserializedheaderforwork()) // 0 + 7
    82  
    83  	buf = append(buf, header_hash[:]...) // 0 + 7 + 32  = 39
    84  
    85  	binary.LittleEndian.PutUint32(scratch[0:4], bl.Nonce) // check whether it needs to be big endian
    86  	buf = append(buf, scratch[:4]...)                     // 0 + 7 + 32  + 4 = 43
    87  
    88  	// next place the ExtraNonce
    89  	buf = append(buf, bl.ExtraNonce[:]...) // total 7 + 32 + 4 + 32
    90  
    91  	buf = append(buf, 0) // total 7 + 32 + 4 + 32 + 1 = 76
    92  
    93  	if len(buf) != 76 {
    94  		panic(fmt.Sprintf("Getwork not equal to  76 bytes  actual %d", len(buf)))
    95  
    96  	}
    97  	return buf
    98  }
    99  
   100  // copy the nonce and the extra nonce from the getwork to the block
   101  func (bl *Block) CopyNonceFromBlockWork(work []byte) (err error) {
   102  	if len(work) < 74 {
   103  		return fmt.Errorf("work buffer is Invalid")
   104  	}
   105  
   106  	bl.Timestamp = uint64(binary.LittleEndian.Uint32(work[2:]))
   107  	bl.Nonce = binary.LittleEndian.Uint32(work[7+32:])
   108  	copy(bl.ExtraNonce[:], work[7+32+4:75])
   109  	return
   110  }
   111  
   112  // copy the nonce and the extra nonce from the getwork to the block
   113  func (bl *Block) SetExtraNonce(extranonce []byte) (err error) {
   114  
   115  	if len(extranonce) == 0 {
   116  		return fmt.Errorf("extra nonce is invalid")
   117  	}
   118  	max := len(extranonce)
   119  	if max > 32 {
   120  		max = 32
   121  	}
   122  	copy(bl.ExtraNonce[:], extranonce[0:max])
   123  	return
   124  }
   125  
   126  // clear extra nonce
   127  func (bl *Block) ClearExtraNonce() {
   128  	for i := range bl.ExtraNonce {
   129  		bl.ExtraNonce[i] = 0
   130  	}
   131  }
   132  
   133  // clear nonce
   134  func (bl *Block) ClearNonce() {
   135  	bl.Nonce = 0
   136  }
   137  
   138  // Get PoW hash , this is very slow function
   139  func (bl *Block) GetPoWHash() (hash crypto.Hash) {
   140  	long_header := bl.GetBlockWork()
   141  	rlog.Tracef(9, "longheader %x\n", long_header)
   142  
   143      if bl.Major_Version <=3 { // HF 1 to 3 use crypto night
   144  	    tmphash := cryptonight.SlowHash(long_header)
   145  	    copy(hash[:], tmphash[:32])
   146      }else{
   147          tmphash := astrobwt.POW_0alloc(long_header) // new astrobwt algorithm
   148  	    copy(hash[:], tmphash[:32])
   149      }
   150  
   151  	return
   152  }
   153  
   154  // serialize block header for calculating PoW
   155  func (bl *Block) getserializedheaderforwork() []byte {
   156  	var serialised bytes.Buffer
   157  
   158  	buf := make([]byte, binary.MaxVarintLen64)
   159  
   160  	n := binary.PutUvarint(buf, uint64(bl.Major_Version))
   161  	serialised.Write(buf[:n])
   162  
   163  	n = binary.PutUvarint(buf, uint64(bl.Minor_Version))
   164  	serialised.Write(buf[:n])
   165  
   166  	// it is placed in pow
   167  	//n = binary.PutUvarint(buf, bl.Timestamp)
   168  	//serialised.Write(buf[:n])
   169  
   170  	// write miner tx
   171  	serialised.Write(bl.Miner_TX.Serialize())
   172  
   173  	// write tips,, merkle tree should be replaced with something faster
   174  	tips_treehash := bl.GetTipsHash()
   175  	n = binary.PutUvarint(buf, uint64(len(tips_treehash)))
   176  	serialised.Write(buf[:n])
   177  	serialised.Write(tips_treehash[:]) // actual tips hash
   178  
   179  	tx_treehash := bl.GetTXSHash()                        // hash of all transactions
   180  	n = binary.PutUvarint(buf, uint64(len(bl.Tx_hashes))) // count of all transactions
   181  	serialised.Write(buf[:n])
   182  	serialised.Write(tx_treehash[:]) // actual transactions hash
   183  
   184  	return serialised.Bytes()
   185  }
   186  
   187  // serialize block header
   188  func (bl *Block) SerializeHeader() []byte {
   189  
   190  	var serialised bytes.Buffer
   191  
   192  	buf := make([]byte, binary.MaxVarintLen64)
   193  
   194  	n := binary.PutUvarint(buf, uint64(bl.Major_Version))
   195  	serialised.Write(buf[:n])
   196  
   197  	n = binary.PutUvarint(buf, uint64(bl.Minor_Version))
   198  	serialised.Write(buf[:n])
   199  
   200  	n = binary.PutUvarint(buf, bl.Timestamp)
   201  	serialised.Write(buf[:n])
   202  
   203  	binary.LittleEndian.PutUint32(buf[0:8], bl.Nonce) // check whether it needs to be big endian
   204  	serialised.Write(buf[:4])
   205  
   206  	serialised.Write(bl.ExtraNonce[:])
   207  
   208  	// write miner address
   209  	serialised.Write(bl.Miner_TX.Serialize())
   210  
   211  	return serialised.Bytes()
   212  
   213  }
   214  
   215  // serialize entire block ( block_header + miner_tx + tx_list )
   216  func (bl *Block) Serialize() []byte {
   217  	var serialized bytes.Buffer
   218  	buf := make([]byte, binary.MaxVarintLen64)
   219  
   220  	header := bl.SerializeHeader()
   221  	serialized.Write(header)
   222  
   223  	serialized.Write(bl.Proof[:]) // write proof  NOT implemented
   224  
   225  	// miner tx should always be coinbase
   226  	//minex_tx := bl.Miner_tx.Serialize()
   227  	//serialized.Write(minex_tx)
   228  
   229  	n := binary.PutUvarint(buf, uint64(len(bl.Tips)))
   230  	serialized.Write(buf[:n])
   231  
   232  	for _, hash := range bl.Tips {
   233  		serialized.Write(hash[:])
   234  	}
   235  
   236  	//fmt.Printf("serializing tx hashes %d\n", len(bl.Tx_hashes))
   237  
   238  	n = binary.PutUvarint(buf, uint64(len(bl.Tx_hashes)))
   239  	serialized.Write(buf[:n])
   240  
   241  	for _, hash := range bl.Tx_hashes {
   242  		serialized.Write(hash[:])
   243  	}
   244  
   245  	return serialized.Bytes()
   246  }
   247  
   248  // get block transactions tree hash
   249  func (bl *Block) GetTipsHash() (result crypto.Hash) {
   250  
   251  	/*if len(bl.Tips) == 0 { // case for genesis block
   252  	  panic("Block does NOT refer any tips")
   253  	 }*/
   254  
   255  	// add all the remaining hashes
   256  	h := keccak.New256()
   257  	for i := range bl.Tips {
   258  		h.Write(bl.Tips[i][:])
   259  	}
   260  	r := h.Sum(nil)
   261  	copy(result[:], r)
   262  	return
   263  }
   264  
   265  // get block transactions
   266  // we have discarded the merkle tree and have shifted to a plain version
   267  func (bl *Block) GetTXSHash() (result crypto.Hash) {
   268  	h := keccak.New256()
   269  	for i := range bl.Tx_hashes {
   270  		h.Write(bl.Tx_hashes[i][:])
   271  	}
   272  	r := h.Sum(nil)
   273  	copy(result[:], r)
   274  
   275  	return
   276  }
   277  
   278  // only parses header
   279  func (bl *Block) DeserializeHeader(buf []byte) (err error) {
   280  	done := 0
   281  	bl.Major_Version, done = binary.Uvarint(buf)
   282  	if done <= 0 {
   283  		return fmt.Errorf("Invalid Major Version in Block\n")
   284  	}
   285  	buf = buf[done:]
   286  
   287  	bl.Minor_Version, done = binary.Uvarint(buf)
   288  	if done <= 0 {
   289  		return fmt.Errorf("Invalid Minor Version in Block\n")
   290  	}
   291  	buf = buf[done:]
   292  
   293  	bl.Timestamp, done = binary.Uvarint(buf)
   294  	if done <= 0 {
   295  		return fmt.Errorf("Invalid Timestamp in Block\n")
   296  	}
   297  	buf = buf[done:]
   298  
   299  	//copy(bl.Prev_Hash[:], buf[:32]) // hash is always 32 byte
   300  	//buf = buf[32:]
   301  
   302  	bl.Nonce = binary.LittleEndian.Uint32(buf)
   303  
   304  	buf = buf[4:]
   305  
   306  	copy(bl.ExtraNonce[:], buf[0:32])
   307  
   308  	buf = buf[32:]
   309  
   310  	// parse miner tx
   311  	err = bl.Miner_TX.DeserializeHeader(buf)
   312  	if err != nil {
   313  		return err
   314  	}
   315  
   316  	return
   317  }
   318  
   319  //parse entire block completely
   320  func (bl *Block) Deserialize(buf []byte) (err error) {
   321  	done := 0
   322  
   323  	defer func() {
   324  		if r := recover(); r != nil {
   325  			fmt.Printf("Panic while deserialising block, block hex_dump below to make a testcase/debug\n")
   326  			fmt.Printf("%s\n", hex.EncodeToString(buf))
   327  
   328  			fmt.Printf("Recovered while parsing block, Stack trace below block_hash ")
   329  			fmt.Printf("Stack trace  \n%s", debug.Stack())
   330  			err = fmt.Errorf("Invalid Block")
   331  			return
   332  		}
   333  	}()
   334  
   335  	err = bl.DeserializeHeader(buf)
   336  	if err != nil {
   337  		return fmt.Errorf("Block Header could not be parsed\n")
   338  	}
   339  
   340  	buf = buf[len(bl.SerializeHeader()):] // skup number of bytes processed
   341  
   342  	// read 32 byte proof
   343  	copy(bl.Proof[:], buf[0:32])
   344  	buf = buf[32:]
   345  
   346  	// header finished here
   347  
   348  	// read and parse transaction
   349  	/*err = bl.Miner_tx.DeserializeHeader(buf)
   350  
   351  	if err != nil {
   352  		return fmt.Errorf("Cannot parse miner TX  %x", buf)
   353  	}
   354  
   355  	// if tx was parse, make sure it's coin base
   356  	if len(bl.Miner_tx.Vin) != 1 || bl.Miner_tx.Vin[0].(transaction.Txin_gen).Height > config.MAX_CHAIN_HEIGHT {
   357  		// serialize transaction again to get the tx size, so as parsing could continue
   358  		return fmt.Errorf("Invalid Miner TX")
   359  	}
   360  
   361  	miner_tx_serialized_size := bl.Miner_tx.Serialize()
   362  	buf = buf[len(miner_tx_serialized_size):]
   363  	*/
   364  
   365  	tips_count, done := binary.Uvarint(buf)
   366  	if done <= 0 || done > 1 {
   367  		return fmt.Errorf("Invalid Tips count in Block\n")
   368  	}
   369  	buf = buf[done:]
   370  
   371  	// remember first tx is merkle root
   372  
   373  	for i := uint64(0); i < tips_count; i++ {
   374  		//fmt.Printf("Parsing transaction hash %d  tx_count %d\n", i, tx_count)
   375  		var h crypto.Hash
   376  		copy(h[:], buf[:32])
   377  		buf = buf[32:]
   378  
   379  		bl.Tips = append(bl.Tips, h)
   380  
   381  	}
   382  
   383  	//fmt.Printf("miner tx %x\n", miner_tx_serialized_size)
   384  	// read number of transactions
   385  	tx_count, done := binary.Uvarint(buf)
   386  	if done <= 0 {
   387  		return fmt.Errorf("Invalid Tx count in Block\n")
   388  	}
   389  	buf = buf[done:]
   390  
   391  	// remember first tx is merkle root
   392  
   393  	for i := uint64(0); i < tx_count; i++ {
   394  		//fmt.Printf("Parsing transaction hash %d  tx_count %d\n", i, tx_count)
   395  		var h crypto.Hash
   396  		copy(h[:], buf[:32])
   397  		buf = buf[32:]
   398  
   399  		bl.Tx_hashes = append(bl.Tx_hashes, h)
   400  
   401  	}
   402  
   403  	//fmt.Printf("%d member in tx hashes \n",len(bl.Tx_hashes))
   404  
   405  	return
   406  
   407  }