protocol.go 7.87 KB
Newer Older
1
// Package sign handles contract and signature operations.
2
3
4
package sign

import (
Axel's avatar
Axel committed
5
6
	"bytes"
	"errors"
7
	"fmt"
Axel's avatar
Axel committed
8
	"io/ioutil"
Loïck Bonniot's avatar
Loïck Bonniot committed
9
	"os"
10
	"time"
11
12
13

	cAPI "dfss/dfssc/api"
	"dfss/dfssc/common"
Richer Maximilien's avatar
Richer Maximilien committed
14
	dAPI "dfss/dfssd/api"
Axel's avatar
Axel committed
15
16
17
18
	tAPI "dfss/dfsst/api"
	"dfss/net"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
Loïck Bonniot's avatar
Loïck Bonniot committed
19
	"github.com/spf13/viper"
20
21
)

22
// Sign performs all the message exchanges for the contract to be signed
23
24
25
26
27
28
//
// * Initialize the SignatureManager from starter.go
// * Compute the reversed map [mail -> ID] of signers
// * Make channels for handlers
// * Promises rounds
// * Signature round
29
func (m *SignatureManager) Sign() error {
30
31
32
33
34
35

	defer func() {
		m.finished = true
		m.closeConnections()
	}()

Axel's avatar
Axel committed
36
	nextIndex, err := m.Initialize()
37
38
39
40
	if err != nil {
		return err
	}

41
	m.makeSignersHashToIDMap()
42
43
	m.cServerIface.incomingPromises = make(chan interface{}, chanBufferSize)
	m.cServerIface.incomingSignatures = make(chan interface{}, chanBufferSize)
44

45
46
	// Cooldown delay, let other clients wake-up their channels
	time.Sleep(time.Second)
47

Loïck Bonniot's avatar
Loïck Bonniot committed
48
49
	seqLen := len(m.sequence)

50
	// Promess rounds
51
	// Follow the sequence until there is no next occurence of me
52
	for m.currentIndex >= 0 {
Loïck Bonniot's avatar
Loïck Bonniot committed
53
		stopIfNeeded(m.currentIndex)
Loïck Bonniot's avatar
Loïck Bonniot committed
54
		m.OnProgressUpdate(m.currentIndex, seqLen+1)
Loïck Bonniot's avatar
Loïck Bonniot committed
55
		time.Sleep(viper.GetDuration("slowdown"))
56
		dAPI.DLog("starting round at index [" + fmt.Sprintf("%d", m.currentIndex) + "] with nextIndex=" + fmt.Sprintf("%d", nextIndex))
Richer Maximilien's avatar
Richer Maximilien committed
57

Loïck Bonniot's avatar
Loïck Bonniot committed
58
		// Set of promises we are waiting for
Axel's avatar
Axel committed
59
60
		var pendingSet []common.SequenceCoordinate
		pendingSet, err = common.GetPendingSet(m.sequence, m.myID, m.currentIndex)
61
62
		if err != nil {
			return err
63
64
		}

65
		// Set of the promises we must send
Axel's avatar
Axel committed
66
67
		var sendSet []common.SequenceCoordinate
		sendSet, err = common.GetSendSet(m.sequence, m.myID, m.currentIndex)
68
69
		if err != nil {
			return err
70
71
		}

72
		// Exchange messages
Axel's avatar
Axel committed
73
74
75
76
		err = m.promiseRound(pendingSet, sendSet)
		if err != nil {
			return err
		}
77

78
		m.currentIndex = nextIndex
Axel's avatar
Axel committed
79
		nextIndex, err = common.FindNextIndex(m.sequence, m.myID, m.currentIndex)
80
81
		if err != nil {
			return err
82
83
84
		}
	}

Loïck Bonniot's avatar
Loïck Bonniot committed
85
86
	// Signature round
	stopIfNeeded(-1)
Loïck Bonniot's avatar
Loïck Bonniot committed
87
	m.OnProgressUpdate(seqLen, seqLen+1)
88
	dAPI.DLog("entering signature round")
89
	err = m.ExchangeAllSignatures()
90
91
92
	if err != nil {
		return err
	}
Loïck Bonniot's avatar
Loïck Bonniot committed
93

94
	dAPI.DLog("exiting signature round")
Loïck Bonniot's avatar
Loïck Bonniot committed
95
	m.OnProgressUpdate(seqLen+1, seqLen+1)
Axel's avatar
Axel committed
96
	return m.PersistSignaturesToFile()
97
98
99
}

// GetClient retrieves the Client to the specified sequence id provided it exists
100
101
102
103
func (m *SignatureManager) GetClient(to uint32) (client *cAPI.ClientClient, mail string) {
	mail = m.contract.Signers[to].Email
	client = m.peers[mail]
	return
104
105
}

106
// makeSignersHashToIDMap builds an association to reverse a hash to the sequence ID
107
108
func (m *SignatureManager) makeSignersHashToIDMap() {
	m.hashToID = make(map[string]uint32)
109
110
	signers := m.contract.Signers
	for id, signer := range signers {
111
		m.hashToID[signer.Hash] = uint32(id)
112
113
114
	}
}

