mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 09:57:27 +00:00 
			
		
		
		
	add usage instructions
This commit is contained in:
		
							parent
							
								
									f58262c8b0
								
							
						
					
					
						commit
						894509f1da
					
				@ -1,12 +1,40 @@
 | 
				
			|||||||
Payment channel implementation sketch
 | 
					# Unidrectional Payment Channels
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Simplifications:
 | 
					This module implements simple but feature complete unidirectional payment channels. Channels can be opened by a sender and closed immediately by the receiver, or by the sender subject to a dispute period. There are no top-ups or partial withdrawals (yet). Channels support multiple currencies.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 - unidirectional paychans
 | 
					>Note: This is a work in progress. More feature planned. More test cases needed.
 | 
				
			||||||
 - no top ups or partial withdrawals (only opening and closing)
 | 
					
 | 
				
			||||||
 | 
					# Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Create a channel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kvcli paychan create --from <your account name> --to <receivers address> --amount 100KVA --chain-id <your chain ID>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Send an off-chain payment
 | 
				
			||||||
 | 
					Send a payment for 10 KVA.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kvcli paychan pay --from <your account name> --sen-amt 90KVA --rec-amt 10KVA --chan-id <ID of channel> --filename payment.json --chain-id <your chain ID>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Send the file payment.json to your receiver. Then they run the following to verify.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kvcli paychan verify --filename payment.json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Close a channel
 | 
				
			||||||
 | 
					The receiver can close immediately at any time.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kvcli paychan submit --from <receiver's account name> --payment payment.json --chain-id <your chain ID>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The sender can close subject to a dispute period during which the receiver can overrule them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kvcli paychan submit --from <receiver's account name> --payment payment.json --chain-id <your chain ID>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Get info on a channel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kvcli get --chan-id <ID of channel>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 TODO
 | 
					# TODOs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 - in code TODOs
 | 
					 - in code TODOs
 | 
				
			||||||
 - Tidy up - method descriptions, heading comments, remove uneccessary comments, README/docs
 | 
					 - Tidy up - method descriptions, heading comments, remove uneccessary comments, README/docs
 | 
				
			||||||
 - Find a better name for Queue - clarify distinction between int slice and abstract queue concept
 | 
					 - Find a better name for Queue - clarify distinction between int slice and abstract queue concept
 | 
				
			||||||
@ -22,5 +50,5 @@ Simplifications:
 | 
				
			|||||||
 - use custom errors instead of using sdk.ErrInternal
 | 
					 - use custom errors instead of using sdk.ErrInternal
 | 
				
			||||||
 - split off signatures from update as with txs/msgs - testing easier, code easier to use, doesn't store sigs unecessarily on chain
 | 
					 - split off signatures from update as with txs/msgs - testing easier, code easier to use, doesn't store sigs unecessarily on chain
 | 
				
			||||||
 - consider removing pubKey from UpdateSignature - instead let channel module access accountMapper
 | 
					 - consider removing pubKey from UpdateSignature - instead let channel module access accountMapper
 | 
				
			||||||
 - remove printout from tests when app initialised
 | 
					 | 
				
			||||||
 - refactor queue into one object
 | 
					 - refactor queue into one object
 | 
				
			||||||
 | 
					 - remove printout during tests caused by mock app initialisation
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,6 @@ package lcd
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import ()
 | 
					import ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// implement thing that polls blockchain and handles paychan disputes
 | 
					// implement a thing to poll blockchain and handles paychan disputes
 | 
				
			||||||
// needs plugged into LCD - add a "background processes" slot in the LCD run function?
 | 
					// needs plugged into LCD - add a "background processes" slot in the LCD run function?
 | 
				
			||||||
