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 */