mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-10-31 21:47:27 +00:00 
			
		
		
		
	initial sketch
This commit is contained in:
		
							parent
							
								
									b04556844f
								
							
						
					
					
						commit
						afb7de58dd
					
				| @ -55,8 +55,9 @@ func main() { | ||||
| 		)...) | ||||
| 
 | ||||
| 	rootCmd.AddCommand( | ||||
| 		client.PostCommands( | ||||
| 		client.PostCommands( // this just wraps the input cmds with common flags
 | ||||
| 			bankcmd.SendTxCmd(cdc), | ||||
| 			// paychan commands...
 | ||||
| 			//ibccmd.IBCTransferCmd(cdc),
 | ||||
| 			//ibccmd.IBCRelayCmd(cdc),
 | ||||
| 			//stakecmd.GetCmdCreateValidator(cdc),
 | ||||
|  | ||||
							
								
								
									
										15
									
								
								internal/x/paychan/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								internal/x/paychan/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| Paychan Sketch | ||||
| 
 | ||||
| Simplifications: | ||||
| 
 | ||||
|  - unidirectional paychans | ||||
|  - no top ups or partial withdrawals (only opening and closing) | ||||
|  - no protection against fund lock up from dissapearing receiver | ||||
| 
 | ||||
| 
 | ||||
|  TODO | ||||
|   - fill in details | ||||
|   - add tests | ||||
|   - is errors.go needed? | ||||
|   - is wire.go needed? | ||||
|   - remove simplifications | ||||
							
								
								
									
										68
									
								
								internal/x/paychan/client/cmd/cmd.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								internal/x/paychan/client/cmd/cmd.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | ||||
| package cli | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/spf13/cobra" | ||||
| 	//"github.com/cosmos/cosmos-sdk/client/context"
 | ||||
| 	//sdk "github.com/cosmos/cosmos-sdk/types"
 | ||||
| 	//"github.com/cosmos/cosmos-sdk/wire"
 | ||||
| 	//"github.com/cosmos/cosmos-sdk/x/auth"
 | ||||
| ) | ||||
| 
 | ||||
| // list of functions that return pointers to cobra commands
 | ||||
| // No local storage needed for cli acting as a sender
 | ||||
| 
 | ||||
| // create paychan
 | ||||
| // close paychan
 | ||||
| // get paychan(s)
 | ||||
| // send paychan payment
 | ||||
| // get balance from receiver
 | ||||
| 
 | ||||
| // example from x/auth
 | ||||
| /* | ||||
| func GetAccountCmd(storeName string, cdc *wire.Codec, decoder auth.AccountDecoder) *cobra.Command { | ||||
| 	return &cobra.Command{ | ||||
| 		Use:   "account [address]", | ||||
| 		Short: "Query account balance", | ||||
| 		Args:  cobra.ExactArgs(1), | ||||
| 		RunE: func(cmd *cobra.Command, args []string) error { | ||||
| 
 | ||||
| 			// find the key to look up the account
 | ||||
| 			addr := args[0] | ||||
| 
 | ||||
| 			key, err := sdk.GetAccAddressBech32(addr) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			// perform query
 | ||||
| 			ctx := context.NewCoreContextFromViper() | ||||
| 			res, err := ctx.Query(auth.AddressStoreKey(key), storeName) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			// Check if account was found
 | ||||
| 			if res == nil { | ||||
| 				return sdk.ErrUnknownAddress("No account with address " + addr + | ||||
| 					" was found in the state.\nAre you sure there has been a transaction involving it?") | ||||
| 			} | ||||
| 
 | ||||
| 			// decode the value
 | ||||
| 			account, err := decoder(res) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			// print out whole account
 | ||||
| 			output, err := wire.MarshalJSONIndent(cdc, account) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			fmt.Println(string(output)) | ||||
| 			return nil | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| */ | ||||
							
								
								
									
										7
									
								
								internal/x/paychan/client/rest/channel-watcher.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								internal/x/paychan/client/rest/channel-watcher.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| package lcd | ||||
| 
 | ||||
| import () | ||||
| 
 | ||||
| // implement thing that polls blockchain and handles paychan disputes
 | ||||
| // needs plugged into LCD - add a "background processes" slot in the LCD run function?
 | ||||
| // eventually LCD evolves into paychan (network) daemon
 | ||||
							
								
								
									
										22
									
								
								internal/x/paychan/client/rest/rest.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								internal/x/paychan/client/rest/rest.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| package rest | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gorilla/mux" | ||||
| 	//"github.com/tendermint/go-crypto/keys"
 | ||||
| 	//"github.com/cosmos/cosmos-sdk/client/context"
 | ||||
| 	//"github.com/cosmos/cosmos-sdk/wire"
 | ||||
| ) | ||||
| 
 | ||||
| // RegisterRoutes registers paychan-related REST handlers to a router
 | ||||
| func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) { | ||||
| 	//r.HandleFunc("/accounts/{address}/send", SendRequestHandlerFn(cdc, kb, ctx)).Methods("POST")
 | ||||
| } | ||||
| 
 | ||||
