Merge branch 'develop' into qt5.4

This commit is contained in:
obscuren 2015-01-28 22:21:26 +01:00
commit 8284bef072
15 changed files with 1174 additions and 61 deletions

155
accounts/abi/abi.go Normal file
View File

@ -0,0 +1,155 @@
package abi
import (
"encoding/json"
"fmt"
"io"
"strings"
"github.com/ethereum/go-ethereum/crypto"
)
// Callable method given a `Name` and whether the method is a constant.
// If the method is `Const` no transaction needs to be created for this
// particular Method call. It can easily be simulated using a local VM.
// For example a `Balance()` method only needs to retrieve something
// from the storage and therefor requires no Tx to be send to the
// network. A method such as `Transact` does require a Tx and thus will
// be flagged `true`.
// Input specifies the required input parameters for this gives method.
type Method struct {
Name string
Const bool
Input []Argument
Return Type // not yet implemented
}
// Returns the methods string signature according to the ABI spec.
//
// Example
//
// function foo(uint32 a, int b) = "foo(uint32,int256)"
//
// Please note that "int" is substitute for its canonical representation "int256"
func (m Method) String() (out string) {
out += m.Name
types := make([]string, len(m.Input))
i := 0
for _, input := range m.Input {
types[i] = input.Type.String()
i++
}
out += "(" + strings.Join(types, ",") + ")"
return
}
func (m Method) Id() []byte {
return crypto.Sha3([]byte(m.String()))[:4]
}
// Argument holds the name of the argument and the corresponding type.
// Types are used when packing and testing arguments.
type Argument struct {
Name string
Type Type
}
func (a *Argument) UnmarshalJSON(data []byte) error {
var extarg struct {
Name string
Type string
}
err := json.Unmarshal(data, &extarg)
if err != nil {
return fmt.Errorf("argument json err: %v", err)
}
a.Type, err = NewType(extarg.Type)
if err != nil {
return err
}
a.Name = extarg.Name
return nil
}
// The ABI holds information about a contract's context and available
// invokable methods. It will allow you to type check function calls and
// packs data accordingly.
type ABI struct {
Methods map[string]Method
}
// tests, tests whether the given input would result in a successful
// call. Checks argument list count and matches input to `input`.
func (abi ABI) pack(name string, args ...interface{}) ([]byte, error) {
method := abi.Methods[name]
var ret []byte
for i, a := range args {
input := method.Input[i]
packed, err := input.Type.pack(a)
if err != nil {
return nil, fmt.Errorf("`%s` %v", name, err)
}
ret = append(ret, packed...)
}
return ret, nil
}
// Pack the given method name to conform the ABI. Method call's data
// will consist of method_id, args0, arg1, ... argN. Method id consists
// of 4 bytes and arguments are all 32 bytes.
// Method ids are created from the first 4 bytes of the hash of the
// methods string signature. (signature = baz(uint32,string32))
func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
method, exist := abi.Methods[name]
if !exist {
return nil, fmt.Errorf("method '%s' not found", name)
}
// start with argument count match
if len(args) != len(method.Input) {
return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Input))
}
arguments, err := abi.pack(name, args...)
if err != nil {
return nil, err
}
// Set function id
packed := abi.Methods[name].Id()
packed = append(packed, arguments...)
return packed, nil
}
func (abi *ABI) UnmarshalJSON(data []byte) error {
var methods []Method
if err := json.Unmarshal(data, &methods); err != nil {
return err
}
abi.Methods = make(map[string]Method)
for _, method := range methods {
abi.Methods[method.Name] = method
}
return nil
}
func JSON(reader io.Reader) (ABI, error) {
dec := json.NewDecoder(reader)
var abi ABI
if err := dec.Decode(&abi); err != nil {
return ABI{}, err
}
return abi, nil
}

330
accounts/abi/abi_test.go Normal file
View File

