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

[c][gui] Add signature cancellation feature

parent 075c808d
Pipeline #1813 passed with stage
...@@ -18,6 +18,12 @@ import ( ...@@ -18,6 +18,12 @@ import (
// * Promises rounds // * Promises rounds
// * Signature round // * Signature round
func (m *SignatureManager) Sign() error { func (m *SignatureManager) Sign() error {
defer func() {
m.finished = true
m.closeConnections()
}()
myID, nextIndex, err := m.Initialize() myID, nextIndex, err := m.Initialize()
if err != nil { if err != nil {
return err return err
...@@ -72,9 +78,7 @@ func (m *SignatureManager) Sign() error { ...@@ -72,9 +78,7 @@ func (m *SignatureManager) Sign() error {
dAPI.DLog("exiting signature round") dAPI.DLog("exiting signature round")
m.OnProgressUpdate(seqLen+1, seqLen+1) m.OnProgressUpdate(seqLen+1, seqLen+1)
// Network's job is done, cleaning time return nil
// Shutdown and platform client and TODO peer server & connections
return m.platformConn.Close()
} }
// GetClient retrieves the Client to the specified sequence id provided it exists // GetClient retrieves the Client to the specified sequence id provided it exists
...@@ -135,11 +139,13 @@ func (m *SignatureManager) promiseRound(pendingSet, sendSet []uint32, myID uint3 ...@@ -135,11 +139,13 @@ func (m *SignatureManager) promiseRound(pendingSet, sendSet []uint32, myID uint3
} }
} }
// closeAllPeerClient tries to close all established connection with other peers // closeConnections tries to close all established connection with other peers and platform.
func (m *SignatureManager) closeAllPeerClient() { // It also stops the local server.
for k, client := range m.peersConn { func (m *SignatureManager) closeConnections() {
_ = client.Close() _ = m.platformConn.Close()
// Remove associated grpc client for k, peer := range m.peersConn {
_ = peer.Close()
delete(m.peers, k) delete(m.peers, k)
} }
m.cServer.Stop()
} }
...@@ -3,7 +3,6 @@ package sign ...@@ -3,7 +3,6 @@ package sign
import ( import (
"errors" "errors"
"fmt" "fmt"
"log"
"strconv" "strconv"
"sync" "sync"
"time" "time"
...@@ -43,10 +42,13 @@ type SignatureManager struct { ...@@ -43,10 +42,13 @@ type SignatureManager struct {
mail string mail string
archives *Archives archives *Archives
seal []byte seal []byte
cancelled bool
finished bool
// Callbacks // Callbacks
OnSignerStatusUpdate func(mail string, status SignerStatus, data string) OnSignerStatusUpdate func(mail string, status SignerStatus, data string)
OnProgressUpdate func(current int, end int) OnProgressUpdate func(current int, end int)
Cancel chan interface{}
} }
// Archives stores the received and sent messages, as evidence if needed // Archives stores the received and sent messages, as evidence if needed
...@@ -69,6 +71,7 @@ func NewSignatureManager(passphrase string, c *contract.JSON) (*SignatureManager ...@@ -69,6 +71,7 @@ func NewSignatureManager(passphrase string, c *contract.JSON) (*SignatureManager
sentSignatures: make([]*cAPI.Signature, 0), sentSignatures: make([]*cAPI.Signature, 0),
receivedSignatures: make([]*cAPI.Signature, 0), receivedSignatures: make([]*cAPI.Signature, 0),
}, },
Cancel: make(chan interface{}),
} }
var err error var err error
_, _, _, err = m.auth.LoadFiles() _, _, _, err = m.auth.LoadFiles()
...@@ -80,7 +83,7 @@ func NewSignatureManager(passphrase string, c *contract.JSON) (*SignatureManager ...@@ -80,7 +83,7 @@ func NewSignatureManager(passphrase string, c *contract.JSON) (*SignatureManager
dAPI.SetIdentifier(m.mail) dAPI.SetIdentifier(m.mail)
m.cServer = m.GetServer() m.cServer = m.GetServer()
go func() { log.Fatalln(net.Listen("0.0.0.0:"+strconv.Itoa(viper.GetInt("local_port")), m.cServer)) }() go func() { _ = net.Listen("0.0.0.0:"+strconv.Itoa(viper.GetInt("local_port")), m.cServer) }()
conn, err := net.Connect(viper.GetString("platform_addrport"), m.auth.Cert, m.auth.Key, m.auth.CA, nil) conn, err := net.Connect(viper.GetString("platform_addrport"), m.auth.Cert, m.auth.Key, m.auth.CA, nil)
if err != nil { if err != nil {
...@@ -114,28 +117,49 @@ func (m *SignatureManager) ConnectToPeers() error { ...@@ -114,28 +117,49 @@ func (m *SignatureManager) ConnectToPeers() error {
Ip: localIps, Ip: localIps,
}) })
if err != nil { if err != nil {
m.finished = true
m.closeConnections()
return err return err
} }
for { c := make(chan error)
go connectToPeersLoop(m, stream, c)
select {
case err = <-c:
if err != nil {
m.finished = true
m.closeConnections()
}
return err
case <-m.Cancel:
m.cancelled = true
m.closeConnections()
return errors.New("Signature cancelled")
}
}
func connectToPeersLoop(m *SignatureManager, stream pAPI.Platform_JoinSignatureClient, c chan error) {
for !m.cancelled {
userConnected, err := stream.Recv() userConnected, err := stream.Recv()
if err != nil { if err != nil {
return err c <- err
return
} }
errorCode := userConnected.GetErrorCode() errorCode := userConnected.GetErrorCode()
if errorCode.Code != pAPI.ErrorCode_SUCCESS { if errorCode.Code != pAPI.ErrorCode_SUCCESS {
return errors.New(errorCode.Message) c <- errors.New(errorCode.Message)
return
} }
ready, err := m.addPeer(userConnected.User) ready, err := m.addPeer(userConnected.User)
if err != nil { if err != nil {
continue // Unable to connect to this user, ignore it for the moment continue // Unable to connect to this user, ignore it for the moment
} }
if ready { if ready {
break c <- nil
return
} }
} }
return nil
} }
// addPeer stores a peer from the platform and tries to establish a connection to this peer. // addPeer stores a peer from the platform and tries to establish a connection to this peer.
...@@ -158,6 +182,11 @@ func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) { ...@@ -158,6 +182,11 @@ func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) {
if err == nil { if err == nil {
break break
} }
if m.cancelled {
err = errors.New("Signature cancelled")
break
}
} }
if err != nil { if err != nil {
...@@ -200,28 +229,46 @@ func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) { ...@@ -200,28 +229,46 @@ func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) {
func (m *SignatureManager) SendReadySign() (signatureUUID string, err error) { func (m *SignatureManager) SendReadySign() (signatureUUID string, err error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel() defer cancel()
launch, err := m.platform.ReadySign(ctx, &pAPI.ReadySignRequest{
ContractUuid: m.contract.UUID, c := make(chan *pAPI.LaunchSignature)
}) go func() {
if err != nil { launch, _ := m.platform.ReadySign(ctx, &pAPI.ReadySignRequest{
ContractUuid: m.contract.UUID,
})
c <- launch
}()
var launch *pAPI.LaunchSignature
select {
case launch = <-c: // OK
case <-m.Cancel:
m.cancelled = true
m.closeConnections()
err = errors.New("Signature cancelled")
return return
} }
errorCode := launch.GetErrorCode() errorCode := launch.GetErrorCode()
if errorCode.Code != pAPI.ErrorCode_SUCCESS { if errorCode.Code != pAPI.ErrorCode_SUCCESS {
err = errors.New(errorCode.Code.String() + " " + errorCode.Message) err = errors.New(errorCode.Code.String() + " " + errorCode.Message)
m.finished = true
m.closeConnections()
return return
} }
// Check signers from platform data // Check signers from platform data
if len(m.contract.Signers) != len(launch.KeyHash) { if len(m.contract.Signers) != len(launch.KeyHash) {
err = errors.New("Corrupted DFSS file: bad number of signers, unable to sign safely") err = errors.New("Corrupted DFSS file: bad number of signers, unable to sign safely")
m.finished = true
m.closeConnections()
return return
} }
for i, s := range m.contract.Signers { for i, s := range m.contract.Signers {
if s.Hash != fmt.Sprintf("%x", launch.KeyHash[i]) { if s.Hash != fmt.Sprintf("%x", launch.KeyHash[i]) {
err = errors.New("Corrupted DFSS file: signer " + s.Email + " has an invalid hash, unable to sign safely") err = errors.New("Corrupted DFSS file: signer " + s.Email + " has an invalid hash, unable to sign safely")
m.finished = true
m.closeConnections()
return return
} }
} }
...@@ -264,3 +311,8 @@ func (m *SignatureManager) FindID() (uint32, error) { ...@@ -264,3 +311,8 @@ func (m *SignatureManager) FindID() (uint32, error) {
} }
return 0, errors.New("Mail couldn't be found amongst signers") return 0, errors.New("Mail couldn't be found amongst signers")
} }
// IsTerminated returns true if the signature is cancelled or finished
func (m *SignatureManager) IsTerminated() bool {
return m.cancelled || m.finished
}
...@@ -22,9 +22,11 @@ type Widget struct { ...@@ -22,9 +22,11 @@ type Widget struct {
table *ui.QTableWidget table *ui.QTableWidget
progressBar *ui.QProgressBar progressBar *ui.QProgressBar
feedbackLabel *ui.QLabel feedbackLabel *ui.QLabel
cancelButton *ui.QPushButton
lines []line lines []line
statusMax, statusCurrent int32 statusMax, statusCurrent int32
feedback string feedback string
running bool
} }
func NewWidget(contract *contract.JSON, pwd string) *Widget { func NewWidget(contract *contract.JSON, pwd string) *Widget {
...@@ -40,6 +42,7 @@ func NewWidget(contract *contract.JSON, pwd string) *Widget { ...@@ -40,6 +42,7 @@ func NewWidget(contract *contract.JSON, pwd string) *Widget {
w.feedbackLabel = ui.NewLabelFromDriver(w.FindChild("mainLabel")) w.feedbackLabel = ui.NewLabelFromDriver(w.FindChild("mainLabel"))
w.table = ui.NewTableWidgetFromDriver(w.FindChild("signersTable")) w.table = ui.NewTableWidgetFromDriver(w.FindChild("signersTable"))
w.progressBar = ui.NewProgressBarFromDriver(w.FindChild("progressBar")) w.progressBar = ui.NewProgressBarFromDriver(w.FindChild("progressBar"))
w.cancelButton = ui.NewPushButtonFromDriver(w.FindChild("cancelButton"))
m, err := sign.NewSignatureManager( m, err := sign.NewSignatureManager(
pwd, pwd,
...@@ -56,6 +59,18 @@ func NewWidget(contract *contract.JSON, pwd string) *Widget { ...@@ -56,6 +59,18 @@ func NewWidget(contract *contract.JSON, pwd string) *Widget {
w.statusMax = int32(max) w.statusMax = int32(max)
} }
w.cancelButton.OnClicked(func() {
// Render an immediate feedback to user
f := "Cancelling signature process..."
w.feedback = f
w.feedbackLabel.SetText(f)
w.cancelButton.SetDisabled(true)
w.statusMax = 1
w.statusCurrent = 0
// Ask for cancellation in a separate goroutine to avoid blocking Qt
go func() { w.manager.Cancel <- true }()
})
w.initLines() w.initLines()
w.signerUpdated(viper.GetString("email"), sign.StatusConnected, "It's you!") w.signerUpdated(viper.GetString("email"), sign.StatusConnected, "It's you!")
go func() { go func() {
...@@ -70,6 +85,8 @@ func NewWidget(contract *contract.JSON, pwd string) *Widget { ...@@ -70,6 +85,8 @@ func NewWidget(contract *contract.JSON, pwd string) *Widget {
return w return w
} }
// execute() is called in a goroutine OUTSIDE of Qt loop.
// WE SHOULD NOT CALL ANY QT FUNCTION FROM IT.
func (w *Widget) execute() error { func (w *Widget) execute() error {
w.feedback = "Connecting to peers..." w.feedback = "Connecting to peers..."
err := w.manager.ConnectToPeers() err := w.manager.ConnectToPeers()
...@@ -84,6 +101,7 @@ func (w *Widget) execute() error { ...@@ -84,6 +101,7 @@ func (w *Widget) execute() error {
} }
w.feedback = "Signature in progress..." w.feedback = "Signature in progress..."
w.running = true
err = w.manager.Sign() err = w.manager.Sign()
if err != nil { if err != nil {
return err return err
...@@ -109,6 +127,7 @@ func (w *Widget) Tick() { ...@@ -109,6 +127,7 @@ func (w *Widget) Tick() {
w.feedbackLabel.SetText(w.feedback) w.feedbackLabel.SetText(w.feedback)
w.progressBar.SetMaximum(w.statusMax) w.progressBar.SetMaximum(w.statusMax)
w.progressBar.SetValue(w.statusCurrent) w.progressBar.SetValue(w.statusCurrent)
w.cancelButton.SetDisabled(w.running || w.manager.IsTerminated())
for _, l := range w.lines { for _, l := range w.lines {
l.cellA.SetIcon(icons[l.status]) l.cellA.SetIcon(icons[l.status])
l.cellA.SetText(icons_labels[l.status]) l.cellA.SetText(icons_labels[l.status])
......
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