Преглед изворни кода

Updated RPC

* Added a generic RawMessage deserialiser
* Updated ethereum.js
* Updated coin test app
obscuren пре 10 година
родитељ
комит
f22684439a
5 измењених фајлова са 151 додато и 135 уклоњено
  1. 3 2
      cmd/mist/assets/examples/coin.html
  2. 58 22
      cmd/mist/assets/ext/ethereum.js/dist/ethereum.js
  3. 3 3
      rpc/api.go
  4. 26 108
      rpc/args.go
  5. 61 0
      rpc/util.go

+ 3 - 2
cmd/mist/assets/examples/coin.html

@@ -77,7 +77,8 @@
 	}
 	document.querySelector("#contract_addr").innerHTML = address;
 
-	var contract = web3.eth.contract(address, desc);
+	var Contract = web3.eth.contract(desc);
+	contract = new Contract(address);
 	contract.Changed({from: eth.coinbase}).changed(function() {
 		refresh();
 	});
@@ -88,7 +89,7 @@
 		var table = document.querySelector("#table_body");
 		table.innerHTML = ""; // clear
 
-		var storage = eth.storageAt(address);
+		var storage = eth.getStorage(address);
 		table.innerHTML = "";
 		for( var item in storage ) {
 			table.innerHTML += "<tr><td>"+item+"</td><td>"+web3.toDecimal(storage[item])+"</td></tr>";

+ 58 - 22
cmd/mist/assets/ext/ethereum.js/dist/ethereum.js

@@ -1684,7 +1684,7 @@ var methods = [
         inputFormatter: [utils.toHex, function(param){ return (!param) ? false : true; }]},
     { name: 'getUncle', call: uncleCall,
         outputFormatter: formatters.outputBlockFormatter,
-        inputFormatter: [utils.toHex, function(param){ return (!param) ? false : true; }]},
+        inputFormatter: [utils.toHex, utils.toHex, function(param){ return (!param) ? false : true; }]},
     { name: 'getCompilers', call: 'eth_getCompilers' },
     { name: 'getBlockTransactionCount', call: getBlockTransactionCountCall,
         outputFormatter: utils.toDecimal,
@@ -1703,9 +1703,9 @@ var methods = [
         inputFormatter: formatters.inputTransactionFormatter },
     { name: 'call', call: 'eth_call', addDefaultblock: 2,
         inputFormatter: formatters.inputCallFormatter },
-    { name: 'compile.solidity', call: 'eth_compileSolidity' },
-    { name: 'compile.lll', call: 'eth_compileLLL' },
-    { name: 'compile.serpent', call: 'eth_compileSerpent' },
+    { name: 'compile.solidity', call: 'eth_compileSolidity', inputFormatter: utils.toHex },
+    { name: 'compile.lll', call: 'eth_compileLLL', inputFormatter: utils.toHex },
+    { name: 'compile.serpent', call: 'eth_compileSerpent', inputFormatter: utils.toHex },
     { name: 'flush', call: 'eth_flush' },
 
     // deprecated methods
@@ -1939,12 +1939,34 @@ var getOptions = function (options) {
 
     options = options || {};
 
-    if (options.topics)
-        console.warn('"topics" is deprecated, is "topic" instead');
+    if (options.topic) {
+        console.warn('"topic" is deprecated, is "topics" instead');
+        options.topics = options.topic;
+    }
+
+    if (options.earliest) {
+        console.warn('"earliest" is deprecated, is "fromBlock" instead');
+        options.fromBlock = options.earliest;
+    }
+
+    if (options.latest) {
+        console.warn('"latest" is deprecated, is "toBlock" instead');
+        options.toBlock = options.latest;
+    }
+
+    if (options.skip) {
+        console.warn('"skip" is deprecated, is "offset" instead');
+        options.offset = options.skip;
+    }
+
+    if (options.max) {
+        console.warn('"max" is deprecated, is "limit" instead');
+        options.limit = options.max;
+    }
 
     // make sure topics, get converted to hex
-    if(options.topic instanceof Array) {
-        options.topic = options.topic.map(function(topic){
+    if(options.topics instanceof Array) {
+        options.topics = options.topics.map(function(topic){
             return utils.toHex(topic);
         });
     }
@@ -1952,13 +1974,13 @@ var getOptions = function (options) {
 
     // evaluate lazy properties
     return {
+        fromBlock: utils.toHex(options.fromBlock),
+        toBlock: utils.toHex(options.toBlock),
+        limit: utils.toHex(options.limit),
+        offset: utils.toHex(options.offset),
         to: options.to,
-        topic: options.topic,
-        earliest: options.earliest,
-        latest: options.latest,
-        max: options.max,
-        skip: options.skip,
-        address: options.address
+        address: options.address,
+        topics: options.topics
     };
 };
 
@@ -2269,6 +2291,7 @@ if ("build" !== 'build') {/*
 */}
 
 var HttpProvider = function (host) {
+    this.name  = 'HTTP';
     this.handlers = [];
     this.host = host || 'http://localhost:8080';
 };
@@ -2280,8 +2303,14 @@ HttpProvider.prototype.send = function (payload, callback) {
     // ASYNC
     if(typeof callback === 'function') {
         request.onreadystatechange = function() {
-            if(request.readyState === 4 && request.status === 200) {
-                callback(JSON.parse(request.responseText));
+            if(request.readyState === 4) {
+                var result = '';
+                try {
+                    result = JSON.parse(request.responseText)
+                } catch(error) {
+                    result = error;
+                }
+                callback(result, request.status);
             }
         };
 
@@ -2518,19 +2547,26 @@ var requestManager = function() {
             return null;
         }
 
-        // ASYNC (only when callback is given, and it a HttpProvidor)
-        if(typeof callback === 'function' && provider.host){
-            provider.send(payload, function(result){
+        // HTTP ASYNC (only when callback is given, and it a HttpProvidor)
+        if(typeof callback === 'function' && provider.name === 'HTTP'){
+            provider.send(payload, function(result, status){
 
                 if (!jsonrpc.isValidResponse(result)) {
-                    console.log(result);
-                    if(typeof result === 'object' && result.error && result.error.message)
+                    if(typeof result === 'object' && result.error && result.error.message) {
                         console.error(result.error.message);
+                        callback(result.error);
+                    } else {
+                        callback(new Error({
+                            status: status,
+                            error: result,
+                            message: 'Bad Request'
+                        }));
+                    }
                     return null;
                 }
 
                 // format the output
-                callback((typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result);
+                callback(null, (typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result);
             });
 
         // SYNC

+ 3 - 3
rpc/api.go

@@ -165,7 +165,7 @@ func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) erro
 	id = self.filterManager.InstallFilter(filter)
 	self.logs[id] = &logFilter{timeout: time.Now()}
 
-	*reply = id
+	*reply = i2hex(id)
 
 	return nil
 }
@@ -417,7 +417,7 @@ func (p *EthereumApi) WhisperMessages(id int, reply *interface{}) error {
 
 func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error {
 	// Spec at https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC
-	rpclogger.DebugDetailf("%T %s", req.Params, req.Params)
+	rpclogger.Infof("%s %s", req.Method, req.Params)
 	switch req.Method {
 	case "web3_sha3":
 		args := new(Sha3Args)
@@ -446,7 +446,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
 			return err
 		}
 		return p.GetBalance(args, reply)
-	case "eth_getStorage":
+	case "eth_getStorage", "eth_storageAt":
 		// TODO handle BlockNumber
 		args := new(GetStorageArgs)
 		if err := json.Unmarshal(req.Params, &args); err != nil {

+ 26 - 108
rpc/args.go

@@ -3,32 +3,26 @@ package rpc
 import (
 	"bytes"
 	"encoding/json"
+	"fmt"
 	"math/big"
 
 	"github.com/ethereum/go-ethereum/ethutil"
 )
 
-// Unmarshal state is a helper method which has the ability to decode messsages
-// that use the `defaultBlock` (https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter)
-// For example a `call`: [{to: "0x....", data:"0x..."}, "latest"]. The first argument is the transaction
-// message and the second one refers to the block height (or state) to which to apply this `call`.
-func unmarshalState(b []byte, iface interface{}, str *string) (err error) {
-	var data []json.RawMessage
-	if err = json.Unmarshal(b, &data); err != nil && len(data) == 0 {
+func blockNumber(raw json.RawMessage, number *int64) (err error) {
+	var str string
+	if err = json.Unmarshal(raw, &str); err != nil {
 		return errDecodeArgs
 	}
 
-	if err = json.Unmarshal(data[0], iface); err != nil {
-		return errDecodeArgs
-	}
-
-	// Second argument is optional (transact doesn't require it)
-	if len(data) > 1 {
-		if err = json.Unmarshal(data[1], str); err != nil {
-			return errDecodeArgs
-		}
+	switch str {
+	case "latest":
+		*number = -1
+	case "pending":
+		*number = 0
+	default:
+		*number = ethutil.String2Big(str).Int64()
 	}
-
 	return nil
 }
 
@@ -88,20 +82,12 @@ type NewTxArgs struct {
 	GasPrice *big.Int
 	Data     string
 
-	BlockHeight string
+	BlockNumber int64
 }
 
 func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
-	var obj struct {
-		From     string `json:"from"`
-		To       string `json:"to"`
-		Value    string `json:"value"`
-		Gas      string `json:"gas"`
-		GasPrice string `json:"gasPrice"`
-		Data     string `json:"data"`
-	}
-	var height string
-	if err = unmarshalState(b, &obj, &height); err != nil {
+	var obj struct{ From, To, Value, Gas, GasPrice, Data string }
+	if err = UnmarshalRawMessages(b, &obj, &args.BlockNumber); err != nil {
 		return err
 	}
 
@@ -111,7 +97,6 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
 	args.Gas = ethutil.Big(obj.Gas)
 	args.GasPrice = ethutil.Big(obj.GasPrice)
 	args.Data = obj.Data
-	args.BlockHeight = height
 
 	return nil
 }
@@ -122,24 +107,10 @@ type GetStorageArgs struct {
 }
 
 func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) {
-	var obj []interface{}
-	r := bytes.NewReader(b)
-	if err := json.NewDecoder(r).Decode(&obj); err != nil {
+	if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil {
 		return errDecodeArgs
 	}
-
-	if len(obj) < 1 {
-		return errArguments
-	}
-	args.Address = obj[0].(string)
-
-	if len(obj) > 1 {
-		if obj[1].(string) == "latest" {
-			args.BlockNumber = -1
-		} else {
-			args.BlockNumber = ethutil.Big(obj[1].(string)).Int64()
-		}
-	}
+	fmt.Println(args)
 
 	return nil
 }
@@ -158,25 +129,18 @@ type GetStorageAtArgs struct {
 }
 
 func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) {
-	var obj []interface{}
-	r := bytes.NewReader(b)
-	if err := json.NewDecoder(r).Decode(&obj); err != nil {
+	var obj []string
+	if err = UnmarshalRawMessages(b, &obj, &args.BlockNumber); err != nil {
 		return errDecodeArgs
 	}
-
 	if len(obj) < 2 {
-		return errArguments
+		return errDecodeArgs
 	}
-	args.Address = obj[0].(string)
-	args.Key = obj[1].(string)
 
-	if len(obj) > 2 {
-		if obj[2].(string) == "latest" {
-			args.BlockNumber = -1
-		} else {
-			args.BlockNumber = ethutil.Big(obj[2].(string)).Int64()
-		}
-	}
+	args.Address = obj[0]
+	args.Key = obj[1]
+
+	fmt.Println(args)
 
 	return nil
 }
@@ -198,26 +162,10 @@ type GetTxCountArgs struct {
 }
 
 func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) {
-	var obj []interface{}
-	r := bytes.NewReader(b)
-	if err := json.NewDecoder(r).Decode(&obj); err != nil {
+	if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil {
 		return errDecodeArgs
 	}
 
-	if len(obj) < 1 {
-		return errArguments
-
-	}
-	args.Address = obj[0].(string)
-
-	if len(obj) > 1 {
-		if obj[1].(string) == "latest" {
-			args.BlockNumber = -1
-		} else {
-			args.BlockNumber = ethutil.Big(obj[1].(string)).Int64()
-		}
-	}
-
 	return nil
 }
 
@@ -234,25 +182,10 @@ type GetBalanceArgs struct {
 }
 
 func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) {
-	var obj []interface{}
-	r := bytes.NewReader(b)
-	if err := json.NewDecoder(r).Decode(&obj); err != nil {
+	if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil {
 		return errDecodeArgs
 	}
 
-	if len(obj) < 1 {
-		return errArguments
-	}
-	args.Address = obj[0].(string)
-
-	if len(obj) > 1 {
-		if obj[1].(string) == "latest" {
-			args.BlockNumber = -1
-		} else {
-			args.BlockNumber = ethutil.Big(obj[1].(string)).Int64()
-		}
-	}
-
 	return nil
 }
 
@@ -269,25 +202,10 @@ type GetDataArgs struct {
 }
 
 func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) {
-	var obj []interface{}
-	r := bytes.NewReader(b)
-	if err := json.NewDecoder(r).Decode(&obj); err != nil {
+	if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil {
 		return errDecodeArgs
 	}
 
-	if len(obj) < 1 {
-		return errArguments
-	}
-	args.Address = obj[0].(string)
-
-	if len(obj) > 1 {
-		if obj[1].(string) == "latest" {
-			args.BlockNumber = -1
-		} else {
-			args.BlockNumber = ethutil.Big(obj[1].(string)).Int64()
-		}
-	}
-
 	return nil
 }
 

+ 61 - 0
rpc/util.go

@@ -18,8 +18,11 @@ package rpc
 
 import (
 	"encoding/json"
+	"fmt"
 	"io"
+	"math/big"
 	"net/http"
+	"reflect"
 	"time"
 
 	"github.com/ethereum/go-ethereum/ethutil"
@@ -32,6 +35,60 @@ var rpclogger = logger.NewLogger("RPC")
 
 type JsonWrapper struct{}
 
+// Unmarshal state is a helper method which has the ability to decode messsages
+// that use the `defaultBlock` (https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter)
+// For example a `call`: [{to: "0x....", data:"0x..."}, "latest"]. The first argument is the transaction
+// message and the second one refers to the block height (or state) to which to apply this `call`.
+func UnmarshalRawMessages(b []byte, iface interface{}, number *int64) (err error) {
+	var data []json.RawMessage
+	if err = json.Unmarshal(b, &data); err != nil && len(data) == 0 {
+		return errDecodeArgs
+	}
+
+	// Number index determines the index in the array for a possible block number
+	numberIndex := 0
+
+	value := reflect.ValueOf(iface)
+	rvalue := reflect.Indirect(value)
+
+	switch rvalue.Kind() {
+	case reflect.Slice:
+		// This is a bit of a cheat, but `data` is expected to be larger than 2 if iface is a slice
+		if number != nil {
+			numberIndex = len(data) - 1
+		} else {
+			numberIndex = len(data)
+		}
+
+		slice := reflect.MakeSlice(rvalue.Type(), numberIndex, numberIndex)
+		for i, raw := range data[0:numberIndex] {
+			v := slice.Index(i).Interface()
+			if err = json.Unmarshal(raw, &v); err != nil {
+				fmt.Println(err, v)
+				return err
+			}
+			slice.Index(i).Set(reflect.ValueOf(v))
+		}
+		reflect.Indirect(rvalue).Set(slice) //value.Set(slice)
+	case reflect.Struct:
+		fallthrough
+	default:
+		if err = json.Unmarshal(data[0], iface); err != nil {
+			return errDecodeArgs
+		}
+		numberIndex = 1
+	}
+
+	// <0 index means out of bound for block number
+	if numberIndex >= 0 && len(data) > numberIndex {
+		if err = blockNumber(data[numberIndex], number); err != nil {
+			return errDecodeArgs
+		}
+	}
+
+	return nil
+}
+
 func (self JsonWrapper) Send(writer io.Writer, v interface{}) (n int, err error) {
 	var payload []byte
 	payload, err = json.Marshal(v)
@@ -80,6 +137,10 @@ func fromHex(s string) []byte {
 	return nil
 }
 
+func i2hex(n int) string {
+	return toHex(big.NewInt(int64(n)).Bytes())
+}
+
 type RpcServer interface {
 	Start()
 	Stop()