@ -0,0 +1,330 @@
package abi
import (
"bytes"
"math/big"
"reflect"
"strings"
"testing"
"github.com/ethereum/go-ethereum/crypto"
)
const jsondata = `
[
{ "name" : "balance", "const" : true },
{ "name" : "send", "const" : false, "input" : [ { "name" : "amount", "type" : "uint256" } ] }
]`
const jsondata2 = `
[
{ "name" : "balance", "const" : true },
{ "name" : "send", "const" : false, "input" : [ { "name" : "amount", "type" : "uint256" } ] },
{ "name" : "test", "const" : false, "input" : [ { "name" : "number", "type" : "uint32" } ] },
{ "name" : "string", "const" : false, "input" : [ { "name" : "input", "type" : "string" } ] },
{ "name" : "bool", "const" : false, "input" : [ { "name" : "input", "type" : "bool" } ] },
{ "name" : "address", "const" : false, "input" : [ { "name" : "input", "type" : "address" } ] },
{ "name" : "string32", "const" : false, "input" : [ { "name" : "input", "type" : "string32" } ] },
{ "name" : "uint64[2]", "const" : false, "input" : [ { "name" : "input", "type" : "uint64[2]" } ] },
{ "name" : "uint64[]", "const" : false, "input" : [ { "name" : "input", "type" : "uint64[]" } ] },
{ "name" : "foo", "const" : false, "input" : [ { "name" : "input", "type" : "uint32" } ] },
{ "name" : "bar", "const" : false, "input" : [ { "name" : "input", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
{ "name" : "slice", "const" : false, "input" : [ { "name" : "input", "type" : "uint32[2]" } ] },
{ "name" : "slice256", "const" : false, "input" : [ { "name" : "input", "type" : "uint256[2]" } ] }
]`
func TestType(t *testing.T) {
typ, err := NewType("uint32")
if err != nil {
t.Error(err)
}
if typ.Kind != reflect.Ptr {
t.Error("expected uint32 to have kind Ptr")
}
typ, err = NewType("uint32[]")
if err != nil {
t.Error(err)
}
if typ.Kind != reflect.Slice {
t.Error("expected uint32[] to have type slice")
}
if typ.Type != ubig_ts {
t.Error("expcted uith32[] to have type uint64")
}
typ, err = NewType("uint32[2]")
if err != nil {
t.Error(err)
}
if typ.Kind != reflect.Slice {
t.Error("expected uint32[2] to have kind slice")
}
if typ.Type != ubig_ts {
t.Error("expcted uith32[2] to have type uint64")
}
if typ.Size != 2 {
t.Error("expected uint32[2] to have a size of 2")
}
}
func TestReader(t *testing.T) {
Uint256, _ := NewType("uint256")
exp := ABI{
Methods: map[string]Method{
"balance": Method{
"balance", true, nil, Type{},
},
"send": Method{
"send", false, []Argument{
Argument{"amount", Uint256},
}, Type{},
},
},
}
abi, err := JSON(strings.NewReader(jsondata))
if err != nil {
t.Error(err)
}
// deep equal fails for some reason
t.Skip()
if !reflect.DeepEqual(abi, exp) {
t.Errorf("\nabi: %v\ndoes not match exp: %v", abi, exp)
}
}
func TestTestNumbers(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Error(err)
t.FailNow()
}
if _, err := abi.Pack("balance"); err != nil {
t.Error(err)
}
if _, err := abi.Pack("balance", 1); err == nil {
t.Error("expected error for balance(1)")
}
if _, err := abi.Pack("doesntexist", nil); err == nil {
t.Errorf("doesntexist shouldn't exist")
}
if _, err := abi.Pack("doesntexist", 1); err == nil {
t.Errorf("doesntexist(1) shouldn't exist")
}
if _, err := abi.Pack("send", big.NewInt(1000)); err != nil {
t.Error(err)
}
i := new(int)
*i = 1000
if _, err := abi.Pack("send", i); err == nil {
t.Errorf("expected send( ptr ) to throw, requires *big.Int instead of *int")
}
if _, err := abi.Pack("send", 1000); err != nil {
t.Error("expected send(1000) to cast to big")
}
if _, err := abi.Pack("test", uint32(1000)); err != nil {
t.Error(err)
}
}
func TestTestString(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Error(err)
t.FailNow()
}
if _, err := abi.Pack("string", "hello world"); err != nil {
t.Error(err)
}
str10 := string(make([]byte, 10))
if _, err := abi.Pack("string32", str10); err != nil {
t.Error(err)
}
str32 := string(make([]byte, 32))
if _, err := abi.Pack("string32", str32); err != nil {
t.Error(err)
}
str33 := string(make([]byte, 33))
if _, err := abi.Pack("string32", str33); err == nil {
t.Error("expected str33 to throw out of bound error")
}
}
func TestTestBool(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Error(err)
t.FailNow()
}
if _, err := abi.Pack("bool", true); err != nil {
t.Error(err)
}
}
func TestTestSlice(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Error(err)
t.FailNow()
}
addr := make([]byte, 20)
if _, err := abi.Pack("address", addr); err != nil {
t.Error(err)
}
addr = make([]byte, 21)
if _, err := abi.Pack("address", addr); err == nil {
t.Error("expected address of 21 width to throw")
}
slice := make([]byte, 2)
if _, err := abi.Pack("uint64[2]", slice); err != nil {
t.Error(err)
}
if _, err := abi.Pack("uint64[]", slice); err != nil {
t.Error(err)
}
}
func TestTestAddress(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Error(err)
t.FailNow()
}
addr := make([]byte, 20)
if _, err := abi.Pack("address", addr); err != nil {
t.Error(err)
}
}
func TestMethodSignature(t *testing.T) {
String, _ := NewType("string")
String32, _ := NewType("string32")
m := Method{"foo", false, []Argument{Argument{"bar", String32}, Argument{"baz", String}}, Type{}}
exp := "foo(string32,string)"
if m.String() != exp {
t.Error("signature mismatch", exp, "!=", m.String())
}
idexp := crypto.Sha3([]byte(exp))[:4]
if !bytes.Equal(m.Id(), idexp) {
t.Errorf("expected ids to match %x != %x", m.Id(), idexp)
}
uintt, _ := NewType("uint")
m = Method{"foo", false, []Argument{Argument{"bar", uintt}}, Type{}}
exp = "foo(uint256)"
if m.String() != exp {
t.Error("signature mismatch", exp, "!=", m.String())
}
}
func TestPack(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Error(err)
t.FailNow()
}
sig := crypto.Sha3([]byte("foo(uint32)"))[:4]
sig = append(sig, make([]byte, 32)...)
sig[35] = 10
packed, err := abi.Pack("foo", uint32(10))
if err != nil {
t.Error(err)
t.FailNow()
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
}
func TestMultiPack(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Error(err)
t.FailNow()
}
sig := crypto.Sha3([]byte("bar(uint32,uint16)"))[:4]
sig = append(sig, make([]byte, 64)...)
sig[35] = 10
sig[67] = 11
packed, err := abi.Pack("bar", uint32(10), uint16(11))
if err != nil {
t.Error(err)
t.FailNow()
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
}
func TestPackSlice(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Error(err)
t.FailNow()
}
sig := crypto.Sha3([]byte("slice(uint32[2])"))[:4]
sig = append(sig, make([]byte, 64)...)
sig[35] = 1
sig[67] = 2
packed, err := abi.Pack("slice", []uint32{1, 2})
if err != nil {
t.Error(err)
t.FailNow()
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
}
func TestPackSliceBig(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Error(err)
t.FailNow()
}
sig := crypto.Sha3([]byte("slice256(uint256[2])"))[:4]
sig = append(sig, make([]byte, 64)...)
sig[35] = 1
sig[67] = 2
packed, err := abi.Pack("slice256", []*big.Int{big.NewInt(1), big.NewInt(2)})
if err != nil {
t.Error(err)
t.FailNow()
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
}

10
accounts/abi/doc.go Normal file
View File

@ -0,0 +1,10 @@
// Package abi implements the Ethereum ABI (Application Binary
// Interface).
//
// The Ethereum ABI is strongly typed, known at compile time
// and static. This ABI will handle basic type casting; unsigned
// to signed and visa versa. It does not handle slice casting such
// as unsigned slice to signed slice. Bit size type casting is also
// handled. ints with a bit size of 32 will be properly cast to int256,
// etc.
package abi

