Commit 9c837227 authored by Loïck Bonniot's avatar Loïck Bonniot

[c][gui] Add signature screen

parent b1909095
Pipeline #774 failed with stage
......@@ -10,6 +10,8 @@ import (
func signContract(args []string) {
filename := args[0]
fmt.Println("You are going to sign the following contract:")
showContract(args)
contract := getContract(filename)
if contract == nil {
os.Exit(1)
......@@ -24,6 +26,8 @@ func signContract(args []string) {
fmt.Fprintln(os.Stderr, err)
os.Exit(2)
}
manager.OnSignerStatusUpdate = signFeedbackFn
err = manager.ConnectToPeers()
if err != nil {
fmt.Fprintln(os.Stderr, err)
......@@ -49,6 +53,7 @@ func signContract(args []string) {
fmt.Println("Everybody is ready, starting the signature", signatureUUID)
// Signature
manager.OnProgressUpdate = signProgressFn
err = manager.Sign()
if err != nil {
fmt.Fprintln(os.Stderr, err)
......@@ -64,3 +69,13 @@ func signContract(args []string) {
fmt.Println("Signature complete! See .proof file for evidences.")
}
func signFeedbackFn(mail string, status sign.SignerStatus, data string) {
if status == sign.StatusConnecting {
fmt.Println("- Trying to connect with", mail, "/", data)
} else if status == sign.StatusConnected {
fmt.Println(" Successfully connected!", "[", data, "]")
}
}
func signProgressFn(current int, max int) {}
......@@ -29,12 +29,15 @@ func (m *SignatureManager) Sign() error {
// Cooldown delay, let other clients wake-up their channels
time.Sleep(time.Second)
seqLen := len(m.sequence)
// Promess rounds
// Follow the sequence until there is no next occurence of me
for m.currentIndex >= 0 {
m.OnProgressUpdate(m.currentIndex, seqLen+1)
dAPI.DLog("starting round at index [" + fmt.Sprintf("%d", m.currentIndex) + "] with nextIndex=" + fmt.Sprintf("%d", nextIndex))
// Set of the promise we are waiting for
// Set of promises we are waiting for
var pendingSet []uint32
pendingSet, err = common.GetPendingSet(m.sequence, myID, m.currentIndex)
if err != nil {
......@@ -58,6 +61,7 @@ func (m *SignatureManager) Sign() error {
}
}
m.OnProgressUpdate(seqLen, seqLen+1)
dAPI.DLog("entering signature round")
// Signature round
err = m.ExchangeAllSignatures()
......@@ -65,6 +69,7 @@ func (m *SignatureManager) Sign() error {
return err
}
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
......@@ -125,7 +130,7 @@ func (m *SignatureManager) promiseRound(pendingSet, sendSet []uint32, myID uint3
// Verifying we sent all the due promises
for range sendSet {
_ = <-c
<-c
}
}
......
......@@ -42,6 +42,10 @@ type SignatureManager struct {
keyHash [][]byte
mail string
archives *Archives
// Callbacks
OnSignerStatusUpdate func(mail string, status SignerStatus, data string)
OnProgressUpdate func(current int, end int)
}
// Archives stores the received and sent messages, as evidence if needed
......@@ -139,10 +143,11 @@ func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) {
}
addrPort := user.Ip + ":" + strconv.Itoa(int(user.Port))
fmt.Println("- Trying to connect with", user.Email, "/", addrPort)
m.OnSignerStatusUpdate(user.Email, StatusConnecting, addrPort)
conn, err := net.Connect(addrPort, m.auth.Cert, m.auth.Key, m.auth.CA)
if err != nil {
m.OnSignerStatusUpdate(user.Email, StatusError, err.Error())
return false, err
}
......@@ -158,12 +163,13 @@ func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) {
defer cancel()
msg, err := client.Discover(ctx, &cAPI.Hello{Version: dfss.Version})
if err != nil {
m.OnSignerStatusUpdate(user.Email, StatusError, err.Error())
return false, err
}
// Printing answer: application version
// TODO check certificate
fmt.Println(" Successfully connected!", "[", msg.Version, "]")
m.OnSignerStatusUpdate(user.Email, StatusConnected, msg.Version)
// Check if we have any other peer to connect to
if lastConnection == nil {
......
package sign
// SignerStatus represents the current state of a signer.
type SignerStatus int
// These constants represent the different states of a signer.
const (
StatusWaiting SignerStatus = iota
StatusConnecting
StatusConnected
StatusError
)
......@@ -41,3 +41,9 @@ func NewWidget(conf *config.Config, onAuth func()) *Widget {
return &Widget{QWidget: form}
}
func (w *Widget) Q() *ui.QWidget {
return w.QWidget
}
func (w *Widget) Tick() {}
......@@ -86,3 +86,9 @@ func (w *Widget) SignersList() (list []string) {
return
}
func (w *Widget) Q() *ui.QWidget {
return w.QWidget
}
func (w *Widget) Tick() {}
......@@ -5,41 +5,100 @@ import (
"dfss/gui/authform"
"dfss/gui/config"
"dfss/gui/contractform"
"dfss/gui/signform"
"dfss/gui/userform"
"github.com/visualfc/goqt/ui"
)
type window struct {
*ui.QMainWindow
current widget
conf *config.Config
}
type widget interface {
Q() *ui.QWidget
Tick()
}
func main() {
// Load configuration
conf := config.Load()
// Start first window
ui.Run(func() {
window := ui.NewMainWindow()
var newuser *userform.Widget
var newauth *authform.Widget
var newcontract *contractform.Widget
newauth = authform.NewWidget(&conf, func() {
window.SetCentralWidget(newcontract)
})
newuser = userform.NewWidget(&conf, func(pwd string) {
window.SetCentralWidget(newauth)
})
newcontract = contractform.NewWidget(&conf)
w := &window{
QMainWindow: ui.NewMainWindow(),
conf: &conf,
}
if conf.Authenticated {
window.SetCentralWidget(newcontract)
w.addActions()
w.showNewContractForm()
} else if conf.Registered {
window.SetCentralWidget(newauth)
w.showAuthForm()
} else {
window.SetCentralWidget(newuser)
w.showUserForm()
}
window.SetWindowTitle("DFSS Client v" + dfss.Version)
window.Show()
timer := ui.NewTimerWithParent(w)
timer.OnTimeout(func() {
w.current.Tick()
})
timer.StartWithMsec(1000)
w.SetWindowTitle("DFSS Client v" + dfss.Version)
w.SetWindowIcon(ui.NewIconWithFilename(":/images/digital_signature_pen.png"))
w.Show()
})
}
func (w *window) addActions() {
openAct := ui.NewActionWithTextParent("&Open", w)
openAct.SetShortcuts(ui.QKeySequence_Open)
openAct.OnTriggered(func() {
w.showSignForm()
})
w.MenuBar().AddAction(openAct)
}
func (w *window) setScreen(wi widget) {
old := w.CentralWidget()
w.SetCentralWidget(wi.Q())
w.current = wi
if old != nil {
old.DeleteLater()
}
}
func (w *window) showUserForm() {
w.setScreen(userform.NewWidget(w.conf, func(pwd string) {
w.showAuthForm()
}))
}
func (w *window) showAuthForm() {
w.setScreen(authform.NewWidget(w.conf, func() {
w.showNewContractForm()
w.addActions()
}))
}
func (w *window) showNewContractForm() {
w.setScreen(contractform.NewWidget(w.conf))
}
func (w *window) showSignForm() {
home := config.GetHomeDir()
filter := "Contract file (*.json);;Any (*.*)"
filename := ui.QFileDialogGetOpenFileNameWithParentCaptionDirFilterSelectedfilterOptions(w, "Select the contract file", home, filter, &filter, 0)
if filename != "" {
config.PasswordDialog(func(err error, pwd string) {
widget := signform.NewWidget(w.conf, filename, pwd)
if widget != nil {
w.setScreen(widget)
}
})
}
}
package signform
import (
"dfss/dfssc/sign"
"github.com/visualfc/goqt/ui"
)
var icons map[sign.SignerStatus]*ui.QIcon
var icons_labels = map[sign.SignerStatus]string{
sign.StatusWaiting: "Waiting",
sign.StatusConnecting: "Connecting",
sign.StatusConnected: "Connected",
sign.StatusError: "Error",
}
var iconsLoaded = false
func loadIcons() {
if iconsLoaded {
return
}
icons = map[sign.SignerStatus]*ui.QIcon{
sign.StatusWaiting: ui.NewIconWithFilename(":/images/time.png"),
sign.StatusConnecting: ui.NewIconWithFilename(":/images/time.png"),
sign.StatusConnected: ui.NewIconWithFilename(":/images/connected.png"),
sign.StatusError: ui.NewIconWithFilename(":/images/error.png"),
}
iconsLoaded = true
}
package signform
import (
"encoding/json"
"io/ioutil"
"dfss/dfssc/sign"
"dfss/dfssp/contract"
"dfss/gui/config"
"github.com/visualfc/goqt/ui"
)
type line struct {
status sign.SignerStatus
info, mail string
cellA, cellB, cellC *ui.QTableWidgetItem
}
type Widget struct {
*ui.QWidget
manager *sign.SignatureManager
contract *contract.JSON
table *ui.QTableWidget
progressBar *ui.QProgressBar
feedbackLabel *ui.QLabel
lines []line
statusMax, statusCurrent int32
feedback string
}
func NewWidget(conf *config.Config, onAuth func()) *Widget {
func NewWidget(conf *config.Config, filename, pwd string) *Widget {
loadIcons()
file := ui.NewFileWithName(":/signform/signform.ui")
loader := ui.NewUiLoader()
form := loader.Load(file)
w := &Widget{QWidget: form}
w.feedbackLabel = ui.NewLabelFromDriver(w.FindChild("mainLabel"))
w.table = ui.NewTableWidgetFromDriver(w.FindChild("signersTable"))
w.progressBar = ui.NewProgressBarFromDriver(w.FindChild("progressBar"))
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil
}
w.contract = new(contract.JSON)
err = json.Unmarshal(data, w.contract)
if err != nil {
return nil
}
home := config.GetHomeDir()
m, err := sign.NewSignatureManager(
home+config.CAFile,
home+config.CertFile,
home+config.KeyFile,
conf.Platform,
pwd,
9005, // TODO change port
w.contract,
)
if err != nil {
// TODO
}
w.manager = m
w.manager.OnSignerStatusUpdate = w.signerUpdated
w.manager.OnProgressUpdate = func(current int, max int) {
w.statusCurrent = int32(current)
w.statusMax = int32(max)
}
w.initLines()
w.signerUpdated(conf.Email, sign.StatusConnected, "It's you!")
go func() {
err = w.execute()
if err != nil {
w.feedback = err.Error()
} else {
w.feedback = "Contract signed successfully!"
}
}()
return w
}
func (w *Widget) execute() error {
w.feedback = "Connecting to peers..."
err := w.manager.ConnectToPeers()
if err != nil {
return err
}
w.feedback = "Waiting for peers..."
_, err = w.manager.SendReadySign()
if err != nil {
return err
}
w.feedback = "Signature in progress..."
err = w.manager.Sign()
if err != nil {
return err
}
w.feedback = "Storing file..."
return w.manager.PersistSignaturesToFile() // TODO choose destination
}
func (w *Widget) signerUpdated(mail string, status sign.SignerStatus, data string) {
for i, s := range w.contract.Signers {
if s.Email == mail {
w.lines[i].status = status
w.lines[i].info = data
break
}
}
}
// Tick updates the whole screen, as we cannot directly update the screen on each callback.
// It is called by the screen coordinator via a timer.
func (w *Widget) Tick() {
w.feedbackLabel.SetText(w.feedback)
w.progressBar.SetMaximum(w.statusMax)
w.progressBar.SetValue(w.statusCurrent)
for _, l := range w.lines {
l.cellA.SetIcon(icons[l.status])
l.cellA.SetText(icons_labels[l.status])
l.cellB.SetText(l.info)
}
}
func (w *Widget) initLines() {
w.table.SetRowCount(int32(len(w.contract.Signers)))
for i, s := range w.contract.Signers {
status := ui.NewTableWidgetItemWithIconTextType(icons[sign.StatusWaiting], icons_labels[sign.StatusWaiting], int32(ui.QTableWidgetItem_UserType))
w.table.SetItem(int32(i), 0, status)
info := ui.NewTableWidgetItemWithTextType("", int32(ui.QTableWidgetItem_UserType))
w.table.SetItem(int32(i), 1, info)
mail := ui.NewTableWidgetItemWithTextType(s.Email, int32(ui.QTableWidgetItem_UserType))
w.table.SetItem(int32(i), 2, mail)
// We must store items, otherwise GC will remove it and cause the application to crash.
// Trust me, hard to debug...
w.lines = append(w.lines, line{sign.StatusWaiting, "", s.Email, status, info, mail})
}
}
func (w *Widget) Q() *ui.QWidget {
return w.QWidget
}
......@@ -50,8 +50,20 @@
</property>
<item>
<widget class="QLabel" name="mainLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:18pt;&quot;&gt;Preparing signature...&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Preparing signature...</string>
</property>
</widget>
</item>
......@@ -82,6 +94,9 @@
</item>
<item>
<widget class="QTableWidget" name="signersTable">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
......@@ -101,7 +116,7 @@
<number>200</number>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>200</number>
<number>50</number>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
......@@ -122,7 +137,7 @@
</column>
<column>
<property name="text">
<string>Ping</string>
<string>Info</string>
</property>
</column>
<column>
......
......@@ -71,3 +71,9 @@ func copyCA(from string, to string) error {
return ioutil.WriteFile(to, file, 0600)
}
func (w *Widget) Q() *ui.QWidget {
return w.QWidget
}
func (w *Widget) Tick() {}
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