Skip to content

Commit 114de63

Browse files
aaroncAlessio Treglia
authored and
Alessio Treglia
committed
Allow custom key types and address formats (#4232)
Add additional parameter to NewAnteHandler for custom SignatureVerificationGasConsumer (the existing one is now called DefaultSigVerificationGasConsumer). Add addressVerifier field to sdk.Config which allows for custom address verification (to override the current fixed 20 byte address format). DefaultSigVerificationGasConsumer now uses type switching as opposed to string comparison. Other zones like Ethermint can now concretely specify which key types they accept. Closes: #3685
1 parent 67f1e12 commit 114de63

File tree

11 files changed

+154
-40
lines changed

11 files changed

+154
-40
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#3685 The default signature verification gas logic (`DefaultSigVerificationGasConsumer`) now specifies explicit key types rather than string pattern matching. This means that zones that depended on string matching to allow other keys will need to write a custom `SignatureVerificationGasConsumer` function.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#3685 Add `SetAddressVerifier` and `GetAddressVerifier` to `sdk.Config` to allow SDK users to configure custom address format verification logic (to override the default limitation of 20-byte addresses).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#3685 Add an additional parameter to NewAnteHandler for a custom `SignatureVerificationGasConsumer` (the default logic is now in `DefaultSigVerificationGasConsumer). This allows SDK users to configure their own logic for which key types are accepted and how those key types consume gas.

cmd/gaia/app/app.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b
195195
)
196196
app.SetInitChainer(app.initChainer)
197197
app.SetBeginBlocker(app.BeginBlocker)
198-
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper))
198+
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper, auth.DefaultSigVerificationGasConsumer))
199199
app.SetEndBlocker(app.EndBlocker)
200200

201201
if loadLatest {

cmd/gaia/cmd/gaiadebug/hack.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp
191191
app.SetInitChainer(app.initChainer)
192192
app.SetBeginBlocker(app.BeginBlocker)
193193
app.SetEndBlocker(app.EndBlocker)
194-
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper))
194+
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper, auth.DefaultSigVerificationGasConsumer))
195195
app.MountStores(app.keyMain, app.keyAccount, app.keyStaking, app.keySlashing, app.keyParams)
196196
app.MountStore(app.tkeyParams, sdk.StoreTypeTransient)
197197
err := app.LoadLatestVersion(app.keyMain)

types/address.go

+24-6
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,21 @@ func AccAddressFromHex(address string) (addr AccAddress, err error) {
8686
return AccAddress(bz), nil
8787
}
8888

