Skip to content
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

Qck 501 intents as memofield #475

Merged
merged 2 commits into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.20-alpine3.17 AS builder
FROM golang:1.20-alpine3.18 AS builder
RUN apk add --no-cache git musl-dev openssl-dev linux-headers ca-certificates build-base

WORKDIR /src/app/
Expand All @@ -22,7 +22,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
LINK_STATICALLY=true make build

# Add to a distroless container
FROM alpine:3.17
FROM alpine:3.18
COPY --from=builder /src/app/build/quicksilverd /usr/local/bin/quicksilverd
RUN adduser -S -h /quicksilver -D quicksilver -u 1000
USER quicksilver
Expand Down
3 changes: 2 additions & 1 deletion x/interchainstaking/keeper/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,14 @@ func (k *Keeper) HandleReceiptTransaction(ctx sdk.Context, txn *tx.Tx, hash stri

if len(memo) > 0 {
// process memo
memoIntent, memoFields, err = zone.DecodeMemo(assets, memo)
memoFields, err = zone.DecodeMemo(memo)
if err != nil {
// What should we do on error here? just log?
k.Logger(ctx).Error("error decoding memo", "error", err.Error(), "memo", memo)
}
memoRTS = memoFields.RTS()
mappedAddress, _ = memoFields.AccountMap()
memoIntent, _ = memoFields.Intent(assets, &zone)
}

// update state
Expand Down
2 changes: 1 addition & 1 deletion x/interchainstaking/types/ibc_packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func ParseEpochMsgMemo(memo, msgType string) (epochNumber int64, err error) {

epochNumber, err = strconv.ParseInt(parts[1], 10, 64)
if err != nil {
return 0, fmt.Errorf("msg type %s: %w: %w", msgType, err, ErrUnexpectedEpochMsgMemo)
return 0, fmt.Errorf("msg type %s: %w", msgType, err)
}

return epochNumber, err
Expand Down
77 changes: 27 additions & 50 deletions x/interchainstaking/types/zones.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package types

import (
"bytes"
"encoding/base64"
"errors"
"fmt"
Expand Down Expand Up @@ -88,34 +87,17 @@ COINS:
return out
}

func (z *Zone) ConvertMemoToOrdinalIntents(coins sdk.Coins, memo string) (ValidatorIntents, error) {
// should we be return DelegatorIntent here?

validatorIntents, _, err := z.DecodeMemo(coins, memo)
if err != nil {
return ValidatorIntents{}, fmt.Errorf("error decoding memo: %w", err)
}

return validatorIntents, nil
}

// decode memo
// if zone.Is_118:
// decode as we have ( up to 8 validators)
// return
//
// decode as we have (up to 6 validators)
// look for separator 0xFF
// field_id = [
// 0x00 = map
// 0x01 = rts
// 0x02 = validator intent
// ] // will scale to future fields

var separator = []byte{byte(255)}

const (
FieldTypeAccountMap int = iota
FieldTypeReturnToSender
FieldTypeIntent
// add more here.
)

Expand All @@ -136,6 +118,19 @@ func (m MemoFields) AccountMap() ([]byte, bool) {
return field.Data, found
}

func (m MemoFields) Intent(coins sdk.Coins, zone *Zone) (ValidatorIntents, bool) {
field, found := m[FieldTypeIntent]
if !found {
return nil, false
}

validatorIntents, err := zone.validatorIntentsFromBytes(coins, field.Data)
if err != nil {
return validatorIntents, false
}
return validatorIntents, true
}

func (m *MemoField) Validate() error {
switch m.ID {
case FieldTypeAccountMap:
Expand All @@ -149,54 +144,36 @@ func (m *MemoField) Validate() error {
}
case FieldTypeReturnToSender:
// do nothing - we ignore data if RTS
case FieldTypeIntent:
if len(m.Data)%21 != 0 { // memo must be one byte (1-200) weight then 20 byte valoperAddress
return fmt.Errorf("invalid length for validator intent memo field %d", len(m.Data))
}
default:
return fmt.Errorf("invalid field type %d", m.ID)
}

return nil
}

func (z *Zone) DecodeMemo(coins sdk.Coins, memo string) (validatorIntents ValidatorIntents, memoFields MemoFields, err error) {
func (z *Zone) DecodeMemo(memo string) (memoFields MemoFields, err error) {
if memo == "" {
return validatorIntents, memoFields, errors.New("memo length unexpectedly zero")
return memoFields, nil
}

memoBytes, err := base64.StdEncoding.DecodeString(memo)
if err != nil {
return validatorIntents, memoFields, fmt.Errorf("failed to decode base64 message: %w", err)
}

parts := bytes.Split(memoBytes, separator)
valWeightsBytes := parts[0]
if len(valWeightsBytes)%21 != 0 { // memo must be one byte (1-200) weight then 20 byte valoperAddress
return validatorIntents, memoFields, fmt.Errorf("unable to determine intent from memo: Message was incorrect length: %d", len(memoBytes))
return memoFields, fmt.Errorf("failed to decode base64 message: %w", err)
}

switch {
case len(parts) == 0:
return validatorIntents, memoFields, errors.New("invalid memo format")

case len(parts) == 1:
if len(valWeightsBytes)/21 > 8 {
return validatorIntents, memoFields, errors.New("memo format not currently supported")
}

default:
// iterate through all non-validator weights parts of the memo
memoFields, err = ParseMemoFields(parts[1])
if err != nil {
return validatorIntents, memoFields, fmt.Errorf("unable to decode memo field: %w", err)
}
memoFields, err = ParseMemoFields(memoBytes)
if err != nil {
return memoFields, fmt.Errorf("unable to decode memo field: %w", err)
}

validatorIntents, err = z.validatorIntentsFromBytes(coins, valWeightsBytes)

return validatorIntents, memoFields, err
return memoFields, err
}

func (z *Zone) validatorIntentsFromBytes(coins sdk.Coins, weightBytes []byte) (ValidatorIntents, error) {
validatorIntents := make(ValidatorIntents, 0)

func (z *Zone) validatorIntentsFromBytes(coins sdk.Coins, weightBytes []byte) (validatorIntents ValidatorIntents, err error) {
for index := 0; index < len(weightBytes); {
// truncate weight to 200
rawWeight := int64(weightBytes[index])
Expand Down
57 changes: 37 additions & 20 deletions x/interchainstaking/types/zones_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,51 +120,59 @@ func TestDecodeMemo(t *testing.T) {
wantErr bool
}{
{
memo: "WoS/+Ex92tEcuMBzhukZKMVnXKS8bqaQBJTx9zza4rrxyLiP9fwLijOc",
memo: "AipahL/4TH3a0Ry4wHOG6RkoxWdcpLxuppAElPH3PNriuvHIuI/1/AuKM5w=",
amount: 100,
expectedIntent: map[string]sdk.Dec{
"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0": sdk.NewDec(45),
"cosmosvaloper156gqf9837u7d4c4678yt3rl4ls9c5vuursrrzf": sdk.NewDec(55),
},
expectedMemoFields: types.MemoFields{
2: types.MemoField{ID: 2, Data: []uint8{0x5a, 0x84, 0xbf, 0xf8, 0x4c, 0x7d, 0xda, 0xd1, 0x1c, 0xb8, 0xc0, 0x73, 0x86, 0xe9, 0x19, 0x28, 0xc5, 0x67, 0x5c, 0xa4, 0xbc, 0x6e, 0xa6, 0x90, 0x4, 0x94, 0xf1, 0xf7, 0x3c, 0xda, 0xe2, 0xba, 0xf1, 0xc8, 0xb8, 0x8f, 0xf5, 0xfc, 0xb, 0x8a, 0x33, 0x9c}},
},
},
{
memo: "RoS/+Ex92tEcuMBzhukZKMVnXKS8RqaQBJTx9zza4rrxyLiP9fwLijOcPK/59acWzdcBME6ub8f0LID97qWE",
memo: "Aj9GhL/4TH3a0Ry4wHOG6RkoxWdcpLxGppAElPH3PNriuvHIuI/1/AuKM5w8r/n1pxbN1wEwTq5vx/QsgP3upYQ=",
amount: 1000,
expectedIntent: map[string]sdk.Dec{
"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0": sdk.NewDec(350),
"cosmosvaloper156gqf9837u7d4c4678yt3rl4ls9c5vuursrrzf": sdk.NewDec(350),
"cosmosvaloper14lultfckehtszvzw4ehu0apvsr77afvyju5zzy": sdk.NewDec(300),
},

expectedMemoFields: types.MemoFields{
2: types.MemoField{ID: 2, Data: []uint8{0x46, 0x84, 0xbf, 0xf8, 0x4c, 0x7d, 0xda, 0xd1, 0x1c, 0xb8, 0xc0, 0x73, 0x86, 0xe9, 0x19, 0x28, 0xc5, 0x67, 0x5c, 0xa4, 0xbc, 0x46, 0xa6, 0x90, 0x4, 0x94, 0xf1, 0xf7, 0x3c, 0xda, 0xe2, 0xba, 0xf1, 0xc8, 0xb8, 0x8f, 0xf5, 0xfc, 0xb, 0x8a, 0x33, 0x9c, 0x3c, 0xaf, 0xf9, 0xf5, 0xa7, 0x16, 0xcd, 0xd7, 0x1, 0x30, 0x4e, 0xae, 0x6f, 0xc7, 0xf4, 0x2c, 0x80, 0xfd, 0xee, 0xa5, 0x84}},
},
},
{
memo: "ToS/+Ex92tEcuMBzhukZKMVnXKS8NKaQBJTx9zza4rrxyLiP9fwLijOcPK/59acWzdcBME6ub8f0LID97qWECuxJKXmxBM1YBQyWHSAXDiwmMY78",
memo: "AlROhL/4TH3a0Ry4wHOG6RkoxWdcpLw0ppAElPH3PNriuvHIuI/1/AuKM5w8r/n1pxbN1wEwTq5vx/QsgP3upYQK7EkpebEEzVgFDJYdIBcOLCYxjvw=",
amount: 10,
expectedIntent: map[string]sdk.Dec{
"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0": sdk.NewDecWithPrec(39, 1),
"cosmosvaloper156gqf9837u7d4c4678yt3rl4ls9c5vuursrrzf": sdk.NewDecWithPrec(26, 1),
"cosmosvaloper14lultfckehtszvzw4ehu0apvsr77afvyju5zzy": sdk.NewDec(3),
"cosmosvaloper1a3yjj7d3qnx4spgvjcwjq9cw9snrrrhu5h6jll": sdk.NewDecWithPrec(5, 1),
},
expectedMemoFields: types.MemoFields{
2: types.MemoField{ID: 2, Data: []uint8{0x4e, 0x84, 0xbf, 0xf8, 0x4c, 0x7d, 0xda, 0xd1, 0x1c, 0xb8, 0xc0, 0x73, 0x86, 0xe9, 0x19, 0x28, 0xc5, 0x67, 0x5c, 0xa4, 0xbc, 0x34, 0xa6, 0x90, 0x4, 0x94, 0xf1, 0xf7, 0x3c, 0xda, 0xe2, 0xba, 0xf1, 0xc8, 0xb8, 0x8f, 0xf5, 0xfc, 0xb, 0x8a, 0x33, 0x9c, 0x3c, 0xaf, 0xf9, 0xf5, 0xa7, 0x16, 0xcd, 0xd7, 0x1, 0x30, 0x4e, 0xae, 0x6f, 0xc7, 0xf4, 0x2c, 0x80, 0xfd, 0xee, 0xa5, 0x84, 0xa, 0xec, 0x49, 0x29, 0x79, 0xb1, 0x4, 0xcd, 0x58, 0x5, 0xc, 0x96, 0x1d, 0x20, 0x17, 0xe, 0x2c, 0x26, 0x31, 0x8e, 0xfc}},
},
},
{
name: "val intents and memo fields",
memo: "WoS/+Ex92tEcuMBzhukZKMVnXKS8bqaQBJTx9zza4rrxyLiP9fwLijOc/wACAQI=",
memo: "AipahL/4TH3a0Ry4wHOG6RkoxWdcpLxuppAElPH3PNriuvHIuI/1/AuKM5wAAgEC",
amount: 100,
expectedIntent: map[string]sdk.Dec{
"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0": sdk.NewDec(45),
"cosmosvaloper156gqf9837u7d4c4678yt3rl4ls9c5vuursrrzf": sdk.NewDec(55),
},
expectedMemoFields: types.MemoFields{
0: {
ID: 0,
Data: []byte{1, 2},
},
0: types.MemoField{ID: 0, Data: []uint8{0x1, 0x2}},
2: types.MemoField{ID: 2, Data: []uint8{0x5a, 0x84, 0xbf, 0xf8, 0x4c, 0x7d, 0xda, 0xd1, 0x1c, 0xb8, 0xc0, 0x73, 0x86, 0xe9, 0x19, 0x28, 0xc5, 0x67, 0x5c, 0xa4, 0xbc, 0x6e, 0xa6, 0x90, 0x4, 0x94, 0xf1, 0xf7, 0x3c, 0xda, 0xe2, 0xba, 0xf1, 0xc8, 0xb8, 0x8f, 0xf5, 0xfc, 0xb, 0x8a, 0x33, 0x9c}},
},
},
{
name: "empty memo",
memo: "",
wantErr: true,
wantErr: false,
},
{
name: "invalid length",
Expand All @@ -180,17 +188,23 @@ func TestDecodeMemo(t *testing.T) {

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
validatorIntents, memoFields, err := zone.DecodeMemo(sdk.NewCoins(sdk.NewCoin("uatom", sdk.NewInt(int64(tc.amount)))), tc.memo)
memoFields, err := zone.DecodeMemo(tc.memo)

if tc.wantErr {
require.Error(t, err)
return
}

require.NoError(t, err)
for _, v := range validatorIntents {
if !tc.expectedIntent[v.ValoperAddress].Equal(v.Weight) {
t.Errorf("Got %v expected %v", v.Weight, tc.expectedIntent[v.ValoperAddress])
validatorIntents, found := memoFields.Intent(sdk.NewCoins(sdk.NewCoin("uatom", sdk.NewInt(int64(tc.amount)))), &zone)
if len(tc.expectedIntent) > 0 {
require.True(t, found)
for _, v := range validatorIntents {
if !tc.expectedIntent[v.ValoperAddress].Equal(v.Weight) {
t.Errorf("Got %v expected %v", v.Weight, tc.expectedIntent[v.ValoperAddress])
}
}
} else {
require.False(t, found)
}

require.Equal(t, tc.expectedMemoFields, memoFields)
Expand Down Expand Up @@ -222,7 +236,8 @@ func TestUpdateIntentWithMemo(t *testing.T) {
"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0": sdk.NewDecWithPrec(45, 2),
"cosmosvaloper156gqf9837u7d4c4678yt3rl4ls9c5vuursrrzf": sdk.NewDecWithPrec(55, 2),
},
memo: "WoS/+Ex92tEcuMBzhukZKMVnXKS8bqaQBJTx9zza4rrxyLiP9fwLijOc",
memo: "AipahL/4TH3a0Ry4wHOG6RkoxWdcpLxuppAElPH3PNriuvHIuI/1/AuKM5w=",

amount: 100,
expectedIntent: map[string]sdk.Dec{
"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0": sdk.NewDecWithPrec(45, 2),
Expand All @@ -235,7 +250,7 @@ func TestUpdateIntentWithMemo(t *testing.T) {
"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0": sdk.NewDecWithPrec(45, 2),
"cosmosvaloper156gqf9837u7d4c4678yt3rl4ls9c5vuursrrzf": sdk.NewDecWithPrec(55, 2),
},
memo: "WoS/+Ex92tEcuMBzhukZKMVnXKS8bqaQBJTx9zza4rrxyLiP9fwLijOc",
memo: "AipahL/4TH3a0Ry4wHOG6RkoxWdcpLxuppAElPH3PNriuvHIuI/1/AuKM5w=",
amount: 1000,
expectedIntent: map[string]sdk.Dec{
"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0": sdk.NewDecWithPrec(45, 2),
Expand All @@ -248,7 +263,7 @@ func TestUpdateIntentWithMemo(t *testing.T) {
"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0": sdk.NewDecWithPrec(25, 2),
"cosmosvaloper156gqf9837u7d4c4678yt3rl4ls9c5vuursrrzf": sdk.NewDecWithPrec(75, 2),
},
memo: "WoS/+Ex92tEcuMBzhukZKMVnXKS8bqaQBJTx9zza4rrxyLiP9fwLijOc",
memo: "AipahL/4TH3a0Ry4wHOG6RkoxWdcpLxuppAElPH3PNriuvHIuI/1/AuKM5w=",
amount: 100,
expectedIntent: map[string]sdk.Dec{
"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0": sdk.NewDecWithPrec(35, 2),
Expand All @@ -261,7 +276,7 @@ func TestUpdateIntentWithMemo(t *testing.T) {
"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0": sdk.NewDecWithPrec(25, 2),
"cosmosvaloper156gqf9837u7d4c4678yt3rl4ls9c5vuursrrzf": sdk.NewDecWithPrec(75, 2),
},
memo: "RoS/+Ex92tEcuMBzhukZKMVnXKS8RqaQBJTx9zza4rrxyLiP9fwLijOcPK/59acWzdcBME6ub8f0LID97qWE",
memo: "Aj9GhL/4TH3a0Ry4wHOG6RkoxWdcpLxGppAElPH3PNriuvHIuI/1/AuKM5w8r/n1pxbN1wEwTq5vx/QsgP3upYQ=",
amount: 1000,
expectedIntent: map[string]sdk.Dec{
"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0": sdk.NewDecWithPrec(30, 2),
Expand All @@ -272,8 +287,10 @@ func TestUpdateIntentWithMemo(t *testing.T) {
}

for caseidx, tc := range testCases {
memoIntent, _, err := zone.DecodeMemo(sdk.NewCoins(sdk.NewCoin("uatom", sdk.NewInt(int64(tc.amount)))), tc.memo)
memoFields, err := zone.DecodeMemo(tc.memo)
require.NoError(t, err)
memoIntent, found := memoFields.Intent(sdk.NewCoins(sdk.NewCoin("uatom", sdk.NewInt(int64(tc.amount)))), &zone)
require.True(t, found)
intent := zone.UpdateZoneIntentWithMemo(memoIntent, intentFromDecSlice(tc.originalIntent), sdk.NewDec(int64(tc.baseAmount)))
for idx, v := range intent.Intents.Sort() {
if !tc.expectedIntent[v.ValoperAddress].Equal(v.Weight) {
Expand Down Expand Up @@ -314,7 +331,7 @@ func TestUpdateIntentWithMemoBad(t *testing.T) {
}

for _, tc := range testCases {
_, _, err := zone.DecodeMemo(sdk.NewCoins(sdk.NewCoin("uatom", sdk.NewInt(int64(tc.amount)))), tc.memo)
_, err := zone.DecodeMemo(tc.memo)
require.Errorf(t, err, tc.errorMsg)
}
}
Expand Down