106
accounts/abi/numbers.go Normal file
View File

@ -0,0 +1,106 @@
package abi
import (
"math/big"
"reflect"
"github.com/ethereum/go-ethereum/ethutil"
)
var big_t = reflect.TypeOf(&big.Int{})
var ubig_t = reflect.TypeOf(&big.Int{})
var byte_t = reflect.TypeOf(byte(0))
var byte_ts = reflect.TypeOf([]byte(nil))
var uint_t = reflect.TypeOf(uint(0))
var uint8_t = reflect.TypeOf(uint8(0))
var uint16_t = reflect.TypeOf(uint16(0))
var uint32_t = reflect.TypeOf(uint32(0))
var uint64_t = reflect.TypeOf(uint64(0))
var int_t = reflect.TypeOf(int(0))
var int8_t = reflect.TypeOf(int8(0))
var int16_t = reflect.TypeOf(int16(0))
var int32_t = reflect.TypeOf(int32(0))
var int64_t = reflect.TypeOf(int64(0))
var uint_ts = reflect.TypeOf([]uint(nil))
var uint8_ts = reflect.TypeOf([]uint8(nil))
var uint16_ts = reflect.TypeOf([]uint16(nil))
var uint32_ts = reflect.TypeOf([]uint32(nil))
var uint64_ts = reflect.TypeOf([]uint64(nil))
var ubig_ts = reflect.TypeOf([]*big.Int(nil))
var int_ts = reflect.TypeOf([]int(nil))
var int8_ts = reflect.TypeOf([]int8(nil))
var int16_ts = reflect.TypeOf([]int16(nil))
var int32_ts = reflect.TypeOf([]int32(nil))
var int64_ts = reflect.TypeOf([]int64(nil))
var big_ts = reflect.TypeOf([]*big.Int(nil))
// U256 will ensure unsigned 256bit on big nums
func U256(n *big.Int) []byte {
return ethutil.LeftPadBytes(ethutil.U256(n).Bytes(), 32)
}
func S256(n *big.Int) []byte {
sint := ethutil.S256(n)
ret := ethutil.LeftPadBytes(sint.Bytes(), 32)
if sint.Cmp(ethutil.Big0) < 0 {
for i, b := range ret {
if b == 0 {
ret[i] = 1
continue
}
break
}
}
return ret
}
// S256 will ensure signed 256bit on big nums
func U2U256(n uint64) []byte {
return U256(big.NewInt(int64(n)))
}
func S2S256(n int64) []byte {
return S256(big.NewInt(n))
}
// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
func packNum(value reflect.Value, to byte) []byte {
switch kind := value.Kind(); kind {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if to == UintTy {
return U2U256(value.Uint())
} else {
return S2S256(int64(value.Uint()))
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if to == UintTy {
return U2U256(uint64(value.Int()))
} else {
return S2S256(value.Int())
}
case reflect.Ptr:
// This only takes care of packing and casting. No type checking is done here. It should be done prior to using this function.
if to == UintTy {
return U256(value.Interface().(*big.Int))
} else {
return S256(value.Interface().(*big.Int))
}
}
return nil
}
// checks whether the given reflect value is signed. This also works for slices with a number type
func isSigned(v reflect.Value) bool {
switch v.Type() {
case ubig_ts, big_ts, big_t, ubig_t:
return true
case int_ts, int8_ts, int16_ts, int32_ts, int64_ts, int_t, int8_t, int16_t, int32_t, int64_t:
return true
}
return false
}

View File

@ -0,0 +1,72 @@
package abi
import (
"bytes"
"math/big"
"reflect"
"testing"
)
func TestNumberTypes(t *testing.T) {
ubytes := make([]byte, 32)
ubytes[31] = 1
sbytesmin := []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
unsigned := U256(big.NewInt(1))
if !bytes.Equal(unsigned, ubytes) {
t.Error("expected %x got %x", ubytes, unsigned)
}
signed := S256(big.NewInt(1))
if !bytes.Equal(signed, ubytes) {
t.Error("expected %x got %x", ubytes, unsigned)
}
signed = S256(big.NewInt(-1))
if !bytes.Equal(signed, sbytesmin) {
t.Error("expected %x got %x", ubytes, unsigned)
}
}
func TestPackNumber(t *testing.T) {
ubytes := make([]byte, 32)
ubytes[31] = 1
sbytesmin := []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
maxunsigned := []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
packed := packNum(reflect.ValueOf(1), IntTy)
if !bytes.Equal(packed, ubytes) {
t.Errorf("expected %x got %x", ubytes, packed)
}
packed = packNum(reflect.ValueOf(-1), IntTy)
if !bytes.Equal(packed, sbytesmin) {
t.Errorf("expected %x got %x", ubytes, packed)
}
packed = packNum(reflect.ValueOf(1), UintTy)
if !bytes.Equal(packed, ubytes) {
t.Errorf("expected %x got %x", ubytes, packed)
}
packed = packNum(reflect.ValueOf(-1), UintTy)
if !bytes.Equal(packed, maxunsigned) {
t.Errorf("expected %x got %x", maxunsigned, packed)
}
packed = packNum(reflect.ValueOf("string"), UintTy)
if packed != nil {
t.Errorf("expected 'string' to pack to nil. got %x instead", packed)
}
}
func TestSigned(t *testing.T) {
if isSigned(reflect.ValueOf(uint(10))) {
t.Error()
}
if !isSigned(reflect.ValueOf(int(10))) {
t.Error()
}
if !isSigned(reflect.ValueOf(big.NewInt(10))) {
t.Error()
}
}

106
accounts/abi/profile.cov Normal file
View File

@ -0,0 +1,106 @@
mode: set
github.com/ethereum/go-ethereum/abi/abi.go:27.39,31.32 4 1
github.com/ethereum/go-ethereum/abi/abi.go:35.2,37.8 2 1
github.com/ethereum/go-ethereum/abi/abi.go:31.32,34.3 2 1
github.com/ethereum/go-ethereum/abi/abi.go:40.29,42.2 1 1
github.com/ethereum/go-ethereum/abi/abi.go:51.53,57.16 3 1
github.com/ethereum/go-ethereum/abi/abi.go:61.2,62.16 2 1
github.com/ethereum/go-ethereum/abi/abi.go:65.2,67.12 2 1
github.com/ethereum/go-ethereum/abi/abi.go:57.16,59.3 1 0
github.com/ethereum/go-ethereum/abi/abi.go:62.16,64.3 1 0
github.com/ethereum/go-ethereum/abi/abi.go:79.71,83.25 3 1
github.com/ethereum/go-ethereum/abi/abi.go:94.2,94.17 1 1
github.com/ethereum/go-ethereum/abi/abi.go:83.25,87.17 3 1
github.com/ethereum/go-ethereum/abi/abi.go:90.3,90.31 1 1
github.com/ethereum/go-ethereum/abi/abi.go:87.17,89.4 1 1
github.com/ethereum/go-ethereum/abi/abi.go:102.71,104.12 2 1
github.com/ethereum/go-ethereum/abi/abi.go:109.2,109.36 1 1
github.com/ethereum/go-ethereum/abi/abi.go:113.2,114.16 2 1
github.com/ethereum/go-ethereum/abi/abi.go:119.2,122.20 3 1
github.com/ethereum/go-ethereum/abi/abi.go:104.12,106.3 1 1
github.com/ethereum/go-ethereum/abi/abi.go:109.36,111.3 1 1
github.com/ethereum/go-ethereum/abi/abi.go:114.16,116.3 1 1
github.com/ethereum/go-ethereum/abi/abi.go:125.50,127.55 2 1
github.com/ethereum/go-ethereum/abi/abi.go:131.2,132.33 2 1
github.com/ethereum/go-ethereum/abi/abi.go:136.2,136.12 1 1
github.com/ethereum/go-ethereum/abi/abi.go:127.55,129.3 1 0
github.com/ethereum/go-ethereum/abi/abi.go:132.33,134.3 1 1
github.com/ethereum/go-ethereum/abi/abi.go:139.42,143.41 3 1
github.com/ethereum/go-ethereum/abi/abi.go:147.2,147.17 1 1
github.com/ethereum/go-ethereum/abi/abi.go:143.41,145.3 1 0
github.com/ethereum/go-ethereum/abi/numbers.go:39.30,41.2 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:43.30,46.32 3 1
github.com/ethereum/go-ethereum/abi/numbers.go:56.2,56.12 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:46.32,47.25 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:47.25,48.14 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:52.4,52.9 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:48.14,50.13 2 1
github.com/ethereum/go-ethereum/abi/numbers.go:59.30,61.2 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:63.29,65.2 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:67.51,68.36 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:91.2,91.12 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:69.2,70.19 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:75.2,76.19 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:81.2,83.19 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:70.19,72.4 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:72.4,74.4 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:76.19,78.4 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:78.4,80.4 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:83.19,85.4 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:85.4,87.4 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:94.37,95.18 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:101.2,101.14 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:96.2,97.14 1 1
github.com/ethereum/go-ethereum/abi/numbers.go:98.2,99.14 1 1
github.com/ethereum/go-ethereum/abi/type.go:32.46,35.16 2 1
github.com/ethereum/go-ethereum/abi/type.go:38.2,43.9 3 1
github.com/ethereum/go-ethereum/abi/type.go:55.2,56.16 2 1
github.com/ethereum/go-ethereum/abi/type.go:60.2,64.55 4 1
github.com/ethereum/go-ethereum/abi/type.go:69.2,69.13 1 1
github.com/ethereum/go-ethereum/abi/type.go:111.2,113.8 2 1
github.com/ethereum/go-ethereum/abi/type.go:35.16,37.3 1 0
github.com/ethereum/go-ethereum/abi/type.go:44.2,47.17 2 1
github.com/ethereum/go-ethereum/abi/type.go:48.2,50.12 2 1
github.com/ethereum/go-ethereum/abi/type.go:51.2,52.60 1 0
github.com/ethereum/go-ethereum/abi/type.go:56.16,58.3 1 0
github.com/ethereum/go-ethereum/abi/type.go:64.55,67.3 2 1
github.com/ethereum/go-ethereum/abi/type.go:69.13,72.16 3 1
github.com/ethereum/go-ethereum/abi/type.go:73.3,74.21 1 0
github.com/ethereum/go-ethereum/abi/type.go:75.3,76.22 1 1
github.com/ethereum/go-ethereum/abi/type.go:77.3,78.66 1 0
github.com/ethereum/go-ethereum/abi/type.go:80.3,81.16 1 1
github.com/ethereum/go-ethereum/abi/type.go:82.3,86.17 4 0
github.com/ethereum/go-ethereum/abi/type.go:87.3,91.18 4 1
github.com/ethereum/go-ethereum/abi/type.go:92.3,93.27 1 1
github.com/ethereum/go-ethereum/abi/type.go:94.3,95.30 1 0
github.com/ethereum/go-ethereum/abi/type.go:96.3,100.21 4 1
github.com/ethereum/go-ethereum/abi/type.go:101.3,104.17 3 1
github.com/ethereum/go-ethereum/abi/type.go:107.3,108.60 1 0
github.com/ethereum/go-ethereum/abi/type.go:104.17,106.5 1 1
github.com/ethereum/go-ethereum/abi/type.go:116.37,118.2 1 1
github.com/ethereum/go-ethereum/abi/type.go:126.51,128.36 2 1
github.com/ethereum/go-ethereum/abi/type.go:179.2,179.20 1 0
github.com/ethereum/go-ethereum/abi/type.go:129.2,130.23 1 1
github.com/ethereum/go-ethereum/abi/type.go:133.3,133.34 1 1
github.com/ethereum/go-ethereum/abi/type.go:134.2,135.23 1 1
github.com/ethereum/go-ethereum/abi/type.go:138.3,138.34 1 1
github.com/ethereum/go-ethereum/abi/type.go:139.2,142.49 1 1
github.com/ethereum/go-ethereum/abi/type.go:145.3,145.34 1 1
github.com/ethereum/go-ethereum/abi/type.go:146.2,147.42 1 1
github.com/ethereum/go-ethereum/abi/type.go:150.3,150.60 1 1
github.com/ethereum/go-ethereum/abi/type.go:151.2,152.42 1 1
github.com/ethereum/go-ethereum/abi/type.go:157.3,157.23 1 1
github.com/ethereum/go-ethereum/abi/type.go:162.3,162.78 1 1
github.com/ethereum/go-ethereum/abi/type.go:166.3,167.36 2 1
github.com/ethereum/go-ethereum/abi/type.go:170.3,170.21 1 1
github.com/ethereum/go-ethereum/abi/type.go:171.2,172.19 1 1
github.com/ethereum/go-ethereum/abi/type.go:130.23,132.4 1 0
github.com/ethereum/go-ethereum/abi/type.go:135.23,137.4 1 0
github.com/ethereum/go-ethereum/abi/type.go:142.49,144.4 1 1
github.com/ethereum/go-ethereum/abi/type.go:147.42,149.4 1 1
github.com/ethereum/go-ethereum/abi/type.go:152.42,154.4 1 1
github.com/ethereum/go-ethereum/abi/type.go:157.23,159.4 1 1
github.com/ethereum/go-ethereum/abi/type.go:162.78,164.4 1 0
github.com/ethereum/go-ethereum/abi/type.go:167.36,169.4 1 1
github.com/ethereum/go-ethereum/abi/type.go:172.19,174.4 1 1
github.com/ethereum/go-ethereum/abi/type.go:174.4,176.4 1 0

190
accounts/abi/type.go Normal file
View File

@ -0,0 +1,190 @@
package abi
import (
"fmt"
"reflect"
"regexp"
"strconv"
"github.com/ethereum/go-ethereum/ethutil"
)
const (
IntTy byte = iota
UintTy
BoolTy
SliceTy
AddressTy
RealTy
)
// Type is the reflection of the supported argument type
type Type struct {
Kind reflect.Kind
Type reflect.Type
Size int
T byte // Our own type checking
stringKind string // holds the unparsed string for deriving signatures
}
// New type returns a fully parsed Type given by the input string or an error if it can't be parsed.
//
// Strings can be in the format of:
//
// Input = Type [ "[" [ Number ] "]" ] Name .
// Type = [ "u" ] "int" [ Number ] .
//
// Examples:
//
// string int uint real
// string32 int8 uint8 uint[]
// address int256 uint256 real[2]
func NewType(t string) (typ Type, err error) {
// 1. full string 2. type 3. (opt.) is slice 4. (opt.) size
freg, err := regexp.Compile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?")
if err != nil {
return Type{}, err
}
res := freg.FindAllStringSubmatch(t, -1)[0]
var (
isslice bool
size int
)
switch {
case res[3] != "":
// err is ignored. Already checked for number through the regexp
size, _ = strconv.Atoi(res[3])
isslice = true
case res[2] != "":
isslice = true
size = -1
case res[0] == "":
return Type{}, fmt.Errorf("type parse error for `%s`", t)
}
treg, err := regexp.Compile("([a-zA-Z]+)([0-9]*)?")
if err != nil {
return Type{}, err
}
parsedType := treg.FindAllStringSubmatch(res[1], -1)[0]
vsize, _ := strconv.Atoi(parsedType[2])
vtype := parsedType[1]
// substitute canonical representation
if vsize == 0 && (vtype == "int" || vtype == "uint") {
vsize = 256
t += "256"
}
if isslice {
typ.Kind = reflect.Slice
typ.Size = size
switch vtype {
case "int":
typ.Type = big_ts
case "uint":
typ.Type = ubig_ts
default:
return Type{}, fmt.Errorf("unsupported arg slice type: %s", t)
}
} else {
switch vtype {
case "int":
typ.Kind = reflect.Ptr
typ.Type = big_t
typ.Size = 256
typ.T = IntTy
case "uint":
typ.Kind = reflect.Ptr
typ.Type = ubig_t
typ.Size = 256
typ.T = UintTy
case "bool":
typ.Kind = reflect.Bool
case "real": // TODO
typ.Kind = reflect.Invalid
case "address":
typ.Kind = reflect.Slice
typ.Type = byte_ts
typ.Size = 20
typ.T = AddressTy
case "string":
typ.Kind = reflect.String
typ.Size = -1
if vsize > 0 {
typ.Size = 32
}
default:
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
}
}
typ.stringKind = t
return
}
func (t Type) String() (out string) {
return t.stringKind
}
// Test the given input parameter `v` and checks if it matches certain
// criteria
// * Big integers are checks for ptr types and if the given value is
// assignable
// * Integer are checked for size
// * Strings, addresses and bytes are checks for type and size
func (t Type) pack(v interface{}) ([]byte, error) {
value := reflect.ValueOf(v)
switch kind := value.Kind(); kind {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if t.Type != ubig_t {
return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
}
return packNum(value, t.T), nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if t.Type != ubig_t {
return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
}
return packNum(value, t.T), nil
case reflect.Ptr:
// If the value is a ptr do a assign check (only used by
// big.Int for now)
if t.Type == ubig_t && value.Type() != ubig_t {
return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
}
return packNum(value, t.T), nil
case reflect.String:
if t.Size > -1 && value.Len() > t.Size {
return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size)
}
return []byte(ethutil.LeftPadString(t.String(), 32)), nil
case reflect.Slice:
if t.Size > -1 && value.Len() > t.Size {
return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size)
}
// Address is a special slice. The slice acts as one rather than a list of elements.
if t.T == AddressTy {
return ethutil.LeftPadBytes(v.([]byte), 32), nil
}
// Signed / Unsigned check
if (t.T != IntTy && isSigned(value)) || (t.T == UintTy && isSigned(value)) {
return nil, fmt.Errorf("slice of incompatible types.")
}
var packed []byte
for i := 0; i < value.Len(); i++ {
packed = append(packed, packNum(value.Index(i), t.T)...)
}
return packed, nil
case reflect.Bool:
if value.Bool() {
return ethutil.LeftPadBytes(ethutil.Big1.Bytes(), 32), nil
} else {
return ethutil.LeftPadBytes(ethutil.Big0.Bytes(), 32), nil
}
}
panic("unreached")
}

