From 0031f388ac1f6f4a23c5c75e5eeb4a007f0b2f31 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 29 Jan 2015 20:39:26 +0100 Subject: [PATCH] More dapp samples * Info DApp, coin DApp * Additional rpc methods --- cmd/mist/assets/examples/coin.html | 89 ++++++++++++++++++++++++ cmd/mist/assets/examples/info.html | 72 ++++++++++++++++++++ rpc/args.go | 43 ++++++++++-- rpc/message.go | 62 ++++++++++++++++- rpc/packages.go | 105 +++++++++++++++++++++-------- xeth/types.go | 19 ++++-- xeth/world.go | 2 +- 7 files changed, 351 insertions(+), 41 deletions(-) create mode 100644 cmd/mist/assets/examples/coin.html create mode 100644 cmd/mist/assets/examples/info.html diff --git a/cmd/mist/assets/examples/coin.html b/cmd/mist/assets/examples/coin.html new file mode 100644 index 000000000..297d7e042 --- /dev/null +++ b/cmd/mist/assets/examples/coin.html @@ -0,0 +1,89 @@ + + + + + + + + + +

JevCoin

+
+ Balance + +
+ +
+ Amount: + + + +
+ + +
+ + + + + + + diff --git a/cmd/mist/assets/examples/info.html b/cmd/mist/assets/examples/info.html new file mode 100644 index 000000000..c4df8ea64 --- /dev/null +++ b/cmd/mist/assets/examples/info.html @@ -0,0 +1,72 @@ + + + + + + + + + +

