git.gammaspectra.live/P2Pool/consensus@v0.0.0-20240403173234-a039820b20c9/monero/transaction/extra.go (about) 1 package transaction 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "git.gammaspectra.live/P2Pool/consensus/monero/crypto" 9 "git.gammaspectra.live/P2Pool/consensus/types" 10 "git.gammaspectra.live/P2Pool/consensus/utils" 11 "io" 12 ) 13 14 const TxExtraTagPadding = 0x00 15 const TxExtraTagPubKey = 0x01 16 const TxExtraTagNonce = 0x02 17 const TxExtraTagMergeMining = 0x03 18 const TxExtraTagAdditionalPubKeys = 0x04 19 const TxExtraTagMysteriousMinergate = 0xde 20 21 const TxExtraPaddingMaxCount = 255 22 const TxExtraNonceMaxCount = 255 23 const TxExtraAdditionalPubKeysMaxCount = 4096 24 25 const TxExtraTemplateNonceSize = 4 26 27 type ExtraTags []ExtraTag 28 29 type ExtraTag struct { 30 // VarInt has different meanings. In TxExtraTagMergeMining it is depth, while in others it is length 31 VarInt uint64 `json:"var_int"` 32 Tag uint8 `json:"tag"` 33 HasVarInt bool `json:"has_var_int"` 34 Data types.Bytes `json:"data"` 35 } 36 37 func (t *ExtraTags) UnmarshalBinary(data []byte) (err error) { 38 reader := bytes.NewReader(data) 39 return t.FromReader(reader) 40 } 41 42 func (t *ExtraTags) BufferLength() (length int) { 43 for _, tag := range *t { 44 length += tag.BufferLength() 45 } 46 return length 47 } 48 49 func (t *ExtraTags) MarshalBinary() ([]byte, error) { 50 51 return t.AppendBinary(make([]byte, 0, t.BufferLength())) 52 } 53 54 func (t *ExtraTags) AppendBinary(preAllocatedBuf []byte) (buf []byte, err error) { 55 if t == nil { 56 return nil, nil 57 } 58 buf = preAllocatedBuf 59 for _, tag := range *t { 60 if buf, err = tag.AppendBinary(buf); err != nil { 61 return nil, err 62 } 63 } 64 65 return buf, nil 66 } 67 68 func (t *ExtraTags) SideChainHashingBlob(preAllocatedBuf []byte, zeroTemplateId bool) (buf []byte, err error) { 69 if t == nil { 70 return nil, nil 71 } 72 buf = preAllocatedBuf 73 for _, tag := range *t { 74 if buf, err = tag.SideChainHashingBlob(buf, zeroTemplateId); err != nil { 75 return nil, err 76 } 77 } 78 79 return buf, nil 80 } 81 82 func (t *ExtraTags) FromReader(reader utils.ReaderAndByteReader) (err error) { 83 var tag ExtraTag 84 for { 85 if err = tag.FromReader(reader); err != nil { 86 if err == io.EOF { 87 return nil 88 } 89 return err 90 } 91 if t.GetTag(tag.Tag) != nil { 92 return errors.New("tag already exists") 93 } 94 *t = append(*t, tag) 95 } 96 } 97 98 func (t *ExtraTags) GetTag(tag uint8) *ExtraTag { 99 for i := range *t { 100 if (*t)[i].Tag == tag { 101 return &(*t)[i] 102 } 103 } 104 105 return nil 106 } 107 108 func (t *ExtraTag) UnmarshalBinary(data []byte) error { 109 reader := bytes.NewReader(data) 110 return t.FromReader(reader) 111 } 112 113 func (t *ExtraTag) BufferLength() int { 114 if t.HasVarInt { 115 return 1 + utils.UVarInt64Size(t.VarInt) + len(t.Data) 116 } 117 return 1 + len(t.Data) 118 } 119 120 func (t *ExtraTag) MarshalBinary() ([]byte, error) { 121 return t.AppendBinary(make([]byte, 0, t.BufferLength())) 122 } 123 124 func (t *ExtraTag) AppendBinary(preAllocatedBuf []byte) ([]byte, error) { 125 buf := preAllocatedBuf 126 buf = append(buf, t.Tag) 127 if t.HasVarInt { 128 buf = binary.AppendUvarint(buf, t.VarInt) 129 } 130 buf = append(buf, t.Data...) 131 return buf, nil 132 } 133 134 func (t *ExtraTag) SideChainHashingBlob(preAllocatedBuf []byte, zeroTemplateId bool) ([]byte, error) { 135 buf := preAllocatedBuf 136 buf = append(buf, t.Tag) 137 if t.HasVarInt { 138 buf = binary.AppendUvarint(buf, t.VarInt) 139 } 140 if zeroTemplateId && t.Tag == TxExtraTagMergeMining { 141 buf = append(buf, make([]byte, len(t.Data))...) 142 } else if t.Tag == TxExtraTagNonce { 143 b := make([]byte, len(t.Data)) 144 //Replace only the first four bytes 145 if len(t.Data) > TxExtraTemplateNonceSize { 146 copy(b[TxExtraTemplateNonceSize:], t.Data[TxExtraTemplateNonceSize:]) 147 } 148 buf = append(buf, b...) 149 } else { 150 buf = append(buf, t.Data...) 151 } 152 return buf, nil 153 } 154 155 func (t *ExtraTag) FromReader(reader utils.ReaderAndByteReader) (err error) { 156 157 if err = binary.Read(reader, binary.LittleEndian, &t.Tag); err != nil { 158 return err 159 } 160 161 switch t.Tag { 162 default: 163 return fmt.Errorf("unknown extra tag %d", t.Tag) 164 case TxExtraTagPadding: 165 var size uint64 166 var zero byte 167 for size = 1; size <= TxExtraPaddingMaxCount; size++ { 168 if zero, err = reader.ReadByte(); err != nil { 169 if err == io.EOF { 170 break 171 } else { 172 return err 173 } 174 } 175 176 if zero != 0 { 177 return errors.New("padding is not zero") 178 } 179 } 180 181 if size > TxExtraPaddingMaxCount { 182 return errors.New("padding is too big") 183 } 184 185 t.Data = make([]byte, size-2) 186 case TxExtraTagPubKey: 187 t.Data = make([]byte, crypto.PublicKeySize) 188 if _, err = io.ReadFull(reader, t.Data); err != nil { 189 return err 190 } 191 case TxExtraTagNonce: 192 t.HasVarInt = true 193 if t.VarInt, err = binary.ReadUvarint(reader); err != nil { 194 return err 195 } else { 196 if t.VarInt > TxExtraNonceMaxCount { 197 return errors.New("nonce is too big") 198 } 199 200 t.Data = make([]byte, t.VarInt) 201 if _, err = io.ReadFull(reader, t.Data); err != nil { 202 return err 203 } 204 } 205 case TxExtraTagMergeMining: 206 t.HasVarInt = true 207 if t.VarInt, err = binary.ReadUvarint(reader); err != nil { 208 return err 209 } else { 210 t.Data = make([]byte, types.HashSize) 211 if _, err = io.ReadFull(reader, t.Data); err != nil { 212 return err 213 } 214 } 215 case TxExtraTagAdditionalPubKeys: 216 t.HasVarInt = true 217 if t.VarInt, err = binary.ReadUvarint(reader); err != nil { 218 return err 219 } else { 220 if t.VarInt > TxExtraAdditionalPubKeysMaxCount { 221 return errors.New("too many public keys") 222 } 223 224 t.Data = make([]byte, types.HashSize*t.VarInt) 225 if _, err = io.ReadFull(reader, t.Data); err != nil { 226 return err 227 } 228 } 229 case TxExtraTagMysteriousMinergate: 230 t.HasVarInt = true 231 if t.VarInt, err = binary.ReadUvarint(reader); err != nil { 232 return err 233 } else { 234 if t.VarInt > TxExtraNonceMaxCount { 235 return errors.New("nonce is too big") 236 } 237 238 t.Data = make([]byte, t.VarInt) 239 if _, err = io.ReadFull(reader, t.Data); err != nil { 240 return err 241 } 242 } 243 } 244 245 return nil 246 }