From 91c664e4bf08540d413f8cdd087d436c2bae2ac6 Mon Sep 17 00:00:00 2001 From: wyjDoraemon <46176410+wyjDoraemon@users.noreply.github.com> Date: Tue, 3 Sep 2019 11:05:46 +0800 Subject: [PATCH] api add createAccount and createKey (#393) * api add createAccount and createKey * skip ci test and reuse data structure * add api ... * fix * fix * fix createAccount alias * fix apinode --- api/accounts.go | 35 +++++++-------- api/hsm.go | 20 +++++---- api/query.go | 8 ++-- api/receivers.go | 6 ++- toolbar/apinode/account.go | 95 +++++++++++++++++++++++++++++++++++++++++ toolbar/apinode/account_test.go | 51 ++++++++++++++++++++++ toolbar/apinode/node.go | 37 ++++++++++++++++ toolbar/apinode/query.go | 72 +++++++++++++++++++++++++++++++ toolbar/apinode/query_test.go | 56 ++++++++++++++++++++++++ 9 files changed, 349 insertions(+), 31 deletions(-) create mode 100644 toolbar/apinode/account.go create mode 100644 toolbar/apinode/account_test.go create mode 100644 toolbar/apinode/query.go create mode 100644 toolbar/apinode/query_test.go diff --git a/api/accounts.go b/api/accounts.go index a0d551ad..b75aa513 100644 --- a/api/accounts.go +++ b/api/accounts.go @@ -15,12 +15,14 @@ import ( "github.com/vapor/protocol/vm/vmutil" ) -// POST /create-account -func (a *API) createAccount(ctx context.Context, ins struct { +type CreateAccountReq struct { RootXPubs []chainkd.XPub `json:"root_xpubs"` Quorum int `json:"quorum"` Alias string `json:"alias"` -}) Response { +} + +// POST /create-account +func (a *API) createAccount(ctx context.Context, ins CreateAccountReq) Response { acc, err := a.wallet.AccountMgr.Create(ins.RootXPubs, ins.Quorum, ins.Alias, signers.BIP0044) if err != nil { return NewErrorResponse(err) @@ -58,10 +60,7 @@ type AccountInfo struct { } // POST /delete-account -func (a *API) deleteAccount(ctx context.Context, filter struct { - AccountID string `json:"account_id"` - AccountAlias string `json:"account_alias"` -}) Response { +func (a *API) deleteAccount(ctx context.Context, filter AccountFilter) Response { accountID := filter.AccountID if filter.AccountAlias != "" { acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias) @@ -114,7 +113,14 @@ func (a *API) validateAddress(ctx context.Context, ins struct { return NewSuccessResponse(resp) } -type addressResp struct { +type AddressReq struct { + AccountID string `json:"account_id"` + AccountAlias string `json:"account_alias"` + From uint `json:"from"` + Count uint `json:"count"` +} + +type AddressResp struct { AccountAlias string `json:"account_alias"` AccountID string `json:"account_id"` Address string `json:"address"` @@ -124,18 +130,13 @@ type addressResp struct { } // SortByIndex implements sort.Interface for addressResp slices -type SortByIndex []addressResp +type SortByIndex []AddressResp func (a SortByIndex) Len() int { return len(a) } func (a SortByIndex) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a SortByIndex) Less(i, j int) bool { return a[i].KeyIndex < a[j].KeyIndex } -func (a *API) listAddresses(ctx context.Context, ins struct { - AccountID string `json:"account_id"` - AccountAlias string `json:"account_alias"` - From uint `json:"from"` - Count uint `json:"count"` -}) Response { +func (a *API) listAddresses(ctx context.Context, ins AddressReq) Response { accountID := ins.AccountID var target *account.Account if ins.AccountAlias != "" { @@ -157,12 +158,12 @@ func (a *API) listAddresses(ctx context.Context, ins struct { return NewErrorResponse(err) } - addresses := []addressResp{} + addresses := []AddressResp{} for _, cp := range cps { if cp.Address == "" || cp.AccountID != target.ID { continue } - addresses = append(addresses, addressResp{ + addresses = append(addresses, AddressResp{ AccountAlias: target.Alias, AccountID: cp.AccountID, Address: cp.Address, diff --git a/api/hsm.go b/api/hsm.go index fb2b4e68..3158c20b 100644 --- a/api/hsm.go +++ b/api/hsm.go @@ -9,19 +9,21 @@ import ( "github.com/vapor/crypto/ed25519/chainkd" ) -type createKeyResp struct { +type CreateKeyReq struct { + Alias string `json:"alias"` + Password string `json:"password"` + Mnemonic string `json:"mnemonic"` + Language string `json:"language"` +} + +type CreateKeyResp struct { Alias string `json:"alias"` XPub chainkd.XPub `json:"xpub"` File string `json:"file"` Mnemonic string `json:"mnemonic,omitempty"` } -func (a *API) pseudohsmCreateKey(ctx context.Context, in struct { - Alias string `json:"alias"` - Password string `json:"password"` - Mnemonic string `json:"mnemonic"` - Language string `json:"language"` -}) Response { +func (a *API) pseudohsmCreateKey(ctx context.Context, in CreateKeyReq) Response { if in.Language == "" { in.Language = "en" } @@ -30,13 +32,13 @@ func (a *API) pseudohsmCreateKey(ctx context.Context, in struct { if err != nil { return NewErrorResponse(err) } - return NewSuccessResponse(&createKeyResp{Alias: xpub.Alias, XPub: xpub.XPub, File: xpub.File}) + return NewSuccessResponse(&CreateKeyResp{Alias: xpub.Alias, XPub: xpub.XPub, File: xpub.File}) } xpub, mnemonic, err := a.wallet.Hsm.XCreate(in.Alias, in.Password, in.Language) if err != nil { return NewErrorResponse(err) } - return NewSuccessResponse(&createKeyResp{Alias: xpub.Alias, XPub: xpub.XPub, File: xpub.File, Mnemonic: *mnemonic}) + return NewSuccessResponse(&CreateKeyResp{Alias: xpub.Alias, XPub: xpub.XPub, File: xpub.File, Mnemonic: *mnemonic}) } func (a *API) pseudohsmUpdateKeyAlias(ctx context.Context, in struct { diff --git a/api/query.go b/api/query.go index d65acaac..fba82b85 100644 --- a/api/query.go +++ b/api/query.go @@ -283,8 +283,7 @@ func (a *API) decodeRawTransaction(ctx context.Context, ins struct { return NewSuccessResponse(tx) } -// POST /list-unspent-outputs -func (a *API) listUnspentOutputs(ctx context.Context, filter struct { +type ListUtxosReq struct { AccountID string `json:"account_id"` AccountAlias string `json:"account_alias"` ID string `json:"id"` @@ -292,7 +291,10 @@ func (a *API) listUnspentOutputs(ctx context.Context, filter struct { SmartContract bool `json:"smart_contract"` From uint `json:"from"` Count uint `json:"count"` -}) Response { +} + +// POST /list-unspent-outputs +func (a *API) listUnspentOutputs(ctx context.Context, filter ListUtxosReq) Response { accountID := filter.AccountID if filter.AccountAlias != "" { acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias) diff --git a/api/receivers.go b/api/receivers.go index 02d80b98..28564da8 100644 --- a/api/receivers.go +++ b/api/receivers.go @@ -6,10 +6,12 @@ import ( "github.com/vapor/blockchain/txbuilder" ) -func (a *API) createAccountReceiver(ctx context.Context, ins struct { +type AccountFilter struct { AccountID string `json:"account_id"` AccountAlias string `json:"account_alias"` -}) Response { +} + +func (a *API) createAccountReceiver(ctx context.Context, ins AccountFilter) Response { accountID := ins.AccountID if ins.AccountAlias != "" { account, err := a.wallet.AccountMgr.FindByAlias(ins.AccountAlias) diff --git a/toolbar/apinode/account.go b/toolbar/apinode/account.go new file mode 100644 index 00000000..818f7eee --- /dev/null +++ b/toolbar/apinode/account.go @@ -0,0 +1,95 @@ +package apinode + +import ( + "encoding/json" + + "github.com/vapor/api" + "github.com/vapor/blockchain/pseudohsm" + "github.com/vapor/blockchain/query" + "github.com/vapor/blockchain/txbuilder" + "github.com/vapor/crypto/ed25519/chainkd" + "github.com/vapor/errors" +) + +func (n *Node) CreateKey(alias, password string) (*api.CreateKeyResp, error) { + url := "/create-key" + payload, err := json.Marshal(api.CreateKeyReq{Alias: alias, Password: password}) + if err != nil { + return nil, errors.Wrap(err, "json marshal") + } + + res := &api.CreateKeyResp{} + return res, n.request(url, payload, res) +} + +func (n *Node) ListKeys() (*[]pseudohsm.XPub, error) { + url := "/list-keys" + res := &[]pseudohsm.XPub{} + return res, n.request(url, nil, res) +} + +//默认创建单签账户 +func (n *Node) CreateAccount(keyAlias, accountAlias string) (*query.AnnotatedAccount, error) { + xPub, err := n.ListKeys() + if err != nil { + return nil, err + } + + var rootXPub *chainkd.XPub + for _, x := range *xPub { + if x.Alias == keyAlias { + rootXPub = &x.XPub + break + } + } + + if rootXPub == nil { + return nil, errors.New("keyAlias not found!") + } + + return n.postCreateAccount(accountAlias, 1, []chainkd.XPub{*rootXPub}) +} + +//多签账户 +func (n *Node) CreateMultiSignAccount(alias string, quorum int, rootXPubs []chainkd.XPub) (*query.AnnotatedAccount, error) { + return n.postCreateAccount(alias, quorum, rootXPubs) +} + +func (n *Node) postCreateAccount(alias string, quorum int, rootXPubs []chainkd.XPub) (*query.AnnotatedAccount, error) { + url := "/create-account" + payload, err := json.Marshal(api.CreateAccountReq{ + Alias: alias, + Quorum: quorum, + RootXPubs: rootXPubs, + }) + if err != nil { + return nil, errors.Wrap(err, "json marshal") + } + + res := &query.AnnotatedAccount{} + return res, n.request(url, payload, res) +} + +func (n *Node) ListAccounts() (*[]query.AnnotatedAccount, error) { + url := "/list-accounts" + payload, err := json.Marshal(struct{}{}) + if err != nil { + return nil, errors.Wrap(err, "json marshal") + } + + res := &[]query.AnnotatedAccount{} + return res, n.request(url, payload, res) +} + +func (n *Node) CreateAccountReceiver(alias string) (*txbuilder.Receiver, error) { + url := "/create-account-receiver" + payload, err := json.Marshal(api.AccountFilter{ + AccountAlias: alias, + }) + if err != nil { + return nil, errors.Wrap(err, "json marshal") + } + + res := &txbuilder.Receiver{} + return res, n.request(url, payload, res) +} diff --git a/toolbar/apinode/account_test.go b/toolbar/apinode/account_test.go new file mode 100644 index 00000000..df52118b --- /dev/null +++ b/toolbar/apinode/account_test.go @@ -0,0 +1,51 @@ +//+build apinode + +package apinode + +import ( + "fmt" + "testing" +) + +var n *Node + +func TestMain(m *testing.M) { + n = NewNode("http://127.0.0.1:9889") + m.Run() +} + +func TestNodeCreateKey(t *testing.T) { + res, err := n.CreateKey("test10", "123456") + if err != nil { + t.Fatal(err) + } + + fmt.Println(res) +} + +func TestNodeCreateAccount(t *testing.T) { + res, err := n.CreateAccount("test10", "test11") + if err != nil { + t.Fatal(err) + } + + fmt.Println(res) +} + +func TestNodeCreateAccountReceiver(t *testing.T) { + res, err := n.CreateAccountReceiver("test10") + if err != nil { + t.Fatal(err) + } + + fmt.Println(res) +} + +func TestNodeListAccounts(t *testing.T) { + res, err := n.ListAccounts() + if err != nil { + t.Fatal(err) + } + + fmt.Println(res) +} diff --git a/toolbar/apinode/node.go b/toolbar/apinode/node.go index a0567232..d15c068b 100644 --- a/toolbar/apinode/node.go +++ b/toolbar/apinode/node.go @@ -4,6 +4,7 @@ import ( "encoding/json" "github.com/vapor/errors" + "github.com/vapor/netsync/peers" "github.com/vapor/toolbar/common" ) @@ -33,5 +34,41 @@ func (n *Node) request(path string, payload []byte, respData interface{}) error return errors.New(resp.ErrDetail) } + if resp.Data == nil { + return nil + } + return json.Unmarshal(resp.Data, respData) } + +func (n *Node) DisconnectPeer(peerID string) error { + url := "/disconnect-peer" + payload, err := json.Marshal(struct { + PeerID string `json:"peer_id"` + }{ + PeerID: peerID, + }) + if err != nil { + return err + } + + return n.request(url, payload, nil) + +} + +func (n *Node) ConnectPeer(ip string, port uint16) (*peers.Peer, error) { + url := "/connect-peer" + payload, err := json.Marshal(struct { + Ip string `json:"ip"` + Port uint16 `json:"port"` + }{ + Ip: ip, + Port: port, + }) + if err != nil { + return nil, errors.Wrap(err, "json marshal") + } + + res := &peers.Peer{} + return res, n.request(url, payload, res) +} diff --git a/toolbar/apinode/query.go b/toolbar/apinode/query.go new file mode 100644 index 00000000..9486a09a --- /dev/null +++ b/toolbar/apinode/query.go @@ -0,0 +1,72 @@ +package apinode + +import ( + "encoding/json" + + "github.com/vapor/api" + "github.com/vapor/blockchain/query" + "github.com/vapor/errors" + "github.com/vapor/netsync/peers" + "github.com/vapor/wallet" +) + +func (n *Node) ListAddresses(accountAlias string, from, count uint) (*[]api.AddressResp, error) { + url := "/list-addresses" + payload, err := json.Marshal(api.AddressReq{ + AccountAlias: accountAlias, + From: from, + Count: count, + }) + if err != nil { + return nil, errors.Wrap(err, "json marshal") + } + + res := &[]api.AddressResp{} + return res, n.request(url, payload, res) +} + +func (n *Node) ListBalances(accountAlias string) (*[]wallet.AccountBalance, error) { + url := "/list-balances" + payload, err := json.Marshal(api.AccountFilter{ + AccountAlias: accountAlias, + }) + if err != nil { + return nil, errors.Wrap(err, "json marshal") + } + + res := &[]wallet.AccountBalance{} + return res, n.request(url, payload, res) +} + +func (n *Node) ListUtxos(accountAlias string,from, count uint) (*[]query.AnnotatedUTXO, error) { + url := "/list-unspent-outputs" + payload, err := json.Marshal(api.ListUtxosReq{ + AccountAlias: accountAlias, + From: from, + Count: count, + }) + if err != nil { + return nil, errors.Wrap(err, "json marshal") + } + + res := &[]query.AnnotatedUTXO{} + return res, n.request(url, payload, res) +} + +func (n *Node) WalletInfo() (*api.WalletInfo, error) { + url := "/wallet-info" + res := &api.WalletInfo{} + return res, n.request(url, nil, res) +} + +func (n *Node) NetInfo() (*api.NetInfo, error) { + url := "/net-info" + res := &api.NetInfo{} + return res, n.request(url, nil, res) +} + +func (n *Node) ListPeers() (*[]*peers.PeerInfo, error) { + url := "/list-peers" + res := &[]*peers.PeerInfo{} + return res, n.request(url, nil, res) +} diff --git a/toolbar/apinode/query_test.go b/toolbar/apinode/query_test.go new file mode 100644 index 00000000..bf210316 --- /dev/null +++ b/toolbar/apinode/query_test.go @@ -0,0 +1,56 @@ +//+build apinode + +package apinode + +import ( + "fmt" + "testing" +) + +func TestNodeListAddresses(t *testing.T) { + res, err := n.ListAddresses("test10", 0, 10) + if err != nil { + t.Fatal(err) + } + fmt.Println(res) +} + +func TestNodeListBalances(t *testing.T) { + res, err := n.ListBalances("test10") + if err != nil { + t.Fatal(err) + } + fmt.Println(res) +} + +func TestNodeListUtxos(t *testing.T) { + res, err := n.ListUtxos("test10", 0, 10) + if err != nil { + t.Fatal(err) + } + fmt.Println(res) +} + +func TestNodeWalletInfo(t *testing.T) { + res, err := n.WalletInfo() + if err != nil { + t.Fatal(err) + } + fmt.Println(res) +} + +func TestNodeNetInfo(t *testing.T) { + res, err := n.NetInfo() + if err != nil { + t.Fatal(err) + } + fmt.Println(res) +} + +func TestNodeListPeers(t *testing.T) { + res, err := n.ListPeers() + if err != nil { + t.Fatal(err) + } + fmt.Println(res) +} -- 2.11.0