From 765c3547935078b25a0f4d61f6aed6c6c8550005 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 2 Dec 2019 18:32:14 +0300 Subject: [PATCH] consensus: return signed messages from recovery.Get* --- go.mod | 2 +- go.sum | 4 +- pkg/consensus/recovery_message.go | 70 +++++++++++++++++++++----- pkg/consensus/recovery_message_test.go | 47 +++++++++++++++-- 4 files changed, 103 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 7706c27d6..16d201058 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/mattn/go-colorable v0.1.4 // indirect github.com/mattn/go-isatty v0.0.10 // indirect github.com/mr-tron/base58 v1.1.2 - github.com/nspcc-dev/dbft v0.0.0-20191125155719-1c35ab053055 + github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254 github.com/nspcc-dev/rfc6979 v0.1.0 github.com/onsi/ginkgo v1.10.3 // indirect github.com/onsi/gomega v1.7.1 // indirect diff --git a/go.sum b/go.sum index 7b4593fee..5d727e76e 100644 --- a/go.sum +++ b/go.sum @@ -88,8 +88,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nspcc-dev/dbft v0.0.0-20191125155719-1c35ab053055 h1:Rs8zeSFfXOX2RuUjWwsUrQpNbxoWyeXU8lWPlYkxDo0= -github.com/nspcc-dev/dbft v0.0.0-20191125155719-1c35ab053055/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw= +github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254 h1:A4OkQDQOSPsJF8qUmqNvFDzmIGALrvOCZrMktllDoKc= +github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw= github.com/nspcc-dev/neofs-crypto v0.2.0 h1:ftN+59WqxSWz/RCgXYOfhmltOOqU+udsNQSvN6wkFck= github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= github.com/nspcc-dev/rfc6979 v0.1.0 h1:Lwg7esRRoyK1Up/IN1vAef1EmvrBeMHeeEkek2fAJ6c= diff --git a/pkg/consensus/recovery_message.go b/pkg/consensus/recovery_message.go index 3a01a168e..f9b8eac2d 100644 --- a/pkg/consensus/recovery_message.go +++ b/pkg/consensus/recovery_message.go @@ -3,6 +3,7 @@ package consensus import ( "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" + "github.com/nspcc-dev/dbft/crypto" "github.com/nspcc-dev/dbft/payload" "github.com/pkg/errors" ) @@ -138,6 +139,10 @@ func (m *recoveryMessage) AddPayload(p payload.ConsensusPayload) { m.prepareRequest = p.GetPrepareRequest().(*prepareRequest) h := p.Hash() m.preparationHash = &h + m.preparationPayloads = append(m.preparationPayloads, &preparationCompact{ + ValidatorIndex: p.ValidatorIndex(), + InvocationScript: p.(*Payload).Witness.InvocationScript, + }) case payload.PrepareResponseType: m.preparationPayloads = append(m.preparationPayloads, &preparationCompact{ ValidatorIndex: p.ValidatorIndex(), @@ -166,16 +171,33 @@ func (m *recoveryMessage) AddPayload(p payload.ConsensusPayload) { } // GetPrepareRequest implements payload.RecoveryMessage interface. -func (m *recoveryMessage) GetPrepareRequest(p payload.ConsensusPayload) payload.ConsensusPayload { +func (m *recoveryMessage) GetPrepareRequest(p payload.ConsensusPayload, validators []crypto.PublicKey, primary uint16) payload.ConsensusPayload { if m.prepareRequest == nil { return nil } - return fromPayload(prepareRequestType, p.(*Payload), m.prepareRequest) + var compact *preparationCompact + for _, p := range m.preparationPayloads { + if p != nil && p.ValidatorIndex == primary { + compact = p + break + } + } + + if compact == nil { + return nil + } + + req := fromPayload(prepareRequestType, p.(*Payload), m.prepareRequest) + req.SetValidatorIndex(primary) + req.Witness.InvocationScript = compact.InvocationScript + req.Witness.VerificationScript = getVerificationScript(primary, validators) + + return req } // GetPrepareResponses implements payload.RecoveryMessage interface. -func (m *recoveryMessage) GetPrepareResponses(p payload.ConsensusPayload) []payload.ConsensusPayload { +func (m *recoveryMessage) GetPrepareResponses(p payload.ConsensusPayload, validators []crypto.PublicKey) []payload.ConsensusPayload { if m.preparationHash == nil { return nil } @@ -183,38 +205,49 @@ func (m *recoveryMessage) GetPrepareResponses(p payload.ConsensusPayload) []payl ps := make([]payload.ConsensusPayload, len(m.preparationPayloads)) for i, resp := range m.preparationPayloads { - ps[i] = fromPayload(prepareResponseType, p.(*Payload), &prepareResponse{ + r := fromPayload(prepareResponseType, p.(*Payload), &prepareResponse{ preparationHash: *m.preparationHash, }) - ps[i].SetValidatorIndex(resp.ValidatorIndex) + r.SetValidatorIndex(resp.ValidatorIndex) + r.Witness.InvocationScript = resp.InvocationScript + r.Witness.VerificationScript = getVerificationScript(resp.ValidatorIndex, validators) + + ps[i] = r } return ps } // GetChangeViews implements payload.RecoveryMessage interface. -func (m *recoveryMessage) GetChangeViews(p payload.ConsensusPayload) []payload.ConsensusPayload { +func (m *recoveryMessage) GetChangeViews(p payload.ConsensusPayload, validators []crypto.PublicKey) []payload.ConsensusPayload { ps := make([]payload.ConsensusPayload, len(m.changeViewPayloads)) for i, cv := range m.changeViewPayloads { - ps[i] = fromPayload(changeViewType, p.(*Payload), &changeView{ + c := fromPayload(changeViewType, p.(*Payload), &changeView{ newViewNumber: cv.OriginalViewNumber + 1, timestamp: cv.Timestamp, }) - ps[i].SetValidatorIndex(cv.ValidatorIndex) + c.SetValidatorIndex(cv.ValidatorIndex) + c.Witness.InvocationScript = cv.InvocationScript + c.Witness.VerificationScript = getVerificationScript(cv.ValidatorIndex, validators) + + ps[i] = c } return ps } // GetCommits implements payload.RecoveryMessage interface. -func (m *recoveryMessage) GetCommits(p payload.ConsensusPayload) []payload.ConsensusPayload { +func (m *recoveryMessage) GetCommits(p payload.ConsensusPayload, validators []crypto.PublicKey) []payload.ConsensusPayload { ps := make([]payload.ConsensusPayload, len(m.commitPayloads)) for i, c := range m.commitPayloads { - cc := commit{signature: c.Signature} - ps[i] = fromPayload(commitType, p.(*Payload), &cc) - ps[i].SetValidatorIndex(c.ValidatorIndex) + cc := fromPayload(commitType, p.(*Payload), &commit{signature: c.Signature}) + cc.SetValidatorIndex(c.ValidatorIndex) + cc.Witness.InvocationScript = c.InvocationScript + cc.Witness.VerificationScript = getVerificationScript(c.ValidatorIndex, validators) + + ps[i] = cc } return ps @@ -230,6 +263,19 @@ func (m *recoveryMessage) SetPreparationHash(h *util.Uint256) { m.preparationHash = h } +func getVerificationScript(i uint16, validators []crypto.PublicKey) []byte { + if int(i) >= len(validators) { + return nil + } + + pub, ok := validators[i].(*publicKey) + if !ok { + return nil + } + + return pub.GetVerificationScript() +} + func fromPayload(t messageType, recovery *Payload, p io.Serializable) *Payload { return &Payload{ message: message{ diff --git a/pkg/consensus/recovery_message_test.go b/pkg/consensus/recovery_message_test.go index 438235d3d..061ac1731 100644 --- a/pkg/consensus/recovery_message_test.go +++ b/pkg/consensus/recovery_message_test.go @@ -3,16 +3,28 @@ package consensus import ( "testing" + "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/util" + "github.com/nspcc-dev/dbft/crypto" "github.com/nspcc-dev/dbft/payload" "github.com/stretchr/testify/require" ) func TestRecoveryMessage_Setters(t *testing.T) { + const size = 5 + + privs := getKeys(t, size) + pubs := make([]crypto.PublicKey, 5) + for i := range pubs { + pubs[i] = &publicKey{privs[i].PublicKey()} + } + r := &recoveryMessage{} p := new(Payload) p.SetType(payload.RecoveryMessageType) p.SetPayload(r) + // sign payload to have verification script + require.NoError(t, p.Sign(privs[0])) req := &prepareRequest{ timestamp: 87, @@ -24,6 +36,8 @@ func TestRecoveryMessage_Setters(t *testing.T) { p1 := new(Payload) p1.SetType(payload.PrepareRequestType) p1.SetPayload(req) + p1.SetValidatorIndex(0) + require.NoError(t, p1.Sign(privs[0])) t.Run("prepare response is added", func(t *testing.T) { p2 := new(Payload) @@ -31,24 +45,28 @@ func TestRecoveryMessage_Setters(t *testing.T) { p2.SetPayload(&prepareResponse{ preparationHash: p1.Hash(), }) + p2.SetValidatorIndex(1) + require.NoError(t, p2.Sign(privs[1])) r.AddPayload(p2) require.NotNil(t, r.PreparationHash()) require.Equal(t, p1.Hash(), *r.PreparationHash()) - ps := r.GetPrepareResponses(p) + ps := r.GetPrepareResponses(p, pubs) require.Len(t, ps, 1) require.Equal(t, p2, ps[0]) + require.True(t, ps[0].(*Payload).Verify()) }) t.Run("prepare request is added", func(t *testing.T) { - pr := r.GetPrepareRequest(p) + pr := r.GetPrepareRequest(p, pubs, p1.ValidatorIndex()) require.Nil(t, pr) r.AddPayload(p1) - pr = r.GetPrepareRequest(p) + pr = r.GetPrepareRequest(p, pubs, p1.ValidatorIndex()) require.NotNil(t, pr) require.Equal(t, p1, pr) + require.True(t, pr.(*Payload).Verify()) }) t.Run("change view is added", func(t *testing.T) { @@ -58,23 +76,42 @@ func TestRecoveryMessage_Setters(t *testing.T) { newViewNumber: 1, timestamp: 12345, }) + p3.SetValidatorIndex(3) + require.NoError(t, p3.Sign(privs[3])) r.AddPayload(p3) - ps := r.GetChangeViews(p) + ps := r.GetChangeViews(p, pubs) require.Len(t, ps, 1) require.Equal(t, p3, ps[0]) + require.True(t, ps[0].(*Payload).Verify()) }) t.Run("commit is added", func(t *testing.T) { p4 := new(Payload) p4.SetType(payload.CommitType) p4.SetPayload(randomMessage(t, commitType)) + p4.SetValidatorIndex(4) + require.NoError(t, p4.Sign(privs[4])) r.AddPayload(p4) - ps := r.GetCommits(p) + ps := r.GetCommits(p, pubs) require.Len(t, ps, 1) require.Equal(t, p4, ps[0]) + require.True(t, ps[0].(*Payload).Verify()) }) } + +func getKeys(t *testing.T, n int) []*privateKey { + privs := make([]*privateKey, 0, n) + for i := 0; i < n; i++ { + priv, err := keys.NewPrivateKey() + require.NoError(t, err) + require.NotNil(t, priv) + + privs = append(privs, &privateKey{PrivateKey: priv}) + } + + return privs +}