From 519c591ee2a83b27b9431c02b9f322c3a1416e9c Mon Sep 17 00:00:00 2001 From: ht <22350747@qq.com> Date: Thu, 14 Apr 2022 22:05:00 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E6=9A=82=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/tx.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/tx.go b/client/tx.go index e0f0ff5..dcc3bf8 100644 --- a/client/tx.go +++ b/client/tx.go @@ -4,6 +4,7 @@ import ( "context" "encoding/hex" "errors" + "fmt" "strings" "time" @@ -22,6 +23,9 @@ func (base baseClient) QueryTx(hash string) (sdk.ResultQueryTx, error) { } res, err := base.Tx(context.Background(), tx, true) + + fmt.Println("交易结果=====================", res) + if err != nil { return sdk.ResultQueryTx{}, err } From d5aa78846688e5a359b4892e2f2133464a545f6b Mon Sep 17 00:00:00 2001 From: ht <22350747@qq.com> Date: Fri, 15 Apr 2022 10:00:09 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E6=9A=82=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/tx.go | 4 ++++ feegrant/codec.go | 22 +++++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/client/tx.go b/client/tx.go index dcc3bf8..4749cdc 100644 --- a/client/tx.go +++ b/client/tx.go @@ -31,6 +31,9 @@ func (base baseClient) QueryTx(hash string) (sdk.ResultQueryTx, error) { } resBlocks, err := base.getResultBlocks([]*ctypes.ResultTx{res}) + + fmt.Println("区块内容=====================", resBlocks) + if err != nil { return sdk.ResultQueryTx{}, err } @@ -220,6 +223,7 @@ func (base baseClient) parseTxResult(res *ctypes.ResultTx, resBlock *ctypes.Resu decode := base.encodingConfig.TxConfig.TxDecoder() if tx, err = decode(res.Tx); err != nil { + fmt.Println("没解析成功,err", err) return sdk.ResultQueryTx{}, err } diff --git a/feegrant/codec.go b/feegrant/codec.go index dabeb03..0163172 100644 --- a/feegrant/codec.go +++ b/feegrant/codec.go @@ -17,11 +17,27 @@ func init() { amino.Seal() } -// RegisterInterfaces No duplicate registration +//// RegisterInterfaces No duplicate registration +//func RegisterInterfaces(registry types.InterfaceRegistry) { +// registry.RegisterImplementations( +// (*sdk.Msg)(nil), +// &MsgGrantAllowance{}, +// &MsgRevokeAllowance{}, +// ) +//} + +// RegisterInterfaces registers the interfaces types with the interface registry func RegisterInterfaces(registry types.InterfaceRegistry) { - registry.RegisterImplementations( - (*sdk.Msg)(nil), + registry.RegisterImplementations((*sdk.Msg)(nil), &MsgGrantAllowance{}, &MsgRevokeAllowance{}, ) + + registry.RegisterInterface( + "cosmos.feegrant.v1beta1.FeeAllowanceI", + (*FeeAllowanceI)(nil), + &BasicAllowance{}, + &PeriodicAllowance{}, + &AllowedMsgAllowance{}, + ) } From 99705fa59e006dfca7d97ac30024dac006722cbf Mon Sep 17 00:00:00 2001 From: ht <22350747@qq.com> Date: Fri, 15 Apr 2022 12:43:12 +0800 Subject: [PATCH 3/6] =?UTF-8?q?PeriodicAllowance=E5=AE=9E=E7=8E=B0FeeAllow?= =?UTF-8?q?anceI=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/tx.go | 6 --- feegrant/codec.go | 12 +---- feegrant/periodic_fee.go | 95 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 16 deletions(-) create mode 100644 feegrant/periodic_fee.go diff --git a/client/tx.go b/client/tx.go index 4749cdc..1f3a646 100644 --- a/client/tx.go +++ b/client/tx.go @@ -4,7 +4,6 @@ import ( "context" "encoding/hex" "errors" - "fmt" "strings" "time" @@ -24,16 +23,12 @@ func (base baseClient) QueryTx(hash string) (sdk.ResultQueryTx, error) { res, err := base.Tx(context.Background(), tx, true) - fmt.Println("交易结果=====================", res) - if err != nil { return sdk.ResultQueryTx{}, err } resBlocks, err := base.getResultBlocks([]*ctypes.ResultTx{res}) - fmt.Println("区块内容=====================", resBlocks) - if err != nil { return sdk.ResultQueryTx{}, err } @@ -223,7 +218,6 @@ func (base baseClient) parseTxResult(res *ctypes.ResultTx, resBlock *ctypes.Resu decode := base.encodingConfig.TxConfig.TxDecoder() if tx, err = decode(res.Tx); err != nil { - fmt.Println("没解析成功,err", err) return sdk.ResultQueryTx{}, err } diff --git a/feegrant/codec.go b/feegrant/codec.go index 0163172..081a4c3 100644 --- a/feegrant/codec.go +++ b/feegrant/codec.go @@ -17,18 +17,10 @@ func init() { amino.Seal() } -//// RegisterInterfaces No duplicate registration -//func RegisterInterfaces(registry types.InterfaceRegistry) { -// registry.RegisterImplementations( -// (*sdk.Msg)(nil), -// &MsgGrantAllowance{}, -// &MsgRevokeAllowance{}, -// ) -//} - // RegisterInterfaces registers the interfaces types with the interface registry func RegisterInterfaces(registry types.InterfaceRegistry) { - registry.RegisterImplementations((*sdk.Msg)(nil), + registry.RegisterImplementations( + (*sdk.Msg)(nil), &MsgGrantAllowance{}, &MsgRevokeAllowance{}, ) diff --git a/feegrant/periodic_fee.go b/feegrant/periodic_fee.go new file mode 100644 index 0000000..0d1a83a --- /dev/null +++ b/feegrant/periodic_fee.go @@ -0,0 +1,95 @@ +package feegrant + +import ( + "fmt" + sdk "github.com/irisnet/core-sdk-go/types" + "time" +) + +var _ FeeAllowanceI = (*PeriodicAllowance)(nil) + +func (m *PeriodicAllowance) Accept(ctx sdk.Context, fee sdk.Coins, _ []sdk.Msg) (remove bool, err error) { + blockTime := ctx.BlockTime() + + if m.Basic.Expiration != nil && blockTime.After(*m.Basic.Expiration) { + return true, sdk.Wrap(fmt.Errorf("fee allowance expired : %s", "absolute limit")) + } + + m.tryResetPeriod(blockTime) + + // deduct from both the current period and the max amount + var isNeg bool + m.PeriodCanSpend, isNeg = m.PeriodCanSpend.SafeSub(fee) + if isNeg { + return false, sdk.Wrap(fmt.Errorf("fee limit exceeded : %s", "period limit")) + } + + if m.Basic.SpendLimit != nil { + m.Basic.SpendLimit, isNeg = m.Basic.SpendLimit.SafeSub(fee) + if isNeg { + return false, sdk.Wrap(fmt.Errorf("fee limit exceeded : %s", "absolute limit")) + } + + return m.Basic.SpendLimit.IsZero(), nil + } + + return false, nil +} + +// tryResetPeriod will check if the PeriodReset has been hit. If not, it is a no-op. +// If we hit the reset period, it will top up the PeriodCanSpend amount to +// min(PeriodSpendLimit, Basic.SpendLimit) so it is never more than the maximum allowed. +// It will also update the PeriodReset. If we are within one Period, it will update from the +// last PeriodReset (eg. if you always do one tx per day, it will always reset the same time) +// If we are more then one period out (eg. no activity in a week), reset is one Period from the execution of this method +func (m *PeriodicAllowance) tryResetPeriod(blockTime time.Time) { + if blockTime.Before(m.PeriodReset) { + return + } + + // set PeriodCanSpend to the lesser of Basic.SpendLimit and PeriodSpendLimit + if _, isNeg := m.Basic.SpendLimit.SafeSub(m.PeriodSpendLimit); isNeg && !m.Basic.SpendLimit.Empty() { + m.PeriodCanSpend = m.Basic.SpendLimit + } else { + m.PeriodCanSpend = m.PeriodSpendLimit + } + + // If we are within the period, step from expiration (eg. if you always do one tx per day, it will always reset the same time) + // If we are more then one period out (eg. no activity in m week), reset is one period from this time + m.PeriodReset = m.PeriodReset.Add(m.Period) + if blockTime.After(m.PeriodReset) { + m.PeriodReset = blockTime.Add(m.Period) + } +} + +func (m *PeriodicAllowance) ValidateBasic() error { + if err := m.Basic.ValidateBasic(); err != nil { + return err + } + + if !m.PeriodSpendLimit.IsValid() { + return sdk.Wrap(fmt.Errorf("invalid coins , spend amount is invalid: %v", m.PeriodSpendLimit)) + } + if !m.PeriodSpendLimit.IsAllPositive() { + return sdk.Wrap(fmt.Errorf("invalid coins : %s", "spend limit must be positive")) + } + if !m.PeriodCanSpend.IsValid() { + return sdk.Wrap(fmt.Errorf("invalid coins , can spend amount is invalid: %v", m.PeriodCanSpend)) + } + // We allow 0 for CanSpend + if m.PeriodCanSpend.IsAnyNegative() { + return sdk.Wrap(fmt.Errorf("invalid coins : %s", "can spend must not be negative")) + } + + // ensure PeriodSpendLimit can be subtracted from total (same coin types) + if m.Basic.SpendLimit != nil && !m.PeriodSpendLimit.DenomsSubsetOf(m.Basic.SpendLimit) { + return sdk.Wrap(fmt.Errorf("invalid coins : %s", "period spend limit has different currency than basic spend limit")) + } + + // check times + if m.Period.Seconds() < 0 { + return sdk.Wrap(fmt.Errorf("invalid duration : %s", "negative clock step")) + } + + return nil +} From 163d24414483c4209c1f026bdc3d045fb51880d9 Mon Sep 17 00:00:00 2001 From: ht <22350747@qq.com> Date: Fri, 15 Apr 2022 12:43:35 +0800 Subject: [PATCH 4/6] =?UTF-8?q?AllowedMsgAllowance=E5=AE=9E=E7=8E=B0FeeAll?= =?UTF-8?q?owanceI=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feegrant/filtered_fee.go | 122 +++++++++++++++++++++++++++++++++++++++ types/tx_msg.go | 5 ++ 2 files changed, 127 insertions(+) create mode 100644 feegrant/filtered_fee.go diff --git a/feegrant/filtered_fee.go b/feegrant/filtered_fee.go new file mode 100644 index 0000000..4fd4140 --- /dev/null +++ b/feegrant/filtered_fee.go @@ -0,0 +1,122 @@ +package feegrant + +import ( + "fmt" + "github.com/gogo/protobuf/proto" + + "github.com/irisnet/core-sdk-go/common/codec/types" + sdk "github.com/irisnet/core-sdk-go/types" +) + +// TODO: Revisit this once we have propoer gas fee framework. +// Tracking issues https://github.com/cosmos/cosmos-sdk/issues/9054, https://github.com/cosmos/cosmos-sdk/discussions/9072 +const ( + gasCostPerIteration = uint64(10) +) + +var _ FeeAllowanceI = (*AllowedMsgAllowance)(nil) +var _ types.UnpackInterfacesMessage = (*AllowedMsgAllowance)(nil) + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (a *AllowedMsgAllowance) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var allowance FeeAllowanceI + return unpacker.UnpackAny(a.Allowance, &allowance) +} + +// NewAllowedMsgFeeAllowance creates new filtered fee allowance. +func NewAllowedMsgAllowance(allowance FeeAllowanceI, allowedMsgs []string) (*AllowedMsgAllowance, error) { + msg, ok := allowance.(proto.Message) + if !ok { + return nil, sdk.Wrap(fmt.Errorf("failed packing protobuf message to Any : cannot proto marshal %T", msg)) + } + any, err := types.NewAnyWithValue(msg) + if err != nil { + return nil, err + } + + return &AllowedMsgAllowance{ + Allowance: any, + AllowedMessages: allowedMsgs, + }, nil +} + +// GetAllowance returns allowed fee allowance. +func (a *AllowedMsgAllowance) GetAllowance() (FeeAllowanceI, error) { + allowance, ok := a.Allowance.GetCachedValue().(FeeAllowanceI) + if !ok { + return nil, sdk.Wrap(fmt.Errorf("message not allowed : %s", "failed to get allowance")) + } + + return allowance, nil +} + +// SetAllowance sets allowed fee allowance. +func (a *AllowedMsgAllowance) SetAllowance(allowance FeeAllowanceI) error { + var err error + a.Allowance, err = types.NewAnyWithValue(allowance.(proto.Message)) + if err != nil { + return sdk.Wrap(fmt.Errorf("failed packing protobuf message to Any : cannot proto marshal %T", allowance)) + } + + return nil +} + +// Accept method checks for the filtered messages has valid expiry +func (a *AllowedMsgAllowance) Accept(ctx sdk.Context, fee sdk.Coins, msgs []sdk.Msg) (bool, error) { + if !a.allMsgTypesAllowed(ctx, msgs) { + return false, sdk.Wrap(fmt.Errorf("message not allowed : %s", "message does not exist in allowed messages")) + } + + allowance, err := a.GetAllowance() + if err != nil { + return false, err + } + + remove, err := allowance.Accept(ctx, fee, msgs) + if err == nil && !remove { + if err = a.SetAllowance(allowance); err != nil { + return false, err + } + } + return remove, err +} + +func (a *AllowedMsgAllowance) allowedMsgsToMap(ctx sdk.Context) map[string]bool { + msgsMap := make(map[string]bool, len(a.AllowedMessages)) + for _, msg := range a.AllowedMessages { + ctx.GasMeter().ConsumeGas(gasCostPerIteration, "check msg") + msgsMap[msg] = true + } + + return msgsMap +} + +func (a *AllowedMsgAllowance) allMsgTypesAllowed(ctx sdk.Context, msgs []sdk.Msg) bool { + msgsMap := a.allowedMsgsToMap(ctx) + + for _, msg := range msgs { + ctx.GasMeter().ConsumeGas(gasCostPerIteration, "check msg") + if !msgsMap[sdk.MsgTypeURL(msg)] { + return false + } + } + + return true +} + +// ValidateBasic implements FeeAllowance and enforces basic sanity checks +func (a *AllowedMsgAllowance) ValidateBasic() error { + if a.Allowance == nil { + return sdk.Wrap(fmt.Errorf("no allowance : %s", "allowance should not be empty")) + } + if len(a.AllowedMessages) == 0 { + return sdk.Wrap(fmt.Errorf("allowed messages are empty : %s", "allowed messages shouldn't be empty")) + } + + allowance, err := a.GetAllowance() + if err != nil { + return err + } + + return allowance.ValidateBasic() +} diff --git a/types/tx_msg.go b/types/tx_msg.go index e75b7b6..a83e3c0 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -85,3 +85,8 @@ type TxDecoder func(txBytes []byte) (Tx, error) // TxEncoder marshals transaction to bytes type TxEncoder func(tx Tx) ([]byte, error) + +// MsgTypeURL returns the TypeURL of a `sdk.Msg`. +func MsgTypeURL(msg Msg) string { + return "/" + proto.MessageName(msg) +} From d1e6d80d6a82bbbfea3c5bcaf0443fbe286fa524 Mon Sep 17 00:00:00 2001 From: ht <22350747@qq.com> Date: Fri, 15 Apr 2022 13:45:02 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feegrant/filtered_fee.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feegrant/filtered_fee.go b/feegrant/filtered_fee.go index 4fd4140..34de149 100644 --- a/feegrant/filtered_fee.go +++ b/feegrant/filtered_fee.go @@ -23,7 +23,7 @@ func (a *AllowedMsgAllowance) UnpackInterfaces(unpacker types.AnyUnpacker) error return unpacker.UnpackAny(a.Allowance, &allowance) } -// NewAllowedMsgFeeAllowance creates new filtered fee allowance. +// NewAllowedMsgAllowance creates new filtered fee allowance. func NewAllowedMsgAllowance(allowance FeeAllowanceI, allowedMsgs []string) (*AllowedMsgAllowance, error) { msg, ok := allowance.(proto.Message) if !ok { From 499350b6e65e93ba43a6ccf67633eb719c916ecf Mon Sep 17 00:00:00 2001 From: ht <22350747@qq.com> Date: Sat, 16 Apr 2022 18:18:29 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/tx.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/tx.go b/client/tx.go index 1f3a646..e0f0ff5 100644 --- a/client/tx.go +++ b/client/tx.go @@ -22,13 +22,11 @@ func (base baseClient) QueryTx(hash string) (sdk.ResultQueryTx, error) { } res, err := base.Tx(context.Background(), tx, true) - if err != nil { return sdk.ResultQueryTx{}, err } resBlocks, err := base.getResultBlocks([]*ctypes.ResultTx{res}) - if err != nil { return sdk.ResultQueryTx{}, err }