View File

@ -0,0 +1,95 @@
/*
This file is part of go-ethereum
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Gustav Simonsson <gustav.simonsson@gmail.com>
* @date 2015
*
*/
/*
This abstracts part of a user's interaction with an account she controls.
It's not an abstraction of core Ethereum accounts data type / logic -
for that see the core processing code of blocks / txs.
Currently this is pretty much a passthrough to the KeyStore2 interface,
and accounts persistence is derived from stored keys' addresses
*/
package accounts
import (
crand "crypto/rand"
"github.com/ethereum/go-ethereum/crypto"
)
// TODO: better name for this struct?
type Account struct {
Address []byte
}
type AccountManager struct {
keyStore crypto.KeyStore2
}
// TODO: get key by addr - modify KeyStore2 GetKey to work with addr
// TODO: pass through passphrase for APIs which require access to private key?
func NewAccountManager(keyStore crypto.KeyStore2) AccountManager {
am := &AccountManager{
keyStore: keyStore,
}
return *am
}
func (am *AccountManager) Sign(fromAccount *Account, keyAuth string, toSign []byte) (signature []byte, err error) {
key, err := am.keyStore.GetKey(fromAccount.Address, keyAuth)
if err != nil {
return nil, err
}
signature, err = crypto.Sign(toSign, key.PrivateKey)
return signature, err
}
func (am AccountManager) NewAccount(auth string) (*Account, error) {
key, err := am.keyStore.GenerateNewKey(crand.Reader, auth)
if err != nil {
return nil, err
}
ua := &Account{
Address: key.Address,
}
return ua, err
}
// set of accounts == set of keys in given key store
// TODO: do we need persistence of accounts as well?
func (am *AccountManager) Accounts() ([]Account, error) {
addresses, err := am.keyStore.GetKeyAddresses()
if err != nil {
return nil, err
}
accounts := make([]Account, len(addresses))
for i, addr := range addresses {
accounts[i] = Account{
Address: addr,
}
}
return accounts, err
}

