-
Notifications
You must be signed in to change notification settings - Fork 107
/
backend_insecure.go
205 lines (180 loc) · 5.84 KB
/
backend_insecure.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package beacon
import (
"encoding/hex"
"fmt"
"github.com/tendermint/tendermint/abci/types"
beacon "github.com/oasisprotocol/oasis-core/go/beacon/api"
"github.com/oasisprotocol/oasis-core/go/common/cbor"
"github.com/oasisprotocol/oasis-core/go/consensus/api/transaction"
"github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api"
beaconState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/beacon/state"
genesis "github.com/oasisprotocol/oasis-core/go/genesis/api"
)
type backendInsecure struct {
app *beaconApplication
}
func (impl *backendInsecure) OnInitChain(
ctx *api.Context,
state *beaconState.MutableState,
params *beacon.ConsensusParameters,
doc *genesis.Document,
) error {
// If the backend is configured to use explicitly set epochs, there
// is nothing further to do. And yes, this ignores the base epoch,
// but that's how certain tests are written.
if params.DebugMockBackend {
return nil
}
// Set the initial epoch.
baseEpoch := doc.Beacon.Base
if err := state.SetEpoch(ctx, baseEpoch, ctx.InitialHeight()); err != nil {
return fmt.Errorf("beacon: failed to set initial epoch: %w", err)
}
impl.app.doEmitEpochEvent(ctx, baseEpoch)
// Arm the initial epoch transition.
return impl.scheduleEpochTransitionBlock(ctx, state, params.InsecureParameters, doc.Beacon.Base+1)
}
func (impl *backendInsecure) OnBeginBlock(
ctx *api.Context,
state *beaconState.MutableState,
params *beacon.ConsensusParameters,
req types.RequestBeginBlock,
) error {
future, err := state.GetFutureEpoch(ctx)
if err != nil {
return fmt.Errorf("beacon: failed to get future epoch: %w", err)
}
if future == nil {
return nil
}
height := ctx.BlockHeight() + 1 // Current height is ctx.BlockHeight() + 1
switch {
case future.Height < height:
// What the fuck, we missed transitioning the epoch?
ctx.Logger().Error("height mismatch in defered set",
"height", height,
"expected_height", future.Height,
)
return fmt.Errorf("beacon: height mismatch in defered set")
case future.Height > height:
// The epoch transition is scheduled to happen in the grim
// darkness of the far future.
return nil
case future.Height == height:
// Time to fire the scheduled epoch transition.
}
// Transition the epoch.
ctx.Logger().Info("setting epoch",
"epoch", future.Epoch,
"current_height", height,
)
if err = state.SetEpoch(ctx, future.Epoch, height); err != nil {
return fmt.Errorf("beacon: failed to set epoch: %w", err)
}
if err = state.ClearFutureEpoch(ctx); err != nil {
return fmt.Errorf("beacon: failed to clear future epoch: %w", err)
}
if !params.DebugMockBackend {
if err = impl.scheduleEpochTransitionBlock(ctx, state, params.InsecureParameters, future.Epoch+1); err != nil {
return err
}
}
impl.app.doEmitEpochEvent(ctx, future.Epoch)
// Generate the beacon
return impl.onEpochChangeBeacon(ctx, state, params, future.Epoch, req)
}
func (impl *backendInsecure) scheduleEpochTransitionBlock(
ctx *api.Context,
state *beaconState.MutableState,
params *beacon.InsecureParameters,
nextEpoch beacon.EpochTime,
) error {
// Schedule the epoch transition based on block height.
nextHeight := int64(nextEpoch) * params.Interval
return impl.app.scheduleEpochTransitionBlock(ctx, state, nextEpoch, nextHeight)
}
func (impl *backendInsecure) onEpochChangeBeacon(
ctx *api.Context,
state *beaconState.MutableState,
params *beacon.ConsensusParameters,
epoch beacon.EpochTime,
req types.RequestBeginBlock,
) error {
var entropy []byte
entropyCtx := prodEntropyCtx
height := ctx.BlockHeight()
if height <= ctx.InitialHeight() {
// No meaningful previous commit, use the block hash. This isn't
// fantastic, but it's only for one epoch.
ctx.Logger().Debug("onBeaconEpochChange: using block hash as entropy")
entropy = req.Hash
} else {
// Use the previous commit hash as the entropy input, under the theory
// that the merkle root of all the commits that went into the last
// block is harder for any single validator to game than the block
// hash.
//
// Note: This is still insecure, and is vulnerable to adversarial
// manipulation. If this is a problem, don't use this backend.
ctx.Logger().Debug("onBeaconEpochChange: using commit hash as entropy")
entropy = req.Header.GetLastCommitHash()
}
if len(entropy) == 0 {
return fmt.Errorf("beacon: failed to obtain entropy")
}
b := GetBeacon(epoch, entropyCtx, entropy)
ctx.Logger().Debug("onBeaconEpochChange: generated beacon",
"epoch", epoch,
"beacon", hex.EncodeToString(b),
"block_hash", hex.EncodeToString(entropy),
"height", ctx.BlockHeight(),
)
return impl.app.onNewBeacon(ctx, b)
}
func (impl *backendInsecure) ExecuteTx(
ctx *api.Context,
state *beaconState.MutableState,
params *beacon.ConsensusParameters,
tx *transaction.Transaction,
) error {
switch tx.Method {
case MethodSetEpoch:
if !params.DebugMockBackend {
return fmt.Errorf("beacon: method '%s' is disabled via consensus", MethodSetEpoch)
}
return impl.doTxSetEpoch(ctx, state, tx.Body)
default:
return fmt.Errorf("beacon: invalid method: %s", tx.Method)
}
}
func (impl *backendInsecure) doTxSetEpoch(
ctx *api.Context,
state *beaconState.MutableState,
txBody []byte,
) error {
now, _, err := state.GetEpoch(ctx)
if err != nil {
return err
}
var epoch beacon.EpochTime
if err := cbor.Unmarshal(txBody, &epoch); err != nil {
return err
}
if epoch <= now {
ctx.Logger().Error("explicit epoch transition does not advance time",
"epoch", now,
"new_epoch", epoch,
)
return fmt.Errorf("beacon: explicit epoch does not advance time")
}
height := ctx.BlockHeight() + 1 // Current height is ctx.BlockHeight() + 1
ctx.Logger().Info("scheduling explicit epoch transition",
"epoch", epoch,
"next_height", height+1,
"is_check_only", ctx.IsCheckOnly(),
)
if err := state.SetFutureEpoch(ctx, epoch, height+1); err != nil {
return err
}
return nil
}