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 (
// * Promises rounds
// * Signature round
func (m *SignatureManager) Sign() error {
defer func() {
m.finished = true
m.closeConnections()
}()
myID, nextIndex, err := m.Initialize()
if err != nil {
return err
......@@ -72,9 +78,7 @@ func (m *SignatureManager) Sign() error {
dAPI.DLog("exiting signature round")
m.OnProgressUpdate(seqLen+1, seqLen+1)
// Network's job is done, cleaning time
// Shutdown and platform client and TODO peer server & connections
return m.platformConn.Close()
return nil
}
// 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
}
}
// closeAllPeerClient tries to close all established connection with other peers
func (m *SignatureManager) closeAllPeerClient() {
for k, client := range m.peersConn {
_ = client.Close()
// Remove associated grpc client
// 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()
delete(m.peers, k)
}
m.cServer.Stop()
}
......@@ -3,7 +3,6 @@ package sign
import (
"errors"
"fmt"
"log"
"strconv"
"sync"
"time"
......@@ -43,10 +42,13 @@ type SignatureManager struct {
mail string
archives *Archives
seal []byte
cancelled bool
finished bool
// Callbacks
OnSignerStatusUpdate func(mail string, status SignerStatus, data string)
OnProgressUpdate func(current int, end int)
Cancel chan interface{}
}
// Archives stores the received and sent messages, as evidence if needed
......@@ -69,6 +71,7 @@ func NewSignatureManager(passphrase string, c *contract.JSON) (*SignatureManager
sentSignatures: make([]*cAPI.Signature, 0),
receivedSignatures: make([]*cAPI.Signature, 0),
},
Cancel: make(chan interface{}),
}
var err error
_, _, _, err = m.auth.LoadFiles()
......@@ -80,7 +83,7 @@ func NewSignatureManager(passphrase string, c *contract.JSON) (*SignatureManager
dAPI.SetIdentifier(m.mail)
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)
if err != nil {
......@@ -114,28 +117,49 @@ func (m *SignatureManager) ConnectToPeers() error {
Ip: localIps,
})
if err != nil {
m.finished = true
m.closeConnections()
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()
if err != nil {
return err
c <- err
return
}
errorCode := userConnected.GetErrorCode()
if errorCode.Code != pAPI.ErrorCode_SUCCESS {
return errors.New(errorCode.Message)
c <- errors.New(errorCode.Message)
return
}
ready, err := m.addPeer(userConnected.User)
if err != nil {
continue // Unable to connect to this user, ignore it for the moment
}
if ready {
break
c <- nil
return
}
}
return nil
}
// 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) {
if err == nil {
break
}
if m.cancelled {
err = errors.New("Signature cancelled")
break
}
}
if err != nil {
......@@ -200,28 +229,46 @@ func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) {
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, &pAPI.ReadySignRequest{
ContractUuid: m.contract.UUID,
})
if err != nil {
c := make(chan *pAPI.LaunchSignature)
go func() {
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
}
errorCode := launch.GetErrorCode()
if errorCode.Code != pAPI.ErrorCode_SUCCESS {
err = errors.New(errorCode.Code.String() + " " + errorCode.Message)
m.finished = true
m.closeConnections()
return
}
// Check signers from platform data
if len(m.contract.Signers) != len(launch.KeyHash) {
err = errors.New("Corrupted DFSS file: bad number of signers, unable to sign safely")
m.finished = true
m.closeConnections()
return
}
for i, s := range m.contract.Signers {
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")
m.finished = true
m.closeConnections()
return
}
}
......@@ -264,3 +311,8 @@ func (m *SignatureManager) FindID() (uint32, error) {
}
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 {
table *ui.QTableWidget
progressBar *ui.QProgressBar
feedbackLabel *ui.QLabel
cancelButton *ui.QPushButton
lines []line
statusMax, statusCurrent int32
feedback string
running bool
}
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.table = ui.NewTableWidgetFromDriver(w.FindChild("signersTable"))
w.progressBar = ui.NewProgressBarFromDriver(w.FindChild("progressBar"))
w.cancelButton = ui.NewPushButtonFromDriver(w.FindChild("cancelButton"))
m, err := sign.NewSignatureManager(
pwd,
......@@ -56,6 +59,18 @@ func NewWidget(contract *contract.JSON, pwd string) *Widget {
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.signerUpdated(viper.GetString("email"), sign.StatusConnected, "It's you!")
go func() {
......@@ -70,6 +85,8 @@ func NewWidget(contract *contract.JSON, pwd string) *Widget {
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 {
w.feedback = "Connecting to peers..."
err := w.manager.ConnectToPeers()
......@@ -84,6 +101,7 @@ func (w *Widget) execute() error {
}
w.feedback = "Signature in progress..."
w.running = true
err = w.manager.Sign()
if err != nil {
return err
......@@ -109,6 +127,7 @@ func (w *Widget) Tick() {
w.feedbackLabel.SetText(w.feedback)
w.progressBar.SetMaximum(w.statusMax)
w.progressBar.SetValue(w.statusCurrent)
w.cancelButton.SetDisabled(w.running || w.manager.IsTerminated())
for _, l := range w.lines {
l.cellA.SetIcon(icons[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