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

feat: wallet withdraw test #386

Merged
merged 13 commits into from
Mar 15, 2024
5 changes: 5 additions & 0 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ checks:
options:
timeout: 5m
type: pingpong
withdraw:
options:
target-address: 0xec44cb15b1b033e74d55ac5d0e24d861bde54532
timeout: 5m
type: withdraw
pss:
options:
address-prefix: 2
Expand Down
6 changes: 6 additions & 0 deletions config/local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ bee-configs:
welcome-message: "Welcome to the Swarm, this is a local cluster!"
warmup-time: 0s
allow-private-cidrs: true
withdrawal-addresses-whitelist: 0xec44cb15b1b033e74d55ac5d0e24d861bde54532

bootnode-local:
_inherit: "bee-local"
Expand Down Expand Up @@ -263,6 +264,11 @@ checks:
options:
timeout: 5m
type: pingpong
ci-withdraw:
options:
target-address: 0xec44cb15b1b033e74d55ac5d0e24d861bde54532
timeout: 5m
type: withdraw
ci-pss:
options:
count: 3
Expand Down
2 changes: 2 additions & 0 deletions pkg/bee/api/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ var policies = [][]string{
{"maintainer", "/chequebook/cheque", "GET"},
{"maintainer", "/chequebook/address", "GET"},
{"maintainer", "/chequebook/balance", "GET"},
{"maintainer", "/wallet", "GET"},
{"maintainer", "/wallet/withdraw/*", "POST"},
{"maintainer", "/chunks/*", "(GET)|(DELETE)"},
{"maintainer", "/reservestate", "GET"},
{"maintainer", "/chainstate", "GET"},
Expand Down
31 changes: 31 additions & 0 deletions pkg/bee/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"sync"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/swarm"
"github.com/ethersphere/beekeeper/pkg/bee/api"
"github.com/ethersphere/beekeeper/pkg/bee/debugapi"
Expand Down Expand Up @@ -847,3 +848,33 @@ func (c *Client) GetStake(ctx context.Context) (*big.Int, error) {
func (c *Client) WithdrawStake(ctx context.Context) (string, error) {
return c.debug.Stake.WithdrawStake(ctx)
}

// WalletBalance fetches the balance for the given token
func (c *Client) WalletBalance(ctx context.Context, token string) (*big.Int, error) {
resp, err := c.debug.Node.Wallet(ctx)
if err != nil {
return nil, err
}

if token == "BZZ" {
return resp.BZZ.Int, nil
}

return resp.NativeToken.Int, nil
}

// Withdraw transfers token from eth address to the provided address
func (c *Client) Withdraw(ctx context.Context, token, addr string, amount int64) error {
resp, err := c.debug.Node.Withdraw(ctx, token, addr, amount)
if err != nil {
return err
}

var zeroHash common.Hash

if resp == zeroHash {
return errors.New("withdraw returned zero hash")
}

return nil
}
28 changes: 28 additions & 0 deletions pkg/bee/debugapi/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package debugapi

import (
"context"
"fmt"
"net/http"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/beekeeper/pkg/bigint"

"github.com/ethersphere/bee/pkg/swarm"
Expand Down Expand Up @@ -249,3 +251,29 @@ func (n *NodeService) Topology(ctx context.Context) (resp Topology, err error) {

return
}

type Wallet struct {
BZZ *bigint.BigInt `json:"bzzBalance"`
NativeToken *bigint.BigInt `json:"nativeTokenBalance"`
}

// Wallet returns the wallet state
func (n *NodeService) Wallet(ctx context.Context) (resp Wallet, err error) {
err = n.client.requestJSON(ctx, http.MethodGet, "/wallet", nil, &resp)
return
}

// Withdraw calls wallet withdraw endpoint
func (n *NodeService) Withdraw(ctx context.Context, token, addr string, amount int64) (tx common.Hash, err error) {
endpoint := fmt.Sprintf("/wallet/withdraw/%s?address=%s&amount=%d", token, addr, amount)

r := struct {
TransactionHash common.Hash `json:"transactionHash"`
}{}

if err = n.client.requestJSON(ctx, http.MethodPost, endpoint, nil, &r); err != nil {
return
}

return r.TransactionHash, nil
}
81 changes: 81 additions & 0 deletions pkg/check/withdraw/withdraw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package withdraw

import (
"context"
"errors"
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/beekeeper/pkg/beekeeper"
"github.com/ethersphere/beekeeper/pkg/logging"
"github.com/ethersphere/beekeeper/pkg/orchestration"
test "github.com/ethersphere/beekeeper/pkg/test"
)

// Options represents check options
type Options struct {
TargetAddr string
}

// NewDefaultOptions returns new default options
func NewDefaultOptions() Options {
return Options{}
}

// compile check whether Check implements interface
var _ beekeeper.Action = (*Check)(nil)

// Check instance
type Check struct {
logger logging.Logger
}

// NewCheck returns new check
func NewCheck(logger logging.Logger) beekeeper.Action {
return &Check{
logger: logger,
}
}

func (c *Check) Run(ctx context.Context, cluster orchestration.Cluster, opts interface{}) (err error) {
o, ok := opts.(Options)
if !ok {
return fmt.Errorf("invalid options type")
}

var checkCase *test.CheckCase

if checkCase, err = test.NewCheckCase(ctx, cluster, test.CaseOptions{}, c.logger); err != nil {
return err
}

target := checkCase.Bee(1)

c.logger.Infof("target is %s", target.Name())

c.logger.Info("withdrawing native...")

if err := target.Withdraw(ctx, "NativeToken", o.TargetAddr); err != nil {
Copy link
Member

@istae istae Mar 13, 2024

Choose a reason for hiding this comment

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

the balance before and after the withdrawal should be compared

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's being tested inside the function, this way I can use the same method for different coin/addresses:

https://github.com/ethersphere/beekeeper/pull/386/files#diff-26ffebd2abeeaff0e1d88eab9a47503856f31fcfc6fd6704b431ea5c4b72727cR125

return fmt.Errorf("withdraw native: %w", err)
}

c.logger.Info("success")
c.logger.Info("withdrawing to a non whitelisted address")

var zeroAddr common.Address

if err := target.Withdraw(ctx, "NativeToken", zeroAddr.String()); err == nil {
return errors.New("withdraw to non-whitelisted address expected to fail")
}

c.logger.Info("success")
c.logger.Info("withdrawing bzz...")

if err := target.Withdraw(ctx, "BZZ", o.TargetAddr); err != nil {
return fmt.Errorf("withdraw bzz: %w", err)
}

c.logger.Info("success")

return nil
}
1 change: 1 addition & 0 deletions pkg/config/bee.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type BeeConfig struct {
Verbosity *uint64 `yaml:"verbosity"`
WelcomeMessage *string `yaml:"welcome-message"`
WarmupTime *time.Duration `yaml:"warmup-time"`
WithdrawAddress *string `yaml:"withdrawal-addresses-whitelist"`
}

// Export exports BeeConfig to orchestration.Config
Expand Down
19 changes: 19 additions & 0 deletions pkg/config/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/ethersphere/beekeeper/pkg/check/settlements"
"github.com/ethersphere/beekeeper/pkg/check/smoke"
"github.com/ethersphere/beekeeper/pkg/check/soc"
"github.com/ethersphere/beekeeper/pkg/check/withdraw"
"github.com/ethersphere/beekeeper/pkg/logging"
"github.com/ethersphere/beekeeper/pkg/random"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -574,6 +575,24 @@ var Checks = map[string]CheckType{
return nil, fmt.Errorf("applying options: %w", err)
}

return opts, nil
},
},
"withdraw": {
NewAction: withdraw.NewCheck,
NewOptions: func(checkGlobalConfig CheckGlobalConfig, check Check) (interface{}, error) {
checkOpts := new(struct {
TargetAddr *string `yaml:"target-address"`
})
if err := check.Options.Decode(checkOpts); err != nil {
return nil, fmt.Errorf("decoding check %s options: %w", check.Type, err)
}
opts := withdraw.NewDefaultOptions()

if err := applyCheckConfig(checkGlobalConfig, checkOpts, &opts); err != nil {
return nil, fmt.Errorf("applying options: %w", err)
}

return opts, nil
},
},
Expand Down
1 change: 1 addition & 0 deletions pkg/orchestration/k8s/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ tracing-service-name: {{.TracingServiceName}}
verbosity: {{.Verbosity}}
welcome-message: {{.WelcomeMessage}}
warmup-time: {{.WarmupTime}}
withdrawal-addresses-whitelist: {{.WithdrawAddress}}
`
)

Expand Down
1 change: 1 addition & 0 deletions pkg/orchestration/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,5 @@ type Config struct {
Verbosity uint64 // log verbosity level 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=trace
WelcomeMessage string // send a welcome message string during handshakes
WarmupTime time.Duration // warmup time pull/pushsync protocols
WithdrawAddress string // allowed addresses for wallet withdrawal
}
34 changes: 34 additions & 0 deletions pkg/test/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"bytes"
"context"
"fmt"
"math/big"
"math/rand"
"time"

"github.com/ethersphere/bee/pkg/swarm"
"github.com/ethersphere/beekeeper/pkg/bee"
Expand Down Expand Up @@ -96,3 +98,35 @@ func (b *BeeV2) NewChunkUploader(ctx context.Context) (*ChunkUploader, error) {
logger: b.logger,
}, nil
}

type Wallet struct {
BZZ, Native *big.Int
}

const amount int64 = 1000000

func (b *BeeV2) Withdraw(ctx context.Context, token, addr string) error {
before, err := b.client.WalletBalance(ctx, token)
if err != nil {
return fmt.Errorf("(%s) wallet balance %w", b.name, err)
}

if err := b.client.Withdraw(ctx, token, addr, amount); err != nil {
return fmt.Errorf("(%s) withdraw balance %w", b.name, err)
}

time.Sleep(3 * time.Second)

after, err := b.client.WalletBalance(ctx, token)
if err != nil {
return fmt.Errorf("(%s) wallet balance %w", b.name, err)
}

want := big.NewInt(0).Sub(before, big.NewInt(amount))

if after.Cmp(want) > 0 {
return fmt.Errorf("incorrect balance after withdraw:\ngot %d\nwant %d", after, want)
}

return nil
}
Loading