Skip to content

Commit

Permalink
DataStore-backed Deciders in place of witnesses (#384)
Browse files Browse the repository at this point in the history
DB Changes:
- Created HashPreimageDB for use in HashPreimageExistsDecider. Updating the HashPreimageExistsDecider and PreimageExistenceOnMultipleHashes tests to account for DB update
- Added SignedByDB to store signatures for messages for use in SignedByDecider and SignedByQuantifier
- Removed deprecated KeyValueStoreDecider
- Made StateChannelMessageDBInterface extend MessageDB and SignedByDBInterface so that the StateChannelClient, SignedByQuantifier, SignedByDecider, etc. all use the same, consistent datastore

Other Additions:
- Added MessageSubscriber interface to subscribe to messages received

Improvements:
- Moving state-db.ts into db directory
- Moving the keccak256 function into the crypto.ts file
- Moving the HashFunction type into the crypto.ts file
  • Loading branch information
willmeister committed Aug 20, 2019
1 parent 2dae7ed commit 29a16e6
Show file tree
Hide file tree
Showing 33 changed files with 634 additions and 667 deletions.
77 changes: 77 additions & 0 deletions packages/core/src/app/ovm/db/hash-preimage-db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { HashPreimageDbInterface } from '../../../types/ovm/db'
import { HashAlgorithm } from '../../../types/utils'
import { DB } from '../../../types/db'
import { hashFunctionFor } from '../../utils'
import { Message } from '../../../types/serialization'

interface Record {
preimage: Buffer
hashAlgorithm: HashAlgorithm
hash: Buffer
}

/**
* DB to store and access hashes and their associated preimages.
*/
export class HashPreimageDb implements HashPreimageDbInterface {
public constructor(private readonly db: DB) {}

public async handleMessage(message: Message): Promise<void> {
// TODO: handle each specific type of message when we formally define different messages.
if (message.data && 'preimage' in message.data) {
await this.storePreimage(
Buffer.from(message.data['preimage']),
HashAlgorithm.KECCAK256
)
}
}

public async storePreimage(
preimage: Buffer,
hashAlgorithm: HashAlgorithm
): Promise<void> {
const hash: Buffer = hashFunctionFor(hashAlgorithm)(preimage)

const serialized: Buffer = HashPreimageDb.serializeRecord({
preimage,
hashAlgorithm,
hash,
})

await this.db.bucket(Buffer.from(hashAlgorithm)).put(hash, serialized)
}

public async getPreimage(
hash: Buffer,
hashAlgorithm: HashAlgorithm
): Promise<Buffer | undefined> {
const recordBuffer: Buffer = await this.db
.bucket(Buffer.from(hashAlgorithm))
.get(hash)

if (!recordBuffer) {
return undefined
}

return HashPreimageDb.deserializeRecord(recordBuffer).preimage
}

private static serializeRecord(record: Record): Buffer {
return Buffer.from(
JSON.stringify({
preimage: record.preimage.toString(),
hashAlgorithm: record.hashAlgorithm,
hash: record.hash.toString(),
})
)
}

private static deserializeRecord(serialized: Buffer): Record {
const obj: {} = JSON.parse(serialized.toString())
return {
preimage: Buffer.from(obj['preimage']),
hashAlgorithm: obj['hashAlgorithm'],
hash: Buffer.from(obj['hash']),
}
}
}
3 changes: 3 additions & 0 deletions packages/core/src/app/ovm/db/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './hash-preimage-db'
export * from './signed-by-db'
export * from './state-db'
95 changes: 95 additions & 0 deletions packages/core/src/app/ovm/db/signed-by-db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { SignedByDBInterface } from '../../../types/ovm/db/signed-by-db.interface'
import { Message, SignedMessage } from '../../../types/serialization'
import { DB } from '../../../types/db'
import { decryptWithPublicKey, Md5Hash } from '../../utils'

interface Record {
signerPublicKey: Buffer
signature: Buffer
message: Buffer
}

/**
* DB to store and access message signatures.
*/
export class SignedByDB implements SignedByDBInterface {
public constructor(private readonly db: DB) {}

public async handleMessage(
message: Message,
signedMessage?: SignedMessage
): Promise<void> {
if (!!signedMessage) {
await this.storeSignedMessage(
signedMessage.signedMessage,
signedMessage.sender
)
}
}

public async storeSignedMessage(
signature: Buffer,
signerPublicKey: Buffer
): Promise<void> {
const message: Buffer = decryptWithPublicKey(
signerPublicKey,
signature
) as Buffer
const serialized: Buffer = SignedByDB.serializeRecord({
signerPublicKey,
signature,
message,
})

await this.db
.bucket(signerPublicKey)
.put(SignedByDB.getKey(message), serialized)
}

public async getMessageSignature(
message: Buffer,
signerPublicKey
): Promise<Buffer | undefined> {
const recordBuffer: Buffer = await this.db
.bucket(signerPublicKey)
.get(SignedByDB.getKey(message))

if (!recordBuffer) {
return undefined
}

return SignedByDB.deserializeRecord(recordBuffer).signature
}

public async getAllSignedBy(signerPublicKey: Buffer): Promise<Buffer[]> {
const signed: Buffer[] = await this.db
.bucket(signerPublicKey)
.iterator()
.values()

return signed.map((m) => SignedByDB.deserializeRecord(m).message)
}

private static getKey(message: Buffer): Buffer {
return Md5Hash(message)
}

private static serializeRecord(record: Record): Buffer {
return Buffer.from(
JSON.stringify({
signerPublicKey: record.signerPublicKey.toString(),
signature: record.signature.toString(),
message: record.message.toString(),
})
)
}

private static deserializeRecord(serialized: Buffer): Record {
const obj: {} = JSON.parse(serialized.toString())
return {
signerPublicKey: Buffer.from(obj['signerPublicKey']),
signature: Buffer.from(obj['signature']),
message: Buffer.from(obj['message']),
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { StateDB, VerifiedStateUpdate } from '../../types'
import { BigNumber } from '../utils'
import { StateDB, VerifiedStateUpdate } from '../../../types'
import { BigNumber } from '../../utils'

/**
* StateDB used to store the state for different ranges.
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/app/ovm/deciders/and-decider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
ImplicationProofItem,
Property,
} from '../../../types/ovm'
import { CannotDecideError } from './utils'
import { CannotDecideError, handleCannotDecideError } from './utils'

export interface AndDeciderInput {
left: Property
Expand Down Expand Up @@ -34,10 +34,10 @@ export class AndDecider implements Decider {
const [leftDecision, rightDecision] = await Promise.all([
input.left.decider
.decide(input.left.input, input.leftWitness, noCache)
.catch(() => undefined),
.catch(handleCannotDecideError),
input.right.decider
.decide(input.right.input, input.rightWitness, noCache)
.catch(() => undefined),
.catch(handleCannotDecideError),
])

if (!!leftDecision && !leftDecision.outcome) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Decider, Decision, ImplicationProofItem } from '../../../../types/ovm'
import { ParsedMessage } from '../../../../types/serialization'
import { Message } from '../../../../types/serialization'
import { BigNumber } from '../../../utils'

export interface MessageNonceLessThanInput {
messageWithNonce: ParsedMessage
messageWithNonce: Message
lessThanThis: BigNumber
}

Expand Down Expand Up @@ -35,7 +35,7 @@ export class MessageNonceLessThanDecider implements Decider {
]

return {
outcome: input.messageWithNonce.message.nonce.lt(input.lessThanThis),
outcome: input.messageWithNonce.nonce.lt(input.lessThanThis),
justification,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface ForAllSuchThatInput {
quantifier: Quantifier
quantifierParameters: any
propertyFactory: PropertyFactory
witnessFactory: WitnessFactory | undefined
witnessFactory?: WitnessFactory | undefined
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,66 +1,39 @@
import { Decision } from '../../../types/ovm'
import { DB } from '../../../types/db'
import { KeyValueStoreDecider } from './key-value-store-decider'
import { CannotDecideError, HashFunction } from './utils'
import { Decider, Decision, HashPreimageDbInterface } from '../../../types/ovm'
import { CannotDecideError } from './utils'
import { HashAlgorithm } from '../../../types/utils'

export interface HashInput {
hash: Buffer
}

export interface PreimageWitness {
preimage: Buffer
}

/**
* Decider that determines whether the provided witness is the preimage to the hash in question.
*/
export class HashPreimageExistenceDecider extends KeyValueStoreDecider {
private static readonly UNIQUE_ID = 'HashPreimageDecider'

private readonly hashFunction: HashFunction

constructor(db: DB, hashFunction: HashFunction) {
super(db)

this.hashFunction = hashFunction
}
export class HashPreimageExistenceDecider implements Decider {
constructor(
private readonly db: HashPreimageDbInterface,
private readonly hashAlgorithm: HashAlgorithm
) {}

protected async makeDecision(
public async decide(
input: HashInput,
witness: PreimageWitness
_witness?: undefined,
_noCache?: boolean
): Promise<Decision> {
const outcome =
!!witness && this.hashFunction(witness.preimage).equals(input.hash)
const preimage: Buffer = await this.db.getPreimage(
input.hash,
this.hashAlgorithm
)

if (!outcome) {
if (!preimage) {
throw new CannotDecideError(
`Witness [${JSON.stringify(
witness
)}] does not match hash [${JSON.stringify(
`No preimage is stored for hash [${JSON.stringify(
input
)}], so we cannot decide whether a preimage exists for the hash.`
)
}

await this.storeDecision(
input,
HashPreimageExistenceDecider.serializeDecision(witness, input, outcome)
)

return this.constructDecision(witness.preimage, input.hash, outcome)
}

protected getUniqueId(): string {
return HashPreimageExistenceDecider.UNIQUE_ID
}

protected deserializeDecision(decision: Buffer): Decision {
const json: any[] = JSON.parse(decision.toString())
return this.constructDecision(
Buffer.from(json[0]),
Buffer.from(json[1]),
json[2]
)
return this.constructDecision(preimage, input.hash, true)
}

/**
Expand Down Expand Up @@ -93,26 +66,4 @@ export class HashPreimageExistenceDecider extends KeyValueStoreDecider {
],
}
}

/**
* Creates the buffer to be stored for a Decision
*
* @param witness the HashPreimageWitness
* @param input the input that led to the Decision
* @param outcome the outcome of the Decision
* @returns the Buffer of the serialized data
*/
private static serializeDecision(
witness: PreimageWitness,
input: HashInput,
outcome: boolean
): Buffer {
return Buffer.from(
JSON.stringify([
witness.preimage.toString(),
input.hash.toString(),
outcome,
])
)
}
}
1 change: 0 additions & 1 deletion packages/core/src/app/ovm/deciders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ export * from './and-decider'
export * from './examples'
export * from './for-all-such-that-decider'
export * from './hash-preimage-existence-decider'
export * from './key-value-store-decider'
export * from './not-decider'
export * from './utils'

0 comments on commit 29a16e6

Please sign in to comment.