@@ -55,6 +55,8 @@ var jsrsa = require('jsrsasign');
55
55
var elliptic = require ( 'elliptic' ) ;
56
56
var sha3 = require ( 'js-sha3' ) ;
57
57
var BN = require ( 'bn.js' ) ;
58
+ var Set = require ( 'es6-set' ) ;
59
+ var HashTable = require ( 'hashtable' ) ;
58
60
import * as crypto from "./crypto"
59
61
import * as stats from "./stats"
60
62
import * as sdk_util from "./sdk_util"
@@ -369,6 +371,7 @@ export interface TransactionProtobuf {
369
371
setConfidentialityProtocolVersion ( version :string ) :void ;
370
372
setNonce ( nonce :Buffer ) :void ;
371
373
setToValidators ( Buffer ) :void ;
374
+ getTxid ( ) :string ;
372
375
getChaincodeID ( ) :{ buffer : Buffer } ;
373
376
setChaincodeID ( buffer :Buffer ) :void ;
374
377
getMetadata ( ) :{ buffer : Buffer } ;
@@ -419,6 +422,9 @@ export class Chain {
419
422
// The member services used for this chain
420
423
private memberServices :MemberServices ;
421
424
425
+ // The eventHub service used for this chain
426
+ private eventHub :EventHub ;
427
+
422
428
// The key-val store used for this chain
423
429
private keyValStore :KeyValStore ;
424
430
@@ -430,14 +436,15 @@ export class Chain {
430
436
431
437
// Temporary variables to control how long to wait for deploy and invoke to complete before
432
438
// emitting events. This will be removed when the SDK is able to receive events from the
433
- private deployWaitTime :number = 20 ;
439
+ private deployWaitTime :number = 30 ;
434
440
private invokeWaitTime :number = 5 ;
435
441
436
442
// The crypto primitives object
437
443
cryptoPrimitives :crypto . Crypto ;
438
444
439
445
constructor ( name :string ) {
440
446
this . name = name ;
447
+ this . eventHub = new EventHub ( ) ;
441
448
}
442
449
443
450
/**
@@ -520,6 +527,29 @@ export class Chain {
520
527
}
521
528
} ;
522
529
530
+ /**
531
+ * Get the eventHub service associated this chain.
532
+ * @returns {eventHub } Return the current eventHub service, or undefined if not set.
533
+ */
534
+ getEventHub ( ) :EventHub {
535
+ return this . eventHub ;
536
+ } ;
537
+
538
+ /**
539
+ * Set and connect to the peer to be used as the event source.
540
+ */
541
+ eventHubConnect ( peeraddr : string ) :void {
542
+ this . eventHub . setPeerAddr ( peeraddr ) ;
543
+ this . eventHub . connect ( ) ;
544
+ } ;
545
+
546
+ /**
547
+ * Set and connect to the peer to be used as the event source.
548
+ */
549
+ eventHubDisconnect ( ) :void {
550
+ this . eventHub . disconnect ( ) ;
551
+ } ;
552
+
523
553
/**
524
554
* Determine if security is enabled.
525
555
*/
@@ -1144,6 +1174,10 @@ export class TransactionContext extends events.EventEmitter {
1144
1174
private binding : any ;
1145
1175
private tcert :TCert ;
1146
1176
private attrs :string [ ] ;
1177
+ private complete :boolean ;
1178
+ private timeoutId :any ;
1179
+ private waitTime :number ;
1180
+ private cevent :any ;
1147
1181
1148
1182
constructor ( member :Member , tcert :TCert ) {
1149
1183
super ( ) ;
@@ -1152,6 +1186,8 @@ export class TransactionContext extends events.EventEmitter {
1152
1186
this . memberServices = this . chain . getMemberServices ( ) ;
1153
1187
this . tcert = tcert ;
1154
1188
this . nonce = this . chain . cryptoPrimitives . generateNonce ( ) ;
1189
+ this . complete = false ;
1190
+ this . timeoutId = null ;
1155
1191
}
1156
1192
1157
1193
/**
@@ -1376,7 +1412,47 @@ export class TransactionContext extends events.EventEmitter {
1376
1412
} ) ;
1377
1413
self . getChain ( ) . sendTransaction ( tx , emitter ) ;
1378
1414
} else {
1379
- self . getChain ( ) . sendTransaction ( tx , self ) ;
1415
+ let txType = tx . pb . getType ( ) ;
1416
+ let uuid = tx . pb . getTxid ( ) ;
1417
+ let eh = self . getChain ( ) . getEventHub ( ) ;
1418
+ // async deploy and invokes need to maintain
1419
+ // tx context(completion status(self.complete))
1420
+ if ( txType == _fabricProto . Transaction . Type . CHAINCODE_DEPLOY ) {
1421
+ self . cevent = new EventDeployComplete ( uuid , tx . chaincodeID ) ;
1422
+ self . waitTime = self . getChain ( ) . getDeployWaitTime ( ) ;
1423
+ } else if ( txType == _fabricProto . Transaction . Type . CHAINCODE_INVOKE ) {
1424
+ self . cevent = new EventInvokeComplete ( "Tx " + uuid + " complete" ) ;
1425
+ self . waitTime = self . getChain ( ) . getInvokeWaitTime ( ) ;
1426
+ }
1427
+ eh . registerTxEvent ( uuid , function ( uuid ) {
1428
+ self . complete = true ;
1429
+ if ( self . timeoutId ) {
1430
+ clearTimeout ( self . timeoutId ) ;
1431
+ }
1432
+ eh . unregisterTxEvent ( uuid ) ;
1433
+ self . emit ( "complete" , self . cevent ) ;
1434
+ } ) ;
1435
+ self . getChain ( ) . sendTransaction ( tx , self ) ;
1436
+ // sync query can be skipped as response
1437
+ // is processed and event generated in sendTransaction
1438
+ // no timeout processing is necessary
1439
+ if ( txType != _fabricProto . Transaction . Type . CHAINCODE_QUERY ) {
1440
+ debug ( "waiting %d seconds before emitting complete event" , self . waitTime ) ;
1441
+ self . timeoutId = setTimeout ( function ( ) {
1442
+ debug ( "timeout uuid=" , uuid ) ;
1443
+ if ( ! self . complete )
1444
+ // emit error if eventhub connect otherwise
1445
+ // emit a complete event as done previously
1446
+ if ( eh . isconnected ( ) )
1447
+ self . emit ( "error" , "timed out waiting for transaction to complete" ) ;
1448
+ else
1449
+ self . emit ( "complete" , self . cevent ) ;
1450
+ else
1451
+ eh . unregisterTxEvent ( uuid ) ;
1452
+ } ,
1453
+ self . waitTime * 1000
1454
+ ) ;
1455
+ }
1380
1456
}
1381
1457
} else {
1382
1458
debug ( 'Missing TCert...' ) ;
@@ -2130,7 +2206,6 @@ export class Peer {
2130
2206
let event = new EventDeploySubmitted ( response . msg . toString ( ) , tx . chaincodeID ) ;
2131
2207
debug ( "EventDeploySubmitted event: %j" , event ) ;
2132
2208
eventEmitter . emit ( "submitted" , event ) ;
2133
- self . waitForDeployComplete ( eventEmitter , event ) ;
2134
2209
}
2135
2210
} else {
2136
2211
// Deploy completed with status "FAILURE" or "UNDEFINED"
@@ -2144,7 +2219,6 @@ export class Peer {
2144
2219
eventEmitter . emit ( "error" , new EventTransactionError ( "the invoke response is missing the transaction UUID" ) ) ;
2145
2220
} else {
2146
2221
eventEmitter . emit ( "submitted" , new EventInvokeSubmitted ( response . msg . toString ( ) ) ) ;
2147
- self . waitForInvokeComplete ( eventEmitter ) ;
2148
2222
}
2149
2223
} else {
2150
2224
// Invoke completed with status "FAILURE" or "UNDEFINED"
@@ -2166,43 +2240,6 @@ export class Peer {
2166
2240
} ) ;
2167
2241
} ;
2168
2242
2169
- /**
2170
- * TODO: Temporary hack to wait until the deploy event has hopefully completed.
2171
- * This does not detect if an error occurs in the peer or chaincode when deploying.
2172
- * When peer event listening is added to the SDK, this will be implemented correctly.
2173
- */
2174
- private waitForDeployComplete ( eventEmitter :events . EventEmitter , submitted :EventDeploySubmitted ) : void {
2175
- let waitTime = this . chain . getDeployWaitTime ( ) ;
2176
- debug ( "waiting %d seconds before emitting deploy complete event" , waitTime ) ;
2177
- setTimeout (
2178
- function ( ) {
2179
- let event = new EventDeployComplete (
2180
- submitted . uuid ,
2181
- submitted . chaincodeID ,
2182
- "TODO: get actual results; waited " + waitTime + " seconds and assumed deploy was successful"
2183
- ) ;
2184
- eventEmitter . emit ( "complete" , event ) ;
2185
- } ,
2186
- waitTime * 1000
2187
- ) ;
2188
- }
2189
-
2190
- /**
2191
- * TODO: Temporary hack to wait until the deploy event has hopefully completed.
2192
- * This does not detect if an error occurs in the peer or chaincode when deploying.
2193
- * When peer event listening is added to the SDK, this will be implemented correctly.
2194
- */
2195
- private waitForInvokeComplete ( eventEmitter :events . EventEmitter ) : void {
2196
- let waitTime = this . chain . getInvokeWaitTime ( ) ;
2197
- debug ( "waiting %d seconds before emitting invoke complete event" , waitTime ) ;
2198
- setTimeout (
2199
- function ( ) {
2200
- eventEmitter . emit ( "complete" , new EventInvokeComplete ( "waited " + waitTime + " seconds and assumed invoke was successful" ) ) ;
2201
- } ,
2202
- waitTime * 1000
2203
- ) ;
2204
- }
2205
-
2206
2243
/**
2207
2244
* Remove the peer from the chain.
2208
2245
*/
@@ -2726,3 +2763,146 @@ export function getChain(chainName, create) {
2726
2763
export function newFileKeyValStore ( dir :string ) :KeyValStore {
2727
2764
return new FileKeyValStore ( dir ) ;
2728
2765
}
2766
+
2767
+ /**
2768
+ * The ChainCodeCBE is used internal to the EventHub to hold chaincode event registration callbacks.
2769
+ */
2770
+ class ChainCodeCBE {
2771
+ ccid : string ;
2772
+ eventname : string ;
2773
+ payload : Uint8Array ;
2774
+ cb : Function ;
2775
+ constructor ( ccid : string , eventname : string , payload : Uint8Array , cb : Function ) {
2776
+ this . ccid = ccid ;
2777
+ this . eventname = eventname ;
2778
+ this . payload = payload ;
2779
+ this . cb = cb ;
2780
+ }
2781
+ }
2782
+
2783
+ /**
2784
+ * The EventHub is used to distribute events from a specific event source(peer)
2785
+ */
2786
+ export class EventHub {
2787
+ // peer addr to connect to
2788
+ private peeraddr : string ;
2789
+ // grpc events interface
2790
+ private events : any ;
2791
+ // grpc event client interface
2792
+ private client : any ;
2793
+ // grpc chat streaming interface
2794
+ private call : any ;
2795
+ // hashtable of clients registered for chaincode events
2796
+ private chaincodeRegistrants : any ;
2797
+ // set of clients registered for block events
2798
+ private blockRegistrants : any ;
2799
+ // hashtable of clients registered for transactional events
2800
+ private txRegistrants : any ;
2801
+ // fabric connection state of this eventhub
2802
+ private connected : boolean ;
2803
+ constructor ( ) {
2804
+ this . chaincodeRegistrants = new HashTable ( ) ;
2805
+ this . blockRegistrants = new Set ( ) ;
2806
+ this . txRegistrants = new HashTable ( ) ;
2807
+ this . peeraddr = "localhost:7053" ;
2808
+ this . connected = false ;
2809
+ }
2810
+
2811
+ public setPeerAddr ( peeraddr : string ) {
2812
+ this . peeraddr = peeraddr ;
2813
+ }
2814
+
2815
+ public isconnected ( ) {
2816
+ return this . connected ;
2817
+ }
2818
+
2819
+ public connect ( ) {
2820
+ if ( this . connected ) return ;
2821
+ this . events = grpc . load ( __dirname + "/protos/events.proto" ) . protos ;
2822
+ this . client = new this . events . Events ( this . peeraddr , grpc . credentials . createInsecure ( ) ) ;
2823
+ this . call = this . client . chat ( ) ;
2824
+ this . connected = true ;
2825
+ this . registerBlockEvent ( this . txCallback ) ;
2826
+
2827
+ let eh = this ; // for callback context
2828
+ this . call . on ( 'data' , function ( event ) {
2829
+ if ( event . Event == "chaincodeEvent" ) {
2830
+ var cbe = eh . chaincodeRegistrants . get ( event . chaincodeEvent . chaincodeID + "/" + event . chaincodeEvent . eventName ) ;
2831
+ if ( cbe ) {
2832
+ cbe . payload = event . chaincodeEvent . payload ;
2833
+ cbe . cb ( cbe ) ;
2834
+ }
2835
+ } else if ( event . Event == "block" ) {
2836
+ eh . blockRegistrants . forEach ( function ( cb ) {
2837
+ cb ( event . block ) ;
2838
+ } ) ;
2839
+ }
2840
+ } ) ;
2841
+ this . call . on ( 'end' , function ( ) {
2842
+ eh . call . end ( ) ;
2843
+ // clean up Registrants - should app get notified?
2844
+ eh . chaincodeRegistrants . clear ( ) ;
2845
+ eh . blockRegistrants . clear ( ) ;
2846
+ } ) ;
2847
+ }
2848
+
2849
+ public disconnect ( ) {
2850
+ if ( ! this . connected ) return ;
2851
+ this . unregisterBlockEvent ( this . txCallback ) ;
2852
+ this . call . end ( ) ;
2853
+ this . connected = false ;
2854
+ }
2855
+
2856
+ public registerChaincodeEvent ( ccid : string , eventname : string , callback : Function ) {
2857
+ if ( ! this . connected ) return ;
2858
+ let cb = new ChainCodeCBE ( ccid , eventname , null , callback ) ;
2859
+ let register = { register : { events : [ { eventType : "CHAINCODE" , chaincodeRegInfo :{ chaincodeID : ccid , eventName : eventname } } ] } } ;
2860
+ this . chaincodeRegistrants . put ( ccid + "/" + eventname , cb ) ;
2861
+ this . call . write ( register ) ;
2862
+ }
2863
+
2864
+ public unregisterChaincodeEvent ( ccid : string , eventname : string ) {
2865
+ if ( ! this . connected ) return ;
2866
+ var unregister = { unregister : { events : [ { eventType : "CHAINCODE" , chaincodeRegInfo :{ chaincodeID : ccid , eventName : eventname } } ] } } ;
2867
+ this . chaincodeRegistrants . remove ( ccid + "/" + eventname ) ;
2868
+ this . call . write ( unregister ) ;
2869
+ }
2870
+
2871
+ public registerBlockEvent ( callback :Function ) {
2872
+ if ( ! this . connected ) return ;
2873
+ this . blockRegistrants . add ( callback ) ;
2874
+ if ( this . blockRegistrants . size == 1 ) {
2875
+ var register = { register : { events : [ { eventType : "BLOCK" } ] } } ;
2876
+ this . call . write ( register ) ;
2877
+ }
2878
+ }
2879
+
2880
+ public unregisterBlockEvent ( callback :Function ) {
2881
+ if ( ! this . connected ) return ;
2882
+ if ( this . blockRegistrants . size <= 1 ) {
2883
+ var unregister = { unregister : { events : [ { eventType : "BLOCK" } ] } } ;
2884
+ this . call . write ( unregister ) ;
2885
+ }
2886
+ this . blockRegistrants . delete ( callback ) ;
2887
+ }
2888
+
2889
+ public registerTxEvent ( txid :string , callback :Function ) {
2890
+ debug ( "reg txid " + txid ) ;
2891
+ this . txRegistrants . put ( txid , callback ) ;
2892
+ }
2893
+
2894
+ public unregisterTxEvent ( txid :string ) {
2895
+ this . txRegistrants . remove ( txid ) ;
2896
+ }
2897
+
2898
+ private txCallback = ( event ) => {
2899
+ debug ( "txCallback event=%j" , event ) ;
2900
+ var eh = this ;
2901
+ event . transactions . forEach ( function ( transaction ) {
2902
+ debug ( "transaction.txid=" + transaction . txid ) ;
2903
+ var cb = eh . txRegistrants . get ( transaction . txid ) ;
2904
+ if ( cb )
2905
+ cb ( transaction . txid ) ;
2906
+ } ) ;
2907
+ }
2908
+ }
0 commit comments