Moved content of txs package to sit next to the handlers

This commit is contained in:
Ethan Frey
2017-07-06 16:33:38 +02:00
parent a047e210fa
commit b757467f7b
18 changed files with 169 additions and 159 deletions
+3 -3
View File
@@ -32,8 +32,8 @@ func SigPerm(addr []byte) basecoin.Actor {
return basecoin.NewActor(NameSigs, addr)
}
// Signed allows us to use txs.OneSig and txs.MultiSig (and others??)
type Signed interface {
// Signable allows us to use txs.OneSig and txs.MultiSig (and others??)
type Signable interface {
basecoin.TxLayer
Signers() ([]crypto.PubKey, error)
}
@@ -68,7 +68,7 @@ func addSigners(ctx basecoin.Context, sigs []crypto.PubKey) basecoin.Context {
}
func getSigners(tx basecoin.Tx) ([]crypto.PubKey, basecoin.Tx, error) {
stx, ok := tx.Unwrap().(Signed)
stx, ok := tx.Unwrap().(Signable)
if !ok {
return nil, basecoin.Tx{}, errors.ErrUnauthorized()
}
+5 -6
View File
@@ -12,7 +12,6 @@ import (
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/stack"
"github.com/tendermint/basecoin/state"
"github.com/tendermint/basecoin/txs"
)
func TestSignatureChecks(t *testing.T) {
@@ -21,7 +20,7 @@ func TestSignatureChecks(t *testing.T) {
// generic args
ctx := stack.NewContext("test-chain", log.NewNopLogger())
store := state.NewMemKVStore()
raw := txs.NewRaw([]byte{1, 2, 3, 4})
raw := stack.NewRawTx([]byte{1, 2, 3, 4})
// let's make some keys....
priv1 := crypto.GenPrivKeyEd25519().Wrap()
@@ -65,16 +64,16 @@ func TestSignatureChecks(t *testing.T) {
var tx basecoin.Tx
// this does the signing as needed
if tc.useMultiSig {
mtx := txs.NewMulti(raw)
mtx := NewMulti(raw)
for _, k := range tc.keys {
err := txs.Sign(mtx, k)
err := Sign(mtx, k)
assert.Nil(err, "%d: %+v", i, err)
}
tx = mtx.Wrap()
} else {
otx := txs.NewSig(raw)
otx := NewSig(raw)
for _, k := range tc.keys {
err := txs.Sign(otx, k)
err := Sign(otx, k)
assert.Nil(err, "%d: %+v", i, err)
}
tx = otx.Wrap()
+212
View File
@@ -0,0 +1,212 @@
/*
package auth contains generic Signable implementations that can be used
by your application or tests to handle authentication needs.
It currently supports transaction data as opaque bytes and either single
or multiple private key signatures using straightforward algorithms.
It currently does not support N-of-M key share signing of other more
complex algorithms (although it would be great to add them).
You can create them with NewSig() and NewMultiSig(), and they fulfill
the keys.Signable interface. You can then .Wrap() them to create
a basecoin.Tx.
*/
package auth
import (
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/go-crypto/keys"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/errors"
)
// nolint
const (
// for signatures
ByteSingleTx = 0x16
ByteMultiSig = 0x17
)
// nolint
const (
// for signatures
TypeSingleTx = NameSigs + "/one"
TypeMultiSig = NameSigs + "/multi"
)
// Signed holds one signature of the data
type Signed struct {
Sig crypto.Signature
Pubkey crypto.PubKey
}
// Empty returns true if there is not enough signature info
func (s Signed) Empty() bool {
return s.Sig.Empty() || s.Pubkey.Empty()
}
/**** Registration ****/
func init() {
basecoin.TxMapper.
RegisterImplementation(&OneSig{}, TypeSingleTx, ByteSingleTx).
RegisterImplementation(&MultiSig{}, TypeMultiSig, ByteMultiSig)
}
/**** One Sig ****/
// OneSig lets us wrap arbitrary data with a go-crypto signature
type OneSig struct {
Tx basecoin.Tx `json:"tx"`
Signed `json:"signature"`
}
var _ keys.Signable = &OneSig{}
var _ basecoin.TxLayer = &OneSig{}
// NewSig wraps the tx with a Signable that accepts exactly one signature
func NewSig(tx basecoin.Tx) *OneSig {
return &OneSig{Tx: tx}
}
func (s *OneSig) Wrap() basecoin.Tx {
return basecoin.Tx{s}
}
func (s *OneSig) Next() basecoin.Tx {
return s.Tx
}
func (s *OneSig) ValidateBasic() error {
return s.Tx.ValidateBasic()
}
// TxBytes returns the full data with signatures
func (s *OneSig) TxBytes() ([]byte, error) {
return data.ToWire(s.Wrap())
}
// SignBytes returns the original data passed into `NewSig`
func (s *OneSig) SignBytes() []byte {
res, err := data.ToWire(s.Tx)
if err != nil {
panic(err)
}
return res
}
// Sign will add a signature and pubkey.
//
// Depending on the Signable, one may be able to call this multiple times for multisig
// Returns error if called with invalid data or too many times
func (s *OneSig) Sign(pubkey crypto.PubKey, sig crypto.Signature) error {
signed := Signed{sig, pubkey}
if signed.Empty() {
return errors.ErrMissingSignature()
}
if !s.Empty() {
return errors.ErrTooManySignatures()
}
// set the value once we are happy
s.Signed = signed
return nil
}
// Signers will return the public key(s) that signed if the signature
// is valid, or an error if there is any issue with the signature,
// including if there are no signatures
func (s *OneSig) Signers() ([]crypto.PubKey, error) {
if s.Empty() {
return nil, errors.ErrMissingSignature()
}
if !s.Pubkey.VerifyBytes(s.SignBytes(), s.Sig) {
return nil, errors.ErrInvalidSignature()
}
return []crypto.PubKey{s.Pubkey}, nil
}
/**** MultiSig ****/
// MultiSig lets us wrap arbitrary data with a go-crypto signature
type MultiSig struct {
Tx basecoin.Tx `json:"tx"`
Sigs []Signed `json:"signatures"`
}
var _ keys.Signable = &MultiSig{}
var _ basecoin.TxLayer = &MultiSig{}
// NewMulti wraps the tx with a Signable that accepts arbitrary numbers of signatures
func NewMulti(tx basecoin.Tx) *MultiSig {
return &MultiSig{Tx: tx}
}
func (s *MultiSig) Wrap() basecoin.Tx {
return basecoin.Tx{s}
}
func (s *MultiSig) Next() basecoin.Tx {
return s.Tx
}
func (s *MultiSig) ValidateBasic() error {
return s.Tx.ValidateBasic()
}
// TxBytes returns the full data with signatures
func (s *MultiSig) TxBytes() ([]byte, error) {
return data.ToWire(s.Wrap())
}
// SignBytes returns the original data passed into `NewSig`
func (s *MultiSig) SignBytes() []byte {
res, err := data.ToWire(s.Tx)
if err != nil {
panic(err)
}
return res
}
// Sign will add a signature and pubkey.
//
// Depending on the Signable, one may be able to call this multiple times for multisig
// Returns error if called with invalid data or too many times
func (s *MultiSig) Sign(pubkey crypto.PubKey, sig crypto.Signature) error {
signed := Signed{sig, pubkey}
if signed.Empty() {
return errors.ErrMissingSignature()
}
// set the value once we are happy
s.Sigs = append(s.Sigs, signed)
return nil
}
// Signers will return the public key(s) that signed if the signature
// is valid, or an error if there is any issue with the signature,
// including if there are no signatures
func (s *MultiSig) Signers() ([]crypto.PubKey, error) {
if len(s.Sigs) == 0 {
return nil, errors.ErrMissingSignature()
}
// verify all the signatures before returning them
keys := make([]crypto.PubKey, len(s.Sigs))
data := s.SignBytes()
for i := range s.Sigs {
ms := s.Sigs[i]
if !ms.Pubkey.VerifyBytes(data, ms.Sig) {
return nil, errors.ErrInvalidSignature()
}
keys[i] = ms.Pubkey
}
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)
}
+159
View File
@@ -0,0 +1,159 @@
package auth
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/basecoin/stack"
crypto "github.com/tendermint/go-crypto"
keys "github.com/tendermint/go-crypto/keys"
"github.com/tendermint/go-crypto/keys/cryptostore"
"github.com/tendermint/go-crypto/keys/storage/memstorage"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/basecoin"
)
func checkSignBytes(t *testing.T, bytes []byte, expected string) {
// load it back... unwrap the tx
var preTx basecoin.Tx
err := wire.ReadBinaryBytes(bytes, &preTx)
require.Nil(t, err)
// now make sure this tx is data.Bytes with the info we want
raw, ok := preTx.Unwrap().(stack.RawTx)
require.True(t, ok)
assert.Equal(t, expected, string(raw.Bytes))
}
func TestOneSig(t *testing.T) {
assert, require := assert.New(t), require.New(t)
algo := crypto.NameEd25519
cstore := cryptostore.New(
cryptostore.SecretBox,
memstorage.New(),
keys.MustLoadCodec("english"),
)
n, p := "foo", "bar"
n2, p2 := "other", "thing"
acct, _, err := cstore.Create(n, p, algo)
require.Nil(err, "%+v", err)
acct2, _, err := cstore.Create(n2, p2, algo)
require.Nil(err, "%+v", err)
cases := []struct {
data string
key keys.Info
name, pass string
}{
{"first", acct, n, p},
{"kehfkhefy8y", acct, n, p},
{"second", acct2, n2, p2},
}
for _, tc := range cases {
inner := stack.NewRawTx([]byte(tc.data)).Wrap()
tx := NewSig(inner)
// unsigned version
_, err = tx.Signers()
assert.NotNil(err)
orig, err := tx.TxBytes()
require.Nil(err, "%+v", err)
data := tx.SignBytes()
checkSignBytes(t, data, tc.data)
// sign it
err = cstore.Sign(tc.name, tc.pass, tx)
require.Nil(err, "%+v", err)
// but not twice
err = cstore.Sign(tc.name, tc.pass, tx)
require.NotNil(err)
// make sure it is proper now
sigs, err := tx.Signers()
require.Nil(err, "%+v", err)
if assert.Equal(1, len(sigs)) {
// This must be refactored...
assert.Equal(tc.key.PubKey, sigs[0])
}
// the tx bytes should change after this
after, err := tx.TxBytes()
require.Nil(err, "%+v", err)
assert.NotEqual(orig, after, "%X != %X", orig, after)
// sign bytes are the same
data = tx.SignBytes()
checkSignBytes(t, data, tc.data)
}
}
func TestMultiSig(t *testing.T) {
assert, require := assert.New(t), require.New(t)
algo := crypto.NameEd25519
cstore := cryptostore.New(
cryptostore.SecretBox,
memstorage.New(),
keys.MustLoadCodec("english"),
)
n, p := "foo", "bar"
n2, p2 := "other", "thing"
acct, _, err := cstore.Create(n, p, algo)
require.Nil(err, "%+v", err)
acct2, _, err := cstore.Create(n2, p2, algo)
require.Nil(err, "%+v", err)
type signer struct {
key keys.Info
name, pass string
}
cases := []struct {
data string
signers []signer
}{
{"one", []signer{{acct, n, p}}},
{"two", []signer{{acct2, n2, p2}}},
{"both", []signer{{acct, n, p}, {acct2, n2, p2}}},
}
for _, tc := range cases {
inner := stack.NewRawTx([]byte(tc.data)).Wrap()
tx := NewMulti(inner)
// unsigned version
_, err = tx.Signers()
assert.NotNil(err)
orig, err := tx.TxBytes()
require.Nil(err, "%+v", err)
data := tx.SignBytes()
checkSignBytes(t, data, tc.data)
// sign it
for _, s := range tc.signers {
err = cstore.Sign(s.name, s.pass, tx)
require.Nil(err, "%+v", err)
}
// make sure it is proper now
sigs, err := tx.Signers()
require.Nil(err, "%+v", err)
if assert.Equal(len(tc.signers), len(sigs)) {
for i := range sigs {
// This must be refactored...
assert.Equal(tc.signers[i].key.PubKey, sigs[i])
}
}
// the tx bytes should change after this
after, err := tx.TxBytes()
require.Nil(err, "%+v", err)
assert.NotEqual(orig, after, "%X != %X", orig, after)
// sign bytes are the same
data = tx.SignBytes()
checkSignBytes(t, data, tc.data)
}
}
+3 -4
View File
@@ -5,12 +5,11 @@ import (
"github.com/tendermint/basecoin/errors"
"github.com/tendermint/basecoin/stack"
"github.com/tendermint/basecoin/state"
"github.com/tendermint/basecoin/txs"
)
//nolint
const (
NameChain = "chan"
NameChain = "chain"
)
// Chain enforces that this tx was bound to the named chain
@@ -43,9 +42,9 @@ func (c Chain) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.
return next.DeliverTx(ctx, store, stx)
}
// checkChain makes sure the tx is a txs.Chain and
// checkChain makes sure the tx is a Chain Tx and is on the proper chain
func (c Chain) checkChain(chainID string, tx basecoin.Tx) (basecoin.Tx, error) {
ctx, ok := tx.Unwrap().(*txs.Chain)
ctx, ok := tx.Unwrap().(ChainTx)
if !ok {
return tx, errors.ErrNoChain()
}
+3 -4
View File
@@ -11,7 +11,6 @@ import (
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/stack"
"github.com/tendermint/basecoin/state"
"github.com/tendermint/basecoin/txs"
)
func TestChain(t *testing.T) {
@@ -19,14 +18,14 @@ func TestChain(t *testing.T) {
msg := "got it"
chainID := "my-chain"
raw := txs.NewRaw([]byte{1, 2, 3, 4})
raw := stack.NewRawTx([]byte{1, 2, 3, 4})
cases := []struct {
tx basecoin.Tx
valid bool
errorMsg string
}{
{txs.NewChain(chainID, raw), true, ""},
{txs.NewChain("someone-else", raw), false, "someone-else"},
{NewChainTx(chainID, raw), true, ""},
{NewChainTx("someone-else", raw), false, "someone-else"},
{raw, false, "No chain id provided"},
}
+2 -3
View File
@@ -9,7 +9,6 @@ import (
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/stack"
"github.com/tendermint/basecoin/state"
"github.com/tendermint/basecoin/txs"
)
//nolint
@@ -31,7 +30,7 @@ var _ stack.Middleware = Multiplexer{}
// CheckTx splits the input tx and checks them all - fulfills Middlware interface
func (Multiplexer) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
if mtx, ok := tx.Unwrap().(*txs.MultiTx); ok {
if mtx, ok := tx.Unwrap().(*MultiTx); ok {
return runAll(ctx, store, mtx.Txs, next.CheckTx)
}
return next.CheckTx(ctx, store, tx)
@@ -39,7 +38,7 @@ func (Multiplexer) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoi
// DeliverTx splits the input tx and checks them all - fulfills Middlware interface
func (Multiplexer) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
if mtx, ok := tx.Unwrap().(*txs.MultiTx); ok {
if mtx, ok := tx.Unwrap().(*MultiTx); ok {
return runAll(ctx, store, mtx.Txs, next.DeliverTx)
}
return next.DeliverTx(ctx, store, tx)
+66
View File
@@ -0,0 +1,66 @@
package base
import "github.com/tendermint/basecoin"
// nolint
const (
// for utils...
ByteMultiTx = 0x2
ByteChainTx = 0x3
)
//nolint
const (
TypeMultiTx = NameMultiplexer + "/tx"
TypeChainTx = NameChain + "/tx"
)
func init() {
basecoin.TxMapper.
RegisterImplementation(MultiTx{}, TypeMultiTx, ByteMultiTx).
RegisterImplementation(ChainTx{}, TypeChainTx, ByteChainTx)
}
/**** MultiTx ******/
type MultiTx struct {
Txs []basecoin.Tx `json:"txs"`
}
func NewMultiTx(txs ...basecoin.Tx) basecoin.Tx {
return (MultiTx{Txs: txs}).Wrap()
}
func (mt MultiTx) Wrap() basecoin.Tx {
return basecoin.Tx{mt}
}
func (mt MultiTx) ValidateBasic() error {
for _, t := range mt.Txs {
err := t.ValidateBasic()
if err != nil {
return err
}
}
return nil
}
/*** ChainTx ****/
// ChainTx locks this tx to one chainTx, wrap with this before signing
type ChainTx struct {
Tx basecoin.Tx `json:"tx"`
ChainID string `json:"chain_id"`
}
func NewChainTx(chainID string, tx basecoin.Tx) basecoin.Tx {
return (ChainTx{Tx: tx, ChainID: chainID}).Wrap()
}
func (c ChainTx) Wrap() basecoin.Tx {
return basecoin.Tx{c}
}
func (c ChainTx) ValidateBasic() error {
// TODO: more checks? chainID?
return c.Tx.ValidateBasic()
}
+51
View File
@@ -0,0 +1,51 @@
package base
import (
"strconv"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/basecoin/stack"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/basecoin"
)
func TestEncoding(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
raw := stack.NewRawTx([]byte{0x34, 0xa7})
raw2 := stack.NewRawTx([]byte{0x73, 0x86, 0x22})
cases := []struct {
Tx basecoin.Tx
}{
{raw},
{NewMultiTx(raw, raw2)},
{NewChainTx("foobar", raw)},
}
for idx, tc := range cases {
i := strconv.Itoa(idx)
tx := tc.Tx
// test json in and out
js, err := data.ToJSON(tx)
require.Nil(err, i)
var jtx basecoin.Tx
err = data.FromJSON(js, &jtx)
require.Nil(err, i)
assert.Equal(tx, jtx, i)
// test wire in and out
bin, err := data.ToWire(tx)
require.Nil(err, i)
var wtx basecoin.Tx
err = data.FromWire(bin, &wtx)
require.Nil(err, i)
assert.Equal(tx, wtx, i)
}
}