Skip to content

Commit

Permalink
[rewarding] keep both v1 and v2 claimRewardingInterfaceABI for backwa…
Browse files Browse the repository at this point in the history
…rd compatibility (#4304)
  • Loading branch information
dustinxie committed Jun 20, 2024
1 parent 8741854 commit f001407
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 96 deletions.
14 changes: 6 additions & 8 deletions action/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ func TestBuildRewardingAction(t *testing.T) {
})

t.Run("Success", func(t *testing.T) {
method := _claimRewardingMethod
method := _claimRewardingMethodV1
t.Run("ClaimRewarding", func(t *testing.T) {
inputs := MustNoErrorV(method.Inputs.Pack(big.NewInt(101), []byte("any"), ""))
inputs := MustNoErrorV(method.Inputs.Pack(big.NewInt(101), []byte("any")))
elp, err := eb.BuildRewardingAction(types.NewTx(&types.LegacyTx{
Nonce: 1,
GasPrice: big.NewInt(10004),
Expand All @@ -100,13 +100,12 @@ func TestBuildRewardingAction(t *testing.T) {
r.Equal(big.NewInt(10004), elp.GasPrice())
r.Equal(uint64(10000), elp.GasLimit())
r.Equal(big.NewInt(101), elp.Action().(*ClaimFromRewardingFund).Amount())

})
t.Run("Debug", func(t *testing.T) {
eb := &EnvelopeBuilder{}
eb.SetChainID(4689)

inputs := MustNoErrorV(method.Inputs.Pack(big.NewInt(100), []byte("any"), ""))
inputs := MustNoErrorV(method.Inputs.Pack(big.NewInt(100), []byte("any")))
to := common.HexToAddress("0xA576C141e5659137ddDa4223d209d4744b2106BE")
tx := types.NewTx(&types.LegacyTx{
Nonce: 0,
Expand Down Expand Up @@ -141,8 +140,6 @@ func TestBuildRewardingAction(t *testing.T) {
})
}

func ptr[V any](v V) *V { return &v }

func TestEthTxUtils(t *testing.T) {
r := require.New(t)
var (
Expand All @@ -166,10 +163,11 @@ func TestEthTxUtils(t *testing.T) {

addr, err := address.FromHex("0xA576C141e5659137ddDa4223d209d4744b2106BE")
r.NoError(err)
tx, _ := ptr((&ClaimFromRewardingFundBuilder{Builder: *builder}).
act := (&ClaimFromRewardingFundBuilder{Builder: *builder}).
SetAddress(addr).
SetData([]byte("any")).
SetAmount(big.NewInt(1)).Build()).ToEthTx(chainID)
SetAmount(big.NewInt(1)).Build()
tx, _ := act.ToEthTx(chainID)

var (
signer1, _ = NewEthSigner(iotextypes.Encoding_ETHEREUM_EIP155, chainID)
Expand Down
84 changes: 63 additions & 21 deletions action/claimreward.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,32 @@ const _claimRewardingInterfaceABI = `[
"internalType": "uint8[]",
"name": "data",
"type": "uint8[]"
}
],
"name": "claim",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "string",
"name": "address",
"type": "string"
},
{
"internalType": "uint8[]",
"name": "data",
"type": "uint8[]"
}
],
"name": "claim",
"name": "claimFor",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
Expand All @@ -52,8 +70,10 @@ var (
// ClaimFromRewardingFundGasPerByte represents the claimFromRewardingFund payload gas per uint
ClaimFromRewardingFundGasPerByte = uint64(100)

_claimRewardingMethod abi.Method
_ EthCompatibleAction = (*ClaimFromRewardingFund)(nil)
_claimRewardingMethodV1 abi.Method
_claimRewardingMethodV2 abi.Method
_ EthCompatibleAction = (*ClaimFromRewardingFund)(nil)
errWrongMethodSig = errors.New("wrong method signature")
)

func init() {
Expand All @@ -62,10 +82,14 @@ func init() {
panic(err)
}
var ok bool
_claimRewardingMethod, ok = claimRewardInterface.Methods["claim"]
_claimRewardingMethodV1, ok = claimRewardInterface.Methods["claim"]
if !ok {
panic("fail to load the claim method")
}
_claimRewardingMethodV2, ok = claimRewardInterface.Methods["claimFor"]
if !ok {
panic("fail to load the claimTo method")
}
}

// ClaimFromRewardingFund is the action to claim reward from the rewarding fund
Expand Down Expand Up @@ -185,15 +209,19 @@ func (b *ClaimFromRewardingFundBuilder) Build() ClaimFromRewardingFund {

// encodeABIBinary encodes data in abi encoding
func (c *ClaimFromRewardingFund) encodeABIBinary() ([]byte, error) {
var addr string
if c.address != nil {
addr = c.address.String()
if c.address == nil {
// this is v1 ABI before adding address field
data, err := _claimRewardingMethodV1.Inputs.Pack(c.Amount(), c.Data())
if err != nil {
return nil, err
}
return append(_claimRewardingMethodV1.ID, data...), nil
}
data, err := _claimRewardingMethod.Inputs.Pack(c.Amount(), c.Data(), addr)
data, err := _claimRewardingMethodV2.Inputs.Pack(c.Amount(), c.address.String(), c.Data())
if err != nil {
return nil, err
}
return append(_claimRewardingMethod.ID, data...), nil
return append(_claimRewardingMethodV2.ID, data...), nil
}

// ToEthTx converts action to eth-compatible tx
Expand All @@ -214,29 +242,43 @@ func (c *ClaimFromRewardingFund) ToEthTx(_ uint32) (*types.Transaction, error) {

// NewClaimFromRewardingFundFromABIBinary decodes data into action
func NewClaimFromRewardingFundFromABIBinary(data []byte) (*ClaimFromRewardingFund, error) {
if len(data) <= 4 {
return nil, errDecodeFailure
}

var (
paramsMap = map[string]interface{}{}
ok bool
ok, isV2 bool
ac ClaimFromRewardingFund
)
// sanity check
if len(data) <= 4 || !bytes.Equal(_claimRewardingMethod.ID[:], data[:4]) {
return nil, errDecodeFailure
}
if err := _claimRewardingMethod.Inputs.UnpackIntoMap(paramsMap, data[4:]); err != nil {
return nil, err
switch {
case bytes.Equal(_claimRewardingMethodV2.ID[:], data[:4]):
if err := _claimRewardingMethodV2.Inputs.UnpackIntoMap(paramsMap, data[4:]); err != nil {
return nil, err
}
isV2 = true
case bytes.Equal(_claimRewardingMethodV1.ID[:], data[:4]):
if err := _claimRewardingMethodV1.Inputs.UnpackIntoMap(paramsMap, data[4:]); err != nil {
return nil, err
}
default:
return nil, errWrongMethodSig
}

if ac.amount, ok = paramsMap["amount"].(*big.Int); !ok {
return nil, errDecodeFailure
}
if ac.data, ok = paramsMap["data"].([]byte); !ok {
return nil, errDecodeFailure
}
var s string
if s, ok = paramsMap["address"].(string); !ok {
return nil, errDecodeFailure
}
if len(s) > 0 {
if isV2 {
var s string
if s, ok = paramsMap["address"].(string); !ok {
return nil, errDecodeFailure
}
if len(s) == 0 {
return nil, errors.Wrap(errDecodeFailure, "address is empty")
}
addr, err := address.FromString(s)
if err != nil {
return nil, err
Expand Down
Empty file removed action/claimreward.s
Empty file.
83 changes: 35 additions & 48 deletions action/claimreward_test.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,23 @@
package action_test
package action

import (
"math/big"
"testing"
_ "unsafe"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/iotexproject/iotex-address/address"
"github.com/iotexproject/iotex-proto/golang/iotextypes"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"

"github.com/iotexproject/iotex-core/action"
"github.com/iotexproject/iotex-core/pkg/util/assertions"
)

//go:linkname _claimRewardingMethod github.com/iotexproject/iotex-core/action._claimRewardingMethod
var _claimRewardingMethod abi.Method

//go:linkname _rewardingProtocolEthAddr github.com/iotexproject/iotex-core/action._rewardingProtocolEthAddr
var _rewardingProtocolEthAddr common.Address

func TestClaimRewardIntrinsicGas(t *testing.T) {
r := require.New(t)

builder := &action.ClaimFromRewardingFundBuilder{}
builder := &ClaimFromRewardingFundBuilder{}

rc := builder.Build()
gas, err := rc.IntrinsicGas()
Expand All @@ -51,7 +43,7 @@ func TestClaimRewardIntrinsicGas(t *testing.T) {
func TestClaimRewardSanityCheck(t *testing.T) {
r := require.New(t)

builder := &action.ClaimFromRewardingFundBuilder{}
builder := &ClaimFromRewardingFundBuilder{}

builder.SetAmount(big.NewInt(1))
rc := builder.Build()
Expand All @@ -61,13 +53,13 @@ func TestClaimRewardSanityCheck(t *testing.T) {
builder.SetAmount(big.NewInt(-1))
rc = builder.Build()
err := rc.SanityCheck()
r.ErrorIs(err, action.ErrNegativeValue)
r.ErrorIs(err, ErrNegativeValue)
}

func TestClaimRewardCost(t *testing.T) {
r := require.New(t)

builder := &action.ClaimFromRewardingFundBuilder{}
builder := &ClaimFromRewardingFundBuilder{}

builder.SetGasPrice(big.NewInt(1000000000000))
rc := builder.Build()
Expand Down Expand Up @@ -96,14 +88,14 @@ func TestClaimRewardCost(t *testing.T) {
func TestClaimRewardToEthTx(t *testing.T) {
r := require.New(t)

builder := &action.ClaimFromRewardingFundBuilder{}
builder := &ClaimFromRewardingFundBuilder{}

builder.SetAmount(big.NewInt(101))
rc := builder.Build()
tx, err := rc.ToEthTx(0)
r.NoError(err)
r.Equal(tx.To().String(), _rewardingProtocolEthAddr.String())
r.Equal(tx.Data()[:4], _claimRewardingMethod.ID)
r.Equal(tx.Data()[:4], _claimRewardingMethodV1.ID)
r.Equal(tx.Value().String(), "0")

addr, err := address.FromHex("0xA576C141e5659137ddDa4223d209d4744b2106BE")
Expand All @@ -115,7 +107,7 @@ func TestClaimRewardToEthTx(t *testing.T) {
rc = builder.Build()
tx, err = rc.ToEthTx(0)
r.NoError(err)
r.Equal(tx.Data()[:4], _claimRewardingMethod.ID)
r.Equal(tx.Data()[:4], _claimRewardingMethodV2.ID)
r.Equal(tx.Value().String(), "0")
}

Expand All @@ -134,85 +126,80 @@ func TestNewRewardingClaimFromABIBinary(t *testing.T) {
Indexed: false,
},
abi.Argument{
Name: "data",
Type: assertions.MustNoErrorV(abi.NewType("uint8[]", "uint8[]", nil)),
Name: "address",
Type: assertions.MustNoErrorV(abi.NewType("string", "string", nil)),
Indexed: false,
},
abi.Argument{
Name: "address",
Type: assertions.MustNoErrorV(abi.NewType("string", "string", nil)),
Name: "data",
Type: assertions.MustNoErrorV(abi.NewType("uint8[]", "uint8[]", nil)),
Indexed: false,
},
}
outputs = abi.Arguments{}
)

t.Run("CheckMethodDefine", func(t *testing.T) {
method = abi.NewMethod("claim", "claim", abi.Function, "nonpayable", false, false, inputs, outputs)
r.Equal(method, _claimRewardingMethod)
method = abi.NewMethod("claimFor", "claimFor", abi.Function, "nonpayable", false, false, inputs, outputs)
r.Equal(method, _claimRewardingMethodV2)
})

t.Run("InvalidMethodSignature", func(t *testing.T) {
input := assertions.MustNoErrorV(method.Inputs.Pack(amount, data, addr))
input := assertions.MustNoErrorV(method.Inputs.Pack(amount, addr, data))
methodsig := []byte{'1', '2', '3', 4} // invalid
calldata := append(methodsig, input...)

_, err := action.NewClaimFromRewardingFundFromABIBinary(calldata)
r.ErrorContains(err, "failed to decode")
_, err := NewClaimFromRewardingFundFromABIBinary(calldata)
r.Equal(errWrongMethodSig, err)
})

t.Run("MissingSomeArgument", func(t *testing.T) {
_inputs := _claimRewardingMethod.Inputs
_inputs := _claimRewardingMethodV2.Inputs
calldata := append(
method.ID,
assertions.MustNoErrorV(inputs.Pack(amount, data, addr))...,
assertions.MustNoErrorV(inputs.Pack(amount, addr, data))...,
)

for i := 0; i < len(_inputs); i++ {
old := inputs[i].Name
_inputs[i].Name = "any"
_, err := action.NewClaimFromRewardingFundFromABIBinary(calldata)
r.ErrorContains(err, "failed to decode")
_, err := NewClaimFromRewardingFundFromABIBinary(calldata)
r.Equal(errDecodeFailure, err)
_inputs[i].Name = old
}
})

t.Run("EmptyAddress", func(t *testing.T) {
calldata := append(
method.ID[:],
assertions.MustNoErrorV(method.Inputs.Pack(amount, "", data))...)
_, err := NewClaimFromRewardingFundFromABIBinary(calldata)
r.ErrorContains(err, "address is empty")
})

t.Run("InvalidAddress", func(t *testing.T) {
calldata := append(
method.ID[:],
assertions.MustNoErrorV(method.Inputs.Pack(amount, data, "0x1231231232113"))...)
_, err := action.NewClaimFromRewardingFundFromABIBinary(calldata)
assertions.MustNoErrorV(method.Inputs.Pack(amount, "0x1231231232113", data))...)
_, err := NewClaimFromRewardingFundFromABIBinary(calldata)
r.Equal(address.ErrInvalidAddr, errors.Cause(err))
})

t.Run("Success", func(t *testing.T) {
calldata := append(
method.ID[:],
assertions.MustNoErrorV(method.Inputs.Pack(amount, data, addr))...)
ret, err := action.NewClaimFromRewardingFundFromABIBinary(calldata)
assertions.MustNoErrorV(method.Inputs.Pack(amount, addr, data))...)
ret, err := NewClaimFromRewardingFundFromABIBinary(calldata)
r.NoError(err)
r.Equal(ret.Address().String(), addr)
r.Equal(ret.Amount(), amount)
r.Equal(ret.Data(), data)
})
}

/*
func TestClaimFromRewardingFund(t *testing.T) {
b := ClaimFromRewardingFundBuilder{}
s1 := b.SetAmount(big.NewInt(1)).
SetData([]byte{2}).
Build()
proto := s1.Proto()
s2 := ClaimFromRewardingFund{}
require.NoError(t, s2.LoadProto(proto))
assert.Equal(t, s1.Amount(), s2.Amount())
assert.Equal(t, s2.Data(), s2.Data())
}
*/
func TestClaimFromRewardingFund(t *testing.T) {
r := require.New(t)
c := &action.ClaimFromRewardingFund{}
c := &ClaimFromRewardingFund{}

t.Run("InvalidAmountProtoValue", func(t *testing.T) {
p := &iotextypes.ClaimFromRewardingFund{
Expand Down Expand Up @@ -268,7 +255,7 @@ func TestClaimFromRewardingFund(t *testing.T) {
if i == 1 {
addr, _ = address.FromString("io10a298zmzvrt4guq79a9f4x7qedj59y7ery84he")
}
builder := &action.ClaimFromRewardingFundBuilder{}
builder := &ClaimFromRewardingFundBuilder{}
builder.SetAmount(big.NewInt(205))
builder.SetData([]byte("abc"))
builder.SetAddress(addr)
Expand Down
Loading

0 comments on commit f001407

Please sign in to comment.