forked from TrueCloudLab/neoneo-go
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
|
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.
|
// SetOracle sets oracle module. It can safely be called on the running blockchain.
|
||||||
// To unregister Oracle service use SetOracle(nil).
|
// To unregister Oracle service use SetOracle(nil).
|
||||||
func (bc *Blockchain) SetOracle(mod native.OracleService) {
|
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(),
|
mod.UpdateNativeContract(orc.NEF.Script, orc.GetOracleResponseScript(),
|
||||||
orc.Hash, md.MD.Offset)
|
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 {
|
if err != nil {
|
||||||
bc.log.Error("failed to get oracle key list")
|
bc.log.Error("failed to get oracle key list")
|
||||||
return
|
return
|
||||||
|
@ -364,7 +374,7 @@ func (bc *Blockchain) SetOracle(mod native.OracleService) {
|
||||||
// To unregister Notary service use SetNotary(nil).
|
// To unregister Notary service use SetNotary(nil).
|
||||||
func (bc *Blockchain) SetNotary(mod native.NotaryService) {
|
func (bc *Blockchain) SetNotary(mod native.NotaryService) {
|
||||||
if mod != nil {
|
if mod != nil {
|
||||||
keys, _, err := bc.contracts.Designate.GetDesignatedByRole(bc.dao, noderoles.P2PNotary, bc.BlockHeight())
|
keys, _, err := bc.GetDesignatedByRole(noderoles.P2PNotary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bc.log.Error("failed to get notary key list")
|
bc.log.Error("failed to get notary key list")
|
||||||
return
|
return
|
||||||
|
|
|
@ -747,3 +747,28 @@ func TestNotary(t *testing.T) {
|
||||||
}, 3*time.Second, 100*time.Millisecond)
|
}, 3*time.Second, 100*time.Millisecond)
|
||||||
checkFallbackTxs(t, requests, false)
|
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()
|
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.
|
// 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) {
|
func (n *Notary) OnNewRequest(payload *payload.P2PNotaryRequest) {
|
||||||
if !n.started.Load() {
|
if !n.started.Load() {
|
||||||
|
|
|
@ -206,6 +206,13 @@ func (o *Oracle) Start() {
|
||||||
go o.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() {
|
func (o *Oracle) start() {
|
||||||
o.requestMap <- o.pending // Guaranteed to not block, only AddRequests sends to it.
|
o.requestMap <- o.pending // Guaranteed to not block, only AddRequests sends to it.
|
||||||
o.pending = nil
|
o.pending = nil
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"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/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"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) {
|
func TestOracleFull(t *testing.T) {
|
||||||
bc, validator, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, nil, false)
|
bc, validator, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, nil, false)
|
||||||
e := neotest.NewExecutor(t, bc, validator, committee)
|
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package stateroot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
@ -9,6 +10,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"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/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/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/stateroot"
|
"github.com/nspcc-dev/neo-go/pkg/core/stateroot"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -22,6 +24,7 @@ type (
|
||||||
// Ledger is an interface to Blockchain sufficient for Service.
|
// Ledger is an interface to Blockchain sufficient for Service.
|
||||||
Ledger interface {
|
Ledger interface {
|
||||||
GetConfig() config.Blockchain
|
GetConfig() config.Blockchain
|
||||||
|
GetDesignatedByRole(role noderoles.Role) (keys.PublicKeys, uint32, error)
|
||||||
HeaderHeight() uint32
|
HeaderHeight() uint32
|
||||||
SubscribeForBlocks(ch chan *block.Block)
|
SubscribeForBlocks(ch chan *block.Block)
|
||||||
UnsubscribeFromBlocks(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
|
// 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).
|
// not be started again by calling Start (use a new instance if needed).
|
||||||
Shutdown()
|
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 {
|
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")
|
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)
|
s.SetUpdateValidatorsCallback(s.updateValidators)
|
||||||
}
|
}
|
||||||
return s, nil
|
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())
|
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 {
|
type memoryStore struct {
|
||||||
*storage.MemoryStore
|
*storage.MemoryStore
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue