From 4eef5813965bbf48ad6c71cd77c4bb90cc3bc0ee Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 29 Jun 2017 20:34:43 +0200 Subject: [PATCH] Tested signature middleware with standard tx wrappers --- stack/middleware.go | 2 +- stack/signature.go | 18 +++++--- stack/signature_test.go | 99 +++++++++++++++++++++++++++++++++++++++++ txs/base.go | 3 +- txs/sigs.go | 17 +++++++ 5 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 stack/signature_test.go diff --git a/stack/middleware.go b/stack/middleware.go index 3bd2cb738..7dfc5cf04 100644 --- a/stack/middleware.go +++ b/stack/middleware.go @@ -61,7 +61,7 @@ func NewDefault(chainID string, middlewares ...Middleware) *Stack { mids := []Middleware{ Logger{}, Recovery{}, - SignedHandler{true}, + Signatures{}, Chain{chainID}, } mids = append(mids, middlewares...) diff --git a/stack/signature.go b/stack/signature.go index fa59c210a..ca2402394 100644 --- a/stack/signature.go +++ b/stack/signature.go @@ -1,6 +1,8 @@ package stack import ( + "fmt" + crypto "github.com/tendermint/go-crypto" "github.com/tendermint/basecoin" @@ -13,15 +15,13 @@ const ( NameSigs = "sigs" ) -type SignedHandler struct { - AllowMultiSig bool -} +type Signatures struct{} -func (_ SignedHandler) Name() string { +func (_ Signatures) Name() string { return NameSigs } -var _ Middleware = SignedHandler{} +var _ Middleware = Signatures{} func SigPerm(addr []byte) basecoin.Actor { return basecoin.NewActor(NameSigs, addr) @@ -33,7 +33,7 @@ type Signed interface { Signers() ([]crypto.PubKey, error) } -func (h SignedHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) { +func (h Signatures) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) { sigs, tnext, err := getSigners(tx) if err != nil { return res, err @@ -42,7 +42,7 @@ func (h SignedHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx bas return next.CheckTx(ctx2, store, tnext) } -func (h SignedHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) { +func (h Signatures) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) { sigs, tnext, err := getSigners(tx) if err != nil { return res, err @@ -53,7 +53,9 @@ func (h SignedHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx b func addSigners(ctx basecoin.Context, sigs []crypto.PubKey) basecoin.Context { perms := make([]basecoin.Actor, len(sigs)) + fmt.Printf("Add %d signers\n", len(sigs)) for i, s := range sigs { + fmt.Printf("Add %X\n", s.Address()) perms[i] = SigPerm(s.Address()) } // add the signers to the context and continue @@ -61,8 +63,10 @@ func addSigners(ctx basecoin.Context, sigs []crypto.PubKey) basecoin.Context { } func getSigners(tx basecoin.Tx) ([]crypto.PubKey, basecoin.Tx, error) { + fmt.Println("getSigners") stx, ok := tx.Unwrap().(Signed) if !ok { + fmt.Printf("Not okay: %#v\n", tx) return nil, basecoin.Tx{}, errors.Unauthorized() } sig, err := stx.Signers() diff --git a/stack/signature_test.go b/stack/signature_test.go new file mode 100644 index 000000000..e0d90cba2 --- /dev/null +++ b/stack/signature_test.go @@ -0,0 +1,99 @@ +package stack + +import ( + "strconv" + "testing" + + "github.com/stretchr/testify/assert" + crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/tmlibs/log" + + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/txs" + "github.com/tendermint/basecoin/types" +) + +func TestSignatureChecks(t *testing.T) { + assert := assert.New(t) + + // generic args + ctx := NewContext(log.NewNopLogger()) + store := types.NewMemKVStore() + raw := txs.NewRaw([]byte{1, 2, 3, 4}).Wrap() + + // let's make some keys.... + priv1 := crypto.GenPrivKeyEd25519().Wrap() + actor1 := SigPerm(priv1.PubKey().Address()) + priv2 := crypto.GenPrivKeySecp256k1().Wrap() + actor2 := SigPerm(priv2.PubKey().Address()) + + // test cases to make sure signature checks are solid + cases := []struct { + useMultiSig bool + keys []crypto.PrivKey + check basecoin.Actor + valid bool + }{ + // test with single sigs + {false, []crypto.PrivKey{priv1}, actor1, true}, + {false, []crypto.PrivKey{priv1}, actor2, false}, + {false, []crypto.PrivKey{priv2}, actor2, true}, + {false, []crypto.PrivKey{}, actor2, false}, + + // same with multi sigs + {true, []crypto.PrivKey{priv1}, actor1, true}, + {true, []crypto.PrivKey{priv1}, actor2, false}, + {true, []crypto.PrivKey{priv2}, actor2, true}, + {true, []crypto.PrivKey{}, actor2, false}, + + // make sure both match on a multisig + {true, []crypto.PrivKey{priv1, priv2}, actor1, true}, + {true, []crypto.PrivKey{priv1, priv2}, actor2, true}, + } + + for i, tc := range cases { + idx := strconv.Itoa(i) + + // make the stack check for the given permission + app := New( + Recovery{}, // we need this so panics turn to errors + Signatures{}, + CheckMiddleware{tc.check}, + ).Use(OKHandler{}) + + var tx basecoin.Tx + // this does the signing as needed + if tc.useMultiSig { + mtx := txs.NewMulti(raw) + for _, k := range tc.keys { + err := txs.Sign(mtx, k) + assert.Nil(err, "%d: %+v", i, err) + } + tx = mtx.Wrap() + } else { + otx := txs.NewSig(raw) + for _, k := range tc.keys { + err := txs.Sign(otx, k) + assert.Nil(err, "%d: %+v", i, err) + } + tx = otx.Wrap() + } + + // this will trivial expose the printing error... + // _, err := app.CheckTx(ctx, store, raw) + _, err := app.CheckTx(ctx, store, tx) + if tc.valid { + // TODO: why doen't tmerror print properly??? + assert.Nil(err, "%d: %+v", i, err) + } else { + assert.NotNil(err, idx) + } + + _, err = app.DeliverTx(ctx, store, tx) + if tc.valid { + assert.Nil(err, "%d: %+v", i, err) + } else { + assert.NotNil(err, idx) + } + } +} diff --git a/txs/base.go b/txs/base.go index e02d3c782..406a270ae 100644 --- a/txs/base.go +++ b/txs/base.go @@ -9,7 +9,8 @@ import ( const ( // for utils... - ByteRaw = 0x1 + ByteRaw = 0x1 + // TODO: move fees into a module, multiplexer is standard ByteFees = 0x2 ByteMulti = 0x3 ByteChain = 0x4 diff --git a/txs/sigs.go b/txs/sigs.go index f3c39bb5b..5550f262f 100644 --- a/txs/sigs.go +++ b/txs/sigs.go @@ -49,6 +49,7 @@ type OneSig struct { } var _ keys.Signable = &OneSig{} +var _ basecoin.TxLayer = &OneSig{} func NewSig(tx basecoin.Tx) *OneSig { return &OneSig{Tx: tx} @@ -58,6 +59,10 @@ func (s *OneSig) Wrap() basecoin.Tx { return basecoin.Tx{s} } +func (s *OneSig) Next() basecoin.Tx { + return s.Tx +} + func (s *OneSig) ValidateBasic() error { // TODO: VerifyBytes here, we do it in Signers? if s.Empty() || !s.Pubkey.VerifyBytes(s.SignBytes(), s.Sig) { @@ -119,6 +124,7 @@ type MultiSig struct { } var _ keys.Signable = &MultiSig{} +var _ basecoin.TxLayer = &MultiSig{} func NewMulti(tx basecoin.Tx) *MultiSig { return &MultiSig{Tx: tx} @@ -128,6 +134,10 @@ func (s *MultiSig) Wrap() basecoin.Tx { return basecoin.Tx{s} } +func (s *MultiSig) Next() basecoin.Tx { + return s.Tx +} + func (s *MultiSig) ValidateBasic() error { // TODO: more efficient _, err := s.Signers() @@ -185,3 +195,10 @@ func (s *MultiSig) Signers() ([]crypto.PubKey, error) { return keys, nil } + +func Sign(tx keys.Signable, key crypto.PrivKey) error { + msg := tx.SignBytes() + pubkey := key.PubKey() + sig := key.Sign(msg) + return tx.Sign(pubkey, sig) +}