/
interface.go
364 lines (283 loc) · 11 KB
/
interface.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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
package backend
import (
"context"
"errors"
"math/big"
"strconv"
"strings"
"time"
"bitbucket.org/cpchain/chain/accounts"
"bitbucket.org/cpchain/chain/accounts/abi/bind"
"bitbucket.org/cpchain/chain/accounts/keystore"
"bitbucket.org/cpchain/chain/commons/log"
times "bitbucket.org/cpchain/chain/commons/time"
"bitbucket.org/cpchain/chain/configs"
"bitbucket.org/cpchain/chain/consensus"
"bitbucket.org/cpchain/chain/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/p2p"
)
var (
// defaultTimeGapAllowed is the time gap allowed to build connection with remote peer
defaultTimeGapAllowed = time.Second * time.Duration(times.MaxGapDuration)
)
// Action is type enumerator for FSM action
type Action uint8
// Those actions are the result after handleMsg returned
const (
NoAction Action = iota
BroadcastMsgAction
BroadcastAndInsertBlockAction
)
// MsgCode is type enumerator for FSM message type
type MsgCode uint8
// Those are msg codes used in fsm
const (
NoMsgCode MsgCode = iota
PreprepareMsgCode
PrepareMsgCode
CommitMsgCode
PrepareAndCommitMsgCode
ValidateMsgCode
ImpeachPreprepareMsgCode
ImpeachPrepareMsgCode
ImpeachCommitMsgCode
ImpeachPrepareAndCommitMsgCode
ImpeachValidateMsgCode
)
var (
msgCodeName = map[MsgCode]string{
NoMsgCode: "NoMsgCode",
PreprepareMsgCode: "PreprepareMsgCode",
PrepareMsgCode: "PrepareMsgCode",
CommitMsgCode: "CommitMsgCode",
PrepareAndCommitMsgCode: "PrepareAndCommitMsgCode",
ValidateMsgCode: "ValidateMsgCode",
ImpeachPreprepareMsgCode: "ImpeachPreprepareMsgCode",
ImpeachPrepareMsgCode: "ImpeachPrepareMsgCode",
ImpeachCommitMsgCode: "ImpeachCommitMsgCode",
ImpeachPrepareAndCommitMsgCode: "ImpeachPrepareAndCommitMsgCode",
ImpeachValidateMsgCode: "ImpeachValidateMsgCode",
}
)
func (mc MsgCode) String() string {
if name, ok := msgCodeName[mc]; ok {
return name
}
return "Unknown MsgCode"
}
// DSMStatus represents a Dpor State Machine Status
type DSMStatus struct {
Number uint64
// TODO: i need hash here
State consensus.State
}
// ClientBackend is the client operation interface
type ClientBackend interface {
ChainBackend
ContractBackend
}
// ChainBackend is the chain client operation interface
type ChainBackend interface {
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error)
NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
}
// ImpeachedProposer returns the impeached proposer of a header if the block is an impeach block
func ImpeachedProposer(header *types.Header) (common.Address, error) {
if header == nil || header.Number == nil {
return common.Address{}, errors.New("invalid header to retrieve the impeached proposer")
}
if !header.Impeachment() {
return common.Address{}, errors.New("cannot retrieve impeached proposer from an non-impeach block header")
}
blockNumber := header.Number.Uint64()
viewLen, termLen := configs.ChainConfigInfo().Dpor.ViewLen, configs.ChainConfigInfo().Dpor.TermLen
proposerIndex := ((blockNumber - 1) % (viewLen * termLen)) % termLen
if len(header.Dpor.Proposers) != int(termLen) {
return common.Address{}, errors.New("invalid header.Dpor.Proposers list, length is not" + strconv.Itoa(int(termLen)))
}
return header.Dpor.Proposers[proposerIndex], nil
}
// ContractBackend is the contract client operation interface
type ContractBackend interface {
bind.ContractBackend
}
// ContractCaller is used to call the contract with given key and client.
// TODO: remove this later
type ContractCaller struct {
Key *keystore.Key
Client ClientBackend
GasLimit uint64
}
// NewContractCaller returns a ContractCaller.
func NewContractCaller(key *keystore.Key, client ClientBackend, gasLimit uint64) (*ContractCaller, error) {
return &ContractCaller{
Key: key,
Client: client,
GasLimit: gasLimit,
}, nil
}
// VerifyBlockFn verifies basic fields of a block
type VerifyBlockFn func(block *types.Block) error
// SignFn is a signer callback function to request a hash to be signed by a
// backing account.
type SignFn func(accounts.Account, []byte) ([]byte, error)
// HandleGeneratedImpeachBlock handles generated impeach block
type HandleGeneratedImpeachBlock func(block *types.Block) error
// ConsensusStateMachine is a state machine used for consensus protocol for validators msg processing
type ConsensusStateMachine interface {
Status() DSMStatus
Faulty() uint64
FSM(input *BlockOrHeader, msgCode MsgCode) ([]*BlockOrHeader, Action, MsgCode, error)
}
// DporService provides functions used by dpor handler
type DporService interface {
// Coinbase returns current coinbase
Coinbase() common.Address
// TermLength returns term length
TermLength() uint64
// Faulty returns the number of faulty nodes
Faulty() uint64
// ViewLength returns view length
ViewLength() uint64
// ValidatorsNum returns number of validators
ValidatorsNum() uint64
// Period returns period of block generation
Period() time.Duration
// BlockDelay returns max delay of preprepare block propagation
BlockDelay() time.Duration
// TermOf returns the term number of given block number
TermOf(number uint64) uint64
// FutureTermOf returns the future term number of given block number
FutureTermOf(number uint64) uint64
// VerifyProposerOf verifies if an address is a proposer of given term
VerifyProposerOf(signer common.Address, term uint64) (bool, error)
// VerifyValidatorOf verifies if an address is a validator of given term
VerifyValidatorOf(signer common.Address, term uint64) (bool, error)
// ValidatorsOf returns the list of validators in committee for the specified block number
ValidatorsOf(number uint64) ([]common.Address, error)
// ProposersOf returns the list of proposers in committee for the specified block number
ProposersOf(number uint64) ([]common.Address, error)
// ProposerOf returns the proposer of the specified block number by rpt and election calculation
ProposerOf(number uint64) (common.Address, error)
// ValidatorsOfTerm returns the list of validators in committee for the specified term
ValidatorsOfTerm(term uint64) ([]common.Address, error)
// ProposersOfTerm returns the list of proposers in committee for the specified term
ProposersOfTerm(term uint64) ([]common.Address, error)
// VerifyHeaderWithState verifies the given header
// if in preprepared state, verify basic fields
// if in prepared state, verify if enough prepare sigs
// if in committed state, verify if enough commit sigs
VerifyHeaderWithState(header *types.Header, state consensus.State) error
// ValidateBlock verifies a block
ValidateBlock(block *types.Block, verifySigs bool, verifyProposers bool) error
// SignHeader signs the block if not signed it yet
SignHeader(header *types.Header, state consensus.State) error
// BroadcastBlock broadcasts a block to normal peers(not pbft replicas)
BroadcastBlock(block *types.Block, prop bool)
// InsertChain inserts a block to chain
InsertChain(block *types.Block) error
// Status returns a pbft replica's status
Status() *consensus.PbftStatus
// StatusUpdate updates status of dpor
StatusUpdate() error
// CreateImpeachBlock returns an impeachment block
CreateImpeachBlock() (*types.Block, error)
// CreateFailbackImpeachBlocks creates impeachment blocks with failback timestamps
CreateFailbackImpeachBlocks() (firstImpeachment *types.Block, secondImpeachment *types.Block, err error)
// GetCurrentBlock returns current block
GetCurrentBlock() *types.Block
// HasBlockInChain returns if a block is in local chain
HasBlockInChain(hash common.Hash, number uint64) bool
// GetBlockFromChain returns a block from local chain with given hash and number
GetBlockFromChain(hash common.Hash, number uint64) *types.Block
// ImpeachTimeout returns the timeout for impeachment
ImpeachTimeout() time.Duration
// ECRecoverProposer recovers proposer's address from a seal of a header
ECRecoverProposer(header *types.Header) (common.Address, error)
// ECRecoverSigs recovers signer address and corresponding signature, it ignores empty signature and return empty
// addresses if one of the sigs are illegal
ECRecoverSigs(header *types.Header, state consensus.State) ([]common.Address, []types.DporSignature, error)
// Update the signature to prepare signature cache(two kinds of sigs, one for prepared, another for final)
UpdatePrepareSigsCache(validator common.Address, hash common.Hash, sig types.DporSignature)
// Update the signature to final signature cache(two kinds of sigs, one for prepared, another for final)
UpdateFinalSigsCache(validator common.Address, hash common.Hash, sig types.DporSignature)
// GetMac signs a Mac
GetMac() (string, []byte, error)
// SyncFrom tries to sync block from given peer
SyncFrom(p *p2p.Peer)
// Synchronize tries to sync block from best peer
Synchronize()
}
// HandlerMode indicates the run mode of handler
type HandlerMode int
// Those are handler mode
const (
LBFTMode HandlerMode = iota
LBFT2Mode
)
// ValidMacSig recovers an address from a signed mac
func ValidMacSig(mac string, sig []byte) (valid bool, signer common.Address, err error) {
log.Debug("received mac", "mac", mac)
// check if mac prefix is valid
split := "|"
s := strings.Split(mac, split)
if len(s) != 2 {
log.Warn("wrong mac format", "mac", mac)
return
}
prefix, timeString := s[0], s[1]
if prefix != "cpchain" {
log.Warn("wrong mac prefix", "prefix", prefix)
return
}
// check if remote time is valid
remoteTime, err := time.Parse(time.RFC3339, timeString)
if err != nil {
log.Warn("err when parsing time string", "time", timeString)
return
}
timeGap := time.Now().Sub(remoteTime)
log.Debug("remote time", "time", remoteTime.Format(time.RFC3339))
log.Debug("local time", "time", time.Now().Format(time.RFC3339))
log.Debug("time gap", "seconds", timeGap.Seconds())
if timeGap > defaultTimeGapAllowed {
return
}
var hash common.Hash
hasher := sha3.NewKeccak256()
hasher.Write([]byte(mac))
hasher.Sum(hash[:0])
// recover address
pubkey, err := crypto.Ecrecover(hash.Bytes(), sig)
if err != nil {
return
}
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
// check passed
valid = true
return
}
// IsCheckPoint returns if a given block number is in a checkpoint with given
// termLen and viewLen
func IsCheckPoint(number uint64, termLen uint64, viewLen uint64) bool {
if number == 0 {
return false
}
if termLen == 0 || viewLen == 0 {
return true
}
return number%(termLen*viewLen) == 0
}
// TermOf returns the term index of given block number
func TermOf(blockNum uint64) uint64 {
if blockNum == 0 {
return 0 // block number 0 is a special case, its term is set to 0
}
termLen := configs.ChainConfigInfo().Dpor.TermLen
viewLen := configs.ChainConfigInfo().Dpor.ViewLen
return (blockNum - 1) / (termLen * viewLen)
}