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 }