Commit 1b61bc0e authored by Richer Maximilien's avatar Richer Maximilien Committed by Loïck Bonniot

[c] Persist contract signatures to a JSON file

- Signatures are written to a file called `$email-$contractUUID.proof`
- Close connection client-side
- Preserve peer connections
parent 6c665a0c
...@@ -54,5 +54,13 @@ func signContract(args []string) { ...@@ -54,5 +54,13 @@ func signContract(args []string) {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(5) os.Exit(5)
} }
// Persist evidencies, if any // Persist evidencies, if any
err = manager.PersistSignaturesToFile()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(5)
}
fmt.Println("Signature complete ! See .proof file for evidences.")
} }
package sign package sign
/*
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
...@@ -59,4 +58,3 @@ func TestComputeFile(t *testing.T) { ...@@ -59,4 +58,3 @@ func TestComputeFile(t *testing.T) {
assert.Equal(t, "37fd29decfb2d689439478b1f64b60441534c1e373a7023676c94ac6772639edab46f80139d167a2741f159e62b3064eca58bb331d32cd10770f29064af2a9de", fmt.Sprintf("%x", m.hash)) assert.Equal(t, "37fd29decfb2d689439478b1f64b60441534c1e373a7023676c94ac6772639edab46f80139d167a2741f159e62b3064eca58bb331d32cd10770f29064af2a9de", fmt.Sprintf("%x", m.hash))
assert.Equal(t, "contract.txt", m.filename) assert.Equal(t, "contract.txt", m.filename)
} }
*/
package sign
import (
"encoding/json"
"fmt"
"io/ioutil"
cAPI "dfss/dfssc/api"
"dfss/dfssp/contract"
)
// SignedContractJSON is an union of contract and realted signatures
type SignedContractJSON struct {
Contract contract.JSON
Signatures []cAPI.Signature
}
// PersistSignaturesToFile save contract informations and signatures to disk
func (m *SignatureManager) PersistSignaturesToFile() error {
// Check content, don't write an empty file
if len(m.archives.recievedSignatures) == 0 {
return fmt.Errorf("No stored signatures, cannot create an empty file (yes I'm a coward)")
}
// Fill JSON struct
signedContract := SignedContractJSON{
Contract: *m.contract,
Signatures: make(
[]cAPI.Signature,
len(m.archives.sentSignatures)+len(m.archives.recievedSignatures),
),
}
for i, s := range m.archives.sentSignatures {
signedContract.Signatures[i] = *s
}
for i, s := range m.archives.recievedSignatures {
signedContract.Signatures[len(m.archives.sentSignatures)+i] = *s
}
proof, err := json.MarshalIndent(signedContract, "", " ")
if err != nil {
return err
}
return ioutil.WriteFile(m.mail+"-"+m.contract.UUID+".proof", proof, 0600)
}
...@@ -66,6 +66,13 @@ func (m *SignatureManager) Sign() error { ...@@ -66,6 +66,13 @@ func (m *SignatureManager) Sign() error {
dAPI.DLog("{" + fmt.Sprintf("%d", myID) + "} Exit signature round") dAPI.DLog("{" + fmt.Sprintf("%d", myID) + "} Exit signature round")
// Network's job is done, cleaning time
// Shutdown and platform client and TODO peer server & connections
err = m.platformConn.Close()
if err != nil {
return err
}
return nil return nil
} }
...@@ -144,3 +151,16 @@ func (m *SignatureManager) promiseRound(pendingSet, sendSet []uint32, myID uint3 ...@@ -144,3 +151,16 @@ func (m *SignatureManager) promiseRound(pendingSet, sendSet []uint32, myID uint3
} }
} }
} }
// closeAllPeerClient tries to close all established connection with other peers
func (m *SignatureManager) closeAllPeerClient() {
for k, client := range m.peersConn {
err := client.Close()
if err != nil {
// We don't care
}
// Remove associated grpc client
delete(m.peers, k)
fmt.Println("- Close connection to " + k)
}
}
...@@ -24,6 +24,8 @@ type SignatureManager struct { ...@@ -24,6 +24,8 @@ type SignatureManager struct {
localPort int localPort int
contract *contract.JSON // contains the contractUUID, the list of the signers' hashes, the hash of the contract contract *contract.JSON // contains the contractUUID, the list of the signers' hashes, the hash of the contract
platform pAPI.PlatformClient platform pAPI.PlatformClient
platformConn *grpc.ClientConn
peersConn map[string]*grpc.ClientConn
peers map[string]*cAPI.ClientClient peers map[string]*cAPI.ClientClient
hashToID map[string]uint32 hashToID map[string]uint32
nbReady int nbReady int
...@@ -75,7 +77,9 @@ func NewSignatureManager(fileCA, fileCert, fileKey, addrPort, passphrase string, ...@@ -75,7 +77,9 @@ func NewSignatureManager(fileCA, fileCert, fileKey, addrPort, passphrase string,
} }
m.platform = pAPI.NewPlatformClient(conn) m.platform = pAPI.NewPlatformClient(conn)
m.platformConn = conn
m.peersConn = make(map[string]*grpc.ClientConn)
m.peers = make(map[string]*cAPI.ClientClient) m.peers = make(map[string]*cAPI.ClientClient)
for _, u := range c.Signers { for _, u := range c.Signers {
if u.Email != m.auth.Cert.Subject.CommonName { if u.Email != m.auth.Cert.Subject.CommonName {
...@@ -139,6 +143,9 @@ func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) { ...@@ -139,6 +143,9 @@ func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) {
client := cAPI.NewClientClient(conn) client := cAPI.NewClientClient(conn)
lastConnection := m.peers[user.Email] lastConnection := m.peers[user.Email]
m.peers[user.Email] = &client m.peers[user.Email] = &client
// The connection is encapsulated into the interface, so we
// need to create another way to access it
m.peersConn[user.Email] = conn
ctx, cancel := context.WithTimeout(context.Background(), time.Minute) ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel() defer cancel()
......
...@@ -14,6 +14,17 @@ import ( ...@@ -14,6 +14,17 @@ import (
"github.com/bmizerany/assert" "github.com/bmizerany/assert"
) )
// TestSignContract unroll the whole signature process.
//
// GOOD CASE
// - Start platform
// - Register client1, client2 and client3
// - Create contract `contract.txt`
// - Sign it
// - Check if all proof files are present
//
// TODO BAD CASES
func TestSignContract(t *testing.T) { func TestSignContract(t *testing.T) {
// Cleanup // Cleanup
eraseDatabase() eraseDatabase()
...@@ -101,4 +112,20 @@ func TestSignContract(t *testing.T) { ...@@ -101,4 +112,20 @@ func TestSignContract(t *testing.T) {
assert.T(t, r.Match(output), "Regex is not satisfied: ", r.String()) assert.T(t, r.Match(output), "Regex is not satisfied: ", r.String())
} }
} }
// Ensure that all the files are present
proofFile := regexp.MustCompile(`client[0-9]+@example.com.*\.proof`)
files, _ := ioutil.ReadDir("./")
matches := 0
for _, file := range files {
if proofFile.Match([]byte(file.Name())) {
matches++
err = os.Remove("./" + file.Name())
assert.T(t, err == nil, "Cannot remove .proof matching file")
}
}
assert.T(t, matches == 3, "Missing proof file ?")
time.Sleep(time.Second)
} }
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