Info

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Block number
Peer count
Default block
Accounts
Gas price
Mining
Listening
Coinbase
+ + + + + + diff --git a/rpc/args.go b/rpc/args.go index 79519e7d2..aaa017c4e 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -69,10 +69,28 @@ func (a *PushTxArgs) requirementsPushTx() error { type GetStorageArgs struct { Address string - Key string } func (obj *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { + if err = json.Unmarshal(b, &obj.Address); err != nil { + return NewErrorResponse(ErrorDecodeArgs) + } + return +} + +func (a *GetStorageArgs) requirements() error { + if len(a.Address) == 0 { + return NewErrorResponse("GetStorageAt requires an 'address' value as argument") + } + return nil +} + +type GetStateArgs struct { + Address string + Key string +} + +func (obj *GetStateArgs) UnmarshalJSON(b []byte) (err error) { arg0 := "" if err = json.Unmarshal(b, arg0); err == nil { obj.Address = arg0 @@ -81,7 +99,7 @@ func (obj *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { return NewErrorResponse(ErrorDecodeArgs) } -func (a *GetStorageArgs) requirements() error { +func (a *GetStateArgs) requirements() error { if a.Address == "" { return NewErrorResponse("GetStorageAt requires an 'address' value as argument") } @@ -92,9 +110,8 @@ func (a *GetStorageArgs) requirements() error { } type GetStorageAtRes struct { - Key string `json:"key"` - Value string `json:"value"` - Address string `json:"address"` + Key string `json:"key"` + Value string `json:"value"` } type GetTxCountArgs struct { @@ -218,3 +235,19 @@ func toFilterOptions(options *FilterOptions) core.FilterOptions { type FilterChangedArgs struct { n int } + +type DbArgs struct { + Database string + Key string + Value string +} + +func (a *DbArgs) requirements() error { + if len(a.Database) == 0 { + return NewErrorResponse("DbPutArgs requires an 'Database' value as argument") + } + if len(a.Key) == 0 { + return NewErrorResponse("DbPutArgs requires an 'Key' value as argument") + } + return nil +} diff --git a/rpc/message.go b/rpc/message.go index 05f66ee95..a76eaece4 100644 --- a/rpc/message.go +++ b/rpc/message.go @@ -126,13 +126,28 @@ func (req *RpcRequest) ToPushTxArgs() (*PushTxArgs, error) { return args, nil } -func (req *RpcRequest) ToGetStorageArgs() (*GetStorageArgs, error) { - if len(req.Params) < 2 { +func (req *RpcRequest) ToGetStateArgs() (*GetStateArgs, error) { + if len(req.Params) < 1 { + return nil, NewErrorResponse(ErrorArguments) + } + + args := new(GetStateArgs) + // TODO need to pass both arguments + r := bytes.NewReader(req.Params[0]) + err := json.NewDecoder(r).Decode(args) + if err != nil { + return nil, NewErrorResponse(ErrorDecodeArgs) + } + rpclogger.DebugDetailf("%T %v", args, args) + return args, nil +} + +func (req *RpcRequest) ToStorageAtArgs() (*GetStorageArgs, error) { + if len(req.Params) < 1 { return nil, NewErrorResponse(ErrorArguments) } args := new(GetStorageArgs) - // TODO need to pass both arguments r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(args) if err != nil { @@ -239,3 +254,44 @@ func toLogs(logs state.Logs) (ls []Log) { return } + +func (req *RpcRequest) ToDbPutArgs() (*DbArgs, error) { + if len(req.Params) < 3 { + return nil, NewErrorResponse(ErrorArguments) + } + + var args DbArgs + err := json.Unmarshal(req.Params[0], &args.Database) + if err != nil { + return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + } + err = json.Unmarshal(req.Params[1], &args.Key) + if err != nil { + return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + } + err = json.Unmarshal(req.Params[2], &args.Value) + if err != nil { + return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + } + rpclogger.DebugDetailf("%T %v", args, args) + return &args, nil +} + +func (req *RpcRequest) ToDbGetArgs() (*DbArgs, error) { + if len(req.Params) < 2 { + return nil, NewErrorResponse(ErrorArguments) + } + + var args DbArgs + err := json.Unmarshal(req.Params[0], &args.Database) + if err != nil { + return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + } + + err = json.Unmarshal(req.Params[1], &args.Key) + if err != nil { + return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + } + rpclogger.DebugDetailf("%T %v", args, args) + return &args, nil +} diff --git a/rpc/packages.go b/rpc/packages.go index e8dc570fd..2de1f06cd 100644 --- a/rpc/packages.go +++ b/rpc/packages.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/event/filter" "github.com/ethereum/go-ethereum/state" @@ -63,13 +64,17 @@ type EthereumApi struct { mut sync.RWMutex logs map[int]state.Logs + + db ethutil.Database } func NewEthereumApi(xeth *xeth.XEth) *EthereumApi { + db, _ := ethdb.NewLDBDatabase("dapps") api := &EthereumApi{ xeth: xeth, filterManager: filter.NewFilterManager(xeth.Backend().EventMux()), logs: make(map[int]state.Logs), + db: db, } go api.filterManager.Start() @@ -91,29 +96,6 @@ func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) erro return nil } -type Log struct { - Address string `json:"address"` - Topics []string `json:"topics"` - Data string `json:"data"` -} - -func toLogs(logs state.Logs) (ls []Log) { - ls = make([]Log, len(logs)) - - for i, log := range logs { - var l Log - l.Topics = make([]string, len(log.Topics())) - l.Address = toHex(log.Address()) - l.Data = toHex(log.Data()) - for j, topic := range log.Topics() { - l.Topics[j] = toHex(topic) - } - ls[i] = l - } - - return -} - func (self *EthereumApi) FilterChanged(id int, reply *interface{}) error { self.mut.RLock() defer self.mut.RUnlock() @@ -176,7 +158,7 @@ func (p *EthereumApi) PushTx(args *PushTxArgs, reply *interface{}) error { return nil } -func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) error { +func (p *EthereumApi) GetStateAt(args *GetStateArgs, reply *interface{}) error { err := args.requirements() if err != nil { return err @@ -184,6 +166,7 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) err state := p.xeth.State().SafeGet(args.Address) + value := state.StorageString(args.Key) var hx string if strings.Index(args.Key, "0x") == 0 { hx = string([]byte(args.Key)[2:]) @@ -192,9 +175,18 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) err i, _ := new(big.Int).SetString(args.Key, 10) hx = ethutil.Bytes2Hex(i.Bytes()) } - rpclogger.Debugf("GetStorageAt(%s, %s)\n", args.Address, hx) - value := state.Storage(ethutil.Hex2Bytes(hx)) - *reply = GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value.Str()} + rpclogger.Debugf("GetStateAt(%s, %s)\n", args.Address, hx) + *reply = map[string]string{args.Key: value.Str()} + return nil +} + +func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) error { + err := args.requirements() + if err != nil { + return err + } + + *reply = p.xeth.State().SafeGet(args.Address).Storage() return nil } @@ -213,11 +205,21 @@ func (p *EthereumApi) GetCoinbase(reply *interface{}) error { return nil } +func (p *EthereumApi) Accounts(reply *interface{}) error { + *reply = p.xeth.Accounts() + return nil +} + func (p *EthereumApi) GetIsMining(reply *interface{}) error { *reply = p.xeth.IsMining() return nil } +func (p *EthereumApi) BlockNumber(reply *interface{}) error { + *reply = p.xeth.Backend().ChainManager().CurrentBlock().Number() + return nil +} + func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *interface{}) error { err := args.requirements() if err != nil { @@ -251,6 +253,28 @@ func (p *EthereumApi) Sha3(args *Sha3Args, reply *interface{}) error { return nil } +func (p *EthereumApi) DbPut(args *DbArgs, reply *interface{}) error { + err := args.requirements() + if err != nil { + return err + } + + p.db.Put([]byte(args.Database+args.Key), []byte(args.Value)) + *reply = true + return nil +} + +func (p *EthereumApi) DbGet(args *DbArgs, reply *interface{}) error { + err := args.requirements() + if err != nil { + return err + } + + res, _ := p.db.Get([]byte(args.Database + args.Key)) + *reply = string(res) + return nil +} + func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error { // Spec at https://github.com/ethereum/wiki/wiki/Generic-ON-RPC rpclogger.DebugDetailf("%T %s", req.Params, req.Params) @@ -263,6 +287,10 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return p.GetIsMining(reply) case "eth_peerCount": return p.GetPeerCount(reply) + case "eth_number": + return p.BlockNumber(reply) + case "eth_accounts": + return p.Accounts(reply) case "eth_countAt": args, err := req.ToGetTxCountArgs() if err != nil { @@ -282,7 +310,13 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error } return p.GetBalanceAt(args, reply) case "eth_stateAt": - args, err := req.ToGetStorageArgs() + args, err := req.ToGetStateArgs() + if err != nil { + return err + } + return p.GetStateAt(args, reply) + case "eth_storageAt": + args, err := req.ToStorageAtArgs() if err != nil { return err } @@ -317,12 +351,27 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.FilterChanged(args, reply) + case "eth_gasPrice": + *reply = "1000000000000000" + return nil case "web3_sha3": args, err := req.ToSha3Args() if err != nil { return err } return p.Sha3(args, reply) + case "db_put": + args, err := req.ToDbPutArgs() + if err != nil { + return err + } + return p.DbPut(args, reply) + case "db_get": + args, err := req.ToDbGetArgs() + if err != nil { + return err + } + return p.DbGet(args, reply) default: return NewErrorResponse(fmt.Sprintf("%v %s", ErrorNotImplemented, req.Method)) } diff --git a/xeth/types.go b/xeth/types.go index 4d8543eb1..bee730ba1 100644 --- a/xeth/types.go +++ b/xeth/types.go @@ -35,20 +35,31 @@ func NewObject(state *state.StateObject) *Object { func (self *Object) StorageString(str string) *ethutil.Value { if ethutil.IsHex(str) { - return self.Storage(ethutil.Hex2Bytes(str[2:])) + return self.storage(ethutil.Hex2Bytes(str[2:])) } else { - return self.Storage(ethutil.RightPadBytes([]byte(str), 32)) + return self.storage(ethutil.RightPadBytes([]byte(str), 32)) } } func (self *Object) StorageValue(addr *ethutil.Value) *ethutil.Value { - return self.Storage(addr.Bytes()) + return self.storage(addr.Bytes()) } -func (self *Object) Storage(addr []byte) *ethutil.Value { +func (self *Object) storage(addr []byte) *ethutil.Value { return self.StateObject.GetStorage(ethutil.BigD(addr)) } +func (self *Object) Storage() (storage map[string]string) { + storage = make(map[string]string) + + it := self.StateObject.Trie().Iterator() + for it.Next() { + storage[toHex(it.Key)] = toHex(it.Value) + } + + return +} + // Block interface exposed to QML type Block struct { //Transactions string `json:"transactions"` diff --git a/xeth/world.go b/xeth/world.go index cdceec50d..9cbdd9461 100644 --- a/xeth/world.go +++ b/xeth/world.go @@ -11,7 +11,7 @@ func NewState(xeth *XEth) *State { } func (self *State) State() *state.StateDB { - return self.xeth.chainManager.State() + return self.xeth.chainManager.TransState() } func (self *State) Get(addr string) *Object {