diff --git a/package.json b/package.json index 2d83d87..65cb447 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hollowdb", - "version": "1.4.1", + "version": "1.4.2", "description": "A decentralized privacy-preserving key-value database", "license": "MIT", "homepage": "https://github.com/firstbatchxyz/hollowdb#readme", diff --git a/src/contracts/build/hollowdb-htx.contract.js b/src/contracts/build/hollowdb-htx.contract.js index eb71423..b8df779 100644 --- a/src/contracts/build/hollowdb-htx.contract.js +++ b/src/contracts/build/hollowdb-htx.contract.js @@ -13,6 +13,14 @@ var InvalidFunctionError = new ContractError("Invalid function."); var ArrayLengthMismatchError = new ContractError("Key and value counts mismatch."); + // src/contracts/modifiers/apply.ts + async function apply(caller, input, state, ...modifiers) { + for (const modifier of modifiers) { + input = await modifier(caller, input, state); + } + return input; + } + // src/contracts/utils/index.ts var verifyProof = async (proof, psignals, verificationKey) => { if (!verificationKey) { @@ -54,12 +62,6 @@ return input; }; }; - async function apply(caller, input, state, ...modifiers) { - for (const modifier of modifiers) { - input = await modifier(caller, input, state); - } - return input; - } // src/contracts/hollowdb-htx.contract.ts var handle = async (state, action) => { diff --git a/src/contracts/build/hollowdb-set.contract.js b/src/contracts/build/hollowdb-set.contract.js index 9d9f54a..54e17d7 100644 --- a/src/contracts/build/hollowdb-set.contract.js +++ b/src/contracts/build/hollowdb-set.contract.js @@ -13,6 +13,14 @@ var InvalidFunctionError = new ContractError("Invalid function."); var ArrayLengthMismatchError = new ContractError("Key and value counts mismatch."); + // src/contracts/modifiers/apply.ts + async function apply(caller, input, state, ...modifiers) { + for (const modifier of modifiers) { + input = await modifier(caller, input, state); + } + return input; + } + // src/contracts/utils/index.ts var verifyProof = async (proof, psignals, verificationKey) => { if (!verificationKey) { @@ -80,12 +88,6 @@ return input; }; }; - async function apply(caller, input, state, ...modifiers) { - for (const modifier of modifiers) { - input = await modifier(caller, input, state); - } - return input; - } // src/contracts/hollowdb-set.contract.ts var handle = async (state, action) => { @@ -207,6 +209,11 @@ state.evolve = srcTxId; return { state }; } + case "seState": { + const newState = await apply(caller, input.value, state, onlyOwner); + state = newState; + return { state }; + } default: input; throw InvalidFunctionError; diff --git a/src/contracts/build/hollowdb.contract.js b/src/contracts/build/hollowdb.contract.js index c21a7e2..0ef90d2 100644 --- a/src/contracts/build/hollowdb.contract.js +++ b/src/contracts/build/hollowdb.contract.js @@ -13,6 +13,14 @@ var InvalidFunctionError = new ContractError("Invalid function."); var ArrayLengthMismatchError = new ContractError("Key and value counts mismatch."); + // src/contracts/modifiers/apply.ts + async function apply(caller, input, state, ...modifiers) { + for (const modifier of modifiers) { + input = await modifier(caller, input, state); + } + return input; + } + // src/contracts/utils/index.ts var verifyProof = async (proof, psignals, verificationKey) => { if (!verificationKey) { @@ -80,12 +88,6 @@ return input; }; }; - async function apply(caller, input, state, ...modifiers) { - for (const modifier of modifiers) { - input = await modifier(caller, input, state); - } - return input; - } // src/contracts/hollowdb.contract.ts var handle = async (state, action) => { diff --git a/src/contracts/hollowdb-set.contract.ts b/src/contracts/hollowdb-set.contract.ts index 6d5b575..a985716 100644 --- a/src/contracts/hollowdb-set.contract.ts +++ b/src/contracts/hollowdb-set.contract.ts @@ -1,6 +1,6 @@ import {CantEvolveError, InvalidFunctionError, KeyExistsError} from './errors'; import {apply, onlyNonNullValue, onlyNonNullValues, onlyOwner, onlyProofVerified, onlyWhitelisted} from './modifiers'; -import type {ContractHandle} from './types'; +import type {ContractHandle, ContractState} from './types'; import {hashToGroup} from './utils'; type Mode = {proofs: ['auth']; whitelists: ['put', 'update', 'set']}; @@ -22,7 +22,15 @@ export type SetManyInput = { }; }; -export const handle: ContractHandle | SetManyInput> = async (state, action) => { +export type SetStateInput = { + function: 'seState'; + value: ContractState; +}; + +export const handle: ContractHandle | SetManyInput | SetStateInput> = async ( + state, + action +) => { const {caller, input} = action; switch (input.function) { case 'get': { @@ -158,6 +166,12 @@ export const handle: ContractHandle | SetManyInput< return {state}; } + case 'seState': { + const newState = await apply(caller, input.value, state, onlyOwner); + state = newState; + return {state}; + } + default: // type-safe way to make sure all switch cases are handled input satisfies never; diff --git a/src/hollowdb-set.ts b/src/hollowdb-set.ts index b4be870..4eee917 100644 --- a/src/hollowdb-set.ts +++ b/src/hollowdb-set.ts @@ -1,5 +1,5 @@ import {BaseSDK} from '.'; -import {SetInput, SetManyInput} from './contracts/hollowdb-set.contract'; +import {SetInput, SetManyInput, SetStateInput} from './contracts/hollowdb-set.contract'; /** Just like HollowDB SDK, but supports `Set` and `SetMany` operations. * The user must be whitelisted for `set` separately to use them. @@ -42,4 +42,19 @@ export class SetSDK extends BaseSDK>): Promise { + await this.base.dryWriteInteraction({ + function: 'seState', + value: state, + }); + } } diff --git a/tests/set.test.ts b/tests/set.test.ts index b5057cc..0629470 100644 --- a/tests/set.test.ts +++ b/tests/set.test.ts @@ -55,4 +55,24 @@ describe('set tests', () => { expect(results[i]?.val).toBe(values[i].val); } }); + + it('should allow overwriting contract state', async () => { + const oldState = await owner.getState(); + const newVersion = 'im-overwritten-oh-no'; + await owner.setState({ + ...oldState, + version: newVersion, + }); + + const newState = await owner.getState(); + expect(newState.version).toBe(newVersion); + + // this kind of acts like a kill switch + // you should NOT reset the state like this + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + await owner.setState({}); + const emptyState = await owner.getState(); + expect(JSON.stringify(emptyState)).toBe('{}'); + }); });