Skip to content

Commit

Permalink
fixes for CausalReference + a couple of passing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sbazerque committed Nov 21, 2022
1 parent 806ed7c commit 6f38a7a
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 21 deletions.
42 changes: 21 additions & 21 deletions src/data/collections/causal/CausalReference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Timestamps } from 'util/timestamps';

import { Identity } from '../../identity';
import { Hash, HashedObject, MutableObject, MutationOp, MutableContentEvents, ClassRegistry } from '../../model';
import { RefUpdateOp } from '../mutable/MutableReference';

import { Authorizer } from '../../model/causal/Authorization';
import { Verification } from '../../model/causal/Authorization';
Expand All @@ -26,9 +25,10 @@ function sig(op: CausalRefUpdateOp<any>) {
// the following should return -1 if u2 comes after u1;

function compareUpdateSigs(u1: UpdateSig, u2: UpdateSig) {

if (u2.sequence > u1.sequence) {
return -1;
} else if (u1.sequence < u2.sequence) {
} else if (u1.sequence > u2.sequence) {
return 1;
} else { // u2.sequence === u1.sequence
if (Timestamps.after(u2.timestamp, u1.timestamp)) {
Expand All @@ -54,7 +54,7 @@ class CausalReference<T> extends BaseCausalCollection<T> {
_largestSequence?: number;

constructor(config?: CausalCollectionConfig) {
super([CausalRefUpdateOp.className], config);
super([CausalRefUpdateOp.className], {...config, supportsUndo: true});

this.setRandomId();

Expand Down Expand Up @@ -104,34 +104,33 @@ class CausalReference<T> extends BaseCausalCollection<T> {

let mutated = false;

if (op instanceof RefUpdateOp) {
if (op instanceof CausalRefUpdateOp) {

//console.log('processing sequence ' + op.sequence + ', valid=' + valid)

const up = sig(op);

let idx: number; // the position of op in the array

if (!this._allAppliedOps.has(up.opHash)) {
// find the right place for the op in the array:
const length = this._causallyOrderedUpdates.length
idx = length;

// if the op has not been applied, we find the right place for it:
const length = this._causallyOrderedUpdates.length
idx = length;
while (idx>0 && compareUpdateSigs(this._causallyOrderedUpdates[idx-1], up) > 0) {
idx=idx-1;
}

while (idx>0 && compareUpdateSigs(this._causallyOrderedUpdates[idx-1], up) > 0) {
idx=idx-1;
}
// and then insert it there, if it was not there already:

// and then insert it there:
// NOTE: since the compare above failed, upds[idx-1] <= up
if (idx===0 || this._causallyOrderedUpdates[idx-1].opHash !== up.opHash) {
// upds[idx-1] < up => insert in position idx
this._causallyOrderedUpdates.splice(idx, 0, up);
} else {

// else, we just go through the array until we find it
idx = length-1;

while (idx>0 && this._causallyOrderedUpdates[idx-1].opHash !== up.opHash) {
idx=idx-1;
}
// upds[idx-1] === up => use old position idx-1
idx = idx-1;
}

let newValueIdx: number|undefined;
let newValueOp: CausalRefUpdateOp<T>|undefined;

Expand All @@ -148,13 +147,14 @@ class CausalReference<T> extends BaseCausalCollection<T> {
if (this._latestValidIdx === idx) {
// the current value has been invalidated, look for the next-best

let nextValueIdx = length;
let nextValueIdx = length-1;

while (nextValueIdx>=0 && !this.isValidOp(this._causallyOrderedUpdates[nextValueIdx].opHash)) {
nextValueIdx=nextValueIdx-1;
}

if (nextValueIdx >= 0) {

newValueIdx = nextValueIdx;
newValueOp = await this.loadOp(this._causallyOrderedUpdates[nextValueIdx].opHash) as CausalRefUpdateOp<T>;
} else {
Expand Down
97 changes: 97 additions & 0 deletions test/data/collections/causal/reference.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { describeProxy } from 'config';
import { RNGImpl } from 'crypto/random';
import { Identity, RSAKeyPair } from 'data/identity';
import { CausalReference, CausalSet } from 'data/collections';

import { Store } from 'storage/store';
import { IdbBackend } from 'storage/backends';

describeProxy('[CRF] Causal references', () => {

test('[CRF01] Causal reference set', async (done) => {

let r = new CausalReference<string>({acceptedTypes: ['string']});

expect (r.getValue()).toBeUndefined();

await r.setValue('hi');

expect (r.getValue() === 'hi').toBeTruthy();

await r.setValue('bye');

expect (r.getValue() === 'bye').toBeTruthy();

done();
});

test('[CRF02] Causal reference undo', async (done) => {

let store = new Store(new IdbBackend('CRF02 - ' + new RNGImpl().randomHexString(128)));

let kp0 = await RSAKeyPair.generate(2048);
let i0 = Identity.fromKeyPair({}, kp0);

await store.save(kp0);
await store.save(i0);

let kp1 = await RSAKeyPair.generate(2048);
let i1 = Identity.fromKeyPair({}, kp1);

await store.save(kp1);
await store.save(i1);


let mutWriters = new CausalSet<Identity>({writer: i0, acceptedTypes: [Identity.className]});
let ref = new CausalReference<string>({mutableWriters: mutWriters});

await store.save(mutWriters);
await store.save(ref);

const check = await ref.canSetValue('hi', i1);

expect(check).toBeFalsy();

await mutWriters.add(i1, i0);
await mutWriters.save();

const recheck = await ref.canSetValue('hi', i1);

expect(recheck).toBeTruthy();

await ref.setValue('1', i1);
await ref.save();

expect (ref.getValue()).toEqual('1');

await ref.setValue('2', i1);
await ref.save();

expect (ref.getValue()).toEqual('2');

let mutWritersClone = await store.load(mutWriters.hash()) as CausalSet<Identity>;

await ref.setValue('3', i1);
await ref.save();

expect (ref.getValue()).toEqual('3');

await ref.setValue('4', i1);
await ref.save();

expect (ref.getValue()).toEqual('4');

await mutWritersClone.delete(i1, i0);

await mutWritersClone.save();

console.log('FINAL LOADING!')

let refClone = await store.load(ref.hash()) as CausalReference<string>;

expect (refClone.getValue()).toEqual('2');

done();
});

});

0 comments on commit 6f38a7a

Please sign in to comment.