2019-11-28 16:06:09 +00:00
|
|
|
package state
|
2018-04-16 20:15:30 +00:00
|
|
|
|
|
|
|
import (
|
2020-12-13 15:26:35 +00:00
|
|
|
"errors"
|
|
|
|
"math"
|
|
|
|
"math/big"
|
|
|
|
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
2020-06-09 09:12:56 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
2021-01-13 12:34:10 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2020-11-18 20:10:48 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
2020-12-13 15:26:35 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
2018-04-16 20:15:30 +00:00
|
|
|
)
|
|
|
|
|
2019-11-28 16:06:09 +00:00
|
|
|
// Contract holds information about a smart contract in the NEO blockchain.
|
|
|
|
type Contract struct {
|
2021-02-09 09:02:38 +00:00
|
|
|
ContractBase
|
|
|
|
UpdateCounter uint16 `json:"updatecounter"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContractBase represents part shared by native and user-deployed contracts.
|
|
|
|
type ContractBase struct {
|
|
|
|
ID int32 `json:"id"`
|
|
|
|
Hash util.Uint160 `json:"hash"`
|
|
|
|
NEF nef.File `json:"nef"`
|
|
|
|
Manifest manifest.Manifest `json:"manifest"`
|
2018-04-16 20:15:30 +00:00
|
|
|
}
|
2019-09-30 16:52:16 +00:00
|
|
|
|
2021-02-09 09:25:38 +00:00
|
|
|
// NativeContract holds information about native contract.
|
|
|
|
type NativeContract struct {
|
|
|
|
ContractBase
|
2021-03-11 11:39:51 +00:00
|
|
|
UpdateHistory []uint32 `json:"updatehistory"`
|
2021-02-09 09:25:38 +00:00
|
|
|
}
|
|
|
|
|
2019-09-30 16:52:16 +00:00
|
|
|
// DecodeBinary implements Serializable interface.
|
2020-12-13 15:26:35 +00:00
|
|
|
func (c *Contract) DecodeBinary(r *io.BinReader) {
|
2021-07-06 16:56:23 +00:00
|
|
|
si := stackitem.DecodeBinary(r)
|
2020-12-13 15:26:35 +00:00
|
|
|
if r.Err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
r.Err = c.FromStackItem(si)
|
2019-09-30 16:52:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// EncodeBinary implements Serializable interface.
|
2020-12-13 15:26:35 +00:00
|
|
|
func (c *Contract) EncodeBinary(w *io.BinWriter) {
|
|
|
|
si, err := c.ToStackItem()
|
|
|
|
if err != nil {
|
|
|
|
w.Err = err
|
|
|
|
return
|
|
|
|
}
|
2021-07-06 16:56:23 +00:00
|
|
|
stackitem.EncodeBinary(si, w)
|
2020-12-13 15:26:35 +00:00
|
|
|
}
|
|
|
|
|
2021-05-12 20:17:03 +00:00
|
|
|
// ToStackItem converts state.Contract to stackitem.Item.
|
2020-12-13 15:26:35 +00:00
|
|
|
func (c *Contract) ToStackItem() (stackitem.Item, error) {
|
2021-02-03 18:09:50 +00:00
|
|
|
rawNef, err := c.NEF.Bytes()
|
2020-12-13 15:26:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-02-03 18:09:50 +00:00
|
|
|
m, err := c.Manifest.ToStackItem()
|
2021-01-13 12:34:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-12-13 15:26:35 +00:00
|
|
|
return stackitem.NewArray([]stackitem.Item{
|
|
|
|
stackitem.Make(c.ID),
|
|
|
|
stackitem.Make(c.UpdateCounter),
|
|
|
|
stackitem.NewByteArray(c.Hash.BytesBE()),
|
2021-01-13 12:34:10 +00:00
|
|
|
stackitem.NewByteArray(rawNef),
|
2021-02-03 18:09:50 +00:00
|
|
|
m,
|
2020-12-13 15:26:35 +00:00
|
|
|
}), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FromStackItem fills Contract's data from given stack itemized contract
|
|
|
|
// representation.
|
|
|
|
func (c *Contract) FromStackItem(item stackitem.Item) error {
|
|
|
|
arr, ok := item.Value().([]stackitem.Item)
|
|
|
|
if !ok {
|
|
|
|
return errors.New("not an array")
|
|
|
|
}
|
|
|
|
bi, ok := arr[0].Value().(*big.Int)
|
|
|
|
if !ok {
|
|
|
|
return errors.New("ID is not an integer")
|
|
|
|
}
|
|
|
|
if !bi.IsInt64() || bi.Int64() > math.MaxInt32 || bi.Int64() < math.MinInt32 {
|
|
|
|
return errors.New("ID not in int32 range")
|
|
|
|
}
|
|
|
|
c.ID = int32(bi.Int64())
|
|
|
|
bi, ok = arr[1].Value().(*big.Int)
|
|
|
|
if !ok {
|
|
|
|
return errors.New("UpdateCounter is not an integer")
|
|
|
|
}
|
|
|
|
if !bi.IsInt64() || bi.Int64() > math.MaxUint16 || bi.Int64() < 0 {
|
|
|
|
return errors.New("UpdateCounter not in uint16 range")
|
|
|
|
}
|
|
|
|
c.UpdateCounter = uint16(bi.Int64())
|
|
|
|
bytes, err := arr[2].TryBytes()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.Hash, err = util.Uint160DecodeBytesBE(bytes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
bytes, err = arr[3].TryBytes()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-01-13 12:34:10 +00:00
|
|
|
c.NEF, err = nef.FileFromBytes(bytes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-03 18:09:50 +00:00
|
|
|
m := new(manifest.Manifest)
|
|
|
|
err = m.FromStackItem(arr[4])
|
2020-12-13 15:26:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-03 18:09:50 +00:00
|
|
|
c.Manifest = *m
|
|
|
|
return nil
|
2019-09-30 16:52:16 +00:00
|
|
|
}
|
|
|
|
|
2020-11-18 20:10:48 +00:00
|
|
|
// CreateContractHash creates deployed contract hash from transaction sender
|
|
|
|
// and contract script.
|
2021-01-22 09:22:48 +00:00
|
|
|
func CreateContractHash(sender util.Uint160, checksum uint32, name string) util.Uint160 {
|
2020-11-18 20:10:48 +00:00
|
|
|
w := io.NewBufBinWriter()
|
|
|
|
emit.Opcodes(w.BinWriter, opcode.ABORT)
|
|
|
|
emit.Bytes(w.BinWriter, sender.BytesBE())
|
2021-01-22 09:22:48 +00:00
|
|
|
emit.Int(w.BinWriter, int64(checksum))
|
|
|
|
emit.String(w.BinWriter, name)
|
2020-11-18 20:10:48 +00:00
|
|
|
if w.Err != nil {
|
|
|
|
panic(w.Err)
|
2020-06-09 09:12:56 +00:00
|
|
|
}
|
2020-11-18 20:10:48 +00:00
|
|
|
return hash.Hash160(w.Bytes())
|
2019-10-10 14:56:58 +00:00
|
|
|
}
|