Skip to content
This repository has been archived by the owner on Jul 1, 2023. It is now read-only.

Commit

Permalink
Merge pull request #34 from dbpunk-labs/feat/store_sdk
Browse files Browse the repository at this point in the history
Feat/store sdk
  • Loading branch information
imotai authored Feb 17, 2023
2 parents 444bf0c + cf59a3f commit efb36ff
Show file tree
Hide file tree
Showing 20 changed files with 408 additions and 47 deletions.
60 changes: 43 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,51 @@ yarn add db3.js

## Use db3.js in action

### Build db3 client

```typescript
/*
|----------------------------|
| use db3js open a database |
|----------------------------|
*/

// build sign function
const sign = await getSign()

// build database factory
const dbFactory = new DB3Factory({
node: 'http://127.0.0.1:26659',
sign,
nonce
})
// the key seed
const mnemonic ='...'
// create a wallet
const wallet = DB3BrowserWallet.createNew(mnemonic, 'DB3_SECP259K1')
// build db3 client
const client = new DB3Client('http://127.0.0.1:26659', wallet)
```
### Create a database

// open database with an address
const db = dbFactory.open("0x5ca8d43c15fb366d80e221d11a34894eb0975da6")
```typescript
const [dbId, txId] = await client.createDatabase()
const db = initializeDB3('http://127.0.0.1:26659', dbId, wallet)
```

### Create a collection

```typescript
// add a index to collection
const indexList: Index[] = [
{
name: 'idx1',
id: 1,
fields: [
{
fieldPath: 'name',
valueMode: {
oneofKind: 'order',
order: Index_IndexField_Order.ASCENDING,
},
},
],
},
]
// create a collecion
const collectionRef = await collection(db, 'cities', indexList)
// add a doc to collection
const result = await addDoc(collectionRef, {
name: 'beijing',
address: 'north',
})
// get all docs from collection
const docs = await getDocs(collectionRef)
```

