native: cache all roles in Designate

This commit is contained in:
Evgeniy Stratonikov 2021-01-15 14:05:30 +03:00
parent 9a99054e6b
commit db4e7558e0
2 changed files with 61 additions and 26 deletions

View file

@ -30,6 +30,8 @@ type Designate struct {
rolesChangedFlag atomic.Value rolesChangedFlag atomic.Value
oracles atomic.Value oracles atomic.Value
stateVals atomic.Value
notaries atomic.Value
// p2pSigExtensionsEnabled defines whether the P2P signature extensions logic is relevant. // p2pSigExtensionsEnabled defines whether the P2P signature extensions logic is relevant.
p2pSigExtensionsEnabled bool p2pSigExtensionsEnabled bool
@ -37,7 +39,7 @@ type Designate struct {
OracleService atomic.Value OracleService atomic.Value
} }
type oraclesData struct { type roleData struct {
nodes keys.PublicKeys nodes keys.PublicKeys
addr util.Uint160 addr util.Uint160
height uint32 height uint32
@ -109,20 +111,18 @@ func (s *Designate) PostPersist(ic *interop.Context) error {
return nil return nil
} }
nodeKeys, height, err := s.GetDesignatedByRole(ic.DAO, RoleOracle, math.MaxUint32) if err := s.updateCachedRoleData(&s.oracles, ic.DAO, RoleOracle); err != nil {
if err != nil {
return err return err
} }
if err := s.updateCachedRoleData(&s.stateVals, ic.DAO, RoleStateValidator); err != nil {
return err
}
if s.p2pSigExtensionsEnabled {
if err := s.updateCachedRoleData(&s.notaries, ic.DAO, RoleP2PNotary); err != nil {
return err
}
}
od := &oraclesData{
nodes: nodeKeys,
addr: oracleHashFromNodes(nodeKeys),
height: height,
}
s.oracles.Store(od)
if orc, _ := s.OracleService.Load().(services.Oracle); orc != nil {
orc.UpdateOracleNodes(od.nodes.Copy())
}
s.rolesChangedFlag.Store(false) s.rolesChangedFlag.Store(false)
return nil return nil
} }
@ -157,7 +157,7 @@ func (s *Designate) rolesChanged() bool {
return rc == nil || rc.(bool) return rc == nil || rc.(bool)
} }
func oracleHashFromNodes(nodes keys.PublicKeys) util.Uint160 { func majorityHashFromNodes(nodes keys.PublicKeys) util.Uint160 {
if len(nodes) == 0 { if len(nodes) == 0 {
return util.Uint160{} return util.Uint160{}
} }
@ -165,15 +165,47 @@ func oracleHashFromNodes(nodes keys.PublicKeys) util.Uint160 {
return hash.Hash160(script) return hash.Hash160(script)
} }
func (s *Designate) updateCachedRoleData(v *atomic.Value, d dao.DAO, r Role) error {
nodeKeys, height, err := s.GetDesignatedByRole(d, r, math.MaxUint32)
if err != nil {
return err
}
v.Store(&roleData{
nodes: nodeKeys,
addr: majorityHashFromNodes(nodeKeys),
height: height,
})
if r == RoleOracle {
if orc, _ := s.OracleService.Load().(services.Oracle); orc != nil {
orc.UpdateOracleNodes(nodeKeys.Copy())
}
}
return nil
}
func (s *Designate) getCachedRoleData(r Role) *roleData {
var val interface{}
switch r {
case RoleOracle:
val = s.oracles.Load()
case RoleStateValidator:
val = s.stateVals.Load()
case RoleP2PNotary:
val = s.notaries.Load()
}
if val != nil {
return val.(*roleData)
}
return nil
}
func (s *Designate) getLastDesignatedHash(d dao.DAO, r Role) (util.Uint160, error) { func (s *Designate) getLastDesignatedHash(d dao.DAO, r Role) (util.Uint160, error) {
if !s.isValidRole(r) { if !s.isValidRole(r) {
return util.Uint160{}, ErrInvalidRole return util.Uint160{}, ErrInvalidRole
} }
if r == RoleOracle && !s.rolesChanged() { if !s.rolesChanged() {
odVal := s.oracles.Load() if val := s.getCachedRoleData(r); val != nil {
if odVal != nil { return val.addr, nil
od := odVal.(*oraclesData)
return od.addr, nil
} }
} }
nodes, _, err := s.GetDesignatedByRole(d, r, math.MaxUint32) nodes, _, err := s.GetDesignatedByRole(d, r, math.MaxUint32)
@ -181,7 +213,7 @@ func (s *Designate) getLastDesignatedHash(d dao.DAO, r Role) (util.Uint160, erro
return util.Uint160{}, err return util.Uint160{}, err
} }
// We only have hashing defined for oracles now. // We only have hashing defined for oracles now.
return oracleHashFromNodes(nodes), nil return majorityHashFromNodes(nodes), nil
} }
// GetDesignatedByRole returns nodes for role r. // GetDesignatedByRole returns nodes for role r.
@ -189,13 +221,9 @@ func (s *Designate) GetDesignatedByRole(d dao.DAO, r Role, index uint32) (keys.P
if !s.isValidRole(r) { if !s.isValidRole(r) {
return nil, 0, ErrInvalidRole return nil, 0, ErrInvalidRole
} }
if r == RoleOracle && !s.rolesChanged() { if !s.rolesChanged() {
odVal := s.oracles.Load() if val := s.getCachedRoleData(r); val != nil && val.height <= index {
if odVal != nil { return val.nodes.Copy(), val.height, nil
od := odVal.(*oraclesData)
if od.height <= index {
return od.nodes, od.height, nil
}
} }
} }
kvs, err := d.GetStorageItemsWithPrefix(s.ContractID, []byte{byte(r)}) kvs, err := d.GetStorageItemsWithPrefix(s.ContractID, []byte{byte(r)})

View file

@ -93,6 +93,13 @@ func TestDesignate_DesignateAsRoleTx(t *testing.T) {
bc.getNodesByRole(t, false, native.RoleOracle, 100500, 0) bc.getNodesByRole(t, false, native.RoleOracle, 100500, 0)
bc.getNodesByRole(t, true, native.RoleOracle, 0, 0) // returns an empty list bc.getNodesByRole(t, true, native.RoleOracle, 0, 0) // returns an empty list
bc.getNodesByRole(t, true, native.RoleOracle, index, 1) // returns pubs bc.getNodesByRole(t, true, native.RoleOracle, index, 1) // returns pubs
priv1, err := keys.NewPrivateKey()
require.NoError(t, err)
pubs = keys.PublicKeys{priv1.PublicKey()}
bc.setNodesByRole(t, true, native.RoleStateValidator, pubs)
bc.getNodesByRole(t, true, native.RoleStateValidator, bc.BlockHeight()+1, 1)
} }
func TestDesignate_DesignateAsRole(t *testing.T) { func TestDesignate_DesignateAsRole(t *testing.T) {