| // handler functions ...
 | ||||
| // create paychan
 | ||||
| // close paychan
 | ||||
| // get paychan(s)
 | ||||
| // send paychan payment
 | ||||
| // get balance from receiver
 | ||||
| // get balance from local storage
 | ||||
| // handle incoming payment
 | ||||
							
								
								
									
										33
									
								
								internal/x/paychan/handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								internal/x/paychan/handler.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| package paychan | ||||
| 
 | ||||
| import ( | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	"reflect" | ||||
| ) | ||||
| 
 | ||||
| // NewHandler returns a handler for "paychan" type messages.
 | ||||
| func NewHandler(k Keeper) sdk.Handler { | ||||
| 	return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { | ||||
| 		switch msg := msg.(type) { | ||||
| 		case MsgSend: | ||||
| 			return handleMsgSend(ctx, k, msg) | ||||
| 		case MsgIssue: | ||||
| 			return handleMsgIssue(ctx, k, msg) | ||||
| 		default: | ||||
| 			errMsg := "Unrecognized paychan Msg type: " + reflect.TypeOf(msg).Name() | ||||
| 			return sdk.ErrUnknownRequest(errMsg).Result() | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Handle CreateMsg.
 | ||||
| func handleMsgCreate(ctx sdk.Context, k Keeper, msg MsgSend) sdk.Result { | ||||
| 	// k.CreatePaychan(args...)
 | ||||
| 	// handle erros
 | ||||
| } | ||||
| 
 | ||||
| // Handle CloseMsg.
 | ||||
| func handleMsgClose(ctx sdk.Context, k Keeper, msg MsgIssue) sdk.Result { | ||||
| 	// k.ClosePaychan(args...)
 | ||||
| 	// handle errors
 | ||||
| } | ||||
							
								
								
									
										62
									
								
								internal/x/paychan/keeper.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								internal/x/paychan/keeper.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | ||||
| package paychan | ||||
| 
 | ||||
| // keeper of the paychan store
 | ||||
| type Keeper struct { | ||||
| 	storeKey   sdk.StoreKey | ||||
| 	cdc        *wire.Codec // needed?
 | ||||
| 	coinKeeper bank.Keeper | ||||
| 
 | ||||
| 	// codespace
 | ||||
| 	codespace sdk.CodespaceType // ??
 | ||||
| } | ||||
| 
 | ||||
| func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper { | ||||
| 	keeper := Keeper{ | ||||
| 		storeKey:   key, | ||||
| 		cdc:        cdc, | ||||
| 		coinKeeper: ck, | ||||
| 		codespace:  codespace, | ||||
| 	} | ||||
| 	return keeper | ||||
| } | ||||
| 
 | ||||
| // bunch of business logic ...
 | ||||
| 
 | ||||
| func (keeper Keeper) GetPaychan(paychanID) Paychan { | ||||
| 	// load from DB
 | ||||
| 	// unmarshall
 | ||||
| 	// return
 | ||||
| } | ||||
| 
 | ||||
| func (keeper Keeper) setPaychan(pych Paychan) sdk.Error { | ||||
| 	// marshal
 | ||||
| 	// write to db
 | ||||
| } | ||||
| 
 | ||||
| func (keeer Keeper) CreatePaychan(receiver sdkAddress, amt sdk.Coins) (Paychan, sdk.Error) { | ||||
| 	// subtract coins from sender
 | ||||
| 	// create new Paychan struct (create ID)
 | ||||
| 	// save to db
 | ||||
| 
 | ||||
| 	// validation:
 | ||||
| 	// sender has enough coins
 | ||||
| 	// receiver address exists?
 | ||||
| 	// paychan doesn't exist already
 | ||||
| } | ||||
| 
 | ||||
| func (keeper Keeper) ClosePaychan() sdk.Error { | ||||
| 	// add coins to sender
 | ||||
| 	// add coins to receiver
 | ||||
| 	// delete paychan from db
 | ||||
| 
 | ||||
| 	// validation:
 | ||||
| 	// paychan exists
 | ||||
| 	// output coins are less than paychan balance
 | ||||
| 	// sender and receiver addresses exist?
 | ||||
| } | ||||
| 
 | ||||
| func paychanKey(Paychan) { | ||||
| 	// concat sender and receiver and integer ID
 | ||||
| } | ||||
| 
 | ||||
| // maybe getAllPaychans(sender sdk.address) []Paychan
 | ||||
							
								
								
									
										93
									
								
								internal/x/paychan/keeper_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								internal/x/paychan/keeper_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | ||||
| package paychan | ||||
| 
 | ||||
| import ( | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| // example from x/bank
 | ||||
| func TestKeeper(t *testing.T) { | ||||
| 	// ms, authKey := setupMultiStore()
 | ||||
| 
 | ||||
| 	// cdc := wire.NewCodec()
 | ||||
| 	// auth.RegisterBaseAccount(cdc)
 | ||||
| 
 | ||||
| 	// ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
 | ||||
| 	// accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{})
 | ||||
| 	// coinKeeper := NewKeeper(accountMapper)
 | ||||
| 
 | ||||
| 	// addr := sdk.Address([]byte("addr1"))
 | ||||
| 	// addr2 := sdk.Address([]byte("addr2"))
 | ||||
| 	// addr3 := sdk.Address([]byte("addr3"))
 | ||||
| 	// acc := accountMapper.NewAccountWithAddress(ctx, addr)
 | ||||
| 
 | ||||
| 	// // Test GetCoins/SetCoins
 | ||||
| 	// accountMapper.SetAccount(ctx, acc)
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{}))
 | ||||
| 
 | ||||
| 	// coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}}))
 | ||||
| 
 | ||||
| 	// // Test HasCoins
 | ||||
| 	// assert.True(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}))
 | ||||
| 	// assert.True(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}))
 | ||||
| 	// assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}))
 | ||||
| 	// assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}))
 | ||||
| 
 | ||||
| 	// // Test AddCoins
 | ||||
| 	// coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 25}}))
 | ||||
| 
 | ||||
| 	// coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 15}})
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 15}, {"foocoin", 25}}))
 | ||||
