Skip to content

🚸 Allocation helpers #240

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

Merged
merged 3 commits into from
Nov 1, 2021
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
20 changes: 16 additions & 4 deletions backend/ethereum/channel/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,15 @@ func ToEthState(s *channel.State) adjudicator.ChannelState {
locked := make([]adjudicator.ChannelSubAlloc, len(s.Locked))
for i, sub := range s.Locked {
// Create index map.
indexMap := sub.IndexMap
if len(indexMap) == 0 {
indexMap = make([]uint16, s.NumParts())
indexMap := make([]uint16, s.NumParts())
if len(sub.IndexMap) == 0 {
for i := range indexMap {
indexMap[i] = uint16(i)
}
} else {
for i, x := range sub.IndexMap {
indexMap[i] = uint16(x)
}
}

locked[i] = adjudicator.ChannelSubAlloc{ID: sub.ID, Balances: sub.Bals, IndexMap: indexMap}
Expand Down Expand Up @@ -242,7 +245,8 @@ func pwToCommonAddresses(addr []wallet.Address) []common.Address {
func FromEthState(app channel.App, s *adjudicator.ChannelState) channel.State {
locked := make([]channel.SubAlloc, len(s.Outcome.Locked))
for i, sub := range s.Outcome.Locked {
locked[i] = *channel.NewSubAlloc(sub.ID, sub.Balances, sub.IndexMap)
indexMap := makeIndexMap(sub.IndexMap)
locked[i] = *channel.NewSubAlloc(sub.ID, sub.Balances, indexMap)
}
alloc := channel.Allocation{
Assets: fromEthAssets(s.Outcome.Assets),
Expand All @@ -269,6 +273,14 @@ func FromEthState(app channel.App, s *adjudicator.ChannelState) channel.State {
}
}

func makeIndexMap(m []uint16) []channel.Index {
_m := make([]channel.Index, len(m))
for i, x := range m {
_m[i] = channel.Index(x)
}
return _m
}

func fromEthAssets(assets []common.Address) []channel.Asset {
_assets := make([]channel.Asset, len(assets))
for i, a := range assets {
Expand Down
80 changes: 77 additions & 3 deletions channel/allocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,79 @@ type (
// where all participants' assets are deposited.
// The same Asset should be shareable by multiple Allocation instances.
// Decoding happens with AppBackend.DecodeAsset.
Asset = perunio.Encoder
Asset perunio.Encoder
)

var _ perunio.Serializer = (*Allocation)(nil)
var _ perunio.Serializer = (*Balances)(nil)
var _ perunbig.Summer = (*Allocation)(nil)
var _ perunbig.Summer = (*Balances)(nil)

// NewAllocation returns a new allocation for the given number of participants and assets.
func NewAllocation(numParts int, assets ...Asset) *Allocation {
return &Allocation{
Assets: assets,
Balances: MakeBalances(len(assets), numParts),
}
}

// AssetIndex returns the index of the asset in the allocation.
func (a *Allocation) AssetIndex(asset Asset) (Index, bool) {
for idx, _asset := range a.Assets {
if ok, err := perunio.EqualEncoding(asset, _asset); ok && err == nil {
return Index(idx), true
}
}
return 0, false
}

// SetBalance sets the balance for the given asset and participant.
func (a *Allocation) SetBalance(part Index, asset Asset, val Bal) {
assetIdx, ok := a.AssetIndex(asset)
if !ok {
log.Panicf("asset not found: %v", a)
}
a.Balances[assetIdx][part] = new(big.Int).Set(val)
}

// SetAssetBalances sets the balances for the given asset and all participants.
func (a *Allocation) SetAssetBalances(asset Asset, val []Bal) {
assetIdx, ok := a.AssetIndex(asset)
if !ok {
log.Panicf("asset not found: %v", a)
}
a.Balances[assetIdx] = CloneBals(val)
}

// Balance gets the balance for the given asset and participant.
func (a *Allocation) Balance(part Index, asset Asset) Bal {
assetIdx, ok := a.AssetIndex(asset)
if !ok {
log.Panicf("asset not found: %v", a)
}
return a.Balances[assetIdx][part]
}

// AddToBalance adds a given amount to the balance of the specified participant
// for the given asset.
func (a *Allocation) AddToBalance(part Index, asset Asset, val Bal) {
bal := a.Balance(part, asset)
bal.Add(bal, val)
}

// SubFromBalance subtracts a given amount from the balance of the specified
// participant for the given asset.
func (a *Allocation) SubFromBalance(part Index, asset Asset, val Bal) {
bal := a.Balance(part, asset)
bal.Sub(bal, val)
}

// TransferBalance transfers the given amount from one participant to the other.
func (a *Allocation) TransferBalance(from, to Index, asset Asset, val Bal) {
a.SubFromBalance(from, asset, val)
a.AddToBalance(to, asset, val)
}

// NumParts returns the number of participants of this Allocation. It returns -1 if
// there are no Balances, i.e., if the Allocation is invalid.
func (a *Allocation) NumParts() int {
Expand Down Expand Up @@ -133,6 +198,15 @@ func (a Allocation) Clone() (clone Allocation) {
return clone
}

// MakeBalances returns a new Balances object of the specified size.
func MakeBalances(numAssets, numParticipants int) Balances {
balances := make([][]*big.Int, numAssets)
for i := range balances {
balances[i] = make([]*big.Int, numParticipants)
}
return balances
}

// Clone returns a deep copy of the Balance object.
// If it is nil, it returns nil.
func (b Balances) Clone() Balances {
Expand Down Expand Up @@ -450,9 +524,9 @@ func (b Balances) Sum() []Bal {
}

// NewSubAlloc creates a new sub-allocation.
func NewSubAlloc(id ID, bals []Bal, indexMap []uint16) *SubAlloc {
func NewSubAlloc(id ID, bals []Bal, indexMap []Index) *SubAlloc {
if indexMap == nil {
indexMap = []uint16{}
indexMap = []Index{}
}
return &SubAlloc{ID: id, Bals: bals, IndexMap: indexMap}
}
Expand Down
15 changes: 14 additions & 1 deletion channel/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,20 @@ import (
)

// Index is the type for the number of participants, assets, sub-allocations, actions and alike.
type Index = uint16
type Index uint16

// Encode encodes the object onto an io.Writer.
func (i Index) Encode(w stdio.Writer) error {
return io.Encode(w, uint16(i))
}

// Decode decodes an object from an io.Reader.
func (i *Index) Decode(r stdio.Reader) error {
var _i uint16
err := io.Decode(r, &_i)
*i = Index(_i)
return err
}

type (
// Phase is a phase of the channel pushdown automaton.
Expand Down
14 changes: 6 additions & 8 deletions client/test/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,17 +318,15 @@ func (r *role) LedgerChannelProposal(rng *rand.Rand, cfg ExecConfig) *client.Led
r.log.Panic("Invalid ExecConfig: App does not specify an app.")
}

cfgInitBals := cfg.InitBals()
initBals := &channel.Allocation{
Assets: []channel.Asset{cfg.Asset()},
Balances: channel.Balances{cfgInitBals[:]},
}
cfgPeerAddrs := cfg.Peers()
peers, asset, bals := cfg.Peers(), cfg.Asset(), cfg.InitBals()
alloc := channel.NewAllocation(len(peers), asset)
alloc.SetAssetBalances(asset, bals[:])

prop, err := client.NewLedgerChannelProposal(
r.challengeDuration,
r.setup.Wallet.NewRandomAccount(rng).Address(),
initBals,
cfgPeerAddrs[:],
alloc,
peers[:],
client.WithNonceFrom(rng),
cfg.App())
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion client/virtual_channel_settlement.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ func (c *Channel) forceFinalState(ctx context.Context, final channel.SignedState
return err
}
for i, sig := range final.Sigs {
if err := c.machine.AddSig(ctx, uint16(i), sig); err != nil {
if err := c.machine.AddSig(ctx, channel.Index(i), sig); err != nil {
return err
}
}
Expand Down
22 changes: 10 additions & 12 deletions client/virtual_channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,15 +172,14 @@ func setupVirtualChannelTest(t *testing.T, ctx context.Context) (vct virtualChan
go ingrid.Client.Handle(openingProposalHandlerIngrid, updateProposalHandlerIngrid)

// Establish ledger channel between Alice and Ingrid.
initAllocAlice := channel.Allocation{
Assets: []channel.Asset{asset},
Balances: [][]channel.Bal{initBalsAlice},
}
peersAlice := []wire.Address{alice.Identity.Address(), ingrid.Identity.Address()}
initAllocAlice := channel.NewAllocation(len(peersAlice), asset)
initAllocAlice.SetAssetBalances(asset, initBalsAlice)
lcpAlice, err := client.NewLedgerChannelProposal(
challengeDuration,
alice.Identity.Address(),
&initAllocAlice,
[]wire.Address{alice.Identity.Address(), ingrid.Identity.Address()},
initAllocAlice,
peersAlice,
)
require.NoError(err, "creating ledger channel proposal")

Expand All @@ -193,15 +192,14 @@ func setupVirtualChannelTest(t *testing.T, ctx context.Context) (vct virtualChan
}

// Establish ledger channel between Bob and Ingrid.
initAllocBob := channel.Allocation{
Assets: []channel.Asset{asset},
Balances: [][]channel.Bal{initBalsBob},
}
peersBob := []wire.Address{bob.Identity.Address(), ingrid.Identity.Address()}
initAllocBob := channel.NewAllocation(len(peersBob), asset)
initAllocBob.SetAssetBalances(asset, initBalsBob)
lcpBob, err := client.NewLedgerChannelProposal(
challengeDuration,
bob.Identity.Address(),
&initAllocBob,
[]wire.Address{bob.Identity.Address(), ingrid.Identity.Address()},
initAllocBob,
peersBob,
)
require.NoError(err, "creating ledger channel proposal")

Expand Down