mirror of
https://github.com/hackku21/loc-chain-backend.git
synced 2024-10-27 20:34:03 +00:00
Clean up transaction logging logic for encounters
This commit is contained in:
parent
934b322835
commit
6ba038e4bf
@ -1,10 +1,14 @@
|
|||||||
import {Config, Controllers, HTTPServer, Files, Middlewares, Routing, Unit} from '@extollo/lib'
|
import {Config, Controllers, HTTPServer, Files, Middlewares, Routing, Unit} from '@extollo/lib'
|
||||||
import {CommandLine} from "@extollo/cli";
|
import {CommandLine} from "@extollo/cli"
|
||||||
import {FirebaseUnit} from "./app/units/FirebaseUnit";
|
import {FirebaseUnit} from "./app/units/FirebaseUnit"
|
||||||
|
import {Blockchain} from "./app/units/Blockchain"
|
||||||
|
import {Transaction} from "./app/units/rtdb/Transaction"
|
||||||
|
|
||||||
export const Units = [
|
export const Units = [
|
||||||
Config,
|
Config,
|
||||||
FirebaseUnit,
|
FirebaseUnit,
|
||||||
|
Blockchain,
|
||||||
|
Transaction,
|
||||||
Files,
|
Files,
|
||||||
CommandLine,
|
CommandLine,
|
||||||
Controllers,
|
Controllers,
|
||||||
|
@ -25,7 +25,7 @@ export class FirebaseResource<T extends FirebaseResourceItem> extends Iterable<T
|
|||||||
return new Promise<number>((res, rej) => {
|
return new Promise<number>((res, rej) => {
|
||||||
this.ref().orderByChild('seqID')
|
this.ref().orderByChild('seqID')
|
||||||
.on('value', snapshot => {
|
.on('value', snapshot => {
|
||||||
res((this.resolveObject(snapshot.val()).reverse()?.[0]?.seqID || 0) + 1)
|
res((this.resolveObject(snapshot.val()).reverse()?.[0]?.seqID ?? -1) + 1)
|
||||||
}, rej)
|
}, rej)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -66,7 +66,22 @@ export class FirebaseResource<T extends FirebaseResourceItem> extends Iterable<T
|
|||||||
*/
|
*/
|
||||||
async push(item: T): Promise<T> {
|
async push(item: T): Promise<T> {
|
||||||
item.seqID = await this.getNextID()
|
item.seqID = await this.getNextID()
|
||||||
|
// @ts-ignore
|
||||||
|
delete item.firebaseID
|
||||||
await this.ref().push(item)
|
await this.ref().push(item)
|
||||||
|
|
||||||
|
// Look up the firebaseID
|
||||||
|
await new Promise<void>((res, rej) => {
|
||||||
|
this.ref().orderByChild('seqID')
|
||||||
|
.limitToLast(1)
|
||||||
|
.on('value', snapshot => {
|
||||||
|
if ( snapshot.val() ) {
|
||||||
|
item.firebaseID = Object.keys(snapshot.val())[0]
|
||||||
|
}
|
||||||
|
res()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +156,9 @@ export class Blockchain extends Unit {
|
|||||||
public async submitTransactions(group: [TransactionResourceItem, TransactionResourceItem]) {
|
public async submitTransactions(group: [TransactionResourceItem, TransactionResourceItem]) {
|
||||||
const lastBlock = await this.getLastBlock()
|
const lastBlock = await this.getLastBlock()
|
||||||
|
|
||||||
|
this.logging.verbose('Last block:')
|
||||||
|
this.logging.verbose(lastBlock)
|
||||||
|
|
||||||
const block: BlockResourceItem = {
|
const block: BlockResourceItem = {
|
||||||
timestamp: (new Date).getTime(),
|
timestamp: (new Date).getTime(),
|
||||||
uuid: uuid_v4(),
|
uuid: uuid_v4(),
|
||||||
@ -178,6 +181,7 @@ export class Blockchain extends Unit {
|
|||||||
public async getGenesisBlock(): Promise<Block> {
|
public async getGenesisBlock(): Promise<Block> {
|
||||||
const message = openpgp.Message.fromText("0000")
|
const message = openpgp.Message.fromText("0000")
|
||||||
const privateKey = this.config.get("app.gpg.key.private")
|
const privateKey = this.config.get("app.gpg.key.private")
|
||||||
|
|
||||||
return new Block({
|
return new Block({
|
||||||
timestamp: (new Date).getTime(),
|
timestamp: (new Date).getTime(),
|
||||||
uuid: '0000',
|
uuid: '0000',
|
||||||
@ -186,8 +190,10 @@ export class Blockchain extends Unit {
|
|||||||
lastBlockUUID: '',
|
lastBlockUUID: '',
|
||||||
proof: (await openpgp.sign({
|
proof: (await openpgp.sign({
|
||||||
message,
|
message,
|
||||||
privateKeys: privateKey
|
privateKeys: await openpgp.readKey({
|
||||||
})).toString(),
|
armoredKey: privateKey
|
||||||
|
}),
|
||||||
|
})),
|
||||||
firebaseID: '',
|
firebaseID: '',
|
||||||
seqID: -1,
|
seqID: -1,
|
||||||
})
|
})
|
||||||
@ -202,7 +208,7 @@ export class Blockchain extends Unit {
|
|||||||
|
|
||||||
const genesis = (await this.getGenesisBlock()).toItem()
|
const genesis = (await this.getGenesisBlock()).toItem()
|
||||||
await (<BlockResource>this.app().make(BlockResource)).push(genesis)
|
await (<BlockResource>this.app().make(BlockResource)).push(genesis)
|
||||||
return this.getLastBlock()
|
return new Block(genesis)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -231,7 +237,9 @@ export class Blockchain extends Unit {
|
|||||||
// Sign the hash using the server's private key
|
// Sign the hash using the server's private key
|
||||||
return (await openpgp.sign({
|
return (await openpgp.sign({
|
||||||
message,
|
message,
|
||||||
privateKeys: privateKey
|
privateKeys: await openpgp.readKey({
|
||||||
|
armoredKey: privateKey,
|
||||||
|
})
|
||||||
})).toString()
|
})).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,49 +12,71 @@ import { Blockchain } from "../Blockchain"
|
|||||||
*/
|
*/
|
||||||
@Singleton()
|
@Singleton()
|
||||||
export class Transaction extends Unit {
|
export class Transaction extends Unit {
|
||||||
|
private processing: boolean = false
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
protected readonly firebase!: FirebaseUnit
|
protected readonly firebase!: FirebaseUnit
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
protected readonly blockchain!: Blockchain
|
protected readonly blockchain!: Blockchain
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
protected readonly logging!: Logging
|
||||||
|
|
||||||
|
claim() {
|
||||||
|
if ( !this.processing ) {
|
||||||
|
this.processing = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
release() {
|
||||||
|
this.processing = false
|
||||||
|
}
|
||||||
|
|
||||||
public async compareTransactions(transaction1: TransactionResourceItem, transaction2: TransactionResourceItem) {
|
public async compareTransactions(transaction1: TransactionResourceItem, transaction2: TransactionResourceItem) {
|
||||||
// verify signature
|
// verify signature
|
||||||
const result1 = await openpgp.verify({
|
const result1 = await openpgp.verify({
|
||||||
publicKeys: await openpgp.readKey({
|
publicKeys: await openpgp.readKey({
|
||||||
armoredKey: transaction2.partnerPublicKey
|
armoredKey: transaction2.partnerPublicKey
|
||||||
}),
|
}),
|
||||||
message: openpgp.Message.fromText(transaction1.combinedHash),
|
message: await openpgp.readMessage({
|
||||||
signature: await openpgp.readSignature({
|
armoredMessage: transaction1.validationSignature,
|
||||||
armoredSignature: transaction1.validationSignature // parse detached signature
|
}),
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const result2 = await openpgp.verify({
|
const result2 = await openpgp.verify({
|
||||||
publicKeys: await openpgp.readKey({
|
publicKeys: await openpgp.readKey({
|
||||||
armoredKey: transaction1.partnerPublicKey
|
armoredKey: transaction1.partnerPublicKey
|
||||||
}),
|
}),
|
||||||
message: openpgp.Message.fromText(transaction2.combinedHash),
|
message: await openpgp.readMessage({
|
||||||
signature: await openpgp.readSignature({
|
armoredMessage: transaction2.validationSignature,
|
||||||
armoredSignature: transaction2.validationSignature // parse detached signature
|
}),
|
||||||
})
|
|
||||||
})
|
})
|
||||||
return await (result1.signatures[0].verified) && await (result2.signatures[0].verified)
|
|
||||||
|
return (await result1.signatures[0].verified) && (await result2.signatures[0].verified)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async up() {
|
public async up() {
|
||||||
this.firebase.ref("transaction").on("value", async () => {
|
this.firebase.ref("transaction").on("child_added", async () => {
|
||||||
// array of pairs of tranaction resource items
|
this.logging.debug('Received child_added event for transactions reference.')
|
||||||
const groupedTransactions: [TransactionResourceItem, TransactionResourceItem][] = []
|
if ( !this.claim() ) return
|
||||||
|
|
||||||
|
// array of pairs of transaction resource items
|
||||||
|
let groupedTransactions: [TransactionResourceItem, TransactionResourceItem][] = []
|
||||||
// collection of transaction resource items
|
// collection of transaction resource items
|
||||||
let transactions = await TransactionResource.collect().collect()
|
let transactions = await TransactionResource.collect().collect()
|
||||||
|
|
||||||
// compare each item
|
// compare each item
|
||||||
transactions.each(transaction1 => {
|
await transactions.promiseMap(async transaction1 => {
|
||||||
// for each item that is not itself
|
// for each item that is not itself
|
||||||
transactions.where("combinedHash", "!=", transaction1.combinedHash)
|
await transactions.where('combinedHash', '!=', transaction1.combinedHash)
|
||||||
// get a second item
|
// get a second item
|
||||||
.each(transaction2 => {
|
.promiseMap(async transaction2 => {
|
||||||
//if the item matches
|
//if the item matches
|
||||||
if (this.compareTransactions(transaction1, transaction2)) {
|
if ( await this.compareTransactions(transaction1, transaction2) ) {
|
||||||
// and remove the two matching items
|
// and remove the two matching items
|
||||||
transactions = transactions.whereNotIn("combinedHash", [transaction1.combinedHash, transaction2.combinedHash])
|
transactions = transactions.whereNotIn("combinedHash", [transaction1.combinedHash, transaction2.combinedHash])
|
||||||
// insert grouped items into groupedTransactions
|
// insert grouped items into groupedTransactions
|
||||||
@ -62,14 +84,34 @@ export class Transaction extends Unit {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const seenCombinedHashes: string[] = []
|
||||||
|
groupedTransactions = groupedTransactions.filter(group => {
|
||||||
|
const key = group.map(x => x.combinedHash).sort().join('-')
|
||||||
|
if ( !seenCombinedHashes.includes(key) ) {
|
||||||
|
seenCombinedHashes.push(key)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
for (const group of groupedTransactions) {
|
for (const group of groupedTransactions) {
|
||||||
await this.blockchain.submitTransactions(group)
|
const block = await this.blockchain.submitTransactions(group)
|
||||||
|
|
||||||
|
this.logging.verbose('Created block:')
|
||||||
|
this.logging.verbose(block)
|
||||||
|
|
||||||
await this.firebase.ref("transaction").child(group[0].firebaseID).remove()
|
await this.firebase.ref("transaction").child(group[0].firebaseID).remove()
|
||||||
await this.firebase.ref("transaction").child(group[1].firebaseID).remove()
|
await this.firebase.ref("transaction").child(group[1].firebaseID).remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.release()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async down() {
|
public async down() {
|
||||||
|
// Release all subscriptions before shutdown
|
||||||
|
this.firebase.ref("transaction").off()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user