mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-30 09:33:36 +00:00
core: implement skeletons for NEO/GAS native contracts
This commit is contained in:
parent
25354c44f9
commit
76700f31cf
9 changed files with 893 additions and 105 deletions
|
@ -198,6 +198,9 @@ func (bc *Blockchain) init() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := bc.initNative(); err != nil {
|
||||
return err
|
||||
}
|
||||
return bc.storeBlock(genesisBlock)
|
||||
}
|
||||
if ver != version {
|
||||
|
@ -270,6 +273,27 @@ func (bc *Blockchain) init() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (bc *Blockchain) initNative() error {
|
||||
ic := bc.newInteropContext(trigger.Application, bc.dao, nil, nil)
|
||||
|
||||
gas := native.NewGAS()
|
||||
neo := native.NewNEO()
|
||||
neo.GAS = gas
|
||||
gas.NEO = neo
|
||||
|
||||
if err := gas.Initialize(ic); err != nil {
|
||||
return fmt.Errorf("can't initialize GAS native contract: %v", err)
|
||||
}
|
||||
if err := neo.Initialize(ic); err != nil {
|
||||
return fmt.Errorf("can't initialize NEO native contract: %v", err)
|
||||
}
|
||||
|
||||
bc.contracts.SetGAS(gas)
|
||||
bc.contracts.SetNEO(neo)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run runs chain loop.
|
||||
func (bc *Blockchain) Run() {
|
||||
persistTimer := time.NewTimer(persistInterval)
|
||||
|
@ -639,7 +663,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
|||
return err
|
||||
}
|
||||
case *transaction.StateTX:
|
||||
if err := processStateTX(cache, t); err != nil {
|
||||
if err := bc.processStateTX(cache, tx, t); err != nil {
|
||||
return err
|
||||
}
|
||||
case *transaction.PublishTX:
|
||||
|
@ -887,22 +911,9 @@ func processTXWithValidatorsSubtract(output *transaction.Output, account *state.
|
|||
|
||||
// modAccountVotes adds given value to given account voted validators.
|
||||
func modAccountVotes(account *state.Account, dao *dao.Cached, value util.Fixed8) error {
|
||||
for _, vote := range account.Votes {
|
||||
validator, err := dao.GetValidatorStateOrNew(vote)
|
||||
if err != nil {
|
||||
if err := native.ModifyAccountVotes(account, dao, value); err != nil {
|
||||
return err
|
||||
}
|
||||
validator.Votes += value
|
||||
if validator.UnregisteredAndHasNoVotes() {
|
||||
if err := dao.DeleteValidatorState(validator); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := dao.PutValidatorState(validator); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(account.Votes) > 0 {
|
||||
vc, err := dao.GetValidatorsCount()
|
||||
if err != nil {
|
||||
|
@ -937,55 +948,19 @@ func processValidatorStateDescriptor(descriptor *transaction.StateDescriptor, da
|
|||
return nil
|
||||
}
|
||||
|
||||
func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, dao *dao.Cached) error {
|
||||
func (bc *Blockchain) processAccountStateDescriptor(descriptor *transaction.StateDescriptor, t *transaction.Transaction, dao *dao.Cached) error {
|
||||
hash, err := util.Uint160DecodeBytesBE(descriptor.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account, err := dao.GetAccountStateOrNew(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if descriptor.Field == "Votes" {
|
||||
balance := account.GetBalanceValues()[GoverningTokenID()]
|
||||
if err = modAccountVotes(account, dao, -balance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
votes := keys.PublicKeys{}
|
||||
err := votes.DecodeBytes(descriptor.Value)
|
||||
if err != nil {
|
||||
if err := votes.DecodeBytes(descriptor.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(votes) > state.MaxValidatorsVoted {
|
||||
return errors.New("voting candidate limit exceeded")
|
||||
}
|
||||
if len(votes) > 0 {
|
||||
account.Votes = votes
|
||||
for _, vote := range account.Votes {
|
||||
validatorState, err := dao.GetValidatorStateOrNew(vote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
validatorState.Votes += balance
|
||||
if err = dao.PutValidatorState(validatorState); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
vc, err := dao.GetValidatorsCount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vc[len(account.Votes)-1] += balance
|
||||
err = dao.PutValidatorsCount(vc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
account.Votes = nil
|
||||
}
|
||||
return dao.PutAccountState(account)
|
||||
ic := bc.newInteropContext(trigger.Application, dao, nil, t)
|
||||
return bc.contracts.NEO.VoteInternal(ic, hash, votes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1807,59 +1782,14 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P
|
|||
return nil, err
|
||||
}
|
||||
case *transaction.StateTX:
|
||||
if err := processStateTX(cache, t); err != nil {
|
||||
if err := bc.processStateTX(cache, tx, t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validators := cache.GetValidators()
|
||||
sort.Slice(validators, func(i, j int) bool {
|
||||
// Unregistered validators go to the end of the list.
|
||||
if validators[i].Registered != validators[j].Registered {
|
||||
return validators[i].Registered
|
||||
}
|
||||
// The most-voted validators should end up in the front of the list.
|
||||
if validators[i].Votes != validators[j].Votes {
|
||||
return validators[i].Votes > validators[j].Votes
|
||||
}
|
||||
// Ties are broken with public keys.
|
||||
return validators[i].PublicKey.Cmp(validators[j].PublicKey) == -1
|
||||
})
|
||||
|
||||
validatorsCount, err := cache.GetValidatorsCount()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
count := validatorsCount.GetWeightedAverage()
|
||||
standByValidators, err := bc.GetStandByValidators()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count < len(standByValidators) {
|
||||
count = len(standByValidators)
|
||||
}
|
||||
|
||||
uniqueSBValidators := standByValidators.Unique()
|
||||
result := keys.PublicKeys{}
|
||||
for _, validator := range validators {
|
||||
if validator.RegisteredAndHasVotes() || uniqueSBValidators.Contains(validator.PublicKey) {
|
||||
result = append(result, validator.PublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
if result.Len() >= count {
|
||||
result = result[:count]
|
||||
} else {
|
||||
for i := 0; i < uniqueSBValidators.Len() && result.Len() < count; i++ {
|
||||
if !result.Contains(uniqueSBValidators[i]) {
|
||||
result = append(result, uniqueSBValidators[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Sort(result)
|
||||
return result, nil
|
||||
return bc.contracts.NEO.GetValidatorsInternal(bc, cache)
|
||||
}
|
||||
|
||||
// GetEnrollments returns all registered validators and non-registered SB validators
|
||||
|
@ -1896,11 +1826,11 @@ func (bc *Blockchain) GetEnrollments() ([]*state.Validator, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func processStateTX(dao *dao.Cached, tx *transaction.StateTX) error {
|
||||
func (bc *Blockchain) processStateTX(dao *dao.Cached, t *transaction.Transaction, tx *transaction.StateTX) error {
|
||||
for _, desc := range tx.Descriptors {
|
||||
switch desc.Type {
|
||||
case transaction.Account:
|
||||
if err := processAccountStateDescriptor(desc, dao); err != nil {
|
||||
if err := bc.processAccountStateDescriptor(desc, t, dao); err != nil {
|
||||
return err
|
||||
}
|
||||
case transaction.Validator:
|
||||
|
|
|
@ -32,8 +32,10 @@ type DAO interface {
|
|||
GetCurrentBlockHeight() (uint32, error)
|
||||
GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error)
|
||||
GetHeaderHashes() ([]util.Uint256, error)
|
||||
GetNativeContractState(h util.Uint160) ([]byte, error)
|
||||
GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error)
|
||||
GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error)
|
||||
GetNextBlockValidators() (keys.PublicKeys, error)
|
||||
GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem
|
||||
GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error)
|
||||
GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error)
|
||||
|
@ -53,8 +55,10 @@ type DAO interface {
|
|||
PutAssetState(as *state.Asset) error
|
||||
PutContractState(cs *state.Contract) error
|
||||
PutCurrentHeader(hashAndIndex []byte) error
|
||||
PutNativeContractState(h util.Uint160, value []byte) error
|
||||
PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error
|
||||
PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error
|
||||
PutNextBlockValidators(keys.PublicKeys) error
|
||||
PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error
|
||||
PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error
|
||||
PutValidatorState(vs *state.Validator) error
|
||||
|
@ -207,6 +211,18 @@ func (dao *Simple) DeleteContractState(hash util.Uint160) error {
|
|||
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.
|
||||
|
||||
// -- start nep5 balances.
|
||||
|
@ -310,6 +326,38 @@ func (dao *Simple) putUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin
|
|||
|
||||
// -- start validator.
|
||||
|
||||
// GetNextBlockValidators retrieves next block validators from store or nil if they are missing.
|
||||
func (dao *Simple) GetNextBlockValidators() (keys.PublicKeys, error) {
|
||||
key := []byte{byte(storage.STNextValidators)}
|
||||
buf, err := dao.Store.Get(key)
|
||||
if err != nil {
|
||||
if err == storage.ErrKeyNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pubs keys.PublicKeys
|
||||
r := io.NewBinReaderFromBuf(buf)
|
||||
r.ReadArray(&pubs)
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
return pubs, nil
|
||||
}
|
||||
|
||||
// PutNextBlockValidators puts next block validators to store.
|
||||
func (dao *Simple) PutNextBlockValidators(pubs keys.PublicKeys) error {
|
||||
w := io.NewBufBinWriter()
|
||||
w.WriteArray(pubs)
|
||||
if w.Err != nil {
|
||||
return w.Err
|
||||
}
|
||||
|
||||
key := []byte{byte(storage.STNextValidators)}
|
||||
return dao.Store.Put(key, w.Bytes())
|
||||
}
|
||||
|
||||
// GetValidatorStateOrNew gets validator from store or created new one in case of error.
|
||||
func (dao *Simple) GetValidatorStateOrNew(publicKey *keys.PublicKey) (*state.Validator, error) {
|
||||
validatorState, err := dao.GetValidatorState(publicKey)
|
||||
|
|
|
@ -42,9 +42,23 @@ type ContractMD struct {
|
|||
|
||||
// Contracts is a set of registered native contracts.
|
||||
type Contracts struct {
|
||||
NEO *NEO
|
||||
GAS *GAS
|
||||
Contracts []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{
|
||||
|
|
134
pkg/core/native/native_gas.go
Normal file
134
pkg/core/native/native_gas.go
Normal file
|
@ -0,0 +1,134 @@
|
|||
package native
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"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/storage"
|
||||
"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/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/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
)
|
||||
|
||||
// GAS represents GAS native contract.
|
||||
type GAS struct {
|
||||
nep5TokenNative
|
||||
NEO *NEO
|
||||
}
|
||||
|
||||
const gasSyscallName = "Neo.Native.Tokens.GAS"
|
||||
|
||||
// NewGAS returns GAS native contract.
|
||||
func NewGAS() *GAS {
|
||||
nep5 := newNEP5Native(gasSyscallName)
|
||||
nep5.name = "GAS"
|
||||
nep5.symbol = "gas"
|
||||
nep5.decimals = 8
|
||||
nep5.factor = 100000000
|
||||
|
||||
g := &GAS{nep5TokenNative: *nep5}
|
||||
|
||||
desc := newDescriptor("getSysFeeAmount", smartcontract.IntegerType,
|
||||
manifest.NewParameter("index", smartcontract.IntegerType))
|
||||
md := newMethodAndPrice(g.getSysFeeAmount, 1, smartcontract.NoneFlag)
|
||||
g.AddMethod(md, desc, true)
|
||||
|
||||
g.onPersist = chainOnPersist(g.onPersist, g.OnPersist)
|
||||
g.incBalance = g.increaseBalance
|
||||
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 {
|
||||
if sign := amount.Sign(); sign == 0 {
|
||||
return nil
|
||||
} else if sign == -1 && acc.GAS.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
|
||||
return errors.New("insufficient funds")
|
||||
}
|
||||
acc.GAS.Balance.Add(&acc.GAS.Balance, amount)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Initialize initializes GAS contract.
|
||||
func (g *GAS) Initialize(ic *interop.Context) error {
|
||||
data, err := ic.DAO.GetNativeContractState(g.Hash)
|
||||
if err == nil {
|
||||
return g.initFromStore(data)
|
||||
} else if err != storage.ErrKeyNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := g.nep5TokenNative.Initialize(); err != nil {
|
||||
return err
|
||||
}
|
||||
h, _, err := getStandbyValidatorsHash(ic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.mint(ic, h, big.NewInt(30000000*g.factor))
|
||||
return ic.DAO.PutNativeContractState(g.Hash, g.serializeState())
|
||||
}
|
||||
|
||||
// OnPersist implements Contract interface.
|
||||
func (g *GAS) OnPersist(ic *interop.Context) error {
|
||||
//for _ ,tx := range ic.block.Transactions {
|
||||
// g.burn(ic, tx.Sender, tx.SystemFee + tx.NetworkFee)
|
||||
//}
|
||||
//validators := g.NEO.getNextBlockValidators(ic)
|
||||
//var netFee util.Fixed8
|
||||
//for _, tx := range ic.block.Transactions {
|
||||
// netFee += tx.NetworkFee
|
||||
//}
|
||||
//g.mint(ic, <primary>, netFee)
|
||||
return ic.DAO.PutNativeContractState(g.Hash, g.serializeState())
|
||||
}
|
||||
|
||||
func (g *GAS) getSysFeeAmount(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||
index := toBigInt(args[0])
|
||||
h := ic.Chain.GetHeaderHash(int(index.Int64()))
|
||||
_, sf, err := ic.DAO.GetBlock(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return vm.NewBigIntegerItem(big.NewInt(int64(sf)))
|
||||
}
|
||||
|
||||
func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, []*keys.PublicKey, error) {
|
||||
vs, err := ic.Chain.GetStandByValidators()
|
||||
if err != nil {
|
||||
return util.Uint160{}, nil, err
|
||||
}
|
||||
s, err := smartcontract.CreateMultiSigRedeemScript(len(vs)/2+1, vs)
|
||||
if err != nil {
|
||||
return util.Uint160{}, nil, err
|
||||
}
|
||||
return hash.Hash160(s), vs, nil
|
||||
}
|
||||
|
||||
func chainOnPersist(fs ...func(*interop.Context) error) func(*interop.Context) error {
|
||||
return func(ic *interop.Context) error {
|
||||
for i := range fs {
|
||||
if fs[i] != nil {
|
||||
if err := fs[i](ic); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
380
pkg/core/native/native_neo.go
Normal file
380
pkg/core/native/native_neo.go
Normal file
|
@ -0,0 +1,380 @@
|
|||
package native
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||
"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/keys"
|
||||
"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/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// NEO represents NEO native contract.
|
||||
type NEO struct {
|
||||
nep5TokenNative
|
||||
GAS *GAS
|
||||
}
|
||||
|
||||
const neoSyscallName = "Neo.Native.Tokens.NEO"
|
||||
|
||||
// NewNEO returns NEO native contract.
|
||||
func NewNEO() *NEO {
|
||||
nep5 := newNEP5Native(neoSyscallName)
|
||||
nep5.name = "NEO"
|
||||
nep5.symbol = "neo"
|
||||
nep5.decimals = 0
|
||||
nep5.factor = 1
|
||||
|
||||
n := &NEO{nep5TokenNative: *nep5}
|
||||
|
||||
desc := newDescriptor("unclaimedGas", smartcontract.IntegerType,
|
||||
manifest.NewParameter("account", smartcontract.Hash160Type),
|
||||
manifest.NewParameter("end", smartcontract.IntegerType))
|
||||
md := newMethodAndPrice(n.unclaimedGas, 1, smartcontract.NoneFlag)
|
||||
n.AddMethod(md, desc, true)
|
||||
|
||||
desc = newDescriptor("registerValidator", smartcontract.BoolType,
|
||||
manifest.NewParameter("pubkey", smartcontract.PublicKeyType))
|
||||
md = newMethodAndPrice(n.registerValidator, 1, smartcontract.NoneFlag)
|
||||
n.AddMethod(md, desc, false)
|
||||
|
||||
desc = newDescriptor("vote", smartcontract.BoolType,
|
||||
manifest.NewParameter("account", smartcontract.Hash160Type),
|
||||
manifest.NewParameter("pubkeys", smartcontract.ArrayType))
|
||||
md = newMethodAndPrice(n.vote, 1, smartcontract.NoneFlag)
|
||||
n.AddMethod(md, desc, false)
|
||||
|
||||
desc = newDescriptor("getRegisteredValidators", smartcontract.ArrayType)
|
||||
md = newMethodAndPrice(n.getRegisteredValidators, 1, smartcontract.NoneFlag)
|
||||
n.AddMethod(md, desc, true)
|
||||
|
||||
desc = newDescriptor("getValidators", smartcontract.ArrayType)
|
||||
md = newMethodAndPrice(n.getValidators, 1, smartcontract.NoneFlag)
|
||||
n.AddMethod(md, desc, true)
|
||||
|
||||
desc = newDescriptor("getNextBlockValidators", smartcontract.ArrayType)
|
||||
md = newMethodAndPrice(n.getNextBlockValidators, 1, smartcontract.NoneFlag)
|
||||
n.AddMethod(md, desc, true)
|
||||
|
||||
n.onPersist = chainOnPersist(n.onPersist, n.OnPersist)
|
||||
n.incBalance = n.increaseBalance
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// Initialize initializes NEO contract.
|
||||
func (n *NEO) Initialize(ic *interop.Context) error {
|
||||
data, err := ic.DAO.GetNativeContractState(n.Hash)
|
||||
if err == nil {
|
||||
return n.initFromStore(data)
|
||||
} else if err != storage.ErrKeyNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := n.nep5TokenNative.Initialize(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h, vs, err := getStandbyValidatorsHash(ic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.mint(ic, h, big.NewInt(100000000*n.factor))
|
||||
|
||||
for i := range vs {
|
||||
if err := n.registerValidatorInternal(ic, vs[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (n *NEO) serializeState() []byte {
|
||||
return emit.IntToBytes(&n.totalSupply)
|
||||
}
|
||||
|
||||
// OnPersist implements Contract interface.
|
||||
func (n *NEO) OnPersist(ic *interop.Context) error {
|
||||
pubs, err := n.GetValidatorsInternal(ic.Chain, ic.DAO)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ic.DAO.PutNextBlockValidators(pubs); err != nil {
|
||||
return err
|
||||
}
|
||||
return ic.DAO.PutNativeContractState(n.Hash, n.serializeState())
|
||||
}
|
||||
|
||||
func (n *NEO) increaseBalance(ic *interop.Context, acc *state.Account, amount *big.Int) error {
|
||||
if sign := amount.Sign(); sign == 0 {
|
||||
return nil
|
||||
} else if sign == -1 && acc.NEO.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
|
||||
return errors.New("insufficient funds")
|
||||
}
|
||||
if err := n.distributeGas(ic, acc); err != nil {
|
||||
return err
|
||||
}
|
||||
acc.NEO.Balance.Add(&acc.NEO.Balance, amount)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NEO) distributeGas(ic *interop.Context, acc *state.Account) error {
|
||||
if ic.Block == nil {
|
||||
return nil
|
||||
}
|
||||
sys, net, err := ic.Chain.CalculateClaimable(util.Fixed8(acc.NEO.Balance.Int64()), acc.NEO.BalanceHeight, ic.Block.Index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
acc.NEO.BalanceHeight = ic.Block.Index
|
||||
n.GAS.mint(ic, acc.ScriptHash, big.NewInt(int64(sys+net)))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NEO) unclaimedGas(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||
u := toUint160(args[0])
|
||||
end := uint32(toBigInt(args[1]).Int64())
|
||||
bs, err := ic.DAO.GetNEP5Balances(u)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tr := bs.Trackers[n.Hash]
|
||||
|
||||
sys, net, err := ic.Chain.CalculateClaimable(util.Fixed8(tr.Balance), tr.LastUpdatedBlock, end)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return vm.NewBigIntegerItem(big.NewInt(int64(sys.Add(net))))
|
||||
}
|
||||
|
||||
func (n *NEO) registerValidator(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||
err := n.registerValidatorInternal(ic, toPublicKey(args[0]))
|
||||
return vm.NewBoolItem(err == nil)
|
||||
}
|
||||
|
||||
func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey) error {
|
||||
_, err := ic.DAO.GetValidatorState(pub)
|
||||
if err == nil {
|
||||
return err
|
||||
}
|
||||
return ic.DAO.PutValidatorState(&state.Validator{PublicKey: pub})
|
||||
}
|
||||
|
||||
func (n *NEO) vote(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||
acc := toUint160(args[0])
|
||||
arr := args[1].Value().([]vm.StackItem)
|
||||
var pubs keys.PublicKeys
|
||||
for i := range arr {
|
||||
pub := new(keys.PublicKey)
|
||||
bs, err := arr[i].TryBytes()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
} else if err := pub.DecodeBytes(bs); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pubs = append(pubs, pub)
|
||||
}
|
||||
err := n.VoteInternal(ic, acc, pubs)
|
||||
return vm.NewBoolItem(err == nil)
|
||||
}
|
||||
|
||||
// VoteInternal votes from account h for validarors specified in pubs.
|
||||
func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.PublicKeys) error {
|
||||
ok, err := runtime.CheckHashedWitness(ic, h)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !ok {
|
||||
return errors.New("invalid signature")
|
||||
}
|
||||
acc, err := ic.DAO.GetAccountState(h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
balance := util.Fixed8(acc.NEO.Balance.Int64())
|
||||
if err := ModifyAccountVotes(acc, ic.DAO, -balance); err != nil {
|
||||
return err
|
||||
}
|
||||
pubs = pubs.Unique()
|
||||
var newPubs keys.PublicKeys
|
||||
for _, pub := range pubs {
|
||||
_, err := ic.DAO.GetValidatorState(pub)
|
||||
if err != nil {
|
||||
if err == storage.ErrKeyNotFound {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
newPubs = append(newPubs, pub)
|
||||
}
|
||||
if lp, lv := len(newPubs), len(acc.Votes); lp != lv {
|
||||
vc, err := ic.DAO.GetValidatorsCount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if lv > 0 {
|
||||
vc[lv-1] -= balance
|
||||
}
|
||||
if len(newPubs) > 0 {
|
||||
vc[lp-1] += balance
|
||||
}
|
||||
if err := ic.DAO.PutValidatorsCount(vc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
acc.Votes = newPubs
|
||||
return ModifyAccountVotes(acc, ic.DAO, balance)
|
||||
}
|
||||
|
||||
// ModifyAccountVotes modifies votes of the specified account by value (can be negative).
|
||||
func ModifyAccountVotes(acc *state.Account, d dao.DAO, value util.Fixed8) error {
|
||||
for _, vote := range acc.Votes {
|
||||
validator, err := d.GetValidatorStateOrNew(vote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
validator.Votes += value
|
||||
if validator.UnregisteredAndHasNoVotes() {
|
||||
if err := d.DeleteValidatorState(validator); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := d.PutValidatorState(validator); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NEO) getRegisteredValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
|
||||
vs := ic.DAO.GetValidators()
|
||||
arr := make([]vm.StackItem, len(vs))
|
||||
for i := range vs {
|
||||
arr[i] = vm.NewStructItem([]vm.StackItem{
|
||||
vm.NewByteArrayItem(vs[i].PublicKey.Bytes()),
|
||||
vm.NewBigIntegerItem(big.NewInt(int64(vs[i].Votes))),
|
||||
})
|
||||
}
|
||||
return vm.NewArrayItem(arr)
|
||||
}
|
||||
|
||||
// GetValidatorsInternal returns a list of current validators.
|
||||
func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) ([]*keys.PublicKey, error) {
|
||||
validatorsCount, err := d.GetValidatorsCount()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(validatorsCount) == 0 {
|
||||
sb, err := bc.GetStandByValidators()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sb, nil
|
||||
}
|
||||
|
||||
validators := d.GetValidators()
|
||||
sort.Slice(validators, func(i, j int) bool {
|
||||
// Unregistered validators go to the end of the list.
|
||||
if validators[i].Registered != validators[j].Registered {
|
||||
return validators[i].Registered
|
||||
}
|
||||
// The most-voted validators should end up in the front of the list.
|
||||
if validators[i].Votes != validators[j].Votes {
|
||||
return validators[i].Votes > validators[j].Votes
|
||||
}
|
||||
// Ties are broken with public keys.
|
||||
return validators[i].PublicKey.Cmp(validators[j].PublicKey) == -1
|
||||
})
|
||||
|
||||
count := validatorsCount.GetWeightedAverage()
|
||||
standByValidators, err := bc.GetStandByValidators()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count < len(standByValidators) {
|
||||
count = len(standByValidators)
|
||||
}
|
||||
|
||||
uniqueSBValidators := standByValidators.Unique()
|
||||
result := keys.PublicKeys{}
|
||||
for _, validator := range validators {
|
||||
if validator.RegisteredAndHasVotes() || uniqueSBValidators.Contains(validator.PublicKey) {
|
||||
result = append(result, validator.PublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
if result.Len() >= count {
|
||||
result = result[:count]
|
||||
} else {
|
||||
for i := 0; i < uniqueSBValidators.Len() && result.Len() < count; i++ {
|
||||
if !result.Contains(uniqueSBValidators[i]) {
|
||||
result = append(result, uniqueSBValidators[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Sort(result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (n *NEO) getValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
|
||||
result, err := n.GetValidatorsInternal(ic.Chain, ic.DAO)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return pubsToArray(result)
|
||||
}
|
||||
|
||||
func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
|
||||
result, err := n.GetNextBlockValidatorsInternal(ic.Chain, ic.DAO)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return pubsToArray(result)
|
||||
}
|
||||
|
||||
// GetNextBlockValidatorsInternal returns next block validators.
|
||||
func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) ([]*keys.PublicKey, error) {
|
||||
result, err := d.GetNextBlockValidators()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if result == nil {
|
||||
return bc.GetStandByValidators()
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func pubsToArray(pubs keys.PublicKeys) vm.StackItem {
|
||||
arr := make([]vm.StackItem, len(pubs))
|
||||
for i := range pubs {
|
||||
arr[i] = vm.NewByteArrayItem(pubs[i].Bytes())
|
||||
}
|
||||
return vm.NewArrayItem(arr)
|
||||
}
|
||||
|
||||
func toPublicKey(s vm.StackItem) *keys.PublicKey {
|
||||
buf, err := s.TryBytes()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pub := new(keys.PublicKey)
|
||||
if err := pub.DecodeBytes(buf); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return pub
|
||||
}
|
229
pkg/core/native/native_nep5.go
Normal file
229
pkg/core/native/native_nep5.go
Normal file
|
@ -0,0 +1,229 @@
|
|||
package native
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"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/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/vm"
|
||||
)
|
||||
|
||||
// nep5TokenNative represents NEP-5 token contract.
|
||||
type nep5TokenNative struct {
|
||||
ContractMD
|
||||
name string
|
||||
symbol string
|
||||
decimals int64
|
||||
factor int64
|
||||
totalSupply big.Int
|
||||
onPersist func(*interop.Context) error
|
||||
incBalance func(*interop.Context, *state.Account, *big.Int) error
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) Metadata() *ContractMD {
|
||||
return &c.ContractMD
|
||||
}
|
||||
|
||||
var _ Contract = (*nep5TokenNative)(nil)
|
||||
|
||||
func newNEP5Native(name string) *nep5TokenNative {
|
||||
n := &nep5TokenNative{ContractMD: *NewContractMD(name)}
|
||||
|
||||
desc := newDescriptor("name", smartcontract.StringType)
|
||||
md := newMethodAndPrice(n.Name, 1, smartcontract.NoneFlag)
|
||||
n.AddMethod(md, desc, true)
|
||||
|
||||
desc = newDescriptor("symbol", smartcontract.StringType)
|
||||
md = newMethodAndPrice(n.Symbol, 1, smartcontract.NoneFlag)
|
||||
n.AddMethod(md, desc, true)
|
||||
|
||||
desc = newDescriptor("decimals", smartcontract.IntegerType)
|
||||
md = newMethodAndPrice(n.Decimals, 1, smartcontract.NoneFlag)
|
||||
n.AddMethod(md, desc, true)
|
||||
|
||||
desc = newDescriptor("balanceOf", smartcontract.IntegerType,
|
||||
manifest.NewParameter("account", smartcontract.Hash160Type))
|
||||
md = newMethodAndPrice(n.balanceOf, 1, smartcontract.NoneFlag)
|
||||
n.AddMethod(md, desc, true)
|
||||
|
||||
desc = newDescriptor("transfer", smartcontract.BoolType,
|
||||
manifest.NewParameter("from", smartcontract.Hash160Type),
|
||||
manifest.NewParameter("to", smartcontract.Hash160Type),
|
||||
manifest.NewParameter("amount", smartcontract.IntegerType),
|
||||
)
|
||||
md = newMethodAndPrice(n.Transfer, 1, smartcontract.NoneFlag)
|
||||
n.AddMethod(md, desc, false)
|
||||
|
||||
n.AddEvent("Transfer", desc.Parameters...)
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) Initialize() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) Name(_ *interop.Context, _ []vm.StackItem) vm.StackItem {
|
||||
return vm.NewByteArrayItem([]byte(c.name))
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) Symbol(_ *interop.Context, _ []vm.StackItem) vm.StackItem {
|
||||
return vm.NewByteArrayItem([]byte(c.symbol))
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []vm.StackItem) vm.StackItem {
|
||||
return vm.NewBigIntegerItem(big.NewInt(c.decimals))
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) Transfer(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||
from := toUint160(args[0])
|
||||
to := toUint160(args[1])
|
||||
amount := toBigInt(args[2])
|
||||
err := c.transfer(ic, from, to, amount)
|
||||
return vm.NewBoolItem(err == nil)
|
||||
}
|
||||
|
||||
func addrToStackItem(u *util.Uint160) vm.StackItem {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
return vm.NewByteArrayItem(u.BytesBE())
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) {
|
||||
ne := state.NotificationEvent{
|
||||
ScriptHash: c.Hash,
|
||||
Item: vm.NewArrayItem([]vm.StackItem{
|
||||
vm.NewByteArrayItem([]byte("Transfer")),
|
||||
addrToStackItem(from),
|
||||
addrToStackItem(to),
|
||||
vm.NewBigIntegerItem(amount),
|
||||
}),
|
||||
}
|
||||
ic.Notifications = append(ic.Notifications, ne)
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, amount *big.Int) error {
|
||||
if amount.Sign() == -1 {
|
||||
return errors.New("negative amount")
|
||||
}
|
||||
|
||||
accFrom, err := ic.DAO.GetAccountStateOrNew(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isEmpty := from.Equals(to) || amount.Sign() == 0
|
||||
inc := amount
|
||||
if isEmpty {
|
||||
inc = big.NewInt(0)
|
||||
}
|
||||
if err := c.incBalance(ic, accFrom, inc); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ic.DAO.PutAccountState(accFrom); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isEmpty {
|
||||
accTo, err := ic.DAO.GetAccountStateOrNew(to)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.incBalance(ic, accTo, amount); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ic.DAO.PutAccountState(accTo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.emitTransfer(ic, &from, &to, amount)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) balanceOf(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||
h := toUint160(args[0])
|
||||
bs, err := ic.DAO.GetNEP5Balances(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
balance := bs.Trackers[c.Hash].Balance
|
||||
return vm.NewBigIntegerItem(big.NewInt(balance))
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) mint(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
||||
c.addTokens(ic, h, amount)
|
||||
c.emitTransfer(ic, nil, &h, amount)
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) burn(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
||||
amount = new(big.Int).Neg(amount)
|
||||
c.addTokens(ic, h, amount)
|
||||
c.emitTransfer(ic, &h, nil, amount)
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
||||
if sign := amount.Sign(); sign == -1 {
|
||||
panic("negative amount")
|
||||
} else if sign == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
acc, err := ic.DAO.GetAccountStateOrNew(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := c.incBalance(ic, acc, amount); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := ic.DAO.PutAccountState(acc); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.totalSupply.Add(&c.totalSupply, amount)
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) OnPersist(ic *interop.Context) error {
|
||||
return c.onPersist(ic)
|
||||
}
|
||||
|
||||
func newDescriptor(name string, ret smartcontract.ParamType, ps ...manifest.Parameter) *manifest.Method {
|
||||
return &manifest.Method{
|
||||
Name: name,
|
||||
Parameters: ps,
|
||||
ReturnType: ret,
|
||||
}
|
||||
}
|
||||
|
||||
func newMethodAndPrice(f Method, price int64, flags smartcontract.CallFlag) *MethodAndPrice {
|
||||
return &MethodAndPrice{
|
||||
Func: f,
|
||||
Price: price,
|
||||
RequiredFlags: flags,
|
||||
}
|
||||
}
|
||||
|
||||
func toBigInt(s vm.StackItem) *big.Int {
|
||||
bi, err := s.TryInteger()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bi
|
||||
}
|
||||
|
||||
func toUint160(s vm.StackItem) util.Uint160 {
|
||||
buf, err := s.TryBytes()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
u, err := util.Uint160DecodeBytesBE(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return u
|
||||
}
|
|
@ -33,6 +33,8 @@ type Account struct {
|
|||
ScriptHash util.Uint160
|
||||
IsFrozen bool
|
||||
Votes []*keys.PublicKey
|
||||
GAS NEP5BalanceState
|
||||
NEO NEOBalanceState
|
||||
Balances map[util.Uint256][]UnspentBalance
|
||||
Unclaimed UnclaimedBalances
|
||||
}
|
||||
|
@ -55,6 +57,8 @@ func (s *Account) DecodeBinary(br *io.BinReader) {
|
|||
br.ReadBytes(s.ScriptHash[:])
|
||||
s.IsFrozen = br.ReadBool()
|
||||
br.ReadArray(&s.Votes)
|
||||
s.GAS.DecodeBinary(br)
|
||||
s.NEO.DecodeBinary(br)
|
||||
|
||||
s.Balances = make(map[util.Uint256][]UnspentBalance)
|
||||
lenBalances := br.ReadVarUint()
|
||||
|
@ -80,6 +84,8 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) {
|
|||
bw.WriteBytes(s.ScriptHash[:])
|
||||
bw.WriteBool(s.IsFrozen)
|
||||
bw.WriteArray(s.Votes)
|
||||
s.GAS.EncodeBinary(bw)
|
||||
s.NEO.EncodeBinary(bw)
|
||||
|
||||
bw.WriteVarUint(uint64(len(s.Balances)))
|
||||
for k, v := range s.Balances {
|
||||
|
|
45
pkg/core/state/native_state.go
Normal file
45
pkg/core/state/native_state.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
)
|
||||
|
||||
// NEP5BalanceState represents balance state of a NEP5-token.
|
||||
type NEP5BalanceState struct {
|
||||
Balance big.Int
|
||||
}
|
||||
|
||||
// NEOBalanceState represents balance state of a NEO-token.
|
||||
type NEOBalanceState struct {
|
||||
NEP5BalanceState
|
||||
BalanceHeight uint32
|
||||
}
|
||||
|
||||
// EncodeBinary implements io.Serializable interface.
|
||||
func (s *NEP5BalanceState) EncodeBinary(w *io.BinWriter) {
|
||||
w.WriteVarBytes(emit.IntToBytes(&s.Balance))
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable interface.
|
||||
func (s *NEP5BalanceState) DecodeBinary(r *io.BinReader) {
|
||||
buf := r.ReadVarBytes()
|
||||
if r.Err != nil {
|
||||
return
|
||||
}
|
||||
s.Balance = *emit.BytesToInt(buf)
|
||||
}
|
||||
|
||||
// EncodeBinary implements io.Serializable interface.
|
||||
func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) {
|
||||
s.NEP5BalanceState.EncodeBinary(w)
|
||||
w.WriteU32LE(s.BalanceHeight)
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable interface.
|
||||
func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
|
||||
s.NEP5BalanceState.DecodeBinary(r)
|
||||
s.BalanceHeight = r.ReadU32LE()
|
||||
}
|
|
@ -12,10 +12,12 @@ const (
|
|||
STAccount KeyPrefix = 0x40
|
||||
STCoin KeyPrefix = 0x44
|
||||
STSpentCoin KeyPrefix = 0x45
|
||||
STNextValidators KeyPrefix = 0x47
|
||||
STValidator KeyPrefix = 0x48
|
||||
STAsset KeyPrefix = 0x4c
|
||||
STNotification KeyPrefix = 0x4d
|
||||
STContract KeyPrefix = 0x50
|
||||
STNativeContract KeyPrefix = 0x51
|
||||
STStorage KeyPrefix = 0x70
|
||||
STNEP5Transfers KeyPrefix = 0x72
|
||||
STNEP5Balances KeyPrefix = 0x73
|
||||
|
|
Loading…
Reference in a new issue