neoneo-go/pkg/core/native/native_gas.go
Roman Khimov 64c780ad7a native: optimize totalSupply operations during token burn/mint
We burn GAS in OnPersist for every transaction so some buffer reuse here is
quite natural.

This also doesn't change a lot in the overall TPS picture, maybe adding some
1%.
2021-08-03 17:59:38 +03:00

158 lines
4.2 KiB
Go

package native
import (
"errors"
"fmt"
"math/big"
"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/native/nativenames"
"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/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"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/stackitem"
)
// GAS represents GAS native contract.
type GAS struct {
nep17TokenNative
NEO *NEO
initialSupply int64
}
const gasContractID = -6
// GASFactor is a divisor for finding GAS integral value.
const GASFactor = NEOTotalSupply
// newGAS returns GAS native contract.
func newGAS(init int64) *GAS {
g := &GAS{initialSupply: init}
defer g.UpdateHash()
nep17 := newNEP17Native(nativenames.Gas, gasContractID)
nep17.symbol = "GAS"
nep17.decimals = 8
nep17.factor = GASFactor
nep17.incBalance = g.increaseBalance
nep17.balFromBytes = g.balanceFromBytes
g.nep17TokenNative = *nep17
desc := newDescriptor("refuel", smartcontract.VoidType,
manifest.NewParameter("account", smartcontract.Hash160Type),
manifest.NewParameter("amount", smartcontract.IntegerType))
md := newMethodAndPrice(g.refuel, 1<<15, callflag.States|callflag.AllowNotify)
g.AddMethod(md, desc)
return g
}
func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int, checkBal *big.Int) error {
acc, err := state.NEP17BalanceFromBytes(*si)
if err != nil {
return err
}
if sign := amount.Sign(); sign == 0 {
// Requested self-transfer amount can be higher than actual balance.
if checkBal != nil && acc.Balance.Cmp(checkBal) < 0 {
err = errors.New("insufficient funds")
}
return err
} else if sign == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
return errors.New("insufficient funds")
}
acc.Balance.Add(&acc.Balance, amount)
if acc.Balance.Sign() != 0 {
*si = acc.Bytes()
} else {
*si = nil
}
return nil
}
func (g *GAS) balanceFromBytes(si *state.StorageItem) (*big.Int, error) {
acc, err := state.NEP17BalanceFromBytes(*si)
if err != nil {
return nil, err
}
return &acc.Balance, err
}
func (g *GAS) refuel(ic *interop.Context, args []stackitem.Item) stackitem.Item {
acc := toUint160(args[0])
gas := toBigInt(args[1])
if !gas.IsInt64() || gas.Sign() == -1 {
panic("invalid GAS value")
}
ok, err := runtime.CheckHashedWitness(ic, acc)
if !ok || err != nil {
panic(fmt.Errorf("%w: %v", ErrInvalidWitness, err))
}
g.burn(ic, acc, gas)
ic.VM.GasLimit += gas.Int64()
return stackitem.Null{}
}
// Initialize initializes GAS contract.
func (g *GAS) Initialize(ic *interop.Context) error {
if err := g.nep17TokenNative.Initialize(ic); err != nil {
return err
}
_, totalSupply := g.nep17TokenNative.getTotalSupply(ic.DAO)
if totalSupply.Sign() != 0 {
return errors.New("already initialized")
}
h, err := getStandbyValidatorsHash(ic)
if err != nil {
return err
}
g.mint(ic, h, big.NewInt(g.initialSupply), false)
return nil
}
// OnPersist implements Contract interface.
func (g *GAS) OnPersist(ic *interop.Context) error {
if len(ic.Block.Transactions) == 0 {
return nil
}
for _, tx := range ic.Block.Transactions {
absAmount := big.NewInt(tx.SystemFee + tx.NetworkFee)
g.burn(ic, tx.Sender(), absAmount)
}
validators := g.NEO.GetNextBlockValidatorsInternal()
primary := validators[ic.Block.PrimaryIndex].GetScriptHash()
var netFee int64
for _, tx := range ic.Block.Transactions {
netFee += tx.NetworkFee
}
g.mint(ic, primary, big.NewInt(int64(netFee)), false)
return nil
}
// PostPersist implements Contract interface.
func (g *GAS) PostPersist(ic *interop.Context) error {
return nil
}
// BalanceOf returns native GAS token balance for the acc.
func (g *GAS) BalanceOf(d dao.DAO, acc util.Uint160) *big.Int {
return g.balanceOfInternal(d, acc)
}
func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, error) {
s, err := smartcontract.CreateDefaultMultiSigRedeemScript(ic.Chain.GetStandByValidators())
if err != nil {
return util.Uint160{}, err
}
return hash.Hash160(s), nil
}