Skip to content

Commit

Permalink
Update NNS contract in Sidechain deployment procedure (#2408)
Browse files Browse the repository at this point in the history
  • Loading branch information
cthulhu-rider committed Jul 14, 2023
2 parents 967e273 + 29877e9 commit e5ac376
Show file tree
Hide file tree
Showing 9 changed files with 1,008 additions and 229 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,13 @@ require (
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.4.0 // indirect
golang.org/x/exp v0.0.0-20221227203929-1b447090c38c // indirect
golang.org/x/mod v0.6.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.1.0 // indirect
golang.org/x/tools v0.2.0 // indirect
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
Expand Down Expand Up @@ -572,6 +573,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -780,6 +782,7 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
7 changes: 7 additions & 0 deletions pkg/morph/deploy/contracts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package deploy

// various common methods of the NeoFS contracts.
const (
methodUpdate = "update"
methodVersion = "version"
)
73 changes: 66 additions & 7 deletions pkg/morph/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/neorpc"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/notary"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/wallet"
Expand All @@ -21,9 +22,9 @@ import (
// Blockchain groups services provided by particular Neo blockchain network
// representing NeoFS Sidechain that are required for its deployment.
type Blockchain interface {
// RPCActor groups functions needed to compose and send transactions to the
// blockchain.
actor.RPCActor
// RPCActor groups functions needed to compose and send transactions (incl.
// Notary service requests) to the blockchain.
notary.RPCActor

// GetCommittee returns list of public keys owned by Neo blockchain committee
// members. Resulting list is non-empty, unique and unsorted.
Expand All @@ -40,7 +41,14 @@ type Blockchain interface {
// to stop the process via Unsubscribe.
ReceiveBlocks(*neorpc.BlockFilter, chan<- *block.Block) (id string, err error)

// Unsubscribe stops background process started by ReceiveBlocks by ID.
// ReceiveNotaryRequests starts background process that forwards new notary
// requests of the blockchain to the provided channel. The process skips
// requests that don't match specified filter. Returns unique identifier to be
// used to stop the process via Unsubscribe.
ReceiveNotaryRequests(*neorpc.TxFilter, chan<- *result.NotaryRequestEvent) (string, error)

// Unsubscribe stops background process started by ReceiveBlocks or
// ReceiveNotaryRequests by ID.
Unsubscribe(id string) error
}

Expand Down Expand Up @@ -93,11 +101,16 @@ type Prm struct {
// 1. NNS contract deployment
// 2. launch of a notary service for the committee
// 3. committee group initialization
// 4. deployment of the NeoFS system contracts (currently not done)
// 4. deployment/update of the NeoFS system contracts (currently only NNS)
// 5. deployment of custom contracts
//
// See project documentation for details.
func Deploy(ctx context.Context, prm Prm) error {
// wrap the parent context into the context of the current function so that
// transaction wait routines do not leak
ctx, cancel := context.WithCancel(ctx)
defer cancel()

committee, err := prm.Blockchain.GetCommittee()
if err != nil {
return fmt.Errorf("get Neo committee of the network: %w", err)
Expand All @@ -121,9 +134,19 @@ func Deploy(ctx context.Context, prm Prm) error {
return errors.New("local account does not belong to any Neo committee member")
}

chNewBlock := make(chan struct{}, 1)

monitor, err := newBlockchainMonitor(prm.Logger, prm.Blockchain, chNewBlock)
if err != nil {
return fmt.Errorf("init blockchain monitor: %w", err)
}

defer monitor.stop()

deployNNSPrm := deployNNSContractPrm{
logger: prm.Logger,
blockchain: prm.Blockchain,
monitor: monitor,
localAcc: prm.LocalAccount,
localNEF: prm.NNS.Common.NEF,
localManifest: prm.NNS.Common.Manifest,
Expand Down Expand Up @@ -169,6 +192,7 @@ func Deploy(ctx context.Context, prm Prm) error {
err = enableNotary(ctx, enableNotaryPrm{
logger: prm.Logger,
blockchain: prm.Blockchain,
monitor: monitor,
nnsOnChainAddress: nnsOnChainAddress,
systemEmail: prm.NNS.SystemEmail,
committee: committee,
Expand All @@ -181,11 +205,24 @@ func Deploy(ctx context.Context, prm Prm) error {

prm.Logger.Info("Notary service successfully enabled for the committee")

go autoReplenishNotaryBalance(ctx, prm.Logger, prm.Blockchain, prm.LocalAccount, chNewBlock)

err = listenCommitteeNotaryRequests(ctx, listenCommitteeNotaryRequestsPrm{
logger: prm.Logger,
blockchain: prm.Blockchain,
localAcc: prm.LocalAccount,
committee: committee,
})
if err != nil {
return fmt.Errorf("start listener of committee notary requests: %w", err)
}

prm.Logger.Info("initializing committee group for contract management...")

committeeGroupKey, err := initCommitteeGroup(ctx, initCommitteeGroupPrm{
logger: prm.Logger,
blockchain: prm.Blockchain,
monitor: monitor,
nnsOnChainAddress: nnsOnChainAddress,
systemEmail: prm.NNS.SystemEmail,
committee: committee,
Expand All @@ -199,7 +236,29 @@ func Deploy(ctx context.Context, prm Prm) error {

prm.Logger.Info("committee group successfully initialized", zap.Stringer("public key", committeeGroupKey.PublicKey()))

// TODO: deploy contracts
prm.Logger.Info("updating on-chain NNS contract...")

err = updateNNSContract(ctx, updateNNSContractPrm{
logger: prm.Logger,
blockchain: prm.Blockchain,
monitor: monitor,
localAcc: prm.LocalAccount,
localNEF: prm.NNS.Common.NEF,
localManifest: prm.NNS.Common.Manifest,
systemEmail: prm.NNS.SystemEmail,
committee: committee,
committeeGroupKey: committeeGroupKey,
buildVersionedExtraUpdateArgs: noExtraUpdateArgs,
})
if err != nil {
return fmt.Errorf("update NNS contract on the chain: %w", err)
}

prm.Logger.Info("on-chain NNS contract successfully updated")

// TODO: deploy/update other contracts

return nil
}

func noExtraUpdateArgs(contractVersion) ([]interface{}, error) { return nil, nil }
95 changes: 46 additions & 49 deletions pkg/morph/deploy/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ type initCommitteeGroupPrm struct {

blockchain Blockchain

// based on blockchain
monitor *blockchainMonitor

nnsOnChainAddress util.Uint160
systemEmail string

Expand All @@ -36,19 +39,18 @@ type initCommitteeGroupPrm struct {

// initCommitteeGroup initializes committee group and returns corresponding private key.
func initCommitteeGroup(ctx context.Context, prm initCommitteeGroupPrm) (*keys.PrivateKey, error) {
monitor, err := newBlockchainMonitor(prm.logger, prm.blockchain)
if err != nil {
return nil, fmt.Errorf("init blockchain monitor: %w", err)
}
defer monitor.stop()
// wrap the parent context into the context of the current function so that
// transaction wait routines do not leak
ctx, cancel := context.WithCancel(ctx)
defer cancel()

inv := invoker.New(prm.blockchain, nil)
const leaderCommitteeIndex = 0
var committeeGroupKey *keys.PrivateKey
var leaderTick func()

upperLoop:
for ; ; monitor.waitForNextBlock(ctx) {
for ; ; prm.monitor.waitForNextBlock(ctx) {
select {
case <-ctx.Done():
return nil, fmt.Errorf("wait for committee group key to be distributed: %w", ctx.Err())
Expand Down Expand Up @@ -100,6 +102,8 @@ upperLoop:
continue
}

var err error

if committeeGroupKey == nil {
committeeGroupKey, err = prm.keyStorage.GetPersistedPrivateKey()
if err != nil {
Expand All @@ -109,7 +113,7 @@ upperLoop:
}

if leaderTick == nil {
leaderTick, err = initShareCommitteeGroupKeyAsLeaderTick(prm, monitor, committeeGroupKey)
leaderTick, err = initShareCommitteeGroupKeyAsLeaderTick(ctx, prm, committeeGroupKey)
if err != nil {
prm.logger.Error("failed to construct action sharing committee group key between committee members as leader, will try again later",
zap.Error(err))
Expand All @@ -124,16 +128,17 @@ upperLoop:
// initShareCommitteeGroupKeyAsLeaderTick returns a function that preserves
// context of the committee group key distribution by leading committee member
// between calls.
func initShareCommitteeGroupKeyAsLeaderTick(prm initCommitteeGroupPrm, monitor *blockchainMonitor, committeeGroupKey *keys.PrivateKey) (func(), error) {
_actor, err := actor.NewSimple(prm.blockchain, prm.localAcc)
func initShareCommitteeGroupKeyAsLeaderTick(ctx context.Context, prm initCommitteeGroupPrm, committeeGroupKey *keys.PrivateKey) (func(), error) {
localActor, err := actor.NewSimple(prm.blockchain, prm.localAcc)
if err != nil {
return nil, fmt.Errorf("init transaction sender from local account: %w", err)
}

_invoker := invoker.New(prm.blockchain, nil)
invkr := invoker.New(prm.blockchain, nil)

// multi-tick context
mDomainsToVubs := make(map[string][2]uint32) // 1st - register, 2nd - addRecord
mDomainsToRegisterTxs := make(map[string]*transactionGroupMonitor, len(prm.committee))
mDomainsToSetRecordTxs := make(map[string]*transactionGroupMonitor, len(prm.committee))

return func() {
prm.logger.Info("distributing committee group key between committee members using NNS...")
Expand All @@ -144,63 +149,57 @@ func initShareCommitteeGroupKeyAsLeaderTick(prm initCommitteeGroupPrm, monitor *

l.Info("synchronizing committee group key with NNS domain record...")

_, err := lookupNNSDomainRecord(_invoker, prm.nnsOnChainAddress, domain)
_, err := lookupNNSDomainRecord(invkr, prm.nnsOnChainAddress, domain)
if err != nil {
if errors.Is(err, errMissingDomain) {
l.Info("NNS domain is missing, registration is needed")

vubs, ok := mDomainsToVubs[domain]
if ok && vubs[0] > 0 {
l.Info("transaction registering NNS domain was sent earlier, checking relevance...")

if cur := monitor.currentHeight(); cur <= vubs[0] {
l.Info("previously sent transaction registering NNS domain may still be relevant, will wait for the outcome",
zap.Uint32("current height", cur), zap.Uint32("retry after height", vubs[0]))
return
txMonitor, ok := mDomainsToRegisterTxs[domain]
if ok {
if txMonitor.isPending() {
l.Info("previously sent transaction registering NNS domain is still pending, will wait for the outcome")
continue
}

l.Info("previously sent transaction registering NNS domain expired without side-effect")
} else {
txMonitor = newTransactionGroupMonitor(localActor)
mDomainsToRegisterTxs[domain] = txMonitor
}

l.Info("sending new transaction registering domain in the NNS...")

_, vub, err := _actor.SendCall(prm.nnsOnChainAddress, methodNNSRegister,
domain, _actor.Sender(), prm.systemEmail, nnsRefresh, nnsRetry, nnsExpire, nnsMinimum)
txID, vub, err := localActor.SendCall(prm.nnsOnChainAddress, methodNNSRegister,
domain, localActor.Sender(), prm.systemEmail, nnsRefresh, nnsRetry, nnsExpire, nnsMinimum)
if err != nil {
vubs[0] = 0
mDomainsToVubs[domain] = vubs
if isErrNotEnoughGAS(err) {
l.Info("not enough GAS to register domain in the NNS, will try again later")
} else {
l.Error("failed to send transaction registering domain in the NNS, will try again later", zap.Error(err))
}
return
continue
}

vubs[0] = vub
mDomainsToVubs[domain] = vubs
l.Info("transaction registering domain in the NNS has been successfully sent, will wait for the outcome",
zap.Stringer("tx", txID), zap.Uint32("vub", vub))

l.Info("transaction registering domain in the NNS has been successfully sent, will wait for the outcome")
txMonitor.trackPendingTransactionsAsync(ctx, vub, txID)

continue
} else if !errors.Is(err, errMissingDomainRecord) {
l.Error("failed to lookup NNS domain record, will try again later", zap.Error(err))
return
continue
}

l.Info("missing record of the NNS domain, needed to be set")

vubs, ok := mDomainsToVubs[domain]
if ok && vubs[1] > 0 {
l.Info("transaction setting NNS domain record was sent earlier, checking relevance...")

if cur := monitor.currentHeight(); cur <= vubs[1] {
l.Info("previously sent transaction setting NNS domain record may still be relevant, will wait for the outcome",
zap.Uint32("current height", cur), zap.Uint32("retry after height", vubs[1]))
return
txMonitor, ok := mDomainsToSetRecordTxs[domain]
if ok {
if txMonitor.isPending() {
l.Info("previously sent transaction setting NNS domain record is still pending, will wait for the outcome")
continue
}

l.Info("previously sent transaction setting NNS domain record expired without side-effect")
} else {
txMonitor = newTransactionGroupMonitor(localActor)
mDomainsToSetRecordTxs[domain] = txMonitor
}

l.Info("sharing encrypted committee group key with the committee member...")
Expand All @@ -209,28 +208,26 @@ func initShareCommitteeGroupKeyAsLeaderTick(prm initCommitteeGroupPrm, monitor *
if err != nil {
l.Error("failed to encrypt committee group key to share with the committee member, will try again later",
zap.Error(err))
return
continue
}

l.Info("sending new transaction setting domain record in the NNS...")

_, vub, err := _actor.SendCall(prm.nnsOnChainAddress, methodNNSAddRecord,
txID, vub, err := localActor.SendCall(prm.nnsOnChainAddress, methodNNSAddRecord,
domain, int64(nns.TXT), keyCipher)
if err != nil {
vubs[1] = 0
mDomainsToVubs[domain] = vubs
if isErrNotEnoughGAS(err) {
l.Info("not enough GAS to set NNS domain record, will try again later")
} else {
l.Error("failed to send transaction setting NNS domain record, will try again later", zap.Error(err))
}
return
continue
}

vubs[1] = vub
mDomainsToVubs[domain] = vubs
l.Info("transaction setting NNS domain record has been successfully sent, will wait for the outcome",
zap.Stringer("tx", txID), zap.Uint32("vub", vub))

l.Info("transaction setting NNS domain record has been successfully sent, will wait for the outcome")
txMonitor.trackPendingTransactionsAsync(ctx, vub, txID)

continue
}
Expand Down
Loading

0 comments on commit e5ac376

Please sign in to comment.