Skip to content

Commit 5e26b1d

Browse files
committed
ir/internal: add possibility to deploy custom native contract
Also, implement new MetaData contract and GAS contract implementation without economic. Signed-off-by: Pavel Karpy <[email protected]>
1 parent 94876ec commit 5e26b1d

File tree

11 files changed

+1350
-2
lines changed

11 files changed

+1350
-2
lines changed

pkg/innerring/internal/blockchain/blockchain.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
1212
"github.com/nspcc-dev/neo-go/pkg/consensus"
1313
"github.com/nspcc-dev/neo-go/pkg/core"
14+
"github.com/nspcc-dev/neo-go/pkg/core/interop"
1415
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
1516
"github.com/nspcc-dev/neo-go/pkg/core/storage"
1617
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
@@ -48,7 +49,7 @@ type Blockchain struct {
4849
// New returns new Blockchain configured by the specified Config. New panics if
4950
// any required Config field is zero or unset. Resulting Blockchain is ready to
5051
// run. Launched Blockchain should be finally stopped.
51-
func New(cfg *config.Consensus, wallet *config.Wallet, errChan chan<- error, log *zap.Logger) (res *Blockchain, err error) {
52+
func New(cfg *config.Consensus, wallet *config.Wallet, errChan chan<- error, log *zap.Logger, customNatives ...func(cfg neogoconfig.ProtocolConfiguration) []interop.Contract) (res *Blockchain, err error) {
5253
switch {
5354
case cfg.Storage.Type == "":
5455
panic("uninitialized storage config")
@@ -259,7 +260,7 @@ func New(cfg *config.Consensus, wallet *config.Wallet, errChan chan<- error, log
259260
}
260261
}()
261262

262-
bc, err := core.NewBlockchain(bcStorage, cfgBase.Blockchain(), log)
263+
bc, err := core.NewBlockchain(bcStorage, cfgBase.Blockchain(), log, customNatives...)
263264
if err != nil {
264265
return nil, fmt.Errorf("init core blockchain component: %w", err)
265266
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package metachain
2+
3+
import (
4+
"github.com/nspcc-dev/neofs-node/pkg/innerring/config"
5+
"github.com/nspcc-dev/neofs-node/pkg/innerring/internal/blockchain"
6+
"go.uber.org/zap"
7+
)
8+
9+
// NewMetaChain returns side chain with redefined/custom native contracts.
10+
// See [contracts.NewCustomNatives] for details.
11+
func NewMetaChain(cfg *config.Consensus, wallet *config.Wallet, errChan chan<- error, log *zap.Logger) (*blockchain.Blockchain, error) {
12+
return blockchain.New(cfg, wallet, errChan, log, NewCustomNatives)
13+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package metachain
2+
3+
import (
4+
neogoconfig "github.com/nspcc-dev/neo-go/pkg/config"
5+
"github.com/nspcc-dev/neo-go/pkg/core/interop"
6+
"github.com/nspcc-dev/neo-go/pkg/core/native"
7+
"github.com/nspcc-dev/neofs-node/pkg/innerring/internal/metachain/gas"
8+
"github.com/nspcc-dev/neofs-node/pkg/innerring/internal/metachain/meta"
9+
)
10+
11+
// NewCustomNatives returns custom list of native contracts for metadata
12+
// side chain. Returned contracts:
13+
// - Management
14+
// - Ledger
15+
// - NEO
16+
// - redefined GAS (see [gas.NewGAS] for details)
17+
// - Policy
18+
// - Designate
19+
// - Notary
20+
// - new native metadata contract (see [meta.NewMetadata] for details).
21+
func NewCustomNatives(cfg neogoconfig.ProtocolConfiguration) []interop.Contract {
22+
mgmt := native.NewManagement()
23+
ledger := native.NewLedger()
24+
25+
g := gas.NewGAS()
26+
n := native.NewNEO(cfg)
27+
p := native.NewPolicy()
28+
29+
n.GAS = g
30+
n.Policy = p
31+
32+
mgmt.NEO = n
33+
mgmt.Policy = p
34+
ledger.Policy = p
35+
36+
desig := native.NewDesignate(cfg.Genesis.Roles)
37+
desig.NEO = n
38+
39+
notary := native.NewNotary()
40+
notary.Policy = p
41+
notary.GAS = g
42+
notary.NEO = n
43+
notary.Desig = desig
44+
45+
return []interop.Contract{
46+
mgmt,
47+
ledger,
48+
n,
49+
g,
50+
p,
51+
desig,
52+
notary,
53+
meta.NewMetadata(n),
54+
}
55+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package gas
2+
3+
import (
4+
"math/big"
5+
6+
"github.com/nspcc-dev/neo-go/pkg/config"
7+
"github.com/nspcc-dev/neo-go/pkg/core/dao"
8+
"github.com/nspcc-dev/neo-go/pkg/core/interop"
9+
"github.com/nspcc-dev/neo-go/pkg/core/native"
10+
"github.com/nspcc-dev/neo-go/pkg/core/native/nativeids"
11+
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
12+
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
13+
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
14+
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
15+
"github.com/nspcc-dev/neo-go/pkg/util"
16+
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
17+
)
18+
19+
// DefaultBalance is a balance of every account in redefined [GAS] native
20+
// contract.
21+
const DefaultBalance = 100
22+
23+
var _ = (native.IGAS)(&GAS{})
24+
25+
func (g *GAS) Metadata() *interop.ContractMD {
26+
return &g.ContractMD
27+
}
28+
29+
// GAS represents GAS custom native contract. It always returns [DefaultBalance] as a
30+
// balance, has no-op `Burn`, `Mint`, `Transfer` operations.
31+
type GAS struct {
32+
interop.ContractMD
33+
symbol string
34+
decimals int64
35+
factor int64
36+
}
37+
38+
// NewGAS returns [GAS] custom native contract.
39+
func NewGAS() *GAS {
40+
g := &GAS{}
41+
defer g.BuildHFSpecificMD(g.ActiveIn())
42+
43+
g.ContractMD = *interop.NewContractMD(nativenames.Gas, nativeids.GasToken, func(m *manifest.Manifest, hf config.Hardfork) {
44+
m.SupportedStandards = []string{manifest.NEP17StandardName}
45+
})
46+
g.symbol = "GAS"
47+
g.decimals = 8
48+
g.factor = native.GASFactor
49+
50+
desc := native.NewDescriptor("symbol", smartcontract.StringType)
51+
md := native.NewMethodAndPrice(g.Symbol, 0, callflag.NoneFlag)
52+
g.AddMethod(md, desc)
53+
54+
desc = native.NewDescriptor("decimals", smartcontract.IntegerType)
55+
md = native.NewMethodAndPrice(g.Decimals, 0, callflag.NoneFlag)
56+
g.AddMethod(md, desc)
57+
58+
desc = native.NewDescriptor("totalSupply", smartcontract.IntegerType)
59+
md = native.NewMethodAndPrice(g.TotalSupply, 1<<15, callflag.ReadStates)
60+
g.AddMethod(md, desc)
61+
62+
desc = native.NewDescriptor("balanceOf", smartcontract.IntegerType,
63+
manifest.NewParameter("account", smartcontract.Hash160Type))
64+
md = native.NewMethodAndPrice(g.balanceOf, 1<<15, callflag.ReadStates)
65+
g.AddMethod(md, desc)
66+
67+
transferParams := []manifest.Parameter{
68+
manifest.NewParameter("from", smartcontract.Hash160Type),
69+
manifest.NewParameter("to", smartcontract.Hash160Type),
70+
manifest.NewParameter("amount", smartcontract.IntegerType),
71+
}
72+
desc = native.NewDescriptor("transfer", smartcontract.BoolType,
73+
append(transferParams, manifest.NewParameter("data", smartcontract.AnyType))...,
74+
)
75+
md = native.NewMethodAndPrice(g.Transfer, 1<<17, callflag.States|callflag.AllowCall|callflag.AllowNotify)
76+
md.StorageFee = 50
77+
g.AddMethod(md, desc)
78+
79+
eDesc := native.NewEventDescriptor("Transfer", transferParams...)
80+
eMD := native.NewEvent(eDesc)
81+
g.AddEvent(eMD)
82+
83+
return g
84+
}
85+
86+
// Initialize initializes a GAS contract.
87+
func (g *GAS) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
88+
return nil
89+
}
90+
91+
// InitializeCache implements the [interop.Contract] interface.
92+
func (g *GAS) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
93+
return nil
94+
}
95+
96+
// OnPersist implements the [interop.Contract] interface.
97+
func (g *GAS) OnPersist(ic *interop.Context) error {
98+
return nil
99+
}
100+
101+
// PostPersist implements the [interop.Contract] interface.
102+
func (g *GAS) PostPersist(ic *interop.Context) error {
103+
return nil
104+
}
105+
106+
// ActiveIn implements the [interop.Contract] interface.
107+
func (g *GAS) ActiveIn() *config.Hardfork {
108+
return nil
109+
}
110+
111+
// BalanceOf returns native GAS token balance for the acc.
112+
func (g *GAS) BalanceOf(d *dao.Simple, acc util.Uint160) *big.Int {
113+
return big.NewInt(DefaultBalance * native.GASFactor)
114+
}
115+
116+
func (g *GAS) Symbol(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
117+
return stackitem.NewByteArray([]byte(g.symbol))
118+
}
119+
120+
func (g *GAS) Decimals(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
121+
return stackitem.NewBigInteger(big.NewInt(g.decimals))
122+
}
123+
124+
func (g *GAS) TotalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
125+
return stackitem.NewBigInteger(big.NewInt(DefaultBalance * native.GASFactor))
126+
}
127+
128+
func (g *GAS) Transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item {
129+
return stackitem.NewBool(true)
130+
}
131+
132+
// balanceOf is the only difference with default native GAS implementation:
133+
// it always returns fixed number of tokens.
134+
func (g *GAS) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item {
135+
return stackitem.NewBigInteger(big.NewInt(DefaultBalance * native.GASFactor))
136+
}
137+
138+
func (g *GAS) Mint(ic *interop.Context, h util.Uint160, amount *big.Int, callOnPayment bool) {
139+
}
140+
141+
func (g *GAS) Burn(ic *interop.Context, h util.Uint160, amount *big.Int) {
142+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package gas_test
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
7+
"github.com/nspcc-dev/neo-go/pkg/core/native"
8+
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
9+
"github.com/nspcc-dev/neo-go/pkg/neotest"
10+
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
11+
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
12+
"github.com/nspcc-dev/neofs-node/pkg/innerring/internal/metachain"
13+
)
14+
15+
func newGasClient(t *testing.T) (*neotest.ContractInvoker, *neotest.ContractInvoker) {
16+
ch, validators, committee := chain.NewMultiWithOptions(t, &chain.Options{
17+
NewNatives: metachain.NewCustomNatives,
18+
})
19+
e := neotest.NewExecutor(t, ch, validators, committee)
20+
21+
return e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)), e.CommitteeInvoker(e.NativeHash(t, nativenames.Gas))
22+
}
23+
24+
const defaultBalance = 100
25+
26+
func TestGAS(t *testing.T) {
27+
gasValidatorsI, gasCommitteeI := newGasClient(t)
28+
hardcodedBalance := stackitem.NewBigInteger(big.NewInt(defaultBalance * native.GASFactor))
29+
30+
t.Run("committee balance", func(t *testing.T) {
31+
gasCommitteeI.Invoke(t, hardcodedBalance, "balanceOf", gasCommitteeI.Hash)
32+
})
33+
34+
t.Run("new account balance", func(t *testing.T) {
35+
s := gasValidatorsI.NewAccount(t, defaultBalance*native.GASFactor+1)
36+
gasCommitteeI.WithSigners(s).Invoke(t, hardcodedBalance, "balanceOf", s.ScriptHash())
37+
})
38+
39+
t.Run("transfer does not change balance", func(t *testing.T) {
40+
newAcc := gasValidatorsI.NewAccount(t, defaultBalance*native.GASFactor+1)
41+
gasCommitteeI.Invoke(t, stackitem.Bool(true), "transfer", gasCommitteeI.Hash, newAcc.ScriptHash(), 1, stackitem.Null{})
42+
gasCommitteeI.Invoke(t, hardcodedBalance, "balanceOf", newAcc.ScriptHash())
43+
})
44+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package meta
2+
3+
import (
4+
"math"
5+
)
6+
7+
const (
8+
// Metadata contract identifiers.
9+
MetaDataContractID = math.MinInt32
10+
MetaDataContractName = "MetaData"
11+
)
12+
13+
const (
14+
// storage prefixes.
15+
metaContainersPrefix = iota
16+
containerPlacementPrefix
17+
18+
// object prefixes.
19+
addrIndex
20+
lockedByIndex
21+
)
22+
23+
const (
24+
// event names.
25+
putObjectEvent = "ObjectPut"
26+
27+
// limits.
28+
maxREPsClauses = 255
29+
)

0 commit comments

Comments
 (0)