2020-03-25 10:00:11 +00:00
|
|
|
package native
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"math/big"
|
|
|
|
|
2021-07-25 12:00:44 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
2020-03-25 10:00:11 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
2020-12-13 18:25:04 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
2020-03-25 10:00:11 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
2021-12-13 15:16:27 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
2020-03-25 10:00:11 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
2022-01-24 15:36:31 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2020-03-25 10:00:11 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
|
|
)
|
|
|
|
|
|
|
|
// GAS represents GAS native contract.
|
|
|
|
type GAS struct {
|
2020-11-19 15:01:42 +00:00
|
|
|
nep17TokenNative
|
2020-03-25 10:00:11 +00:00
|
|
|
NEO *NEO
|
2022-03-01 10:10:54 +00:00
|
|
|
// Notary is a native Notary contract. It is set only when P2PSigExtensions are on.
|
|
|
|
Notary *Notary
|
2021-07-20 12:54:56 +00:00
|
|
|
|
2021-12-13 15:16:27 +00:00
|
|
|
initialSupply int64
|
|
|
|
p2pSigExtensionsEnabled bool
|
2020-03-25 10:00:11 +00:00
|
|
|
}
|
|
|
|
|
2021-02-15 15:43:10 +00:00
|
|
|
const gasContractID = -6
|
2020-03-25 10:00:11 +00:00
|
|
|
|
2020-04-22 20:00:18 +00:00
|
|
|
// GASFactor is a divisor for finding GAS integral value.
|
|
|
|
const GASFactor = NEOTotalSupply
|
|
|
|
|
2020-10-02 10:50:56 +00:00
|
|
|
// newGAS returns GAS native contract.
|
2021-12-13 15:16:27 +00:00
|
|
|
func newGAS(init int64, p2pSigExtensionsEnabled bool) *GAS {
|
|
|
|
g := &GAS{
|
|
|
|
initialSupply: init,
|
|
|
|
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
|
|
|
|
}
|
2021-02-15 13:40:44 +00:00
|
|
|
defer g.UpdateHash()
|
|
|
|
|
2021-01-15 21:17:31 +00:00
|
|
|
nep17 := newNEP17Native(nativenames.Gas, gasContractID)
|
2020-12-13 18:05:49 +00:00
|
|
|
nep17.symbol = "GAS"
|
2020-11-19 15:01:42 +00:00
|
|
|
nep17.decimals = 8
|
|
|
|
nep17.factor = GASFactor
|
|
|
|
nep17.incBalance = g.increaseBalance
|
2021-04-01 15:16:43 +00:00
|
|
|
nep17.balFromBytes = g.balanceFromBytes
|
2020-03-25 10:00:11 +00:00
|
|
|
|
2020-11-19 15:01:42 +00:00
|
|
|
g.nep17TokenNative = *nep17
|
2020-03-25 10:00:11 +00:00
|
|
|
|
|
|
|
return g
|
|
|
|
}
|
|
|
|
|
2022-05-13 11:49:41 +00:00
|
|
|
func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int, checkBal *big.Int) (func(), error) {
|
2021-07-18 09:39:31 +00:00
|
|
|
acc, err := state.NEP17BalanceFromBytes(*si)
|
2020-04-23 18:28:37 +00:00
|
|
|
if err != nil {
|
2022-05-13 11:49:41 +00:00
|
|
|
return nil, err
|
2020-04-23 18:28:37 +00:00
|
|
|
}
|
2020-03-25 10:00:11 +00:00
|
|
|
if sign := amount.Sign(); sign == 0 {
|
2021-08-02 20:16:12 +00:00
|
|
|
// Requested self-transfer amount can be higher than actual balance.
|
|
|
|
if checkBal != nil && acc.Balance.Cmp(checkBal) < 0 {
|
|
|
|
err = errors.New("insufficient funds")
|
|
|
|
}
|
2022-05-13 11:49:41 +00:00
|
|
|
return nil, err
|
2021-12-02 12:30:20 +00:00
|
|
|
} else if sign == -1 && acc.Balance.CmpAbs(amount) == -1 {
|
2022-05-13 11:49:41 +00:00
|
|
|
return nil, errors.New("insufficient funds")
|
2020-03-25 10:00:11 +00:00
|
|
|
}
|
2020-04-23 18:28:37 +00:00
|
|
|
acc.Balance.Add(&acc.Balance, amount)
|
2020-06-23 18:57:05 +00:00
|
|
|
if acc.Balance.Sign() != 0 {
|
2021-08-04 08:24:02 +00:00
|
|
|
*si = acc.Bytes(nil)
|
2020-06-23 18:57:05 +00:00
|
|
|
} else {
|
2021-03-05 14:06:54 +00:00
|
|
|
*si = nil
|
2020-06-23 18:57:05 +00:00
|
|
|
}
|
2022-05-13 11:49:41 +00:00
|
|
|
return nil, nil
|
2020-03-25 10:00:11 +00:00
|
|
|
}
|
|
|
|
|
2021-04-01 15:16:43 +00:00
|
|
|
func (g *GAS) balanceFromBytes(si *state.StorageItem) (*big.Int, error) {
|
2021-07-18 09:39:31 +00:00
|
|
|
acc, err := state.NEP17BalanceFromBytes(*si)
|
2021-04-01 15:16:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &acc.Balance, err
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Initialize initializes a GAS contract.
|
2020-03-25 10:00:11 +00:00
|
|
|
func (g *GAS) Initialize(ic *interop.Context) error {
|
2020-11-19 15:01:42 +00:00
|
|
|
if err := g.nep17TokenNative.Initialize(ic); err != nil {
|
2020-03-25 10:00:11 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-08-02 21:19:23 +00:00
|
|
|
_, totalSupply := g.nep17TokenNative.getTotalSupply(ic.DAO)
|
|
|
|
if totalSupply.Sign() != 0 {
|
2020-04-22 20:00:18 +00:00
|
|
|
return errors.New("already initialized")
|
2020-03-25 10:00:11 +00:00
|
|
|
}
|
2020-08-10 14:51:46 +00:00
|
|
|
h, err := getStandbyValidatorsHash(ic)
|
2020-03-25 10:00:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-07-20 12:54:56 +00:00
|
|
|
g.mint(ic, h, big.NewInt(g.initialSupply), false)
|
2020-04-22 20:00:18 +00:00
|
|
|
return nil
|
2020-03-25 10:00:11 +00:00
|
|
|
}
|
|
|
|
|
2023-04-26 09:52:59 +00:00
|
|
|
// InitializeCache implements the Contract interface.
|
|
|
|
func (g *GAS) InitializeCache(blockHeight uint32, d *dao.Simple) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// OnPersist implements the Contract interface.
|
2020-03-25 10:00:11 +00:00
|
|
|
func (g *GAS) OnPersist(ic *interop.Context) error {
|
2020-06-18 18:51:29 +00:00
|
|
|
if len(ic.Block.Transactions) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2020-05-08 17:54:24 +00:00
|
|
|
for _, tx := range ic.Block.Transactions {
|
2020-06-23 14:15:35 +00:00
|
|
|
absAmount := big.NewInt(tx.SystemFee + tx.NetworkFee)
|
2020-07-29 16:57:38 +00:00
|
|
|
g.burn(ic, tx.Sender(), absAmount)
|
2020-05-08 17:54:24 +00:00
|
|
|
}
|
2022-04-12 14:29:11 +00:00
|
|
|
validators := g.NEO.GetNextBlockValidatorsInternal(ic.DAO)
|
2021-03-01 12:20:27 +00:00
|
|
|
primary := validators[ic.Block.PrimaryIndex].GetScriptHash()
|
2020-06-23 14:15:35 +00:00
|
|
|
var netFee int64
|
2020-05-08 17:54:24 +00:00
|
|
|
for _, tx := range ic.Block.Transactions {
|
|
|
|
netFee += tx.NetworkFee
|
2021-12-13 15:16:27 +00:00
|
|
|
if g.p2pSigExtensionsEnabled {
|
|
|
|
// Reward for NotaryAssisted attribute will be minted to designated notary nodes
|
|
|
|
// by Notary contract.
|
|
|
|
attrs := tx.GetAttributes(transaction.NotaryAssistedT)
|
|
|
|
if len(attrs) != 0 {
|
|
|
|
na := attrs[0].Value.(*transaction.NotaryAssisted)
|
2022-03-01 10:10:54 +00:00
|
|
|
netFee -= (int64(na.NKeys) + 1) * g.Notary.GetNotaryServiceFeePerKey(ic.DAO)
|
2021-12-13 15:16:27 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-08 17:54:24 +00:00
|
|
|
}
|
2020-11-30 10:05:42 +00:00
|
|
|
g.mint(ic, primary, big.NewInt(int64(netFee)), false)
|
2020-04-22 20:00:18 +00:00
|
|
|
return nil
|
2020-03-25 10:00:11 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// PostPersist implements the Contract interface.
|
2020-12-13 18:36:06 +00:00
|
|
|
func (g *GAS) PostPersist(ic *interop.Context) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-07-25 12:00:44 +00:00
|
|
|
// BalanceOf returns native GAS token balance for the acc.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (g *GAS) BalanceOf(d *dao.Simple, acc util.Uint160) *big.Int {
|
2021-07-25 12:00:44 +00:00
|
|
|
return g.balanceOfInternal(d, acc)
|
|
|
|
}
|
|
|
|
|
2020-08-10 14:51:46 +00:00
|
|
|
func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, error) {
|
2022-01-24 15:36:31 +00:00
|
|
|
cfg := ic.Chain.GetConfig()
|
|
|
|
committee, err := keys.NewPublicKeysFromStrings(cfg.StandbyCommittee)
|
|
|
|
if err != nil {
|
|
|
|
return util.Uint160{}, err
|
|
|
|
}
|
|
|
|
s, err := smartcontract.CreateDefaultMultiSigRedeemScript(committee[:cfg.GetNumOfCNs(0)])
|
2020-03-25 10:00:11 +00:00
|
|
|
if err != nil {
|
2020-08-10 14:51:46 +00:00
|
|
|
return util.Uint160{}, err
|
2020-03-25 10:00:11 +00:00
|
|
|
}
|
2020-08-10 14:51:46 +00:00
|
|
|
return hash.Hash160(s), nil
|
2020-03-25 10:00:11 +00:00
|
|
|
}
|