11import { PoetBlockAnchor } from '@po.et/poet-js'
2+ import { DateTime } from 'luxon'
23import { Collection , Db } from 'mongodb'
34import * as Pino from 'pino'
4- import { identity } from 'ramda'
5+ import { identity , map , filter , tap , complement } from 'ramda'
6+ import { TransactionReceipt } from 'web3-core'
57
68import { EthereumRegistryContract } from 'Helpers/EthereumRegistryContract'
79import { IPFS } from 'Helpers/IPFS'
810import { childWithFileName } from 'Helpers/Logging'
11+ import { asyncPipe } from 'Helpers/asyncPipe'
912import { ClaimIPFSHashPair } from 'Interfaces'
1013
1114export interface Dependencies {
@@ -15,8 +18,13 @@ export interface Dependencies {
1518 readonly ethereumRegistryContract : EthereumRegistryContract
1619}
1720
21+ export interface Configuration {
22+ readonly maximumUnconfirmedTransactionAgeInSeconds : number
23+ }
24+
1825export interface Arguments {
1926 readonly dependencies : Dependencies
27+ readonly configuration : Configuration
2028}
2129
2230export interface Business {
@@ -28,7 +36,15 @@ export interface Business {
2836 readonly confirmClaimFiles : ( claimIPFSHashPairs : ReadonlyArray < ClaimIPFSHashPair > ) => Promise < void >
2937 readonly uploadNextAnchorReceipt : ( ) => Promise < void >
3038 readonly uploadNextClaimFileAnchorReceiptPair : ( ) => Promise < void >
31- readonly registerNextDirectory : ( ) => Promise < void >
39+ readonly writeNextDirectoryToEthereum : ( ) => Promise < void >
40+ readonly setRegistryIndex : (
41+ confirmClaimAndAnchorReceiptDirectory : string ,
42+ registryIndex : number ,
43+ transactionHash : string ,
44+ blockHash : string ,
45+ blockNumber : number ,
46+ ) => Promise < void >
47+ readonly getEthereumTransactionReceipts : ( ) => Promise < void >
3248}
3349
3450interface DbEntry {
@@ -40,6 +56,17 @@ interface DbEntry {
4056 readonly batchDirectoryConfirmed ?: boolean
4157 readonly claimAndAnchorReceiptDirectory ?: string
4258 readonly registryIndex ?: number
59+ readonly registryAdditionTransactionReceipt ?: {
60+ readonly transactionHash ?: string
61+ readonly transactionCreationDate ?: Date
62+ readonly blockHash ?: string
63+ readonly blockNumber ?: number , // comma due to some tslint randomness, will move to eslint in the future
64+ }
65+ readonly onCidAdded ?: {
66+ readonly transactionHash ?: string
67+ readonly blockHash ?: string
68+ readonly blockNumber ?: number , // comma due to some tslint randomness, will move to eslint in the future
69+ }
4370}
4471
4572export const Business = ( {
@@ -49,6 +76,9 @@ export const Business = ({
4976 ipfs,
5077 ethereumRegistryContract,
5178 } ,
79+ configuration : {
80+ maximumUnconfirmedTransactionAgeInSeconds,
81+ } ,
5282} : Arguments ) : Business => {
5383 const businessLogger : Pino . Logger = childWithFileName ( logger , __filename )
5484 const claimAnchorReceiptsCollection : Collection < DbEntry > = db . collection ( 'claimAnchorReceipts' )
@@ -187,26 +217,135 @@ export const Business = ({
187217 await claimAnchorReceiptsCollection . updateOne ( { claimId } , { $set : { claimAndAnchorReceiptDirectory } } )
188218 }
189219
190- const registerNextDirectory = async ( ) => {
220+ const writeNextDirectoryToEthereum = async ( ) => {
191221 const logger = businessLogger . child ( { method : 'registerNextDirectory' } )
192- const entry = await claimAnchorReceiptsCollection . findOne ( {
193- claimAndAnchorReceiptDirectory : { $ne : null } ,
194- registryIndex : null ,
195- } )
196- if ( ! entry )
222+
223+ const entry = await claimAnchorReceiptsCollection . findOneAndUpdate (
224+ {
225+ claimAndAnchorReceiptDirectory : { $ne : null } ,
226+ $or : [
227+ { 'registryAdditionTransactionReceipt.transactionCreationDate' : null } ,
228+ { $and : [
229+ { 'registryAdditionTransactionReceipt.transactionCreationDate' : {
230+ $lt : DateTime . utc ( ) . minus ( { seconds : maximumUnconfirmedTransactionAgeInSeconds } ) . toJSDate ( ) ,
231+ } } ,
232+ { 'registryAdditionTransactionReceipt.blockHash' : null } ,
233+ ] } ,
234+ ] ,
235+ } ,
236+ {
237+ $set : { 'registryAdditionTransactionReceipt.transactionCreationDate' : new Date ( ) } ,
238+ } ,
239+ )
240+ if ( ! entry . value )
197241 return
198- const { claimId, claimAndAnchorReceiptDirectory } = entry
199- const cidCount = await ethereumRegistryContract . getCidCount ( )
242+ const { claimId, claimAndAnchorReceiptDirectory } = entry . value
200243 logger . debug (
201- { claimId, claimAndAnchorReceiptDirectory, cidCount } ,
202- 'Registering next (claim + anchor receipt) directory to Ethereum' ,
244+ { claimId, claimAndAnchorReceiptDirectory } ,
245+ 'Adding (claim + anchor receipt) directory to Ethereum' ,
203246 )
204- await ethereumRegistryContract . addCid ( claimAndAnchorReceiptDirectory )
247+ const transactionHash = await ethereumRegistryContract . addCid ( claimAndAnchorReceiptDirectory )
248+
205249 logger . info (
206- { claimId, claimAndAnchorReceiptDirectory, cidCount } ,
207- '(claim + anchor receipt) directory added to Ethereum ' ,
250+ { claimId, claimAndAnchorReceiptDirectory, transactionHash } ,
251+ '(claim + anchor receipt) transaction sent ' ,
208252 )
209- await claimAnchorReceiptsCollection . updateOne ( { claimId } , { $set : { registryIndex : cidCount } } )
253+
254+ await claimAnchorReceiptsCollection . updateOne (
255+ { claimAndAnchorReceiptDirectory } ,
256+ { $set : { 'registryAdditionTransactionReceipt.transactionHash' : transactionHash } } ,
257+ )
258+
259+ }
260+
261+ const setRegistryIndex = async (
262+ claimAndAnchorReceiptDirectory : string ,
263+ registryIndex : number ,
264+ transactionHash : string ,
265+ blockHash : string ,
266+ blockNumber : number ,
267+ ) => {
268+ const logger = businessLogger . child ( { method : 'setRegistryIndex' } )
269+ await claimAnchorReceiptsCollection . updateOne (
270+ { claimAndAnchorReceiptDirectory } ,
271+ { $set : { registryIndex, onCidAdded : { transactionHash, blockHash, blockNumber } } } ,
272+ )
273+ logger . info (
274+ { claimAndAnchorReceiptDirectory, registryIndex, blockNumber, transactionHash } ,
275+ 'Registry index for (claim + anchor receipt) directory set' ,
276+ )
277+ }
278+
279+ const getEthereumTransactionReceipts = async ( ) => {
280+ const logger = businessLogger . child ( { method : 'getEthereumTransactionReceipts' } )
281+
282+ logger . trace ( 'Looking for transactions without confirmation' )
283+
284+ const entries = await claimAnchorReceiptsCollection . find (
285+ {
286+ 'registryAdditionTransactionReceipt.transactionHash' : { $ne : null } ,
287+ 'registryAdditionTransactionReceipt.blockHash' : null ,
288+ } ,
289+ { projection : { 'registryAdditionTransactionReceipt.transactionHash' : 1 } } ,
290+ ) . toArray ( )
291+
292+ if ( ! entries . length ) {
293+ logger . trace ( 'No transactions without confirmation found' )
294+ return
295+ }
296+
297+ const transactionHashes = entries . map ( _ => _ . registryAdditionTransactionReceipt . transactionHash )
298+
299+ logger . debug ( { transactionHashes } , 'These transactions have no known confirmations yet' )
300+
301+ const receiptToSimplified = ( { transactionHash, blockHash, blockNumber } : TransactionReceipt ) => ( {
302+ transactionHash,
303+ blockHash,
304+ blockNumber,
305+ } )
306+
307+ const logErrors = ( transactionReceipt : TransactionReceipt ) => {
308+ logger . error ( { transactionReceipt } , 'Error in transaction receipt' )
309+ }
310+
311+ const transactionReceiptIsOk = ( transactionReceipt : TransactionReceipt ) => transactionReceipt . status
312+
313+ const filterAndLogErrors = asyncPipe (
314+ tap ( asyncPipe (
315+ filter ( complement ( transactionReceiptIsOk ) ) ,
316+ map ( logErrors ) ,
317+ ) ) ,
318+ filter ( transactionReceiptIsOk ) ,
319+ )
320+
321+ const getReceipts = asyncPipe (
322+ map ( ethereumRegistryContract . getTransactionReceipt ) ,
323+ Promise . all . bind ( Promise ) ,
324+ filter ( identity ) ,
325+ filterAndLogErrors ,
326+ map ( receiptToSimplified ) ,
327+ ) as ( transactionHashes : ReadonlyArray < string > ) => Promise < ReadonlyArray < Partial < TransactionReceipt > > >
328+
329+ const receipts = await getReceipts ( transactionHashes )
330+
331+ if ( ! receipts . length ) {
332+ logger . trace ( { transactionHashes } , 'No transactions receipts available yet for these transactions' )
333+ return
334+ }
335+
336+ logger . debug ( { receipts } , 'Got these transaction receipts' )
337+
338+ await Promise . all ( receipts . map ( ( { transactionHash, blockHash, blockNumber } ) =>
339+ claimAnchorReceiptsCollection . updateOne (
340+ { 'registryAdditionTransactionReceipt.transactionHash' : transactionHash } ,
341+ { $set : {
342+ 'registryAdditionTransactionReceipt.blockHash' : blockHash ,
343+ 'registryAdditionTransactionReceipt.blockNumber' : blockNumber ,
344+ } } ,
345+ ) ,
346+ ) )
347+
348+ logger . info ( { receipts } , 'Transaction receipts set' )
210349 }
211350
212351 return {
@@ -218,6 +357,8 @@ export const Business = ({
218357 confirmClaimFiles,
219358 uploadNextAnchorReceipt,
220359 uploadNextClaimFileAnchorReceiptPair,
221- registerNextDirectory,
360+ writeNextDirectoryToEthereum,
361+ setRegistryIndex,
362+ getEthereumTransactionReceipts,
222363 }
223364}
0 commit comments