## Show Your Support
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
"types": "./dist/index.d.ts",
"type": "module",
"scripts": {
"build": "microbundle --entry src/index.ts --external none",
"build:wpt": "microbundle --entry src/index.ts --external none -f umd -o thirdparty/wpt/indexedDB/resources/indexedDB3.js ",
"build": "microbundle --entry src/index.ts --external none -f modern,esm",
"test": "jest",
"benny-sdk": "ts-node-esm --experimental-specifier-resolution=node ./src/benches/sdk_query.ts",
"benny-mutation": "ts-node-esm --experimental-specifier-resolution=node ./src/benches/sdk_mutation.ts",
Expand Down
54 changes: 48 additions & 6 deletions src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,20 @@ import { StorageProvider } from '../provider/storage_provider'
import { Wallet } from '../wallet/wallet'
import { DbId } from '../crypto/id'
import { toB64, fromHEX, toHEX } from '../crypto/crypto_utils'
import { Database, Index } from '../proto/db3_database'
import { QuerySessionInfo } from '../proto/db3_session'

//
//
// the db3 client for developers
//
//
export class DB3Client {
readonly provider: StorageProvider
readonly accountAddress: string
querySessionInfo: QuerySessionInfo | undefined
sessionToken: string | undefined

/**
* new a db3 client with db3 node url and wallet
*
Expand Down Expand Up @@ -74,11 +81,11 @@ export class DB3Client {
* get a database information
*
*/
async getDatabase(addr: string) {
async getDatabase(addr: string): Promise<Database | undefined> {
const token = await this.keepSessionAlive()
const response = await this.provider.getDatabase(addr, token)
this.querySessionInfo!.queryCount += 1
return response?.db
return response.db
}

async listCollection(databaseAddress: string) {
Expand Down Expand Up @@ -130,7 +137,8 @@ export class DB3Client {
) {
const documentMutation: DocumentMutation = {
collectionName,
document: [BSON.serialize(document)],
documents: [BSON.serialize(document)],
ids: [],
}

const meta: BroadcastMeta = {
Expand Down Expand Up @@ -171,21 +179,55 @@ export class DB3Client {
}))
}

async deleteDocument(
databaseAddress: string,
collectionName: string,
ids: string[]
) {
const meta: BroadcastMeta = {
nonce: this.provider.getNonce().toString(),
chainId: ChainId.MainNet,
chainRole: ChainRole.StorageShardChain,
}

const documentMutation: DocumentMutation = {
collectionName,
documents: [],
ids,
}

const dm: DatabaseMutation = {
meta,
collectionMutations: [],
documentMutations: [documentMutation],
dbAddress: fromHEX(databaseAddress),
action: DatabaseAction.DeleteDocument,
}
const payload = DatabaseMutation.toBinary(dm)
return this.provider.sendMutation(payload, PayloadType.DatabasePayload)
}

async keepSessionAlive() {
if (!this.querySessionInfo) {
if (!this.querySessionInfo || !this.sessionToken) {
const response = await this.provider.openSession()
this.sessionToken = response.sessionToken
this.querySessionInfo = response.querySessionInfo
return this.sessionToken
}
// TODO
if (this.querySessionInfo!.queryCount > 1000) {
await this.provider.closeSession()
if (this.querySessionInfo.queryCount > 1000) {
await this.provider.closeSession(
this.sessionToken,
this.querySessionInfo
)
const response = await this.provider.openSession()
this.sessionToken = response.sessionToken
this.querySessionInfo = response.querySessionInfo
return this.sessionToken
}
if (!this.sessionToken) {
throw new Error('sessioToken is not found')
}
return this.sessionToken
}
}
13 changes: 9 additions & 4 deletions src/crypto/crypto_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ export const pathRegex = new RegExp("^m(\\/[0-9]+')+$")

export const replaceDerive = (val: string): string => val.replace("'", '')

export const getMasterKeyFromSeed = (seed: Hex): Keys => {
interface Keys {
key: Uint8Array
chainCode: Uint8Array
}

export const getMasterKeyFromSeed = (seed: string): Keys => {
const h = hmac.create(sha512, ED25519_CURVE)
const I = h.update(fromHEX(seed)).digest()
const IL = I.slice(0, 32)
Expand Down Expand Up @@ -152,10 +157,10 @@ export function toB64(aBytes: Uint8Array): string {
}

export const derivePath = (
path: Path,
seed: Hex,
path: string,
seed: string,
offset = HARDENED_OFFSET
): Keys => {
) => {
if (!isValidPath(path)) {
throw new Error('Invalid derivation path')
}
Expand Down
5 changes: 3 additions & 2 deletions src/crypto/ed25519_keypair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//

import nacl from 'tweetnacl'
import type { Keypair } from './keypair'
import type { ExportedKeypair, Keypair } from './keypair'
import { SignatureScheme, SIGNATURE_SCHEME_TO_FLAG } from './publickey'
import { Ed25519PublicKey } from './ed25519_publickey'
import { mnemonicToSeedHex, isValidHardenedPath } from './mnemonics'
Expand All @@ -36,13 +36,14 @@ export interface Ed25519KeypairData {
}

export class Ed25519Keypair implements Keypair {
keypair: Ed25519KeypairData | nacl.SignKeyPair
/**
* Create a new Ed25519 keypair instance.
* Generate random keypair if no {@link Ed25519Keypair} is provided.
*
* @param keypair Ed25519 keypair
*/
constructor(keypair?: Ed25519KeypairData) {
constructor(keypair: Ed25519KeypairData) {
if (keypair) {
this.keypair = keypair
} else {
Expand Down
8 changes: 5 additions & 3 deletions src/crypto/id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//

// @ts-nocheck
import sha3 from 'js-sha3'
import { toB64, fromHEX } from './crypto_utils'
import { Uint64BE } from 'int64-buffer'
Expand All @@ -23,6 +23,7 @@ const TX_ID_LENGTH = 32
const DB_ID_LENGTH = 20

export class TxId {
data: Uint8Array
constructor(data: Uint8Array) {
const inputDataLength = data.length
if (inputDataLength != TX_ID_LENGTH) {
Expand All @@ -37,7 +38,7 @@ export class TxId {
// from the broadcast response
//
static from(hash: Uint8Array): TxId {
return new TxId(data)
return new TxId(hash)
}

getB64(): string {
Expand All @@ -46,11 +47,12 @@ export class TxId {
}

export class DbId {
addr: string
constructor(sender: string, nonce: number) {
const binary_addr = fromHEX(sender)
const nonceBuf = new Uint64BE(nonce)
let tmp = new Uint8Array(DB_ID_LENGTH + 8)
tmp.set(nonceBuf.toBuffer())
tmp.set(nonceBuf.buffer || nonceBuf.toBuffer())
tmp.set(binary_addr, 8)
this.addr = '0x' + sha3.sha3_256(tmp).slice(0, 40)
}
Expand Down
1 change: 1 addition & 0 deletions src/crypto/secp256k1_publickey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import sha3 from 'js-sha3'
import { fromB64, toB64 } from './crypto_utils'
import {
bytesEqual,
PublicKey,
PublicKeyInitData,
SIGNATURE_SCHEME_TO_FLAG,
} from './publickey'
Expand Down
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export { DB3BrowserWallet } from './wallet/db3_browser_wallet'
export { DB3Client } from './client/client'
export { initializeDB3 } from './store/app'
export { collection } from './store/collection'
export { addDoc, getDocs, deleteDoc } from './store/document'
15 changes: 10 additions & 5 deletions src/provider/storage_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//

// @ts-nocheck
import {
GrpcWebFetchTransport,
GrpcWebOptions,
} from '@protobuf-ts/grpcweb-transport'
import { StorageNodeClient } from '../proto/db3_node.client'
import { WriteRequest, Mutation, PayloadType } from '../proto/db3_mutation'
import { WriteRequest, PayloadType } from '../proto/db3_mutation'
import {
OpenSessionRequest,
BroadcastRequest,
GetAccountRequest,
ListDocumentsRequest,
CloseSessionRequest,
ShowDatabaseRequest,
} from '../proto/db3_node'
import {
CloseSessionPayload,
Expand All @@ -40,6 +42,8 @@ import { fromHEX } from '../crypto/crypto_utils'
// the db3 storage node provider implementation which provides low level methods to exchange with db3 network
//
export class StorageProvider {
readonly client: StorageNodeClient
readonly wallet: Wallet
/**
* new a storage provider with db3 storage grpc url
*/
Expand Down Expand Up @@ -74,11 +78,12 @@ export class StorageProvider {
return new TxId(response.hash)
}

// @ts-nocheck
/**
* build a session with storage node for querying data
*/
async openSession() {
let header = ''
let header
if (typeof window === 'undefined') {
header =
new Date().getTime() +
Expand Down Expand Up @@ -127,9 +132,9 @@ export class StorageProvider {
* get the account information with db3 address
*
*/
async getAccount(addr: string) {
async getAccount(addr: Uint8Array) {
const getAccountRequest: GetAccountRequest = {
addr: address,
addr,
}
const { response } = await this.client.getAccount(getAccountRequest)
return response
Expand Down
12 changes: 12 additions & 0 deletions src/store/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { DB3Client } from '../client/client'
import { Wallet } from '../wallet/wallet'
import { DB3Store } from './database'

export function initializeDB3(
node: string,
dbAddress: string,
wallet: Wallet
): DB3Store {
const dbClient = new DB3Client(node, wallet)
return new DB3Store(dbAddress, dbClient)
}
32 changes: 32 additions & 0 deletions src/store/collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Index } from '../proto/db3_database'
import { DB3Store } from './database'
import { DocumentData, DocumentReference } from './document'
import { Query } from './query'

export class CollectionReference {
readonly type = 'collection'
readonly db: DB3Store
readonly name: string
constructor(db: DB3Store, name: string) {
this.db = db
this.name = name
}
}

export async function collection(
db: DB3Store,
name: string,
index: Index[]
): Promise<CollectionReference>

export async function collection(
db: DB3Store,
name: string,
index: Index[]
): Promise<CollectionReference> {
const collections = await db.getCollections()
if (!collections || !collections[name]) {
await db.client.createCollection(db.address, name, index)
}
return new CollectionReference(db, name)
}
Loading

0 comments on commit efb36ff

Please sign in to comment.