github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/transaction/transaction_extra.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 transaction
    18  
    19  //import "fmt"
    20  import "bytes"
    21  
    22  //import "runtime/debug"
    23  
    24  //import "encoding/binary"
    25  
    26  import "github.com/romana/rlog"
    27  
    28  import "github.com/deroproject/derosuite/crypto"
    29  
    30  // refer https://cryptonote.org/cns/cns005.txt to understand slightly more ( it DOES NOT cover everything)
    31  // much of these constants are understood from tx_extra.h and cryptonote_format_utils.cpp
    32  // TODO pending test case
    33  type EXTRA_TAG byte
    34  
    35  const TX_EXTRA_PADDING EXTRA_TAG = 0 // followed by 1 byte of size, and then upto 255 bytes of padding
    36  const TX_PUBLIC_KEY EXTRA_TAG = 1    // follwed by 32 bytes of tx public key
    37  const TX_EXTRA_NONCE EXTRA_TAG = 2   // followed by 1 byte of size, and then upto 255 bytes of empty nonce
    38  
    39  // TX_EXTRA_MERGE_MINING_TAG  we do NOT suppport merged mining at all
    40  // TX_EXTRA_MYSTERIOUS_MINERGATE_TAG  as the name says mysterious we will not bring it
    41  
    42  // these 2 fields have complicated parsing of extra, other the code was really simple
    43  const TX_EXTRA_NONCE_PAYMENT_ID EXTRA_TAG = 0           // extra nonce within a non coinbase tx, can be unencrypted, is 32 bytes in size
    44  const TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID EXTRA_TAG = 1 // this is encrypted and is 9 bytes in size
    45  
    46  // the field is just named extra and contains CRITICAL information, though some is optional
    47  // parse extra data such as
    48  // tx public key must
    49  // payment id optional
    50  // encrypted payment id optional
    51  func (tx *Transaction) Parse_Extra() (result bool) {
    52  	var err error
    53  	// var length uint64
    54  	var length_int int
    55  
    56  	/*defer func (){
    57  		if r := recover(); r != nil {
    58  				fmt.Printf("Recovered while parsing extra, Stack trace below block_hash %s", tx.GetHash())
    59  				fmt.Printf("Stack trace  \n%s", debug.Stack())
    60  				result = false
    61  			}
    62  	        }()*/
    63  
    64  	buf := bytes.NewReader(tx.Extra)
    65  
    66  	tx.Extra_map = map[EXTRA_TAG]interface{}{}
    67  	tx.PaymentID_map = map[EXTRA_TAG]interface{}{}
    68  
    69  	b := make([]byte, 1)
    70  	//var r uint64
    71  	var n int
    72  	for i := 0; ; i++ {
    73  		if buf.Len() == 0 {
    74  			return true
    75  		}
    76  		n, err = buf.Read(b)
    77  		//if err != nil { // we make that the buffer has atleast 1 byte to read
    78  		//	return false
    79  		//}
    80  
    81  		switch EXTRA_TAG(b[0]) {
    82  		case TX_EXTRA_PADDING: // this is followed by 1 byte length, then length bytes of padding
    83  			n, err = buf.Read(b)
    84  			if err != nil {
    85  				rlog.Tracef(1, "Extra padding length could not be parsed")
    86  				return false
    87  			}
    88  			length_int = int(b[0])
    89  			padding := make([]byte, length_int, length_int)
    90  			n, err = buf.Read(padding)
    91  			if err != nil || n != int(length_int) {
    92  				rlog.Tracef(1, "Extra padding could not be read  ")
    93  				return false
    94  			}
    95  
    96  			// Padding is not added to the extra map
    97  
    98  		case TX_PUBLIC_KEY: // next 32 bytes are tx public key
    99  			var pkey crypto.Key
   100  			n, err = buf.Read(pkey[:])
   101  			if err != nil || n != 32 {
   102  				rlog.Tracef(1, "Tx public key could not be parsed len=%d err=%s ", n, err)
   103  				return false
   104  			}
   105  			tx.Extra_map[TX_PUBLIC_KEY] = pkey
   106  		case TX_EXTRA_NONCE: // this is followed by 1 byte length, then length bytes of data
   107  			n, err = buf.Read(b)
   108  			if err != nil {
   109  				rlog.Tracef(1, "Extra nonce length could not be parsed ")
   110  				return false
   111  			}
   112  
   113  			length_int = int(b[0])
   114  
   115  			extra_nonce := make([]byte, length_int, length_int)
   116  			n, err = buf.Read(extra_nonce)
   117  			if err != nil || n != int(length_int) {
   118  				rlog.Tracef(1, "Extra Nonce could not be read ")
   119  				return false
   120  			}
   121  
   122  			switch length_int {
   123  			case 33: // unencrypted 32 byte  payment id
   124  				if extra_nonce[0] == byte(TX_EXTRA_NONCE_PAYMENT_ID) {
   125  					tx.PaymentID_map[TX_EXTRA_NONCE_PAYMENT_ID] = extra_nonce[1:]
   126  				} else {
   127  					rlog.Tracef(1, "Extra Nonce contains invalid payment id ")
   128  					return false
   129  				}
   130  
   131  			case 9: // encrypted 9 byte payment id
   132  				if extra_nonce[0] == byte(TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID) {
   133  					tx.PaymentID_map[TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID] = extra_nonce[1:]
   134  				} else {
   135  					rlog.Tracef(1, "Extra Nonce contains invalid encrypted payment id ")
   136  					return false
   137  				}
   138  
   139  			default: // consider it as general nonce
   140  				// ignore anything else
   141  			}
   142  
   143  			tx.Extra_map[TX_EXTRA_NONCE] = extra_nonce
   144  
   145  			// NO MORE TAGS are present
   146  
   147  		default: // any any other unknown tag or data, fails the parsing
   148  			rlog.Tracef(1, "Unhandled TAG %d \n", b[0])
   149  			result = false
   150  			return
   151  
   152  		}
   153  	}
   154  
   155  	// we should not reach here
   156  	//return true
   157  }
   158  
   159  // serialize an extra, this is only required while creating new transactions ( both miner and normal)
   160  // doing this on existing transaction will cause them to fail ( due to different placement order )
   161  func (tx *Transaction) Serialize_Extra() []byte {
   162  
   163  	buf := bytes.NewBuffer(nil)
   164  
   165  	// this is mandatory
   166  	if _, ok := tx.Extra_map[TX_PUBLIC_KEY]; ok {
   167  		buf.WriteByte(byte(TX_PUBLIC_KEY)) // write marker
   168  		key := tx.Extra_map[TX_PUBLIC_KEY].(crypto.Key)
   169  		buf.Write(key[:]) // write the key
   170  	} else {
   171  		rlog.Tracef(1, "TX does not contain a Public Key, not possible, the transaction will be rejected")
   172  		return buf.Bytes() // as keys are not provided, no point adding other fields
   173  	}
   174  
   175  	// extra nonce should be serialized only if other nonce are not provided, tx should contain max 1 nonce
   176  	// it can be either, extra nonce, 32 byte payment id or 8 byte encrypted payment id
   177  
   178  	// if payment id are set, they replace nonce
   179  	// first place unencrypted payment id
   180  	if _, ok := tx.PaymentID_map[TX_EXTRA_NONCE_PAYMENT_ID]; ok {
   181  		data_bytes := tx.PaymentID_map[TX_EXTRA_NONCE_PAYMENT_ID].([]byte)
   182  		if len(data_bytes) == 32 { // payment id is valid
   183  			header := append([]byte{byte(TX_EXTRA_NONCE_PAYMENT_ID)}, data_bytes...)
   184  			tx.Extra_map[TX_EXTRA_NONCE] = header // overwrite extra nonce with this
   185  		}
   186  		rlog.Tracef(1, "unencrypted payment id size mismatch expected = %d actual %d", 32, len(data_bytes))
   187  	}
   188  
   189  	// if encrypted nonce is provide, it will overwrite 32 byte nonce
   190  	if _, ok := tx.PaymentID_map[TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID]; ok {
   191  		data_bytes := tx.PaymentID_map[TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID].([]byte)
   192  		if len(data_bytes) == 8 { // payment id is valid
   193  			header := append([]byte{byte(TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID)}, data_bytes...)
   194  			tx.Extra_map[TX_EXTRA_NONCE] = header // overwrite extra nonce with this
   195  		}
   196  		rlog.Tracef(1, "unencrypted payment id size mismatch expected = %d actual %d", 8, len(data_bytes))
   197  	}
   198  
   199  	// TX_EXTRA_NONCE is optional
   200  	// if payment is present, it is packed as extra nonce
   201  	if _, ok := tx.Extra_map[TX_EXTRA_NONCE]; ok {
   202  		buf.WriteByte(byte(TX_EXTRA_NONCE)) // write marker
   203  		data_bytes := tx.Extra_map[TX_EXTRA_NONCE].([]byte)
   204  
   205  		if len(data_bytes) > 255 {
   206  			rlog.Tracef(1, "TX extra none is spilling, trimming the nonce to 254 bytes")
   207  			data_bytes = data_bytes[:254]
   208  		}
   209  		buf.WriteByte(byte(len(data_bytes))) // write length of extra nonce single byte
   210  		buf.Write(data_bytes[:])             // write the nonce data
   211  	}
   212  
   213  	// NOTE: we do not support adding padding for the sake of it
   214  
   215  	return buf.Bytes()
   216  
   217  }
   218  
   219  // resize the nonce by this much bytes,
   220  // positive means add  byte
   221  // negative means decrease size
   222  // this is only required during miner tx to solve chicken and problem
   223  /*
   224  func (tx *Transaction) Resize_Extra_Nonce(int resize_amount) {
   225  
   226      nonce_bytes := tx.Extra_map[TX_EXTRA_NONCE].([]byte)
   227      nonce_bytes = make([]byte, len(nonce_bytes)+resize_amount, len(nonce_bytes)+resize_amount)
   228      tx.Extra_map[TX_EXTRA_NONCE] = nonce_bytes
   229  
   230      tx.Extra = tx.Serialize_Extra()
   231  
   232  }
   233  */