From a96cea486d4a74b7bd8ada1f004b219e7d8203c4 Mon Sep 17 00:00:00 2001
From: Meng-Ying Yang <garfield@dexon.org>
Date: Tue, 25 Dec 2018 16:51:08 +0800
Subject: core: add database/sql support for more types (#102)

* core: types: add database/sql support for BlockNonce

* common: add database/sql support with Big

New Big type is declared to let big.Int support database/sql by
implementing Scan() and Value() on new type.
---
 common/big.go            | 37 ++++++++++++++++++-
 common/big_test.go       | 95 ++++++++++++++++++++++++++++++++++++++++++++++++
 core/types/block.go      | 27 +++++++++++++-
 core/types/block_test.go | 93 +++++++++++++++++++++++++++++++++++++++++++----
 4 files changed, 243 insertions(+), 9 deletions(-)
 create mode 100644 common/big_test.go

diff --git a/common/big.go b/common/big.go
index 65d4377bf..dcc5269a7 100644
--- a/common/big.go
+++ b/common/big.go
@@ -16,7 +16,11 @@
 
 package common
 
-import "math/big"
+import (
+	"database/sql/driver"
+	"fmt"
+	"math/big"
+)
 
 // Common big integers often used
 var (
@@ -28,3 +32,34 @@ var (
 	Big256 = big.NewInt(256)
 	Big257 = big.NewInt(257)
 )
+
+// Big support database/sql Scan and Value.
+type Big big.Int
+
+// Scan implements Scanner for database/sql.
+func (b *Big) Scan(src interface{}) error {
+	newB := new(big.Int)
+	switch t := src.(type) {
+	case int64:
+		*b = Big(*newB.SetInt64(t))
+	case uint64:
+		*b = Big(*newB.SetUint64(t))
+	case []byte:
+		*b = Big(*newB.SetBytes(t))
+	case string:
+		v, ok := newB.SetString(t, 10)
+		if !ok {
+			return fmt.Errorf("invalid string format %v", src)
+		}
+		*b = Big(*v)
+	default:
+		return fmt.Errorf("can't scan %T into Big", src)
+	}
+	return nil
+}
+
+// Value implements valuer for database/sql.
+func (b Big) Value() (driver.Value, error) {
+	b2 := big.Int(b)
+	return (&b2).String(), nil
+}
diff --git a/common/big_test.go b/common/big_test.go
new file mode 100644
index 000000000..0339b4760
--- /dev/null
+++ b/common/big_test.go
@@ -0,0 +1,95 @@
+package common
+
+import (
+	"database/sql/driver"
+	"math/big"
+	"reflect"
+	"testing"
+)
+
+func TestBig_Scan(t *testing.T) {
+	type args struct {
+		src interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		value   Big
+		wantErr bool
+	}{
+		{
+			name:    "scan int64",
+			args:    args{src: int64(-10)},
+			value:   Big(*big.NewInt(-10)),
+			wantErr: false,
+		},
+		{
+			name:    "scan uint64",
+			args:    args{src: uint64(10)},
+			value:   Big(*big.NewInt(10)),
+			wantErr: false,
+		},
+		{
+			name:    "scan bytes",
+			args:    args{src: []byte{0x0a}},
+			value:   Big(*big.NewInt(10)),
+			wantErr: false,
+		},
+		{
+			name:    "scan string",
+			args:    args{src: "-10"},
+			value:   Big(*big.NewInt(-10)),
+			wantErr: false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			b := &Big{}
+			if err := b.Scan(tt.args.src); (err != nil) != tt.wantErr {
+				t.Errorf("Big.Scan() error = %v, wantErr %v", err, tt.wantErr)
+			}
+
+			if !tt.wantErr {
+				if !reflect.DeepEqual(*b, tt.value) {
+					t.Errorf(
+						"Big.Scan() wrong value (got: %v, want: %v)",
+						*b, tt.value,
+					)
+				}
+			}
+		})
+	}
+
+}
+func TestBig_Value(t *testing.T) {
+	r := "12345"
+	b := Big(*big.NewInt(12345))
+	tests := []struct {
+		name    string
+		b       Big
+		want    driver.Value
+		wantErr bool
+	}{
+		{
+			name:    "working value",
+			b:       b,
+			want:    r,
+			wantErr: false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := tt.b.Value()
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Big.Value() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("Hash.Value() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/core/types/block.go b/core/types/block.go
index 23da3ea77..96df245b7 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -18,7 +18,9 @@
 package types
 
 import (
+	"database/sql/driver"
 	"encoding/binary"
+	"fmt"
 	"io"
 	"math/big"
 	"sort"
@@ -37,10 +39,15 @@ var (
 	EmptyUncleHash = CalcUncleHash(nil)
 )
 
+const (
+	// Length of block nonce in bytes.
+	BlockNonceLength = 8
+)
+
 // A BlockNonce is a 64-bit hash which proves (combined with the
 // mix-hash) that a sufficient amount of computation has been carried
 // out on a block.
-type BlockNonce [8]byte
+type BlockNonce [BlockNonceLength]byte
 
 // EncodeNonce converts the given integer to a block nonce.
 func EncodeNonce(i uint64) BlockNonce {
@@ -64,6 +71,24 @@ func (n *BlockNonce) UnmarshalText(input []byte) error {
 	return hexutil.UnmarshalFixedText("BlockNonce", input, n[:])
 }
 
+// Scan implements Scanner for database/sql.
+func (n *BlockNonce) Scan(src interface{}) error {
+	srcB, ok := src.([]byte)
+	if !ok {
+		return fmt.Errorf("can't scan %T into BlockNonce", src)
+	}
+	if len(srcB) != BlockNonceLength {
+		return fmt.Errorf("can't scan []byte of len %d into BlockNonce, want %d", len(srcB), BlockNonceLength)
+	}
+	copy(n[:], srcB)
+	return nil
+}
+
+// Value implements valuer for database/sql.
+func (n BlockNonce) Value() (driver.Value, error) {
+	return n[:], nil
+}
+
 // WitnessData represents the witness data.
 type WitnessData struct {
 	Root        common.Hash
diff --git a/core/types/block_test.go b/core/types/block_test.go
index b2ac5485c..8e1165a99 100644
--- a/core/types/block_test.go
+++ b/core/types/block_test.go
@@ -16,18 +16,14 @@
 
 package types
 
-/*
 import (
-	"bytes"
-	"fmt"
-	"math/big"
+	"database/sql/driver"
 	"reflect"
 	"testing"
-
-	"github.com/dexon-foundation/dexon/common"
-	"github.com/dexon-foundation/dexon/rlp"
 )
 
+/*
+
 // from bcValidBlockTest.json, "SimpleTx"
 func TestBlockEncoding(t *testing.T) {
 	blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0")
@@ -70,3 +66,86 @@ func TestBlockEncoding(t *testing.T) {
 	}
 }
 */
+
+func TestBlockNonce_Scan(t *testing.T) {
+	type args struct {
+		src interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "working scan",
+			args: args{src: []byte{
+				0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc,
+			}},
+			wantErr: false,
+		},
+		{
+			name:    "non working scan",
+			args:    args{src: int64(1234567890)},
+			wantErr: true,
+		},
+		{
+			name: "invalid length scan",
+			args: args{src: []byte{
+				0xb2, 0x6f, 0x2b, 0x34, 0x2a,
+			}},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			n := &BlockNonce{}
+			if err := n.Scan(tt.args.src); (err != nil) != tt.wantErr {
+				t.Errorf("BlockNonce.Scan() error = %v, wantErr %v", err, tt.wantErr)
+			}
+
+			if !tt.wantErr {
+				for i := range n {
+					if n[i] != tt.args.src.([]byte)[i] {
+						t.Errorf(
+							"BlockNonce.Scan() didn't scan the %d src correctly (have %X, want %X)",
+							i, n[i], tt.args.src.([]byte)[i],
+						)
+					}
+				}
+			}
+		})
+	}
+}
+
+func TestBlockNonce_Value(t *testing.T) {
+	b := []byte{
+		0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc,
+	}
+	var nonce BlockNonce
+	nonce.UnmarshalText([]byte("0xb26f2b342aab24bc"))
+	tests := []struct {
+		name    string
+		n       BlockNonce
+		want    driver.Value
+		wantErr bool
+	}{
+		{
+			name:    "Working value",
+			n:       nonce,
+			want:    b,
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := tt.n.Value()
+			if (err != nil) != tt.wantErr {
+				t.Errorf("BlockNonce.Value() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("BlockNonce.Value() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
-- 
cgit v1.2.3