diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 49f6ddffc4..8cf2251f1a 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -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 diff --git a/pkg/services/notary/core_test.go b/pkg/services/notary/core_test.go index 638da3887f..2a00651da6 100644 --- a/pkg/services/notary/core_test.go +++ b/pkg/services/notary/core_test.go @@ -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()) +} diff --git a/pkg/services/notary/notary.go b/pkg/services/notary/notary.go index a567b4d748..55ba8748dd 100644 --- a/pkg/services/notary/notary.go +++ b/pkg/services/notary/notary.go @@ -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() { diff --git a/pkg/services/oracle/oracle.go b/pkg/services/oracle/oracle.go index d1212aff7e..8bce1bf952 100644 --- a/pkg/services/oracle/oracle.go +++ b/pkg/services/oracle/oracle.go @@ -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 diff --git a/pkg/services/oracle/oracle_test.go b/pkg/services/oracle/oracle_test.go index acc0f65095..74b33f4cf7 100644 --- a/pkg/services/oracle/oracle_test.go +++ b/pkg/services/oracle/oracle_test.go @@ -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)