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 }