github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/native_gas.go (about)

     1  package native
     2  
     3  import (
     4  	"errors"
     5  	"math/big"
     6  
     7  	"github.com/nspcc-dev/neo-go/pkg/config"
     8  	"github.com/nspcc-dev/neo-go/pkg/core/dao"
     9  	"github.com/nspcc-dev/neo-go/pkg/core/interop"
    10  	"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
    11  	"github.com/nspcc-dev/neo-go/pkg/core/state"
    12  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    13  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
    14  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    15  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    16  	"github.com/nspcc-dev/neo-go/pkg/util"
    17  )
    18  
    19  // GAS represents GAS native contract.
    20  type GAS struct {
    21  	nep17TokenNative
    22  	NEO    *NEO
    23  	Policy *Policy
    24  
    25  	initialSupply           int64
    26  	p2pSigExtensionsEnabled bool
    27  }
    28  
    29  const gasContractID = -6
    30  
    31  // GASFactor is a divisor for finding GAS integral value.
    32  const GASFactor = NEOTotalSupply
    33  
    34  // newGAS returns GAS native contract.
    35  func newGAS(init int64, p2pSigExtensionsEnabled bool) *GAS {
    36  	g := &GAS{
    37  		initialSupply:           init,
    38  		p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
    39  	}
    40  	defer g.BuildHFSpecificMD(g.ActiveIn())
    41  
    42  	nep17 := newNEP17Native(nativenames.Gas, gasContractID)
    43  	nep17.symbol = "GAS"
    44  	nep17.decimals = 8
    45  	nep17.factor = GASFactor
    46  	nep17.incBalance = g.increaseBalance
    47  	nep17.balFromBytes = g.balanceFromBytes
    48  
    49  	g.nep17TokenNative = *nep17
    50  
    51  	return g
    52  }
    53  
    54  func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int, checkBal *big.Int) (func(), error) {
    55  	acc, err := state.NEP17BalanceFromBytes(*si)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	if sign := amount.Sign(); sign == 0 {
    60  		// Requested self-transfer amount can be higher than actual balance.
    61  		if checkBal != nil && acc.Balance.Cmp(checkBal) < 0 {
    62  			err = errors.New("insufficient funds")
    63  		}
    64  		return nil, err
    65  	} else if sign == -1 && acc.Balance.CmpAbs(amount) == -1 {
    66  		return nil, errors.New("insufficient funds")
    67  	}
    68  	acc.Balance.Add(&acc.Balance, amount)
    69  	if acc.Balance.Sign() != 0 {
    70  		*si = acc.Bytes(nil)
    71  	} else {
    72  		*si = nil
    73  	}
    74  	return nil, nil
    75  }
    76  
    77  func (g *GAS) balanceFromBytes(si *state.StorageItem) (*big.Int, error) {
    78  	acc, err := state.NEP17BalanceFromBytes(*si)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	return &acc.Balance, err
    83  }
    84  
    85  // Initialize initializes a GAS contract.
    86  func (g *GAS) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
    87  	if hf != g.ActiveIn() {
    88  		return nil
    89  	}
    90  
    91  	if err := g.nep17TokenNative.Initialize(ic); err != nil {
    92  		return err
    93  	}
    94  	_, totalSupply := g.nep17TokenNative.getTotalSupply(ic.DAO)
    95  	if totalSupply.Sign() != 0 {
    96  		return errors.New("already initialized")
    97  	}
    98  	h, err := getStandbyValidatorsHash(ic)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	g.mint(ic, h, big.NewInt(g.initialSupply), false)
   103  	return nil
   104  }
   105  
   106  // InitializeCache implements the Contract interface.
   107  func (g *GAS) InitializeCache(blockHeight uint32, d *dao.Simple) error {
   108  	return nil
   109  }
   110  
   111  // OnPersist implements the Contract interface.
   112  func (g *GAS) OnPersist(ic *interop.Context) error {
   113  	if len(ic.Block.Transactions) == 0 {
   114  		return nil
   115  	}
   116  	for _, tx := range ic.Block.Transactions {
   117  		absAmount := big.NewInt(tx.SystemFee + tx.NetworkFee)
   118  		g.burn(ic, tx.Sender(), absAmount)
   119  	}
   120  	validators := g.NEO.GetNextBlockValidatorsInternal(ic.DAO)
   121  	primary := validators[ic.Block.PrimaryIndex].GetScriptHash()
   122  	var netFee int64
   123  	for _, tx := range ic.Block.Transactions {
   124  		netFee += tx.NetworkFee
   125  		if g.p2pSigExtensionsEnabled {
   126  			// Reward for NotaryAssisted attribute will be minted to designated notary nodes
   127  			// by Notary contract.
   128  			attrs := tx.GetAttributes(transaction.NotaryAssistedT)
   129  			if len(attrs) != 0 {
   130  				na := attrs[0].Value.(*transaction.NotaryAssisted)
   131  				netFee -= (int64(na.NKeys) + 1) * g.Policy.GetAttributeFeeInternal(ic.DAO, transaction.NotaryAssistedT)
   132  			}
   133  		}
   134  	}
   135  	g.mint(ic, primary, big.NewInt(int64(netFee)), false)
   136  	return nil
   137  }
   138  
   139  // PostPersist implements the Contract interface.
   140  func (g *GAS) PostPersist(ic *interop.Context) error {
   141  	return nil
   142  }
   143  
   144  // ActiveIn implements the Contract interface.
   145  func (g *GAS) ActiveIn() *config.Hardfork {
   146  	return nil
   147  }
   148  
   149  // BalanceOf returns native GAS token balance for the acc.
   150  func (g *GAS) BalanceOf(d *dao.Simple, acc util.Uint160) *big.Int {
   151  	return g.balanceOfInternal(d, acc)
   152  }
   153  
   154  func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, error) {
   155  	cfg := ic.Chain.GetConfig()
   156  	committee, err := keys.NewPublicKeysFromStrings(cfg.StandbyCommittee)
   157  	if err != nil {
   158  		return util.Uint160{}, err
   159  	}
   160  	s, err := smartcontract.CreateDefaultMultiSigRedeemScript(committee[:cfg.GetNumOfCNs(0)])
   161  	if err != nil {
   162  		return util.Uint160{}, err
   163  	}
   164  	return hash.Hash160(s), nil
   165  }