2018-03-04 13:56:49 +00:00
|
|
|
package transaction
|
|
|
|
|
|
|
|
import (
|
2019-02-20 17:39:32 +00:00
|
|
|
"encoding/json"
|
2020-08-19 13:20:48 +00:00
|
|
|
"errors"
|
2018-03-17 11:53:21 +00:00
|
|
|
"fmt"
|
2018-03-04 13:56:49 +00:00
|
|
|
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
2018-03-04 13:56:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Attribute represents a Transaction attribute.
|
|
|
|
type Attribute struct {
|
2020-09-16 11:50:31 +00:00
|
|
|
Type AttrType
|
|
|
|
Value interface {
|
|
|
|
io.Serializable
|
|
|
|
// toJSONMap is used for embedded json struct marshalling.
|
|
|
|
// Anonymous interface fields are not considered anonymous by
|
|
|
|
// json lib and marshaling Value together with type makes code
|
|
|
|
// harder to follow.
|
2023-04-03 10:34:24 +00:00
|
|
|
toJSONMap(map[string]any)
|
2020-09-16 11:50:31 +00:00
|
|
|
}
|
2020-05-13 21:17:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// attrJSON is used for JSON I/O of Attribute.
|
|
|
|
type attrJSON struct {
|
2020-08-11 17:58:56 +00:00
|
|
|
Type string `json:"type"`
|
2018-03-04 13:56:49 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// DecodeBinary implements the Serializable interface.
|
2019-09-16 16:31:49 +00:00
|
|
|
func (attr *Attribute) DecodeBinary(br *io.BinReader) {
|
2020-08-11 17:58:56 +00:00
|
|
|
attr.Type = AttrType(br.ReadB())
|
2019-08-28 16:27:06 +00:00
|
|
|
|
2020-10-15 10:06:22 +00:00
|
|
|
switch t := attr.Type; t {
|
2020-08-19 13:20:48 +00:00
|
|
|
case HighPriority:
|
2020-10-15 10:06:22 +00:00
|
|
|
return
|
2020-09-16 11:50:31 +00:00
|
|
|
case OracleResponseT:
|
|
|
|
attr.Value = new(OracleResponse)
|
2020-10-14 16:07:16 +00:00
|
|
|
case NotValidBeforeT:
|
|
|
|
attr.Value = new(NotValidBefore)
|
2020-10-15 11:45:29 +00:00
|
|
|
case ConflictsT:
|
|
|
|
attr.Value = new(Conflicts)
|
2020-11-18 11:26:13 +00:00
|
|
|
case NotaryAssistedT:
|
|
|
|
attr.Value = new(NotaryAssisted)
|
2019-08-28 16:27:06 +00:00
|
|
|
default:
|
2020-10-15 10:06:22 +00:00
|
|
|
if t >= ReservedLowerBound && t <= ReservedUpperBound {
|
|
|
|
attr.Value = new(Reserved)
|
|
|
|
break
|
|
|
|
}
|
2020-08-11 17:58:56 +00:00
|
|
|
br.Err = fmt.Errorf("failed decoding TX attribute usage: 0x%2x", int(attr.Type))
|
2019-09-16 16:31:49 +00:00
|
|
|
return
|
2018-03-04 13:56:49 +00:00
|
|
|
}
|
2020-10-15 10:06:22 +00:00
|
|
|
attr.Value.DecodeBinary(br)
|
2018-03-04 13:56:49 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// EncodeBinary implements the Serializable interface.
|
2019-09-16 16:31:49 +00:00
|
|
|
func (attr *Attribute) EncodeBinary(bw *io.BinWriter) {
|
2020-08-11 17:58:56 +00:00
|
|
|
bw.WriteB(byte(attr.Type))
|
2020-10-15 10:06:22 +00:00
|
|
|
switch t := attr.Type; t {
|
2020-08-19 13:20:48 +00:00
|
|
|
case HighPriority:
|
2020-11-18 11:26:13 +00:00
|
|
|
case OracleResponseT, NotValidBeforeT, ConflictsT, NotaryAssistedT:
|
2020-09-16 11:50:31 +00:00
|
|
|
attr.Value.EncodeBinary(bw)
|
2019-08-28 16:27:06 +00:00
|
|
|
default:
|
2020-10-15 10:06:22 +00:00
|
|
|
if t >= ReservedLowerBound && t <= ReservedUpperBound {
|
|
|
|
attr.Value.EncodeBinary(bw)
|
|
|
|
break
|
|
|
|
}
|
2020-08-11 17:58:56 +00:00
|
|
|
bw.Err = fmt.Errorf("failed encoding TX attribute usage: 0x%2x", attr.Type)
|
2018-03-04 13:56:49 +00:00
|
|
|
}
|
|
|
|
}
|
2019-02-20 17:39:32 +00:00
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// MarshalJSON implements the json Marshaller interface.
|
2019-02-20 17:39:32 +00:00
|
|
|
func (attr *Attribute) MarshalJSON() ([]byte, error) {
|
2023-04-03 10:34:24 +00:00
|
|
|
m := map[string]any{"type": attr.Type.String()}
|
2020-09-16 11:50:31 +00:00
|
|
|
if attr.Value != nil {
|
|
|
|
attr.Value.toJSONMap(m)
|
|
|
|
}
|
|
|
|
return json.Marshal(m)
|
2019-02-20 17:39:32 +00:00
|
|
|
}
|
2020-05-13 21:17:39 +00:00
|
|
|
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaller interface.
|
|
|
|
func (attr *Attribute) UnmarshalJSON(data []byte) error {
|
|
|
|
aj := new(attrJSON)
|
|
|
|
err := json.Unmarshal(data, aj)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-11 17:58:56 +00:00
|
|
|
switch aj.Type {
|
2020-10-15 11:50:51 +00:00
|
|
|
case HighPriority.String():
|
2020-08-19 13:20:48 +00:00
|
|
|
attr.Type = HighPriority
|
2020-10-15 11:50:51 +00:00
|
|
|
return nil
|
|
|
|
case OracleResponseT.String():
|
2020-09-16 11:50:31 +00:00
|
|
|
attr.Type = OracleResponseT
|
|
|
|
// Note: because `type` field will not be present in any attribute
|
|
|
|
// value, we can unmarshal the same data. The overhead is minimal.
|
|
|
|
attr.Value = new(OracleResponse)
|
2020-10-15 11:50:51 +00:00
|
|
|
case NotValidBeforeT.String():
|
2020-10-14 16:07:16 +00:00
|
|
|
attr.Type = NotValidBeforeT
|
|
|
|
attr.Value = new(NotValidBefore)
|
2020-10-15 11:45:29 +00:00
|
|
|
case ConflictsT.String():
|
|
|
|
attr.Type = ConflictsT
|
|
|
|
attr.Value = new(Conflicts)
|
2020-11-18 11:26:13 +00:00
|
|
|
case NotaryAssistedT.String():
|
|
|
|
attr.Type = NotaryAssistedT
|
|
|
|
attr.Value = new(NotaryAssisted)
|
2020-05-13 21:17:39 +00:00
|
|
|
default:
|
2020-08-11 17:58:56 +00:00
|
|
|
return errors.New("wrong Type")
|
2020-05-13 21:17:39 +00:00
|
|
|
}
|
2020-10-15 11:50:51 +00:00
|
|
|
return json.Unmarshal(data, attr.Value)
|
2020-05-13 21:17:39 +00:00
|
|
|
}
|