diff --git a/bigint/bigint.go b/bigint/bigint.go new file mode 100644 index 000000000..3692d0cb9 --- /dev/null +++ b/bigint/bigint.go @@ -0,0 +1,44 @@ +// Copyright 2021 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bigint + +import ( + "encoding/json" + "fmt" + "math/big" +) + +type BigInt struct { + *big.Int +} + +func (i *BigInt) MarshalJSON() ([]byte, error) { + if i.Int == nil { + return []byte("null"), nil + } + + return []byte(fmt.Sprintf(`"%s"`, i.String())), nil +} + +func (i *BigInt) UnmarshalJSON(b []byte) error { + var val string + err := json.Unmarshal(b, &val) + if err != nil { + return err + } + + if i.Int == nil { + i.Int = new(big.Int) + } + + i.SetString(val, 10) + + return nil +} + +//Wrap wraps big.Int pointer into BigInt struct. +func Wrap(i *big.Int) *BigInt { + return &BigInt{Int: i} +} diff --git a/core/commands/cheque/cheque.go b/core/commands/cheque/cheque.go index d056ed91a..c3d1ab1e9 100644 --- a/core/commands/cheque/cheque.go +++ b/core/commands/cheque/cheque.go @@ -9,8 +9,13 @@ import ( cmds "github.com/TRON-US/go-btfs-cmds" "github.com/TRON-US/go-btfs/chain" "github.com/TRON-US/go-btfs/settlement/swap/chequebook" + "golang.org/x/net/context" ) +type StorePriceRet struct { + Price string `json:"price"` +} + type CashChequeRet struct { TxHash string } @@ -42,10 +47,29 @@ Chequebook services include issue cheque to peer, receive cheque and store opera "cash": CashChequeCmd, "list": ListChequeCmd, "history": ChequeHistoryCmd, + "price": StorePriceCmd, //"info": ChequeInfo, }, } +var StorePriceCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Get btfs store price.", + }, + RunTimeout: 5 * time.Minute, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + price, err := chain.SettleObject.OracleService.GetPrice(context.Background()) + if err != nil { + return err + } + + return cmds.EmitOnce(res, &StorePriceRet{ + Price: price.String(), + }) + }, + Type: StorePriceRet{}, +} + var ChequeHistoryCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Display the cheque records.", diff --git a/core/commands/chequebook/chequebook.go b/core/commands/chequebook/chequebook.go new file mode 100644 index 000000000..08cfc3c72 --- /dev/null +++ b/core/commands/chequebook/chequebook.go @@ -0,0 +1,19 @@ +package chequebook + +import ( + cmds "github.com/TRON-US/go-btfs-cmds" +) + +var ChequeBookCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Interact with chequebook services on BTFS.", + ShortDescription: ` +Chequebook services include balance, address, withdraw, deposit operations.`, + }, + Subcommands: map[string]*cmds.Command{ + "balance": ChequeBookBalanceCmd, + "address": ChequeBookAddrCmd, + "withdraw": ChequeBookWithdrawCmd, + "deposit": ChequeBookDepositCmd, + }, +} diff --git a/core/commands/chequebook/chequebook_address.go b/core/commands/chequebook/chequebook_address.go new file mode 100644 index 000000000..e8a96c267 --- /dev/null +++ b/core/commands/chequebook/chequebook_address.go @@ -0,0 +1,27 @@ +package chequebook + +import ( + "time" + + cmds "github.com/TRON-US/go-btfs-cmds" + "github.com/TRON-US/go-btfs/chain" +) + +type ChequeBookAddrCmdRet struct { + Addr string `json:"addr"` +} + +var ChequeBookAddrCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Get chequebook address.", + }, + RunTimeout: 5 * time.Minute, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + addr := chain.SettleObject.ChequebookService.Address() + + return cmds.EmitOnce(res, &ChequeBookAddrCmdRet{ + Addr: addr.String(), + }) + }, + Type: &ChequeBookAddrCmdRet{}, +} diff --git a/core/commands/chequebook/chequebook_balance.go b/core/commands/chequebook/chequebook_balance.go new file mode 100644 index 000000000..b9e1f83c5 --- /dev/null +++ b/core/commands/chequebook/chequebook_balance.go @@ -0,0 +1,31 @@ +package chequebook + +import ( + "time" + + cmds "github.com/TRON-US/go-btfs-cmds" + "github.com/TRON-US/go-btfs/chain" + "golang.org/x/net/context" +) + +type ChequeBookBalanceCmdRet struct { + Balance string `json:"balance"` +} + +var ChequeBookBalanceCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Get chequebook balance.", + }, + RunTimeout: 5 * time.Minute, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + balance, err := chain.SettleObject.ChequebookService.AvailableBalance(context.Background()) + if err != nil { + return err + } + + return cmds.EmitOnce(res, &ChequeBookBalanceCmdRet{ + Balance: balance.String(), + }) + }, + Type: &ChequeBookBalanceCmdRet{}, +} diff --git a/core/commands/chequebook/chequebook_deposit.go b/core/commands/chequebook/chequebook_deposit.go new file mode 100644 index 000000000..8c02a19b7 --- /dev/null +++ b/core/commands/chequebook/chequebook_deposit.go @@ -0,0 +1,40 @@ +package chequebook + +import ( + "fmt" + "math/big" + "time" + + cmds "github.com/TRON-US/go-btfs-cmds" + "github.com/TRON-US/go-btfs/chain" + "golang.org/x/net/context" +) + +type ChequeBookDepositCmdRet struct { + Hash string `json:"hash"` +} + +var ChequeBookDepositCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Deposit from beneficiary to chequebook contract account.", + }, + Arguments: []cmds.Argument{ + cmds.StringArg("amount", true, false, "deposit amount."), + }, + RunTimeout: 5 * time.Minute, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + amount, ok := new(big.Int).SetString(req.Arguments[0], 10) + if !ok { + return fmt.Errorf("amount:%s cannot be parsed", req.Arguments[0]) + } + hash, err := chain.SettleObject.ChequebookService.Deposit(context.Background(), amount) + if err != nil { + return err + } + + return cmds.EmitOnce(res, &ChequeBookDepositCmdRet{ + Hash: hash.String(), + }) + }, + Type: &ChequeBookDepositCmdRet{}, +} diff --git a/core/commands/chequebook/chequebook_withdraw.go b/core/commands/chequebook/chequebook_withdraw.go new file mode 100644 index 000000000..d202b880b --- /dev/null +++ b/core/commands/chequebook/chequebook_withdraw.go @@ -0,0 +1,40 @@ +package chequebook + +import ( + "fmt" + "math/big" + "time" + + cmds "github.com/TRON-US/go-btfs-cmds" + "github.com/TRON-US/go-btfs/chain" + "golang.org/x/net/context" +) + +type ChequeBookWithdrawCmdRet struct { + Hash string `json:"hash"` +} + +var ChequeBookWithdrawCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Withdraw from chequebook contract account to beneficiary.", + }, + Arguments: []cmds.Argument{ + cmds.StringArg("amount", true, false, "withdraw amount."), + }, + RunTimeout: 5 * time.Minute, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + amount, ok := new(big.Int).SetString(req.Arguments[0], 10) + if !ok { + return fmt.Errorf("amount:%s cannot be parsed", req.Arguments[0]) + } + hash, err := chain.SettleObject.ChequebookService.Withdraw(context.Background(), amount) + if err != nil { + return err + } + + return cmds.EmitOnce(res, &ChequeBookWithdrawCmdRet{ + Hash: hash.String(), + }) + }, + Type: &ChequeBookWithdrawCmdRet{}, +} diff --git a/core/commands/root.go b/core/commands/root.go index b5a270d6d..8cef73e57 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -4,10 +4,12 @@ import ( "errors" "github.com/TRON-US/go-btfs/core/commands/cheque" + "github.com/TRON-US/go-btfs/core/commands/chequebook" cmdenv "github.com/TRON-US/go-btfs/core/commands/cmdenv" dag "github.com/TRON-US/go-btfs/core/commands/dag" name "github.com/TRON-US/go-btfs/core/commands/name" ocmd "github.com/TRON-US/go-btfs/core/commands/object" + settlement "github.com/TRON-US/go-btfs/core/commands/settlements" "github.com/TRON-US/go-btfs/core/commands/storage" "github.com/TRON-US/go-btfs/core/commands/storage/challenge" "github.com/TRON-US/go-btfs/core/commands/storage/upload/upload" @@ -120,51 +122,53 @@ The CLI will exit with one of the following values: var CommandsDaemonCmd = CommandsCmd(Root) var rootSubcommands = map[string]*cmds.Command{ - "add": AddCmd, - "bitswap": BitswapCmd, - "block": BlockCmd, - "cat": CatCmd, - "commands": CommandsDaemonCmd, - "files": FilesCmd, - "filestore": FileStoreCmd, - "get": GetCmd, - "pubsub": PubsubCmd, - "repo": RepoCmd, - "stats": StatsCmd, - "bootstrap": BootstrapCmd, - "test": TestCmd, - "config": ConfigCmd, - "dag": dag.DagCmd, - "dht": DhtCmd, - "diag": DiagCmd, - "dns": DNSCmd, - "id": IDCmd, - "key": KeyCmd, - "log": LogCmd, - "ls": LsCmd, - "mount": MountCmd, - "name": name.NameCmd, - "object": ocmd.ObjectCmd, - "pin": PinCmd, - "ping": PingCmd, - "p2p": P2PCmd, - "refs": RefsCmd, - "resolve": ResolveCmd, - "swarm": SwarmCmd, - "tar": TarCmd, - "file": unixfs.UnixFSCmd, - "urlstore": urlStoreCmd, - "version": VersionCmd, - "shutdown": daemonShutdownCmd, - "restart": restartCmd, - "cid": CidCmd, - "rm": RmCmd, - "storage": storage.StorageCmd, - "metadata": MetadataCmd, - "guard": GuardCmd, - "wallet": WalletCmd, - "tron": TronCmd, - "cheque": cheque.ChequeCmd, + "add": AddCmd, + "bitswap": BitswapCmd, + "block": BlockCmd, + "cat": CatCmd, + "commands": CommandsDaemonCmd, + "files": FilesCmd, + "filestore": FileStoreCmd, + "get": GetCmd, + "pubsub": PubsubCmd, + "repo": RepoCmd, + "stats": StatsCmd, + "bootstrap": BootstrapCmd, + "test": TestCmd, + "config": ConfigCmd, + "dag": dag.DagCmd, + "dht": DhtCmd, + "diag": DiagCmd, + "dns": DNSCmd, + "id": IDCmd, + "key": KeyCmd, + "log": LogCmd, + "ls": LsCmd, + "mount": MountCmd, + "name": name.NameCmd, + "object": ocmd.ObjectCmd, + "pin": PinCmd, + "ping": PingCmd, + "p2p": P2PCmd, + "refs": RefsCmd, + "resolve": ResolveCmd, + "swarm": SwarmCmd, + "tar": TarCmd, + "file": unixfs.UnixFSCmd, + "urlstore": urlStoreCmd, + "version": VersionCmd, + "shutdown": daemonShutdownCmd, + "restart": restartCmd, + "cid": CidCmd, + "rm": RmCmd, + "storage": storage.StorageCmd, + "metadata": MetadataCmd, + "guard": GuardCmd, + "wallet": WalletCmd, + "tron": TronCmd, + "cheque": cheque.ChequeCmd, + "chequebook": chequebook.ChequeBookCmd, + "settlement": settlement.SettlementCmd, //"update": ExternalBinary(), } diff --git a/core/commands/settlements/list.go b/core/commands/settlements/list.go new file mode 100644 index 000000000..e7efd6a7e --- /dev/null +++ b/core/commands/settlements/list.go @@ -0,0 +1,83 @@ +package settlement + +import ( + "math/big" + "time" + + cmds "github.com/TRON-US/go-btfs-cmds" + "github.com/TRON-US/go-btfs/bigint" + "github.com/TRON-US/go-btfs/chain" +) + +type settlementResponse struct { + Peer string `json:"peer"` + SettlementReceived *bigint.BigInt `json:"received"` + SettlementSent *bigint.BigInt `json:"sent"` +} + +type settlementsResponse struct { + TotalSettlementReceived *bigint.BigInt `json:"totalReceived"` + TotalSettlementSent *bigint.BigInt `json:"totalSent"` + Settlements []settlementResponse `json:"settlements"` +} + +var ListSettlementCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "list all settlements.", + }, + RunTimeout: 5 * time.Minute, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + settlementsSent, err := chain.SettleObject.SwapService.SettlementsSent() + if err != nil { + return err + } + settlementsReceived, err := chain.SettleObject.SwapService.SettlementsReceived() + if err != nil { + return err + } + + totalReceived := big.NewInt(0) + totalSent := big.NewInt(0) + + settlementResponses := make(map[string]settlementResponse) + + for a, b := range settlementsSent { + settlementResponses[a] = settlementResponse{ + Peer: a, + SettlementSent: bigint.Wrap(b), + SettlementReceived: bigint.Wrap(big.NewInt(0)), + } + totalSent.Add(b, totalSent) + } + + for a, b := range settlementsReceived { + if _, ok := settlementResponses[a]; ok { + t := settlementResponses[a] + t.SettlementReceived = bigint.Wrap(b) + settlementResponses[a] = t + } else { + settlementResponses[a] = settlementResponse{ + Peer: a, + SettlementSent: bigint.Wrap(big.NewInt(0)), + SettlementReceived: bigint.Wrap(b), + } + } + totalReceived.Add(b, totalReceived) + } + settlementResponsesArray := make([]settlementResponse, len(settlementResponses)) + i := 0 + for k := range settlementResponses { + settlementResponsesArray[i] = settlementResponses[k] + i++ + } + + rsp := settlementsResponse{ + TotalSettlementReceived: bigint.Wrap(totalReceived), + TotalSettlementSent: bigint.Wrap(totalSent), + Settlements: settlementResponsesArray, + } + + return cmds.EmitOnce(res, &rsp) + }, + Type: &settlementsResponse{}, +} diff --git a/core/commands/settlements/peer.go b/core/commands/settlements/peer.go new file mode 100644 index 000000000..f94505d86 --- /dev/null +++ b/core/commands/settlements/peer.go @@ -0,0 +1,65 @@ +package settlement + +import ( + "errors" + "fmt" + "math/big" + "time" + + cmds "github.com/TRON-US/go-btfs-cmds" + "github.com/TRON-US/go-btfs/bigint" + "github.com/TRON-US/go-btfs/chain" + "github.com/TRON-US/go-btfs/settlement" +) + +var PeerSettlementCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Get chequebook balance.", + }, + RunTimeout: 5 * time.Minute, + Arguments: []cmds.Argument{ + cmds.StringArg("peer-id", true, false, "Peer id."), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + peerID := req.Arguments[0] + peerexists := false + + received, err := chain.SettleObject.SwapService.TotalReceived(peerID) + if err != nil { + if !errors.Is(err, settlement.ErrPeerNoSettlements) { + return err + } else { + received = big.NewInt(0) + } + } + + if err == nil { + peerexists = true + } + + sent, err := chain.SettleObject.SwapService.TotalSent(peerID) + if err != nil { + if !errors.Is(err, settlement.ErrPeerNoSettlements) { + return err + } else { + sent = big.NewInt(0) + } + } + + if err == nil { + peerexists = true + } + + if !peerexists { + return fmt.Errorf("can not get settlements for peer:%s", peerID) + } + + rsp := settlementResponse{ + Peer: peerID, + SettlementReceived: bigint.Wrap(received), + SettlementSent: bigint.Wrap(sent), + } + return cmds.EmitOnce(res, &rsp) + }, + Type: &settlementResponse{}, +} diff --git a/core/commands/settlements/settlement.go b/core/commands/settlements/settlement.go new file mode 100644 index 000000000..1cc65bb84 --- /dev/null +++ b/core/commands/settlements/settlement.go @@ -0,0 +1,15 @@ +package settlement + +import ( + cmds "github.com/TRON-US/go-btfs-cmds" +) + +var SettlementCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Interact with chequebook services on BTFS.", + }, + Subcommands: map[string]*cmds.Command{ + "list": ListSettlementCmd, + "peer": PeerSettlementCmd, + }, +}