Judgements and refused setuid searches, which any caller may ask, roll in one bounded key of the node's and add no key to the bucket every node shares; a key per act is kept for hand-overs, access acts and searches started (issue 774, ADR 0563 §2; rev-l3 round 2)
This commit is contained in:
@@ -242,6 +242,54 @@ func (h *HandActs) readOne(key string) (*HandAct, uint64, error) {
|
||||
return &a, e.Revision, nil
|
||||
}
|
||||
|
||||
// judgedKey keeps the node's judgements and refused searches, the last keptJudged of them: any caller may ask them, so
|
||||
// they roll in one key and never add one (ADR 0563 §2: a wrong caller gains nothing, and fills no bucket).
|
||||
const (
|
||||
judgedKey = "judged"
|
||||
keptJudged = 50
|
||||
)
|
||||
|
||||
// roll adds a to the rolling key by compare-and-set, the oldest falling out.
|
||||
func (h *HandActs) roll(a HandAct) error {
|
||||
for range 5 {
|
||||
e, err := h.State.Get(judgedKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var kept []HandAct
|
||||
if e != nil {
|
||||
if err := json.Unmarshal(e.Value, &kept); err != nil {
|
||||
kept = nil // an unreadable roll is started again, never a reason to refuse
|
||||
}
|
||||
}
|
||||
kept = append(kept, a)
|
||||
if len(kept) > keptJudged {
|
||||
kept = kept[len(kept)-keptJudged:]
|
||||
}
|
||||
if e == nil {
|
||||
_, err = h.State.Create(judgedKey, kept)
|
||||
} else {
|
||||
_, err = h.State.Update(judgedKey, kept, e.Revision)
|
||||
}
|
||||
if !errors.Is(err, stdio.ErrChanged) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return errors.New("the engine's judgements kept changing under its write")
|
||||
}
|
||||
|
||||
func (h *HandActs) rolled() ([]HandAct, error) {
|
||||
e, err := h.State.Get(judgedKey)
|
||||
if err != nil || e == nil {
|
||||
return nil, err
|
||||
}
|
||||
var kept []HandAct
|
||||
if err := json.Unmarshal(e.Value, &kept); err != nil {
|
||||
return nil, fmt.Errorf("the engine's judgements cannot be read: %w", err)
|
||||
}
|
||||
return kept, nil
|
||||
}
|
||||
|
||||
func (h *HandActs) record(a HandAct) error {
|
||||
_, err := h.State.Create(actKey(a.ID), a)
|
||||
return err
|
||||
@@ -286,6 +334,12 @@ func (h *HandActs) Acts(limit int) (any, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
judged, err := h.rolled()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acts = append(acts, judged...)
|
||||
sort.SliceStable(acts, func(i, j int) bool { return acts[i].At.Before(acts[j].At) })
|
||||
out := make([]HandAct, 0, len(acts))
|
||||
for i := len(acts) - 1; i >= 0; i-- {
|
||||
out = append(out, acts[i])
|
||||
@@ -308,7 +362,7 @@ func (h *HandActs) Judge(caller string) (any, error) {
|
||||
if err != nil {
|
||||
a.State, a.Said = HandRefused, err.Error()
|
||||
}
|
||||
if rerr := h.record(a); rerr != nil && err == nil {
|
||||
if rerr := h.roll(a); rerr != nil && err == nil {
|
||||
return nil, rerr
|
||||
}
|
||||
if err != nil {
|
||||
@@ -339,7 +393,13 @@ func (h *HandActs) Search(caller, why string) (any, error) {
|
||||
if err != nil {
|
||||
a.State, a.Said = HandRefused, err.Error()
|
||||
}
|
||||
if rerr := h.record(a); rerr != nil {
|
||||
// A search started is bounded to one an hour and kept as an act of its own; a refused one, which any caller may
|
||||
// cause, rolls with the judgements.
|
||||
record := h.record
|
||||
if err != nil {
|
||||
record = h.roll
|
||||
}
|
||||
if rerr := record(a); rerr != nil {
|
||||
return nil, rerr
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
@@ -105,5 +105,34 @@ func TestASetuidSearchAskIsBelievedOnlySignedForThisNodeAndOnce(t *testing.T) {
|
||||
if _, err := h.Call(EngineCall{Verb: ActSetuidSearch, Caller: "ace.node-tools"}); err == nil || !strings.Contains(err.Error(), "the next may start") || started != 2 {
|
||||
t.Errorf("a search with the acts gone: %v, %d started", err, started)
|
||||
}
|
||||
// rev-l3 round 2 (ADR 0563 §2, a wrong caller gains nothing): judgements and refused searches, which any caller
|
||||
// may ask, keep one rolling key of the node's, so ten thousand of them write a bounded number of bytes and add no
|
||||
// key to the bucket every node shares.
|
||||
keysNow := func() (int, int) {
|
||||
all, _ := h.State.Keys("")
|
||||
size := 0
|
||||
for _, k := range all {
|
||||
if e, _ := h.State.Get(k); e != nil {
|
||||
size += len(e.Value)
|
||||
}
|
||||
}
|
||||
return len(all), size
|
||||
}
|
||||
_, _ = h.Call(EngineCall{Verb: ActJudgeDirectories, Caller: "unproven"})
|
||||
keysBefore, _ := keysNow()
|
||||
for i := range 10000 {
|
||||
verb := ActJudgeDirectories
|
||||
if i%10 == 0 {
|
||||
verb = ActSetuidSearch
|
||||
}
|
||||
_, _ = h.Call(EngineCall{Verb: verb, Caller: "unproven"})
|
||||
}
|
||||
if n, size := keysNow(); n != keysBefore || size > 64<<10 {
|
||||
t.Errorf("ten thousand judgements and refused searches left %d keys (was %d) of %d bytes", n, keysBefore, size)
|
||||
}
|
||||
listed, _ := h.Acts(0)
|
||||
if got := listed.(map[string]any)["acts"].([]HandAct); len(got) == 0 || got[0].Caller != "unproven" {
|
||||
t.Errorf("acts does not show the latest judgement first: %d", len(got))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user