| 
 | ||||
| 	// // Test SubtractCoins
 | ||||
| 	// coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})
 | ||||
| 	// coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}}))
 | ||||
| 
 | ||||
| 	// coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}})
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}}))
 | ||||
| 
 | ||||
| 	// coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 10}})
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 15}}))
 | ||||
| 	// assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 1}}))
 | ||||
| 
 | ||||
| 	// // Test SendCoins
 | ||||
| 	// coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 5}})
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}}))
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}}))
 | ||||
| 
 | ||||
| 	// _, err2 := coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 50}})
 | ||||
| 	// assert.Implements(t, (*sdk.Error)(nil), err2)
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}}))
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}}))
 | ||||
| 
 | ||||
| 	// coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 30}})
 | ||||
| 	// coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"barcoin", 10}, {"foocoin", 5}})
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}}))
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}}))
 | ||||
| 
 | ||||
| 	// // Test InputOutputCoins
 | ||||
| 	// input1 := NewInput(addr2, sdk.Coins{{"foocoin", 2}})
 | ||||
| 	// output1 := NewOutput(addr, sdk.Coins{{"foocoin", 2}})
 | ||||
| 	// coinKeeper.InputOutputCoins(ctx, []Input{input1}, []Output{output1})
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}}))
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}}))
 | ||||
| 
 | ||||
| 	// inputs := []Input{
 | ||||
| 	// 	NewInput(addr, sdk.Coins{{"foocoin", 3}}),
 | ||||
| 	// 	NewInput(addr2, sdk.Coins{{"barcoin", 3}, {"foocoin", 2}}),
 | ||||
| 	// }
 | ||||
| 
 | ||||
| 	// outputs := []Output{
 | ||||
| 	// 	NewOutput(addr, sdk.Coins{{"barcoin", 1}}),
 | ||||
| 	// 	NewOutput(addr3, sdk.Coins{{"barcoin", 2}, {"foocoin", 5}}),
 | ||||