18
accounts/accounts_test.go Normal file
View File

@ -0,0 +1,18 @@
package accounts
import (
"github.com/ethereum/go-ethereum/crypto"
"testing"
)
func TestAccountManager(t *testing.T) {
ks := crypto.NewKeyStorePlain(crypto.DefaultDataDir())
am := NewAccountManager(ks)
pass := "" // not used but required by API
a1, err := am.NewAccount(pass)
toSign := crypto.GetEntropyCSPRNG(32)
_, err = am.Sign(a1, pass, toSign)
if err != nil {
t.Fatal(err)
}
}

View File

@ -133,8 +133,7 @@ func ImportPreSaleKey(keyStore KeyStore2, keyJSON []byte, password string) (*Key
if err != nil { if err != nil {
return nil, err return nil, err
} }
id := uuid.NewRandom() key.Id = uuid.NewRandom()
key.Id = &id
err = keyStore.StoreKey(key, password) err = keyStore.StoreKey(key, password)
return key, err return key, err
} }
@ -167,9 +166,10 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
ecKey := ToECDSA(ethPriv) ecKey := ToECDSA(ethPriv)
key = &Key{ key = &Key{
Id: nil, Id: nil,
Address: PubkeyToAddress(ecKey.PublicKey),
PrivateKey: ecKey, PrivateKey: ecKey,
} }
derivedAddr := ethutil.Bytes2Hex(key.Address()) derivedAddr := ethutil.Bytes2Hex(key.Address)
expectedAddr := preSaleKeyStruct.EthAddr expectedAddr := preSaleKeyStruct.EthAddr
if derivedAddr != expectedAddr { if derivedAddr != expectedAddr {
err = errors.New("decrypted addr not equal to expected addr") err = errors.New("decrypted addr not equal to expected addr")
@ -223,3 +223,8 @@ func PKCS7Unpad(in []byte) []byte {
} }
return in[:len(in)-int(padding)] return in[:len(in)-int(padding)]
} }
func PubkeyToAddress(p ecdsa.PublicKey) []byte {
pubBytes := FromECDSAPub(&p)
return Sha3(pubBytes[1:])[12:]
}

