Skip to content

Commit

Permalink
feat: immutibility and contracts that allow document history (dashpay…
Browse files Browse the repository at this point in the history
…/rs-platform#79)

* added contract and document type fields for mutability and historical data

* added in immutability

* basic contract history

* better test for import contract

* added test for mutability for DashPay contact Requests

* better updating of documents, and documents with keep history

* added in tests verifying historical contracts

* added test and fixed limit issue

* fixed some warnings

* various fixes and tests

* formatting and clippy fixes

* update to node bindings

* fixed bindings

* fixed linting

* clippy warnings

* clippy warnings

* added more fixes

* changed readonly to readOnly
  • Loading branch information
QuantumExplorer authored Mar 10, 2022
1 parent 01550ea commit 16a27c6
Show file tree
Hide file tree
Showing 19 changed files with 3,310 additions and 199 deletions.
18 changes: 14 additions & 4 deletions packages/rs-drive-nodejs/Drive.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,42 +68,52 @@ class Drive {

/**
* @param {DataContract} dataContract
* @param {Date} blockTime
* @param {boolean} [useTransaction=false]
* @returns {Promise<void>}
*/
async applyContract(dataContract, useTransaction = false) {
return driveApplyContractAsync.call(this.drive, dataContract.toBuffer(), useTransaction);
async applyContract(dataContract, blockTime, useTransaction = false) {
return driveApplyContractAsync.call(
this.drive,
dataContract.toBuffer(),
blockTime,
useTransaction,
);
}

/**
* @param {Document} document
* @param {Date} blockTime
* @param {boolean} [useTransaction=false]
* @returns {Promise<void>}
*/
async createDocument(document, useTransaction = false) {
async createDocument(document, blockTime, useTransaction = false) {
return driveCreateDocumentAsync.call(
this.drive,
document.toBuffer(),
document.getDataContract().toBuffer(),
document.getType(),
document.getOwnerId().toBuffer(),
true,
blockTime,
useTransaction,
);
}

/**
* @param {Document} document
* @param {Date} blockTime
* @param {boolean} [useTransaction=false]
* @returns {Promise<void>}
*/
async updateDocument(document, useTransaction = false) {
async updateDocument(document, blockTime, useTransaction = false) {
return driveUpdateDocumentAsync.call(
this.drive,
document.toBuffer(),
document.getDataContract().toBuffer(),
document.getType(),
document.getOwnerId().toBuffer(),
blockTime,
useTransaction,
);
}
Expand Down
26 changes: 18 additions & 8 deletions packages/rs-drive-nodejs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{option::Option::None, path::Path, sync::mpsc, thread};

use grovedb::{PrefixedRocksDbStorage, Storage};
use neon::prelude::*;
use neon::types::JsDate;
use rs_drive::drive::Drive;

type DriveCallback = Box<
Expand Down Expand Up @@ -269,20 +270,23 @@ impl DriveWrapper {

fn js_apply_contract(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let js_contract_cbor = cx.argument::<JsBuffer>(0)?;
let js_using_transaction = cx.argument::<JsBoolean>(1)?;
let js_callback = cx.argument::<JsFunction>(2)?.root(&mut cx);
let js_block_time = cx.argument::<JsDate>(1)?;
let js_using_transaction = cx.argument::<JsBoolean>(2)?;
let js_callback = cx.argument::<JsFunction>(3)?.root(&mut cx);

let drive = cx
.this()
.downcast_or_throw::<JsBox<DriveWrapper>, _>(&mut cx)?;

let contract_cbor = converter::js_buffer_to_vec_u8(js_contract_cbor, &mut cx);
let using_transaction = js_using_transaction.value(&mut cx);
let block_time = js_block_time.value(&mut cx);

drive
.send_to_drive_thread(move |drive: &mut Drive, transaction, channel| {
let result = drive.apply_contract(
contract_cbor,
block_time,
using_transaction.then(|| transaction).flatten(),
);

Expand Down Expand Up @@ -319,8 +323,9 @@ impl DriveWrapper {
let js_document_type_name = cx.argument::<JsString>(2)?;
let js_owner_id = cx.argument::<JsBuffer>(3)?;
let js_override_document = cx.argument::<JsBoolean>(4)?;
let js_using_transaction = cx.argument::<JsBoolean>(5)?;
let js_callback = cx.argument::<JsFunction>(6)?.root(&mut cx);
let js_block_time = cx.argument::<JsDate>(5)?;
let js_using_transaction = cx.argument::<JsBoolean>(6)?;
let js_callback = cx.argument::<JsFunction>(7)?.root(&mut cx);

let drive = cx
.this()
Expand All @@ -330,7 +335,8 @@ impl DriveWrapper {
let contract_cbor = converter::js_buffer_to_vec_u8(js_contract_cbor, &mut cx);
let document_type_name = js_document_type_name.value(&mut cx);
let owner_id = converter::js_buffer_to_vec_u8(js_owner_id, &mut cx);
let js_override_document = js_override_document.value(&mut cx);
let override_document = js_override_document.value(&mut cx);
let block_time = js_block_time.value(&mut cx);
let using_transaction = js_using_transaction.value(&mut cx);

drive
Expand All @@ -340,7 +346,8 @@ impl DriveWrapper {
&contract_cbor,
&document_type_name,
Some(&owner_id),
js_override_document,
override_document,
block_time,
using_transaction.then(|| transaction).flatten(),
);

Expand Down Expand Up @@ -376,8 +383,9 @@ impl DriveWrapper {
let js_contract_cbor = cx.argument::<JsBuffer>(1)?;
let js_document_type_name = cx.argument::<JsString>(2)?;
let js_owner_id = cx.argument::<JsBuffer>(3)?;
let js_using_transaction = cx.argument::<JsBoolean>(4)?;
let js_callback = cx.argument::<JsFunction>(5)?.root(&mut cx);
let js_block_time = cx.argument::<JsDate>(4)?;
let js_using_transaction = cx.argument::<JsBoolean>(5)?;
let js_callback = cx.argument::<JsFunction>(6)?.root(&mut cx);

let drive = cx
.this()
Expand All @@ -387,6 +395,7 @@ impl DriveWrapper {
let contract_cbor = converter::js_buffer_to_vec_u8(js_contract_cbor, &mut cx);
let document_type_name = js_document_type_name.value(&mut cx);
let owner_id = converter::js_buffer_to_vec_u8(js_owner_id, &mut cx);
let block_time = js_block_time.value(&mut cx);
let using_transaction = js_using_transaction.value(&mut cx);

drive
Expand All @@ -396,6 +405,7 @@ impl DriveWrapper {
&contract_cbor,
&document_type_name,
Some(&owner_id),
block_time,
using_transaction.then(|| transaction).flatten(),
);

Expand Down
38 changes: 20 additions & 18 deletions packages/rs-drive-nodejs/test/Drive.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ const TEST_DATA_PATH = './test_data';
describe('Drive', () => {
let drive;
let dataContract;
let blockTime;
let documents;

beforeEach(() => {
drive = new Drive(TEST_DATA_PATH);

dataContract = getDataContractFixture();
blockTime = new Date();
documents = getDocumentsFixture(dataContract);
});

Expand All @@ -44,13 +46,13 @@ describe('Drive', () => {
});

it('should create contract if not exists', async () => {
const result = await drive.applyContract(dataContract);

const result = await drive.applyContract(dataContract, blockTime);
blockTime.setSeconds(blockTime.getSeconds() + 10);
expect(result).to.equals(0);
});

it('should update existing contract', async () => {
await drive.applyContract(dataContract);
await drive.applyContract(dataContract, blockTime);

dataContract.setDocumentSchema('newDocumentType', {
type: 'object',
Expand All @@ -61,8 +63,8 @@ describe('Drive', () => {
},
additionalProperties: false,
});

const result = await drive.applyContract(dataContract);
blockTime.setSeconds(blockTime.getSeconds() + 10);
const result = await drive.applyContract(dataContract, blockTime);

expect(result).to.equals(0);
});
Expand All @@ -72,14 +74,14 @@ describe('Drive', () => {
beforeEach(async () => {
await drive.createRootTree();

await drive.applyContract(dataContract);
await drive.applyContract(dataContract, blockTime);
});

context('without indices', () => {
it('should create a document', async () => {
const documentWithoutIndices = documents[0];

const result = await drive.createDocument(documentWithoutIndices);
const result = await drive.createDocument(documentWithoutIndices, blockTime);

expect(result).to.equals(0);
});
Expand All @@ -89,7 +91,7 @@ describe('Drive', () => {
it('should create a document', async () => {
const documentWithIndices = documents[3];

const result = await drive.createDocument(documentWithIndices);
const result = await drive.createDocument(documentWithIndices, blockTime);

expect(result).to.equals(0);
});
Expand All @@ -100,20 +102,20 @@ describe('Drive', () => {
beforeEach(async () => {
await drive.createRootTree();

await drive.applyContract(dataContract);
await drive.applyContract(dataContract, blockTime);
});

context('without indices', () => {
it('should should update a document', async () => {
// Create a document
const documentWithoutIndices = documents[0];

await drive.createDocument(documentWithoutIndices);
await drive.createDocument(documentWithoutIndices, blockTime);

// Update the document
documentWithoutIndices.set('name', 'Bob');

const result = await drive.updateDocument(documentWithoutIndices);
const result = await drive.updateDocument(documentWithoutIndices, blockTime);

expect(result).to.equals(0);
});
Expand All @@ -124,12 +126,12 @@ describe('Drive', () => {
// Create a document
const documentWithIndices = documents[3];

await drive.createDocument(documentWithIndices);
await drive.createDocument(documentWithIndices, blockTime);

// Update the document
documentWithIndices.set('firstName', 'Bob');

const result = await drive.updateDocument(documentWithIndices);
const result = await drive.updateDocument(documentWithIndices, blockTime);

expect(result).to.equals(0);
});
Expand All @@ -140,15 +142,15 @@ describe('Drive', () => {
beforeEach(async () => {
await drive.createRootTree();

await drive.applyContract(dataContract);
await drive.applyContract(dataContract, blockTime);
});

context('without indices', () => {
it('should should delete the document', async () => {
// Create a document
const documentWithoutIndices = documents[3];

await drive.createDocument(documentWithoutIndices);
await drive.createDocument(documentWithoutIndices, blockTime);

const result = await drive.deleteDocument(
dataContract,
Expand All @@ -165,7 +167,7 @@ describe('Drive', () => {
// Create a document
const documentWithIndices = documents[3];

await drive.createDocument(documentWithIndices);
await drive.createDocument(documentWithIndices, blockTime);

const result = await drive.deleteDocument(
dataContract,
Expand All @@ -182,13 +184,13 @@ describe('Drive', () => {
beforeEach(async () => {
await drive.createRootTree();

await drive.applyContract(dataContract);
await drive.applyContract(dataContract, blockTime);
});

it('should query existing documents', async () => {
// Create documents
await Promise.all(
documents.map((document) => drive.createDocument(document)),
documents.map((document) => drive.createDocument(document, blockTime)),
);

const fetchedDocuments = await drive.queryDocuments(dataContract, 'indexedDocument', {
Expand Down
2 changes: 1 addition & 1 deletion packages/rs-drive/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn setup_contract(
let contract_cbor = json_document_to_cbor(path, Some(crate::drive::defaults::PROTOCOL_VERSION));
let contract = Contract::from_cbor(&contract_cbor).expect("contract should be deserialized");
drive
.apply_contract(contract_cbor, transaction)
.apply_contract(contract_cbor, 0f64, transaction)
.expect("contract should be applied");
contract
}
Expand Down
4 changes: 4 additions & 0 deletions packages/rs-drive/src/contract/defaults.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub const DEFAULT_CONTRACT_KEEPS_HISTORY: bool = false;
pub const DEFAULT_CONTRACT_MUTABILITY: bool = true;
pub const DEFAULT_CONTRACT_DOCUMENTS_KEEPS_HISTORY: bool = false;
pub const DEFAULT_CONTRACT_DOCUMENT_MUTABILITY: bool = true;
Loading

0 comments on commit 16a27c6

Please sign in to comment.