forked from TrueCloudLab/neoneo-go
core/native: untangle native contracts initialization
The notion of NativeContractState shouldn't ever existed, native contract is a contract and its state is saved as regular contract state which is critical because we'll have MPT calculations over this state soon. Initial minting should be done in Neo.Native.Deploy because it generates notification that should have proper transaction context. RegisterNative() shouldn't exist as a public method, native contracts are only registered at block 0 and they can do it internally, no outside user should be able to mess with it. Move some structures from `native` package to `interop` also to avoid circular references as interop.Context has to have a list of native contracts (exposing them via Blockchainer is again too dangerous, it's too powerful tool).
This commit is contained in:
parent
869c7d6afa
commit
30836ca69b
10 changed files with 171 additions and 216 deletions
|
@ -208,10 +208,6 @@ func (bc *Blockchain) init() error {
|
||||||
// and the genesis block as first block.
|
// and the genesis block as first block.
|
||||||
bc.log.Info("restoring blockchain", zap.String("version", version))
|
bc.log.Info("restoring blockchain", zap.String("version", version))
|
||||||
|
|
||||||
if err = bc.registerNative(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
bHeight, err := bc.dao.GetCurrentBlockHeight()
|
bHeight, err := bc.dao.GetCurrentBlockHeight()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -273,34 +269,6 @@ func (bc *Blockchain) init() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *Blockchain) registerNative() error {
|
|
||||||
gas := native.NewGAS()
|
|
||||||
neo := native.NewNEO()
|
|
||||||
neo.GAS = gas
|
|
||||||
gas.NEO = neo
|
|
||||||
|
|
||||||
data, err := bc.dao.GetNativeContractState(gas.Hash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = gas.InitFromStore(data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err = bc.dao.GetNativeContractState(neo.Hash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = neo.InitFromStore(data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
bc.contracts.SetGAS(gas)
|
|
||||||
bc.contracts.SetNEO(neo)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run runs chain loop.
|
// Run runs chain loop.
|
||||||
func (bc *Blockchain) Run() {
|
func (bc *Blockchain) Run() {
|
||||||
persistTimer := time.NewTimer(persistInterval)
|
persistTimer := time.NewTimer(persistInterval)
|
||||||
|
@ -855,11 +823,6 @@ func (bc *Blockchain) LastBatch() *storage.MemBatch {
|
||||||
return bc.lastBatch
|
return bc.lastBatch
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterNative registers native contract in the blockchain.
|
|
||||||
func (bc *Blockchain) RegisterNative(c native.Contract) {
|
|
||||||
bc.contracts.Add(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// processOutputs processes transaction outputs.
|
// processOutputs processes transaction outputs.
|
||||||
func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
|
func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
|
||||||
for index, output := range tx.Outputs {
|
for index, output := range tx.Outputs {
|
||||||
|
@ -2065,7 +2028,7 @@ func (bc *Blockchain) secondsPerBlock() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *Blockchain) newInteropContext(trigger trigger.Type, d dao.DAO, block *block.Block, tx *transaction.Transaction) *interop.Context {
|
func (bc *Blockchain) newInteropContext(trigger trigger.Type, d dao.DAO, block *block.Block, tx *transaction.Transaction) *interop.Context {
|
||||||
ic := interop.NewContext(trigger, bc, d, block, tx, bc.log)
|
ic := interop.NewContext(trigger, bc, d, bc.contracts.Contracts, block, tx, bc.log)
|
||||||
switch {
|
switch {
|
||||||
case tx != nil:
|
case tx != nil:
|
||||||
ic.Container = tx
|
ic.Container = tx
|
||||||
|
|
|
@ -32,7 +32,6 @@ type DAO interface {
|
||||||
GetCurrentBlockHeight() (uint32, error)
|
GetCurrentBlockHeight() (uint32, error)
|
||||||
GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error)
|
GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error)
|
||||||
GetHeaderHashes() ([]util.Uint256, error)
|
GetHeaderHashes() ([]util.Uint256, error)
|
||||||
GetNativeContractState(h util.Uint160) ([]byte, error)
|
|
||||||
GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error)
|
GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error)
|
||||||
GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error)
|
GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error)
|
||||||
GetNextBlockValidators() (keys.PublicKeys, error)
|
GetNextBlockValidators() (keys.PublicKeys, error)
|
||||||
|
@ -55,7 +54,6 @@ type DAO interface {
|
||||||
PutAssetState(as *state.Asset) error
|
PutAssetState(as *state.Asset) error
|
||||||
PutContractState(cs *state.Contract) error
|
PutContractState(cs *state.Contract) error
|
||||||
PutCurrentHeader(hashAndIndex []byte) error
|
PutCurrentHeader(hashAndIndex []byte) error
|
||||||
PutNativeContractState(h util.Uint160, value []byte) error
|
|
||||||
PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error
|
PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error
|
||||||
PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error
|
PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error
|
||||||
PutNextBlockValidators(keys.PublicKeys) error
|
PutNextBlockValidators(keys.PublicKeys) error
|
||||||
|
@ -211,18 +209,6 @@ func (dao *Simple) DeleteContractState(hash util.Uint160) error {
|
||||||
return dao.Store.Delete(key)
|
return dao.Store.Delete(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNativeContractState retrieves native contract state from the store.
|
|
||||||
func (dao *Simple) GetNativeContractState(h util.Uint160) ([]byte, error) {
|
|
||||||
key := storage.AppendPrefix(storage.STNativeContract, h.BytesBE())
|
|
||||||
return dao.Store.Get(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutNativeContractState puts native contract state into the store.
|
|
||||||
func (dao *Simple) PutNativeContractState(h util.Uint160, value []byte) error {
|
|
||||||
key := storage.AppendPrefix(storage.STNativeContract, h.BytesBE())
|
|
||||||
return dao.Store.Put(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- end contracts.
|
// -- end contracts.
|
||||||
|
|
||||||
// -- start nep5 balances.
|
// -- start nep5 balances.
|
||||||
|
|
|
@ -7,8 +7,14 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto"
|
"github.com/nspcc-dev/neo-go/pkg/crypto"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,6 +22,7 @@ import (
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Chain blockchainer.Blockchainer
|
Chain blockchainer.Blockchainer
|
||||||
Container crypto.Verifiable
|
Container crypto.Verifiable
|
||||||
|
Natives []Contract
|
||||||
Trigger trigger.Type
|
Trigger trigger.Type
|
||||||
Block *block.Block
|
Block *block.Block
|
||||||
Tx *transaction.Transaction
|
Tx *transaction.Transaction
|
||||||
|
@ -25,11 +32,12 @@ type Context struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContext returns new interop context.
|
// NewContext returns new interop context.
|
||||||
func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
|
func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, natives []Contract, block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
|
||||||
dao := dao.NewCached(d)
|
dao := dao.NewCached(d)
|
||||||
nes := make([]state.NotificationEvent, 0)
|
nes := make([]state.NotificationEvent, 0)
|
||||||
return &Context{
|
return &Context{
|
||||||
Chain: bc,
|
Chain: bc,
|
||||||
|
Natives: natives,
|
||||||
Trigger: trigger,
|
Trigger: trigger,
|
||||||
Block: block,
|
Block: block,
|
||||||
Tx: tx,
|
Tx: tx,
|
||||||
|
@ -48,3 +56,64 @@ type Function struct {
|
||||||
Func func(*Context, *vm.VM) error
|
Func func(*Context, *vm.VM) error
|
||||||
Price int
|
Price int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Method is a signature for a native method.
|
||||||
|
type Method = func(ic *Context, args []vm.StackItem) vm.StackItem
|
||||||
|
|
||||||
|
// MethodAndPrice is a native-contract method descriptor.
|
||||||
|
type MethodAndPrice struct {
|
||||||
|
Func Method
|
||||||
|
Price int64
|
||||||
|
RequiredFlags smartcontract.CallFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contract is an interface for all native contracts.
|
||||||
|
type Contract interface {
|
||||||
|
Initialize(*Context) error
|
||||||
|
Metadata() *ContractMD
|
||||||
|
OnPersist(*Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContractMD represents native contract instance.
|
||||||
|
type ContractMD struct {
|
||||||
|
Manifest manifest.Manifest
|
||||||
|
ServiceName string
|
||||||
|
ServiceID uint32
|
||||||
|
Script []byte
|
||||||
|
Hash util.Uint160
|
||||||
|
Methods map[string]MethodAndPrice
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContractMD returns Contract with the specified list of methods.
|
||||||
|
func NewContractMD(name string) *ContractMD {
|
||||||
|
c := &ContractMD{
|
||||||
|
ServiceName: name,
|
||||||
|
ServiceID: emit.InteropNameToID([]byte(name)),
|
||||||
|
Methods: make(map[string]MethodAndPrice),
|
||||||
|
}
|
||||||
|
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
emit.Syscall(w.BinWriter, c.ServiceName)
|
||||||
|
c.Script = w.Bytes()
|
||||||
|
c.Hash = hash.Hash160(c.Script)
|
||||||
|
c.Manifest = *manifest.DefaultManifest(c.Hash)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMethod adds new method to a native contract.
|
||||||
|
func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method, safe bool) {
|
||||||
|
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, *desc)
|
||||||
|
c.Methods[desc.Name] = *md
|
||||||
|
if safe {
|
||||||
|
c.Manifest.SafeMethods.Add(desc.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEvent adds new event to a native contract.
|
||||||
|
func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) {
|
||||||
|
c.Manifest.ABI.Events = append(c.Manifest.ABI.Events, manifest.Event{
|
||||||
|
Name: name,
|
||||||
|
Parameters: ps,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -4,80 +4,20 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Method is a signature for a native method.
|
|
||||||
type Method = func(ic *interop.Context, args []vm.StackItem) vm.StackItem
|
|
||||||
|
|
||||||
// MethodAndPrice is a native-contract method descriptor.
|
|
||||||
type MethodAndPrice struct {
|
|
||||||
Func Method
|
|
||||||
Price int64
|
|
||||||
RequiredFlags smartcontract.CallFlag
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contract is an interface for all native contracts.
|
|
||||||
type Contract interface {
|
|
||||||
Metadata() *ContractMD
|
|
||||||
OnPersist(*interop.Context) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContractMD represents native contract instance.
|
|
||||||
type ContractMD struct {
|
|
||||||
Manifest manifest.Manifest
|
|
||||||
ServiceName string
|
|
||||||
ServiceID uint32
|
|
||||||
Script []byte
|
|
||||||
Hash util.Uint160
|
|
||||||
Methods map[string]MethodAndPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contracts is a set of registered native contracts.
|
// Contracts is a set of registered native contracts.
|
||||||
type Contracts struct {
|
type Contracts struct {
|
||||||
NEO *NEO
|
NEO *NEO
|
||||||
GAS *GAS
|
GAS *GAS
|
||||||
Contracts []Contract
|
Contracts []interop.Contract
|
||||||
}
|
|
||||||
|
|
||||||
// SetGAS sets GAS native contract.
|
|
||||||
func (cs *Contracts) SetGAS(g *GAS) {
|
|
||||||
cs.GAS = g
|
|
||||||
cs.Contracts = append(cs.Contracts, g)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNEO sets NEO native contract.
|
|
||||||
func (cs *Contracts) SetNEO(n *NEO) {
|
|
||||||
cs.NEO = n
|
|
||||||
cs.Contracts = append(cs.Contracts, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContractMD returns Contract with the specified list of methods.
|
|
||||||
func NewContractMD(name string) *ContractMD {
|
|
||||||
c := &ContractMD{
|
|
||||||
ServiceName: name,
|
|
||||||
ServiceID: emit.InteropNameToID([]byte(name)),
|
|
||||||
Methods: make(map[string]MethodAndPrice),
|
|
||||||
}
|
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
|
||||||
emit.Syscall(w.BinWriter, c.ServiceName)
|
|
||||||
c.Script = w.Bytes()
|
|
||||||
c.Hash = hash.Hash160(c.Script)
|
|
||||||
c.Manifest = *manifest.DefaultManifest(c.Hash)
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByHash returns native contract with the specified hash.
|
// ByHash returns native contract with the specified hash.
|
||||||
func (cs *Contracts) ByHash(h util.Uint160) Contract {
|
func (cs *Contracts) ByHash(h util.Uint160) interop.Contract {
|
||||||
for _, ctr := range cs.Contracts {
|
for _, ctr := range cs.Contracts {
|
||||||
if ctr.Metadata().Hash.Equals(h) {
|
if ctr.Metadata().Hash.Equals(h) {
|
||||||
return ctr
|
return ctr
|
||||||
|
@ -87,7 +27,7 @@ func (cs *Contracts) ByHash(h util.Uint160) Contract {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByID returns native contract with the specified id.
|
// ByID returns native contract with the specified id.
|
||||||
func (cs *Contracts) ByID(id uint32) Contract {
|
func (cs *Contracts) ByID(id uint32) interop.Contract {
|
||||||
for _, ctr := range cs.Contracts {
|
for _, ctr := range cs.Contracts {
|
||||||
if ctr.Metadata().ServiceID == id {
|
if ctr.Metadata().ServiceID == id {
|
||||||
return ctr
|
return ctr
|
||||||
|
@ -96,33 +36,21 @@ func (cs *Contracts) ByID(id uint32) Contract {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddMethod adds new method to a native contract.
|
// NewContracts returns new set of native contracts with new GAS and NEO
|
||||||
func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method, safe bool) {
|
// contracts.
|
||||||
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, *desc)
|
|
||||||
c.Methods[desc.Name] = *md
|
|
||||||
if safe {
|
|
||||||
c.Manifest.SafeMethods.Add(desc.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddEvent adds new event to a native contract.
|
|
||||||
func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) {
|
|
||||||
c.Manifest.ABI.Events = append(c.Manifest.ABI.Events, manifest.Event{
|
|
||||||
Name: name,
|
|
||||||
Parameters: ps,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContracts returns new empty set of native contracts.
|
|
||||||
func NewContracts() *Contracts {
|
func NewContracts() *Contracts {
|
||||||
return &Contracts{
|
cs := new(Contracts)
|
||||||
Contracts: []Contract{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds new native contracts to the list.
|
gas := NewGAS()
|
||||||
func (cs *Contracts) Add(c Contract) {
|
neo := NewNEO()
|
||||||
cs.Contracts = append(cs.Contracts, c)
|
neo.GAS = gas
|
||||||
|
gas.NEO = neo
|
||||||
|
|
||||||
|
cs.GAS = gas
|
||||||
|
cs.Contracts = append(cs.Contracts, gas)
|
||||||
|
cs.NEO = neo
|
||||||
|
cs.Contracts = append(cs.Contracts, neo)
|
||||||
|
return cs
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNativeInterop returns an interop getter for a given set of contracts.
|
// GetNativeInterop returns an interop getter for a given set of contracts.
|
||||||
|
@ -139,7 +67,7 @@ func (cs *Contracts) GetNativeInterop(ic *interop.Context) func(uint32) *vm.Inte
|
||||||
}
|
}
|
||||||
|
|
||||||
// getNativeInterop returns native contract interop.
|
// getNativeInterop returns native contract interop.
|
||||||
func getNativeInterop(ic *interop.Context, c Contract) func(v *vm.VM) error {
|
func getNativeInterop(ic *interop.Context, c interop.Contract) func(v *vm.VM) error {
|
||||||
return func(v *vm.VM) error {
|
return func(v *vm.VM) error {
|
||||||
h := v.GetContextScriptHash(0)
|
h := v.GetContextScriptHash(0)
|
||||||
if !h.Equals(c.Metadata().Hash) {
|
if !h.Equals(c.Metadata().Hash) {
|
||||||
|
|
|
@ -13,16 +13,12 @@ func Deploy(ic *interop.Context, _ *vm.VM) error {
|
||||||
if ic.Block.Index != 0 {
|
if ic.Block.Index != 0 {
|
||||||
return errors.New("native contracts can be deployed only at 0 block")
|
return errors.New("native contracts can be deployed only at 0 block")
|
||||||
}
|
}
|
||||||
gas := NewGAS()
|
|
||||||
neo := NewNEO()
|
|
||||||
neo.GAS = gas
|
|
||||||
gas.NEO = neo
|
|
||||||
|
|
||||||
if err := gas.Initialize(ic); err != nil {
|
for _, native := range ic.Natives {
|
||||||
return fmt.Errorf("can't initialize GAS native contract: %v", err)
|
if err := native.Initialize(ic); err != nil {
|
||||||
}
|
md := native.Metadata()
|
||||||
if err := neo.Initialize(ic); err != nil {
|
return fmt.Errorf("initializing %s native contract: %v", md.ServiceName, err)
|
||||||
return fmt.Errorf("can't initialize NEO native contract: %v", err)
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,12 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GAS represents GAS native contract.
|
// GAS represents GAS native contract.
|
||||||
|
@ -24,13 +22,17 @@ type GAS struct {
|
||||||
|
|
||||||
const gasSyscallName = "Neo.Native.Tokens.GAS"
|
const gasSyscallName = "Neo.Native.Tokens.GAS"
|
||||||
|
|
||||||
|
// GASFactor is a divisor for finding GAS integral value.
|
||||||
|
const GASFactor = NEOTotalSupply
|
||||||
|
const initialGAS = 30000000
|
||||||
|
|
||||||
// NewGAS returns GAS native contract.
|
// NewGAS returns GAS native contract.
|
||||||
func NewGAS() *GAS {
|
func NewGAS() *GAS {
|
||||||
nep5 := newNEP5Native(gasSyscallName)
|
nep5 := newNEP5Native(gasSyscallName)
|
||||||
nep5.name = "GAS"
|
nep5.name = "GAS"
|
||||||
nep5.symbol = "gas"
|
nep5.symbol = "gas"
|
||||||
nep5.decimals = 8
|
nep5.decimals = 8
|
||||||
nep5.factor = 100000000
|
nep5.factor = GASFactor
|
||||||
|
|
||||||
g := &GAS{nep5TokenNative: *nep5}
|
g := &GAS{nep5TokenNative: *nep5}
|
||||||
|
|
||||||
|
@ -44,16 +46,6 @@ func NewGAS() *GAS {
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
// initFromStore initializes variable contract parameters from the store.
|
|
||||||
func (g *GAS) InitFromStore(data []byte) error {
|
|
||||||
g.totalSupply = *emit.BytesToInt(data)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GAS) serializeState() []byte {
|
|
||||||
return emit.IntToBytes(&g.totalSupply)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GAS) increaseBalance(_ *interop.Context, acc *state.Account, amount *big.Int) error {
|
func (g *GAS) increaseBalance(_ *interop.Context, acc *state.Account, amount *big.Int) error {
|
||||||
if sign := amount.Sign(); sign == 0 {
|
if sign := amount.Sign(); sign == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
@ -66,22 +58,18 @@ func (g *GAS) increaseBalance(_ *interop.Context, acc *state.Account, amount *bi
|
||||||
|
|
||||||
// Initialize initializes GAS contract.
|
// Initialize initializes GAS contract.
|
||||||
func (g *GAS) Initialize(ic *interop.Context) error {
|
func (g *GAS) Initialize(ic *interop.Context) error {
|
||||||
data, err := ic.DAO.GetNativeContractState(g.Hash)
|
if err := g.nep5TokenNative.Initialize(ic); err != nil {
|
||||||
if err == nil {
|
|
||||||
return g.InitFromStore(data)
|
|
||||||
} else if err != storage.ErrKeyNotFound {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if g.nep5TokenNative.getTotalSupply(ic).Sign() != 0 {
|
||||||
if err := g.nep5TokenNative.Initialize(); err != nil {
|
return errors.New("already initialized")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
h, _, err := getStandbyValidatorsHash(ic)
|
h, _, err := getStandbyValidatorsHash(ic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
g.mint(ic, h, big.NewInt(30000000*g.factor))
|
g.mint(ic, h, big.NewInt(initialGAS*GASFactor))
|
||||||
return ic.DAO.PutNativeContractState(g.Hash, g.serializeState())
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPersist implements Contract interface.
|
// OnPersist implements Contract interface.
|
||||||
|
@ -95,7 +83,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
|
||||||
// netFee += tx.NetworkFee
|
// netFee += tx.NetworkFee
|
||||||
//}
|
//}
|
||||||
//g.mint(ic, <primary>, netFee)
|
//g.mint(ic, <primary>, netFee)
|
||||||
return ic.DAO.PutNativeContractState(g.Hash, g.serializeState())
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GAS) getSysFeeAmount(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
func (g *GAS) getSysFeeAmount(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,6 +26,9 @@ type NEO struct {
|
||||||
|
|
||||||
const neoSyscallName = "Neo.Native.Tokens.NEO"
|
const neoSyscallName = "Neo.Native.Tokens.NEO"
|
||||||
|
|
||||||
|
// NEOTotalSupply is the total amount of NEO in the system.
|
||||||
|
const NEOTotalSupply = 100000000
|
||||||
|
|
||||||
// NewNEO returns NEO native contract.
|
// NewNEO returns NEO native contract.
|
||||||
func NewNEO() *NEO {
|
func NewNEO() *NEO {
|
||||||
nep5 := newNEP5Native(neoSyscallName)
|
nep5 := newNEP5Native(neoSyscallName)
|
||||||
|
@ -74,22 +76,19 @@ func NewNEO() *NEO {
|
||||||
|
|
||||||
// Initialize initializes NEO contract.
|
// Initialize initializes NEO contract.
|
||||||
func (n *NEO) Initialize(ic *interop.Context) error {
|
func (n *NEO) Initialize(ic *interop.Context) error {
|
||||||
data, err := ic.DAO.GetNativeContractState(n.Hash)
|
if err := n.nep5TokenNative.Initialize(ic); err != nil {
|
||||||
if err == nil {
|
|
||||||
return n.InitFromStore(data)
|
|
||||||
} else if err != storage.ErrKeyNotFound {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := n.nep5TokenNative.Initialize(); err != nil {
|
if n.nep5TokenNative.getTotalSupply(ic).Sign() != 0 {
|
||||||
return err
|
return errors.New("already initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
h, vs, err := getStandbyValidatorsHash(ic)
|
h, vs, err := getStandbyValidatorsHash(ic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
n.mint(ic, h, big.NewInt(100000000*n.factor))
|
n.mint(ic, h, big.NewInt(NEOTotalSupply))
|
||||||
|
|
||||||
for i := range vs {
|
for i := range vs {
|
||||||
if err := n.registerValidatorInternal(ic, vs[i]); err != nil {
|
if err := n.registerValidatorInternal(ic, vs[i]); err != nil {
|
||||||
|
@ -97,19 +96,9 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ic.DAO.PutNativeContractState(n.Hash, n.serializeState())
|
|
||||||
}
|
|
||||||
|
|
||||||
// initFromStore initializes variable contract parameters from the store.
|
|
||||||
func (n *NEO) InitFromStore(data []byte) error {
|
|
||||||
n.totalSupply = *emit.BytesToInt(data)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) serializeState() []byte {
|
|
||||||
return emit.IntToBytes(&n.totalSupply)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnPersist implements Contract interface.
|
// OnPersist implements Contract interface.
|
||||||
func (n *NEO) OnPersist(ic *interop.Context) error {
|
func (n *NEO) OnPersist(ic *interop.Context) error {
|
||||||
pubs, err := n.GetValidatorsInternal(ic.Chain, ic.DAO)
|
pubs, err := n.GetValidatorsInternal(ic.Chain, ic.DAO)
|
||||||
|
@ -119,7 +108,7 @@ func (n *NEO) OnPersist(ic *interop.Context) error {
|
||||||
if err := ic.DAO.PutNextBlockValidators(pubs); err != nil {
|
if err := ic.DAO.PutNextBlockValidators(pubs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ic.DAO.PutNativeContractState(n.Hash, n.serializeState())
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) increaseBalance(ic *interop.Context, acc *state.Account, amount *big.Int) error {
|
func (n *NEO) increaseBalance(ic *interop.Context, acc *state.Account, amount *big.Int) error {
|
||||||
|
|
|
@ -10,28 +10,31 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
)
|
)
|
||||||
|
|
||||||
// nep5TokenNative represents NEP-5 token contract.
|
// nep5TokenNative represents NEP-5 token contract.
|
||||||
type nep5TokenNative struct {
|
type nep5TokenNative struct {
|
||||||
ContractMD
|
interop.ContractMD
|
||||||
name string
|
name string
|
||||||
symbol string
|
symbol string
|
||||||
decimals int64
|
decimals int64
|
||||||
factor int64
|
factor int64
|
||||||
totalSupply big.Int
|
onPersist func(*interop.Context) error
|
||||||
onPersist func(*interop.Context) error
|
incBalance func(*interop.Context, *state.Account, *big.Int) error
|
||||||
incBalance func(*interop.Context, *state.Account, *big.Int) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Metadata() *ContractMD {
|
// totalSupplyKey is the key used to store totalSupply value.
|
||||||
|
var totalSupplyKey = []byte{11}
|
||||||
|
|
||||||
|
func (c *nep5TokenNative) Metadata() *interop.ContractMD {
|
||||||
return &c.ContractMD
|
return &c.ContractMD
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Contract = (*nep5TokenNative)(nil)
|
var _ interop.Contract = (*nep5TokenNative)(nil)
|
||||||
|
|
||||||
func newNEP5Native(name string) *nep5TokenNative {
|
func newNEP5Native(name string) *nep5TokenNative {
|
||||||
n := &nep5TokenNative{ContractMD: *NewContractMD(name)}
|
n := &nep5TokenNative{ContractMD: *interop.NewContractMD(name)}
|
||||||
|
|
||||||
desc := newDescriptor("name", smartcontract.StringType)
|
desc := newDescriptor("name", smartcontract.StringType)
|
||||||
md := newMethodAndPrice(n.Name, 1, smartcontract.NoneFlag)
|
md := newMethodAndPrice(n.Name, 1, smartcontract.NoneFlag)
|
||||||
|
@ -45,6 +48,10 @@ func newNEP5Native(name string) *nep5TokenNative {
|
||||||
md = newMethodAndPrice(n.Decimals, 1, smartcontract.NoneFlag)
|
md = newMethodAndPrice(n.Decimals, 1, smartcontract.NoneFlag)
|
||||||
n.AddMethod(md, desc, true)
|
n.AddMethod(md, desc, true)
|
||||||
|
|
||||||
|
desc = newDescriptor("totalSupply", smartcontract.IntegerType)
|
||||||
|
md = newMethodAndPrice(n.TotalSupply, 1, smartcontract.NoneFlag)
|
||||||
|
n.AddMethod(md, desc, true)
|
||||||
|
|
||||||
desc = newDescriptor("balanceOf", smartcontract.IntegerType,
|
desc = newDescriptor("balanceOf", smartcontract.IntegerType,
|
||||||
manifest.NewParameter("account", smartcontract.Hash160Type))
|
manifest.NewParameter("account", smartcontract.Hash160Type))
|
||||||
md = newMethodAndPrice(n.balanceOf, 1, smartcontract.NoneFlag)
|
md = newMethodAndPrice(n.balanceOf, 1, smartcontract.NoneFlag)
|
||||||
|
@ -63,7 +70,7 @@ func newNEP5Native(name string) *nep5TokenNative {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Initialize() error {
|
func (c *nep5TokenNative) Initialize(_ *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +86,23 @@ func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []vm.StackItem) vm.Stac
|
||||||
return vm.NewBigIntegerItem(big.NewInt(c.decimals))
|
return vm.NewBigIntegerItem(big.NewInt(c.decimals))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *nep5TokenNative) TotalSupply(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
|
||||||
|
return vm.NewBigIntegerItem(c.getTotalSupply(ic))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int {
|
||||||
|
si := ic.DAO.GetStorageItem(c.Hash, totalSupplyKey)
|
||||||
|
if si == nil {
|
||||||
|
return big.NewInt(0)
|
||||||
|
}
|
||||||
|
return emit.BytesToInt(si.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *nep5TokenNative) saveTotalSupply(ic *interop.Context, supply *big.Int) error {
|
||||||
|
si := &state.StorageItem{Value: emit.IntToBytes(supply)}
|
||||||
|
return ic.DAO.PutStorageItem(c.Hash, totalSupplyKey, si)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Transfer(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
func (c *nep5TokenNative) Transfer(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||||
from := toUint160(args[0])
|
from := toUint160(args[0])
|
||||||
to := toUint160(args[1])
|
to := toUint160(args[1])
|
||||||
|
@ -185,7 +209,12 @@ func (c *nep5TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.totalSupply.Add(&c.totalSupply, amount)
|
supply := c.getTotalSupply(ic)
|
||||||
|
supply.Add(supply, amount)
|
||||||
|
err = c.saveTotalSupply(ic, supply)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) OnPersist(ic *interop.Context) error {
|
func (c *nep5TokenNative) OnPersist(ic *interop.Context) error {
|
||||||
|
@ -200,8 +229,8 @@ func newDescriptor(name string, ret smartcontract.ParamType, ps ...manifest.Para
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMethodAndPrice(f Method, price int64, flags smartcontract.CallFlag) *MethodAndPrice {
|
func newMethodAndPrice(f interop.Method, price int64, flags smartcontract.CallFlag) *interop.MethodAndPrice {
|
||||||
return &MethodAndPrice{
|
return &interop.MethodAndPrice{
|
||||||
Func: f,
|
Func: f,
|
||||||
Price: price,
|
Price: price,
|
||||||
RequiredFlags: flags,
|
RequiredFlags: flags,
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
@ -17,11 +16,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type testNative struct {
|
type testNative struct {
|
||||||
meta native.ContractMD
|
meta interop.ContractMD
|
||||||
blocks chan uint32
|
blocks chan uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tn *testNative) Metadata() *native.ContractMD {
|
func (tn *testNative) Initialize(_ *interop.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tn *testNative) Metadata() *interop.ContractMD {
|
||||||
return &tn.meta
|
return &tn.meta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,11 +37,16 @@ func (tn *testNative) OnPersist(ic *interop.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ native.Contract = (*testNative)(nil)
|
var _ interop.Contract = (*testNative)(nil)
|
||||||
|
|
||||||
|
// registerNative registers native contract in the blockchain.
|
||||||
|
func (bc *Blockchain) registerNative(c interop.Contract) {
|
||||||
|
bc.contracts.Contracts = append(bc.contracts.Contracts, c)
|
||||||
|
}
|
||||||
|
|
||||||
func newTestNative() *testNative {
|
func newTestNative() *testNative {
|
||||||
tn := &testNative{
|
tn := &testNative{
|
||||||
meta: *native.NewContractMD("Test.Native.Sum"),
|
meta: *interop.NewContractMD("Test.Native.Sum"),
|
||||||
blocks: make(chan uint32, 1),
|
blocks: make(chan uint32, 1),
|
||||||
}
|
}
|
||||||
desc := &manifest.Method{
|
desc := &manifest.Method{
|
||||||
|
@ -49,7 +57,7 @@ func newTestNative() *testNative {
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.IntegerType,
|
ReturnType: smartcontract.IntegerType,
|
||||||
}
|
}
|
||||||
md := &native.MethodAndPrice{
|
md := &interop.MethodAndPrice{
|
||||||
Func: tn.sum,
|
Func: tn.sum,
|
||||||
Price: 1,
|
Price: 1,
|
||||||
RequiredFlags: smartcontract.NoneFlag,
|
RequiredFlags: smartcontract.NoneFlag,
|
||||||
|
@ -76,7 +84,7 @@ func TestNativeContract_Invoke(t *testing.T) {
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
|
|
||||||
tn := newTestNative()
|
tn := newTestNative()
|
||||||
chain.RegisterNative(tn)
|
chain.registerNative(tn)
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "sum", int64(14), int64(28))
|
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "sum", int64(14), int64(28))
|
||||||
|
|
|
@ -17,7 +17,6 @@ const (
|
||||||
STAsset KeyPrefix = 0x4c
|
STAsset KeyPrefix = 0x4c
|
||||||
STNotification KeyPrefix = 0x4d
|
STNotification KeyPrefix = 0x4d
|
||||||
STContract KeyPrefix = 0x50
|
STContract KeyPrefix = 0x50
|
||||||
STNativeContract KeyPrefix = 0x51
|
|
||||||
STStorage KeyPrefix = 0x70
|
STStorage KeyPrefix = 0x70
|
||||||
STNEP5Transfers KeyPrefix = 0x72
|
STNEP5Transfers KeyPrefix = 0x72
|
||||||
STNEP5Balances KeyPrefix = 0x73
|
STNEP5Balances KeyPrefix = 0x73
|
||||||
|
|
Loading…
Reference in a new issue