// eventually LCD evolves into paychan (network) daemon
 | 
					// eventually LCD could evolve into paychan (network) daemon
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@ func EndBlocker(ctx sdk.Context, k Keeper) sdk.Tags {
 | 
				
			|||||||
	tags := sdk.EmptyTags()
 | 
						tags := sdk.EmptyTags()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Iterate through submittedUpdatesQueue
 | 
						// Iterate through submittedUpdatesQueue
 | 
				
			||||||
	// TODO optimise so it doesn't pull every update from DB every block
 | 
						// TODO optimise so it doesn't pull every channel update from DB every block
 | 
				
			||||||
	q := k.getSubmittedUpdatesQueue(ctx)
 | 
						q := k.getSubmittedUpdatesQueue(ctx)
 | 
				
			||||||
	var sUpdate SubmittedUpdate
 | 
						var sUpdate SubmittedUpdate
 | 
				
			||||||
	var found bool
 | 
						var found bool
 | 
				
			||||||
@ -31,6 +31,5 @@ func EndBlocker(ctx sdk.Context, k Keeper) sdk.Tags {
 | 
				
			|||||||
			tags.AppendTags(channelTags)
 | 
								tags.AppendTags(channelTags)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return tags
 | 
						return tags
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -11,12 +11,13 @@ import (
 | 
				
			|||||||
// Keeper of the paychan store
 | 
					// Keeper of the paychan store
 | 
				
			||||||
// Handles validation internally. Does not rely on calling code to do validation.
 | 
					// Handles validation internally. Does not rely on calling code to do validation.
 | 
				
			||||||
// Aim to keep public methods safe, private ones not necessaily.
 | 
					// Aim to keep public methods safe, private ones not necessaily.
 | 
				
			||||||
 | 
					// Keepers contain main business logic of the module.
 | 
				
			||||||
type Keeper struct {
 | 
					type Keeper struct {
 | 
				
			||||||
	storeKey   sdk.StoreKey
 | 
						storeKey   sdk.StoreKey
 | 
				
			||||||
	cdc        *wire.Codec // needed to serialize objects before putting them in the store
 | 
						cdc        *wire.Codec // needed to serialize objects before putting them in the store
 | 
				
			||||||
	coinKeeper bank.Keeper
 | 
						coinKeeper bank.Keeper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//codespace sdk.CodespaceType
 | 
						//codespace sdk.CodespaceType TODO custom errors
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Called when creating new app.
 | 
					// Called when creating new app.
 | 
				
			||||||
@ -30,12 +31,10 @@ func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper) Keeper {
 | 
				
			|||||||
	return keeper
 | 
						return keeper
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ============================================== Main Business Logic
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Create a new payment channel and lock up sender funds.
 | 
					// Create a new payment channel and lock up sender funds.
 | 
				
			||||||
func (k Keeper) CreateChannel(ctx sdk.Context, sender sdk.AccAddress, receiver sdk.AccAddress, coins sdk.Coins) (sdk.Tags, sdk.Error) {
 | 
					func (k Keeper) CreateChannel(ctx sdk.Context, sender sdk.AccAddress, receiver sdk.AccAddress, coins sdk.Coins) (sdk.Tags, sdk.Error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check addresses valid (Technicaly don't need to check sender address is valid as SubtractCoins does that)
 | 
						// Check addresses valid (Technicaly don't need to check sender address is valid as SubtractCoins checks)
 | 
				
			||||||
	if len(sender) == 0 {
 | 
						if len(sender) == 0 {
 | 
				
			||||||
		return nil, sdk.ErrInvalidAddress(sender.String())
 | 
							return nil, sdk.ErrInvalidAddress(sender.String())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -71,6 +70,7 @@ func (k Keeper) CreateChannel(ctx sdk.Context, sender sdk.AccAddress, receiver s
 | 
				
			|||||||
	return tags, err
 | 
						return tags, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initiate the close of a payment channel, subject to dispute period.
 | 
				
			||||||
func (k Keeper) InitCloseChannelBySender(ctx sdk.Context, update Update) (sdk.Tags, sdk.Error) {
 | 
					func (k Keeper) InitCloseChannelBySender(ctx sdk.Context, update Update) (sdk.Tags, sdk.Error) {
 | 
				
			||||||
	// This is roughly the default path for non unidirectional channels
 | 
						// This is roughly the default path for non unidirectional channels
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -87,6 +87,7 @@ func (k Keeper) InitCloseChannelBySender(ctx sdk.Context, update Update) (sdk.Ta
 | 
				
			|||||||
	q := k.getSubmittedUpdatesQueue(ctx)
 | 
						q := k.getSubmittedUpdatesQueue(ctx)
 | 
				
			||||||
	if q.Contains(update.ChannelID) {
 | 
						if q.Contains(update.ChannelID) {
 | 
				
			||||||
		// Someone has previously tried to update channel
 | 
							// Someone has previously tried to update channel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// In bidirectional channels the new update is compared against existing and replaces it if it has a higher sequence number.
 | 
							// In bidirectional channels the new update is compared against existing and replaces it if it has a higher sequence number.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// existingSUpdate, found := k.getSubmittedUpdate(ctx, update.ChannelID)
 | 
							// existingSUpdate, found := k.getSubmittedUpdate(ctx, update.ChannelID)
 | 
				
			||||||
@ -97,7 +98,7 @@ func (k Keeper) InitCloseChannelBySender(ctx sdk.Context, update Update) (sdk.Ta
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// However in unidirectional case, only the sender can close a channel this way. No clear need for them to be able to submit an update replacing a previous one they sent, so don't allow it.
 | 
							// However in unidirectional case, only the sender can close a channel this way. No clear need for them to be able to submit an update replacing a previous one they sent, so don't allow it.
 | 
				
			||||||
		// TODO tags
 | 
							// TODO tags
 | 
				
			||||||
		// TODO custom errors return sdk.EmptyTags(), sdk.NewError("Sender can't submit an update for channel if one has already been submitted.")
 | 
							// TODO custom errors
 | 
				
			||||||
		sdk.ErrInternal("Sender can't submit an update for channel if one has already been submitted.")
 | 
							sdk.ErrInternal("Sender can't submit an update for channel if one has already been submitted.")
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		// No one has tried to update channel
 | 
							// No one has tried to update channel
 | 
				
			||||||
@ -113,6 +114,7 @@ func (k Keeper) InitCloseChannelBySender(ctx sdk.Context, update Update) (sdk.Ta
 | 
				
			|||||||
	return tags, nil
 | 
						return tags, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Immediately close a channel.
 | 
				
			||||||
func (k Keeper) CloseChannelByReceiver(ctx sdk.Context, update Update) (sdk.Tags, sdk.Error) {
 | 
					func (k Keeper) CloseChannelByReceiver(ctx sdk.Context, update Update) (sdk.Tags, sdk.Error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// get the channel
 | 
						// get the channel
 | 
				
			||||||
@ -138,8 +140,7 @@ func (k Keeper) CloseChannelByReceiver(ctx sdk.Context, update Update) (sdk.Tags
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Main function that compare updates against each other.
 | 
					// Main function that compare updates against each other.
 | 
				
			||||||
// Pure function
 | 
					// Pure function, Not needed in unidirectional case.
 | 
				
			||||||
// Not needed in unidirectional case.
 | 
					 | 
				
			||||||
// func (k Keeper) applyNewUpdate(existingSUpdate SubmittedUpdate, proposedUpdate Update) SubmittedUpdate {
 | 
					// func (k Keeper) applyNewUpdate(existingSUpdate SubmittedUpdate, proposedUpdate Update) SubmittedUpdate {
 | 
				
			||||||
// 	var returnUpdate SubmittedUpdate
 | 
					// 	var returnUpdate SubmittedUpdate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -183,7 +184,8 @@ func VerifyUpdate(channel Channel, update Update) sdk.Error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// unsafe close channel - doesn't check if update matches existing channel TODO make safer?
 | 
					// unsafe close channel - doesn't check if update matches existing channel
 | 
				
			||||||
 | 
					// TODO make safer?
 | 
				
			||||||
func (k Keeper) closeChannel(ctx sdk.Context, update Update) (sdk.Tags, sdk.Error) {
 | 
					func (k Keeper) closeChannel(ctx sdk.Context, update Update) (sdk.Tags, sdk.Error) {
 | 
				
			||||||
	var err sdk.Error
 | 
						var err sdk.Error
 | 
				
			||||||
	var tags sdk.Tags
 | 
						var tags sdk.Tags
 | 
				
			||||||
@ -194,7 +196,6 @@ func (k Keeper) closeChannel(ctx sdk.Context, update Update) (sdk.Tags, sdk.Erro
 | 
				
			|||||||
	// Add coins to sender and receiver
 | 
						// Add coins to sender and receiver
 | 
				
			||||||
	// TODO check for possible errors first to avoid coins being half paid out?
 | 
						// TODO check for possible errors first to avoid coins being half paid out?
 | 
				
			||||||
	for i, coins := range update.Payout {
 | 
						for i, coins := range update.Payout {
 | 
				
			||||||
		// TODO check somewhere if coins are not negative?
 | 
					 | 
				
			||||||
		_, tags, err = k.coinKeeper.AddCoins(ctx, channel.Participants[i], coins)
 | 
							_, tags, err = k.coinKeeper.AddCoins(ctx, channel.Participants[i], coins)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			panic(err)
 | 
								panic(err)
 | 
				
			||||||
@ -223,7 +224,7 @@ func verifySignatures(channel Channel, update Update) bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// =========================================== QUEUE
 | 
					// =========================================== SUBMITTED UPDATES QUEUE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (k Keeper) addToSubmittedUpdatesQueue(ctx sdk.Context, sUpdate SubmittedUpdate) {
 | 
					func (k Keeper) addToSubmittedUpdatesQueue(ctx sdk.Context, sUpdate SubmittedUpdate) {
 | 
				
			||||||
	// always overwrite prexisting values - leave paychan logic to higher levels
 | 
						// always overwrite prexisting values - leave paychan logic to higher levels
 | 
				
			||||||
 | 
				
			|||||||
@ -3,8 +3,6 @@ package paychan
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
						sdk "github.com/cosmos/cosmos-sdk/types"
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
	//"github.com/stretchr/testify/require"
 | 
					 | 
				
			||||||
	//"github.com/tendermint/tendermint/crypto"
 | 
					 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -315,129 +313,3 @@ func TestKeeper(t *testing.T) {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
func privAndAddr() (crypto.PrivKey, sdk.AccAddress) {
 | 
					 | 
				
			||||||
	priv := ed25519.GenPrivKey()
 | 
					 | 
				
			||||||
	addr := sdk.AccAddress(priv.PubKey().Address())
 | 
					 | 
				
			||||||
	return priv, addr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
sig, err := priv.Sign(signBytes)
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) {
 | 
					 | 
				
			||||||
	// create db
 | 
					 | 
				
			||||||
	db := dbm.NewMemDB()
 | 
					 | 
				
			||||||
	// create keys
 | 
					 | 
				
			||||||
	authKey := sdk.NewKVStoreKey("authkey")
 | 
					 | 
				
			||||||
	paychanKey := sdk.NewKVStoreKey("paychankey")
 | 
					 | 
				
			||||||
	// create new multistore around db
 | 
					 | 
				
			||||||
	ms := store.NewCommitMultiStore(db) // DB handle plus store key maps
 | 
					 | 
				
			||||||
	// register separate stores in the multistore
 | 
					 | 
				
			||||||
	ms.MountStoreWithDB(authKey, sdk.StoreTypeIAVL, db) // sets store key map
 | 
					 | 
				
			||||||
	ms.MountStoreWithDB(paychanKey, sdk.StoreTypeIAVL, db)
 | 
					 | 
				
			||||||
	ms.LoadLatestVersion()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ms, authKey, paychanKey
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func setupCodec() *wire.Codec {
 | 
					 | 
				
			||||||
	cdc := wire.NewCodec()
 | 
					 | 
				
			||||||
	auth.RegisterBaseAccount(cdc)
 | 
					 | 
				
			||||||
	// TODO might need to register paychan struct
 | 
					 | 
				
			||||||
	return cdc
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestKeeper(t *testing.T) {
 | 
					 | 
				
			||||||
	// Setup
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// create multistore and key
 | 
					 | 
				
			||||||
	ms, authKey, paychanKey := setupMultiStore()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// create and initialise codec(s)
 | 
					 | 
				
			||||||
	cdc := setupCodec()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// create context
 | 
					 | 
				
			||||||
	ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// create accountMapper
 | 
					 | 
				
			||||||
	accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// create coinkeeper
 | 
					 | 
				
			||||||
	coinKeeper := bank.NewKeeper(accountMapper)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// create keeper
 | 
					 | 
				
			||||||
	paychanKeeper := NewKeeper(cdc, paychanKey, coinKeeper)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Test no paychans exist
 | 
					 | 
				
			||||||
	_, exists := paychanKeeper.GetPaychan(ctx, sdk.Address{}, sdk.Address{}, 0)
 | 
					 | 
				
			||||||
	if exists {
 | 
					 | 
				
			||||||
		t.Error("payment channel found when none exist")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Test paychan can be set and get
 | 
					 | 
				
			||||||
	p := Paychan{
 | 
					 | 
				
			||||||
		Sender:   sdk.Address([]byte("senderAddress")),
 | 
					 | 
				
			||||||
		Receiver: sdk.Address([]byte("receiverAddress")),
 | 
					 | 
				
			||||||
		Id:       0,
 | 
					 | 
				
			||||||
		Balance:  sdk.Coins{{"KVA", 100}},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	paychanKeeper.setPaychan(ctx, p)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, exists = paychanKeeper.GetPaychan(ctx, p.Sender, p.Receiver, p.Id)
 | 
					 | 
				
			||||||
	if !exists {
 | 
					 | 
				
			||||||
		t.Error("payment channel not found")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Test create paychan under normal conditions
 | 
					 | 
				
			||||||
	senderAddress := sdk.Address([]byte("senderAddress"))
 | 
					 | 
				
			||||||
	senderFunds := sdk.Coins{{"KVA", 100}}
 | 
					 | 
				
			||||||
	receiverAddress := sdk.Address([]byte("receiverAddress"))
 | 
					 | 
				
			||||||
	balance := sdk.Coins{{"KVA", 10}}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	coinKeeper.SetCoins(ctx, senderAddress, senderFunds)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, err := paychanKeeper.CreatePaychan(ctx, senderAddress, receiverAddress, balance)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Error("unexpected error created payment channel", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	p, exists = paychanKeeper.GetPaychan(ctx, senderAddress, receiverAddress, 0)
 | 
					 | 
				
			||||||
	if !exists {
 | 
					 | 
				
			||||||
		t.Error("payment channel missing")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if !p.Balance.IsEqual(balance) {
 | 
					 | 
				
			||||||
		t.Error("payment channel balance incorrect", p.Balance, balance)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	expectedNewSenderFunds := senderFunds.Minus(balance)
 | 
					 | 
				
			||||||
	if !coinKeeper.GetCoins(ctx, senderAddress).IsEqual(expectedNewSenderFunds) {
 | 
					 | 
				
			||||||
		t.Error("sender has incorrect balance after paychan creation")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Test close paychan under normal conditions
 | 
					 | 
				
			||||||
	senderFunds = coinKeeper.GetCoins(ctx, senderAddress)
 | 
					 | 
				
			||||||
	receiverAmount := sdk.Coins{{"KVA", 9}}
 | 
					 | 
				
			||||||
	_, err = paychanKeeper.ClosePaychan(ctx, senderAddress, receiverAddress, 0, receiverAmount)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Error("unexpected error closing payment channel", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// paychan shouldn't exist
 | 
					 | 
				
			||||||
	_, exists = paychanKeeper.GetPaychan(ctx, senderAddress, receiverAddress, 0)
 | 
					 | 
				
			||||||
	if exists {
 | 
					 | 
				
			||||||
		t.Error("payment channel should not exist")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// sender's funds should have increased
 | 
					 | 
				
			||||||
	expectedNewSenderFunds = senderFunds.Plus(balance.Minus(receiverAmount))
 | 
					 | 
				
			||||||
	if !coinKeeper.GetCoins(ctx, senderAddress).IsEqual(expectedNewSenderFunds) {
 | 
					 | 
				
			||||||
		t.Error("sender has incorrect balance after paychan creation", expectedNewSenderFunds)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// receiver's funds should have increased
 | 
					 | 
				
			||||||
	expectedNewReceiverFunds := receiverAmount // started at zero
 | 
					 | 
				
			||||||
	if !coinKeeper.GetCoins(ctx, receiverAddress).IsEqual(expectedNewReceiverFunds) {
 | 
					 | 
				
			||||||
		t.Error("receiver has incorrect balance after paychan creation")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -21,10 +21,10 @@ func createMockApp(accountSeeds []string) (sdk.Context, bank.Keeper, Keeper, []s
 | 
				
			|||||||
	// create channel keeper
 | 
						// create channel keeper
 | 
				
			||||||
	keyChannel := sdk.NewKVStoreKey("channel")
 | 
						keyChannel := sdk.NewKVStoreKey("channel")
 | 
				
			||||||
	channelKeeper := NewKeeper(mApp.Cdc, keyChannel, coinKeeper)
 | 
						channelKeeper := NewKeeper(mApp.Cdc, keyChannel, coinKeeper)
 | 
				
			||||||
	// add router?
 | 
						// could add router for msg tests
 | 
				
			||||||
	//mapp.Router().AddRoute("channel", NewHandler(channelKeeper))
 | 
						//mapp.Router().AddRoute("channel", NewHandler(channelKeeper))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mApp.CompleteSetup([]*sdk.KVStoreKey{keyChannel}) // needs to be called I think to finish setup
 | 
						mApp.CompleteSetup([]*sdk.KVStoreKey{keyChannel})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// create some accounts
 | 
						// create some accounts
 | 
				
			||||||
	genAccFunding := sdk.Coins{sdk.NewCoin("KVA", 1000)}
 | 
						genAccFunding := sdk.Coins{sdk.NewCoin("KVA", 1000)}
 | 
				
			||||||
@ -57,6 +57,5 @@ func createTestGenAccounts(accountSeeds []string, genCoins sdk.Coins) (genAccs [
 | 
				
			|||||||
		pubKeys = append(pubKeys, pubKey)
 | 
							pubKeys = append(pubKeys, pubKey)
 | 
				
			||||||
		addrs = append(addrs, addr)
 | 
							addrs = append(addrs, addr)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -16,12 +16,14 @@ type Channel struct {
 | 
				
			|||||||
	Coins        sdk.Coins
 | 
						Coins        sdk.Coins
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ChannelID int64 // TODO should this be positive only
 | 
					const ChannelDisputeTime = int64(2000) // measured in blocks TODO pick reasonable time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ChannelID int64 // TODO should this be positive only?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The data that is passed between participants as payments, and submitted to the blockchain to close a channel.
 | 
					// The data that is passed between participants as payments, and submitted to the blockchain to close a channel.
 | 
				
			||||||
type Update struct {
 | 
					type Update struct {
 | 
				
			||||||
	ChannelID ChannelID
 | 
						ChannelID ChannelID
 | 
				
			||||||
	Payout    Payout //map[string]sdk.Coins // map of bech32 addresses to coins
 | 
						Payout    Payout
 | 
				
			||||||
	//Sequence  int64 Not needed for unidirectional channels
 | 
						//Sequence  int64 Not needed for unidirectional channels
 | 
				
			||||||
	Sigs [1]UpdateSignature // only sender needs to sign in unidirectional
 | 
						Sigs [1]UpdateSignature // only sender needs to sign in unidirectional
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -40,11 +42,6 @@ func (u Update) GetSignBytes() []byte {
 | 
				
			|||||||
	return sdk.MustSortJSON(bz)
 | 
						return sdk.MustSortJSON(bz)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type UpdateSignature struct {
 | 
					 | 
				
			||||||
	PubKey          crypto.PubKey
 | 
					 | 
				
			||||||
	CryptoSignature crypto.Signature
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Payout [2]sdk.Coins // a list of coins to be paid to each of Channel.Participants
 | 
					type Payout [2]sdk.Coins // a list of coins to be paid to each of Channel.Participants
 | 
				
			||||||
func (p Payout) IsNotNegative() bool {
 | 
					func (p Payout) IsNotNegative() bool {
 | 
				
			||||||
	result := true
 | 
						result := true
 | 
				
			||||||
@ -62,17 +59,10 @@ func (p Payout) Sum() sdk.Coins {
 | 
				
			|||||||
	return total
 | 
						return total
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Get the coins associated with payout address. TODO constrain payouts to only have one entry per address
 | 
					type UpdateSignature struct {
 | 
				
			||||||
/*func (payouts Payouts) Get(addr sdk.AccAddress) (sdk.Coins, bool) {
 | 
						PubKey          crypto.PubKey
 | 
				
			||||||
	for _, p := range payouts {
 | 
						CryptoSignature crypto.Signature
 | 
				
			||||||
		if reflect.DeepEqual(p.Address, addr) {
 | 
					 | 
				
			||||||
			return p.Coins, true
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil, false
 | 
					 | 
				
			||||||
}*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const ChannelDisputeTime = int64(2000) // measured in blocks TODO pick reasonable time
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// An update that has been submitted to the blockchain, but not yet acted on.
 | 
					// An update that has been submitted to the blockchain, but not yet acted on.
 | 
				
			||||||
type SubmittedUpdate struct {
 | 
					type SubmittedUpdate struct {
 | 
				
			||||||
@ -135,19 +125,6 @@ type MsgCreate struct {
 | 
				
			|||||||
	Coins        sdk.Coins
 | 
						Coins        sdk.Coins
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//Create a new message.
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
Called in client code when constructing transaction from cli args to send to the network.
 | 
					 | 
				
			||||||
maybe just a placeholder for more advanced future functionality?
 | 
					 | 
				
			||||||
func (msg CreatMsg) NewMsgCreate(sender sdk.Address, receiver sdk.Address, amount sdk.Coins) MsgCreate {
 | 
					 | 
				
			||||||
	return MsgCreate{
 | 
					 | 
				
			||||||
		sender
 | 
					 | 
				
			||||||
		receiver
 | 
					 | 
				
			||||||
		amount
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (msg MsgCreate) Type() string { return "paychan" }
 | 
					func (msg MsgCreate) Type() string { return "paychan" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (msg MsgCreate) GetSignBytes() []byte {
 | 
					func (msg MsgCreate) GetSignBytes() []byte {
 | 
				
			||||||
@ -164,7 +141,7 @@ func (msg MsgCreate) ValidateBasic() sdk.Error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	//TODO implement
 | 
						//TODO implement
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/* old logic
 | 
				
			||||||
	// check if all fields present / not 0 valued
 | 
						// check if all fields present / not 0 valued
 | 
				
			||||||
	if len(msg.Sender) == 0 {
 | 
						if len(msg.Sender) == 0 {
 | 
				
			||||||
		return sdk.ErrInvalidAddress(msg.Sender.String())
 | 
							return sdk.ErrInvalidAddress(msg.Sender.String())
 | 
				
			||||||
@ -198,12 +175,6 @@ type MsgSubmitUpdate struct {
 | 
				
			|||||||
	Submitter sdk.AccAddress
 | 
						Submitter sdk.AccAddress
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// func (msg MsgSubmitUpdate) NewMsgSubmitUpdate(update Update) MsgSubmitUpdate {
 | 
					 | 
				
			||||||
// 	return MsgSubmitUpdate{
 | 
					 | 
				
			||||||
// 		update
 | 
					 | 
				
			||||||
// 	}
 | 
					 | 
				
			||||||
// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (msg MsgSubmitUpdate) Type() string { return "paychan" }
 | 
					func (msg MsgSubmitUpdate) Type() string { return "paychan" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (msg MsgSubmitUpdate) GetSignBytes() []byte {
 | 
					func (msg MsgSubmitUpdate) GetSignBytes() []byte {
 | 
				
			||||||
@ -217,7 +188,7 @@ func (msg MsgSubmitUpdate) GetSignBytes() []byte {
 | 
				
			|||||||
func (msg MsgSubmitUpdate) ValidateBasic() sdk.Error {
 | 
					func (msg MsgSubmitUpdate) ValidateBasic() sdk.Error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO implement
 | 
						// TODO implement
 | 
				
			||||||
	/*
 | 
						/* old logic
 | 
				
			||||||
	// check if all fields present / not 0 valued
 | 
						// check if all fields present / not 0 valued
 | 
				
			||||||
	if len(msg.Sender) == 0 {
 | 
						if len(msg.Sender) == 0 {
 | 
				
			||||||
		return sdk.ErrInvalidAddress(msg.Sender.String())
 | 
							return sdk.ErrInvalidAddress(msg.Sender.String())
 | 
				
			||||||
@ -245,7 +216,5 @@ func (msg MsgSubmitUpdate) ValidateBasic() sdk.Error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (msg MsgSubmitUpdate) GetSigners() []sdk.AccAddress {
 | 
					func (msg MsgSubmitUpdate) GetSigners() []sdk.AccAddress {
 | 
				
			||||||
	// Signing not strictly necessary as signatures contained within the channel update.
 | 
					 | 
				
			||||||
	// TODO add signature by submitting address
 | 
					 | 
				
			||||||
	return []sdk.AccAddress{msg.Submitter}
 | 
						return []sdk.AccAddress{msg.Submitter}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ func RegisterWire(cdc *wire.Codec) {
 | 
				
			|||||||
	cdc.RegisterConcrete(MsgSubmitUpdate{}, "paychan/MsgSubmitUpdate", nil)
 | 
						cdc.RegisterConcrete(MsgSubmitUpdate{}, "paychan/MsgSubmitUpdate", nil)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO move this to near the msg definitions
 | 
					// TODO move this to near the msg definitions?
 | 
				
			||||||
var msgCdc = wire.NewCodec()
 | 
					var msgCdc = wire.NewCodec()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user