forked from dedis/cothority
/
server.go
124 lines (111 loc) · 3.91 KB
/
server.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
package byzcoin
import (
"sync"
"github.com/dedis/cothority/log"
"github.com/dedis/cothority/protocols/byzcoin/blockchain/blkparser"
"github.com/dedis/cothority/sda"
)
// BlockServer is a struct where Client can connect and that instantiate ByzCoin
// protocols when needed.
type BlockServer interface {
AddTransaction(blkparser.Tx)
Instantiate(n *sda.TreeNodeInstance) (sda.ProtocolInstance, error)
}
// Server is the long-term control service that listens for transactions and
// dispatch them to a new ByzCoin for each new signing that we want to do.
// It creates the ByzCoin protocols and run them. only used by the root since
// only the root participates to the creation of the block.
type Server struct {
// transactions pool where all the incoming transactions are stored
transactions []blkparser.Tx
// lock associated
transactionLock sync.Mutex
// how many transactions should we give to an instance
blockSize int
timeOutMs uint64
fail uint
// blockSignatureChan is the channel used to pass out the signatures that
// ByzCoin's instances have made
blockSignatureChan chan BlockSignature
// enoughBlock signals the server we have enough
// no comments..
transactionChan chan blkparser.Tx
requestChan chan bool
responseChan chan []blkparser.Tx
}
// NewByzCoinServer returns a new fresh ByzCoinServer. It must be given the blockSize in order
// to efficiently give the transactions to the ByzCoin instances.
func NewByzCoinServer(blockSize int, timeOutMs uint64, fail uint) *Server {
s := &Server{
blockSize: blockSize,
timeOutMs: timeOutMs,
fail: fail,
blockSignatureChan: make(chan BlockSignature),
transactionChan: make(chan blkparser.Tx),
requestChan: make(chan bool),
responseChan: make(chan []blkparser.Tx),
}
go s.listenEnoughBlocks()
return s
}
// AddTransaction add a new transactions to the list of transactions to commit
func (s *Server) AddTransaction(tr blkparser.Tx) {
s.transactionChan <- tr
}
// ListenClientTransactions will bind to a port a listen for incoming connection
// from clients. These client will be able to pass the transactions to the
// server.
func (s *Server) ListenClientTransactions() {
panic("not implemented yet")
}
// Instantiate takes blockSize transactions and create the byzcoin instances.
func (s *Server) Instantiate(node *sda.TreeNodeInstance) (sda.ProtocolInstance, error) {
// wait until we have enough blocks
currTransactions := s.WaitEnoughBlocks()
log.Lvl2("Instantiate ByzCoin Round with", len(currTransactions), "transactions")
pi, err := NewByzCoinRootProtocol(node, currTransactions, s.timeOutMs, s.fail)
return pi, err
}
// BlockSignaturesChan returns a channel that is given each new block signature as
// soon as they are arrive (Wether correct or not).
func (s *Server) BlockSignaturesChan() <-chan BlockSignature {
return s.blockSignatureChan
}
func (s *Server) onDoneSign(blk BlockSignature) {
s.blockSignatureChan <- blk
}
// WaitEnoughBlocks is called to wait on the server until it has enough
// transactions to make a block
func (s *Server) WaitEnoughBlocks() []blkparser.Tx {
s.requestChan <- true
transactions := <-s.responseChan
return transactions
}
func (s *Server) listenEnoughBlocks() {
// TODO the server should have a transaction pool instead:
var transactions []blkparser.Tx
var want bool
for {
select {
case tr := <-s.transactionChan:
// FIXME this will lead to a very large slice if the client sends many
if len(transactions) < s.blockSize {
transactions = append(transactions, tr)
}
if want {
if len(transactions) >= s.blockSize {
s.responseChan <- transactions[:s.blockSize]
transactions = transactions[s.blockSize:]
want = false
}
}
case <-s.requestChan:
want = true
if len(transactions) >= s.blockSize {
s.responseChan <- transactions[:s.blockSize]
transactions = transactions[s.blockSize:]
want = false
}
}
}
}