neoneo-go/pkg/core/native/native_gas.go
Anna Shaleva c7a040ff81 core: fix bug with native contracts methods initialisation
`NewNEO()` and `NewGAS()` methods are trying to initialise
both `onPersist` and `incBalance` methods of NEO and GAS AFTER
nep5TokenNative is set to the VALUE of created nep5 token.
In this situation an attemmpt to call the corresponding native contracts
methods (e.g. transfer native GAS) leads to contract invocation failure,
as far as `nep5TokenNative.incBalance` method is nil.

Fixed this by initializing both `onPersist` and `incBalance` methods
before getting the value of nep5 contract.
2020-05-20 23:16:04 +03:00

128 lines
3.3 KiB
Go

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/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"
)
// GAS represents GAS native contract.
type GAS struct {
nep5TokenNative
NEO *NEO
}
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.
func NewGAS() *GAS {
g := &GAS{}
nep5 := newNEP5Native(gasSyscallName)
nep5.name = "GAS"
nep5.symbol = "gas"
nep5.decimals = 8
nep5.factor = GASFactor
nep5.onPersist = chainOnPersist(g.onPersist, g.OnPersist)
nep5.incBalance = g.increaseBalance
g.nep5TokenNative = *nep5
desc := newDescriptor("getSysFeeAmount", smartcontract.IntegerType,
manifest.NewParameter("index", smartcontract.IntegerType))
md := newMethodAndPrice(g.getSysFeeAmount, 1, smartcontract.NoneFlag)
g.AddMethod(md, desc, true)
return g
}
func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int) error {
acc, err := state.NEP5BalanceStateFromBytes(si.Value)
if err != nil {
return err
}
if sign := amount.Sign(); sign == 0 {
return nil
} else if sign == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
return errors.New("insufficient funds")
}
acc.Balance.Add(&acc.Balance, amount)
si.Value = acc.Bytes()
return nil
}
// Initialize initializes GAS contract.
func (g *GAS) Initialize(ic *interop.Context) error {
if err := g.nep5TokenNative.Initialize(ic); err != nil {
return err
}
if g.nep5TokenNative.getTotalSupply(ic).Sign() != 0 {
return errors.New("already initialized")
}
h, _, err := getStandbyValidatorsHash(ic)
if err != nil {
return err
}
g.mint(ic, h, big.NewInt(initialGAS*GASFactor))
return nil
}
// 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 nil
}
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
}
}