-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[OTE-553] Timestamp nonce #1970
Changes from 20 commits
a2d9900
42a35ef
d24dc06
741c8cd
f1a8c12
3e469e6
03b7ed1
952dfdc
207a4db
a6ae93c
3fdb5b8
9e4e31b
721f00b
0742da7
9d56920
2e193fb
23a90c2
5d057b7
5625b2e
fe97218
d866fa7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package ante | |
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
errorsmod "cosmossdk.io/errors" | ||
txsigning "cosmossdk.io/x/tx/signing" | ||
|
@@ -13,6 +14,7 @@ import ( | |
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" | ||
"github.com/dydxprotocol/v4-chain/protocol/lib/metrics" | ||
accountpluskeeper "github.com/dydxprotocol/v4-chain/protocol/x/accountplus/keeper" | ||
accountplustypes "github.com/dydxprotocol/v4-chain/protocol/x/accountplus/types" | ||
gometrics "github.com/hashicorp/go-metrics" | ||
"google.golang.org/protobuf/types/known/anypb" | ||
) | ||
|
@@ -63,7 +65,8 @@ func (svd SigVerificationDecorator) AnteHandle( | |
return ctx, err | ||
} | ||
|
||
// check that signer length and signature length are the same | ||
// Check that signer length and signature length are the same. | ||
// The ordering of the sigs and signers have matching ordering (sigs[i] belongs to signers[i]). | ||
if len(sigs) != len(signers) { | ||
err := errorsmod.Wrapf( | ||
sdkerrors.ErrUnauthorized, | ||
|
@@ -78,6 +81,7 @@ func (svd SigVerificationDecorator) AnteHandle( | |
// only messages that use `GoodTilBlock` for replay protection. | ||
skipSequenceValidation := ShouldSkipSequenceValidation(tx.GetMsgs()) | ||
|
||
// Iterate on sig and signer pairs. | ||
for i, sig := range sigs { | ||
acc, err := sdkante.GetSignerAcc(ctx, svd.ak, signers[i]) | ||
if err != nil { | ||
|
@@ -95,35 +99,28 @@ func (svd SigVerificationDecorator) AnteHandle( | |
// `GoodTilBlock` for replay protection. | ||
if !skipSequenceValidation { | ||
if accountpluskeeper.IsTimestampNonce(sig.Sequence) { | ||
tsNonce := sig.Sequence | ||
blockTs := uint64(ctx.BlockTime().UnixMilli()) | ||
address := acc.GetAddress() | ||
|
||
if !accountpluskeeper.IsValidTimestampNonce(tsNonce, blockTs) { | ||
return ctx, errorsmod.Wrapf( | ||
sdkerrors.ErrWrongSequence, | ||
"timestamp nonce %d not within valid time window", tsNonce, | ||
defer telemetry.ModuleMeasureSince( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be put within ProcessTimestampNonce so that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
A bit confused. As u said defer run after the function below is run. In this case it is right above There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A defer statement defers the execution of a function until the surrounding function returns. https://go.dev/tour/flowcontrol/12 |
||
accountplustypes.ModuleName, | ||
time.Now(), | ||
metrics.TimestampNonce, | ||
metrics.Latency, | ||
) | ||
err = svd.akp.ProcessTimestampNonce(ctx, acc, sig.Sequence) | ||
|
||
if err == nil { | ||
telemetry.IncrCounterWithLabels( | ||
[]string{metrics.TimestampNonce, metrics.Valid, metrics.Count}, | ||
1, | ||
[]gometrics.Label{metrics.GetLabelForIntValue(metrics.ExecMode, int(ctx.ExecMode()))}, | ||
) | ||
} | ||
accountState, found := svd.akp.GetAccountState(ctx, address) | ||
if !found { | ||
err := svd.akp.InitializeAccountWithTimestampNonceDetails(ctx, address, tsNonce) | ||
if err != nil { | ||
return ctx, errorsmod.Wrapf( | ||
sdkerrors.ErrLogic, | ||
fmt.Sprintf("failed to initialize AccountState for address %d", address), | ||
) | ||
} | ||
return ctx, nil | ||
} else { | ||
accountpluskeeper.EjectStaleTimestampNonces(&accountState, blockTs) | ||
tsNonceAccepted := accountpluskeeper.AttemptTimestampNonceUpdate(tsNonce, &accountState) | ||
if !tsNonceAccepted { | ||
return ctx, errorsmod.Wrapf( | ||
sdkerrors.ErrWrongSequence, | ||
"timestamp nonce %d rejected", tsNonce, | ||
) | ||
} | ||
svd.akp.SetAccountState(ctx, address, accountState) | ||
telemetry.IncrCounterWithLabels( | ||
[]string{metrics.TimestampNonce, metrics.Invalid, metrics.Count}, | ||
1, | ||
[]gometrics.Label{metrics.GetLabelForIntValue(metrics.ExecMode, int(ctx.ExecMode()))}, | ||
) | ||
return ctx, errorsmod.Wrapf(sdkerrors.ErrWrongSequence, err.Error()) | ||
} | ||
} else { | ||
if sig.Sequence != acc.GetSequence() { | ||
|
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package ante | ||
|
||
import ( | ||
errorsmod "cosmossdk.io/errors" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" | ||
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" | ||
accountpluskeeper "github.com/dydxprotocol/v4-chain/protocol/x/accountplus/keeper" | ||
) | ||
|
||
// IsTimestampNonceTx returns `true` if the supplied `tx` consist of a single signature that uses a timestamp nonce | ||
// value for sequence | ||
func IsTimestampNonceTx(ctx sdk.Context, tx sdk.Tx) (bool, error) { | ||
sigTx, ok := tx.(authsigning.SigVerifiableTx) | ||
if !ok { | ||
return false, errorsmod.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") | ||
} | ||
signatures, err := sigTx.GetSignaturesV2() | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
if len(signatures) != 1 { | ||
return false, errorsmod.Wrap(sdkerrors.ErrTxDecode, "more than one signature") | ||
} | ||
|
||
return accountpluskeeper.IsTimestampNonce(signatures[0].Sequence), nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package ante_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/cosmos/cosmos-sdk/codec" | ||
codectypes "github.com/cosmos/cosmos-sdk/codec/types" | ||
"github.com/cosmos/cosmos-sdk/testutil/testdata" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/types/tx/signing" | ||
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" | ||
"github.com/dydxprotocol/v4-chain/protocol/testutil/constants" | ||
sdktest "github.com/dydxprotocol/v4-chain/protocol/testutil/sdk" | ||
"github.com/dydxprotocol/v4-chain/protocol/x/accountplus/ante" | ||
"github.com/dydxprotocol/v4-chain/protocol/x/accountplus/keeper" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestIsTimestampNonceTx(t *testing.T) { | ||
tests := map[string]struct { | ||
seqs []uint64 | ||
expectedResult bool | ||
expectedErr bool | ||
}{ | ||
"Returns false for non-ts nonce": { | ||
seqs: []uint64{0}, | ||
expectedResult: false, | ||
expectedErr: false, | ||
}, | ||
"Returns true for ts nonce": { | ||
seqs: []uint64{keeper.TimestampNonceSequenceCutoff}, | ||
expectedResult: true, | ||
expectedErr: false, | ||
}, | ||
"Returns error for more than one signature": { | ||
seqs: []uint64{keeper.TimestampNonceSequenceCutoff, keeper.TimestampNonceSequenceCutoff}, | ||
expectedResult: false, | ||
expectedErr: true, | ||
}, | ||
} | ||
|
||
// Run tests. | ||
for name, tc := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
// Initialize some test setup which builds a test transaction from a slice of messages. | ||
var reg codectypes.InterfaceRegistry | ||
protoCfg := authtx.NewTxConfig(codec.NewProtoCodec(reg), authtx.DefaultSignModes) | ||
builder := protoCfg.NewTxBuilder() | ||
err := builder.SetMsgs([]sdk.Msg{constants.Msg_Send}...) | ||
require.NoError(t, err) | ||
|
||
// Create signatures | ||
var signatures []signing.SignatureV2 | ||
for _, seq := range tc.seqs { | ||
signatures = append(signatures, getSignature(seq)) | ||
} | ||
err = builder.SetSignatures(signatures...) | ||
|
||
require.NoError(t, err) | ||
tx := builder.GetTx() | ||
ctx, _, _ := sdktest.NewSdkContextWithMultistore() | ||
|
||
// Invoke the function under test. | ||
result, err := ante.IsTimestampNonceTx(ctx, tx) | ||
|
||
// Assert the results. | ||
if tc.expectedErr { | ||
require.NotNil(t, err) | ||
} | ||
require.Equal(t, tc.expectedResult, result) | ||
}) | ||
} | ||
} | ||
|
||
func getSignature(seq uint64) signing.SignatureV2 { | ||
_, pubKey, _ := testdata.KeyTestPubAddr() | ||
return signing.SignatureV2{ | ||
PubKey: pubKey, | ||
Data: &signing.SingleSignatureData{ | ||
SignMode: signing.SignMode_SIGN_MODE_DIRECT, | ||
Signature: nil, | ||
}, | ||
Sequence: seq, | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider logging the timestamp nonce check for better debugging.
The check for timestamp nonce transactions can be logged for better debugging and monitoring.