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

[api] web3 rewarding action #3691

Merged
merged 13 commits into from
Dec 22, 2022
30 changes: 29 additions & 1 deletion action/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ type Builder struct {
}

var (
_stakingProtocolAddr, _ = address.FromString(address.StakingProtocolAddr)
_stakingProtocolAddr, _ = address.FromString(address.StakingProtocolAddr)
_rewardingProtocolAddr, _ = address.FromString(address.RewardingProtocol)
)

// SetVersion sets action's version.
Expand Down Expand Up @@ -202,6 +203,20 @@ func (b *EnvelopeBuilder) BuildStakingAction(tx *types.Transaction) (Envelope, e
return b.build(), nil
}

// BuildRewardingAction loads rewarding action into envelope from abi-encoded data
func (b *EnvelopeBuilder) BuildRewardingAction(tx *types.Transaction) (Envelope, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defined but not used?
need to use it in web3server_utils.go similar to how BuildStakingAction is used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is prepared for another PR and that PR will use this function as similar BuildStakingAction, because accord the meeting discussed, this feature should split two PRs, one is this envelope actions and another is invoking API.

Copy link
Member

@dustinxie dustinxie Dec 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after discussing with haixiang, better do it in one PR altogether.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool, have added it.

if !bytes.Equal(tx.To().Bytes(), _rewardingProtocolAddr.Bytes()) {
return nil, ErrInvalidAct
}
b.setEnvelopeCommonFields(tx)
act, err := newRewardingActionFromABIBinary(tx.Data())
if err != nil {
return nil, err
}
b.elp.payload = act
return b.build(), nil
}

func newStakingActionFromABIBinary(data []byte) (actionPayload, error) {
if len(data) <= 4 {
return nil, ErrInvalidABI
Expand Down Expand Up @@ -235,3 +250,16 @@ func newStakingActionFromABIBinary(data []byte) (actionPayload, error) {
}
return nil, ErrInvalidABI
}

func newRewardingActionFromABIBinary(data []byte) (actionPayload, error) {
if len(data) <= 4 {
return nil, ErrInvalidABI
}
if act, err := NewClaimFromRewardingFundFromABIBinary(data); err == nil {
return act, nil
}
if act, err := NewDepositToRewardingFundFromABIBinary(data); err == nil {
return act, nil
}
return nil, ErrInvalidABI
}
41 changes: 41 additions & 0 deletions action/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@
package action

import (
"encoding/hex"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/iotexproject/iotex-core/pkg/version"
)
Expand All @@ -27,3 +31,40 @@ func TestActionBuilder(t *testing.T) {
assert.Equal(t, uint64(10003), act.GasLimit())
assert.Equal(t, big.NewInt(10004), act.GasPrice())
}

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

eb := &EnvelopeBuilder{}
eb.SetChainID(2)

claimData, _ := hex.DecodeString("2df163ef000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000")
tx := types.NewTransaction(1, common.HexToAddress("0x0000000000000000000000000000000000000001"), big.NewInt(100), 10000, big.NewInt(10004), claimData)

env, err := eb.BuildRewardingAction(tx)
r.Nil(env)
r.EqualValues("invalid action type", err.Error())

tx = types.NewTransaction(1, common.HexToAddress(_rewardingProtocolAddr.Hex()), big.NewInt(100), 10000, big.NewInt(10004), claimData)
env, err = eb.BuildRewardingAction(tx)
r.Nil(err)
r.IsType(&ClaimFromRewardingFund{}, env.Action())
r.EqualValues(big.NewInt(10004), env.GasPrice())
r.EqualValues(10000, env.GasLimit())
r.EqualValues(big.NewInt(101), env.Action().(*ClaimFromRewardingFund).Amount())

depositData, _ := hex.DecodeString("27852a6b000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003")
tx = types.NewTransaction(1, common.HexToAddress("0x0000000000000000000000000000000000000001"), big.NewInt(100), 10000, big.NewInt(10004), depositData)

env, err = eb.BuildRewardingAction(tx)
r.Nil(env)
r.EqualValues("invalid action type", err.Error())

tx = types.NewTransaction(1, common.HexToAddress(_rewardingProtocolAddr.Hex()), big.NewInt(100), 10000, big.NewInt(10004), depositData)
env, err = eb.BuildRewardingAction(tx)
r.Nil(err)
r.IsType(&DepositToRewardingFund{}, env.Action())
r.EqualValues(big.NewInt(10004), env.GasPrice())
r.EqualValues(10000, env.GasLimit())
r.EqualValues(big.NewInt(101), env.Action().(*DepositToRewardingFund).Amount())
}
87 changes: 87 additions & 0 deletions action/claimreward.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,63 @@
package action

import (
"bytes"
"math/big"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"

"github.com/iotexproject/iotex-address/address"
"github.com/iotexproject/iotex-core/pkg/util/byteutil"
"github.com/iotexproject/iotex-proto/golang/iotextypes"
)

const _claimRewardingInterfaceABI = `[
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint8[]",
"name": "data",
"type": "uint8[]"
}
],
"name": "claim",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]`

var (
// ClaimFromRewardingFundBaseGas represents the base intrinsic gas for claimFromRewardingFund
ClaimFromRewardingFundBaseGas = uint64(10000)
// ClaimFromRewardingFundGasPerByte represents the claimFromRewardingFund payload gas per uint
ClaimFromRewardingFundGasPerByte = uint64(100)

_claimRewardingMethod abi.Method
)

