github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/transaction/attribute.go (about)

     1  package transaction
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/nspcc-dev/neo-go/pkg/io"
     9  )
    10  
    11  // AttrValue represents a Transaction Attribute value.
    12  type AttrValue interface {
    13  	io.Serializable
    14  	// toJSONMap is used for embedded json struct marshalling.
    15  	// Anonymous interface fields are not considered anonymous by
    16  	// json lib and marshaling Value together with type makes code
    17  	// harder to follow.
    18  	toJSONMap(map[string]any)
    19  	// Copy returns a deep copy of the attribute value.
    20  	Copy() AttrValue
    21  }
    22  
    23  // Attribute represents a Transaction attribute.
    24  type Attribute struct {
    25  	Type  AttrType
    26  	Value AttrValue
    27  }
    28  
    29  // attrJSON is used for JSON I/O of Attribute.
    30  type attrJSON struct {
    31  	Type string `json:"type"`
    32  }
    33  
    34  // DecodeBinary implements the Serializable interface.
    35  func (attr *Attribute) DecodeBinary(br *io.BinReader) {
    36  	attr.Type = AttrType(br.ReadB())
    37  
    38  	switch t := attr.Type; t {
    39  	case HighPriority:
    40  		return
    41  	case OracleResponseT:
    42  		attr.Value = new(OracleResponse)
    43  	case NotValidBeforeT:
    44  		attr.Value = new(NotValidBefore)
    45  	case ConflictsT:
    46  		attr.Value = new(Conflicts)
    47  	case NotaryAssistedT:
    48  		attr.Value = new(NotaryAssisted)
    49  	default:
    50  		if t >= ReservedLowerBound && t <= ReservedUpperBound {
    51  			attr.Value = new(Reserved)
    52  			break
    53  		}
    54  		br.Err = fmt.Errorf("failed decoding TX attribute usage: 0x%2x", int(attr.Type))
    55  		return
    56  	}
    57  	attr.Value.DecodeBinary(br)
    58  }
    59  
    60  // EncodeBinary implements the Serializable interface.
    61  func (attr *Attribute) EncodeBinary(bw *io.BinWriter) {
    62  	bw.WriteB(byte(attr.Type))
    63  	switch t := attr.Type; t {
    64  	case HighPriority:
    65  	case OracleResponseT, NotValidBeforeT, ConflictsT, NotaryAssistedT:
    66  		attr.Value.EncodeBinary(bw)
    67  	default:
    68  		if t >= ReservedLowerBound && t <= ReservedUpperBound {
    69  			attr.Value.EncodeBinary(bw)
    70  			break
    71  		}
    72  		bw.Err = fmt.Errorf("failed encoding TX attribute usage: 0x%2x", attr.Type)
    73  	}
    74  }
    75  
    76  // MarshalJSON implements the json Marshaller interface.
    77  func (attr *Attribute) MarshalJSON() ([]byte, error) {
    78  	m := map[string]any{"type": attr.Type.String()}
    79  	if attr.Value != nil {
    80  		attr.Value.toJSONMap(m)
    81  	}
    82  	return json.Marshal(m)
    83  }
    84  
    85  // UnmarshalJSON implements the json.Unmarshaller interface.
    86  func (attr *Attribute) UnmarshalJSON(data []byte) error {
    87  	aj := new(attrJSON)
    88  	err := json.Unmarshal(data, aj)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	switch aj.Type {
    93  	case HighPriority.String():
    94  		attr.Type = HighPriority
    95  		return nil
    96  	case OracleResponseT.String():
    97  		attr.Type = OracleResponseT
    98  		// Note: because `type` field will not be present in any attribute
    99  		// value, we can unmarshal the same data. The overhead is minimal.
   100  		attr.Value = new(OracleResponse)
   101  	case NotValidBeforeT.String():
   102  		attr.Type = NotValidBeforeT
   103  		attr.Value = new(NotValidBefore)
   104  	case ConflictsT.String():
   105  		attr.Type = ConflictsT
   106  		attr.Value = new(Conflicts)
   107  	case NotaryAssistedT.String():
   108  		attr.Type = NotaryAssistedT
   109  		attr.Value = new(NotaryAssisted)
   110  	default:
   111  		return errors.New("wrong Type")
   112  	}
   113  	return json.Unmarshal(data, attr.Value)
   114  }
   115  
   116  // Copy creates a deep copy of the Attribute.
   117  func (attr *Attribute) Copy() *Attribute {
   118  	if attr == nil {
   119  		return nil
   120  	}
   121  	cp := &Attribute{
   122  		Type: attr.Type,
   123  	}
   124  	if attr.Value != nil {
   125  		cp.Value = attr.Value.Copy()
   126  	}
   127  	return cp
   128  }