Skip to content

Commit

Permalink
fix: ignore iris strange trace (#127)
Browse files Browse the repository at this point in the history
insert balances fails because the amount field has non-UTF-8 characters.

This is due to a strange iris trace that is interpreted as bank balance trace with a value `\n$\n\x05uiris\x12\x1b509625143506063788050678455`. When this value is unmarshaled into a `sdk.Coin`, it doesn't trigger any error but fills the `sdk.Coin.Amount` field with invalid UTF-8 characters, leading to the database error mentioned earlier.

Actually, this iris trace is not a balance bank trace, because the format of the key is `0x02+len(addr)+addr`, whereas the balance key format is `0x02+len(addr)+addr+denom`.

So, despite we didn't manage to find the module corresponding to this trace, what appears to be the best fix is to ensure the bank processor ignores this trace.

- This commit adds tests to the existing code: edef1fc
- This commit ensures the bank processor ignores traces like the one of Iris: 8c0c7df
  • Loading branch information
tbruyelle authored Jun 1, 2022
1 parent f0701ff commit 7da0f0e
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 4 deletions.
1 change: 0 additions & 1 deletion tracelistener/processor/bank_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (

func TestBankProcessorOwnsKey(t *testing.T) {
d := bankProcessor{}

tests := []struct {
name string
prefix []byte
Expand Down
6 changes: 3 additions & 3 deletions tracelistener/processor/datamarshaler/impl_v44.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,21 @@ func (d DataMarshaler) Bank(data tracelistener.TraceOperation) (models.BalanceRo
}

if err := getCodec().Unmarshal(data.Value, &coins); err != nil {
return models.BalanceRow{}, err
return models.BalanceRow{}, fmt.Errorf("cannot unmarshal balance coin: %w", err)
}

// Since SDK 0.44.x x/bank now deletes keys from store when the balance is 0
// (picture someone who sends all their balance to another address).
// To work around this issue, we don't return when coin is invalid when data.Operation is "delete",
// and we set balance == 0 instead.
if !coins.IsValid() {
if err := coins.Validate(); err != nil {
if data.Operation == tracelistener.DeleteOp.String() {
// rawAddress still contains the length prefix, so we have to jump it by
// reading 1 byte after len(addrBytes)
denom := rawAddress[len(addrBytes)+1:]
coins.Denom = string(denom)
} else {
return models.BalanceRow{}, nil
return models.BalanceRow{}, fmt.Errorf("invalid balance coin: %w", err)
}
}

Expand Down
97 changes: 97 additions & 0 deletions tracelistener/processor/datamarshaler/impl_v44_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,107 @@ package datamarshaler
import (
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank/types"
clientTypes "github.com/cosmos/ibc-go/v2/modules/core/02-client/types"
models "github.com/emerishq/demeris-backend-models/tracelistener"
"github.com/emerishq/tracelistener/tracelistener"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)

func TestDataMarshalerBank(t *testing.T) {
var (
coins = sdk.Coin{
Denom: "uatom",
Amount: sdk.NewInt(100),
}
coinBz, _ = coins.Marshal()
)
tests := []struct {
name string
tr tracelistener.TraceOperation
expectedError string
expectedBalanceRow models.BalanceRow
}{
{
name: "fail: key doesn't have address",
tr: tracelistener.TraceOperation{
Operation: tracelistener.WriteOp.String(),
Key: types.BalancesPrefix,
},
expectedError: "cannot parse address from balance store key, invalid key",
},
{
name: "fail: key have wrong address length",
tr: tracelistener.TraceOperation{
Operation: tracelistener.WriteOp.String(),
Key: append(types.BalancesPrefix, []byte{4, 'a', 'd', 'd'}...),
},
expectedError: "cannot parse address from balance store key, invalid key",
},
{
name: "fail: value is empty",
tr: tracelistener.TraceOperation{
Operation: tracelistener.WriteOp.String(),
Key: append(types.BalancesPrefix, []byte{3, 'a', 'd', 'd'}...),
},
expectedError: "invalid balance coin: invalid denom: ",
},
{
name: "ok: value is not a valid coin",
tr: tracelistener.TraceOperation{
Operation: tracelistener.WriteOp.String(),
Key: append(types.BalancesPrefix, []byte{3, 'a', 'd', 'd'}...),
Value: []byte("\n$\n\x05uiris\x12\x1b509625143506063788050678455"),
},
expectedError: "invalid balance coin: invalid denom: \n\x05uiris\x12\x1b509625143506063788050678455",
},
{
name: "ok: value is not a valid coin but operation is delete",
tr: tracelistener.TraceOperation{
Operation: tracelistener.DeleteOp.String(),
Key: append(types.BalancesPrefix, []byte{3, 'a', 'd', 'd'}...),
},
expectedBalanceRow: models.BalanceRow{
Address: "616464",
Amount: "0",
},
},
{
name: "ok",
tr: tracelistener.TraceOperation{
Operation: tracelistener.WriteOp.String(),
Key: append(types.BalancesPrefix, []byte{3, 'a', 'd', 'd'}...),
Value: coinBz,
},
expectedBalanceRow: models.BalanceRow{
Address: "616464",
Amount: "100uatom",
Denom: "uatom",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require := require.New(t)
assert := assert.New(t)
dm := NewDataMarshaler(zap.L().Sugar())

row, err := dm.Bank(tt.tr)

if tt.expectedError != "" {
require.EqualError(err, tt.expectedError)
return
}
require.NoError(err)
assert.Equal(tt.expectedBalanceRow, row)
})
}

}

func Test_ParseIBCChainID(t *testing.T) {
tt := []struct {
name string
Expand Down

0 comments on commit 7da0f0e

Please sign in to comment.