115
// promiseRound describes a promise round: reception and sending
Axel's avatar
Axel committed
116
func (m *SignatureManager) promiseRound(pendingSet, sendSet []common.SequenceCoordinate) error {
117
	// Reception of the due promises
Axel's avatar
Axel committed
118
	var promises []*cAPI.Promise
119
	for len(pendingSet) > 0 {
120
121
122
		select {
		case promiseIface := <-m.cServerIface.incomingPromises:
			promise := (promiseIface).(*cAPI.Promise)
Axel's avatar
Axel committed
123
124
			valid, senderID := m.checkPromise(pendingSet, promise)
			if valid {
125
				var err error
Axel's avatar
Axel committed
126
				pendingSet, err = common.RemoveCoordinate(pendingSet, senderID)
127
128
129
				if err != nil {
					continue
				}
Axel's avatar
Axel committed
130
131
132
				promises = append(promises, promise)
			} else {
				return m.resolve()
133
			}
134
135

		case <-time.After(time.Minute):
Axel's avatar
Axel committed
136
			return m.resolve()
137
		}
138
139
	}

Axel's avatar
Axel committed
140
141
142
143
	// Now that we received everything, we update the evidence we will give to the ttp
	m.updateReceivedPromises(promises)
	m.lastValidIndex = m.currentIndex

144
	c := make(chan *cAPI.Promise, chanBufferSize)
145
	// Sending of due promises
Axel's avatar
Axel committed
146
147
148
	for _, coord := range sendSet {
		go func(coord common.SequenceCoordinate, m *SignatureManager) {
			promise, err := m.CreatePromise(m.myID, coord.Signer, uint32(m.currentIndex))
149
			if err == nil {
Axel's avatar
Axel committed
150
				_ = m.SendEvidence(promise, nil, coord.Signer)
151
152
			}
			c <- promise
Axel's avatar
Axel committed
153
		}(coord, m)
154
155
	}

156
	// Verifying we sent all the due promises
157
	for range sendSet {
Loïck Bonniot's avatar
Loïck Bonniot committed
158
		<-c
159
	}
Axel's avatar
Axel committed
160
161

	return nil
162
}
163

164
165
166
167
168
169
// closeConnections tries to close all established connection with other peers and platform.
// It also stops the local server.
func (m *SignatureManager) closeConnections() {
	_ = m.platformConn.Close()
	for k, peer := range m.peersConn {
		_ = peer.Close()
170
171
		delete(m.peers, k)
	}
172
	m.cServer.Stop()
173
}
Axel's avatar
Axel committed
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

// updateReceivedPromises : updates the RecievedPromises field of the SignatureManager with the provided promises:
// if we don't yet have a promise from this signer, we add it to the array.
// otherwise we replace the one we have by the provided promise.
func (m *SignatureManager) updateReceivedPromises(promises []*cAPI.Promise) {
	for _, p := range promises {
		present, index := m.containsPromiseFrom(p.Context.SenderKeyHash, p.Index)

		if present {
			// it's present, so there is no index error
			_ = m.removeReceivedPromise(index)
		}
		m.archives.receivedPromises = append(m.archives.receivedPromises, p)
	}
}

// containsPromiseFrom : determines if the SignatureManager has already archived a promise from the specified signer, previous to the specified index.
func (m *SignatureManager) containsPromiseFrom(signer []byte, index uint32) (bool, int) {
	for i, p := range m.archives.receivedPromises {
		if bytes.Equal(p.Context.SenderKeyHash, signer) {
			return p.Index < index, i
		}
	}
	return false, 0
}

// removeReceivedPromise : removes the promise at the specified index from the archived received promises.
// If the index is invalid, return an error.
// If the promise is not there, does nothing.
func (m *SignatureManager) removeReceivedPromise(index int) error {
	promises := m.archives.receivedPromises
	if index < 0 || index >= len(promises) {
		return errors.New("Index out of range")
	}

	m.archives.receivedPromises = append(promises[:index], promises[index+1:]...)

	return nil
}

// callForResolve : calls the ttp for resolution.
func (m *SignatureManager) callForResolve() (*tAPI.TTPResponse, error) {
	selfPromise, err := m.CreatePromise(m.myID, m.myID, uint32(m.lastValidIndex))
	if err != nil {
		return nil, err
	}

	toSend := append(m.archives.receivedPromises, selfPromise)

	request := &tAPI.AlertRequest{Promises: toSend}

	ctx, cancel := context.WithTimeout(context.Background(), net.DefaultTimeout)
	defer cancel()
	response, err := m.ttp.Alert(ctx, request)
	if err != nil {
		return nil, errors.New(grpc.ErrorDesc(err))
	}

	return response, nil
}

// resolve : calls for the resolution, and persists the contract if obtained.
func (m *SignatureManager) resolve() error {
	if m.ttp == nil {
		return errors.New("No connection to TTP, aborting!")
	}

	response, err := m.callForResolve()
	if err != nil {
		return err
	}
	if response.Abort {
		return nil
	}
	return ioutil.WriteFile(m.mail+"-"+m.contract.UUID+".proof", response.Contract, 0600)
}

// checkPromise : verifies that the promise is valid wrt the expected promises.
// We assume that the promise data is consistent wrt the platform seal.
func (m *SignatureManager) checkPromise(expected []common.SequenceCoordinate, promise *cAPI.Promise) (bool, uint32) {
	// the promise is consistent, but not for the expected signature
	// this should not happen
	if promise.Context.SignatureUUID != m.uuid {
		return false, 0
	}

	// the promise is not for us
	recipientID, exist := m.hashToID[fmt.Sprintf("%x", promise.Context.RecipientKeyHash)]
	if !exist || recipientID != m.myID {
		return false, 0
	}

	// we didn't expect a promise from this client
	senderID, exist := m.hashToID[fmt.Sprintf("%x", promise.Context.SenderKeyHash)]
	if !exist {
		return false, 0
	}
	for _, c := range expected {
		if c.Signer == senderID && c.Index == promise.Index {
			return true, senderID
		}
	}

	return false, 0
}
279

Loïck Bonniot's avatar
Loïck Bonniot committed
280
281
282
283
284
285
286
287
288
func stopIfNeeded(index int) {
	s := viper.GetInt("stopbefore")
	if s == 0 {
		return
	}

	if index == -1 && s == -1 || index+1 == s {
		os.Exit(0)
	}
289
}