func init() {
claimRewardInterface, err := abi.JSON(strings.NewReader(_claimRewardingInterfaceABI))
if err != nil {
panic(err)
}
var ok bool
_claimRewardingMethod, ok = claimRewardInterface.Methods["claim"]
if !ok {
panic("fail to load the claim method")
}
}

// ClaimFromRewardingFund is the action to claim reward from the rewarding fund
type ClaimFromRewardingFund struct {
AbstractAction
Expand Down Expand Up @@ -108,3 +149,49 @@ func (b *ClaimFromRewardingFundBuilder) Build() ClaimFromRewardingFund {
b.claim.AbstractAction = b.Builder.Build()
return b.claim
}

// encodeABIBinary encodes data in abi encoding
func (c *ClaimFromRewardingFund) encodeABIBinary() ([]byte, error) {
data, err := _claimRewardingMethod.Inputs.Pack(c.Amount(), c.Data())
if err != nil {
return nil, err
}
return append(_claimRewardingMethod.ID, data...), nil
}

// ToEthTx converts action to eth-compatible tx
func (c *ClaimFromRewardingFund) ToEthTx() (*types.Transaction, error) {
addr, err := address.FromString(address.RewardingProtocol)
if err != nil {
return nil, err
}
ethAddr := common.BytesToAddress(addr.Bytes())
data, err := c.encodeABIBinary()
if err != nil {
return nil, err
}
return types.NewTransaction(c.Nonce(), ethAddr, big.NewInt(0), c.GasLimit(), c.GasPrice(), data), nil
}

// NewClaimFromRewardingFundFromABIBinary decodes data into action
func NewClaimFromRewardingFundFromABIBinary(data []byte) (*ClaimFromRewardingFund, error) {
var (
paramsMap = map[string]interface{}{}
ok 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
}
if ac.amount, ok = paramsMap["amount"].(*big.Int); !ok {
return nil, errDecodeFailure
}
if ac.data, ok = paramsMap["data"].([]byte); !ok {
return nil, errDecodeFailure
}
return &ac, nil
}
146 changes: 146 additions & 0 deletions action/claimreward_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package action

import (
"encoding/hex"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/iotexproject/iotex-address/address"
"github.com/stretchr/testify/require"
)

var (
_defaultGasPrice = big.NewInt(1000000000000)
_errNegativeNumberMsg = "negative value"
)

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

rc := &ClaimFromRewardingFund{}
data := rc.Serialize()
r.NotNil(data)

rc.amount = big.NewInt(100)
rc.data = []byte{1}
data = rc.Serialize()
r.NotNil(data)
r.EqualValues("0a03313030120101", hex.EncodeToString(data))
}

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

rc := &ClaimFromRewardingFund{}
gas, err := rc.IntrinsicGas()
r.NoError(err)
r.EqualValues(10000, gas)

rc.amount = big.NewInt(100000000)
gas, err = rc.IntrinsicGas()
r.NoError(err)
r.EqualValues(10000, gas)

rc.data = []byte{1}
gas, err = rc.IntrinsicGas()
r.NoError(err)
r.EqualValues(10100, gas)
}

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

rc := &ClaimFromRewardingFund{}

rc.amount = big.NewInt(1)
err := rc.SanityCheck()
r.NoError(err)

rc.amount = big.NewInt(-1)
err = rc.SanityCheck()
r.NotNil(err)
r.EqualValues(_errNegativeNumberMsg, err.Error())
}

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

rc := &ClaimFromRewardingFund{}
rc.gasPrice = _defaultGasPrice
cost, err := rc.Cost()
r.Nil(err)
r.EqualValues("10000000000000000", cost.String())

rc.amount = big.NewInt(100)
cost, err = rc.Cost()
r.Nil(err)
r.EqualValues("10000000000000000", cost.String())

rc.data = []byte{1}
cost, err = rc.Cost()
r.Nil(err)
r.EqualValues("10100000000000000", cost.String())
}

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

rc := &ClaimFromRewardingFund{}
rc.amount = big.NewInt(101)
data, err := rc.encodeABIBinary()
r.Nil(err)
r.EqualValues(
"2df163ef000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000",
hex.EncodeToString(data),
)

rc.data = []byte{1, 2, 3}
data, err = rc.encodeABIBinary()
r.Nil(err)
r.EqualValues(
"2df163ef000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
hex.EncodeToString(data),
)
}

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

rewardingPool, _ := address.FromString(address.RewardingProtocol)
rewardEthAddr := common.BytesToAddress(rewardingPool.Bytes())

rc := &ClaimFromRewardingFund{}

rc.amount = big.NewInt(101)
tx, err := rc.ToEthTx()
r.Nil(err)
r.EqualValues(rewardEthAddr, *tx.To())
r.EqualValues(
"2df163ef000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000",
hex.EncodeToString(tx.Data()),
)
r.EqualValues("0", tx.Value().String())

rc.data = []byte{1, 2, 3}
tx, err = rc.ToEthTx()
r.Nil(err)
r.EqualValues(rewardEthAddr, *tx.To())
r.EqualValues(
"2df163ef000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
hex.EncodeToString(tx.Data()),
)
r.EqualValues("0", tx.Value().String())
}

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

data, _ := hex.DecodeString("2df163ef000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003")

rc, err := NewClaimFromRewardingFundFromABIBinary(data)
r.Nil(err)
r.IsType(&ClaimFromRewardingFund{}, rc)
r.EqualValues("101", rc.Amount().String())
r.EqualValues([]byte{1, 2, 3}, rc.Data())
}
Loading