forked from Onyx-Protocol/Onyx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
generator.go
103 lines (91 loc) · 2.19 KB
/
generator.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
// Package generator implements the Chain Core generator.
//
// A Chain Core configured as a generator produces new blocks
// on an interval.
package generator
import (
"context"
"sync"
"time"
"chain/database/pg"
"chain/log"
"chain/protocol"
"chain/protocol/bc"
"chain/protocol/bc/legacy"
)
// A BlockSigner signs blocks.
type BlockSigner interface {
// SignBlock returns an ed25519 signature over the block's sighash.
// See also the Chain Protocol spec for the complete required behavior
// of a block signer.
SignBlock(ctx context.Context, marshalledBlock []byte) (signature []byte, err error)
}
// Generator collects pending transactions and produces new blocks on
// an interval.
type Generator struct {
// config
db pg.DB
chain *protocol.Chain
signers []BlockSigner
mu sync.Mutex
pool []*legacy.Tx // in topological order
poolHashes map[bc.Hash]bool
}
// New creates and initializes a new Generator.
func New(
c *protocol.Chain,
s []BlockSigner,
db pg.DB,
) *Generator {
return &Generator{
db: db,
chain: c,
signers: s,
poolHashes: make(map[bc.Hash]bool),
}
}
// PendingTxs returns all of the pendings txs that will be
// included in the generator's next block.
func (g *Generator) PendingTxs() []*legacy.Tx {
g.mu.Lock()
defer g.mu.Unlock()
txs := make([]*legacy.Tx, len(g.pool))
copy(txs, g.pool)
return txs
}
// Submit adds a new pending tx to the pending tx pool.
func (g *Generator) Submit(ctx context.Context, tx *legacy.Tx) error {
g.mu.Lock()
defer g.mu.Unlock()
if g.poolHashes[tx.ID] {
return nil
}
g.poolHashes[tx.ID] = true
g.pool = append(g.pool, tx)
return nil
}
// Generate runs in a loop, making one new block
// every block period. It returns when its context
// is canceled.
// After each attempt to make a block, it calls health
// to report either an error or nil to indicate success.
func (g *Generator) Generate(
ctx context.Context,
period time.Duration,
health func(error),
) {
ticks := time.Tick(period)
for {
select {
case <-ctx.Done():
log.Printf(ctx, "Deposed, Generate exiting")
return
case <-ticks:
err := g.makeBlock(ctx)
health(err)
if err != nil {
log.Error(ctx, err)
}
}
}
}