From 4280d842653c3f9e87d839a4c7380c92e747fc3f Mon Sep 17 00:00:00 2001 From: Ilja von Hoessle Date: Wed, 19 Jun 2024 16:00:58 +0200 Subject: [PATCH 1/6] feat: Adapt packages to Multiasset extension --- channel/adjudicator.go | 20 ++-- channel/adjudicator_sub.go | 6 +- channel/funder.go | 19 ++-- channel/funder_test.go | 4 +- channel/test/setup.go | 97 +++++++++++------ channel/types/asset.go | 2 +- channel/types/asset_test.go | 4 +- client/client.go | 4 +- client/transaction.go | 2 +- wire/balances.go | 212 ++++++++++++++++++++++++++---------- wire/balances_test.go | 5 +- wire/channel_test.go | 4 +- wire/scval/scval.go | 25 ++++- wire/state.go | 85 +++++++++++++-- wire/state_test.go | 10 +- 15 files changed, 361 insertions(+), 138 deletions(-) diff --git a/channel/adjudicator.go b/channel/adjudicator.go index a865f54..3f7a63f 100644 --- a/channel/adjudicator.go +++ b/channel/adjudicator.go @@ -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()), @@ -60,14 +60,20 @@ 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 { diff --git a/channel/adjudicator_sub.go b/channel/adjudicator_sub.go index 4dcd01b..e29138b 100644 --- a/channel/adjudicator_sub.go +++ b/channel/adjudicator_sub.go @@ -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 @@ -48,7 +48,7 @@ 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, @@ -56,7 +56,7 @@ func NewAdjudicatorSub(ctx context.Context, cid pchannel.ID, cb *client.Contract chanControl: wire.Control{}, cid: cid, perunAddr: perunAddr, - assetAddr: assetAddr, + assetAddrs: assetAddrs, events: make(chan event.PerunEvent, DefaultBufferSize), subErrors: make(chan error, 1), pollInterval: DefaultSubscriptionPollingInterval, diff --git a/channel/funder.go b/channel/funder.go index 7384156..fde1150 100644 --- a/channel/funder.go +++ b/channel/funder.go @@ -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, } @@ -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 { @@ -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 { diff --git a/channel/funder_test.go b/channel/funder_test.go index ab3e928..7dd31d3 100644 --- a/channel/funder_test.go +++ b/channel/funder_test.go @@ -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} diff --git a/channel/test/setup.go b/channel/test/setup.go index 29027e9..1564b9f 100644 --- a/channel/test/setup.go +++ b/channel/test/setup.go @@ -34,6 +34,7 @@ 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" @@ -49,14 +50,14 @@ const ( ) 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 { @@ -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 { @@ -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) + tokenAddressOne, _ := Deploy(t, depTokenOneKp, relPathAsset) + tokenAddressTwo, _ := Deploy(t, depTokenTwoKp, relPathAsset) + + tokenAddresses := []xdr.ScAddress{tokenAddressOne, tokenAddressTwo} + perunAddress, _ := Deploy(t, depPerunKp, relPathPerun) - require.NoError(t, InitTokenContract(depTokenKp, tokenAddress)) + require.NoError(t, InitTokenContract(depTokenOneKp, tokenAddressOne)) + require.NoError(t, InitTokenContract(depTokenTwoKp, tokenAddressTwo)) - SetupAccountsAndContracts(t, depTokenKp, kpsToFund[:2], tokenAddress, initTokenBalance) + SetupAccountsAndContracts(t, depTokenKps, kpsToFund[:2], tokenAddresses, initTokenBalance) - assetContractID, err := types.NewStellarAssetFromScAddress(tokenAddress) - require.NoError(t, err) + 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]) @@ -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 } @@ -301,14 +325,15 @@ 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...), diff --git a/channel/types/asset.go b/channel/types/asset.go index 7875650..52766ce 100644 --- a/channel/types/asset.go +++ b/channel/types/asset.go @@ -1,4 +1,4 @@ -// Copyright 2023 PolyCrypt GmbH +// Copyright 2024 PolyCrypt GmbH // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/channel/types/asset_test.go b/channel/types/asset_test.go index bbc9e30..3d1062b 100644 --- a/channel/types/asset_test.go +++ b/channel/types/asset_test.go @@ -1,4 +1,4 @@ -// Copyright 2023 PolyCrypt GmbH +// Copyright 2024 PolyCrypt GmbH // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ import ( "testing" ) -func TestMarshalAndUnmarshalBinary(t *testing.T) { +func TestAssetMarshalAndUnmarshalBinary(t *testing.T) { var hash xdr.Hash copy(hash[:], []byte("testhashfortestingonly!testhash")) diff --git a/client/client.go b/client/client.go index f0870f6..1b3c0ef 100644 --- a/client/client.go +++ b/client/client.go @@ -83,9 +83,9 @@ func (cb *ContractBackend) Abort(ctx context.Context, perunAddr xdr.ScAddress, s return nil } -func (cb *ContractBackend) Fund(ctx context.Context, perunAddr xdr.ScAddress, assetAddr xdr.ScAddress, chanID pchannel.ID, fudnerIdx bool) error { +func (cb *ContractBackend) Fund(ctx context.Context, perunAddr xdr.ScAddress, chanID pchannel.ID, funderIdx bool) error { - fundTxArgs, err := buildChanIdxTxArgs(chanID, fudnerIdx) + fundTxArgs, err := buildChanIdxTxArgs(chanID, funderIdx) if err != nil { return errors.New("error while building fund tx") } diff --git a/client/transaction.go b/client/transaction.go index be7730d..a37d75f 100644 --- a/client/transaction.go +++ b/client/transaction.go @@ -137,7 +137,7 @@ func syncWithSorobanRPC(ledgerToWaitFor uint32) { result := struct { Sequence uint32 `json:"sequence"` }{} - ch := jhttp.NewChannel("http://localhost:"+strconv.Itoa(sorobanRPCPort)+"/soroban/rpc", nil) ///soroban/rpc: + ch := jhttp.NewChannel("http://localhost:"+strconv.Itoa(sorobanRPCPort)+"/soroban/rpc", nil) sorobanRPCClient := jrpc2.NewClient(ch, nil) err := sorobanRPCClient.CallResult(context.Background(), "getLatestLedger", nil, &result) if err != nil { diff --git a/wire/balances.go b/wire/balances.go index ef70d86..9ea5ac1 100644 --- a/wire/balances.go +++ b/wire/balances.go @@ -29,28 +29,28 @@ import ( var MaxBalance = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 127), big.NewInt(1)) type Balances struct { - BalA xdr.Int128Parts - BalB xdr.Int128Parts - Token xdr.ScAddress + BalA xdr.ScVec //{xdr.Int128Parts, xdr.Int128Parts} + BalB xdr.ScVec //{xdr.Int128Parts, xdr.Int128Parts} + Tokens xdr.ScVec // multiasset xdr.ScAddress -> xdr.ScVec{xdr.ScAddress1, xdr.ScAddress2} } const ( - SymbolBalancesBalA xdr.ScSymbol = "bal_a" - SymbolBalancesBalB xdr.ScSymbol = "bal_b" - SymbolBalancesToken xdr.ScSymbol = "token" + SymbolBalancesBalA xdr.ScSymbol = "bal_a" + SymbolBalancesBalB xdr.ScSymbol = "bal_b" + SymbolBalancesTokens xdr.ScSymbol = "tokens" ) func (b Balances) ToScVal() (xdr.ScVal, error) { var err error - balA, err := scval.WrapInt128Parts(b.BalA) + balA, err := scval.WrapVec(b.BalA) if err != nil { return xdr.ScVal{}, err } - balB, err := scval.WrapInt128Parts(b.BalB) + balB, err := scval.WrapVec(b.BalB) if err != nil { return xdr.ScVal{}, err } - token, err := scval.WrapScAddress(b.Token) + tokens, err := scval.WrapVec(b.Tokens) if err != nil { return xdr.ScVal{}, err } @@ -58,9 +58,9 @@ func (b Balances) ToScVal() (xdr.ScVal, error) { []xdr.ScSymbol{ SymbolBalancesBalA, SymbolBalancesBalB, - SymbolBalancesToken, + SymbolBalancesTokens, }, - []xdr.ScVal{balA, balB, token}, + []xdr.ScVal{balA, balB, tokens}, ) if err != nil { return xdr.ScVal{}, err @@ -80,29 +80,34 @@ func (b *Balances) FromScVal(v xdr.ScVal) error { if err != nil { return err } - balA, ok := balAVal.GetI128() + + balA, ok := balAVal.GetVec() if !ok { - return errors.New("expected i128") + return errors.New("expected vec of i128") } + balBVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolBalancesBalB), *m) if err != nil { return err } - balB, ok := balBVal.GetI128() + + balB, ok := balBVal.GetVec() if !ok { - return errors.New("expected i128") + return errors.New("expected vec of i128") } - tokenVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolBalancesToken), *m) + + tokenVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolBalancesTokens), *m) if err != nil { return err } - token, ok := tokenVal.GetAddress() + tokens, ok := tokenVal.GetVec() if !ok { - return errors.New("expected address") + return errors.New("expected vec of addresses") } - b.BalA = balA - b.BalB = balB - b.Token = token + + b.BalA = *balA + b.BalB = *balB + b.Tokens = *tokens return nil } @@ -148,67 +153,126 @@ func MakeBalances(alloc channel.Allocation) (Balances, error) { if err := alloc.Valid(); err != nil { return Balances{}, err } - // single asset - if len(alloc.Assets) != 1 { - return Balances{}, errors.New("expected exactly one asset") - } // No sub-channels if len(alloc.Locked) != 0 { return Balances{}, errors.New("expected no locked funds") } - asset := alloc.Assets[0] - sa, err := types.ToStellarAsset(asset) - if err != nil { - return Balances{}, err - } - token, err := sa.MakeScAddress() - if err != nil { - return Balances{}, err + assets := alloc.Assets + var stAssets []channel.Asset + + var tokens xdr.ScVec + for i, ast := range assets { + _, ok := ast.(*types.StellarAsset) + if !ok { + return Balances{}, errors.New("expected stellar asset") + } + sa, err := types.ToStellarAsset(assets[i]) + if err != nil { + return Balances{}, err + } + token, err := sa.MakeScAddress() + if err != nil { + return Balances{}, err + } + + tokenVal := scval.MustWrapScAddress(token) + + tokens = append(tokens, tokenVal) + stAssets = append(stAssets, sa) + } if alloc.NumParts() != 2 { return Balances{}, errors.New("expected exactly two parts") } - balA := alloc.Balance(0, asset) - xdrBalA, err := MakeInt128Parts(balA) - if err != nil { - return Balances{}, err - } + bals := channel.NewAllocation(2, stAssets...) //alloc.Balance(0, assets) + + var balPartVec []xdr.ScVec + + var balAVecScVal xdr.ScVec + + var balScVal xdr.ScVal + + for _, balsPart := range bals.Balances { + balAVecScVal = xdr.ScVec{} + + for _, val := range balsPart { + xdrBalA, err := MakeInt128Parts(val) + if err != nil { + return Balances{}, err + } + + balScVal, err = scval.WrapInt128Parts(xdrBalA) + if err != nil { + return Balances{}, err + } + + balAVecScVal = append(balAVecScVal, balScVal) + } + + balPartVec = append(balPartVec, balAVecScVal) - balB := alloc.Balance(1, asset) - xdrBalB, err := MakeInt128Parts(balB) - if err != nil { - return Balances{}, err } return Balances{ - BalA: xdrBalA, - BalB: xdrBalB, - Token: token, + BalA: balPartVec[0], + BalB: balPartVec[1], + Tokens: tokens, }, nil } func ToAllocation(b Balances) (*channel.Allocation, error) { - asset, err := types.NewStellarAssetFromScAddress(b.Token) - if err != nil { - return nil, err - } - alloc := channel.NewAllocation(2, asset) - balA, err := ToBigInt(b.BalA) - if err != nil { - return nil, err + var balsPart xdr.ScVal + + var stAssets []channel.Asset + + var alloc *channel.Allocation + + // iterate for asset addresses inside allocation + + for i, val := range b.Tokens { + + balsPart = b.BalA[i] + token, ok := val.GetAddress() + if !ok { + return nil, errors.New("expected address") + } + + stAsset, err := types.NewStellarAssetFromScAddress(token) + if err != nil { + return nil, err + } + + stAssets = append(stAssets, stAsset) + } - alloc.SetBalance(0, asset, balA) - balB, err := ToBigInt(b.BalB) - if err != nil { - return nil, err + alloc = channel.NewAllocation(2, stAssets...) + + for i, _ := range b.Tokens { + balsPartVec := *balsPart.MustVec() + + for _, val := range balsPartVec { + bal, ok := val.GetI128() + if !ok { + return nil, errors.New("expected i128") + } + + balInt, err := ToBigInt(bal) + if err != nil { + return nil, err + } + + alloc.SetBalance(channel.Index(i), stAssets[i], balInt) + + } + } - alloc.SetBalance(1, asset, balB) - if err = alloc.Valid(); err != nil { + + if err := alloc.Valid(); err != nil { return nil, err } return alloc, nil @@ -252,3 +316,33 @@ func makeAllocation(asset channel.Asset, balA, balB *big.Int) (*channel.Allocati return alloc, nil } + +func makeAllocationMulti(assets []channel.Asset, balsA, balsB []*big.Int) (*channel.Allocation, error) { + + if len(balsA) != len(balsB) { + return nil, errors.New("expected equal number of balances") + } + + if len(assets) != len(balsA) { + return nil, errors.New("expected equal number of assets and balances") + } + + numParts := 2 + + alloc := channel.NewAllocation(numParts, assets...) + + for i, _ := range assets { + alloc.SetBalance(channel.Index(0), assets[i], balsA[i]) + } + for i, _ := range assets { + alloc.SetBalance(channel.Index(1), assets[i], balsB[i]) + } + + alloc.Locked = make([]channel.SubAlloc, 0) + + if err := alloc.Valid(); err != nil { + return nil, err + } + + return alloc, nil +} diff --git a/wire/balances_test.go b/wire/balances_test.go index 51c16cc..852ecce 100644 --- a/wire/balances_test.go +++ b/wire/balances_test.go @@ -1,4 +1,4 @@ -// Copyright 2023 PolyCrypt GmbH +// Copyright 2024 PolyCrypt GmbH // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,7 +23,8 @@ import ( func TestBalances(t *testing.T) { // Balances XDR generated by soroban contract - x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 97, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 98, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 15, 0, 0, 0, 5, 116, 111, 107, 101, 110, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 1, 206, 189, 184, 3, 222, 31, 3, 119, 148, 96, 26, 56, 67, 233, 206, 144, 53, 117, 109, 200, 172, 60, 79, 190, 69, 198, 194, 72, 127, 57, 239, 18} + x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 97, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 98, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 0, 0, 15, 0, 0, 0, 6, 116, 111, 107, 101, 110, 115, 0, 0, 0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 18, 0, 0, 0, 1, 128, 17, 187, 244, 205, 240, 78, 91, 198, 172, 136, 105, 53, 185, 154, 164, 178, 192, 202, 189, 225, 51, 249, 215, 251, 62, 101, 103, 153, 240, 168, 150, 0, 0, 0, 18, 0, 0, 0, 1, 4, 202, 219, 74, 87, 15, 210, 228, 101, 46, + 129, 65, 1, 80, 153, 18, 204, 230, 201, 162, 50, 93, 110, 236, 141, 113, 0, 202, 248, 89, 243, 224} balances := &wire.Balances{} err := balances.UnmarshalBinary(x) require.NoError(t, err) diff --git a/wire/channel_test.go b/wire/channel_test.go index 83d5240..571dd9a 100644 --- a/wire/channel_test.go +++ b/wire/channel_test.go @@ -1,4 +1,4 @@ -// Copyright 2023 PolyCrypt GmbH +// Copyright 2024 PolyCrypt GmbH // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import ( ) func TestChannel(t *testing.T) { - x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 7, 99, 111, 110, 116, 114, 111, 108, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 15, 0, 0, 0, 6, 99, 108, 111, 115, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 100, 105, 115, 112, 117, 116, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 102, 117, 110, 100, 101, 100, 95, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 102, 117, 110, 100, 101, 100, 95, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 9, 116, 105, 109, 101, 115, 116, 97, 109, 112, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 11, 119, 105, 116, 104, 100, 114, 97, 119, 110, 95, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 11, 119, 105, 116, 104, 100, 114, 97, 119, 110, 95, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 6, 112, 97, 114, 97, 109, 115, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 1, 97, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 15, 0, 0, 0, 4, 97, 100, 100, 114, 0, 0, 0, 18, 0, 0, 0, 1, 33, 51, 38, 216, 94, 104, 60, 95, 201, 68, 34, 38, 19, 154, 177, 126, 166, 239, 35, 70, 87, 92, 111, 155, 136, 232, 97, 200, 2, 113, 142, 45, 0, 0, 0, 15, 0, 0, 0, 6, 112, 117, 98, 107, 101, 121, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 138, 168, 57, 43, 69, 220, 86, 65, 46, 142, 9, 41, 77, 209, 98, 227, 110, 250, 188, 94, 141, 146, 8, 84, 176, 10, 181, 208, 111, 23, 195, 128, 0, 0, 0, 15, 0, 0, 0, 1, 98, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 15, 0, 0, 0, 4, 97, 100, 100, 114, 0, 0, 0, 18, 0, 0, 0, 1, 54, 126, 124, 170, 14, 66, 237, 32, 130, 122, 190, 185, 235, 5, 11, 7, 174, 52, 43, 111, 248, 178, 245, 217, 196, 79, 72, 69, 30, 92, 173, 135, 0, 0, 0, 15, 0, 0, 0, 6, 112, 117, 98, 107, 101, 121, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 198, 110, 153, 155, 207, 65, 232, 251, 170, 116, 250, 218, 34, 78, 186, 124, 12, 123, 14, 67, 225, 189, 2, 217, 179, 38, 247, 170, 250, 88, 172, 100, 0, 0, 0, 15, 0, 0, 0, 18, 99, 104, 97, 108, 108, 101, 110, 103, 101, 95, 100, 117, 114, 97, 116, 105, 111, 110, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 15, 0, 0, 0, 5, 110, 111, 110, 99, 101, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 167, 227, 246, 89, 1, 11, 117, 248, 64, 115, 191, 29, 226, 14, 42, 64, 180, 8, 5, 38, 96, 118, 210, 229, 2, 82, 96, 187, 137, 133, 56, 90, 0, 0, 0, 15, 0, 0, 0, 5, 115, 116, 97, 116, 101, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 8, 98, 97, 108, 97, 110, 99, 101, 115, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 97, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 98, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 15, 0, 0, 0, 5, 116, 111, 107, 101, 110, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 1, 115, 100, 94, 175, 122, 86, 241, 234, 76, 123, 122, 25, 44, 31, 17, 97, 248, 88, 65, 165, 45, 214, 88, 65, 124, 10, 171, 36, 105, 116, 213, 227, 0, 0, 0, 15, 0, 0, 0, 10, 99, 104, 97, 110, 110, 101, 108, 95, 105, 100, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 24, 255, 93, 22, 90, 12, 183, 31, 139, 73, 106, 35, 75, 37, 233, 72, 131, 174, 135, 56, 56, 175, 189, 129, 239, 63, 190, 175, 7, 186, 165, 24, 0, 0, 0, 15, 0, 0, 0, 9, 102, 105, 110, 97, 108, 105, 122, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 7, 118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0} + x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 7, 99, 111, 110, 116, 114, 111, 108, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 15, 0, 0, 0, 6, 99, 108, 111, 115, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 100, 105, 115, 112, 117, 116, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 102, 117, 110, 100, 101, 100, 95, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 102, 117, 110, 100, 101, 100, 95, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 9, 116, 105, 109, 101, 115, 116, 97, 109, 112, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 11, 119, 105, 116, 104, 100, 114, 97, 119, 110, 95, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 11, 119, 105, 116, 104, 100, 114, 97, 119, 110, 95, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 6, 112, 97, 114, 97, 109, 115, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 1, 97, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 15, 0, 0, 0, 4, 97, 100, 100, 114, 0, 0, 0, 18, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0, 0, 0, 6, 112, 117, 98, 107, 101, 121, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 198, 153, 177, 23, 76, 180, 122, 102, 52, 30, 80, 147, 46, 49, 163, 127, 251, 58, 254, 154, 75, 186, 184, 153, 108, 6, 116, 220, 131, 143, 97, 140, 0, 0, 0, 15, 0, 0, 0, 1, 98, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 15, 0, 0, 0, 4, 97, 100, 100, 114, 0, 0, 0, 18, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 15, 0, 0, 0, 6, 112, 117, 98, 107, 101, 121, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 99, 13, 175, 216, 105, 51, 63, 52, 68, 82, 87, 83, 199, 170, 184, 58, 211, 76, 135, 232, 230, 143, 174, 174, 111, 39, 26, 212, 53, 22, 6, 155, 0, 0, 0, 15, 0, 0, 0, 18, 99, 104, 97, 108, 108, 101, 110, 103, 101, 95, 100, 117, 114, 97, 116, 105, 111, 110, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 15, 0, 0, 0, 5, 110, 111, 110, 99, 101, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 75, 60, 158, 169, 217, 40, 139, 208, 106, 190, 90, 13, 126, 217, 53, 98, 124, 215, 177, 245, 13, 175, 97, 83, 236, 185, 177, 161, 136, 196, 36, 133, 0, 0, 0, 15, 0, 0, 0, 5, 115, 116, 97, 116, 101, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 8, 98, 97, 108, 97, 110, 99, 101, 115, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 97, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 98, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 0, 0, 15, 0, 0, 0, 6, 116, 111, 107, 101, 110, 115, 0, 0, 0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 18, 0, 0, 0, 1, 128, 17, 187, 244, 205, 240, 78, 91, 198, 172, 136, 105, 53, 185, 154, 164, 178, 192, 202, 189, 225, 51, 249, 215, 251, 62, 101, 103, 153, 240, 168, 150, 0, 0, 0, 18, 0, 0, 0, 1, 4, 202, 219, 74, 87, 15, 210, 228, 101, 46, 129, 65, 1, 80, 153, 18, 204, 230, 201, 162, 50, 93, 110, 236, 141, 113, 0, 202, 248, 89, 243, 224, 0, 0, 0, 15, 0, 0, 0, 10, 99, 104, 97, 110, 110, 101, 108, 95, 105, 100, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 222, 22, 185, 148, 194, 189, 153, 126, 66, 217, 129, 251, 38, 156, 53, 101, 188, 215, 148, 251, 131, 159, 229, 241, 183, 60, 222, 88, 141, 179, 162, 32, 0, 0, 0, 15, 0, 0, 0, 9, 102, 105, 110, 97, 108, 105, 122, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 7, 118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0} channel := &wire.Channel{} err := channel.UnmarshalBinary(x) require.NoError(t, err) diff --git a/wire/scval/scval.go b/wire/scval/scval.go index 9bae5b5..711fd29 100644 --- a/wire/scval/scval.go +++ b/wire/scval/scval.go @@ -1,4 +1,4 @@ -// Copyright 2023 PolyCrypt GmbH +// Copyright 2024 PolyCrypt GmbH // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,6 +20,10 @@ func WrapScAddress(address xdr.ScAddress) (xdr.ScVal, error) { return xdr.NewScVal(xdr.ScValTypeScvAddress, address) } +func WrapScAddresses(address []xdr.ScAddress) (xdr.ScVal, error) { + return xdr.NewScVal(xdr.ScValTypeScvAddress, address) +} + func MustWrapScAddress(address xdr.ScAddress) xdr.ScVal { v, err := WrapScAddress(address) if err != nil { @@ -28,10 +32,29 @@ func MustWrapScAddress(address xdr.ScAddress) xdr.ScVal { return v } +func MakeScVecFromScAddresses(addresses []xdr.ScAddress) xdr.ScVec { + + var xdrAddresses xdr.ScVec + + for _, val := range addresses { + xdrAddrVal, err := WrapScAddress(val) + if err != nil { + panic("could not wrap address") + } + xdrAddresses = append(xdrAddresses, xdrAddrVal) + } + + return xdrAddresses +} + func WrapInt128Parts(parts xdr.Int128Parts) (xdr.ScVal, error) { return xdr.NewScVal(xdr.ScValTypeScvI128, parts) } +func WrapVec(scVec xdr.ScVec) (xdr.ScVal, error) { + return xdr.NewScVal(xdr.ScValTypeScvVec, &scVec) +} + func MustWrapInt128Parts(parts xdr.Int128Parts) xdr.ScVal { v, err := WrapInt128Parts(parts) if err != nil { diff --git a/wire/state.go b/wire/state.go index eeaa6f9..bfd103c 100644 --- a/wire/state.go +++ b/wire/state.go @@ -1,4 +1,4 @@ -// Copyright 2023 PolyCrypt GmbH +// Copyright 2024 PolyCrypt GmbH // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import ( "fmt" xdr3 "github.com/stellar/go-xdr/xdr3" "github.com/stellar/go/xdr" + "math/big" "perun.network/go-perun/channel" "perun.network/perun-stellar-backend/channel/types" "perun.network/perun-stellar-backend/wire/scval" @@ -200,21 +201,58 @@ func ToState(stellarState State) (channel.State, error) { return channel.State{}, err } - BalA, err := ToBigInt(stellarState.Balances.BalA) - if err != nil { - return channel.State{}, err + var balsABigInt []*big.Int + var balsBBigInt []*big.Int + // for _, _ = range stellarState.Balances.Tokens { //iterate for asset + + balsA := stellarState.Balances.BalA + for _, scVal := range balsA { // iterate for balance within asset + valA := scVal.MustI128() + balAPerun, err := ToBigInt(valA) + if err != nil { + return channel.State{}, err + } + balsABigInt = append(balsABigInt, balAPerun) + + // for _, scVec := range balsAVec { + + // valA := scVec.MustI128() + + // balAPerun, err := ToBigInt(valA) + // if err != nil { + // return channel.State{}, err + // } + // balsABigInt = append(balsABigInt, balAPerun) + // } + } - BalB, err := ToBigInt(stellarState.Balances.BalB) - if err != nil { - return channel.State{}, err + balsB := stellarState.Balances.BalB + for _, scVal := range balsB { // iterate for balance within asset + // balsBVec := *scVal.MustVec() + valB := scVal.MustI128() + balBPerun, err := ToBigInt(valB) + if err != nil { + return channel.State{}, err + } + balsBBigInt = append(balsBBigInt, balBPerun) + // for _, scVec := range balsBVec { + // valB := scVec.MustI128() + // balBPerun, err := ToBigInt(valB) + // if err != nil { + // return channel.State{}, err + // } + // balsBBigInt = append(balsBBigInt, balBPerun) + // } } - Assets, err := convertAsset(stellarState.Balances.Token) + // } + + Assets, err := convertAssets(stellarState.Balances.Tokens) if err != nil { return channel.State{}, err } - Alloc, err := makeAllocation(Assets, BalA, BalB) + Alloc, err := makeAllocationMulti(Assets, balsABigInt, balsBBigInt) if err != nil { return channel.State{}, err } @@ -241,3 +279,32 @@ func convertAsset(contractID xdr.ScAddress) (channel.Asset, error) { } return stellarAsset, nil } + +func convertAssets(contractIDs xdr.ScVec) ([]channel.Asset, error) { + + var assets []channel.Asset + + for _, val := range contractIDs { + contractID, ok := val.GetAddress() + if !ok { + return nil, errors.New("could not turn value into address") + } + if contractID.Type != xdr.ScAddressTypeScAddressTypeContract { + return nil, errors.New("invalid address type") + } + asset, err := convertAsset(contractID) + if err != nil { + return nil, err + } + // return asset, nil + + assets = append(assets, asset) + + } + + // stellarAsset, err := types.NewStellarAssetFromScAddress(contractID) + // if err != nil { + // return nil, err + // } + return assets, nil +} diff --git a/wire/state_test.go b/wire/state_test.go index 633676d..b7bcc10 100644 --- a/wire/state_test.go +++ b/wire/state_test.go @@ -1,4 +1,4 @@ -// Copyright 2023 PolyCrypt GmbH +// Copyright 2024 PolyCrypt GmbH // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -28,7 +28,9 @@ import ( func TestState(t *testing.T) { // State XDR generated by soroban contract - x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 8, 98, 97, 108, 97, 110, 99, 101, 115, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 97, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 98, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 15, 0, 0, 0, 5, 116, 111, 107, 101, 110, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 1, 48, 207, 108, 223, 126, 81, 182, 30, 80, 205, 206, 164, 29, 186, 161, 49, 19, 220, 169, 154, 82, 230, 27, 46, 112, 182, 98, 53, 61, 128, 204, 23, 0, 0, 0, 15, 0, 0, 0, 10, 99, 104, 97, 110, 110, 101, 108, 95, 105, 100, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 135, 93, 16, 248, 119, 202, 15, 58, 94, 69, 71, 246, 71, 210, 225, 253, 36, 204, 170, 27, 91, 210, 4, 152, 129, 94, 12, 28, 183, 59, 230, 206, 0, 0, 0, 15, 0, 0, 0, 9, 102, 105, 110, 97, 108, 105, 122, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 7, 118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0} + x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 8, 98, 97, 108, 97, 110, 99, 101, 115, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 97, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 98, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 0, 0, 15, 0, 0, 0, 6, 116, 111, 107, 101, 110, 115, 0, 0, 0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 18, 0, 0, 0, 1, 128, 17, 187, 244, 205, 240, 78, 91, 198, 172, 136, 105, 53, 185, 154, 164, 178, 192, 202, 189, 225, 51, 249, 215, 251, 62, 101, 103, 153, 240, 168, 150, 0, 0, 0, 18, 0, 0, 0, 1, 4, 202, 219, 74, 87, 15, 210, 228, 101, 46, 129, 65, 1, 80, 153, 18, 204, 230, 201, 162, 50, 93, 110, 236, 141, 113, 0, 202, 248, 89, 243, 224, 0, 0, 0, 15, 0, 0, 0, 10, 99, 104, 97, 110, 110, 101, 108, 95, 105, 100, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 10, 63, 242, 59, 151, 107, 184, 97, 206, 183, 114, 15, 188, 62, 77, 224, 1, 252, 245, 114, 81, 78, 168, 154, 191, 38, 1, 55, 24, 54, 97, 33, 0, 0, 0, 15, 0, 0, 0, 9, 102, 105, 110, 97, 108, 105, 122, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 7, 118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0} + state := &wire.State{} err := state.UnmarshalBinary(x) require.NoError(t, err) @@ -42,7 +44,7 @@ func TestStateConversion(t *testing.T) { rng := polytest.Prng(t) perunFirstState := *ptest.NewRandomState(rng, ptest.WithNumParts(2), - ptest.WithNumAssets(1), + ptest.WithNumAssets(2), ptest.WithNumLocked(0), ptest.WithoutApp(), ptest.WithBalancesInRange(big.NewInt(0).Mul(big.NewInt(1), big.NewInt(100_000_000)), big.NewInt(0).Mul(big.NewInt(1), big.NewInt(100_000_000))), @@ -93,5 +95,5 @@ func checkStellarStateEquality(t *testing.T, first, last wire.State) { require.Equal(t, first.Finalized, last.Finalized) require.Equal(t, first.Balances.BalA, last.Balances.BalA) require.Equal(t, first.Balances.BalB, last.Balances.BalB) - require.Equal(t, first.Balances.Token, last.Balances.Token) + require.Equal(t, first.Balances.Tokens, last.Balances.Tokens) } From 0003db68ec950018438a959f35f28f01e5baaa4d Mon Sep 17 00:00:00 2001 From: Ilja von Hoessle Date: Tue, 25 Jun 2024 14:25:25 +0200 Subject: [PATCH 2/6] * chore(quickstart.sh): Update docker file to use Stellar Protocol 20 * feat(channel): Adapt channel functionality to support multiple assets on one channel, tested for two assets. * feat(client): Adapt contract invocation to new version, temporarily hard coding a transaction fee to 500000 stroops. * feat(payment): Adapt payment channel test/demo to two-asset channels * feat(testdata): Add multichannel contract, perun_soroban_multi_contract.wasm. * feat(wire): Adapt encoding between contract types and backend types to multi-asset channels. --- channel/deploy.go | 2 +- channel/test/setup.go | 7 +++-- channel/test/token.go | 10 ++++--- client/contractbackend.go | 17 +++--------- payment/client_test.go | 6 ++--- payment/test/channel.go | 14 +++++----- payment/test/client.go | 26 ++++++++++-------- payment/test/handle.go | 26 +++++++++++++----- quickstart.sh | 3 +-- testdata/perun_soroban_multi_contract.wasm | Bin 0 -> 24169 bytes wire/balances.go | 16 ++++++++---- wire/state.go | 29 ++------------------- 12 files changed, 72 insertions(+), 84 deletions(-) create mode 100755 testdata/perun_soroban_multi_contract.wasm diff --git a/channel/deploy.go b/channel/deploy.go index cfba7aa..52c897c 100644 --- a/channel/deploy.go +++ b/channel/deploy.go @@ -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: diff --git a/channel/test/setup.go b/channel/test/setup.go index 1564b9f..94d0e43 100644 --- a/channel/test/setup.go +++ b/channel/test/setup.go @@ -42,7 +42,7 @@ import ( ) 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) @@ -119,13 +119,13 @@ func NewTestSetup(t *testing.T) *Setup { relPathAsset, err := getDataFilePath(StellarAssetContractPath) require.NoError(t, err) + perunAddress, _ := Deploy(t, depPerunKp, relPathPerun) + tokenAddressOne, _ := Deploy(t, depTokenOneKp, relPathAsset) tokenAddressTwo, _ := Deploy(t, depTokenTwoKp, relPathAsset) tokenAddresses := []xdr.ScAddress{tokenAddressOne, tokenAddressTwo} - perunAddress, _ := Deploy(t, depPerunKp, relPathPerun) - require.NoError(t, InitTokenContract(depTokenOneKp, tokenAddressOne)) require.NoError(t, InitTokenContract(depTokenTwoKp, tokenAddressTwo)) @@ -340,7 +340,6 @@ func NewParamsWithAddressStateWithAsset(t *testing.T, partsAddr []pwallet.Addres 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))), )) diff --git a/channel/test/token.go b/channel/test/token.go index d90e98c..d4f81d0 100644 --- a/channel/test/token.go +++ b/channel/test/token.go @@ -183,8 +183,10 @@ func Deploy(t *testing.T, kp *keypair.Full, contractPath string) (xdr.ScAddress, require.NoError(t, err) installContractOpInstall := channel.AssembleInstallContractCodeOp(kp.Address(), contractPath) - preFlightOp, minFeeInstall := client.PreflightHostFunctions(hzClient, &deployerAcc, *installContractOpInstall) - txParamsInstall := client.GetBaseTransactionParamsWithFee(&deployerAcc, int64(minFeeInstall), &preFlightOp) + preFlightOp, _ := client.PreflightHostFunctions(hzClient, &deployerAcc, *installContractOpInstall) + + minFeeInstallCustom := 500000 + txParamsInstall := client.GetBaseTransactionParamsWithFee(&deployerAcc, int64(minFeeInstallCustom), &preFlightOp) txSignedInstall, err := client.CreateSignedTransactionWithParams([]*keypair.Full{kp}, txParamsInstall) require.NoError(t, err) @@ -193,8 +195,8 @@ func Deploy(t *testing.T, kp *keypair.Full, contractPath string) (xdr.ScAddress, require.NoError(t, err) createContractOp := channel.AssembleCreateContractOp(kp.Address(), contractPath, "a1", client.NETWORK_PASSPHRASE) - preFlightOpCreate, minFeeCreate := client.PreflightHostFunctions(hzClient, &deployerAcc, *createContractOp) - txParamsCreate := client.GetBaseTransactionParamsWithFee(&deployerAcc, int64(minFeeCreate), &preFlightOpCreate) + preFlightOpCreate, _ := client.PreflightHostFunctions(hzClient, &deployerAcc, *createContractOp) + txParamsCreate := client.GetBaseTransactionParamsWithFee(&deployerAcc, int64(minFeeInstallCustom), &preFlightOpCreate) txSignedCreate, err := client.CreateSignedTransactionWithParams([]*keypair.Full{kp}, txParamsCreate) require.NoError(t, err) diff --git a/client/contractbackend.go b/client/contractbackend.go index a5cda7c..a0a0ed6 100644 --- a/client/contractbackend.go +++ b/client/contractbackend.go @@ -148,29 +148,18 @@ func (c *ContractBackend) InvokeSignedTx(fname string, callTxArgs xdr.ScVec, con txSender.SetHzClient(hzClient) invokeHostFunctionOp := BuildContractCallOp(hzAcc, fnameXdr, callTxArgs, contractAddr) - preFlightOp, minFee := PreflightHostFunctions(hzClient, &hzAcc, *invokeHostFunctionOp) - - txParams := GetBaseTransactionParamsWithFee(&hzAcc, minFee, &preFlightOp) + preFlightOp, _ := PreflightHostFunctions(hzClient, &hzAcc, *invokeHostFunctionOp) + minFeeCustom := int64(500000) + txParams := GetBaseTransactionParamsWithFee(&hzAcc, minFeeCustom, &preFlightOp) txUnsigned, err := txnbuild.NewTransaction(txParams) if err != nil { return xdr.TransactionMeta{}, err } - // txSigned, err := c.tr.createSignedTxFromParams(txParams) txMeta, err := c.tr.sender.SignSendTx(*txUnsigned) if err != nil { return xdr.TransactionMeta{}, err } - // tx, err := hzClient.SubmitTransaction(txSigned) - // if err != nil { - // return xdr.TransactionMeta{}, err - // } - - // txMeta, err := DecodeTxMeta(txSigned) - // if err != nil { - // return xdr.TransactionMeta{}, ErrCouldNotDecodeTxMeta - // } - // _ = txMeta.V3.SorobanMeta.ReturnValue return txMeta, nil } diff --git a/payment/client_test.go b/payment/client_test.go index b262be7..ba8aeb5 100644 --- a/payment/client_test.go +++ b/payment/client_test.go @@ -51,12 +51,12 @@ func runHappyPerun(t *testing.T) { panic(err) } - alicePerun.OpenChannel(bobPerun.WireAddress(), 1000) + alicePerun.OpenChannel(bobPerun.WireAddress(), []float64{1000, 1000}) aliceChannel := alicePerun.Channel bobChannel := bobPerun.AcceptedChannel() - aliceChannel.SendPayment(10) - bobChannel.SendPayment(50) + aliceChannel.SendPayment(10, 0) + bobChannel.SendPayment(50, 1) aliceChannel.Settle() bobChannel.Settle() diff --git a/payment/test/channel.go b/payment/test/channel.go index 91e2366..ae6f5f7 100644 --- a/payment/test/channel.go +++ b/payment/test/channel.go @@ -8,8 +8,8 @@ import ( ) type PaymentChannel struct { - ch *client.Channel - currency channel.Asset + ch *client.Channel + currencies []channel.Asset } func (c *PaymentChannel) GetChannel() *client.Channel { @@ -23,22 +23,22 @@ func (c *PaymentChannel) GetChannelState() *channel.State { return c.ch.State() } -func newPaymentChannel(ch *client.Channel, currency channel.Asset) *PaymentChannel { +func newPaymentChannel(ch *client.Channel, currencies []channel.Asset) *PaymentChannel { return &PaymentChannel{ - ch: ch, - currency: currency, + ch: ch, + currencies: currencies, } } // SendPayment sends a payment to the channel peer. -func (c PaymentChannel) SendPayment(amount int64) { +func (c PaymentChannel) SendPayment(amount int64, assetIdx int) { // Transfer the given amount from us to peer. // Use UpdateBy to update the channel state. err := c.ch.Update(context.TODO(), func(state *channel.State) { icp := big.NewInt(amount) actor := c.ch.Idx() peer := 1 - actor - state.Allocation.TransferBalance(actor, peer, c.currency, icp) + state.Allocation.TransferBalance(actor, peer, c.currencies[assetIdx], icp) }) if err != nil { panic(err) diff --git a/payment/test/client.go b/payment/test/client.go index d4bfff7..d783853 100644 --- a/payment/test/client.go +++ b/payment/test/client.go @@ -19,7 +19,7 @@ import ( type PaymentClient struct { perunClient *pclient.Client account *wallet.Account - currency pchannel.Asset + currencies []pchannel.Asset channels chan *PaymentChannel Channel *PaymentChannel wAddr wire.Address @@ -29,7 +29,7 @@ type PaymentClient struct { func SetupPaymentClient( w *wallet.EphemeralWallet, acc *wallet.Account, - stellarTokenID pchannel.Asset, + stellarTokenIDs []pchannel.Asset, bus *wire.LocalBus, funder *channel.Funder, adj *channel.Adjudicator, @@ -49,7 +49,7 @@ func SetupPaymentClient( c := &PaymentClient{ perunClient: perunClient, account: acc, - currency: stellarTokenID, + currencies: stellarTokenIDs, channels: make(chan *PaymentChannel, 1), wAddr: wireAddr, balance: big.NewInt(0), @@ -69,7 +69,7 @@ func (c *PaymentClient) startWatching(ch *pclient.Channel) { }() } -func (c *PaymentClient) OpenChannel(peer wire.Address, amount float64) { //*PaymentChannel +func (c *PaymentClient) OpenChannel(peer wire.Address, amounts []float64) { // We define the channel participants. The proposer has always index 0. Here // we use the on-chain addresses as off-chain addresses, but we could also // use different ones. @@ -77,14 +77,18 @@ func (c *PaymentClient) OpenChannel(peer wire.Address, amount float64) { //*Paym participants := []wire.Address{c.WireAddress(), peer} // We create an initial allocation which defines the starting balances. - initBal := big.NewInt(int64(amount)) + initBals0 := big.NewInt(int64(amounts[0])) + initBals1 := big.NewInt(int64(amounts[1])) - initAlloc := pchannel.NewAllocation(2, c.currency) - initAlloc.SetAssetBalances(c.currency, []pchannel.Bal{ - initBal, // Our initial balance. - initBal, // Peer's initial balance. + initAlloc := pchannel.NewAllocation(2, c.currencies...) + initAlloc.SetAssetBalances(c.currencies[0], []pchannel.Bal{ + initBals0, // Our initial balance for asset 0. + initBals1, // Peer's initial balance for asset 0. + }) + initAlloc.SetAssetBalances(c.currencies[1], []pchannel.Bal{ + initBals1, // Our initial balance for asset 1. + initBals0, // Peer's initial balance for asset 1. }) - // Prepare the channel proposal by defining the channel parameters. challengeDuration := uint64(10) // On-chain challenge duration in seconds. proposal, err := pclient.NewLedgerChannelProposal( @@ -105,7 +109,7 @@ func (c *PaymentClient) OpenChannel(peer wire.Address, amount float64) { //*Paym // Start the on-chain event watcher. It automatically handles disputes. c.startWatching(ch) - c.Channel = newPaymentChannel(ch, c.currency) + c.Channel = newPaymentChannel(ch, c.currencies) } diff --git a/payment/test/handle.go b/payment/test/handle.go index 1244e2e..00c3c93 100644 --- a/payment/test/handle.go +++ b/payment/test/handle.go @@ -26,7 +26,13 @@ func (c *PaymentClient) HandleProposal(p client.ChannelProposal, r *client.Propo // Check that the channel has the expected assets and funding balances. const assetIdx, clientIdx, peerIdx = 0, 0, 1 - if err := channel.AssertAssetsEqual(lcp.InitBals.Assets, []channel.Asset{c.currency}); err != nil { + + pAssets := make([]channel.Asset, len(c.currencies)) + for i, asset := range c.currencies { + pAssets[i] = channel.Asset(asset) + } + + if err := channel.AssertAssetsEqual(lcp.InitBals.Assets, pAssets); err != nil { return nil, fmt.Errorf("Invalid assets: %v\n", err) } else if lcp.FundingAgreement[assetIdx][clientIdx].Cmp(lcp.FundingAgreement[assetIdx][peerIdx]) != 0 { return nil, fmt.Errorf("Invalid funding balance") @@ -56,7 +62,7 @@ func (c *PaymentClient) HandleProposal(p client.ChannelProposal, r *client.Propo c.startWatching(ch) // Store channel. - c.channels <- newPaymentChannel(ch, c.currency) + c.channels <- newPaymentChannel(ch, c.currencies) } @@ -70,11 +76,19 @@ func (c *PaymentClient) HandleUpdate(cur *channel.State, next client.ChannelUpda } receiverIdx := 1 - next.ActorIdx // This works because we are in a two-party channel. - curBal := cur.Allocation.Balance(receiverIdx, c.currency) - nextBal := next.State.Allocation.Balance(receiverIdx, c.currency) - if nextBal.Cmp(curBal) < 0 { - return fmt.Errorf("Invalid balance: %v", nextBal) + // curBal0 := cur.Allocation.Balance(receiverIdx, c.currencies[0]) + // nextBal0 := next.State.Allocation.Balance(receiverIdx, c.currencies[0]) + // if nextBal0.Cmp(curBal0) < 0 { + // return fmt.Errorf("Invalid balance: %v", nextBal0) + // } + for _, currency := range c.currencies { + curBal := cur.Allocation.Balance(receiverIdx, currency) + nextBal := next.State.Allocation.Balance(receiverIdx, currency) + if nextBal.Cmp(curBal) < 0 { + return fmt.Errorf("Invalid balance for asset %v: %v", currency, nextBal) + } } + return nil }() if err != nil { diff --git a/quickstart.sh b/quickstart.sh index 6e57225..229017c 100755 --- a/quickstart.sh +++ b/quickstart.sh @@ -19,8 +19,7 @@ esac # this is set to the quickstart `soroban-dev` image annointed as the release # for a given Soroban Release, it is captured on Soroban Releases - https://soroban.stellar.org/docs/reference/releases -# QUICKSTART_SOROBAN_DOCKER_SHA=stellar/quickstart:soroban-dev -QUICKSTART_SOROBAN_DOCKER_SHA=stellar/quickstart:testing@sha256:0c756150e7b3c53603fe36bb932c4e7d7ceaef691906b2d3d952771ccc195559 +QUICKSTART_SOROBAN_DOCKER_SHA=stellar/quickstart:latest@sha256:1a82b17a4fae853d24189dd25d4e6b774fa7a1b6356a993e618c6e9bd2f3e04c shift diff --git a/testdata/perun_soroban_multi_contract.wasm b/testdata/perun_soroban_multi_contract.wasm new file mode 100755 index 0000000000000000000000000000000000000000..cbbcb328ac36169d466006616ba71ce9e271d351 GIT binary patch literal 24169 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 Date: Wed, 26 Jun 2024 09:45:30 +0200 Subject: [PATCH 3/6] chore(testdata): Update perun_soroban_token.wasm to soroban-sdk 20.5.0 compiled version --- testdata/perun_soroban_token.wasm | Bin 7620 -> 7347 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/testdata/perun_soroban_token.wasm b/testdata/perun_soroban_token.wasm index 6b5ece45329455d891b6586b9f7218a27a0b9224..eb74d2c9a016a27d20801b119054c90836a25601 100755 GIT binary patch delta 2573 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 From 5a985c03be3c520e6730f91e24bde6d4af34ccd4 Mon Sep 17 00:00:00 2001 From: Ilja von Hoessle Date: Thu, 27 Jun 2024 15:49:50 +0200 Subject: [PATCH 4/6] * fix(channel_test): Adapt test to updated multiasset contract, in which the channel is deleted after the second withdrawal (Bob's withdrawal). Hence, GetChannelInfo returns no channel state anymore after a successful withdrawal. * docs(channel, client): Add logs (temporarily). * test(payment/test, payment_test): Modify TestHappyPerun to test swap. * fix(testdata): Update contract perun_soroban_multi_contract.wasm, including the multiasset fixes. --- channel/adjudicator.go | 6 ++-- channel/adjudicator_test.go | 7 ---- client/client.go | 4 ++- payment/client_test.go | 12 +++++-- payment/test/channel.go | 40 +++++++++++++++------ payment/test/client.go | 18 +++------- payment/test/handle.go | 6 ++-- testdata/perun_soroban_multi_contract.wasm | Bin 24169 -> 23664 bytes 8 files changed, 52 insertions(+), 41 deletions(-) diff --git a/channel/adjudicator.go b/channel/adjudicator.go index 3f7a63f..302d661 100644 --- a/channel/adjudicator.go +++ b/channel/adjudicator.go @@ -79,7 +79,7 @@ func (a *Adjudicator) Subscribe(ctx context.Context, cid pchannel.ID) (pchannel. 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) @@ -114,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) diff --git a/channel/adjudicator_test.go b/channel/adjudicator_test.go index 3128006..12e986b 100644 --- a/channel/adjudicator_test.go +++ b/channel/adjudicator_test.go @@ -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() @@ -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) } diff --git a/client/client.go b/client/client.go index 1b3c0ef..28b4d01 100644 --- a/client/client.go +++ b/client/client.go @@ -124,7 +124,7 @@ func (cb *ContractBackend) Fund(ctx context.Context, perunAddr xdr.ScAddress, ch func (cb *ContractBackend) Close(ctx context.Context, perunAddr xdr.ScAddress, state *pchannel.State, sigs []pwallet.Sig) error { - log.Println("Close called") + log.Println("Close called by ContractBackend") closeTxArgs, err := buildSignedStateTxArgs(*state, sigs) if err != nil { return errors.New("error while building fund tx") @@ -219,6 +219,8 @@ func (cb *ContractBackend) Dispute(ctx context.Context, perunAddr xdr.ScAddress, return nil } func (cb *ContractBackend) Withdraw(ctx context.Context, perunAddr xdr.ScAddress, req pchannel.AdjudicatorReq) error { + log.Println("Withdraw called by ContractBackend") + chanID, partyIdx := req.Tx.State.ID, req.Idx withdrawerIdx := partyIdx == 1 if partyIdx > 1 { diff --git a/payment/client_test.go b/payment/client_test.go index ba8aeb5..b7ee632 100644 --- a/payment/client_test.go +++ b/payment/client_test.go @@ -18,6 +18,8 @@ package payment_test import ( "log" + "math/big" + "perun.network/go-perun/channel" "perun.network/go-perun/wire" chtest "perun.network/perun-stellar-backend/channel/test" paytest "perun.network/perun-stellar-backend/payment/test" @@ -51,12 +53,16 @@ func runHappyPerun(t *testing.T) { panic(err) } - alicePerun.OpenChannel(bobPerun.WireAddress(), []float64{1000, 1000}) + balances := channel.Balances{ + {big.NewInt(1000), big.NewInt(0)}, + {big.NewInt(0), big.NewInt(1000)}, + } + + alicePerun.OpenChannel(bobPerun.WireAddress(), balances) aliceChannel := alicePerun.Channel bobChannel := bobPerun.AcceptedChannel() - aliceChannel.SendPayment(10, 0) - bobChannel.SendPayment(50, 1) + aliceChannel.PerformSwap() aliceChannel.Settle() bobChannel.Settle() diff --git a/payment/test/channel.go b/payment/test/channel.go index ae6f5f7..0cf4374 100644 --- a/payment/test/channel.go +++ b/payment/test/channel.go @@ -2,7 +2,6 @@ package test import ( "context" - "math/big" "perun.network/go-perun/channel" "perun.network/go-perun/client" ) @@ -30,18 +29,37 @@ func newPaymentChannel(ch *client.Channel, currencies []channel.Asset) *PaymentC } } -// SendPayment sends a payment to the channel peer. -func (c PaymentChannel) SendPayment(amount int64, assetIdx int) { - // Transfer the given amount from us to peer. - // Use UpdateBy to update the channel state. - err := c.ch.Update(context.TODO(), func(state *channel.State) { - icp := big.NewInt(amount) - actor := c.ch.Idx() - peer := 1 - actor - state.Allocation.TransferBalance(actor, peer, c.currencies[assetIdx], icp) +// // SendPayment sends a payment to the channel peer. +// func (c PaymentChannel) SendPayment(amount int64, assetIdx int) { +// // Transfer the given amount from us to peer. +// // Use UpdateBy to update the channel state. +// err := c.ch.Update(context.TODO(), func(state *channel.State) { +// icp := big.NewInt(amount) +// actor := c.ch.Idx() +// peer := 1 - actor +// state.Allocation.TransferBalance(actor, peer, c.currencies[assetIdx], icp) +// }) +// if err != nil { +// panic(err) +// } +// } + +// PerformSwap performs a swap by "swapping" the balances of the two +// participants for both assets. +func (c PaymentChannel) PerformSwap() { + err := c.ch.Update(context.TODO(), func(state *channel.State) { // We use context.TODO to keep the code simple. + // We simply swap the balances for the two assets. + state.Balances = channel.Balances{ + {state.Balances[0][1], state.Balances[0][0]}, + {state.Balances[1][1], state.Balances[1][0]}, + } + + // Set the state to final because we do not expect any other updates + // than this swap. + state.IsFinal = true }) if err != nil { - panic(err) + panic(err) // We panic on error to keep the code simple. } } diff --git a/payment/test/client.go b/payment/test/client.go index d783853..83d9424 100644 --- a/payment/test/client.go +++ b/payment/test/client.go @@ -69,26 +69,17 @@ func (c *PaymentClient) startWatching(ch *pclient.Channel) { }() } -func (c *PaymentClient) OpenChannel(peer wire.Address, amounts []float64) { +func (c *PaymentClient) OpenChannel(peer wire.Address, balances pchannel.Balances) { // We define the channel participants. The proposer has always index 0. Here // we use the on-chain addresses as off-chain addresses, but we could also // use different ones. participants := []wire.Address{c.WireAddress(), peer} - // We create an initial allocation which defines the starting balances. - initBals0 := big.NewInt(int64(amounts[0])) - initBals1 := big.NewInt(int64(amounts[1])) - initAlloc := pchannel.NewAllocation(2, c.currencies...) - initAlloc.SetAssetBalances(c.currencies[0], []pchannel.Bal{ - initBals0, // Our initial balance for asset 0. - initBals1, // Peer's initial balance for asset 0. - }) - initAlloc.SetAssetBalances(c.currencies[1], []pchannel.Bal{ - initBals1, // Our initial balance for asset 1. - initBals0, // Peer's initial balance for asset 1. - }) + initAlloc.Balances = balances + + fmt.Println("InitAlloc: ", initAlloc) // Prepare the channel proposal by defining the channel parameters. challengeDuration := uint64(10) // On-chain challenge duration in seconds. proposal, err := pclient.NewLedgerChannelProposal( @@ -110,7 +101,6 @@ func (c *PaymentClient) OpenChannel(peer wire.Address, amounts []float64) { // Start the on-chain event watcher. It automatically handles disputes. c.startWatching(ch) c.Channel = newPaymentChannel(ch, c.currencies) - } func (p *PaymentClient) WireAddress() wire.Address { diff --git a/payment/test/handle.go b/payment/test/handle.go index 00c3c93..246855d 100644 --- a/payment/test/handle.go +++ b/payment/test/handle.go @@ -4,10 +4,10 @@ import ( "context" "fmt" "log" - "time" - + "math/big" "perun.network/go-perun/channel" "perun.network/go-perun/client" + "time" ) // HandleProposal is the callback for incoming channel proposals. @@ -34,7 +34,7 @@ func (c *PaymentClient) HandleProposal(p client.ChannelProposal, r *client.Propo if err := channel.AssertAssetsEqual(lcp.InitBals.Assets, pAssets); err != nil { return nil, fmt.Errorf("Invalid assets: %v\n", err) - } else if lcp.FundingAgreement[assetIdx][clientIdx].Cmp(lcp.FundingAgreement[assetIdx][peerIdx]) != 0 { + } else if lcp.FundingAgreement[assetIdx][peerIdx].Cmp(big.NewInt(0)) != 0 { //lcp.FundingAgreement[assetIdx][clientIdx].Cmp(lcp.FundingAgreement[assetIdx][peerIdx]) != 0 return nil, fmt.Errorf("Invalid funding balance") } return lcp, nil diff --git a/testdata/perun_soroban_multi_contract.wasm b/testdata/perun_soroban_multi_contract.wasm index cbbcb328ac36169d466006616ba71ce9e271d351..05701f50851a6b9e15273cbc2c2b7d954d058725 100755 GIT binary patch delta 2134 zcmb7FU1%It6u#%)+3e5kBvVY9HDYpSOk~$~O^P)WHzJvZ#DWhl0ih&{DF`iz577sW zP_nUDNKCRu2Q>D@yol8YNxBL_eF*x}2f-9Us#1tA{^3JZ5RFFt&dkK0*op=+-@WJl zobNmLoU>oOL)+`LwAngEM}(pi)FUKKktY)L7~Ll1+80MiJ^w^su#`GfACe~; zML&U}FM8!ET$lFL7|%-LxRzybsKA)<)G>hRDLBztPxS%FQ#}A|$pgSr13)sTt074n zQ*cZHz!5I&H=<~8jQ4rI%!1&06Ue?V0@?Wn+u?nk5$;zR;r~1%2&^#vBT)UOBS6g=E~mOc)?YaR{mZ5bmYSZyhj3chc-vq73V zWiQlkKiEVx9}Kp>KRnMz+e~|r(P`4QUE@(%&pPR{!H?-L)3}o;3rDB{IiS;842N%# z$SPB^Mf~N^h$Vstb1erHteHQq8nGxio9j3f=U!P1>qJ2b*D8zqv{kub(4u|&1@G@y zI;m5-iSr6+#7FG6m|d1k`)wWwQ&Sm(8T&M-iZY#cL{^xl5W%IKuJ7jVpz5nhL!xRF zswb|R89CvqxN4NYL|iptwnL06igj|0DBV>7uZ9W%p63byt`JHDh)PN^q8x-)zCEtV zevv6@m6tY31#3BQQlm(ocme!hu|ya%10hU1288g&l!GLUiG-QYdm=Ksb$O65{q!7jb5n%s+)eLeyWjgqQs$QmfXysg@vM+BiHnQqesH2PbDbD z3DhJ~Q*pi{H4*YKHI+_8jG1 zlK3&)#*NICp4yJLeaTkn+hT-1lxnDNlMw_iGDf8emj!jqMSX@r!DPT~j3a|$4I=|% zkJ}W7$BM&~hE=;Zf)s4rczqSwv{#N{T=0qpH{mn~-a`HH^F`exD@{Lv9AG00UW70f z&jMMt|D^1vP$?dD7LOW={C9an*VeZuZNIAhfZJv`{7)JYMLdh5Bkivh(^a zS4N^%hFX4;$zHXkWv<%NGXJ@ynSX0(*~1-`xTSF`ijK9~Rrq;)kZmEt}b5=pH3LNE$3M$22{6G0OE&Yfv(iHTs7&Np}F z+Hz86Vz&H9mF(ffgvQNb)m0{6UcFxCP1kG48yO^u-RSukTQ3>Z{Z1RL`_HrfKW{1W| z!=^bgIn-|p%hLJ8d2Q@`E4=1?cBgBN^Jf;0<#-nHsEP$5OIX-T+zYRk-BZXmS)66) z#l8dK+Rmg2z0P>2DNbLVuf~Ca=oP1dC8T|fpM%y2BXTp$Sn$;mM)K7FBkcAAP@TO1 z6lXU8wb=>CgudDiL}~f~%*o64iVM7t^W!XUssr^Pl~pmOPgw6s{X;bmJzPSc8I z2y?!va11{kXje^*pp(bo?(hCLu1`_9FaEs>(w|Frb-iT`$q z^HXdn%0cR_cn5BRo5Go4XDhYR`Cc)N!*r@!_5;*lDl+sx|3NI&Hs*J)Y#}$%&(2F=h$Sztr7B0oY zWzx>qt9&Txw?w!U3m5tU%E2}oCimzTd%@hMdc{|S@m@;Dut6aA#pb`Mh(%P;FpD+J zL`&O3udY{I% zxfqPtz2Aergb*6D+UrTE4KHfV*UCKH={e?;rL=86S>|ECw~TwHH1zxJpRQ#}LqD7y zJ^77vPQ~rd6`2>7xOeJwY9CkQABltl1Zg+LLJ%$l&Vh2^!F%Z6Ddu=@#Q!%|glVF@ zH$aZf;fUvh&bMf)R=#U#AgJVT4}VChEc=GNFIu zH-|kR*J*DPu6Mjwaeb*3_mz>>qY}2>+O>2hdV1{+i0rfL`D^|@*%TCvE+Q-G0$H&Z zm{EmfX8CtXr#M8mjC5OxD0-u>uaj8!=gO@AO&jt19VYdsz}MM70)bXr@h+|0s<1o{vi?O2brUFcYYv|aD` GpzCiEKpvq0 From 8ccdaec68438b9d3e1e37d9913588b9dac3ebb92 Mon Sep 17 00:00:00 2001 From: Ilja von Hoessle Date: Tue, 2 Jul 2024 13:01:56 +0200 Subject: [PATCH 5/6] * feat(client/ContractBackend): Add GetBalanceUser to query token balance * refactor(payment_test/client_test.go): Change initial balance to have more general swap case. * chore(payment/test/client.go): Remove Printing of initAlloc. * fix(wire/balances.go): Fix balance mismatch, introduced by multiasset feature. --- client/client.go | 24 +++++++++++++++++ payment/client_test.go | 2 +- payment/test/client.go | 1 - wire/balances.go | 60 ++++++++++++++++++------------------------ 4 files changed, 51 insertions(+), 36 deletions(-) diff --git a/client/client.go b/client/client.go index 28b4d01..f50f7c4 100644 --- a/client/client.go +++ b/client/client.go @@ -276,5 +276,29 @@ func (cb *ContractBackend) GetChannelInfo(ctx context.Context, perunAddr xdr.ScA return wire.Channel{}, errors.New("error while decoding return value") } return getChan, nil +} +func (cb *ContractBackend) GetBalanceUser(cID xdr.ScAddress) (string, error) { + tr := cb.GetTransactor() + addr, err := tr.GetAddress() + if err != nil { + return "", err + } + accountId, err := xdr.AddressToAccountId(addr) + if err != nil { + return "", err + } + scAddr, err := xdr.NewScAddress(xdr.ScAddressTypeScAddressTypeAccount, accountId) + if err != nil { + return "", err + } + TokenNameArgs, err := BuildGetTokenBalanceArgs(scAddr) + if err != nil { + return "", err + } + tx, err := cb.InvokeSignedTx("balance", TokenNameArgs, cID) + if err != nil { + return "", err + } + return tx.V3.SorobanMeta.ReturnValue.String(), nil } diff --git a/payment/client_test.go b/payment/client_test.go index b7ee632..2c4fbe2 100644 --- a/payment/client_test.go +++ b/payment/client_test.go @@ -55,7 +55,7 @@ func runHappyPerun(t *testing.T) { balances := channel.Balances{ {big.NewInt(1000), big.NewInt(0)}, - {big.NewInt(0), big.NewInt(1000)}, + {big.NewInt(0), big.NewInt(2000)}, } alicePerun.OpenChannel(bobPerun.WireAddress(), balances) diff --git a/payment/test/client.go b/payment/test/client.go index 83d9424..f063c66 100644 --- a/payment/test/client.go +++ b/payment/test/client.go @@ -79,7 +79,6 @@ func (c *PaymentClient) OpenChannel(peer wire.Address, balances pchannel.Balance initAlloc := pchannel.NewAllocation(2, c.currencies...) initAlloc.Balances = balances - fmt.Println("InitAlloc: ", initAlloc) // Prepare the channel proposal by defining the channel parameters. challengeDuration := uint64(10) // On-chain challenge duration in seconds. proposal, err := pclient.NewLedgerChannelProposal( diff --git a/wire/balances.go b/wire/balances.go index 01371b6..1219b7e 100644 --- a/wire/balances.go +++ b/wire/balances.go @@ -1,4 +1,4 @@ -// Copyright 2023 PolyCrypt GmbH +// Copyright 2024 PolyCrypt GmbH // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -153,15 +153,10 @@ func MakeBalances(alloc channel.Allocation) (Balances, error) { if err := alloc.Valid(); err != nil { return Balances{}, err } - parts := []channel.Index{0, 1} - numParts := len(parts) - // No sub-channels if len(alloc.Locked) != 0 { return Balances{}, errors.New("expected no locked funds") } assets := alloc.Assets - // var stAssets []channel.Asset - var tokens xdr.ScVec for i, ast := range assets { _, ok := ast.(*types.StellarAsset) @@ -180,51 +175,51 @@ func MakeBalances(alloc channel.Allocation) (Balances, error) { tokenVal := scval.MustWrapScAddress(token) tokens = append(tokens, tokenVal) - - } - - if alloc.NumParts() != 2 { - return Balances{}, errors.New("expected exactly two parts") } - bals := make(channel.Balances, len(parts)) - for i := range numParts { - bals[i] = make([]channel.Bal, len(assets)) - for j := range assets { - bals[i][j] = alloc.Balance(parts[i], assets[j]) - } + numParts := alloc.NumParts() + if numParts < 2 { + return Balances{}, errors.New("expected at least two parts") } - var balPartVec []xdr.ScVec + bals := alloc.Balances - var balAVecScVal xdr.ScVec + balPartVecs := make([]xdr.ScVec, numParts) var balScVal xdr.ScVal - for _, balsPart := range bals { - balAVecScVal = xdr.ScVec{} - - for _, val := range balsPart { - xdrBalA, err := MakeInt128Parts(val) + for _, balsAsset := range bals { + for j, val := range balsAsset { + xdrBalPart, err := MakeInt128Parts(val) if err != nil { return Balances{}, err } - balScVal, err = scval.WrapInt128Parts(xdrBalA) + balScVal, err = scval.WrapInt128Parts(xdrBalPart) if err != nil { return Balances{}, err } - balAVecScVal = append(balAVecScVal, balScVal) + if j < numParts { + balPartVecs[j] = append(balPartVecs[j], balScVal) + } else { + return Balances{}, errors.New("unexpected number of parts in balance asset") + } } + } - balPartVec = append(balPartVec, balAVecScVal) - + // Assign the first two parts to BalA and BalB for backward compatibility + var balAPartVec, balBPartVec xdr.ScVec + if numParts > 0 { + balAPartVec = balPartVecs[0] + } + if numParts > 1 { + balBPartVec = balPartVecs[1] } return Balances{ - BalA: balPartVec[0], - BalB: balPartVec[1], + BalA: balAPartVec, + BalB: balBPartVec, Tokens: tokens, }, nil } @@ -338,10 +333,7 @@ func makeAllocationMulti(assets []channel.Asset, balsA, balsB []*big.Int) (*chan alloc := channel.NewAllocation(numParts, assets...) for i, _ := range assets { - alloc.SetBalance(channel.Index(0), assets[i], balsA[i]) - } - for i, _ := range assets { - alloc.SetBalance(channel.Index(1), assets[i], balsB[i]) + alloc.Balances[i] = []*big.Int{balsA[i], balsB[i]} } alloc.Locked = make([]channel.SubAlloc, 0) From 61837f24c196234ff11077c7d1e3e4daf819941e Mon Sep 17 00:00:00 2001 From: Ilja von Hoessle Date: Wed, 3 Jul 2024 14:29:28 +0200 Subject: [PATCH 6/6] feat(client): Invoke get_channel function in the contract without sending a signed transaction - decode the result of the simulated transaction result --- client/client.go | 11 ++--------- client/contractbackend.go | 25 +++++++++++++++++++++++++ client/transaction.go | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/client/client.go b/client/client.go index f50f7c4..949a937 100644 --- a/client/client.go +++ b/client/client.go @@ -263,19 +263,12 @@ func (cb *ContractBackend) GetChannelInfo(ctx context.Context, perunAddr xdr.ScA if err != nil { return wire.Channel{}, errors.New("error while building get_channel tx") } - txMeta, err := cb.InvokeSignedTx("get_channel", getchTxArgs, perunAddr) + chanInfo, err := cb.InvokeUnsignedTx("get_channel", getchTxArgs, perunAddr) if err != nil { return wire.Channel{}, errors.New("error while processing and submitting get_channel tx") } - retVal := txMeta.V3.SorobanMeta.ReturnValue - var getChan wire.Channel - - err = getChan.FromScVal(retVal) - if err != nil { - return wire.Channel{}, errors.New("error while decoding return value") - } - return getChan, nil + return chanInfo, nil } func (cb *ContractBackend) GetBalanceUser(cID xdr.ScAddress) (string, error) { diff --git a/client/contractbackend.go b/client/contractbackend.go index a0a0ed6..e412205 100644 --- a/client/contractbackend.go +++ b/client/contractbackend.go @@ -10,6 +10,7 @@ import ( "github.com/stellar/go/xdr" "perun.network/go-perun/wallet" "perun.network/perun-stellar-backend/wallet/types" + "perun.network/perun-stellar-backend/wire" "sync" ) @@ -129,6 +130,30 @@ func (st *StellarSigner) GetHorizonClient() *horizonclient.Client { return st.hzClient } +func (c *ContractBackend) InvokeUnsignedTx(fname string, callTxArgs xdr.ScVec, contractAddr xdr.ScAddress) (wire.Channel, error) { //xdr.TransactionMeta, error + c.cbMutex.Lock() + defer c.cbMutex.Unlock() + fnameXdr := xdr.ScSymbol(fname) + hzAcc, err := c.tr.GetHorizonAccount() + if err != nil { + return wire.Channel{}, err + } + + hzClient := c.tr.GetHorizonClient() + + txSender, ok := c.tr.sender.(*TxSender) + if !ok { + return wire.Channel{}, errors.New("sender is not of type *TxSender") + } + + txSender.SetHzClient(hzClient) + + invokeHostFunctionOp := BuildContractCallOp(hzAcc, fnameXdr, callTxArgs, contractAddr) + chanInfo, _, _ := PreflightHostFunctionsResult(hzClient, &hzAcc, *invokeHostFunctionOp) + + return chanInfo, nil +} + func (c *ContractBackend) InvokeSignedTx(fname string, callTxArgs xdr.ScVec, contractAddr xdr.ScAddress) (xdr.TransactionMeta, error) { c.cbMutex.Lock() defer c.cbMutex.Unlock() diff --git a/client/transaction.go b/client/transaction.go index a37d75f..167c0b0 100644 --- a/client/transaction.go +++ b/client/transaction.go @@ -8,6 +8,7 @@ import ( "github.com/stellar/go/protocols/horizon" "github.com/stellar/go/txnbuild" "github.com/stellar/go/xdr" + "perun.network/perun-stellar-backend/wire" "strconv" "time" ) @@ -96,6 +97,43 @@ func PreflightHostFunctions(hzClient *horizonclient.Client, return function, result.MinResourceFee } +func PreflightHostFunctionsResult(hzClient *horizonclient.Client, + sourceAccount txnbuild.Account, function txnbuild.InvokeHostFunction, +) (wire.Channel, txnbuild.InvokeHostFunction, int64) { + result, transactionData := simulateTransaction(hzClient, sourceAccount, &function) + + function.Ext = xdr.TransactionExt{ + V: 1, + SorobanData: &transactionData, + } + var getChan wire.Channel + + if len(result.Results) != 1 { + panic("expected one result") + } + + var decodedXdr xdr.ScVal + err := xdr.SafeUnmarshalBase64(result.Results[0].XDR, &decodedXdr) + if err != nil { + panic(err) + } + + decChanInfo := decodedXdr + + if decChanInfo.Type != xdr.ScValTypeScvMap { + return getChan, function, result.MinResourceFee + + } + + err = getChan.FromScVal(decChanInfo) + if err != nil { + + panic(err) + } + + return getChan, function, result.MinResourceFee +} + func simulateTransaction(hzClient *horizonclient.Client, sourceAccount txnbuild.Account, op txnbuild.Operation, ) (RPCSimulateTxResponse, xdr.SorobanTransactionData) {