-
Notifications
You must be signed in to change notification settings - Fork 49
Feat/meta data native contract #3759
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| package metachain | ||
|
|
||
| import ( | ||
| neogoconfig "github.com/nspcc-dev/neo-go/pkg/config" | ||
| "github.com/nspcc-dev/neo-go/pkg/core/interop" | ||
| "github.com/nspcc-dev/neo-go/pkg/core/native" | ||
| "github.com/nspcc-dev/neofs-node/pkg/core/metachain/gas" | ||
| "github.com/nspcc-dev/neofs-node/pkg/core/metachain/meta" | ||
| ) | ||
|
|
||
| // NewCustomNatives returns custom list of native contracts for metadata | ||
| // side chain. Returned contracts: | ||
| // - Management | ||
| // - Ledger | ||
| // - NEO | ||
| // - redefined GAS (see [gas.NewGAS] for details) | ||
| // - Policy | ||
| // - Designate | ||
| // - Notary | ||
| // - new native metadata contract (see [meta.NewMetadata] for details). | ||
| func NewCustomNatives(cfg neogoconfig.ProtocolConfiguration) []interop.Contract { | ||
| mgmt := native.NewManagement() | ||
| ledger := native.NewLedger() | ||
|
|
||
| g := gas.NewGAS() | ||
| n := native.NewNEO(cfg) | ||
| p := native.NewPolicy() | ||
|
|
||
| n.GAS = g | ||
| n.Policy = p | ||
|
|
||
| mgmt.NEO = n | ||
| mgmt.Policy = p | ||
| ledger.Policy = p | ||
|
|
||
| desig := native.NewDesignate(cfg.Genesis.Roles) | ||
| desig.NEO = n | ||
|
|
||
| notary := native.NewNotary() | ||
| notary.Policy = p | ||
| notary.GAS = g | ||
| notary.NEO = n | ||
| notary.Desig = desig | ||
|
|
||
| return []interop.Contract{ | ||
| mgmt, | ||
| ledger, | ||
| n, | ||
| g, | ||
| p, | ||
| desig, | ||
| notary, | ||
| meta.NewMetadata(n), | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,174 @@ | ||||||||||||||||||
| package gas | ||||||||||||||||||
|
|
||||||||||||||||||
| import ( | ||||||||||||||||||
| "fmt" | ||||||||||||||||||
| "math/big" | ||||||||||||||||||
|
|
||||||||||||||||||
| "github.com/nspcc-dev/neo-go/pkg/config" | ||||||||||||||||||
| "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/contract" | ||||||||||||||||||
| "github.com/nspcc-dev/neo-go/pkg/core/native" | ||||||||||||||||||
| "github.com/nspcc-dev/neo-go/pkg/core/native/nativeids" | ||||||||||||||||||
| "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" | ||||||||||||||||||
| "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" | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| // DefaultBalance is a balance of every account in redefined [GAS] native | ||||||||||||||||||
| // contract. | ||||||||||||||||||
| const DefaultBalance = 100 * native.GASFactor | ||||||||||||||||||
|
|
||||||||||||||||||
| var _ = (native.IGAS)(&GAS{}) | ||||||||||||||||||
|
|
||||||||||||||||||
| func (g *GAS) Metadata() *interop.ContractMD { | ||||||||||||||||||
| return &g.ContractMD | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // GAS represents GAS custom native contract. It always returns [DefaultBalance] as a | ||||||||||||||||||
| // balance, has no-op `Burn`, `Mint`, `Transfer` operations. | ||||||||||||||||||
| type GAS struct { | ||||||||||||||||||
| interop.ContractMD | ||||||||||||||||||
| symbol string | ||||||||||||||||||
| decimals int64 | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+33
to
+37
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Keep it as simple as possible. Return constants from |
||||||||||||||||||
|
|
||||||||||||||||||
| // NewGAS returns [GAS] custom native contract. | ||||||||||||||||||
| func NewGAS() *GAS { | ||||||||||||||||||
| g := &GAS{} | ||||||||||||||||||
| defer g.BuildHFSpecificMD(g.ActiveIn()) | ||||||||||||||||||
|
|
||||||||||||||||||
| g.ContractMD = *interop.NewContractMD(nativenames.Gas, nativeids.GasToken, func(m *manifest.Manifest, hf config.Hardfork) { | ||||||||||||||||||
| m.SupportedStandards = []string{manifest.NEP17StandardName} | ||||||||||||||||||
| }) | ||||||||||||||||||
| g.symbol = "GAS" | ||||||||||||||||||
| g.decimals = 8 | ||||||||||||||||||
|
|
||||||||||||||||||
| desc := native.NewDescriptor("symbol", smartcontract.StringType) | ||||||||||||||||||
| md := native.NewMethodAndPrice(g.Symbol, 0, callflag.NoneFlag) | ||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||
| g.AddMethod(md, desc) | ||||||||||||||||||
|
|
||||||||||||||||||
| desc = native.NewDescriptor("decimals", smartcontract.IntegerType) | ||||||||||||||||||
| md = native.NewMethodAndPrice(g.Decimals, 0, callflag.NoneFlag) | ||||||||||||||||||
| g.AddMethod(md, desc) | ||||||||||||||||||
|
|
||||||||||||||||||
| desc = native.NewDescriptor("totalSupply", smartcontract.IntegerType) | ||||||||||||||||||
| md = native.NewMethodAndPrice(g.TotalSupply, 1<<15, callflag.ReadStates) | ||||||||||||||||||
| g.AddMethod(md, desc) | ||||||||||||||||||
|
|
||||||||||||||||||
| desc = native.NewDescriptor("balanceOf", smartcontract.IntegerType, | ||||||||||||||||||
| manifest.NewParameter("account", smartcontract.Hash160Type)) | ||||||||||||||||||
| md = native.NewMethodAndPrice(g.balanceOf, 1<<15, callflag.ReadStates) | ||||||||||||||||||
| g.AddMethod(md, desc) | ||||||||||||||||||
|
|
||||||||||||||||||
| transferParams := []manifest.Parameter{ | ||||||||||||||||||
| manifest.NewParameter("from", smartcontract.Hash160Type), | ||||||||||||||||||
| manifest.NewParameter("to", smartcontract.Hash160Type), | ||||||||||||||||||
| manifest.NewParameter("amount", smartcontract.IntegerType), | ||||||||||||||||||
| } | ||||||||||||||||||
| desc = native.NewDescriptor("transfer", smartcontract.BoolType, | ||||||||||||||||||
| append(transferParams, manifest.NewParameter("data", smartcontract.AnyType))..., | ||||||||||||||||||
| ) | ||||||||||||||||||
| md = native.NewMethodAndPrice(g.Transfer, 1<<17, callflag.States|callflag.AllowCall|callflag.AllowNotify) | ||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Likely ditto for this one. |
||||||||||||||||||
| g.AddMethod(md, desc) | ||||||||||||||||||
|
|
||||||||||||||||||
| eDesc := native.NewEventDescriptor("Transfer", transferParams...) | ||||||||||||||||||
| eMD := native.NewEvent(eDesc) | ||||||||||||||||||
| g.AddEvent(eMD) | ||||||||||||||||||
|
|
||||||||||||||||||
| return g | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // Initialize initializes a GAS contract. | ||||||||||||||||||
| func (g *GAS) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { | ||||||||||||||||||
| return nil | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // InitializeCache implements the [interop.Contract] interface. | ||||||||||||||||||
| func (g *GAS) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { | ||||||||||||||||||
| return nil | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // OnPersist implements the [interop.Contract] interface. | ||||||||||||||||||
| func (g *GAS) OnPersist(ic *interop.Context) error { | ||||||||||||||||||
| return nil | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // PostPersist implements the [interop.Contract] interface. | ||||||||||||||||||
| func (g *GAS) PostPersist(ic *interop.Context) error { | ||||||||||||||||||
| return nil | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // ActiveIn implements the [interop.Contract] interface. | ||||||||||||||||||
| func (g *GAS) ActiveIn() *config.Hardfork { | ||||||||||||||||||
| return nil | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // BalanceOf returns native GAS token balance for the acc. | ||||||||||||||||||
| func (g *GAS) BalanceOf(d *dao.Simple, acc util.Uint160) *big.Int { | ||||||||||||||||||
| return big.NewInt(DefaultBalance * native.GASFactor) | ||||||||||||||||||
|
AnnaShaleva marked this conversation as resolved.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Because now |
||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func (g *GAS) Symbol(_ *interop.Context, _ []stackitem.Item) stackitem.Item { | ||||||||||||||||||
| return stackitem.NewByteArray([]byte(g.symbol)) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func (g *GAS) Decimals(_ *interop.Context, _ []stackitem.Item) stackitem.Item { | ||||||||||||||||||
| return stackitem.NewBigInteger(big.NewInt(g.decimals)) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func (g *GAS) TotalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item { | ||||||||||||||||||
| return stackitem.NewBigInteger(big.NewInt(DefaultBalance)) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func toUint160(s stackitem.Item) util.Uint160 { | ||||||||||||||||||
| u, err := stackitem.ToUint160(s) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| panic(err) | ||||||||||||||||||
| } | ||||||||||||||||||
| return u | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func toBigInt(s stackitem.Item) *big.Int { | ||||||||||||||||||
| bi, err := s.TryInteger() | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| panic(err) | ||||||||||||||||||
| } | ||||||||||||||||||
| return bi | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func (g *GAS) Transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item { | ||||||||||||||||||
| from := toUint160(args[0]) | ||||||||||||||||||
| to := toUint160(args[1]) | ||||||||||||||||||
| amount := toBigInt(args[2]) | ||||||||||||||||||
|
|
||||||||||||||||||
| paymentArgs := []stackitem.Item{ | ||||||||||||||||||
| stackitem.NewByteArray(from.BytesBE()), | ||||||||||||||||||
| stackitem.NewBigInteger(amount), | ||||||||||||||||||
|
Comment on lines
+149
to
+150
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Shorter, simpler. |
||||||||||||||||||
| args[3], | ||||||||||||||||||
| } | ||||||||||||||||||
| cs, err := ic.GetContract(to) | ||||||||||||||||||
| if err == nil { | ||||||||||||||||||
| err = contract.CallFromNative(ic, g.Hash, cs, manifest.MethodOnNEP17Payment, paymentArgs, false) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| panic(fmt.Errorf("failed to call %s: %w", manifest.MethodOnNEP17Payment, err)) | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return stackitem.NewBool(true) | ||||||||||||||||||
|
AnnaShaleva marked this conversation as resolved.
|
||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // balanceOf is the only difference with default native GAS implementation: | ||||||||||||||||||
| // it always returns fixed number of tokens. | ||||||||||||||||||
| func (g *GAS) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item { | ||||||||||||||||||
| return stackitem.NewBigInteger(g.BalanceOf(nil, util.Uint160{})) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func (g *GAS) Mint(ic *interop.Context, h util.Uint160, amount *big.Int, callOnPayment bool) { | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func (g *GAS) Burn(ic *interop.Context, h util.Uint160, amount *big.Int) { | ||||||||||||||||||
| } | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| package gas_test | ||
|
|
||
| import ( | ||
| "math/big" | ||
| "testing" | ||
|
|
||
| "github.com/nspcc-dev/neo-go/pkg/core/native" | ||
| "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" | ||
| "github.com/nspcc-dev/neo-go/pkg/neotest" | ||
| "github.com/nspcc-dev/neo-go/pkg/neotest/chain" | ||
| "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" | ||
| "github.com/nspcc-dev/neofs-node/pkg/core/metachain" | ||
| ) | ||
|
|
||
| func newGasClient(t *testing.T) (*neotest.ContractInvoker, *neotest.ContractInvoker) { | ||
| ch, validators, committee := chain.NewMultiWithOptions(t, &chain.Options{ | ||
| NewNatives: metachain.NewCustomNatives, | ||
| }) | ||
| e := neotest.NewExecutor(t, ch, validators, committee) | ||
|
|
||
| return e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)), e.CommitteeInvoker(e.NativeHash(t, nativenames.Gas)) | ||
| } | ||
|
|
||
| const defaultBalance = 100 * native.GASFactor | ||
|
|
||
| func TestGAS(t *testing.T) { | ||
| gasValidatorsI, gasCommitteeI := newGasClient(t) | ||
| hardcodedBalance := stackitem.NewBigInteger(big.NewInt(defaultBalance * native.GASFactor)) | ||
|
|
||
| t.Run("committee balance", func(t *testing.T) { | ||
| gasCommitteeI.Invoke(t, hardcodedBalance, "balanceOf", gasCommitteeI.Hash) | ||
| }) | ||
|
|
||
| t.Run("new account balance", func(t *testing.T) { | ||
| s := gasValidatorsI.NewAccount(t, defaultBalance*native.GASFactor+1) | ||
| gasCommitteeI.WithSigners(s).Invoke(t, hardcodedBalance, "balanceOf", s.ScriptHash()) | ||
| }) | ||
|
|
||
| t.Run("transfer does not change balance", func(t *testing.T) { | ||
| newAcc := gasValidatorsI.NewAccount(t, defaultBalance*native.GASFactor+1) | ||
| gasCommitteeI.Invoke(t, stackitem.Bool(true), "transfer", gasCommitteeI.Hash, newAcc.ScriptHash(), 1, stackitem.Null{}) | ||
| gasCommitteeI.Invoke(t, hardcodedBalance, "balanceOf", newAcc.ScriptHash()) | ||
| }) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package meta | ||
|
|
||
| import ( | ||
| "math" | ||
| ) | ||
|
|
||
| const ( | ||
| // Metadata contract identifiers. | ||
| MetaDataContractID = math.MinInt32 | ||
| MetaDataContractName = "MetaData" | ||
| ) | ||
|
|
||
| const ( | ||
| // storage prefixes. | ||
| metaContainersPrefix = iota | ||
| containerPlacementPrefix | ||
|
|
||
| // object prefixes. | ||
| addrIndex | ||
| lockedByIndex | ||
| ) | ||
|
|
||
| const ( | ||
| // event names. | ||
| objectPutEvent = "ObjectPut" | ||
| objectDeletedEvent = "ObjectDeleted" | ||
| objectLockedEvent = "ObjectLocked" | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated.