mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-24 23:35:19 +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