@@ -22,7 +22,18 @@ import (
22
22
"github.com/shutter-network/rolling-shutter/rolling-shutter/shdb"
23
23
)
24
24
25
+ // Maximum age of a tx pointer in blocks before it is considered outdated.
26
+ const maxTxPointerAge = 2
27
+
28
+ var errZeroTxPointerAge = errors .New ("tx pointer has age 0" )
29
+
25
30
func (kpr * Keyper ) processNewSlot (ctx context.Context , slot slotticker.Slot ) error {
31
+ fmt .Println ("" )
32
+ fmt .Println ("" )
33
+ fmt .Println (slot .Number )
34
+ fmt .Println ("" )
35
+ fmt .Println ("" )
36
+
26
37
gnosisKeyperDB := gnosisdatabase .New (kpr .dbpool )
27
38
syncedUntil , err := gnosisKeyperDB .GetTransactionSubmittedEventsSyncedUntil (ctx )
28
39
if err != nil {
@@ -35,93 +46,119 @@ func (kpr *Keyper) processNewSlot(ctx context.Context, slot slotticker.Slot) err
35
46
// already been built, so we return an error.
36
47
return errors .Errorf ("processing slot %d for which a block has already been processed" , slot .Number )
37
48
}
49
+ nextBlock := syncedUntil .BlockNumber + 1
38
50
39
51
queries := obskeyper .New (kpr .dbpool )
40
- keyperSet , err := queries .GetKeyperSet (ctx , syncedUntil . BlockNumber )
52
+ keyperSet , err := queries .GetKeyperSet (ctx , nextBlock )
41
53
if err == pgx .ErrNoRows {
42
54
log .Debug ().
43
55
Uint64 ("slot" , slot .Number ).
44
- Int64 ("block-number" , syncedUntil . BlockNumber ).
56
+ Int64 ("block-number" , nextBlock ).
45
57
Msg ("ignoring slot as no keyper set has been found for it" )
46
58
return nil
47
59
}
48
60
if err != nil {
49
- return errors .Wrapf (err , "failed to query keyper set for block %d" , syncedUntil . BlockNumber )
61
+ return errors .Wrapf (err , "failed to query keyper set for block %d" , nextBlock )
50
62
}
51
- for _ , m := range keyperSet .Keypers {
52
- if m == shdb .EncodeAddress (kpr .config .GetAddress ()) {
53
- return kpr .triggerDecryption (ctx , slot , syncedUntil , & keyperSet )
54
- }
63
+ if keyperSet .Contains (kpr .config .GetAddress ()) {
64
+ return kpr .triggerDecryption (ctx , slot , nextBlock , & keyperSet )
55
65
}
56
- log .Debug ().Uint64 ("slot" , slot .Number ).Msg ("ignoring block as not part of keyper set" )
66
+ log .Debug ().
67
+ Uint64 ("slot" , slot .Number ).
68
+ Int64 ("block-number" , nextBlock ).
69
+ Int64 ("keyper-set-index" , keyperSet .KeyperConfigIndex ).
70
+ Str ("address" , kpr .config .GetAddress ().Hex ()).
71
+ Msg ("ignoring block as not part of keyper set" )
57
72
return nil
58
73
}
59
74
60
- func (kpr * Keyper ) triggerDecryption (
61
- ctx context.Context ,
62
- slot slotticker.Slot ,
63
- syncedUntil gnosisdatabase.TransactionSubmittedEventsSyncedUntil ,
64
- keyperSet * obskeyper.KeyperSet ,
65
- ) error {
66
- fmt .Println ("" )
67
- fmt .Println ("" )
68
- fmt .Println (slot .Number )
69
- fmt .Println ("" )
70
- fmt .Println ("" )
75
+ func (kpr * Keyper ) getTxPointer (ctx context.Context , eon int64 , slot int64 , keyperConfigIndex int64 ) (int64 , error ) {
71
76
gnosisKeyperDB := gnosisdatabase .New (kpr .dbpool )
72
- coreKeyperDB := corekeyperdatabase .New (kpr .dbpool )
73
-
74
- eonStruct , err := coreKeyperDB .GetEonForBlockNumber (ctx , syncedUntil .BlockNumber )
75
- if err != nil {
76
- return errors .Wrapf (err , "failed to query eon for block number %d from db" , syncedUntil .BlockNumber )
77
- }
78
- eon := eonStruct .Eon
79
-
80
- var txPointer int64
81
- var txPointerAge int64
77
+ var txPointer , txPointerAge int64
82
78
txPointerDB , err := gnosisKeyperDB .GetTxPointer (ctx , eon )
83
79
if err == pgx .ErrNoRows {
80
+ // The tx pointer is expected to be missing from the db if the eon has just started. In
81
+ // this case, we should initialize it to zero with an age of 1, ie decrypt starting with
82
+ // the first transaction.
83
+ // The tx pointer may also be missing if the keyper has been started late and no decryption
84
+ // key has been generated or received yet (receiving the keys message would update the
85
+ // pointer). In this case, the true age is unknown, as we only know the start block but
86
+ // not the start slot of the eon. However, we can ignore this edge case as it will be
87
+ // resolved automatically when the first keys message is received. If key generation
88
+ // continues to fail, eventually our tx pointer age will exceed the maximum value and we
89
+ // will start participating in the recovery process, albeit a bit late.
90
+ err := gnosisKeyperDB .SetTxPointer (ctx , gnosisdatabase.SetTxPointerParams {
91
+ Eon : eon ,
92
+ Slot : slot ,
93
+ Value : 0 ,
94
+ })
95
+ if err != nil {
96
+ return 0 , errors .Wrap (err , "failed to initialize tx pointer" )
97
+ }
84
98
txPointer = 0
85
- txPointerAge = syncedUntil . BlockNumber - keyperSet . ActivationBlockNumber + 1
99
+ txPointerAge = 1
86
100
} else if err != nil {
87
- return errors .Wrap (err , "failed to query tx pointer from db" )
101
+ return 0 , errors .Wrap (err , "failed to query tx pointer from db" )
88
102
} else {
89
- txPointerAge = syncedUntil .BlockNumber - txPointerDB .Block
90
103
txPointer = txPointerDB .Value
104
+ txPointerAge = slot - txPointerDB .Slot
91
105
}
92
106
if txPointerAge == 0 {
93
107
// A pointer of age 0 means we already received the pointer from a DecryptionKeys message
94
108
// even though we haven't sent our shares yet. In that case, sending our shares is
95
109
// unnecessary.
110
+ return 0 , errZeroTxPointerAge
111
+ }
112
+ // If the tx pointer is outdated, the system has failed to generate decryption keys (or at
113
+ // least we haven't received them). This either means not enough keypers are online or they
114
+ // don't agree on the current value of the tx pointer. In order to recover, we choose the
115
+ // current length of the transaction queue as the new tx pointer, as this is a value
116
+ // everyone can agree on.
117
+ isOutdated := txPointerAge > maxTxPointerAge
118
+ if isOutdated {
96
119
log .Warn ().
97
- Uint64 ("slot" , slot .Number ).
98
- Int64 ("block-number" , syncedUntil .BlockNumber ).
99
- Int64 ("eon" , eon ).
100
- Int64 ("tx-pointer" , txPointer ).
101
- Int64 ("tx-pointer-age" , txPointerAge ).
102
- Msg ("ignoring new block as tx pointer age is 0" )
103
- return nil
104
- }
105
- if txPointerAge > maxTxPointerAge {
106
- // If the tx pointer is outdated, the system has failed to generate decryption keys (or at
107
- // least we haven't received them). This either means not enough keypers are online or they
108
- // don't agree on the current value of the tx pointer. In order to recover, we choose the
109
- // current length of the transaction queue as the new tx pointer, as this is a value
110
- // everyone can agree on.
111
- log .Warn ().
112
- Uint64 ("slot" , slot .Number ).
113
- Int64 ("block-number" , syncedUntil .BlockNumber ).
120
+ Int64 ("slot" , slot ).
114
121
Int64 ("eon" , eon ).
115
122
Int64 ("tx-pointer" , txPointer ).
116
123
Int64 ("tx-pointer-age" , txPointerAge ).
117
124
Msg ("outdated tx pointer" )
118
- txPointer , err = gnosisKeyperDB .GetTransactionSubmittedEventCount (ctx , keyperSet . KeyperConfigIndex )
125
+ txPointer , err = gnosisKeyperDB .GetTransactionSubmittedEventCount (ctx , keyperConfigIndex )
119
126
if err == pgx .ErrNoRows {
120
127
txPointer = 0
121
128
} else if err != nil {
122
- return errors .Wrap (err , "failed to query transaction submitted event count from db" )
129
+ return 0 , errors .Wrap (err , "failed to query transaction submitted event count from db" )
123
130
}
124
131
}
132
+ return txPointer , nil
133
+ }
134
+
135
+ func (kpr * Keyper ) triggerDecryption (
136
+ ctx context.Context ,
137
+ slot slotticker.Slot ,
138
+ nextBlock int64 ,
139
+ keyperSet * obskeyper.KeyperSet ,
140
+ ) error {
141
+ gnosisKeyperDB := gnosisdatabase .New (kpr .dbpool )
142
+ coreKeyperDB := corekeyperdatabase .New (kpr .dbpool )
143
+
144
+ eonStruct , err := coreKeyperDB .GetEonForBlockNumber (ctx , nextBlock )
145
+ if err != nil {
146
+ return errors .Wrapf (err , "failed to query eon for block number %d from db" , nextBlock )
147
+ }
148
+ eon := eonStruct .Eon
149
+
150
+ txPointer , err := kpr .getTxPointer (ctx , eon , int64 (slot .Number ), keyperSet .KeyperConfigIndex )
151
+ if err == errZeroTxPointerAge {
152
+ log .Warn ().
153
+ Uint64 ("slot" , slot .Number ).
154
+ Int64 ("block-number" , nextBlock ).
155
+ Int64 ("eon" , eon ).
156
+ Int64 ("tx-pointer" , txPointer ).
157
+ Msg ("ignoring new block as tx pointer age is 0" )
158
+ return nil
159
+ } else if err != nil {
160
+ return err
161
+ }
125
162
126
163
identityPreimages , err := kpr .getDecryptionIdentityPreimages (ctx , slot , keyperSet .KeyperConfigIndex , txPointer )
127
164
if err != nil {
@@ -137,16 +174,15 @@ func (kpr *Keyper) triggerDecryption(
137
174
return errors .Wrap (err , "failed to insert published tx pointer into db" )
138
175
}
139
176
trigger := epochkghandler.DecryptionTrigger {
140
- BlockNumber : uint64 (syncedUntil . BlockNumber ),
177
+ BlockNumber : uint64 (nextBlock ),
141
178
IdentityPreimages : identityPreimages ,
142
179
}
143
180
event := broker .NewEvent (& trigger )
144
181
log .Debug ().
145
182
Uint64 ("slot" , slot .Number ).
146
- Uint64 ("block-number" , uint64 (syncedUntil . BlockNumber )).
183
+ Uint64 ("block-number" , uint64 (nextBlock )).
147
184
Int ("num-identities" , len (trigger .IdentityPreimages )).
148
185
Int64 ("tx-pointer" , txPointer ).
149
- Int64 ("tx-pointer-age" , txPointerAge ).
150
186
Msg ("sending decryption trigger" )
151
187
kpr .decryptionTriggerChannel <- event
152
188
0 commit comments