commit
2654959414
|
@ -12,6 +12,7 @@ FEATURES
|
||||||
* [gaiad] Added `gaiad export` command to export current state to JSON
|
* [gaiad] Added `gaiad export` command to export current state to JSON
|
||||||
* [x/bank] Tx tags with sender/recipient for indexing & later retrieval
|
* [x/bank] Tx tags with sender/recipient for indexing & later retrieval
|
||||||
* [x/stake] Tx tags with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy
|
* [x/stake] Tx tags with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy
|
||||||
|
* [x/auth] Added ability to change pubkey to auth module
|
||||||
|
|
||||||
IMPROVEMENTS
|
IMPROVEMENTS
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
|
||||||
|
|
||||||
// register message routes
|
// register message routes
|
||||||
app.Router().
|
app.Router().
|
||||||
|
AddRoute("auth", auth.NewHandler(app.accountMapper.(auth.AccountMapper))).
|
||||||
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
|
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
|
||||||
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
|
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
|
||||||
AddRoute("stake", stake.NewHandler(app.stakeKeeper))
|
AddRoute("stake", stake.NewHandler(app.stakeKeeper))
|
||||||
|
|
|
@ -208,6 +208,61 @@ func TestGenesis(t *testing.T) {
|
||||||
assert.Equal(t, acc, res1)
|
assert.Equal(t, acc, res1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMsgChangePubKey(t *testing.T) {
|
||||||
|
|
||||||
|
bapp := newBasecoinApp()
|
||||||
|
|
||||||
|
// Construct some genesis bytes to reflect basecoin/types/AppAccount
|
||||||
|
// Give 77 foocoin to the first key
|
||||||
|
coins, err := sdk.ParseCoins("77foocoin")
|
||||||
|
require.Nil(t, err)
|
||||||
|
baseAcc := auth.BaseAccount{
|
||||||
|
Address: addr1,
|
||||||
|
Coins: coins,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct genesis state
|
||||||
|
err = setGenesisAccounts(bapp, baseAcc)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
// A checkTx context (true)
|
||||||
|
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||||
|
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||||
|
assert.Equal(t, baseAcc, res1.(*types.AppAccount).BaseAccount)
|
||||||
|
|
||||||
|
// Run a CheckDeliver
|
||||||
|
SignCheckDeliver(t, bapp, sendMsg1, []int64{0}, true, priv1)
|
||||||
|
|
||||||
|
// Check balances
|
||||||
|
CheckBalance(t, bapp, addr1, "67foocoin")
|
||||||
|
CheckBalance(t, bapp, addr2, "10foocoin")
|
||||||
|
|
||||||
|
changePubKeyMsg := auth.MsgChangeKey{
|
||||||
|
Address: addr1,
|
||||||
|
NewPubKey: priv2.PubKey(),
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
|
||||||
|
acc := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
|
||||||
|
|
||||||
|
// send a MsgChangePubKey
|
||||||
|
SignCheckDeliver(t, bapp, changePubKeyMsg, []int64{1}, true, priv1)
|
||||||
|
acc = bapp.accountMapper.GetAccount(ctxDeliver, addr1)
|
||||||
|
|
||||||
|
assert.True(t, priv2.PubKey().Equals(acc.GetPubKey()))
|
||||||
|
|
||||||
|
// signing a SendMsg with the old privKey should be an auth error
|
||||||
|
tx := genTx(sendMsg1, []int64{2}, priv1)
|
||||||
|
res := bapp.Deliver(tx)
|
||||||
|
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||||
|
|
||||||
|
// resigning the tx with the new correct priv key should work
|
||||||
|
SignCheckDeliver(t, bapp, sendMsg1, []int64{2}, true, priv2)
|
||||||
|
|
||||||
|
// Check balances
|
||||||
|
CheckBalance(t, bapp, addr1, "57foocoin")
|
||||||
|
CheckBalance(t, bapp, addr2, "20foocoin")
|
||||||
|
}
|
||||||
|
|
||||||
func TestMsgSendWithAccounts(t *testing.T) {
|
func TestMsgSendWithAccounts(t *testing.T) {
|
||||||
bapp := newBasecoinApp()
|
bapp := newBasecoinApp()
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
// NewAnteHandler returns an AnteHandler that checks
|
// NewAnteHandler returns an AnteHandler that checks
|
||||||
// and increments sequence numbers, checks signatures,
|
// and increments sequence numbers, checks signatures,
|
||||||
// and deducts fees from the first signer.
|
// and deducts fees from the first signer.
|
||||||
func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler) sdk.AnteHandler {
|
func NewAnteHandler(am sdk.AccountMapper, feeHandler sdk.FeeHandler) sdk.AnteHandler {
|
||||||
return func(
|
return func(
|
||||||
ctx sdk.Context, tx sdk.Tx,
|
ctx sdk.Context, tx sdk.Tx,
|
||||||
) (_ sdk.Context, _ sdk.Result, abort bool) {
|
) (_ sdk.Context, _ sdk.Result, abort bool) {
|
||||||
|
@ -24,7 +24,6 @@ func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: can tx just implement message?
|
|
||||||
msg := tx.GetMsg()
|
msg := tx.GetMsg()
|
||||||
|
|
||||||
// TODO: will this always be a stdtx? should that be used in the function signature?
|
// TODO: will this always be a stdtx? should that be used in the function signature?
|
||||||
|
@ -62,7 +61,7 @@ func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler)
|
||||||
|
|
||||||
// check signature, return account with incremented nonce
|
// check signature, return account with incremented nonce
|
||||||
signerAcc, res := processSig(
|
signerAcc, res := processSig(
|
||||||
ctx, accountMapper,
|
ctx, am,
|
||||||
signerAddr, sig, signBytes,
|
signerAddr, sig, signBytes,
|
||||||
)
|
)
|
||||||
if !res.IsOK() {
|
if !res.IsOK() {
|
||||||
|
@ -82,7 +81,7 @@ func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the account.
|
// Save the account.
|
||||||
accountMapper.SetAccount(ctx, signerAcc)
|
am.SetAccount(ctx, signerAcc)
|
||||||
signerAccs[i] = signerAcc
|
signerAccs[i] = signerAcc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,9 +51,6 @@ func (acc BaseAccount) GetPubKey() crypto.PubKey {
|
||||||
|
|
||||||
// Implements sdk.Account.
|
// Implements sdk.Account.
|
||||||
func (acc *BaseAccount) SetPubKey(pubKey crypto.PubKey) error {
|
func (acc *BaseAccount) SetPubKey(pubKey crypto.PubKey) error {
|
||||||
if acc.PubKey != nil {
|
|
||||||
return errors.New("cannot override BaseAccount pubkey")
|
|
||||||
}
|
|
||||||
acc.PubKey = pubKey
|
acc.PubKey = pubKey
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,10 @@ func TestBaseAccountAddressPubKey(t *testing.T) {
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, pub1, acc.GetPubKey())
|
assert.Equal(t, pub1, acc.GetPubKey())
|
||||||
|
|
||||||
// can't override pubkey
|
// can override pubkey
|
||||||
err = acc.SetPubKey(pub2)
|
err = acc.SetPubKey(pub2)
|
||||||
assert.NotNil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, pub1, acc.GetPubKey())
|
assert.Equal(t, pub2, acc.GetPubKey())
|
||||||
|
|
||||||
//------------------------------------
|
//------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewHandler returns a handler for "auth" type messages.
|
||||||
|
func NewHandler(am AccountMapper) sdk.Handler {
|
||||||
|
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case MsgChangeKey:
|
||||||
|
return handleMsgChangeKey(ctx, am, msg)
|
||||||
|
default:
|
||||||
|
errMsg := "Unrecognized auth Msg type: " + reflect.TypeOf(msg).Name()
|
||||||
|
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle MsgChangeKey
|
||||||
|
// Should be very expensive, because once this happens, an account is un-prunable
|
||||||
|
func handleMsgChangeKey(ctx sdk.Context, am AccountMapper, msg MsgChangeKey) sdk.Result {
|
||||||
|
|
||||||
|
err := am.setPubKey(ctx, msg.Address, msg.NewPubKey)
|
||||||
|
if err != nil {
|
||||||
|
return err.Result()
|
||||||
|
}
|
||||||
|
|
||||||
|
return sdk.Result{
|
||||||
|
Tags: sdk.NewTags("action", []byte("changePubkey"), "address", msg.Address.Bytes(), "pubkey", msg.NewPubKey.Bytes()),
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,14 +6,15 @@ import (
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ sdk.AccountMapper = (*accountMapper)(nil)
|
var _ sdk.AccountMapper = (*AccountMapper)(nil)
|
||||||
|
|
||||||
// Implements sdk.AccountMapper.
|
// Implements sdk.AccountMapper.
|
||||||
// This AccountMapper encodes/decodes accounts using the
|
// This AccountMapper encodes/decodes accounts using the
|
||||||
// go-amino (binary) encoding/decoding library.
|
// go-amino (binary) encoding/decoding library.
|
||||||
type accountMapper struct {
|
type AccountMapper struct {
|
||||||
|
|
||||||
// The (unexposed) key used to access the store from the Context.
|
// The (unexposed) key used to access the store from the Context.
|
||||||
key sdk.StoreKey
|
key sdk.StoreKey
|
||||||
|
@ -28,23 +29,23 @@ type accountMapper struct {
|
||||||
// NewAccountMapper returns a new sdk.AccountMapper that
|
// NewAccountMapper returns a new sdk.AccountMapper that
|
||||||
// uses go-amino to (binary) encode and decode concrete sdk.Accounts.
|
// uses go-amino to (binary) encode and decode concrete sdk.Accounts.
|
||||||
// nolint
|
// nolint
|
||||||
func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto sdk.Account) accountMapper {
|
func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto sdk.Account) AccountMapper {
|
||||||
return accountMapper{
|
return AccountMapper{
|
||||||
key: key,
|
key: key,
|
||||||
proto: proto,
|
proto: proto,
|
||||||
cdc: cdc,
|
cdc: cdc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements sdk.AccountMapper.
|
// Implaements sdk.AccountMapper.
|
||||||
func (am accountMapper) NewAccountWithAddress(ctx sdk.Context, addr sdk.Address) sdk.Account {
|
func (am AccountMapper) NewAccountWithAddress(ctx sdk.Context, addr sdk.Address) sdk.Account {
|
||||||
acc := am.clonePrototype()
|
acc := am.clonePrototype()
|
||||||
acc.SetAddress(addr)
|
acc.SetAddress(addr)
|
||||||
return acc
|
return acc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements sdk.AccountMapper.
|
// Implements sdk.AccountMapper.
|
||||||
func (am accountMapper) GetAccount(ctx sdk.Context, addr sdk.Address) sdk.Account {
|
func (am AccountMapper) GetAccount(ctx sdk.Context, addr sdk.Address) sdk.Account {
|
||||||
store := ctx.KVStore(am.key)
|
store := ctx.KVStore(am.key)
|
||||||
bz := store.Get(addr)
|
bz := store.Get(addr)
|
||||||
if bz == nil {
|
if bz == nil {
|
||||||
|
@ -55,7 +56,7 @@ func (am accountMapper) GetAccount(ctx sdk.Context, addr sdk.Address) sdk.Accoun
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements sdk.AccountMapper.
|
// Implements sdk.AccountMapper.
|
||||||
func (am accountMapper) SetAccount(ctx sdk.Context, acc sdk.Account) {
|
func (am AccountMapper) SetAccount(ctx sdk.Context, acc sdk.Account) {
|
||||||
addr := acc.GetAddress()
|
addr := acc.GetAddress()
|
||||||
store := ctx.KVStore(am.key)
|
store := ctx.KVStore(am.key)
|
||||||
bz := am.encodeAccount(acc)
|
bz := am.encodeAccount(acc)
|
||||||
|
@ -63,7 +64,7 @@ func (am accountMapper) SetAccount(ctx sdk.Context, acc sdk.Account) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements sdk.AccountMapper.
|
// Implements sdk.AccountMapper.
|
||||||
func (am accountMapper) IterateAccounts(ctx sdk.Context, process func(sdk.Account) (stop bool)) {
|
func (am AccountMapper) IterateAccounts(ctx sdk.Context, process func(sdk.Account) (stop bool)) {
|
||||||
store := ctx.KVStore(am.key)
|
store := ctx.KVStore(am.key)
|
||||||
iter := store.Iterator(nil, nil)
|
iter := store.Iterator(nil, nil)
|
||||||
for {
|
for {
|
||||||
|
@ -79,11 +80,49 @@ func (am accountMapper) IterateAccounts(ctx sdk.Context, process func(sdk.Accoun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the PubKey of the account at address
|
||||||
|
func (am AccountMapper) GetPubKey(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, sdk.Error) {
|
||||||
|
acc := am.GetAccount(ctx, addr)
|
||||||
|
if acc == nil {
|
||||||
|
return nil, sdk.ErrUnknownAddress(addr.String())
|
||||||
|
}
|
||||||
|
return acc.GetPubKey(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am AccountMapper) setPubKey(ctx sdk.Context, addr sdk.Address, newPubKey crypto.PubKey) sdk.Error {
|
||||||
|
acc := am.GetAccount(ctx, addr)
|
||||||
|
if acc == nil {
|
||||||
|
return sdk.ErrUnknownAddress(addr.String())
|
||||||
|
}
|
||||||
|
acc.SetPubKey(newPubKey)
|
||||||
|
am.SetAccount(ctx, acc)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the Sequence of the account at address
|
||||||
|
func (am AccountMapper) GetSequence(ctx sdk.Context, addr sdk.Address) (int64, sdk.Error) {
|
||||||
|
acc := am.GetAccount(ctx, addr)
|
||||||
|
if acc == nil {
|
||||||
|
return 0, sdk.ErrUnknownAddress(addr.String())
|
||||||
|
}
|
||||||
|
return acc.GetSequence(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am AccountMapper) setSequence(ctx sdk.Context, addr sdk.Address, newSequence int64) sdk.Error {
|
||||||
|
acc := am.GetAccount(ctx, addr)
|
||||||
|
if acc == nil {
|
||||||
|
return sdk.ErrUnknownAddress(addr.String())
|
||||||
|
}
|
||||||
|
acc.SetSequence(newSequence)
|
||||||
|
am.SetAccount(ctx, acc)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
// misc.
|
// misc.
|
||||||
|
|
||||||
// Creates a new struct (or pointer to struct) from am.proto.
|
// Creates a new struct (or pointer to struct) from am.proto.
|
||||||
func (am accountMapper) clonePrototype() sdk.Account {
|
func (am AccountMapper) clonePrototype() sdk.Account {
|
||||||
protoRt := reflect.TypeOf(am.proto)
|
protoRt := reflect.TypeOf(am.proto)
|
||||||
if protoRt.Kind() == reflect.Ptr {
|
if protoRt.Kind() == reflect.Ptr {
|
||||||
protoCrt := protoRt.Elem()
|
protoCrt := protoRt.Elem()
|
||||||
|
@ -106,7 +145,7 @@ func (am accountMapper) clonePrototype() sdk.Account {
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am accountMapper) encodeAccount(acc sdk.Account) []byte {
|
func (am AccountMapper) encodeAccount(acc sdk.Account) []byte {
|
||||||
bz, err := am.cdc.MarshalBinaryBare(acc)
|
bz, err := am.cdc.MarshalBinaryBare(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -114,7 +153,7 @@ func (am accountMapper) encodeAccount(acc sdk.Account) []byte {
|
||||||
return bz
|
return bz
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am accountMapper) decodeAccount(bz []byte) (acc sdk.Account) {
|
func (am AccountMapper) decodeAccount(bz []byte) (acc sdk.Account) {
|
||||||
err := am.cdc.UnmarshalBinaryBare(bz, &acc)
|
err := am.cdc.UnmarshalBinaryBare(bz, &acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/tendermint/go-crypto"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgChangeKey - high level transaction of the auth module
|
||||||
|
type MsgChangeKey struct {
|
||||||
|
Address sdk.Address `json:"address"`
|
||||||
|
NewPubKey crypto.PubKey `json:"public_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ sdk.Msg = MsgChangeKey{}
|
||||||
|
|
||||||
|
// NewMsgChangeKey - msg to claim an account and set the PubKey
|
||||||
|
func NewMsgChangeKey(addr sdk.Address, pubkey crypto.PubKey) MsgChangeKey {
|
||||||
|
return MsgChangeKey{Address: addr, NewPubKey: pubkey}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements Msg.
|
||||||
|
func (msg MsgChangeKey) Type() string { return "auth" }
|
||||||
|
|
||||||
|
// Implements Msg.
|
||||||
|
func (msg MsgChangeKey) ValidateBasic() sdk.Error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements Msg.
|
||||||
|
func (msg MsgChangeKey) GetSignBytes() []byte {
|
||||||
|
b, err := json.Marshal(msg) // XXX: ensure some canonical form
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements Msg.
|
||||||
|
func (msg MsgChangeKey) GetSigners() []sdk.Address {
|
||||||
|
return []sdk.Address{msg.Address}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewMsgChangeKey(t *testing.T) {}
|
||||||
|
|
||||||
|
func TestMsgChangeKeyType(t *testing.T) {
|
||||||
|
addr1 := sdk.Address([]byte("input"))
|
||||||
|
newPubKey := crypto.GenPrivKeyEd25519().PubKey()
|
||||||
|
|
||||||
|
var msg = MsgChangeKey{
|
||||||
|
Address: addr1,
|
||||||
|
NewPubKey: newPubKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, msg.Type(), "auth")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMsgChangeKeyValidation(t *testing.T) {
|
||||||
|
|
||||||
|
addr1 := sdk.Address([]byte("input"))
|
||||||
|
|
||||||
|
// emptyPubKey := crypto.PubKeyEd25519{}
|
||||||
|
// var msg = MsgChangeKey{
|
||||||
|
// Address: addr1,
|
||||||
|
// NewPubKey: emptyPubKey,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // fmt.Println(msg.NewPubKey.Empty())
|
||||||
|
// fmt.Println(msg.NewPubKey.Bytes())
|
||||||
|
|
||||||
|
// assert.NotNil(t, msg.ValidateBasic())
|
||||||
|
|
||||||
|
newPubKey := crypto.GenPrivKeyEd25519().PubKey()
|
||||||
|
msg := MsgChangeKey{
|
||||||
|
Address: addr1,
|
||||||
|
NewPubKey: newPubKey,
|
||||||
|
}
|
||||||
|
assert.Nil(t, msg.ValidateBasic())
|
||||||
|
}
|
Loading…
Reference in New Issue