Skip to content

Commit

Permalink
Merge pull request #59 from freespek/igor/storage
Browse files Browse the repository at this point in the history
add a simple implementation of ADR002
  • Loading branch information
konnov authored May 16, 2024
2 parents f5c62a4 + 865e850 commit 1ec07e5
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 0 deletions.
70 changes: 70 additions & 0 deletions solarkraft/src/fetcher/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
* Igor Konnov, 2024.
*/

import JSONbigint from 'json-bigint'
import { OrderedMap } from 'immutable'
import { join } from 'node:path/posix'
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'

const JSONbig = JSONbigint({ useNativeBigInt: true })

/**
* Ordered mapping from field names to their native values (JS),
Expand Down Expand Up @@ -62,3 +67,68 @@ export interface ContractCallEntry {
*/
oldFields: FieldsMap
}

/**
* Store a contract call entry in the file storage.
* @param root the storage root directory
* @param entry a call entry
*/
export function saveContractCallEntry(root: string, entry: ContractCallEntry) {
const filename = getEntryFilename(root, entry)
// convert OrderedMaps to arrays
const simplified = {
...entry,
fields: entry.fields.toArray(),
oldFields: entry.oldFields.toArray(),
}
const contents = JSONbig.stringify(simplified)
writeFileSync(filename, contents)
return filename
}

/**
* Load a contract call entry in the file storage.
* @param root the storage root directory
* @param entry a call entry
* @returns the loaded call entry
*/
export function loadContractCallEntry(filename: string): ContractCallEntry {
const contents = readFileSync(filename)
const loaded = JSONbig.parse(contents)
return {
height: loaded.height as number,
contractId: loaded.contractId as string,
txHash: loaded.txHash as string,
method: loaded.method as string,
methodArgs: loaded.methodArgs as any[],
returnValue: loaded.returnValue,
fields: OrderedMap<string, any>(loaded.fields),
oldFields: OrderedMap<string, any>(loaded.oldFields),
}
}

/**
* Get the filename for a contract call entry. Create the parent directory, if required.
*
* @param root storage root
* @param entry call entry
* @returns the filename
*/
function getEntryFilename(root: string, entry: ContractCallEntry) {
const dir = getOrCreateDirectory(root, entry)
return join(dir, `entry-${entry.txHash}.json`)
}

/**
* Return the parent directory for an entry.
* If this directory does not exist, create it recursively.
*
* @param root storage root
* @param entry call entry
* @returns the directory name
*/
function getOrCreateDirectory(root: string, entry: ContractCallEntry) {
const directory = join(root, entry.contractId, entry.height.toString())
mkdirSync(directory, { recursive: true })
return directory
}
73 changes: 73 additions & 0 deletions solarkraft/test/unit/storage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* Storage tests.
*
* Igor Konnov, 2024
*/

import { assert } from 'chai'
import { mkdtempSync } from 'fs'
import { describe, it } from 'mocha'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import {
loadContractCallEntry,
saveContractCallEntry,
} from '../../src/fetcher/storage.js'
import { OrderedMap } from 'immutable'

const TX_HASH =
'9fb12935fbadcd28aa220d076f11be631590d22c60977a53997a746898322ca3'
const CONTRACT_ID = 'CC22QGTOUMERDNIYN7TPNX3V6EMPHQXVSRR3XY56EADF7YTFISD2ROND'

describe('storage tests', () => {
function storeEntry() {
const root = join(tmpdir(), 'solarkraft-storage-')
mkdtempSync(root)
const filename = saveContractCallEntry(root, {
height: 1000,
txHash: TX_HASH,
contractId: CONTRACT_ID,
method: 'set_i32',
methodArgs: [42],
returnValue: 0,
fields: OrderedMap<string, any>([
['a', 3],
['b', 993143214321423154315154321n],
]),
oldFields: OrderedMap<string, any>([
['a', 1],
['b', 993143214321423154315154322n],
]),
})

return [root, filename]
}

it('store entry', async () => {
const [root, filename] = storeEntry()

assert.equal(
filename,
join(root, CONTRACT_ID, '1000', `entry-${TX_HASH}.json`)
)
})

it('load entry', async () => {
const filename = storeEntry()[1]
const entry = loadContractCallEntry(filename)
assert.equal(entry.height, 1000)
assert.equal(entry.txHash, TX_HASH)
assert.equal(entry.contractId, CONTRACT_ID)
assert.equal(entry.method, 'set_i32')
assert.deepEqual(entry.methodArgs, [42])
assert.equal(entry.returnValue, 0)
assert.deepEqual(entry.fields.toArray(), [
['a', 3],
['b', 993143214321423154315154321n],
])
assert.deepEqual(entry.oldFields.toArray(), [
['a', 1],
['b', 993143214321423154315154322n],
])
})
})

0 comments on commit 1ec07e5

Please sign in to comment.