Commit c0710b02 authored by Loïck Bonniot's avatar Loïck Bonniot

[p] Add "ReadySign" API

parent 3e6c0f5b
......@@ -37,6 +37,14 @@ func signContract(filename string) {
}
// Ignition
fmt.Println("Waiting for other signers to be ready...")
signatureUUID, err := manager.SendReadySign()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(5)
}
fmt.Println("Everybody is ready, starting the signature", signatureUUID)
// Signature
}
......@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"strconv"
"time"
"dfss"
cAPI "dfss/dfssc/api"
......@@ -125,3 +126,24 @@ func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) {
return true, nil
}
// SendReadySign sends the READY signal to the platform, and wait (potentially a long time) for START signal.
func (m *SignatureManager) SendReadySign() (signatureUUID string, err error) {
ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Minute)
defer cancel()
launch, err := m.platform.ReadySign(ctx, &api.ReadySignRequest{
ContractUuid: m.contract.UUID,
})
if err != nil {
return
}
errorCode := launch.GetErrorCode()
if errorCode.Code != api.ErrorCode_SUCCESS {
err = errors.New(errorCode.Code.String() + " " + errorCode.Message)
return
}
signatureUUID = launch.SignatureUuid
return
}
......@@ -210,7 +210,7 @@ func (*ReadySignRequest) Descriptor() ([]byte, []int) { return fileDescriptor0,
type LaunchSignature struct {
ErrorCode *ErrorCode `protobuf:"bytes,1,opt,name=errorCode" json:"errorCode,omitempty"`
SignatureUuid string `protobuf:"bytes,2,opt,name=signatureUuid" json:"signatureUuid,omitempty"`
KeyHash []string `protobuf:"bytes,3,rep,name=keyHash" json:"keyHash,omitempty"`
KeyHash [][]byte `protobuf:"bytes,3,rep,name=keyHash,proto3" json:"keyHash,omitempty"`
}
func (m *LaunchSignature) Reset() { *m = LaunchSignature{} }
......@@ -476,39 +476,39 @@ var fileDescriptor0 = []byte{
0x14, 0x6d, 0xda, 0xec, 0xa3, 0x77, 0x6b, 0x17, 0xb9, 0x03, 0x95, 0x4a, 0x43, 0x93, 0x85, 0x04,
0x42, 0xa8, 0x9d, 0x8a, 0x84, 0x04, 0x6f, 0x59, 0xa9, 0xb6, 0x21, 0x54, 0x4d, 0xee, 0x0a, 0x12,
0x6f, 0x21, 0xf1, 0xd6, 0x68, 0xcd, 0x07, 0xb6, 0x23, 0xb4, 0x27, 0xf8, 0x25, 0xfc, 0x2d, 0x7e,
0x0e, 0xd8, 0x4e, 0x9c, 0x25, 0xa5, 0x42, 0xda, 0x1e, 0x32, 0x9f, 0xeb, 0xeb, 0x73, 0x7c, 0xcf,
0x0e, 0xd8, 0x4e, 0x9c, 0x26, 0xa3, 0x42, 0xda, 0x1e, 0x32, 0xdf, 0xeb, 0xeb, 0x73, 0x7c, 0xce,
0xbd, 0x2e, 0x1c, 0x05, 0xd7, 0x9c, 0x8f, 0xd4, 0x27, 0x1d, 0x79, 0x69, 0x38, 0x4a, 0x57, 0x9e,
0xb8, 0x4e, 0x58, 0x34, 0x4c, 0x59, 0x22, 0x12, 0xd4, 0x92, 0x31, 0xec, 0xc2, 0x01, 0xa1, 0x37,
0xb8, 0x4e, 0x58, 0x34, 0x4c, 0x59, 0x22, 0x12, 0xd4, 0x92, 0x39, 0xec, 0xc2, 0x01, 0xa1, 0x37,
0x21, 0x17, 0x94, 0x11, 0xfa, 0x2d, 0xa3, 0x5c, 0xa0, 0x43, 0xd8, 0xa2, 0x91, 0x17, 0xae, 0xfa,
0xd6, 0xb1, 0xf5, 0xa2, 0x4d, 0x72, 0x80, 0xfa, 0xb0, 0xc3, 0xf2, 0x84, 0x7e, 0x53, 0xc7, 0x0d,
0xc4, 0xbf, 0x2c, 0x68, 0x4f, 0x19, 0x4b, 0xd8, 0x24, 0x09, 0x28, 0x7a, 0x0e, 0xb6, 0x2f, 0xff,
0xeb, 0xc3, 0xdd, 0x71, 0x6f, 0x28, 0x45, 0x86, 0xe5, 0xee, 0x50, 0x7d, 0x88, 0x4e, 0x50, 0x84,
0x11, 0xe5, 0xdc, 0xbb, 0xa1, 0x86, 0xb0, 0x80, 0x78, 0x06, 0xb6, 0xa6, 0xda, 0x83, 0x9d, 0xf9,
0x62, 0x32, 0x99, 0xce, 0xe7, 0x4e, 0x03, 0x01, 0x6c, 0x5f, 0xcc, 0x3e, 0xb9, 0xe4, 0xcc, 0xb1,
0xd4, 0xc6, 0xa9, 0xfb, 0xde, 0x5d, 0x5c, 0x9d, 0x3b, 0x4d, 0x05, 0x3e, 0xbb, 0x64, 0x76, 0x31,
0x3b, 0x73, 0x5a, 0xa8, 0xa7, 0xb2, 0xae, 0xa6, 0x84, 0x38, 0x7f, 0xcc, 0x9f, 0x85, 0xdf, 0xc2,
0x9e, 0x9b, 0x89, 0xe5, 0xff, 0xeb, 0x93, 0x51, 0x91, 0xdc, 0xd2, 0xb8, 0xb8, 0x4c, 0x0e, 0xf0,
0x09, 0x74, 0x8d, 0x3d, 0x34, 0x58, 0x70, 0xca, 0xd0, 0x53, 0x00, 0x7f, 0x15, 0xd2, 0x58, 0x4c,
0x28, 0x13, 0x05, 0x45, 0x25, 0x82, 0x77, 0x60, 0x6b, 0x1a, 0xa5, 0xe2, 0x0e, 0x7f, 0x87, 0xde,
0x65, 0xc2, 0xc5, 0x24, 0x89, 0x05, 0xf3, 0x7c, 0x61, 0xd4, 0x11, 0xd8, 0x4b, 0x8f, 0x2f, 0xf5,
0xc9, 0x7d, 0xa2, 0xd7, 0x68, 0x00, 0xbb, 0xd7, 0xe1, 0x8a, 0xc6, 0x5e, 0x64, 0xbc, 0x28, 0x31,
0x7a, 0x0c, 0xdb, 0x3c, 0xbc, 0x89, 0x29, 0xeb, 0xb7, 0x8e, 0x5b, 0x72, 0xa7, 0x40, 0xca, 0x3e,
0x3f, 0x89, 0x22, 0x29, 0xdb, 0xb7, 0x73, 0xfb, 0x0a, 0x28, 0xed, 0x3b, 0xfc, 0x90, 0x84, 0xf1,
0x5c, 0xe6, 0x79, 0x22, 0x63, 0xd4, 0x28, 0x63, 0xd8, 0xf7, 0x8b, 0xcb, 0x2c, 0xb2, 0x30, 0x28,
0xee, 0x5e, 0x8b, 0xa9, 0xdb, 0xa5, 0x09, 0xcb, 0x5b, 0xdc, 0x21, 0x7a, 0x8d, 0x7f, 0x5a, 0xd0,
0x51, 0xa5, 0xcb, 0x4a, 0x62, 0xea, 0x0b, 0x1a, 0xa0, 0x57, 0xd0, 0xa6, 0xa6, 0xa5, 0x9a, 0x66,
0x6f, 0xdc, 0xad, 0x37, 0x9a, 0xdc, 0x27, 0xfc, 0xa3, 0xdb, 0xdc, 0xa0, 0x7b, 0x04, 0x76, 0xc6,
0x75, 0x8d, 0x8a, 0xac, 0xad, 0xc9, 0x94, 0x26, 0xd1, 0x61, 0xfc, 0x05, 0x6c, 0x6d, 0xbe, 0x2c,
0xfa, 0x96, 0xde, 0x9d, 0xdf, 0xfb, 0x67, 0xe0, 0x7d, 0x53, 0x9b, 0xd5, 0xa6, 0x76, 0xa1, 0x19,
0xa6, 0x9a, 0xb4, 0x4d, 0xe4, 0xaa, 0x2c, 0xcf, 0xae, 0x94, 0xf7, 0x06, 0x1c, 0x42, 0xbd, 0xe0,
0x4e, 0xf9, 0xf5, 0x00, 0xab, 0xf0, 0x0f, 0x38, 0xf8, 0xe8, 0x65, 0xb1, 0xbf, 0x2c, 0x8d, 0x7e,
0xa0, 0x2f, 0xcf, 0xa0, 0xc3, 0xcd, 0xd1, 0x8a, 0x31, 0xf5, 0x60, 0xb5, 0xe4, 0x7c, 0x00, 0x0c,
0x1c, 0xff, 0x6e, 0xc2, 0xee, 0x65, 0xf1, 0xa4, 0xd1, 0x18, 0x76, 0xcd, 0xa0, 0xa2, 0x43, 0xad,
0xb9, 0xf6, 0xac, 0x07, 0x6b, 0x37, 0xc1, 0x0d, 0x34, 0x02, 0x5b, 0xbd, 0x0b, 0xe4, 0xe8, 0x9d,
0xca, 0x13, 0x19, 0xf4, 0x6a, 0x0c, 0xf9, 0xe4, 0xcb, 0x03, 0x2f, 0x01, 0x16, 0x31, 0x33, 0x32,
0x90, 0x13, 0xaa, 0x61, 0xdf, 0x40, 0xfe, 0x0e, 0xf6, 0xab, 0xe3, 0x8f, 0xfa, 0x3a, 0x63, 0xc3,
0x8b, 0xd8, 0x70, 0xf6, 0x14, 0x3a, 0xb5, 0x09, 0x46, 0x4f, 0x74, 0xca, 0xa6, 0xa9, 0x1e, 0xa0,
0x72, 0x56, 0xca, 0xf9, 0xc4, 0x8d, 0x13, 0x4b, 0xea, 0xb7, 0xcb, 0xb6, 0xa2, 0x47, 0x45, 0x3d,
0xf5, 0x36, 0x0f, 0x72, 0xa3, 0xd6, 0xba, 0x88, 0x1b, 0x5f, 0xb7, 0xf5, 0x0f, 0xe4, 0xeb, 0xbf,
0x01, 0x00, 0x00, 0xff, 0xff, 0xa1, 0x73, 0x51, 0x2e, 0x41, 0x05, 0x00, 0x00,
0xd6, 0xb1, 0xf5, 0xa2, 0x4d, 0xf2, 0x00, 0xf5, 0x61, 0x87, 0xe5, 0x05, 0xfd, 0xa6, 0xce, 0x9b,
0x10, 0xff, 0xb2, 0xa0, 0x3d, 0x65, 0x2c, 0x61, 0x93, 0x24, 0xa0, 0xe8, 0x39, 0xd8, 0xbe, 0xfc,
0xaf, 0x0f, 0x77, 0xc7, 0xbd, 0xa1, 0x24, 0x19, 0x96, 0xbb, 0x43, 0xf5, 0x21, 0xba, 0x40, 0x01,
0x46, 0x94, 0x73, 0xef, 0x86, 0x1a, 0xc0, 0x22, 0xc4, 0x33, 0xb0, 0x35, 0xd4, 0x1e, 0xec, 0xcc,
0x17, 0x93, 0xc9, 0x74, 0x3e, 0x77, 0x1a, 0x08, 0x60, 0xfb, 0x62, 0xf6, 0xc9, 0x25, 0x67, 0x8e,
0xa5, 0x36, 0x4e, 0xdd, 0xf7, 0xee, 0xe2, 0xea, 0xdc, 0x69, 0xaa, 0xe0, 0xb3, 0x4b, 0x66, 0x17,
0xb3, 0x33, 0xa7, 0x85, 0x7a, 0xaa, 0xea, 0x6a, 0x4a, 0x88, 0xf3, 0xc7, 0xfc, 0x59, 0xf8, 0x2d,
0xec, 0xb9, 0x99, 0x58, 0xfe, 0x5f, 0x9f, 0xcc, 0x8a, 0xe4, 0x96, 0xc6, 0xc5, 0x65, 0xf2, 0x00,
0x9f, 0x40, 0xd7, 0xd8, 0x43, 0x83, 0x05, 0xa7, 0x0c, 0x3d, 0x05, 0xf0, 0x57, 0x21, 0x8d, 0xc5,
0x84, 0x32, 0x51, 0x40, 0x54, 0x32, 0x78, 0x07, 0xb6, 0xa6, 0x51, 0x2a, 0xee, 0xf0, 0x77, 0xe8,
0x5d, 0x26, 0x5c, 0x4c, 0x92, 0x58, 0x30, 0xcf, 0x17, 0x86, 0x1d, 0x81, 0xbd, 0xf4, 0xf8, 0x52,
0x9f, 0xdc, 0x27, 0x7a, 0x8d, 0x06, 0xb0, 0x7b, 0x1d, 0xae, 0x68, 0xec, 0x45, 0xc6, 0x8b, 0x32,
0x46, 0x8f, 0x61, 0x9b, 0x87, 0x37, 0x31, 0x65, 0xfd, 0xd6, 0x71, 0x4b, 0xee, 0x14, 0x91, 0xb2,
0xcf, 0x4f, 0xa2, 0x48, 0xd2, 0xf6, 0xed, 0xdc, 0xbe, 0x22, 0x94, 0xf6, 0x1d, 0x7e, 0x48, 0xc2,
0x78, 0x2e, 0xeb, 0x3c, 0x91, 0x31, 0x6a, 0x98, 0x31, 0xec, 0xfb, 0xc5, 0x65, 0x16, 0x59, 0x18,
0x14, 0x77, 0xaf, 0xe5, 0xd4, 0xed, 0xd2, 0x84, 0xe5, 0x2d, 0xee, 0x10, 0xbd, 0xc6, 0x3f, 0x2d,
0xe8, 0x28, 0xe9, 0x52, 0x49, 0x4c, 0x7d, 0x41, 0x03, 0xf4, 0x0a, 0xda, 0xd4, 0xb4, 0x54, 0xc3,
0xec, 0x8d, 0xbb, 0xf5, 0x46, 0x93, 0x75, 0xc1, 0x3f, 0xbc, 0xcd, 0x0d, 0xbc, 0x47, 0x60, 0x67,
0x5c, 0x6b, 0x54, 0x60, 0x6d, 0x0d, 0xa6, 0x38, 0x89, 0x4e, 0xe3, 0x2f, 0x60, 0x6b, 0xf3, 0xa5,
0xe8, 0x5b, 0x7a, 0x77, 0xbe, 0xf6, 0xcf, 0x84, 0xeb, 0xa6, 0x36, 0xab, 0x4d, 0xed, 0x42, 0x33,
0x4c, 0x35, 0x68, 0x9b, 0xc8, 0x55, 0x29, 0xcf, 0xae, 0xc8, 0x7b, 0x03, 0x0e, 0xa1, 0x5e, 0x70,
0xa7, 0xfc, 0x7a, 0x80, 0x55, 0xf8, 0x07, 0x1c, 0x7c, 0xf4, 0xb2, 0xd8, 0x5f, 0x96, 0x46, 0x3f,
0xd0, 0x97, 0x67, 0xd0, 0xe1, 0xe6, 0x68, 0xc5, 0x98, 0x7a, 0xb2, 0x2a, 0x59, 0x0d, 0xc0, 0x5a,
0xf2, 0xf8, 0x77, 0x13, 0x76, 0x2f, 0x8b, 0x27, 0x8d, 0xc6, 0xb0, 0x6b, 0x06, 0x15, 0x1d, 0x6a,
0xce, 0x7b, 0xcf, 0x7a, 0x70, 0xef, 0x26, 0xb8, 0x81, 0x46, 0x60, 0xab, 0x77, 0x81, 0x1c, 0xbd,
0x53, 0x79, 0x22, 0x83, 0x5e, 0x0d, 0x21, 0x9f, 0x7c, 0x79, 0xe0, 0x25, 0xc0, 0x22, 0x66, 0x86,
0x06, 0x72, 0x40, 0x35, 0xec, 0x1b, 0xc0, 0xdf, 0xc1, 0x7e, 0x75, 0xfc, 0x51, 0x5f, 0x57, 0x6c,
0x78, 0x11, 0x1b, 0xce, 0x9e, 0x42, 0xa7, 0x36, 0xc1, 0xe8, 0x89, 0x2e, 0xd9, 0x34, 0xd5, 0x03,
0x54, 0xce, 0x4a, 0x39, 0x9f, 0xb8, 0x71, 0x62, 0x49, 0xfe, 0x76, 0xd9, 0x56, 0xf4, 0xa8, 0xd0,
0x53, 0x6f, 0xf3, 0x20, 0x37, 0xea, 0x5e, 0x17, 0x71, 0xe3, 0xeb, 0xb6, 0xfe, 0x81, 0x7c, 0xfd,
0x37, 0x00, 0x00, 0xff, 0xff, 0xe1, 0xe6, 0x41, 0xec, 0x41, 0x05, 0x00, 0x00,
}
......@@ -93,5 +93,5 @@ message ReadySignRequest {
message LaunchSignature {
ErrorCode errorCode = 1;
string signatureUuid = 2;
repeated string keyHash = 3;
repeated bytes keyHash = 3;
}
......@@ -33,7 +33,7 @@ func NewWaitingGroupMap() *WaitingGroupMap {
// Join permits the current goroutine to join a room.
// It returns the listenning channel and a slice containing messages already sent by other members of the room.
// The room is automatically created if unknown.
func (g *WaitingGroupMap) Join(room string) (listen chan interface{}, oldMessages []interface{}) {
func (g *WaitingGroupMap) Join(room string) (listen chan interface{}, oldMessages []interface{}, newRoom bool) {
// Check if the current waiting group knows this room
g.mutex.Lock()
_, present := g.data[room]
......@@ -42,6 +42,7 @@ func (g *WaitingGroupMap) Join(room string) (listen chan interface{}, oldMessage
channels: list.New(),
oldMessages: make([]interface{}, 0),
}
newRoom = true
}
g.mutex.Unlock()
......
......@@ -19,7 +19,7 @@ func TestWaitingGroup(t *testing.T) {
// Add some virtual latency
time.Sleep(time.Duration(i) * time.Millisecond)
// Join the waitingGroupMap
myChan, nbs := w.Join("A")
myChan, nbs, _ := w.Join("A")
w.Broadcast("A", i)
// Wait for other msg
for m := range myChan {
......@@ -43,7 +43,7 @@ func TestCloseWaitingGroup(t *testing.T) {
waitGroup.Add(1)
go func() {
myChan, _ := w.Join("A")
myChan, _, _ := w.Join("A")
for _ = range myChan {
t.Fatal("Should not be here")
}
......
......@@ -73,6 +73,19 @@ func TestAddSigner(t *testing.T) {
assert.Equal(t, signers[1].UserID.Hex(), id.Hex())
}
func TestGetHashChain(t *testing.T) {
c := entities.NewContract()
c.AddSigner(nil, "mail1", []byte{0xaa})
c.AddSigner(nil, "mail2", []byte{0xbb, 0xcc})
c.AddSigner(nil, "mail3", []byte{})
chain := c.GetHashChain()
assert.Equal(t, 3, len(chain))
assert.Equal(t, []byte{0xaa}, chain[0])
assert.Equal(t, []byte{0xbb, 0xcc}, chain[1])
assert.Equal(t, []byte{}, chain[2])
}
func assertContractEqual(t *testing.T, contract, fetched entities.Contract) {
assert.Equal(t, contract.File, fetched.File)
assert.Equal(t, contract.Date.Unix(), fetched.Date.Unix())
......
......@@ -29,8 +29,8 @@ func JoinSignature(db *mgdb.MongoManager, rooms *common.WaitingGroupMap, in *api
}
// Join room
roomID := "contract_" + in.ContractUuid
channel, pendingSigners := rooms.Join(roomID)
roomID := "connect_" + in.ContractUuid
channel, pendingSigners, _ := rooms.Join(roomID)
// Send pendingSigners
for _, p := range pendingSigners {
......@@ -103,3 +103,4 @@ func sendUserToStream(stream *api.Platform_JoinSignatureServer, contractUUID str
User: user,
})
}
......@@ -56,3 +56,20 @@ func TestJoinSignatureBadContract(t *testing.T) {
assert.Equal(t, "unauthorized signature", user.ErrorCode.Message)
assert.Equal(t, api.ErrorCode_INVARG, user.ErrorCode.Code)
}
func TestJoinSignatureBadUUID(t *testing.T) {
dropDataset()
createDataset()
client := clientTest(t)
stream, err := client.JoinSignature(context.Background(), &api.JoinSignatureRequest{
ContractUuid: "VERY_BAD",
Port: 5050,
})
assert.Equal(t, nil, err)
user, err := stream.Recv()
assert.Equal(t, nil, err)
assert.Equal(t, "invalid contract uuid", user.ErrorCode.Message)
assert.Equal(t, api.ErrorCode_INVARG, user.ErrorCode.Code)
}
package contract
import (
"time"
"dfss/dfssp/api"
"dfss/dfssp/common"
"dfss/dfssp/entities"
"dfss/mgdb"
"dfss/net"
"golang.org/x/net/context"
"gopkg.in/mgo.v2/bson"
)
// readySignal is the structure that is transmitted accross goroutines
type readySignal struct {
ready bool // If true, this is the ready signal. If not, this is a new connection signal
data string // Various data (CN or SignatureUUID)
chain [][]byte // Only used to broadcast hash chain (signers hashes in order)
}
// ReadySign is the last job of the platform before the signature can occur.
// When a new client is ready, it joins a waitingGroup a waits for a master broadcast announcing that everybody is ready.
//
// Doing it this way is efficient in time, as only one goroutine deals with the database and do global checks.
func ReadySign(db *mgdb.MongoManager, rooms *common.WaitingGroupMap, ctx *context.Context, in *api.ReadySignRequest) *api.LaunchSignature {
roomID := "ready_" + in.ContractUuid
channel, _, first := rooms.Join(roomID)
cn := net.GetCN(ctx)
// Check UUID
if !bson.IsObjectIdHex(in.ContractUuid) {
return &api.LaunchSignature{ErrorCode: &api.ErrorCode{Code: api.ErrorCode_INVARG}}
}
// If first in the room, create a goroutine for ready check.
// It is absolutely thread safe thanks to a mutex applied on the `first` variable.
if first {
go masterReadyRoutine(db, rooms, in.ContractUuid)
}
// Broadcast identity
rooms.Broadcast(roomID, &readySignal{data: cn})
// Wait for ready signal
for {
select {
case signal, ok := <-channel:
if !ok {
return &api.LaunchSignature{ErrorCode: &api.ErrorCode{Code: api.ErrorCode_INTERR}}
}
s := signal.(*readySignal)
if s.ready {
if len(s.data) > 0 {
return &api.LaunchSignature{
ErrorCode: &api.ErrorCode{Code: api.ErrorCode_SUCCESS},
SignatureUuid: s.data,
KeyHash: s.chain,
}
} // data == "" means the contractUUID is bad
return &api.LaunchSignature{ErrorCode: &api.ErrorCode{Code: api.ErrorCode_INVARG}}
}
case <-(*ctx).Done():
rooms.Unjoin(roomID, channel)
return nil
case <-time.After(10 * time.Minute):
rooms.Unjoin(roomID, channel)
return &api.LaunchSignature{ErrorCode: &api.ErrorCode{Code: api.ErrorCode_INTERR, Message: "timeout"}}
}
}
}
// masterReadyRoutine is a function to be started by the first signer ready as a goroutine.
// It will join the associated ready room and check ready status of each signer when a new signer signals its readiness.
func masterReadyRoutine(db *mgdb.MongoManager, rooms *common.WaitingGroupMap, contractUUID string) {
roomID := "ready_" + contractUUID
channel, oldMessages, _ := rooms.Join(roomID)
// Push oldMessages into the channel.
// It is safe as this sould be a very small slice (the room is just created).
for _, v := range oldMessages {
channel <- v
}
// Get contract signers from database
fetch := entities.Contract{ID: bson.ObjectIdHex(contractUUID)}
contract := entities.Contract{}
err := db.Get("contracts").FindByID(fetch, &contract)
if err != nil {
rooms.Broadcast(roomID, &readySignal{
ready: true,
data: "",
}) // This represents a "error" response
rooms.Unjoin(roomID, channel)
return
}
signersReady := make([]bool, len(contract.Signers))
work := true
for work {
select {
case signal, ok := <-channel:
if !ok { // Channel closed, aborting everything
return
}
cn := signal.(*readySignal).data
ready := FindAndUpdatePendingSigner(cn, &signersReady, &contract.Signers)
if ready {
rooms.Broadcast(roomID, &readySignal{
ready: true,
data: bson.NewObjectId().Hex(),
chain: contract.GetHashChain(),
})
work = false
}
case <-time.After(10 * time.Minute):
work = false
}
}
rooms.Unjoin(roomID, channel)
}
// FindAndUpdatePendingSigner is a utility function to return the state of current signers readiness.
// It has absolutely no interaction with the database.
func FindAndUpdatePendingSigner(mail string, signersReady *[]bool, signers *[]entities.Signer) (ready bool) {
// Find an update ready status
for i, s := range *signers {
if s.Email == mail {
(*signersReady)[i] = true
break
}
}
// Check if everyone is ready
for _, s := range *signersReady {
if s == false {
return
}
}
ready = true
return
}
package contract_test
import (
"testing"
"dfss/dfssp/api"
"dfss/dfssp/contract"
"dfss/dfssp/entities"
"github.com/bmizerany/assert"
"golang.org/x/net/context"
"gopkg.in/mgo.v2/bson"
)
func TestReadySignBadContract(t *testing.T) {
dropDataset()
createDataset()
client := clientTest(t)
result, err := client.ReadySign(context.Background(), &api.ReadySignRequest{
ContractUuid: bson.NewObjectId().Hex(),
})
assert.Equal(t, nil, err)
assert.Equal(t, api.ErrorCode_INVARG, result.ErrorCode.Code)
}
func TestReadySignBadUUID(t *testing.T) {
dropDataset()
createDataset()
client := clientTest(t)
result, err := client.ReadySign(context.Background(), &api.ReadySignRequest{
ContractUuid: "VERY_VERY_BAD",
})
assert.Equal(t, nil, err)
assert.Equal(t, api.ErrorCode_INVARG, result.ErrorCode.Code)
}
func TestFindAndUpdatePendingSigner(t *testing.T) {
signers := []entities.Signer{
entities.Signer{Email: "a"},
entities.Signer{Email: "b"},
entities.Signer{Email: "c"},
}
signersReady := make([]bool, 3)
assert.Equal(t, false, contract.FindAndUpdatePendingSigner("a", &signersReady, &signers))
assert.Equal(t, false, contract.FindAndUpdatePendingSigner("c", &signersReady, &signers))
assert.Equal(t, false, contract.FindAndUpdatePendingSigner("c", &signersReady, &signers))
assert.Equal(t, false, contract.FindAndUpdatePendingSigner("a", &signersReady, &signers))
assert.Equal(t, true, contract.FindAndUpdatePendingSigner("b", &signersReady, &signers))
assert.Equal(t, true, contract.FindAndUpdatePendingSigner("b", &signersReady, &signers))
assert.Equal(t, true, contract.FindAndUpdatePendingSigner("a", &signersReady, &signers))
}
......@@ -58,6 +58,16 @@ func (c *Contract) AddSigner(id *bson.ObjectId, email string, hash []byte) {
c.Signers = append(c.Signers, *signer)
}
// GetHashChain returns the ordered slice of signers hashes.
// It's used to check the dfss file if needed.
func (c *Contract) GetHashChain() [][]byte {
chain := make([][]byte, len(c.Signers))
for i, s := range c.Signers {
chain[i] = s.Hash
}
return chain
}
// ContractRepository to contains every complex methods related to contract
type ContractRepository struct {
Collection *mgdb.MongoCollection
......
......@@ -81,8 +81,11 @@ func (s *platformServer) JoinSignature(in *api.JoinSignatureRequest, stream api.
//
// Handle incoming ReadySignRequest messages
func (s *platformServer) ReadySign(ctx context.Context, in *api.ReadySignRequest) (*api.LaunchSignature, error) {
// TODO
return nil, nil
cn := net.GetCN(&ctx)
if len(cn) == 0 {
return &api.LaunchSignature{ErrorCode: &api.ErrorCode{Code: api.ErrorCode_BADAUTH}}, nil
}
return contract.ReadySign(s.DB, s.Rooms, &ctx, in), nil
}
// GetServer returns the GRPC server associated with the platform
......
......@@ -47,7 +47,6 @@ func TestExport(t *testing.T) {
"pass\n" +
"password\n",
)
cmd.Stdout = os.Stdout
err = cmd.Run()
assert.Equal(t, nil, err)
assert.T(t, common.FileExists(confPath))
......
......@@ -5,6 +5,7 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
......@@ -71,18 +72,31 @@ func TestSignContract(t *testing.T) {
clients[0] = newClient(client1)
clients[1] = newClient(client2)
clients[2] = newClient(client3)
closeChannel := make(chan error, 3)
closeChannel := make(chan []byte, 3)
for i := 0; i < 3; i++ {
setLastArg(clients[i], "sign", true)
setLastArg(clients[i], contractPath, false)
go func(c *exec.Cmd, i int) {
time.Sleep(time.Duration(i*2) * time.Second)
c.Stdin = strings.NewReader("password\nyes\n")
closeChannel <- c.Run()
output, err := c.Output()
if err != nil {
output = nil
}
closeChannel <- output
}(clients[i], i)
}
regexes := []*regexp.Regexp{
regexp.MustCompile(`Everybody is ready, starting the signature [a-f0-9]+`),
regexp.MustCompile(`Do you REALLY want to sign contract\.txt\? Type 'yes' to confirm:`),
}
for i := 0; i < 3; i++ {
assert.Equal(t, nil, <-closeChannel)
output := <-closeChannel
assert.NotEqual(t, nil, output, "The return error should be null")
for _, r := range regexes {
assert.T(t, r.Match(output), "Regex is not satisfied: ", r.String())
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment