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

Create a channel-top-up protocol #1647

Open
NiloCK opened this issue Sep 7, 2023 · 0 comments
Open

Create a channel-top-up protocol #1647

NiloCK opened this issue Sep 7, 2023 · 0 comments
Labels
🏗️ Productionization Things that need to be tackled for nitro to run in a production environment ✨ enhancement New feature or request on-chain-protocol

Comments

@NiloCK
Copy link
Contributor

NiloCK commented Sep 7, 2023

There is currently no way for running ledger channels to be topped up. Topups are desirable because they are generally cheaper than finalizing a channel, disbursing its assets, and opening a new channel via a L1 deposit.

Protocol at a glance:

  • with
    • ledger channel L(X, Y) between participants X and Y.
    • X as the topper-upper (ie, the person who executes the L1 deposit to add funds)
    • top-up amount t
  1. take the current channel outcome and append X: t as the final outcome item
    • the channel outcome now probably resembles { X: x, Y: y, [someGuarantees]..., X: t}
    • exchange and co-sign this updated state
  2. await the Deposited(L(X,Y).channelID, t) event
  3. fold the X: t outcome item back into the previously existing X outcome item
    • result: { X: x+t, Y: y, [someGuarantees}... }
    • exchange and co-sign this updated state

Implementation Details

An ideal implementation of this protocol would leave the ledger channel usable for regular operations while the top-up is being run. Waiting on an on-chain event necessarily makes this a slow operation, but ledger channels are depended on for the faster, off-chain virtual fund and defund operations.

the consensus_channel implementation of ledger channels has customized representation of ledger outcomes:

type LedgerOutcome struct {
	assetAddress types.Address // Address of the asset type
	leader       Balance       // Balance of participants[0]
	follower     Balance       // Balance of participants[1]
	guarantees   map[types.Destination]Guarantee
	// add this?
        topUp        Balance // Balance of a running topup - usually empty
}

and conversions to and from canonical Exit-Format representation:

// AsOutcome converts a LedgerOutcome to an on-chain exit according to the following convention:
//   - the "leader" balance is first
//   - the "follower" balance is second
//   - guarantees follow, sorted according to their target destinations
func (o *LedgerOutcome) AsOutcome() outcome.Exit {
	// The first items are [leader, follower] balances
	allocations := outcome.Allocations{o.leader.AsAllocation(), o.follower.AsAllocation()}

	// Followed by guarantees, _sorted by the target destination_
	keys := make([]types.Destination, 0, len(o.guarantees))
	for k := range o.guarantees {
		keys = append(keys, k)
	}
	sort.Slice(keys, func(i, j int) bool {
		return keys[i].String() < keys[j].String()
	})

	for _, target := range keys {
		allocations = append(allocations, o.guarantees[target].AsAllocation())
	}

                // add this?
	        // the last item is the running topUp balance
	        if (o.topUp != Balance{}) {
		        allocations = append(allocations, o.topUp.AsAllocation())
	        }

	return outcome.Exit{
		outcome.SingleAssetExit{
			Asset:       o.assetAddress,
			Allocations: allocations,
		},
	}
}
// FromExit creates a new LedgerOutcome from the given SingleAssetExit.
//
// It makes the following assumptions about the exit:
//   - The first allocation entry is for the ledger leader
//   - The second allocation entry is for the ledger follower
//   - The last allocation may be a running topup               (added)
//   - All other allocations are guarantees
func FromExit(sae outcome.SingleAssetExit) (LedgerOutcome, error) {
	var (
		leader     = Balance{destination: sae.Allocations[0].Destination, amount: sae.Allocations[0].Amount}
		follower   = Balance{destination: sae.Allocations[1].Destination, amount: sae.Allocations[1].Amount}
		guarantees = make(map[types.Destination]Guarantee)
	)

	for _, a := range sae.Allocations {
		if a.AllocationType == outcome.GuaranteeAllocationType {
			gM, err := outcome.DecodeIntoGuaranteeMetadata(a.Metadata)
			if err != nil {
				return LedgerOutcome{}, fmt.Errorf("failed to decode guarantee metadata: %w", err)
			}

			g := Guarantee{
				amount: a.Amount,
				target: a.Destination,
				left:   gM.Left,
				right:  gM.Right,
			}
			guarantees[a.Destination] = g
		}
	}

	// add
        if len(guarantees) != len(sae.Allocations)-2 {
		last := sae.Allocations[len(sae.Allocations)-1]
		var topUp = Balance{destination: last.Destination, amount: last.Amount}
	}

	return LedgerOutcome{leader: leader, follower: follower, guarantees: guarantees, assetAddress: sae.Asset}, nil
}
@NiloCK NiloCK added ✨ enhancement New feature or request 🏗️ Productionization Things that need to be tackled for nitro to run in a production environment on-chain-protocol labels Sep 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🏗️ Productionization Things that need to be tackled for nitro to run in a production environment ✨ enhancement New feature or request on-chain-protocol
Projects
None yet
Development

No branches or pull requests

1 participant