89+
// VerifyAddressFormat verifies that the provided bytes form a valid address
90+
// according to the default address rules or a custom address verifier set by
91+
// GetConfig().SetAddressVerifier()
92+
func VerifyAddressFormat(bz []byte) error {
93+
verifier := GetConfig().GetAddressVerifier()
94+
if verifier != nil {
95+
return verifier(bz)
96+
} else {
97+
if len(bz) != AddrLen {
98+
return errors.New("Incorrect address length")
99+
}
100+
}
101+
return nil
102+
}
103+
89104
// AccAddressFromBech32 creates an AccAddress from a Bech32 string.
90105
func AccAddressFromBech32(address string) (addr AccAddress, err error) {
91106
if len(strings.TrimSpace(address)) == 0 {
@@ -99,8 +114,9 @@ func AccAddressFromBech32(address string) (addr AccAddress, err error) {
99114
return nil, err
100115
}
101116

102-
if len(bz) != AddrLen {
103-
return nil, errors.New("Incorrect address length")
117+
err = VerifyAddressFormat(bz)
118+
if err != nil {
119+
return nil, err
104120
}
105121

106122
return AccAddress(bz), nil
@@ -229,8 +245,9 @@ func ValAddressFromBech32(address string) (addr ValAddress, err error) {
229245
return nil, err
230246
}
231247

232-
if len(bz) != AddrLen {
233-
return nil, errors.New("Incorrect address length")
248+
err = VerifyAddressFormat(bz)
249+
if err != nil {
250+
return nil, err
234251
}
235252

236253
return ValAddress(bz), nil
@@ -360,8 +377,9 @@ func ConsAddressFromBech32(address string) (addr ConsAddress, err error) {
360377
return nil, err
361378
}
362379

363-
if len(bz) != AddrLen {
364-
return nil, errors.New("Incorrect address length")
380+
err = VerifyAddressFormat(bz)
381+
if err != nil {
382+
return nil, err
365383
}
366384

367385
return ConsAddress(bz), nil

types/address_test.go

+37
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package types_test
22

33
import (
44
"encoding/hex"
5+
"fmt"
56
"math/rand"
67
"testing"
78

@@ -290,3 +291,39 @@ func TestAddressInterface(t *testing.T) {
290291
}
291292

292293
}
294+
295+
func TestCustomAddressVerifier(t *testing.T) {
296+
// Create a 10 byte address
297+
addr := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
298+
accBech := types.AccAddress(addr).String()
299+
valBech := types.ValAddress(addr).String()
300+
consBech := types.ConsAddress(addr).String()
301+
// Verifiy that the default logic rejects this 10 byte address
302+
err := types.VerifyAddressFormat(addr)
303+
require.NotNil(t, err)
304+
_, err = types.AccAddressFromBech32(accBech)
305+
require.NotNil(t, err)
306+
_, err = types.ValAddressFromBech32(valBech)
307+
require.NotNil(t, err)
308+
_, err = types.ConsAddressFromBech32(consBech)
309+
require.NotNil(t, err)
310+
311+
// Set a custom address verifier that accepts 10 or 20 byte addresses
312+
types.GetConfig().SetAddressVerifier(func(bz []byte) error {
313+
n := len(bz)
314+
if n == 10 || n == types.AddrLen {
315+
return nil
316+
}
317+
return fmt.Errorf("incorrect address length %d", n)
318+
})
319+
320+
// Verifiy that the custom logic accepts this 10 byte address
321+
err = types.VerifyAddressFormat(addr)
322+
require.Nil(t, err)
323+
_, err = types.AccAddressFromBech32(accBech)
324+
require.Nil(t, err)
325+
_, err = types.ValAddressFromBech32(valBech)
326+
require.Nil(t, err)
327+
_, err = types.ConsAddressFromBech32(consBech)
328+
require.Nil(t, err)
329+
}

types/config.go

+13
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type Config struct {
1111
sealed bool
1212
bech32AddressPrefix map[string]string
1313
txEncoder TxEncoder
14+
addressVerifier func([]byte) error
1415
}
1516

1617
var (
@@ -73,6 +74,13 @@ func (config *Config) SetTxEncoder(encoder TxEncoder) {
7374
config.txEncoder = encoder
7475
}
7576

77+
// SetAddressVerifier builds the Config with the provided function for verifying that addresses
78+
// have the correct format
79+
func (config *Config) SetAddressVerifier(addressVerifier func([]byte) error) {
80+
config.assertNotSealed()
81+
config.addressVerifier = addressVerifier
82+
}
83+
7684
// Seal seals the config such that the config state could not be modified further
7785
func (config *Config) Seal() *Config {
7886
config.mtx.Lock()
@@ -116,3 +124,8 @@ func (config *Config) GetBech32ConsensusPubPrefix() string {
116124
func (config *Config) GetTxEncoder() TxEncoder {
117125
return config.txEncoder
118126
}
127+
128+
// GetAddressVerifier returns the function to verify that addresses have the correct format
129+
func (config *Config) GetAddressVerifier() func([]byte) error {
130+
return config.addressVerifier
131+
}

x/auth/ante.go

+19-20
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"bytes"
55
"encoding/hex"
66
"fmt"
7-
"strings"
7+
"github.com/tendermint/tendermint/crypto/ed25519"
88
"time"
99

1010
"github.com/tendermint/tendermint/crypto"
@@ -27,10 +27,14 @@ func init() {
2727
copy(simSecp256k1Pubkey[:], bz)
2828
}
2929

30+
// SignatureVerificationGasConsumer is the type of function that is used to both consume gas when verifying signatures
31+
// and also to accept or reject different types of PubKey's. This is where apps can define their own PubKey types.
32+
type SignatureVerificationGasConsumer = func(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params Params) sdk.Result
33+
3034
// NewAnteHandler returns an AnteHandler that checks and increments sequence
3135
// numbers, checks signatures & account numbers, and deducts fees from the first
3236
// signer.
33-
func NewAnteHandler(ak AccountKeeper, fck FeeCollectionKeeper) sdk.AnteHandler {
37+
func NewAnteHandler(ak AccountKeeper, fck FeeCollectionKeeper, sigGasConsumer SignatureVerificationGasConsumer) sdk.AnteHandler {
3438
return func(
3539
ctx sdk.Context, tx sdk.Tx, simulate bool,
3640
) (newCtx sdk.Context, res sdk.Result, abort bool) {
@@ -127,7 +131,7 @@ func NewAnteHandler(ak AccountKeeper, fck FeeCollectionKeeper) sdk.AnteHandler {
127131

128132
// check signature, return account with incremented nonce
129133
signBytes := GetSignBytes(newCtx.ChainID(), stdTx, signerAccs[i], isGenesis)
130-
signerAccs[i], res = processSig(newCtx, signerAccs[i], stdSigs[i], signBytes, simulate, params)
134+
signerAccs[i], res = processSig(newCtx, signerAccs[i], stdSigs[i], signBytes, simulate, params, sigGasConsumer)
131135
if !res.IsOK() {
132136
return newCtx, res, true
133137
}
@@ -168,6 +172,7 @@ func ValidateMemo(stdTx StdTx, params Params) sdk.Result {
168172
// a pubkey, set it.
169173
func processSig(
170174
ctx sdk.Context, acc Account, sig StdSignature, signBytes []byte, simulate bool, params Params,
175+
sigGasConsumer SignatureVerificationGasConsumer,
171176
) (updatedAcc Account, res sdk.Result) {
172177

173178
pubKey, res := ProcessPubKey(acc, sig, simulate)
@@ -188,7 +193,7 @@ func processSig(
188193
consumeSimSigGas(ctx.GasMeter(), pubKey, sig, params)
189194
}
190195

191-
if res := consumeSigVerificationGas(ctx.GasMeter(), sig.Signature, pubKey, params); !res.IsOK() {
196+
if res := sigGasConsumer(ctx.GasMeter(), sig.Signature, pubKey, params); !res.IsOK() {
192197
return nil, res
193198
}
194199

@@ -254,36 +259,30 @@ func ProcessPubKey(acc Account, sig StdSignature, simulate bool) (crypto.PubKey,
254259
return pubKey, sdk.Result{}
255260
}
256261

257-
// consumeSigVerificationGas consumes gas for signature verification based upon
258-
// the public key type. The cost is fetched from the given params and is matched
262+
// DefaultSigVerificationGasConsumer is the default implementation of SignatureVerificationGasConsumer. It consumes gas
263+
// for signature verification based upon the public key type. The cost is fetched from the given params and is matched
259264
// by the concrete type.
260-
//
261-
// TODO: Design a cleaner and flexible way to match concrete public key types.
262-
func consumeSigVerificationGas(
265+
func DefaultSigVerificationGasConsumer(
263266
meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params Params,
264267
) sdk.Result {
265-
266-
pubkeyType := strings.ToLower(fmt.Sprintf("%T", pubkey))
267-
268-
switch {
269-
case strings.Contains(pubkeyType, "ed25519"):
268+
switch pubkey := pubkey.(type) {
269+
case ed25519.PubKeyEd25519:
270270
meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519")
271271
return sdk.ErrInvalidPubKey("ED25519 public keys are unsupported").Result()
272272

273-
case strings.Contains(pubkeyType, "secp256k1"):
273+
case secp256k1.PubKeySecp256k1:
274274
meter.ConsumeGas(params.SigVerifyCostSecp256k1, "ante verify: secp256k1")
275275
return sdk.Result{}
276276

277-
case strings.Contains(pubkeyType, "multisigthreshold"):
277+
case multisig.PubKeyMultisigThreshold:
278278
var multisignature multisig.Multisignature
279279
codec.Cdc.MustUnmarshalBinaryBare(sig, &multisignature)
280280

281-
multisigPubKey := pubkey.(multisig.PubKeyMultisigThreshold)
282-
consumeMultisignatureVerificationGas(meter, multisignature, multisigPubKey, params)
281+
consumeMultisignatureVerificationGas(meter, multisignature, pubkey, params)
283282
return sdk.Result{}
284283

285284
default:
286-
return sdk.ErrInvalidPubKey(fmt.Sprintf("unrecognized public key type: %s", pubkeyType)).Result()
285+
return sdk.ErrInvalidPubKey(fmt.Sprintf("unrecognized public key type: %T", pubkey)).Result()
287286
}
288287
}
289288

@@ -295,7 +294,7 @@ func consumeMultisignatureVerificationGas(meter sdk.GasMeter,
295294
sigIndex := 0
296295
for i := 0; i < size; i++ {
297296
if sig.BitArray.GetIndex(i) {
298-
consumeSigVerificationGas(meter, sig.Sigs[sigIndex], pubkey.PubKeys[i], params)
297+
DefaultSigVerificationGasConsumer(meter, sig.Sigs[sigIndex], pubkey.PubKeys[i], params)
299298
sigIndex++
300299
}
301300
}

0 commit comments

Comments
 (0)