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

     1  package transaction
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"math"
     8  	"strings"
     9  
    10  	"github.com/nspcc-dev/neo-go/pkg/io"
    11  )
    12  
    13  //go:generate stringer -type=OracleResponseCode
    14  
    15  // OracleResponseCode represents result code of oracle response.
    16  type OracleResponseCode byte
    17  
    18  // OracleResponse represents oracle response.
    19  type OracleResponse struct {
    20  	ID     uint64             `json:"id"`
    21  	Code   OracleResponseCode `json:"code"`
    22  	Result []byte             `json:"result"`
    23  }
    24  
    25  // MaxOracleResultSize is the maximum allowed oracle answer size.
    26  const MaxOracleResultSize = math.MaxUint16
    27  
    28  // Enumeration of possible oracle response types.
    29  const (
    30  	Success                 OracleResponseCode = 0x00
    31  	ProtocolNotSupported    OracleResponseCode = 0x10
    32  	ConsensusUnreachable    OracleResponseCode = 0x12
    33  	NotFound                OracleResponseCode = 0x14
    34  	Timeout                 OracleResponseCode = 0x16
    35  	Forbidden               OracleResponseCode = 0x18
    36  	ResponseTooLarge        OracleResponseCode = 0x1a
    37  	InsufficientFunds       OracleResponseCode = 0x1c
    38  	ContentTypeNotSupported OracleResponseCode = 0x1f
    39  	Error                   OracleResponseCode = 0xff
    40  )
    41  
    42  // Various validation errors.
    43  var (
    44  	ErrInvalidResponseCode = errors.New("invalid oracle response code")
    45  	ErrInvalidResult       = errors.New("oracle response != success, but result is not empty")
    46  )
    47  
    48  // IsValid checks if c is valid response code.
    49  func (c OracleResponseCode) IsValid() bool {
    50  	return c == Success || c == ProtocolNotSupported || c == ConsensusUnreachable || c == NotFound ||
    51  		c == Timeout || c == Forbidden || c == ResponseTooLarge ||
    52  		c == InsufficientFunds || c == ContentTypeNotSupported || c == Error
    53  }
    54  
    55  // MarshalJSON implements the json.Marshaler interface.
    56  func (c OracleResponseCode) MarshalJSON() ([]byte, error) {
    57  	return []byte(`"` + c.String() + `"`), nil
    58  }
    59  
    60  // UnmarshalJSON implements the json.Unmarshaler interface.
    61  func (c *OracleResponseCode) UnmarshalJSON(data []byte) error {
    62  	var js string
    63  	if err := json.Unmarshal(data, &js); err != nil {
    64  		return err
    65  	}
    66  	js = strings.ToLower(js)
    67  	switch js {
    68  	case "success":
    69  		*c = Success
    70  	case "protocolnotsupported":
    71  		*c = ProtocolNotSupported
    72  	case "consensusunreachable":
    73  		*c = ConsensusUnreachable
    74  	case "notfound":
    75  		*c = NotFound
    76  	case "timeout":
    77  		*c = Timeout
    78  	case "forbidden":
    79  		*c = Forbidden
    80  	case "responsetoolarge":
    81  		*c = ResponseTooLarge
    82  	case "insufficientfunds":
    83  		*c = InsufficientFunds
    84  	case "contenttypenotsupported":
    85  		*c = ContentTypeNotSupported
    86  	case "error":
    87  		*c = Error
    88  	default:
    89  		return errors.New("invalid oracle response code")
    90  	}
    91  	return nil
    92  }
    93  
    94  // DecodeBinary implements the io.Serializable interface.
    95  func (r *OracleResponse) DecodeBinary(br *io.BinReader) {
    96  	r.ID = br.ReadU64LE()
    97  	r.Code = OracleResponseCode(br.ReadB())
    98  	if !r.Code.IsValid() {
    99  		br.Err = ErrInvalidResponseCode
   100  		return
   101  	}
   102  	r.Result = br.ReadVarBytes(MaxOracleResultSize)
   103  	if r.Code != Success && len(r.Result) > 0 {
   104  		br.Err = ErrInvalidResult
   105  	}
   106  }
   107  
   108  // EncodeBinary implements the io.Serializable interface.
   109  func (r *OracleResponse) EncodeBinary(w *io.BinWriter) {
   110  	w.WriteU64LE(r.ID)
   111  	w.WriteB(byte(r.Code))
   112  	w.WriteVarBytes(r.Result)
   113  }
   114  
   115  func (r *OracleResponse) toJSONMap(m map[string]any) {
   116  	m["id"] = r.ID
   117  	m["code"] = r.Code
   118  	m["result"] = r.Result
   119  }
   120  
   121  // Copy implements the AttrValue interface.
   122  func (r *OracleResponse) Copy() AttrValue {
   123  	return &OracleResponse{
   124  		ID:     r.ID,
   125  		Code:   r.Code,
   126  		Result: bytes.Clone(r.Result),
   127  	}
   128  }