Commit 8ade9e29 authored by Caro Axel's avatar Caro Axel Committed by Loïck Bonniot

[c][t] Add TTP calls from client

parent 1d07c0d8
Pipeline #1826 failed with stage
......@@ -78,13 +78,6 @@ var signCmd = &cobra.Command{
os.Exit(1)
}
// Persist evidencies, if any
err = manager.PersistSignaturesToFile()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println("Signature complete! See .proof file for evidences.")
},
}
......
......@@ -4,6 +4,11 @@ import (
"errors"
)
// SequenceCoordinate : contains both the sequence id of a signer, and the index of the occurence in the sequence
type SequenceCoordinate struct {
Signer, Index uint32
}
// FindNextIndex analyses the specified sequence and tries to find the next occurence of id after the specified index (excluded)
// Therefore to find the first occurence of the id in the sequence, use -1 as the index
//
......@@ -30,8 +35,8 @@ func FindNextIndex(s []uint32, id uint32, index int) (int, error) {
//
// If the index is not the one of the specified id in the sequence, the result still holds, but may be incomplete for your needs
// If the id is not valid for the specified sequence, the result will be the set of ids of the sequence
func GetPendingSet(s []uint32, id uint32, index int) ([]uint32, error) {
res := []uint32{}
func GetPendingSet(s []uint32, id uint32, index int) ([]SequenceCoordinate, error) {
res := []SequenceCoordinate{}
if index >= len(s) || index < 0 {
return res, errors.New("Index out of range")
......@@ -44,8 +49,8 @@ func GetPendingSet(s []uint32, id uint32, index int) ([]uint32, error) {
for curIndex > -1 && s[curIndex] != id {
curID := s[curIndex]
if !contains(res, curID) {
res = append(res, curID)
if !containsCoordinate(res, curID) {
res = append(res, SequenceCoordinate{Signer: curID, Index: uint32(curIndex)})
}
curIndex--
}
......@@ -59,8 +64,8 @@ func GetPendingSet(s []uint32, id uint32, index int) ([]uint32, error) {
//
// If the index is not the one of the specified id in the sequence, the result still holds, but may be incomplete for your needs
// If the id is not valid for the specified sequence, the result will be the set of ids of the sequence
func GetSendSet(s []uint32, id uint32, index int) ([]uint32, error) {
res := []uint32{}
func GetSendSet(s []uint32, id uint32, index int) ([]SequenceCoordinate, error) {
res := []SequenceCoordinate{}
if index >= len(s) || index < 0 {
return res, errors.New("Index out of range")
......@@ -73,8 +78,8 @@ func GetSendSet(s []uint32, id uint32, index int) ([]uint32, error) {
for curIndex < len(s) && s[curIndex] != id {
curID := s[curIndex]
if !contains(res, curID) {
res = append(res, curID)
if !containsCoordinate(res, curID) {
res = append(res, SequenceCoordinate{Signer: curID, Index: uint32(curIndex)})
}
curIndex++
}
......@@ -82,7 +87,17 @@ func GetSendSet(s []uint32, id uint32, index int) ([]uint32, error) {
return res, nil
}
// contains determines if s contains e
// containsCoordinate: determines if s contains the specified signer sequence id e
func containsCoordinate(s []SequenceCoordinate, e uint32) bool {
for _, a := range s {
if a.Signer == e {
return true
}
}
return false
}
// contains : determines if s contains e
func contains(s []uint32, e uint32) bool {
for _, a := range s {
if a == e {
......@@ -108,7 +123,17 @@ func GetAllButOne(s []uint32, e uint32) []uint32 {
return res
}
// Remove an ID from the sequence
// RemoveCoordinate : removes the first occurence of an ID from the coordinates array
func RemoveCoordinate(s []SequenceCoordinate, e uint32) ([]SequenceCoordinate, error) {
for i, a := range s {
if a.Signer == e {
return append(s[:i], s[i+1:]...), nil
}
}
return s, errors.New("ID not in sequence")
}
// Remove the first occurence of an ID from the ids array
func Remove(s []uint32, e uint32) ([]uint32, error) {
for i, a := range s {
if a == e {
......
......@@ -50,19 +50,24 @@ func TestGetPendingSet(t *testing.T) {
res, err = GetPendingSet(s, id, 1)
assert.Equal(t, len(res), 1)
assert.Equal(t, res[0], uint32(0))
assert.Equal(t, res[0].Signer, uint32(0))
assert.Equal(t, res[0].Index, uint32(0))
assert.Equal(t, err, nil)
res, err = GetPendingSet(s, id, 2)
assert.Equal(t, len(res), 2)
assert.Equal(t, res[0], uint32(1))
assert.Equal(t, res[1], uint32(0))
assert.Equal(t, res[0].Signer, uint32(1))
assert.Equal(t, res[0].Index, uint32(1))
assert.Equal(t, res[1].Signer, uint32(0))
assert.Equal(t, res[1].Index, uint32(0))
assert.Equal(t, err, nil)
res, err = GetPendingSet(s, id, 5)
assert.Equal(t, len(res), 2)
assert.Equal(t, res[0], uint32(1))
assert.Equal(t, res[1], uint32(0))
assert.Equal(t, res[0].Signer, uint32(1))
assert.Equal(t, res[0].Index, uint32(4))
assert.Equal(t, res[1].Signer, uint32(0))
assert.Equal(t, res[1].Index, uint32(3))
assert.Equal(t, err, nil)
res, err = GetPendingSet(s, id, -1)
......@@ -76,11 +81,23 @@ func TestGetPendingSet(t *testing.T) {
s = []uint32{0, 1, 0, 2}
res, err = GetPendingSet(s, id, 3)
assert.Equal(t, len(res), 2)
assert.Equal(t, res[0], uint32(0))
assert.Equal(t, res[1], uint32(1))
assert.Equal(t, res[0].Signer, uint32(0))
assert.Equal(t, res[0].Index, uint32(2))
assert.Equal(t, res[1].Signer, uint32(1))
assert.Equal(t, res[1].Index, uint32(1))
assert.Equal(t, err, nil)
}
func TestContainsCoordinate(t *testing.T) {
var s []SequenceCoordinate
s = append(s, SequenceCoordinate{Signer: 0, Index: 0})
s = append(s, SequenceCoordinate{Signer: 1, Index: 1})
assert.Equal(t, containsCoordinate(s, 0), true)
assert.Equal(t, containsCoordinate(s, 1), true)
assert.Equal(t, containsCoordinate(s, 2), false)
}
func TestContains(t *testing.T) {
s := []uint32{0, 1}
......@@ -95,19 +112,24 @@ func TestGetSendSet(t *testing.T) {
res, err := GetSendSet(s, id, 3)
assert.Equal(t, len(res), 2)
assert.Equal(t, res[0], uint32(1))
assert.Equal(t, res[1], uint32(2))
assert.Equal(t, res[0].Signer, uint32(1))
assert.Equal(t, res[0].Index, uint32(4))
assert.Equal(t, res[1].Signer, uint32(2))
assert.Equal(t, res[1].Index, uint32(5))
assert.Equal(t, err, nil)
res, err = GetSendSet(s, id, 0)
assert.Equal(t, len(res), 2)
assert.Equal(t, res[0], uint32(1))
assert.Equal(t, res[1], uint32(2))
assert.Equal(t, res[0].Signer, uint32(1))
assert.Equal(t, res[0].Index, uint32(1))
assert.Equal(t, res[1].Signer, uint32(2))
assert.Equal(t, res[1].Index, uint32(2))
assert.Equal(t, err, nil)
res, err = GetSendSet(s, 1, 4)
assert.Equal(t, len(res), 1)
assert.Equal(t, res[0], uint32(2))
assert.Equal(t, res[0].Signer, uint32(2))
assert.Equal(t, res[0].Index, uint32(5))
assert.Equal(t, err, nil)
res, err = GetSendSet(s, 2, 5)
......@@ -139,6 +161,46 @@ func TestGetAllButOne(t *testing.T) {
assert.Equal(t, res[2], uint32(2))
}
func TestRemoveCoordinate(t *testing.T) {
var s []SequenceCoordinate
s = append(s, SequenceCoordinate{Signer: 0, Index: 0})
s = append(s, SequenceCoordinate{Signer: 1, Index: 1})
assert.Equal(t, len(s), 2)
assert.Equal(t, containsCoordinate(s, 0), true)
assert.Equal(t, containsCoordinate(s, 1), true)
s2, err := RemoveCoordinate(s, 3)
assert.Equal(t, err.Error(), "ID not in sequence")
assert.Equal(t, len(s2), 2)
assert.Equal(t, containsCoordinate(s2, 0), true)
assert.Equal(t, containsCoordinate(s2, 1), true)
s3, err := RemoveCoordinate(s, 0)
assert.Equal(t, err, nil)
assert.Equal(t, len(s3), 1)
assert.Equal(t, containsCoordinate(s3, 0), false)
assert.Equal(t, containsCoordinate(s3, 1), true)
}
func TestRemove(t *testing.T) {
s := []uint32{0, 1}
assert.Equal(t, len(s), 2)
s2, err := Remove(s, 3)
assert.Equal(t, err.Error(), "ID not in sequence")
assert.Equal(t, len(s2), 2)
assert.Equal(t, contains(s2, 0), true)
assert.Equal(t, contains(s2, 1), true)
s3, err := Remove(s, 0)
assert.Equal(t, err, nil)
assert.Equal(t, len(s3), 1)
assert.Equal(t, contains(s3, 0), false)
assert.Equal(t, contains(s3, 1), true)
}
func ExampleFindNextIndex() {
s := []uint32{0, 1, 2, 0, 1, 2}
id := uint32(2)
......@@ -153,6 +215,6 @@ func ExampleFindNextIndex() {
fmt.Println("Send Set:", sSet)
// Output:
// First index: 2
// Pending Set: [1 0]
// Send Set: [0 1]
// Pending Set: [{1 1} {0 0}]
// Send Set: [{0 3} {1 4}]
}
package sign
import (
"encoding/hex"
"errors"
"time"
......@@ -14,20 +15,23 @@ func (m *SignatureManager) createContext(from, to uint32) (*cAPI.Context, error)
if int(from) >= len(m.keyHash) || int(to) >= len(m.keyHash) {
return nil, errors.New("Invalid id for context creation")
}
h, _ := hex.DecodeString(m.contract.File.Hash)
return &cAPI.Context{
RecipientKeyHash: m.keyHash[to],
SenderKeyHash: m.keyHash[from],
Sequence: m.sequence,
Signers: m.keyHash,
ContractDocumentHash: []byte(m.contract.File.Hash),
ContractDocumentHash: h,
SignatureUUID: m.uuid,
Seal: m.seal,
}, nil
}
// CreatePromise creates a promise from 'from' to 'to', in the context of the SignatureManager
// provided the specified sequence indexes are valid
func (m *SignatureManager) CreatePromise(from, to uint32) (*cAPI.Promise, error) {
// provided that the specified sequence indexes are valid
func (m *SignatureManager) CreatePromise(from, to, at uint32) (*cAPI.Promise, error) {
context, err := m.createContext(from, to)
if err != nil {
return nil, err
......@@ -38,7 +42,7 @@ func (m *SignatureManager) CreatePromise(from, to uint32) (*cAPI.Promise, error)
}
return &cAPI.Promise{
Index: uint32(m.currentIndex),
Index: at,
Context: context,
Payload: []byte{0x41},
}, nil
......@@ -69,7 +73,6 @@ func (m *SignatureManager) SendEvidence(promise *cAPI.Promise, signature *cAPI.S
m.archives.mutex.Lock()
if promise != nil {
dAPI.DLog("sent promise to " + mail)
m.archives.sentPromises = append(m.archives.sentPromises, promise)
} else {
dAPI.DLog("sent signature to " + mail)
m.archives.sentSignatures = append(m.archives.sentSignatures, signature)
......
......@@ -2,12 +2,19 @@
package sign
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"time"
cAPI "dfss/dfssc/api"
"dfss/dfssc/common"
dAPI "dfss/dfssd/api"
tAPI "dfss/dfsst/api"
"dfss/net"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
// Sign performs all the message exchanges for the contract to be signed
......@@ -24,7 +31,7 @@ func (m *SignatureManager) Sign() error {
m.closeConnections()
}()
myID, nextIndex, err := m.Initialize()
nextIndex, err := m.Initialize()
if err != nil {
return err
}
......@@ -45,24 +52,27 @@ func (m *SignatureManager) Sign() error {
dAPI.DLog("starting round at index [" + fmt.Sprintf("%d", m.currentIndex) + "] with nextIndex=" + fmt.Sprintf("%d", nextIndex))
// Set of promises we are waiting for
var pendingSet []uint32
pendingSet, err = common.GetPendingSet(m.sequence, myID, m.currentIndex)
var pendingSet []common.SequenceCoordinate
pendingSet, err = common.GetPendingSet(m.sequence, m.myID, m.currentIndex)
if err != nil {
return err
}
// Set of the promises we must send
var sendSet []uint32
sendSet, err = common.GetSendSet(m.sequence, myID, m.currentIndex)
var sendSet []common.SequenceCoordinate
sendSet, err = common.GetSendSet(m.sequence, m.myID, m.currentIndex)
if err != nil {
return err
}
// Exchange messages
m.promiseRound(pendingSet, sendSet, myID)
err = m.promiseRound(pendingSet, sendSet)
if err != nil {
return err
}
m.currentIndex = nextIndex
nextIndex, err = common.FindNextIndex(m.sequence, myID, m.currentIndex)
nextIndex, err = common.FindNextIndex(m.sequence, m.myID, m.currentIndex)
if err != nil {
return err
}
......@@ -78,7 +88,7 @@ func (m *SignatureManager) Sign() error {
dAPI.DLog("exiting signature round")
m.OnProgressUpdate(seqLen+1, seqLen+1)
return nil
return m.PersistSignaturesToFile()
}
// GetClient retrieves the Client to the specified sequence id provided it exists
......@@ -98,45 +108,52 @@ func (m *SignatureManager) makeSignersHashToIDMap() {
}
// promiseRound describes a promise round: reception and sending
func (m *SignatureManager) promiseRound(pendingSet, sendSet []uint32, myID uint32) {
func (m *SignatureManager) promiseRound(pendingSet, sendSet []common.SequenceCoordinate) error {
// Reception of the due promises
var promises []*cAPI.Promise
for len(pendingSet) > 0 {
select {
case promiseIface := <-m.cServerIface.incomingPromises:
promise := (promiseIface).(*cAPI.Promise)
senderID, exist := m.hashToID[fmt.Sprintf("%x", promise.Context.SenderKeyHash)]
if exist {
valid, senderID := m.checkPromise(pendingSet, promise)
if valid {
var err error
pendingSet, err = common.Remove(pendingSet, senderID)
pendingSet, err = common.RemoveCoordinate(pendingSet, senderID)
if err != nil {
continue
}
m.archives.receivedPromises = append(m.archives.receivedPromises, promise)
promises = append(promises, promise)
} else {
return m.resolve()
}
case <-time.After(time.Minute):
// TODO contact TTP
return
return m.resolve()
}
}
// Now that we received everything, we update the evidence we will give to the ttp
m.updateReceivedPromises(promises)
m.lastValidIndex = m.currentIndex
c := make(chan *cAPI.Promise, chanBufferSize)
// Sending of due promises
for _, id := range sendSet {
go func(id uint32, m *SignatureManager) {
promise, err := m.CreatePromise(myID, id)
for _, coord := range sendSet {
go func(coord common.SequenceCoordinate, m *SignatureManager) {
promise, err := m.CreatePromise(m.myID, coord.Signer, uint32(m.currentIndex))
if err == nil {
_ = m.SendEvidence(promise, nil, id)
_ = m.SendEvidence(promise, nil, coord.Signer)
}
c <- promise
}(id, m)
}(coord, m)
}
// Verifying we sent all the due promises
for range sendSet {
<-c
}
return nil
}
// closeConnections tries to close all established connection with other peers and platform.
......@@ -149,3 +166,108 @@ func (m *SignatureManager) closeConnections() {
}
m.cServer.Stop()
}
// 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
}
package sign
import (
"testing"
cAPI "dfss/dfssc/api"
"github.com/stretchr/testify/assert"
)
func TestContainsPromiseFrom(t *testing.T) {
sender := []byte{0, 0, 7}
var promises []*cAPI.Promise
promises = append(promises, &cAPI.Promise{
Context: &cAPI.Context{
SenderKeyHash: []byte{0, 0, 8},
},
Index: uint32(1),
})
promises = append(promises, &cAPI.Promise{
Context: &cAPI.Context{
SenderKeyHash: sender,
},
Index: uint32(1),
})
m := SignatureManager{
archives: &Archives{
receivedPromises: promises,
},
}
present, index := m.containsPromiseFrom([]byte{0, 0, 6}, 0)
assert.Equal(t, present, false)
assert.Equal(t, index, 0)
present, index = m.containsPromiseFrom([]byte{0, 0, 7}, 0)
assert.Equal(t, present, false)
assert.Equal(t, index, 1)
present, index = m.containsPromiseFrom([]byte{0, 0, 7}, 2)
assert.Equal(t, present, true)
assert.Equal(t, index, 1)
}
func TestRemoveReceivedPromise(t *testing.T) {
sender := []byte{0, 0, 7}
var promises []*cAPI.Promise
promises = append(promises, &cAPI.Promise{
Context: &cAPI.Context{
SenderKeyHash: []byte{0, 0, 8},
},
Index: uint32(1),
})
promises = append(promises, &cAPI.Promise{
Context: &cAPI.Context{
SenderKeyHash: sender,
},
Index: uint32(1),
})
m := SignatureManager{
archives: &Archives{
receivedPromises: promises,
},
}
assert.Equal(t, len(m.archives.receivedPromises), 2)
err := m.removeReceivedPromise(-1)
assert.Equal(t, err.Error(), "Index out of range")
err = m.removeReceivedPromise(2)
assert.Equal(t, err.Error(), "Index out of range")
err = m.removeReceivedPromise(1)
assert.Equal(t, err, nil)
assert.Equal(t, len(m.archives.receivedPromises), 1)
present, index := m.containsPromiseFrom(sender, 2)
assert.Equal(t, present, false)
assert.Equal(t, index, 0)
present, index = m.containsPromiseFrom([]byte{0, 0, 8}, 2)
assert.Equal(t, present, true)
assert.Equal(t, index, 0)
}
func TestUpdateRecievedPromises(t *testing.T) {
sender := []byte{0, 0, 7}
var promises []*cAPI.Promise
promises = append(promises, &cAPI.Promise{
Context: &cAPI.Context{
SenderKeyHash: []byte{0, 0, 8},
},
Index: uint32(1),
})
promises = append(promises, &cAPI.Promise{
Context: &cAPI.Context{
SenderKeyHash: sender,
},
Index: uint32(1),
})
m := SignatureManager{
archives: &Archives{
receivedPromises: promises,
},
}
newPromise0 := &cAPI.Promise{
Context: &cAPI.Context{
SenderKeyHash: []byte{0, 0, 0},
},
Index: uint32(2),
}
assert.Equal(t, len(m.archives.receivedPromises), 2)
m.updateReceivedPromises([]*cAPI.Promise{newPromise0})
assert.Equal(t, len(m.archives.receivedPromises), 3)
present, index := m.containsPromiseFrom([]byte{0, 0, 0}, 3)
assert.Equal(t, present, true)
assert.Equal(t, index, 2)
newPromise1 := &cAPI.Promise{
Context: &cAPI.Context{
SenderKeyHash: sender,
},
Index: uint32(2),
}
m.updateReceivedPromises([]*cAPI.Promise{newPromise1})
assert.Equal(t, len(m.archives.receivedPromises), 3)
present, index = m.containsPromiseFrom(sender, 3)
assert.Equal(t, present, true)
assert.Equal(t, index, 2)
}
......@@ -4,6 +4,7 @@ import (
"dfss"
cAPI "dfss/dfssc/api"
pAPI "dfss/dfssp/api"
"dfss/dfsst/entities"
"dfss/net"
"golang.org/x/net/context"
"google.golang.org/grpc"
......@@ -26,6 +27,12 @@ func getServerErrorCode(c chan interface{}, in interface{}) *pAPI.ErrorCode {
//
// Handle incoming TreatPromise messages
func (s *clientServer) TreatPromise(ctx context.Context, in *cAPI.Promise) (*pAPI.ErrorCode, error) {
// we check that the incoming promise is valid (ie. no data inconsistency)
// we do not check that we expected that promise
valid, _, _, _ := entities.IsRequestValid(ctx, []*cAPI.Promise{in})
if !valid {
return &pAPI.ErrorCode{Code: pAPI.ErrorCode_SUCCESS}, nil
}
return getServerErrorCode(s.incomingPromises, in), nil
}
......