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  }