View File

@ -33,7 +33,9 @@ import (
) )
type Key struct { type Key struct {
Id *uuid.UUID // Version 4 "random" for unique id not derived from key data Id uuid.UUID // Version 4 "random" for unique id not derived from key data
// to simplify lookups we also store the address
Address []byte
// we only store privkey as pubkey/address can be derived from it // we only store privkey as pubkey/address can be derived from it
// privkey in this struct is always in plaintext // privkey in this struct is always in plaintext
PrivateKey *ecdsa.PrivateKey PrivateKey *ecdsa.PrivateKey
@ -41,6 +43,7 @@ type Key struct {
type plainKeyJSON struct { type plainKeyJSON struct {
Id []byte Id []byte
Address []byte
PrivateKey []byte PrivateKey []byte
} }
@ -51,18 +54,15 @@ type cipherJSON struct {
} }
type encryptedKeyJSON struct { type encryptedKeyJSON struct {
Id []byte Id []byte
Crypto cipherJSON Address []byte
} Crypto cipherJSON
func (k *Key) Address() []byte {
pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey)
return Sha3(pubBytes[1:])[12:]
} }
func (k *Key) MarshalJSON() (j []byte, err error) { func (k *Key) MarshalJSON() (j []byte, err error) {
jStruct := plainKeyJSON{ jStruct := plainKeyJSON{
*k.Id, k.Id,
k.Address,
FromECDSA(k.PrivateKey), FromECDSA(k.PrivateKey),
} }
j, err = json.Marshal(jStruct) j, err = json.Marshal(jStruct)
@ -78,8 +78,8 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
u := new(uuid.UUID) u := new(uuid.UUID)
*u = keyJSON.Id *u = keyJSON.Id
k.Id = u k.Id = *u
k.Address = keyJSON.Address
k.PrivateKey = ToECDSA(keyJSON.PrivateKey) k.PrivateKey = ToECDSA(keyJSON.PrivateKey)
return err return err
@ -101,7 +101,8 @@ func NewKey(rand io.Reader) *Key {
id := uuid.NewRandom() id := uuid.NewRandom()
key := &Key{ key := &Key{
Id: &id, Id: id,
Address: PubkeyToAddress(privateKeyECDSA.PublicKey),
PrivateKey: privateKeyECDSA, PrivateKey: privateKeyECDSA,
} }
return key return key

View File

@ -69,6 +69,7 @@ import (
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
crand "crypto/rand" crand "crypto/rand"
"encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"io" "io"
@ -96,21 +97,26 @@ func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *K
return GenerateNewKeyDefault(ks, rand, auth) return GenerateNewKeyDefault(ks, rand, auth)
} }
func (ks keyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { func (ks keyStorePassphrase) GetKey(keyAddr []byte, auth string) (key *Key, err error) {
keyBytes, err := DecryptKey(ks, keyId, auth) keyBytes, keyId, err := DecryptKey(ks, keyAddr, auth)
if err != nil { if err != nil {
return nil, err return nil, err
} }
key = &Key{ key = &Key{
Id: keyId, Id: uuid.UUID(keyId),
Address: keyAddr,
PrivateKey: ToECDSA(keyBytes), PrivateKey: ToECDSA(keyBytes),
} }
return key, err return key, err
} }
func (ks keyStorePassphrase) GetKeyAddresses() (addresses [][]byte, err error) {
return GetKeyAddresses(ks.keysDirPath)
}
func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) { func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
authArray := []byte(auth) authArray := []byte(auth)
salt := getEntropyCSPRNG(32) salt := GetEntropyCSPRNG(32)
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
if err != nil { if err != nil {
return err return err
@ -125,7 +131,7 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
return err return err
} }
iv := getEntropyCSPRNG(aes.BlockSize) // 16 iv := GetEntropyCSPRNG(aes.BlockSize) // 16
AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv) AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv)
cipherText := make([]byte, len(toEncrypt)) cipherText := make([]byte, len(toEncrypt))
AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt) AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt)
@ -136,7 +142,8 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
cipherText, cipherText,
} }
keyStruct := encryptedKeyJSON{ keyStruct := encryptedKeyJSON{
*key.Id, key.Id,
key.Address,
cipherStruct, cipherStruct,
} }
keyJSON, err := json.Marshal(keyStruct) keyJSON, err := json.Marshal(keyStruct)
@ -144,54 +151,53 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
return err return err
} }
return WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) return WriteKeyFile(key.Address, ks.keysDirPath, keyJSON)
} }
func (ks keyStorePassphrase) DeleteKey(keyId *uuid.UUID, auth string) (err error) { func (ks keyStorePassphrase) DeleteKey(keyAddr []byte, auth string) (err error) {
// only delete if correct passphrase is given // only delete if correct passphrase is given
_, err = DecryptKey(ks, keyId, auth) _, _, err = DecryptKey(ks, keyAddr, auth)
if err != nil { if err != nil {
return err return err
} }
keyDirPath := path.Join(ks.keysDirPath, keyId.String()) keyDirPath := path.Join(ks.keysDirPath, hex.EncodeToString(keyAddr))
return os.RemoveAll(keyDirPath) return os.RemoveAll(keyDirPath)
} }
func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes []byte, err error) { func DecryptKey(ks keyStorePassphrase, keyAddr []byte, auth string) (keyBytes []byte, keyId []byte, err error) {
fileContent, err := GetKeyFile(ks.keysDirPath, keyId) fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
keyProtected := new(encryptedKeyJSON) keyProtected := new(encryptedKeyJSON)
err = json.Unmarshal(fileContent, keyProtected) err = json.Unmarshal(fileContent, keyProtected)
keyId = keyProtected.Id
salt := keyProtected.Crypto.Salt salt := keyProtected.Crypto.Salt
iv := keyProtected.Crypto.IV iv := keyProtected.Crypto.IV
cipherText := keyProtected.Crypto.CipherText cipherText := keyProtected.Crypto.CipherText
authArray := []byte(auth) authArray := []byte(auth)
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv) plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
keyBytes = plainText[:len(plainText)-32] keyBytes = plainText[:len(plainText)-32]
keyBytesHash := plainText[len(plainText)-32:] keyBytesHash := plainText[len(plainText)-32:]
if !bytes.Equal(Sha3(keyBytes), keyBytesHash) { if !bytes.Equal(Sha3(keyBytes), keyBytesHash) {
err = errors.New("Decryption failed: checksum mismatch") err = errors.New("Decryption failed: checksum mismatch")
return nil, err return nil, nil, err
} }
return keyBytes, err return keyBytes, keyId, err
} }
func getEntropyCSPRNG(n int) []byte { func GetEntropyCSPRNG(n int) []byte {
mainBuff := make([]byte, n) mainBuff := make([]byte, n)
_, err := io.ReadFull(crand.Reader, mainBuff) _, err := io.ReadFull(crand.Reader, mainBuff)
if err != nil { if err != nil {

View File

@ -24,7 +24,7 @@
package crypto package crypto
import ( import (
"code.google.com/p/go-uuid/uuid" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -38,9 +38,10 @@ import (
type KeyStore2 interface { type KeyStore2 interface {
// create new key using io.Reader entropy source and optionally using auth string // create new key using io.Reader entropy source and optionally using auth string
GenerateNewKey(io.Reader, string) (*Key, error) GenerateNewKey(io.Reader, string) (*Key, error)
GetKey(*uuid.UUID, string) (*Key, error) // key from id and auth string GetKey([]byte, string) (*Key, error) // key from addr and auth string
StoreKey(*Key, string) error // store key optionally using auth string GetKeyAddresses() ([][]byte, error) // get all addresses
DeleteKey(*uuid.UUID, string) error // delete key by id and auth string StoreKey(*Key, string) error // store key optionally using auth string
DeleteKey([]byte, string) error // delete key by addr and auth string
} }
type keyStorePlain struct { type keyStorePlain struct {
@ -72,8 +73,8 @@ func GenerateNewKeyDefault(ks KeyStore2, rand io.Reader, auth string) (key *Key,
return key, err return key, err
} }
func (ks keyStorePlain) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { func (ks keyStorePlain) GetKey(keyAddr []byte, auth string) (key *Key, err error) {
fileContent, err := GetKeyFile(ks.keysDirPath, keyId) fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -83,32 +84,50 @@ func (ks keyStorePlain) GetKey(keyId *uuid.UUID, auth string) (key *Key, err err
return key, err return key, err
} }
func (ks keyStorePlain) GetKeyAddresses() (addresses [][]byte, err error) {
return GetKeyAddresses(ks.keysDirPath)
}
func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) { func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) {
keyJSON, err := json.Marshal(key) keyJSON, err := json.Marshal(key)
if err != nil { if err != nil {
return err return err
} }
err = WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) err = WriteKeyFile(key.Address, ks.keysDirPath, keyJSON)
return err return err
} }
func (ks keyStorePlain) DeleteKey(keyId *uuid.UUID, auth string) (err error) { func (ks keyStorePlain) DeleteKey(keyAddr []byte, auth string) (err error) {
keyDirPath := path.Join(ks.keysDirPath, keyId.String()) keyDirPath := path.Join(ks.keysDirPath, hex.EncodeToString(keyAddr))
err = os.RemoveAll(keyDirPath) err = os.RemoveAll(keyDirPath)
return err return err
} }
func GetKeyFile(keysDirPath string, keyId *uuid.UUID) (fileContent []byte, err error) { func GetKeyFile(keysDirPath string, keyAddr []byte) (fileContent []byte, err error) {
id := keyId.String() fileName := hex.EncodeToString(keyAddr)
return ioutil.ReadFile(path.Join(keysDirPath, id, id)) return ioutil.ReadFile(path.Join(keysDirPath, fileName, fileName))
} }
func WriteKeyFile(id string, keysDirPath string, content []byte) (err error) { func WriteKeyFile(addr []byte, keysDirPath string, content []byte) (err error) {
keyDirPath := path.Join(keysDirPath, id) addrHex := hex.EncodeToString(addr)
keyFilePath := path.Join(keyDirPath, id) keyDirPath := path.Join(keysDirPath, addrHex)
keyFilePath := path.Join(keyDirPath, addrHex)
err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user
if err != nil { if err != nil {
return err return err
} }
return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user
} }
func GetKeyAddresses(keysDirPath string) (addresses [][]byte, err error) {
fileInfos, err := ioutil.ReadDir(keysDirPath)
if err != nil {
return nil, err
}
addresses = make([][]byte, len(fileInfos))
for i, fileInfo := range fileInfos {
addresses[i] = make([]byte, 40)
addresses[i] = []byte(fileInfo.Name())
}
return addresses, err
}

View File

@ -15,12 +15,12 @@ func TestKeyStorePlain(t *testing.T) {
} }
k2 := new(Key) k2 := new(Key)
k2, err = ks.GetKey(k1.Id, pass) k2, err = ks.GetKey(k1.Address, pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(k1.Id, k2.Id) { if !reflect.DeepEqual(k1.Address, k2.Address) {
t.Fatal(err) t.Fatal(err)
} }
@ -28,7 +28,7 @@ func TestKeyStorePlain(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
err = ks.DeleteKey(k2.Id, pass) err = ks.DeleteKey(k2.Address, pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -42,11 +42,11 @@ func TestKeyStorePassphrase(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
k2 := new(Key) k2 := new(Key)
k2, err = ks.GetKey(k1.Id, pass) k2, err = ks.GetKey(k1.Address, pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(k1.Id, k2.Id) { if !reflect.DeepEqual(k1.Address, k2.Address) {
t.Fatal(err) t.Fatal(err)
} }
@ -54,7 +54,7 @@ func TestKeyStorePassphrase(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
err = ks.DeleteKey(k2.Id, pass) // also to clean up created files err = ks.DeleteKey(k2.Address, pass) // also to clean up created files
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -68,17 +68,17 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
_, err = ks.GetKey(k1.Id, "bar") // wrong passphrase _, err = ks.GetKey(k1.Address, "bar") // wrong passphrase
if err == nil { if err == nil {
t.Fatal(err) t.Fatal(err)
} }
err = ks.DeleteKey(k1.Id, "bar") // wrong passphrase err = ks.DeleteKey(k1.Address, "bar") // wrong passphrase
if err == nil { if err == nil {
t.Fatal(err) t.Fatal(err)
} }
err = ks.DeleteKey(k1.Id, pass) // to clean up err = ks.DeleteKey(k1.Address, pass) // to clean up
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -15,7 +15,7 @@ if ls $dir/*.go &> /dev/null; then
# echo $dir # echo $dir
if [[ $dir != "./tests/vm" ]] if [[ $dir != "./tests/vm" ]]
then then
go test -covermode=count -coverprofile=$dir/profile.tmp $dir $GOROOT/bin/go test -covermode=count -coverprofile=$dir/profile.tmp $dir
fi fi
if [ -f $dir/profile.tmp ] if [ -f $dir/profile.tmp ]
then then
@ -25,7 +25,7 @@ if ls $dir/*.go &> /dev/null; then
fi fi
done done
go tool cover -func profile.cov $GOROOT/bin/go tool cover -func profile.cov
# To submit the test coverage result to coveralls.io, # To submit the test coverage result to coveralls.io,
# use goveralls (https://github.com/mattn/goveralls) # use goveralls (https://github.com/mattn/goveralls)