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

import (
Caro Axel's avatar
Caro Axel committed
5 6
	"bytes"
	"errors"
7
	"fmt"
Caro Axel's avatar
Caro 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"
Caro Axel's avatar
Caro Axel committed
15 16
	tAPI "dfss/dfsst/api"
	"dfss/net"
17
	"github.com/spf13/viper"
Caro Axel's avatar
Caro Axel committed
18 19
	"golang.org/x/net/context"
	"google.golang.org/grpc"
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()
	}()

Caro Axel's avatar
Caro 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
	round := 0
53
	for m.currentIndex >= 0 {
54 55
		round = round + 1
		stopIfNeeded(round)
Loïck Bonniot's avatar
Loïck Bonniot committed
56
		m.OnProgressUpdate(m.currentIndex, seqLen+1)
Loïck Bonniot's avatar
Loïck Bonniot committed
57
		time.Sleep(viper.GetDuration("slowdown"))
58
		dAPI.DLog("starting round at index [" + fmt.Sprintf("%d", m.currentIndex) + "] with nextIndex=" + fmt.Sprintf("%d", nextIndex))
Richer Maximilien's avatar
Richer Maximilien committed
59

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

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

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

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

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

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

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

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

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

137
		case <-time.After(net.DefaultTimeout):
Caro Axel's avatar
Caro Axel committed
138
			return m.resolve()
139
		}
140 141
	}

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

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

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

	return nil
164
}
165

166 167 168 169 170 171
// 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()
172 173
		delete(m.peers, k)
	}
174
	m.cServer.Stop()
175
}
Caro Axel's avatar
Caro Axel committed
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

// 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 {
240
		dAPI.DLog("unable to contact TTP")
Caro Axel's avatar
Caro Axel committed
241 242 243
		return errors.New("No connection to TTP, aborting!")
	}

244
	dAPI.DLog("contacting TTP")
Caro Axel's avatar
Caro Axel committed
245 246 247 248 249
	response, err := m.callForResolve()
	if err != nil {
		return err
	}
	if response.Abort {
250
		dAPI.DLog("contacted TTP, received abort token")
Caro Axel's avatar
Caro Axel committed
251 252
		return nil
	}
253
	dAPI.DLog("contacted TTP, received signed contract")
Caro Axel's avatar
Caro Axel committed
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
	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
}
285

Loïck Bonniot's avatar
Loïck Bonniot committed
286 287 288 289 290 291
func stopIfNeeded(index int) {
	s := viper.GetInt("stopbefore")
	if s == 0 {
		return
	}

292
	if index == -1 && s == -1 || index == s {
Loïck Bonniot's avatar
Loïck Bonniot committed
293 294
		os.Exit(0)
	}
295
}