From 08d72a9245ce6f1e11f84a6b59d66cb083bea9f9 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Tue, 9 Jun 2015 10:59:44 +0200 Subject: [PATCH] added personal API --- rpc/api/api.go | 31 +++++++--- rpc/api/personal.go | 118 +++++++++++++++++++++++++++++++++++++++ rpc/api/personal_args.go | 81 +++++++++++++++++++++++++++ rpc/api/personal_js.go | 34 +++++++++++ rpc/api/utils.go | 4 ++ 5 files changed, 259 insertions(+), 9 deletions(-) create mode 100644 rpc/api/personal.go create mode 100644 rpc/api/personal_args.go create mode 100644 rpc/api/personal_js.go diff --git a/rpc/api/api.go b/rpc/api/api.go index 067a4d4e86..d2c548ed13 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -1,17 +1,30 @@ package api -import "github.com/ethereum/go-ethereum/rpc/shared" +import ( + "strings" + + "github.com/ethereum/go-ethereum/rpc/shared" +) const ( - // List with all API's which are offered over the IPC interface by default - DefaultIpcApis = "debug,eth,miner,net,web3" + EthApiName = "eth" + DebugApiName = "debug" + MergedApiName = "merged" + MinerApiName = "miner" + NetApiName = "net" + PersonalApiName = "personal" + Web3ApiName = "web3" +) - EthApiName = "eth" - DebugApiName = "debug" - MergedApiName = "merged" - MinerApiName = "miner" - NetApiName = "net" - Web3ApiName = "web3" +var ( + // List with all API's which are offered over the IPC interface by default + DefaultIpcApis = strings.Join([]string{ + EthApiName, + DebugApiName, + MinerApiName, + NetApiName, + PersonalApiName, + }, ",") ) // Ethereum RPC API interface diff --git a/rpc/api/personal.go b/rpc/api/personal.go new file mode 100644 index 0000000000..d00363627a --- /dev/null +++ b/rpc/api/personal.go @@ -0,0 +1,118 @@ +package api + +import ( + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/ethereum/go-ethereum/xeth" +) + +var ( + // mapping between methods and handlers + personalMapping = map[string]personalhandler{ + "personal_listAccounts": (*personal).ListAccounts, + "personal_newAccount": (*personal).NewAccount, + "personal_deleteAccount": (*personal).DeleteAccount, + "personal_unlockAccount": (*personal).UnlockAccount, + } +) + +// net callback handler +type personalhandler func(*personal, *shared.Request) (interface{}, error) + +// net api provider +type personal struct { + xeth *xeth.XEth + ethereum *eth.Ethereum + methods map[string]personalhandler + codec codec.ApiCoder +} + +// create a new net api instance +func NewPersonal(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *personal { + return &personal{ + xeth: xeth, + ethereum: eth, + methods: personalMapping, + codec: coder.New(nil), + } +} + +// collection with supported methods +func (self *personal) Methods() []string { + methods := make([]string, len(self.methods)) + i := 0 + for k := range self.methods { + methods[i] = k + i++ + } + return methods +} + +// Execute given request +func (self *personal) Execute(req *shared.Request) (interface{}, error) { + if callback, ok := self.methods[req.Method]; ok { + return callback(self, req) + } + + return nil, shared.NewNotImplementedError(req.Method) +} + +func (self *personal) Name() string { + return PersonalApiName +} + +func (self *personal) ListAccounts(req *shared.Request) (interface{}, error) { + return self.xeth.Accounts(), nil +} + +func (self *personal) NewAccount(req *shared.Request) (interface{}, error) { + args := new(NewAccountArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + am := self.ethereum.AccountManager() + acc, err := am.NewAccount(args.Passphrase) + return acc.Address.Hex(), err +} + +func (self *personal) DeleteAccount(req *shared.Request) (interface{}, error) { + args := new(DeleteAccountArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + addr := common.HexToAddress(args.Address) + am := self.ethereum.AccountManager() + if err := am.DeleteAccount(addr, args.Passphrase); err == nil { + return true, nil + } else { + return false, err + } +} + +func (self *personal) UnlockAccount(req *shared.Request) (interface{}, error) { + args := new(UnlockAccountArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + var err error + am := self.ethereum.AccountManager() + addr := common.HexToAddress(args.Address) + + if args.Duration == -1 { + err = am.Unlock(addr, args.Passphrase) + } else { + err = am.TimedUnlock(addr, args.Passphrase, time.Duration(args.Duration)*time.Second) + } + + if err == nil { + return true, nil + } + return false, err +} diff --git a/rpc/api/personal_args.go b/rpc/api/personal_args.go new file mode 100644 index 0000000000..b41fc06e79 --- /dev/null +++ b/rpc/api/personal_args.go @@ -0,0 +1,81 @@ +package api + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/rpc/shared" +) + +type NewAccountArgs struct { + Passphrase string +} + +func (args *NewAccountArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + passhrase, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("passhrase", "not a string") + } + args.Passphrase = passhrase + + return nil +} + +type DeleteAccountArgs struct { + Address string + Passphrase string +} + +func (args *DeleteAccountArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + addr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addr + + passhrase, ok := obj[1].(string) + if !ok { + return shared.NewInvalidTypeError("passhrase", "not a string") + } + args.Passphrase = passhrase + + return nil +} + +type UnlockAccountArgs struct { + Address string + Passphrase string + Duration int +} + +func (args *UnlockAccountArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + args.Duration = -1 + + addrstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addrstr + + passphrasestr, ok := obj[1].(string) + if !ok { + return shared.NewInvalidTypeError("passphrase", "not a string") + } + args.Passphrase = passphrasestr + + return nil +} diff --git a/rpc/api/personal_js.go b/rpc/api/personal_js.go new file mode 100644 index 0000000000..7fd9a2deae --- /dev/null +++ b/rpc/api/personal_js.go @@ -0,0 +1,34 @@ +package api + +const Personal_JS = ` +web3.extend({ + property: 'personal', + methods: + [ + new web3.extend.Method({ + name: 'listAccounts', + call: 'personal_listAccounts', + params: 0, + inputFormatter: [], + outputFormatter: function(obj) { return obj; } + }), + new web3.extend.Method({ + name: 'newAccount', + call: 'personal_newAccount', + params: 1, + inputFormatter: [web3.extend.formatters.formatInputString], + outputFormatter: web3.extend.formatters.formatOutputString + }), + new web3.extend.Method({ + name: 'unlockAccount', + call: 'personal_unlockAccount', + params: 3, + inputFormatter: [web3.extend.formatters.formatInputString,web3.extend.formatters.formatInputString,web3.extend.formatters.formatInputInt], + outputFormatter: web3.extend.formatters.formatOutputBool + }) + ], + properties: + [ + ] +}); +` diff --git a/rpc/api/utils.go b/rpc/api/utils.go index 6e6d5c7b05..eae23d3510 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -29,6 +29,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. apis[i] = NewMinerApi(eth, codec) case NetApiName: apis[i] = NewNetApi(xeth, eth, codec) + case PersonalApiName: + apis[i] = NewPersonal(xeth, eth, codec) case Web3ApiName: apis[i] = NewWeb3(xeth, codec) default: @@ -47,6 +49,8 @@ func Javascript(name string) string { return Miner_JS case NetApiName: return Net_JS + case PersonalApiName: + return Personal_JS } return ""