From 3f6e1b2fd3cbc6aeb1bbe658dd5b70945a57dffa Mon Sep 17 00:00:00 2001
From: Taylor Gerring <taylor.gerring@gmail.com>
Date: Mon, 23 Mar 2015 16:04:21 +0100
Subject: [PATCH] db_putHex/db_getHex + tests

---
 rpc/api.go       | 33 +++++++++++++++++---
 rpc/api_test.go  | 46 ++++++++++++++++++++++++++++
 rpc/args.go      | 78 ++++++++++++++++++++++++++++++++++++++++++++----
 rpc/args_test.go | 39 ++++++++++++++++++++----
 4 files changed, 181 insertions(+), 15 deletions(-)

diff --git a/rpc/api.go b/rpc/api.go
index 427032995..4cd88aa71 100644
--- a/rpc/api.go
+++ b/rpc/api.go
@@ -21,7 +21,10 @@ type EthereumApi struct {
 
 func NewEthereumApi(xeth *xeth.XEth, dataDir string) *EthereumApi {
 	// What about when dataDir is empty?
-	db, _ := ethdb.NewLDBDatabase(path.Join(dataDir, "dapps"))
+	db, err := ethdb.NewLDBDatabase(path.Join(dataDir, "dapps"))
+	if err != nil {
+		panic(err)
+	}
 	api := &EthereumApi{
 		eth: xeth,
 		db:  db,
@@ -361,7 +364,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
 			return err
 		}
 
-		api.db.Put([]byte(args.Database+args.Key), []byte(args.Value))
+		api.db.Put([]byte(args.Database+args.Key), args.Value)
 		*reply = true
 	case "db_getString":
 		args := new(DbArgs)
@@ -375,8 +378,30 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
 
 		res, _ := api.db.Get([]byte(args.Database + args.Key))
 		*reply = string(res)
-	case "db_putHex", "db_getHex":
-		return NewNotImplementedError(req.Method)
+	case "db_putHex":
+		args := new(DbHexArgs)
+		if err := json.Unmarshal(req.Params, &args); err != nil {
+			return err
+		}
+
+		if err := args.requirements(); err != nil {
+			return err
+		}
+
+		api.db.Put([]byte(args.Database+args.Key), args.Value)
+		*reply = true
+	case "db_getHex":
+		args := new(DbHexArgs)
+		if err := json.Unmarshal(req.Params, &args); err != nil {
+			return err
+		}
+
+		if err := args.requirements(); err != nil {
+			return err
+		}
+
+		res, _ := api.db.Get([]byte(args.Database + args.Key))
+		*reply = common.ToHex(res)
 	case "shh_post":
 		args := new(WhisperMessageArgs)
 		if err := json.Unmarshal(req.Params, &args); err != nil {
diff --git a/rpc/api_test.go b/rpc/api_test.go
index 727ade007..a00c2f3f1 100644
--- a/rpc/api_test.go
+++ b/rpc/api_test.go
@@ -5,6 +5,8 @@ import (
 	// "sync"
 	"testing"
 	// "time"
+
+	"github.com/ethereum/go-ethereum/xeth"
 )
 
 func TestWeb3Sha3(t *testing.T) {
@@ -24,6 +26,50 @@ func TestWeb3Sha3(t *testing.T) {
 	}
 }
 
+func TestDbStr(t *testing.T) {
+	jsonput := `{"jsonrpc":"2.0","method":"db_putString","params":["testDB","myKey","myString"],"id":64}`
+	jsonget := `{"jsonrpc":"2.0","method":"db_getString","params":["testDB","myKey"],"id":64}`
+	expected := "myString"
+
+	xeth := &xeth.XEth{}
+	api := NewEthereumApi(xeth, "")
+	defer api.db.Close()
+	var response interface{}
+
+	var req RpcRequest
+	json.Unmarshal([]byte(jsonput), &req)
+	_ = api.GetRequestReply(&req, &response)
+
+	json.Unmarshal([]byte(jsonget), &req)
+	_ = api.GetRequestReply(&req, &response)
+
+	if response.(string) != expected {
+		t.Errorf("Expected %s got %s", expected, response)
+	}
+}
+
+func TestDbHexStr(t *testing.T) {
+	jsonput := `{"jsonrpc":"2.0","method":"db_putHex","params":["testDB","beefKey","0xbeef"],"id":64}`
+	jsonget := `{"jsonrpc":"2.0","method":"db_getHex","params":["testDB","beefKey"],"id":64}`
+	expected := "0xbeef"
+
+	xeth := &xeth.XEth{}
+	api := NewEthereumApi(xeth, "")
+	defer api.db.Close()
+	var response interface{}
+
+	var req RpcRequest
+	json.Unmarshal([]byte(jsonput), &req)
+	_ = api.GetRequestReply(&req, &response)
+
+	json.Unmarshal([]byte(jsonget), &req)
+	_ = api.GetRequestReply(&req, &response)
+
+	if response.(string) != expected {
+		t.Errorf("Expected %s got %s", expected, response)
+	}
+}
+
 // func TestFilterClose(t *testing.T) {
 // 	t.Skip()
 // 	api := &EthereumApi{
diff --git a/rpc/args.go b/rpc/args.go
index 504e67c07..06dab99bc 100644
--- a/rpc/args.go
+++ b/rpc/args.go
@@ -497,24 +497,39 @@ func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) {
 type DbArgs struct {
 	Database string
 	Key      string
-	Value    string
+	Value    []byte
 }
 
 func (args *DbArgs) UnmarshalJSON(b []byte) (err error) {
 	var obj []interface{}
-	r := bytes.NewReader(b)
-	if err := json.NewDecoder(r).Decode(&obj); err != nil {
+	if err := json.Unmarshal(b, &obj); err != nil {
 		return NewDecodeParamError(err.Error())
 	}
 
 	if len(obj) < 2 {
 		return NewInsufficientParamsError(len(obj), 2)
 	}
-	args.Database = obj[0].(string)
-	args.Key = obj[1].(string)
+
+	var objstr string
+	var ok bool
+
+	if objstr, ok = obj[0].(string); !ok {
+		return NewDecodeParamError("Database is not a string")
+	}
+	args.Database = objstr
+
+	if objstr, ok = obj[1].(string); !ok {
+		return NewDecodeParamError("Key is not a string")
+	}
+	args.Key = objstr
 
 	if len(obj) > 2 {
-		args.Value = obj[2].(string)
+		objstr, ok = obj[2].(string)
+		if !ok {
+			return NewDecodeParamError("Value is not a string")
+		}
+
+		args.Value = []byte(objstr)
 	}
 
 	return nil
@@ -530,6 +545,57 @@ func (a *DbArgs) requirements() error {
 	return nil
 }
 
+type DbHexArgs struct {
+	Database string
+	Key      string
+	Value    []byte
+}
+
+func (args *DbHexArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 2 {
+		return NewInsufficientParamsError(len(obj), 2)
+	}
+
+	var objstr string
+	var ok bool
+
+	if objstr, ok = obj[0].(string); !ok {
+		return NewDecodeParamError("Database is not a string")
+	}
+	args.Database = objstr
+
+	if objstr, ok = obj[1].(string); !ok {
+		return NewDecodeParamError("Key is not a string")
+	}
+	args.Key = objstr
+
+	if len(obj) > 2 {
+		objstr, ok = obj[2].(string)
+		if !ok {
+			return NewDecodeParamError("Value is not a string")
+		}
+
+		args.Value = common.FromHex(objstr)
+	}
+
+	return nil
+}
+
+func (a *DbHexArgs) requirements() error {
+	if len(a.Database) == 0 {
+		return NewValidationError("Database", "cannot be blank")
+	}
+	if len(a.Key) == 0 {
+		return NewValidationError("Key", "cannot be blank")
+	}
+	return nil
+}
+
 type WhisperMessageArgs struct {
 	Payload  string
 	To       string
diff --git a/rpc/args_test.go b/rpc/args_test.go
index cfdd278b8..2ad53fba2 100644
--- a/rpc/args_test.go
+++ b/rpc/args_test.go
@@ -485,11 +485,11 @@ func TestBlockFilterArgsEmptyArgs(t *testing.T) {
 }
 
 func TestDbArgs(t *testing.T) {
-	input := `["0x74657374","0x6b6579","0x6d79537472696e67"]`
+	input := `["testDB","myKey","0xbeef"]`
 	expected := new(DbArgs)
-	expected.Database = "0x74657374"
-	expected.Key = "0x6b6579"
-	expected.Value = "0x6d79537472696e67"
+	expected.Database = "testDB"
+	expected.Key = "myKey"
+	expected.Value = []byte("0xbeef")
 
 	args := new(DbArgs)
 	if err := json.Unmarshal([]byte(input), &args); err != nil {
@@ -508,7 +508,36 @@ func TestDbArgs(t *testing.T) {
 		t.Errorf("Key shoud be %#v but is %#v", expected.Key, args.Key)
 	}
 
-	if expected.Value != args.Value {
+	if bytes.Compare(expected.Value, args.Value) != 0 {
+		t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value)
+	}
+}
+
+func TestDbHexArgs(t *testing.T) {
+	input := `["testDB","myKey","0xbeef"]`
+	expected := new(DbHexArgs)
+	expected.Database = "testDB"
+	expected.Key = "myKey"
+	expected.Value = []byte{0xbe, 0xef}
+
+	args := new(DbHexArgs)
+	if err := json.Unmarshal([]byte(input), &args); err != nil {
+		t.Error(err)
+	}
+
+	if err := args.requirements(); err != nil {
+		t.Error(err)
+	}
+
+	if expected.Database != args.Database {
+		t.Errorf("Database shoud be %#v but is %#v", expected.Database, args.Database)
+	}
+
+	if expected.Key != args.Key {
+		t.Errorf("Key shoud be %#v but is %#v", expected.Key, args.Key)
+	}
+
+	if bytes.Compare(expected.Value, args.Value) != 0 {
 		t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value)
 	}
 }