| 	// }
 | ||||
| 	// coinKeeper.InputOutputCoins(ctx, inputs, outputs)
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}}))
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}}))
 | ||||
| 	// assert.True(t, coinKeeper.GetCoins(ctx, addr3).IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}}))
 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										98
									
								
								internal/x/paychan/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								internal/x/paychan/types.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | ||||
| package paychan | ||||
| 
 | ||||
| import ( | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types"           | ||||
| ) | ||||
| 
 | ||||
| ////////
 | ||||
| 
 | ||||
| // probably want to convert this to a general purpose "state"
 | ||||
| struct Paychan { | ||||
| 	balance sdk.Coins | ||||
| 	sender sdk.Address | ||||
| 	receiver sdk.Address | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /////////////
 | ||||
| 
 | ||||
| // Message implement the sdk.Msg interface:
 | ||||
| 
 | ||||
| // type Msg interface {
 | ||||
| 
 | ||||
| // 	// Return the message type.
 | ||||
| // 	// Must be alphanumeric or empty.
 | ||||
| // 	Type() string
 | ||||
| 
 | ||||
| // 	// Get the canonical byte representation of the Msg.
 | ||||
| // 	GetSignBytes() []byte
 | ||||
| 
 | ||||
| // 	// ValidateBasic does a simple validation check that
 | ||||
| // 	// doesn't require access to any other information.
 | ||||
| // 	ValidateBasic() Error
 | ||||
| 
 | ||||
| // 	// Signers returns the addrs of signers that must sign.
 | ||||
| // 	// CONTRACT: All signatures must be present to be valid.
 | ||||
| // 	// CONTRACT: Returns addrs in some deterministic order.
 | ||||
| // 	GetSigners() []Address
 | ||||
| // }
 | ||||
| 
 | ||||
| /////////////// CreatePayChan
 | ||||
| // find a less confusing name
 | ||||
| type CreateMsg struct { | ||||
| 	// maybe just wrap a paychan struct
 | ||||
| 	sender sdk.Address | ||||
| 	receiver sdk.Address | ||||
| 	amount sdk.Balance | ||||
| } | ||||
| 
 | ||||
| func (msg CreatMsg) NewCreateMsg() CreateMsg { | ||||
| 	return CreateMsg{ } | ||||
| } | ||||
| 
 | ||||
| func (msg CreateMsg) Type() string { return "paychan" } | ||||
| 
 | ||||
| func (msg CreateMsg) GetSigners() []sdk.Address { | ||||
| 	// sender
 | ||||
| 	//return []sdk.Address{msg.sender}
 | ||||
| } | ||||
| 
 | ||||
| func (msg CreateMsg) GetSignBytes() []byte { | ||||
| 	 | ||||
| } | ||||
| 
 | ||||
| func (msg CreateMsg) ValidateBasic() sdk.Error { | ||||
| 	// verify msg as much as possible without using external information (such as account balance)
 | ||||
| 	// are all fields present
 | ||||
| 	// are all fields valid
 | ||||
| 	// maybe check if sender and receiver is different
 | ||||
| } | ||||
| 
 | ||||
| /////////////////
 | ||||
| type CloseMsg struct { | ||||
| 	// have to include sender and receiver in msg explicitly (rather than just universal paychanID)
 | ||||
| 	//  this gives ability to verify signatures with no external information
 | ||||
| 	sender sdk.Address | ||||
| 	receiver sdk.Address | ||||
| 	id integer | ||||
| 	receiverAmount sdk.Coins // amount the receiver should get - sender amount implicit with paychan balance
 | ||||
| } | ||||
| 
 | ||||
| func (msg CloseMsg) NewCloseMsg( args... ) CloseMsg { | ||||
| 	return CloseMsg{ args... } | ||||
| } | ||||
| 
 | ||||
| func (msg CloseMsg) Type() string { return "paychan" } | ||||
| 
 | ||||
| func (msg CloseMsg) GetSigners() []sdk.Address { | ||||
| 	// sender and receiver
 | ||||
| } | ||||
| 
 | ||||
| func (msg CloseMsg) GetSignBytes() []byte { | ||||
| 	 | ||||
| } | ||||
| 
 | ||||
| func (msg CloseMsg) ValidateBasic() sdk.Error { | ||||
| 	return msg.IBCPacket.ValidateBasic() | ||||
| } | ||||
| 
 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 rhuairahrighairigh
						rhuairahrighairigh