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

Feature: Multiasset Support #18

Merged
merged 6 commits into from
Jul 18, 2024
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
26 changes: 17 additions & 9 deletions channel/adjudicator.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,20 @@ type Adjudicator struct {
log log.Embedding
CB *client.ContractBackend
acc *wallet.Account
assetAddr xdr.ScAddress
assetAddrs xdr.ScVec //xdr.ScAddress
perunAddr xdr.ScAddress
maxIters int
pollingInterval time.Duration
}

// NewAdjudicator returns a new Adjudicator.
func NewAdjudicator(acc *wallet.Account, cb *client.ContractBackend, perunID xdr.ScAddress, assetID xdr.ScAddress) *Adjudicator {
func NewAdjudicator(acc *wallet.Account, cb *client.ContractBackend, perunID xdr.ScAddress, assetIDs xdr.ScVec) *Adjudicator {
return &Adjudicator{
challengeDuration: &DefaultChallengeDuration,
CB: cb,
acc: acc,
perunAddr: perunID,
assetAddr: assetID,
assetAddrs: assetIDs,
maxIters: MaxIterationsUntilAbort,
pollingInterval: DefaultPollingInterval,
log: log.MakeEmbedding(log.Default()),
Expand All @@ -60,20 +60,26 @@ func (a *Adjudicator) GetPerunAddr() xdr.ScAddress {
return a.perunAddr
}

func (a *Adjudicator) GetAssetAddr() xdr.ScAddress {
return a.assetAddr
func (a *Adjudicator) GetAssetAddrs() []xdr.ScAddress {
var addrs []xdr.ScAddress
for _, addrScVal := range a.assetAddrs {
addr := addrScVal.MustAddress()
addrs = append(addrs, addr)
}

return addrs
}

func (a *Adjudicator) Subscribe(ctx context.Context, cid pchannel.ID) (pchannel.AdjudicatorSubscription, error) {
perunAddr := a.GetPerunAddr()
assetAddr := a.GetAssetAddr()
return NewAdjudicatorSub(ctx, cid, a.CB, perunAddr, assetAddr, a.challengeDuration)
assetAddrs := a.GetAssetAddrs()
return NewAdjudicatorSub(ctx, cid, a.CB, perunAddr, assetAddrs, a.challengeDuration)
}

func (a *Adjudicator) Withdraw(ctx context.Context, req pchannel.AdjudicatorReq, smap pchannel.StateMap) error {

if req.Tx.State.IsFinal {
log.Println("Withdraw called")
log.Println("Withdraw called by Adjudicator")

if err := a.Close(ctx, req.Tx.State, req.Tx.Sigs); err != nil {
chanControl, errChanState := a.CB.GetChannelInfo(ctx, a.perunAddr, req.Tx.State.ID)
Expand Down Expand Up @@ -108,13 +114,15 @@ func (a *Adjudicator) Withdraw(ctx context.Context, req pchannel.AdjudicatorReq,
}

func (a *Adjudicator) withdraw(ctx context.Context, req pchannel.AdjudicatorReq) error {
log.Println("withdraw called by Adjudicator")

perunAddress := a.GetPerunAddr()
return a.CB.Withdraw(ctx, perunAddress, req)
}

func (a *Adjudicator) Close(ctx context.Context, state *pchannel.State, sigs []pwallet.Sig) error {

log.Println("Close called")
log.Println("Close called by Adjudicator")
perunAddr := a.GetPerunAddr()

return a.CB.Close(ctx, perunAddr, state, sigs)
Expand Down
6 changes: 3 additions & 3 deletions channel/adjudicator_sub.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type AdjEventSub struct {
chanControl wire.Control
cid pchannel.ID
perunAddr xdr.ScAddress
assetAddr xdr.ScAddress
assetAddrs []xdr.ScAddress
events chan event.PerunEvent
subErrors chan error
err error
Expand All @@ -48,15 +48,15 @@ type AdjEventSub struct {
log log.Embedding
}

func NewAdjudicatorSub(ctx context.Context, cid pchannel.ID, cb *client.ContractBackend, perunAddr xdr.ScAddress, assetAddr xdr.ScAddress, challengeDuration *time.Duration) (pchannel.AdjudicatorSubscription, error) {
func NewAdjudicatorSub(ctx context.Context, cid pchannel.ID, cb *client.ContractBackend, perunAddr xdr.ScAddress, assetAddrs []xdr.ScAddress, challengeDuration *time.Duration) (pchannel.AdjudicatorSubscription, error) {

sub := &AdjEventSub{
challengeDuration: challengeDuration,
cb: cb,
chanControl: wire.Control{},
cid: cid,
perunAddr: perunAddr,
assetAddr: assetAddr,
assetAddrs: assetAddrs,
events: make(chan event.PerunEvent, DefaultBufferSize),
subErrors: make(chan error, 1),
pollInterval: DefaultSubscriptionPollingInterval,
Expand Down
7 changes: 0 additions & 7 deletions channel/adjudicator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ func TestHappyChannel(t *testing.T) {
adjBob := setup.GetAdjudicators()[1]

ctxAliceWithdraw := setup.NewCtx(chtest.DefaultTestTimeout)
ctxBobWithdraw := setup.NewCtx(chtest.DefaultTestTimeout)

adjState := perunState
next := adjState.Clone()
Expand Down Expand Up @@ -91,12 +90,6 @@ func TestHappyChannel(t *testing.T) {

require.NoError(t, err)
require.NoError(t, adjBob.Withdraw(ctx, reqBob, nil))
perunAddrBob := adjBob.GetPerunAddr()
stellarChanBob, err := adjBob.CB.GetChannelInfo(ctxBobWithdraw, perunAddrBob, next.ID)

require.NoError(t, err)

require.True(t, stellarChanBob.Control.WithdrawnB)

}

Expand Down
2 changes: 1 addition & 1 deletion channel/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"os"
)

const PerunContractPath = "../testdata/perun_soroban_contract.wasm"
const PerunContractPath = "../testdata/perun_soroban_multi_contract.wasm"

func AssembleInstallContractCodeOp(sourceAccount string, wasmFileName string) *txnbuild.InvokeHostFunction {
// Assemble the InvokeHostFunction UploadContractWasm operation:
Expand Down
19 changes: 12 additions & 7 deletions channel/funder.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ const DefaultPollingInterval = time.Duration(6) * time.Second
type Funder struct {
cb *client.ContractBackend
perunAddr xdr.ScAddress
assetAddr xdr.ScAddress
assetAddrs xdr.ScVec
maxIters int
pollingInterval time.Duration
}

func NewFunder(acc *wallet.Account, contractBackend *client.ContractBackend, perunAddr xdr.ScAddress, assetAddr xdr.ScAddress) *Funder {
func NewFunder(acc *wallet.Account, contractBackend *client.ContractBackend, perunAddr xdr.ScAddress, assetAddrs xdr.ScVec) *Funder {
return &Funder{
cb: contractBackend,
perunAddr: perunAddr,
assetAddr: assetAddr,
assetAddrs: assetAddrs,
maxIters: MaxIterationsUntilAbort,
pollingInterval: DefaultPollingInterval,
}
Expand All @@ -51,8 +51,13 @@ func (f *Funder) GetPerunAddr() xdr.ScAddress {
return f.perunAddr
}

func (f *Funder) GetAssetAddr() xdr.ScAddress {
return f.assetAddr
func (f *Funder) GetAssetAddrs() []xdr.ScAddress {
var addrs []xdr.ScAddress
for _, addrScVal := range f.assetAddrs {
addr := addrScVal.MustAddress()
addrs = append(addrs, addr)
}
return addrs
}

func (f *Funder) Fund(ctx context.Context, req pchannel.FundingReq) error {
Expand Down Expand Up @@ -136,11 +141,11 @@ func (f *Funder) FundChannel(ctx context.Context, state *pchannel.State, funderI
return errors.New("error while making balances")
}

if !balsStellar.Token.Equals(f.assetAddr) {
if !balsStellar.Tokens.Equals(&f.assetAddrs) {
return errors.New("asset address is not equal to the address stored in the state")
}

return f.cb.Fund(ctx, f.perunAddr, f.assetAddr, state.ID, funderIdx)
return f.cb.Fund(ctx, f.perunAddr, state.ID, funderIdx)
}

func (f *Funder) AbortChannel(ctx context.Context, state *pchannel.State) error {
Expand Down
4 changes: 2 additions & 2 deletions channel/funder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ func TestFunding_Happy(t *testing.T) {

func TestFunding_TimeoutNotFunded(t *testing.T) {
setup := chtest.NewTestSetup(t)
stellarAsset := setup.GetTokenAsset()
stellarAssets := setup.GetTokenAsset()
accs := setup.GetAccounts()
addrAlice := accs[0].Address()
addrBob := accs[1].Address()
addrList := []pwallet.Address{addrAlice, addrBob}
perunParams, perunState := chtest.NewParamsWithAddressStateWithAsset(t, addrList, stellarAsset)
perunParams, perunState := chtest.NewParamsWithAddressStateWithAsset(t, addrList, stellarAssets)
freqAlice := pchannel.NewFundingReq(perunParams, perunState, 0, perunState.Balances)
freqBob := pchannel.NewFundingReq(perunParams, perunState, 1, perunState.Balances)
freqs := []*pchannel.FundingReq{freqAlice, freqBob}
Expand Down
100 changes: 62 additions & 38 deletions channel/test/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,29 +34,30 @@ import (
"perun.network/perun-stellar-backend/channel/types"
"perun.network/perun-stellar-backend/client"
"perun.network/perun-stellar-backend/wallet"
"perun.network/perun-stellar-backend/wire/scval"
pkgtest "polycry.pt/poly-go/test"
"runtime"
"testing"
"time"
)

const (
PerunContractPath = "testdata/perun_soroban_contract.wasm"
PerunContractPath = "testdata/perun_soroban_multi_contract.wasm"
StellarAssetContractPath = "testdata/perun_soroban_token.wasm"
initLumensBalance = "10000000"
initTokenBalance = uint64(2000000)
DefaultTestTimeout = 30
)

type Setup struct {
t *testing.T
accs []*wallet.Account
ws []*wallet.EphemeralWallet
cbs []*client.ContractBackend
Rng *mathrand.Rand
funders []*channel.Funder
adjs []*channel.Adjudicator
assetID pchannel.Asset
t *testing.T
accs []*wallet.Account
ws []*wallet.EphemeralWallet
cbs []*client.ContractBackend
Rng *mathrand.Rand
funders []*channel.Funder
adjs []*channel.Adjudicator
assetIDs []pchannel.Asset
}

func (s *Setup) GetStellarClients() []*client.ContractBackend {
Expand All @@ -71,8 +72,8 @@ func (s *Setup) GetAdjudicators() []*channel.Adjudicator {
return s.adjs
}

func (s *Setup) GetTokenAsset() pchannel.Asset {
return s.assetID
func (s *Setup) GetTokenAsset() []pchannel.Asset {
return s.assetIDs
}

func (s *Setup) GetAccounts() []*wallet.Account {
Expand Down Expand Up @@ -103,26 +104,40 @@ func getDataFilePath(filename string) (string, error) {

func NewTestSetup(t *testing.T) *Setup {

accs, kpsToFund, ws := MakeRandPerunAccsWallets(4)
accs, kpsToFund, ws := MakeRandPerunAccsWallets(5)
require.NoError(t, CreateFundStellarAccounts(kpsToFund, initLumensBalance))

depTokenKp := kpsToFund[2]
depPerunKp := kpsToFund[3]
depTokenOneKp := kpsToFund[2]
depTokenTwoKp := kpsToFund[3]

depTokenKps := []*keypair.Full{depTokenOneKp, depTokenTwoKp}

depPerunKp := kpsToFund[4]

relPathPerun, err := getDataFilePath(PerunContractPath)
require.NoError(t, err)
relPathAsset, err := getDataFilePath(StellarAssetContractPath)
require.NoError(t, err)

tokenAddress, _ := Deploy(t, depTokenKp, relPathAsset)
perunAddress, _ := Deploy(t, depPerunKp, relPathPerun)

require.NoError(t, InitTokenContract(depTokenKp, tokenAddress))
tokenAddressOne, _ := Deploy(t, depTokenOneKp, relPathAsset)
tokenAddressTwo, _ := Deploy(t, depTokenTwoKp, relPathAsset)

SetupAccountsAndContracts(t, depTokenKp, kpsToFund[:2], tokenAddress, initTokenBalance)
tokenAddresses := []xdr.ScAddress{tokenAddressOne, tokenAddressTwo}

assetContractID, err := types.NewStellarAssetFromScAddress(tokenAddress)
require.NoError(t, err)
require.NoError(t, InitTokenContract(depTokenOneKp, tokenAddressOne))
require.NoError(t, InitTokenContract(depTokenTwoKp, tokenAddressTwo))

SetupAccountsAndContracts(t, depTokenKps, kpsToFund[:2], tokenAddresses, initTokenBalance)

var assetContractIDs []pchannel.Asset

for _, tokenAddress := range tokenAddresses {
assetContractID, err := types.NewStellarAssetFromScAddress(tokenAddress)
require.NoError(t, err)
assetContractIDs = append(assetContractIDs, assetContractID)
}

cbs := NewContractBackendsFromKeys(kpsToFund[:2])

Expand All @@ -136,34 +151,43 @@ func NewTestSetup(t *testing.T) *Setup {
channelCBs := []*client.ContractBackend{aliceCB, bobCB}
channelWallets := []*wallet.EphemeralWallet{aliceWallet, bobWallet}

funders, adjs := CreateFundersAndAdjudicators(channelAccs, cbs, perunAddress, tokenAddress)
funders, adjs := CreateFundersAndAdjudicators(channelAccs, cbs, perunAddress, tokenAddresses)

setup := Setup{
t: t,
accs: channelAccs,
ws: channelWallets,
cbs: channelCBs,
funders: funders,
adjs: adjs,
assetID: assetContractID,
t: t,
accs: channelAccs,
ws: channelWallets,
cbs: channelCBs,
funders: funders,
adjs: adjs,
assetIDs: assetContractIDs,
}

return &setup
}

func SetupAccountsAndContracts(t *testing.T, deployerKp *keypair.Full, kps []*keypair.Full, tokenAddress xdr.ScAddress, tokenBalance uint64) {
for _, kp := range kps {
addr, err := types.MakeAccountAddress(kp)
require.NoError(t, err)
require.NoError(t, MintToken(deployerKp, tokenAddress, tokenBalance, addr))
func SetupAccountsAndContracts(t *testing.T, deployerKps []*keypair.Full, kps []*keypair.Full, tokenAddresses []xdr.ScAddress, tokenBalance uint64) {

require.Equal(t, len(deployerKps), len(tokenAddresses))

for i, _ := range deployerKps {
for _, kp := range kps {
addr, err := types.MakeAccountAddress(kp)
require.NoError(t, err)
require.NoError(t, MintToken(deployerKps[i], tokenAddresses[i], tokenBalance, addr))

}
}
}
func CreateFundersAndAdjudicators(accs []*wallet.Account, cbs []*client.ContractBackend, perunAddress, tokenAddress xdr.ScAddress) ([]*channel.Funder, []*channel.Adjudicator) {
func CreateFundersAndAdjudicators(accs []*wallet.Account, cbs []*client.ContractBackend, perunAddress xdr.ScAddress, tokenScAddresses []xdr.ScAddress) ([]*channel.Funder, []*channel.Adjudicator) {
funders := make([]*channel.Funder, len(accs))
adjs := make([]*channel.Adjudicator, len(accs))

tokenVecAddresses := scval.MakeScVecFromScAddresses(tokenScAddresses)

for i, acc := range accs {
funders[i] = channel.NewFunder(acc, cbs[i], perunAddress, tokenAddress)
adjs[i] = channel.NewAdjudicator(acc, cbs[i], perunAddress, tokenAddress)
funders[i] = channel.NewFunder(acc, cbs[i], perunAddress, tokenVecAddresses)
adjs[i] = channel.NewAdjudicator(acc, cbs[i], perunAddress, tokenVecAddresses)
}
return funders, adjs
}
Expand Down Expand Up @@ -301,21 +325,21 @@ func CreateFundStellarAccounts(pairs []*keypair.Full, initialBalance string) err
return nil
}

func NewParamsWithAddressStateWithAsset(t *testing.T, partsAddr []pwallet.Address, asset pchannel.Asset) (*pchannel.Params, *pchannel.State) {
func NewParamsWithAddressStateWithAsset(t *testing.T, partsAddr []pwallet.Address, assets []pchannel.Asset) (*pchannel.Params, *pchannel.State) {

rng := pkgtest.Prng(t)

numParts := 2

return ptest.NewRandomParamsAndState(rng, ptest.WithNumLocked(0).Append(
ptest.WithAssets(asset),
ptest.WithAssets(assets...),
ptest.WithNumAssets(len(assets)),
ptest.WithVersion(0),
ptest.WithNumParts(numParts),
ptest.WithParts(partsAddr...),
ptest.WithIsFinal(false),
ptest.WithLedgerChannel(true),
ptest.WithVirtualChannel(false),
ptest.WithNumAssets(1),
ptest.WithoutApp(),
ptest.WithBalancesInRange(big.NewInt(0).Mul(big.NewInt(1), big.NewInt(100_000)), big.NewInt(0).Mul(big.NewInt(1), big.NewInt(100_000))),
))
Expand Down
Loading
Loading