Merge pull request #3229 from nspcc-dev/fix-service-init
core, services: fix Designation-dependant service initialisation
This commit is contained in:
commit
7a1bf77585
7 changed files with 114 additions and 2 deletions
|
@ -332,6 +332,16 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger) (*Bl
|
|||
return bc, nil
|
||||
}
|
||||
|
||||
// GetDesignatedByRole returns a set of designated public keys for the given role
|
||||
// relevant for the next block.
|
||||
func (bc *Blockchain) GetDesignatedByRole(r noderoles.Role) (keys.PublicKeys, uint32, error) {
|
||||
// Retrieve designated nodes starting from the next block, because the current
|
||||
// block is already stored, thus, dependant services can't use PostPersist callback
|
||||
// to fetch relevant information at their start.
|
||||
res, h, err := bc.contracts.Designate.GetDesignatedByRole(bc.dao, r, bc.BlockHeight()+1)
|
||||
return res, h, err
|
||||
}
|
||||
|
||||
// SetOracle sets oracle module. It can safely be called on the running blockchain.
|
||||
// To unregister Oracle service use SetOracle(nil).
|
||||
func (bc *Blockchain) SetOracle(mod native.OracleService) {
|
||||
|
@ -343,7 +353,7 @@ func (bc *Blockchain) SetOracle(mod native.OracleService) {
|
|||
}
|
||||
mod.UpdateNativeContract(orc.NEF.Script, orc.GetOracleResponseScript(),
|
||||
orc.Hash, md.MD.Offset)
|
||||
keys, _, err := bc.contracts.Designate.GetDesignatedByRole(bc.dao, noderoles.Oracle, bc.BlockHeight())
|
||||
keys, _, err := bc.GetDesignatedByRole(noderoles.Oracle)
|
||||
if err != nil {
|
||||
bc.log.Error("failed to get oracle key list")
|
||||
return
|
||||
|
@ -364,7 +374,7 @@ func (bc *Blockchain) SetOracle(mod native.OracleService) {
|
|||
// To unregister Notary service use SetNotary(nil).
|
||||
func (bc *Blockchain) SetNotary(mod native.NotaryService) {
|
||||
if mod != nil {
|
||||
keys, _, err := bc.contracts.Designate.GetDesignatedByRole(bc.dao, noderoles.P2PNotary, bc.BlockHeight())
|
||||
keys, _, err := bc.GetDesignatedByRole(noderoles.P2PNotary)
|
||||
if err != nil {
|
||||
bc.log.Error("failed to get notary key list")
|
||||
return
|
||||
|
|
|
@ -747,3 +747,28 @@ func TestNotary(t *testing.T) {
|
|||
}, 3*time.Second, 100*time.Millisecond)
|
||||
checkFallbackTxs(t, requests, false)
|
||||
}
|
||||
|
||||
func TestNotary_GenesisRoles(t *testing.T) {
|
||||
const (
|
||||
notaryPath = "./testdata/notary1.json"
|
||||
notaryPass = "one"
|
||||
)
|
||||
|
||||
w, err := wallet.NewWalletFromFile(notaryPath)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, w.Accounts[0].Decrypt(notaryPass, w.Scrypt))
|
||||
acc := w.Accounts[0]
|
||||
|
||||
bc, _, _ := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) {
|
||||
c.P2PSigExtensions = true
|
||||
c.Genesis.Roles = map[noderoles.Role]keys.PublicKeys{
|
||||
noderoles.P2PNotary: {acc.PublicKey()},
|
||||
}
|
||||
})
|
||||
|
||||
_, ntr, _ := getTestNotary(t, bc, "./testdata/notary1.json", "one", func(tx *transaction.Transaction) error { return nil })
|
||||
require.False(t, ntr.IsAuthorized())
|
||||
|
||||
bc.SetNotary(ntr)
|
||||
require.True(t, ntr.IsAuthorized())
|
||||
}
|
||||
|
|
|
@ -230,6 +230,13 @@ func (n *Notary) Shutdown() {
|
|||
n.wallet.Close()
|
||||
}
|
||||
|
||||
// IsAuthorized returns whether Notary service currently is authorized to collect
|
||||
// signatures. It returnes true iff designated Notary node's account provided to
|
||||
// the Notary service in decrypted state.
|
||||
func (n *Notary) IsAuthorized() bool {
|
||||
return n.getAccount() != nil
|
||||
}
|
||||
|
||||
// OnNewRequest is a callback method which is called after a new notary request is added to the notary request pool.
|
||||
func (n *Notary) OnNewRequest(payload *payload.P2PNotaryRequest) {
|
||||
if !n.started.Load() {
|
||||
|
|
|
@ -206,6 +206,13 @@ func (o *Oracle) Start() {
|
|||
go o.start()
|
||||
}
|
||||
|
||||
// IsAuthorized returns whether Oracle service currently is authorized to collect
|
||||
// signatures. It returns true iff designated Oracle node's account provided to
|
||||
// the Oracle service in decrypted state.
|
||||
func (o *Oracle) IsAuthorized() bool {
|
||||
return o.getAccount() != nil
|
||||
}
|
||||
|
||||
func (o *Oracle) start() {
|
||||
o.requestMap <- o.pending // Guaranteed to not block, only AddRequests sends to it.
|
||||
o.pending = nil
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||
"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/core/native/noderoles"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
|
@ -339,6 +340,30 @@ func TestOracle(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestOracle_GenesisRole(t *testing.T) {
|
||||
const (
|
||||
oraclePath = "./testdata/oracle1.json"
|
||||
oraclePass = "one"
|
||||
)
|
||||
w, err := wallet.NewWalletFromFile(oraclePath)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, w.Accounts[0].Decrypt(oraclePass, w.Scrypt))
|
||||
acc := w.Accounts[0]
|
||||
|
||||
bc, _, _ := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) {
|
||||
c.Genesis.Roles = map[noderoles.Role]keys.PublicKeys{
|
||||
noderoles.Oracle: {acc.PublicKey()},
|
||||
}
|
||||
})
|
||||
|
||||
orc, err := oracle.NewOracle(getOracleConfig(t, bc, "./testdata/oracle1.json", "one", nil))
|
||||
require.NoError(t, err)
|
||||
require.False(t, orc.IsAuthorized())
|
||||
|
||||
bc.SetOracle(orc)
|
||||
require.True(t, orc.IsAuthorized())
|
||||
}
|
||||
|
||||
func TestOracleFull(t *testing.T) {
|
||||
bc, validator, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, nil, false)
|
||||
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||
|
|
|
@ -2,6 +2,7 @@ package stateroot
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
@ -9,6 +10,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/stateroot"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
|
@ -22,6 +24,7 @@ type (
|
|||
// Ledger is an interface to Blockchain sufficient for Service.
|
||||
Ledger interface {
|
||||
GetConfig() config.Blockchain
|
||||
GetDesignatedByRole(role noderoles.Role) (keys.PublicKeys, uint32, error)
|
||||
HeaderHeight() uint32
|
||||
SubscribeForBlocks(ch chan *block.Block)
|
||||
UnsubscribeFromBlocks(ch chan *block.Block)
|
||||
|
@ -40,6 +43,10 @@ type (
|
|||
// to Shutdown on the same instance are no-op. The instance that was stopped can
|
||||
// not be started again by calling Start (use a new instance if needed).
|
||||
Shutdown()
|
||||
// IsAuthorized returns whether state root service currently is authorized to sign
|
||||
// state roots. It returns true iff designated StateValidator node's account
|
||||
// provided to the state root service in decrypted state.
|
||||
IsAuthorized() bool
|
||||
}
|
||||
|
||||
service struct {
|
||||
|
@ -116,6 +123,12 @@ func New(cfg config.StateRoot, sm *stateroot.Module, log *zap.Logger, bc Ledger,
|
|||
return nil, errors.New("no wallet account could be unlocked")
|
||||
}
|
||||
|
||||
keys, h, err := bc.GetDesignatedByRole(noderoles.StateValidator)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get designated StateValidators: %w", err)
|
||||
}
|
||||
s.updateValidators(h, keys)
|
||||
|
||||
s.SetUpdateValidatorsCallback(s.updateValidators)
|
||||
}
|
||||
return s, nil
|
||||
|
@ -173,3 +186,9 @@ func (s *service) updateValidators(height uint32, pubs keys.PublicKeys) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsAuthorized implements Service interface.
|
||||
func (s *service) IsAuthorized() bool {
|
||||
_, acc := s.getAccount()
|
||||
return acc != nil
|
||||
}
|
||||
|
|
|
@ -148,6 +148,25 @@ func TestStateRoot(t *testing.T) {
|
|||
require.Equal(t, h, r.Witness[0].ScriptHash())
|
||||
}
|
||||
|
||||
func TestStateRoot_GenesisRole(t *testing.T) {
|
||||
_, _, accs := newMajorityMultisigWithGAS(t, 2)
|
||||
|
||||
bc, _, _ := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) {
|
||||
c.Genesis.Roles = map[noderoles.Role]keys.PublicKeys{
|
||||
noderoles.StateValidator: {accs[0].PublicKey(), accs[1].PublicKey()},
|
||||
}
|
||||
})
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "pass")
|
||||
cfg := createStateRootConfig(w.Path(), "pass")
|
||||
srMod := bc.GetStateModule().(*corestate.Module) // Take full responsibility here.
|
||||
srv, err := stateroot.New(cfg, srMod, zaptest.NewLogger(t), bc, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, srv.IsAuthorized())
|
||||
}
|
||||
|
||||
type memoryStore struct {
|
||||
*storage.MemoryStore
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue