Skip to content

Commit

Permalink
core/ovm: Buffer -> string + signature compatibility (#458)
Browse files Browse the repository at this point in the history
Replacing buffers with strings in core/ovm, making signatures compatible within core/ovm
  • Loading branch information
willmeister committed Sep 25, 2019
1 parent a3cc851 commit bf816d4
Show file tree
Hide file tree
Showing 35 changed files with 756 additions and 781 deletions.
20 changes: 12 additions & 8 deletions packages/core/src/app/aggregator/aggregator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@ import {
Transaction,
TransactionResult,
} from '../../types/serialization'
import { BigNumber, doRangesSpanRange, sign } from '../utils'
import { BigNumber, doRangesSpanRange } from '../utils'
import { BlockManager } from '../../types/block-production'
import { SignatureProvider } from '../../types/keystore'
import { serializeObject } from '../serialization'

export class DefaultAggregator implements Aggregator {
private readonly publicKey: string =
'TODO: figure out public key storage and access'
private readonly privateKey: string =
'TODO: figure out private key storage and access'

public constructor(
private readonly stateManager: StateManager,
private readonly blockManager: BlockManager
private readonly blockManager: BlockManager,
private readonly publicKey: string,
private readonly signatureProvider: SignatureProvider
) {}

public async ingestTransaction(
Expand Down Expand Up @@ -49,9 +48,14 @@ export class DefaultAggregator implements Aggregator {
transaction,
}

const serializedTransaction: string = serializeObject(blockTransaction)

return {
blockTransaction,
witness: sign(this.privateKey, blockTransaction),
witness: await this.signatureProvider.sign(
this.publicKey,
serializedTransaction
),
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class GenericMerkleIntervalTree implements MerkleIntervalTree {
}

public static hash(value: Buffer): Buffer {
return keccak256(value)
return Buffer.from(keccak256(value.toString('hex')), 'hex')
}
/**
* Computes the parent of two GenericMerkleIntervalTreeNode siblings in a tree.
Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/app/block-production/merkle-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,22 @@ export class SparseMerkleTreeImpl implements SparseMerkleTree {
private readonly treeLock: AsyncLock = new AsyncLock({
domainReentrant: true,
})
private readonly hashFunction: (Buffer) => Buffer
private readonly hashBuffer: Buffer = Buffer.alloc(64)

constructor(
private readonly db: DB,
rootHash?: Buffer,
private readonly height: number = 160,
private readonly hashFunction: HashFunction = keccak256
hashFunction: HashFunction = keccak256
) {
assert(!rootHash || rootHash.length === 32, 'Root hash must be 32 bytes')
assert(height > 0, 'SMT height needs to be > 0')

// TODO: Hack for now -- change everything to string if/when it makes sense
this.hashFunction = (buff: Buffer) =>
Buffer.from(hashFunction(buff.toString('hex')), 'hex')

this.populateZeroHashesAndRoot(rootHash)
}

Expand Down
35 changes: 34 additions & 1 deletion packages/core/src/app/keystore/signatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,42 @@ export class DefaultSignatureVerifier implements SignatureVerifier {
}

export class DefaultSignatureProvider implements SignatureProvider {
public constructor(private readonly wallet: ethers.Wallet) {}
public constructor(
private readonly wallet: ethers.Wallet = ethers.Wallet.createRandom()
) {}

public async sign(_address: string, message: string): Promise<string> {
return this.wallet.signMessage(message)
}

public async getAddress(): Promise<string> {
return this.wallet.getAddress()
}
}

export class IdentitySigner implements SignatureProvider {
private static _instance: SignatureProvider
public static instance(): SignatureProvider {
if (!IdentitySigner._instance) {
IdentitySigner._instance = new IdentitySigner()
}
return IdentitySigner._instance
}

public async sign(address: string, message: string): Promise<string> {
return address
}
}

export class IdentityVerifier implements SignatureVerifier {
private static _instance: SignatureVerifier
public static instance(): SignatureVerifier {
if (!IdentityVerifier._instance) {
IdentityVerifier._instance = new IdentityVerifier()
}
return IdentityVerifier._instance
}
public verifyMessage(message: string, signature: string): string {
return signature
}
}
57 changes: 34 additions & 23 deletions packages/core/src/app/ovm/db/hash-preimage-db.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,70 @@
import { HashPreimageDbInterface } from '../../../types/ovm/db'
import { HashAlgorithm } from '../../../types/utils'
import { HashPreimageDBInterface } from '../../../types/ovm/db'
import { HashAlgorithm, Logger } from '../../../types/utils'
import { DB } from '../../../types/db'
import { hashFunctionFor } from '../../utils'
import { getLogger, hashFunctionFor } from '../../utils'
import { Message } from '../../../types/serialization'
import { deserializeObject } from '../../serialization'

const log: Logger = getLogger('hash-preimage-db')

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

/**
/*
* DB to store and access hashes and their associated preimages.
*/
export class HashPreimageDb implements HashPreimageDbInterface {
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 handleMessage(serializedMessage: string): Promise<void> {
try {
const message: Message = deserializeObject(serializedMessage) as Message
if (message.data && 'preimage' in message.data) {
await this.storePreimage(
message.data['preimage'],
HashAlgorithm.KECCAK256
)
}
} catch (e) {
log.debug(
`Received a message that cannot be parsed. Ignoring. Message: ${serializedMessage}, error: ${e.message}, stack: ${e.stack}`
)
}
}

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

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

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

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

if (!recordBuffer) {
return undefined
}

return HashPreimageDb.deserializeRecord(recordBuffer).preimage
return HashPreimageDB.deserializeRecord(recordBuffer).preimage
}

private static serializeRecord(record: Record): Buffer {
Expand All @@ -69,9 +80,9 @@ export class HashPreimageDb implements HashPreimageDbInterface {
private static deserializeRecord(serialized: Buffer): Record {
const obj: {} = JSON.parse(serialized.toString())
return {
preimage: Buffer.from(obj['preimage']),
preimage: obj['preimage'],
hashAlgorithm: obj['hashAlgorithm'],
hash: Buffer.from(obj['hash']),
hash: obj['hash'],
}
}
}
90 changes: 42 additions & 48 deletions packages/core/src/app/ovm/db/signed-by-db.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
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'
import { getLogger, Logger, Md5Hash } from '../../utils'
import { SignatureVerifier } from '../../../types/keystore'
import { DefaultSignatureVerifier } from '../../keystore'
import { deserializeObject, serializeObject } from '../../serialization'
import { SignedMessage } from '../../../types/serialization'

interface Record {
signerPublicKey: Buffer
signature: Buffer
message: Buffer
}
const log: Logger = getLogger('signed-by-db')

/**
* DB to store and access message signatures.
Expand All @@ -21,44 +18,46 @@ export class SignedByDB implements SignedByDBInterface {
) {}

public async handleMessage(
message: Message,
signedMessage?: SignedMessage
serializedMessage: string,
signature?: string
): Promise<void> {
if (!!signedMessage) {
await this.storeSignedMessage(
signedMessage.signedMessage,
signedMessage.sender
)
if (!!signature) {
try {
await this.storeSignedMessage(serializedMessage, signature)
} catch (e) {
log.debug(
`Received a message that cannot be parsed. Ignoring. Message: ${serializedMessage}, error: ${e.message}, stack: ${e.stack}`
)
}
}
}

public async storeSignedMessage(
signature: Buffer,
signerPublicKey: Buffer
serializedMessage: string,
signature: string
): Promise<void> {
// TODO: USE SIGNATURE VERIFIER HERE
const message: Buffer = decryptWithPublicKey(
signerPublicKey,
const signerPublicKey = await this.singatureVerifier.verifyMessage(
serializedMessage,
signature
) as Buffer
const serialized: Buffer = SignedByDB.serializeRecord({
signerPublicKey,
)

const serializedRecord: Buffer = SignedByDB.serializeRecord({
signature,
message,
serializedMessage,
})

await this.db
.bucket(signerPublicKey)
.put(SignedByDB.getKey(message), serialized)
.bucket(Buffer.from(signerPublicKey))
.put(SignedByDB.getKey(serializedMessage), serializedRecord)
}

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

if (!recordBuffer) {
return undefined
Expand All @@ -67,35 +66,30 @@ export class SignedByDB implements SignedByDBInterface {
return SignedByDB.deserializeRecord(recordBuffer).signature
}

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

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

private static getKey(message: Buffer): Buffer {
return Md5Hash(message)
private static getKey(message: string): Buffer {
return Buffer.from(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 serializeRecord(signedMessage: SignedMessage): Buffer {
return Buffer.from(serializeObject(signedMessage))
}

private static deserializeRecord(serialized: Buffer): Record {
const obj: {} = JSON.parse(serialized.toString())
private static deserializeRecord(serialized: Buffer): SignedMessage {
const obj: {} = deserializeObject(serialized.toString())
return {
signerPublicKey: Buffer.from(obj['signerPublicKey']),
signature: Buffer.from(obj['signature']),
message: Buffer.from(obj['message']),
signature: obj['signature'],
serializedMessage: obj['serializedMessage'],
}
}
}
9 changes: 4 additions & 5 deletions packages/core/src/app/ovm/deciders/examples/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ export class Utils {
return (
!!message &&
!!other &&
message.message.channelID.equals(other.message.channelID) &&
message.message.channelID === other.message.channelID &&
message.message.nonce.equals(other.message.nonce) &&
(message.sender.equals(other.sender) ||
message.sender.equals(other.recipient)) &&
(message.recipient.equals(other.recipient) ||
message.recipient.equals(other.sender)) &&
(message.sender === other.sender || message.sender === other.recipient) &&
(message.recipient === other.recipient ||
message.recipient === other.sender) &&
!objectsEqual(message.message.data, other.message.data)
)
}
Expand Down

0 comments on commit bf816d4

Please sign in to comment.