From 18580e152c1a2480b6245ebba4c62c202ed20ac6 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Tue, 19 Apr 2016 18:17:44 +0200 Subject: [PATCH 01/22] VERSION, cmd/geth: bumped version --- VERSION | 2 +- cmd/geth/main.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 88c5fb891d..bc80560fad 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.0 +1.5.0 diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 512a5f1839..6ab4ed45bf 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -45,9 +45,9 @@ import ( const ( ClientIdentifier = "Geth" - Version = "1.4.0-unstable" + Version = "1.5.0-unstable" VersionMajor = 1 - VersionMinor = 4 + VersionMinor = 5 VersionPatch = 0 ) From 5127ec10cb84a615f4d5b314e4c3c102efefe4c9 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Thu, 7 Apr 2016 11:39:22 +0200 Subject: [PATCH 02/22] accouns/abi: refactored ABI package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactored the abi package parsing and type handling. Relying mostly on package reflect as opposed to most of our own type reflection. Our own type reflection is still used however for cases such as Bytes and FixedBytes (abi: bytes•). This also inclused several fixes for slice handling of arbitrary and fixed size for all supported types. This also further removes implicit type casting such as assigning, for example `[2]T{} = []T{1}` will fail, however `[2]T{} == []T{1, 2}` (notice assigning *slice* to fixed size *array*). Assigning arrays to slices will always succeed if they are of the same element type. Incidentally also fixes #2379 --- accounts/abi/abi.go | 57 ++----- accounts/abi/abi_test.go | 318 ++++++++++++++++++++++----------------- accounts/abi/error.go | 80 ++++++++++ accounts/abi/method.go | 39 +++++ accounts/abi/numbers.go | 4 +- accounts/abi/packing.go | 65 ++++++++ accounts/abi/reflect.go | 64 ++++++++ accounts/abi/type.go | 178 +++++++--------------- 8 files changed, 491 insertions(+), 314 deletions(-) create mode 100644 accounts/abi/error.go create mode 100644 accounts/abi/packing.go create mode 100644 accounts/abi/reflect.go diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 9ef7c0f0d3..82ea4a0d5c 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -48,42 +48,6 @@ func JSON(reader io.Reader) (ABI, error) { return abi, nil } -// 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(method Method, args ...interface{}) ([]byte, error) { - // variable input is the output appended at the end of packed - // output. This is used for strings and bytes types input. - var variableInput []byte - - var ret []byte - for i, a := range args { - input := method.Inputs[i] - // pack the input - packed, err := input.Type.pack(a) - if err != nil { - return nil, fmt.Errorf("`%s` %v", method.Name, err) - } - - // check for a slice type (string, bytes, slice) - if input.Type.T == StringTy || input.Type.T == BytesTy || input.Type.IsSlice { - // calculate the offset - offset := len(method.Inputs)*32 + len(variableInput) - // set the offset - ret = append(ret, packNum(reflect.ValueOf(offset), UintTy)...) - // Append the packed output to the variable input. The variable input - // will be appended at the end of the input. - variableInput = append(variableInput, packed...) - } else { - // append the packed value to the input - ret = append(ret, packed...) - } - } - // append the variable input at the end of the packed input - ret = append(ret, variableInput...) - - 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. @@ -102,11 +66,7 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { } method = m } - // Make sure arguments match up and pack them - if len(args) != len(method.Inputs) { - return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Inputs)) - } - arguments, err := abi.pack(method, args...) + arguments, err := method.pack(method, args...) if err != nil { return nil, err } @@ -126,18 +86,21 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { if index+32 > len(output) { return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), index+32) } + elem := t.Type.Elem // first we need to create a slice of the type var refSlice reflect.Value - switch t.Type.T { + switch elem.T { case IntTy, UintTy, BoolTy: // int, uint, bool can all be of type big int. refSlice = reflect.ValueOf([]*big.Int(nil)) case AddressTy: // address must be of slice Address refSlice = reflect.ValueOf([]common.Address(nil)) case HashTy: // hash must be of slice hash refSlice = reflect.ValueOf([]common.Hash(nil)) + case FixedBytesTy: + refSlice = reflect.ValueOf([]byte(nil)) default: // no other types are supported - return nil, fmt.Errorf("abi: unsupported slice type %v", t.Type.T) + return nil, fmt.Errorf("abi: unsupported slice type %v", elem.T) } // get the offset which determines the start of this array ... offset := int(common.BytesToBig(output[index : index+32]).Uint64()) @@ -164,7 +127,7 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { ) // set inter to the correct type (cast) - switch t.Type.T { + switch elem.T { case IntTy, UintTy: inter = common.BytesToBig(returnOutput) case BoolTy: @@ -186,7 +149,7 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { // argument in T. func toGoType(i int, t Argument, output []byte) (interface{}, error) { // we need to treat slices differently - if t.Type.IsSlice { + if (t.Type.IsSlice || t.Type.IsArray) && t.Type.T != BytesTy && t.Type.T != StringTy && t.Type.T != FixedBytesTy { return toGoSlice(i, t, output) } @@ -328,8 +291,8 @@ func set(dst, src reflect.Value, output Argument) error { return fmt.Errorf("abi: cannot unmarshal %v in to array of elem %v", src.Type(), dstType.Elem()) } - if dst.Len() < output.Type.Size { - return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.Size, dst.Len()) + if dst.Len() < output.Type.SliceSize { + return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.SliceSize, dst.Len()) } reflect.Copy(dst, src) default: diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index a1b3e62d95..323718a72b 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -29,6 +29,180 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) +// formatSilceOutput add padding to the value and adds a size +func formatSliceOutput(v ...[]byte) []byte { + off := common.LeftPadBytes(big.NewInt(int64(len(v))).Bytes(), 32) + output := append(off, make([]byte, 0, len(v)*32)...) + + for _, value := range v { + output = append(output, common.LeftPadBytes(value, 32)...) + } + return output +} + +// quick helper padding +func pad(input []byte, size int, left bool) []byte { + if left { + return common.LeftPadBytes(input, size) + } + return common.RightPadBytes(input, size) +} + +func TestTypeCheck(t *testing.T) { + for i, test := range []struct { + typ string + input interface{} + err error + }{ + {"uint", big.NewInt(1), nil}, + {"int", big.NewInt(1), nil}, + {"uint30", big.NewInt(1), nil}, + {"uint30", uint8(1), varErr(reflect.Ptr, reflect.Uint8)}, + {"uint16", uint16(1), nil}, + {"uint16", uint8(1), varErr(reflect.Uint16, reflect.Uint8)}, + {"uint16[]", []uint16{1, 2, 3}, nil}, + {"uint16[]", [3]uint16{1, 2, 3}, nil}, + {"uint16[]", []uint32{1, 2, 3}, typeErr(formatSliceString(reflect.Uint16, -1), formatSliceString(reflect.Uint32, -1))}, + {"uint16[3]", [3]uint32{1, 2, 3}, typeErr(formatSliceString(reflect.Uint16, 3), formatSliceString(reflect.Uint32, 3))}, + {"uint16[3]", [4]uint16{1, 2, 3}, typeErr(formatSliceString(reflect.Uint16, 3), formatSliceString(reflect.Uint16, 4))}, + {"uint16[3]", []uint16{1, 2, 3}, nil}, + {"uint16[3]", []uint16{1, 2, 3, 4}, typeErr(formatSliceString(reflect.Uint16, 3), formatSliceString(reflect.Uint16, 4))}, + {"address[]", []common.Address{common.Address{1}}, nil}, + {"address[1]", []common.Address{common.Address{1}}, nil}, + {"address[1]", [1]common.Address{common.Address{1}}, nil}, + {"address[2]", [1]common.Address{common.Address{1}}, typeErr(formatSliceString(reflect.Array, 2), formatSliceString(reflect.Array, 1))}, + {"bytes32", [32]byte{}, nil}, + {"bytes32", [33]byte{}, typeErr(formatSliceString(reflect.Uint8, 32), formatSliceString(reflect.Uint8, 33))}, + {"bytes32", common.Hash{1}, nil}, + {"bytes31", [31]byte{}, nil}, + {"bytes31", [32]byte{}, typeErr(formatSliceString(reflect.Uint8, 31), formatSliceString(reflect.Uint8, 32))}, + {"bytes", []byte{0, 1}, nil}, + {"bytes", [2]byte{0, 1}, nil}, + {"bytes", common.Hash{1}, nil}, + {"string", "hello world", nil}, + {"bytes32[]", [][32]byte{[32]byte{}}, nil}, + } { + typ, err := NewType(test.typ) + if err != nil { + t.Fatal("unexpected parse error:", err) + } + + err = typeCheck(typ, reflect.ValueOf(test.input)) + if err != nil && test.err == nil { + t.Errorf("%d failed. Expected no err but got: %v", i, err) + continue + } + if err == nil && test.err != nil { + t.Errorf("%d failed. Expected err: %v but got none", i, test.err) + continue + } + + if err != nil && test.err != nil && err.Error() != test.err.Error() { + t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err) + } + } +} + +func TestPack(t *testing.T) { + for i, test := range []struct { + typ string + + input interface{} + output []byte + }{ + {"uint16", uint16(2), pad([]byte{2}, 32, true)}, + {"uint16[]", []uint16{1, 2}, formatSliceOutput([]byte{1}, []byte{2})}, + {"uint256[]", []*big.Int{big.NewInt(1), big.NewInt(2)}, formatSliceOutput([]byte{1}, []byte{2})}, + {"address[]", []common.Address{common.Address{1}, common.Address{2}}, formatSliceOutput(pad([]byte{1}, 20, false), pad([]byte{2}, 20, false))}, + {"bytes32[]", []common.Hash{common.Hash{1}, common.Hash{2}}, formatSliceOutput(pad([]byte{1}, 32, false), pad([]byte{2}, 32, false))}, + } { + typ, err := NewType(test.typ) + if err != nil { + t.Fatal("unexpected parse error:", err) + } + + output, err := typ.pack(reflect.ValueOf(test.input)) + if err != nil { + t.Fatal("unexpected pack error:", err) + } + + if !bytes.Equal(output, test.output) { + t.Errorf("%d failed. Expected bytes: '%x' Got: '%x'", i, test.output, output) + } + } +} + +func TestMethodPack(t *testing.T) { + abi, err := JSON(strings.NewReader(jsondata2)) + if err != nil { + t.Fatal(err) + } + + sig := abi.Methods["slice"].Id() + sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) + + packed, err := abi.Pack("slice", []uint32{1, 2}) + if err != nil { + t.Error(err) + } + + if !bytes.Equal(packed, sig) { + t.Errorf("expected %x got %x", sig, packed) + } + + var addrA, addrB = common.Address{1}, common.Address{2} + sig = abi.Methods["sliceAddress"].Id() + sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) + sig = append(sig, common.LeftPadBytes(addrA[:], 32)...) + sig = append(sig, common.LeftPadBytes(addrB[:], 32)...) + + packed, err = abi.Pack("sliceAddress", []common.Address{addrA, addrB}) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(packed, sig) { + t.Errorf("expected %x got %x", sig, packed) + } + + var addrC, addrD = common.Address{3}, common.Address{4} + sig = abi.Methods["sliceMultiAddress"].Id() + sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) + sig = append(sig, common.LeftPadBytes(addrA[:], 32)...) + sig = append(sig, common.LeftPadBytes(addrB[:], 32)...) + sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) + sig = append(sig, common.LeftPadBytes(addrC[:], 32)...) + sig = append(sig, common.LeftPadBytes(addrD[:], 32)...) + + packed, err = abi.Pack("sliceMultiAddress", []common.Address{addrA, addrB}, []common.Address{addrC, addrD}) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(packed, sig) { + t.Errorf("expected %x got %x", sig, packed) + } + + sig = abi.Methods["slice256"].Id() + sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) + + packed, err = abi.Pack("slice256", []*big.Int{big.NewInt(1), big.NewInt(2)}) + if err != nil { + t.Error(err) + } + + if !bytes.Equal(packed, sig) { + t.Errorf("expected %x got %x", sig, packed) + } +} + const jsondata = ` [ { "type" : "function", "name" : "balance", "const" : true }, @@ -43,7 +217,6 @@ const jsondata2 = ` { "type" : "function", "name" : "string", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "string" } ] }, { "type" : "function", "name" : "bool", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" } ] }, { "type" : "function", "name" : "address", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "address" } ] }, - { "type" : "function", "name" : "string32", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "string32" } ] }, { "type" : "function", "name" : "uint64[2]", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] }, { "type" : "function", "name" : "uint64[]", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] }, { "type" : "function", "name" : "foo", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] }, @@ -54,41 +227,6 @@ const jsondata2 = ` { "type" : "function", "name" : "sliceMultiAddress", "const" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] } ]` -func TestType(t *testing.T) { - typ, err := NewType("uint32") - if err != nil { - t.Error(err) - } - if typ.Kind != reflect.Uint { - t.Error("expected uint32 to have kind Ptr") - } - - typ, err = NewType("uint32[]") - if err != nil { - t.Error(err) - } - if !typ.IsSlice { - t.Error("expected uint32[] to be slice") - } - if typ.Type != ubig_t { - t.Error("expcted uith32[] to have type uint64") - } - - typ, err = NewType("uint32[2]") - if err != nil { - t.Error(err) - } - if !typ.IsSlice { - t.Error("expected uint32[2] to be slice") - } - if typ.Type != ubig_t { - t.Error("expcted uith32[2] to have type uint64") - } - if typ.SliceSize != 2 { - t.Error("expected uint32[2] to have a size of 2") - } -} - func TestReader(t *testing.T) { Uint256, _ := NewType("uint256") exp := ABI{ @@ -164,21 +302,6 @@ func TestTestString(t *testing.T) { 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) { @@ -210,26 +333,10 @@ func TestTestSlice(t *testing.T) { } } -func TestImplicitTypeCasts(t *testing.T) { - abi, err := JSON(strings.NewReader(jsondata2)) - if err != nil { - t.Error(err) - t.FailNow() - } - - slice := make([]uint8, 2) - _, err = abi.Pack("uint64[2]", slice) - expStr := "`uint64[2]` abi: cannot use type uint8 as type uint64" - if err.Error() != expStr { - t.Errorf("expected %v, got %v", expStr, err) - } -} - func TestMethodSignature(t *testing.T) { String, _ := NewType("string") - String32, _ := NewType("string32") - m := Method{"foo", false, []Argument{Argument{"bar", String32, false}, Argument{"baz", String, false}}, nil} - exp := "foo(string32,string)" + m := Method{"foo", false, []Argument{Argument{"bar", String, false}, Argument{"baz", String, false}}, nil} + exp := "foo(string,string)" if m.Sig() != exp { t.Error("signature mismatch", exp, "!=", m.Sig()) } @@ -247,7 +354,7 @@ func TestMethodSignature(t *testing.T) { } } -func TestPack(t *testing.T) { +func TestOldPack(t *testing.T) { abi, err := JSON(strings.NewReader(jsondata2)) if err != nil { t.Error(err) @@ -292,77 +399,6 @@ func TestMultiPack(t *testing.T) { } } -func TestPackSlice(t *testing.T) { - abi, err := JSON(strings.NewReader(jsondata2)) - if err != nil { - t.Error(err) - t.FailNow() - } - - sig := crypto.Keccak256([]byte("slice(uint32[2])"))[:4] - sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...) - sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) - sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) - sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) - - packed, err := abi.Pack("slice", []uint32{1, 2}) - if err != nil { - t.Error(err) - } - - if !bytes.Equal(packed, sig) { - t.Errorf("expected %x got %x", sig, packed) - } - - var addrA, addrB = common.Address{1}, common.Address{2} - sig = abi.Methods["sliceAddress"].Id() - sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...) - sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) - sig = append(sig, common.LeftPadBytes(addrA[:], 32)...) - sig = append(sig, common.LeftPadBytes(addrB[:], 32)...) - - packed, err = abi.Pack("sliceAddress", []common.Address{addrA, addrB}) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(packed, sig) { - t.Errorf("expected %x got %x", sig, packed) - } - - var addrC, addrD = common.Address{3}, common.Address{4} - sig = abi.Methods["sliceMultiAddress"].Id() - sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...) - sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...) - sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) - sig = append(sig, common.LeftPadBytes(addrA[:], 32)...) - sig = append(sig, common.LeftPadBytes(addrB[:], 32)...) - sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) - sig = append(sig, common.LeftPadBytes(addrC[:], 32)...) - sig = append(sig, common.LeftPadBytes(addrD[:], 32)...) - - packed, err = abi.Pack("sliceMultiAddress", []common.Address{addrA, addrB}, []common.Address{addrC, addrD}) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(packed, sig) { - t.Errorf("expected %x got %x", sig, packed) - } - - sig = crypto.Keccak256([]byte("slice256(uint256[2])"))[:4] - sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...) - sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) - sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) - sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) - - packed, err = abi.Pack("slice256", []*big.Int{big.NewInt(1), big.NewInt(2)}) - if err != nil { - t.Error(err) - } - - if !bytes.Equal(packed, sig) { - t.Errorf("expected %x got %x", sig, packed) - } -} func ExampleJSON() { const definition = `[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isBar","outputs":[{"name":"","type":"bool"}],"type":"function"}]` diff --git a/accounts/abi/error.go b/accounts/abi/error.go new file mode 100644 index 0000000000..91a0374fdd --- /dev/null +++ b/accounts/abi/error.go @@ -0,0 +1,80 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "fmt" + "reflect" +) + +// formatSliceString formats the reflection kind with the given slice size +// and returns a formatted string representation. +func formatSliceString(kind reflect.Kind, sliceSize int) string { + if sliceSize == -1 { + return fmt.Sprintf("[]%v", kind) + } + return fmt.Sprintf("[%d]%v", sliceSize, kind) +} + +// sliceTypeCheck checks that the given slice can by assigned to the reflection +// type in t. +func sliceTypeCheck(t Type, val reflect.Value) error { + if !(val.Kind() == reflect.Slice || val.Kind() == reflect.Array) { + return typeErr(formatSliceString(t.Kind, t.SliceSize), val.Type()) + } + if t.IsArray && val.Len() != t.SliceSize { + return typeErr(formatSliceString(t.Elem.Kind, t.SliceSize), formatSliceString(val.Type().Elem().Kind(), val.Len())) + } + + if t.Elem.IsSlice { + if val.Len() > 0 { + return sliceTypeCheck(*t.Elem, val.Index(0)) + } + } else if t.Elem.IsArray { + return sliceTypeCheck(*t.Elem, val.Index(0)) + } + + elemKind := val.Type().Elem().Kind() + if elemKind != t.Elem.Kind { + return typeErr(formatSliceString(t.Elem.Kind, t.SliceSize), val.Type()) + } + return nil +} + +// typeCheck checks that thet given reflection val can be assigned to the reflection +// type in t. +func typeCheck(t Type, value reflect.Value) error { + if t.IsSlice || t.IsArray { + return sliceTypeCheck(t, value) + } + + // Check base type validity. Element types will be checked later on. + if t.Kind != value.Kind() { + return typeErr(t.Kind, value.Kind()) + } + return nil +} + +// varErr returns a formatted error. +func varErr(expected, got reflect.Kind) error { + return typeErr(expected, got) +} + +// typeErr returns a formatted type casting error. +func typeErr(expected, got interface{}) error { + return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected) +} diff --git a/accounts/abi/method.go b/accounts/abi/method.go index 206c7d408b..cad0cd27ff 100644 --- a/accounts/abi/method.go +++ b/accounts/abi/method.go @@ -18,6 +18,7 @@ package abi import ( "fmt" + "reflect" "strings" "github.com/ethereum/go-ethereum/crypto" @@ -38,6 +39,44 @@ type Method struct { Outputs []Argument } +func (m Method) pack(method Method, args ...interface{}) ([]byte, error) { + // Make sure arguments match up and pack them + if len(args) != len(method.Inputs) { + return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Inputs)) + } + // variable input is the output appended at the end of packed + // output. This is used for strings and bytes types input. + var variableInput []byte + + var ret []byte + for i, a := range args { + input := method.Inputs[i] + // pack the input + packed, err := input.Type.pack(reflect.ValueOf(a)) + if err != nil { + return nil, fmt.Errorf("`%s` %v", method.Name, err) + } + + // check for a slice type (string, bytes, slice) + if input.Type.T == StringTy || input.Type.T == BytesTy || input.Type.IsSlice || input.Type.IsArray { + // calculate the offset + offset := len(method.Inputs)*32 + len(variableInput) + // set the offset + ret = append(ret, packNum(reflect.ValueOf(offset), UintTy)...) + // Append the packed output to the variable input. The variable input + // will be appended at the end of the input. + variableInput = append(variableInput, packed...) + } else { + // append the packed value to the input + ret = append(ret, packed...) + } + } + // append the variable input at the end of the packed input + ret = append(ret, variableInput...) + + return ret, nil +} + // Sig returns the methods string signature according to the ABI spec. // // Example diff --git a/accounts/abi/numbers.go b/accounts/abi/numbers.go index 084701de5b..5a31cf2b5b 100644 --- a/accounts/abi/numbers.go +++ b/accounts/abi/numbers.go @@ -24,8 +24,8 @@ import ( ) var ( - big_t = reflect.TypeOf(&big.Int{}) - ubig_t = reflect.TypeOf(&big.Int{}) + big_t = reflect.TypeOf(big.Int{}) + ubig_t = reflect.TypeOf(big.Int{}) byte_t = reflect.TypeOf(byte(0)) byte_ts = reflect.TypeOf([]byte(nil)) uint_t = reflect.TypeOf(uint(0)) diff --git a/accounts/abi/packing.go b/accounts/abi/packing.go new file mode 100644 index 0000000000..2a16d3be1c --- /dev/null +++ b/accounts/abi/packing.go @@ -0,0 +1,65 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "reflect" + + "github.com/ethereum/go-ethereum/common" +) + +// packBytesSlice packs the given bytes as [L, V] as the canonical representation +// bytes slice +func packBytesSlice(bytes []byte, l int) []byte { + len := packNum(reflect.ValueOf(l), UintTy) + return append(len, common.RightPadBytes(bytes, (l+31)/32*32)...) +} + +// packElement packs the given reflect value according to the abi specification in +// t. +func packElement(t Type, reflectValue reflect.Value) []byte { + switch t.T { + case IntTy, UintTy: + return packNum(reflectValue, t.T) + case StringTy: + return packBytesSlice([]byte(reflectValue.String()), reflectValue.Len()) + case AddressTy: + if reflectValue.Kind() == reflect.Array { + reflectValue = mustArrayToByteSlice(reflectValue) + } + + return common.LeftPadBytes(reflectValue.Bytes(), 32) + case BoolTy: + if reflectValue.Bool() { + return common.LeftPadBytes(common.Big1.Bytes(), 32) + } else { + return common.LeftPadBytes(common.Big0.Bytes(), 32) + } + case BytesTy: + if reflectValue.Kind() == reflect.Array { + reflectValue = mustArrayToByteSlice(reflectValue) + } + return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()) + case FixedBytesTy: + if reflectValue.Kind() == reflect.Array { + reflectValue = mustArrayToByteSlice(reflectValue) + } + + return common.LeftPadBytes(reflectValue.Bytes(), 32) + } + panic("abi: fatal error") +} diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go new file mode 100644 index 0000000000..780c64c66e --- /dev/null +++ b/accounts/abi/reflect.go @@ -0,0 +1,64 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import "reflect" + +// indirect recursively dereferences the value until it either gets the value +// or finds a big.Int +func indirect(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Ptr && v.Elem().Type() != big_t { + return indirect(v.Elem()) + } + return v +} + +// reflectIntKind returns the reflect using the given size and +// unsignedness. +func reflectIntKind(unsigned bool, size int) reflect.Kind { + switch size { + case 8: + if unsigned { + return reflect.Uint8 + } + return reflect.Int8 + case 16: + if unsigned { + return reflect.Uint16 + } + return reflect.Int16 + case 32: + if unsigned { + return reflect.Uint32 + } + return reflect.Int32 + case 64: + if unsigned { + return reflect.Uint64 + } + return reflect.Int64 + } + return reflect.Ptr +} + +// mustArrayToBytesSlice creates a new byte slice with the exact same size as value +// and copies the bytes in value to the new slice. +func mustArrayToByteSlice(value reflect.Value) reflect.Value { + slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len()) + reflect.Copy(slice, value) + return slice +} diff --git a/accounts/abi/type.go b/accounts/abi/type.go index 5a5a5ac49e..cad7b82123 100644 --- a/accounts/abi/type.go +++ b/accounts/abi/type.go @@ -21,8 +21,6 @@ import ( "reflect" "regexp" "strconv" - - "github.com/ethereum/go-ethereum/common" ) const ( @@ -40,53 +38,59 @@ const ( // Type is the reflection of the supported argument type type Type struct { - IsSlice bool - SliceSize int + IsSlice, IsArray bool + SliceSize int + + Elem *Type + + Kind reflect.Kind + Type reflect.Type + Size int + T byte // Our own type checking - Kind reflect.Kind - Type reflect.Type - Size int - T byte // Our own type checking stringKind string // holds the unparsed string for deriving signatures } var ( + // fullTypeRegex parses the abi types + // + // Types 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] fullTypeRegex = regexp.MustCompile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?") - typeRegex = regexp.MustCompile("([a-zA-Z]+)([0-9]*)?") + // typeRegex parses the abi sub types + typeRegex = regexp.MustCompile("([a-zA-Z]+)([0-9]*)?") ) -// NewType 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] +// NewType creates a new reflection type of abi type given in t. func NewType(t string) (typ Type, err error) { - // 1. full string 2. type 3. (opt.) is slice 4. (opt.) size - // parse the full representation of the abi-type definition; including: - // * full string - // * type - // * is slice - // * slice size res := fullTypeRegex.FindAllStringSubmatch(t, -1)[0] - // check if type is slice and parse type. switch { case res[3] != "": // err is ignored. Already checked for number through the regexp typ.SliceSize, _ = strconv.Atoi(res[3]) - typ.IsSlice = true + typ.IsArray = true case res[2] != "": typ.IsSlice, typ.SliceSize = true, -1 case res[0] == "": return Type{}, fmt.Errorf("abi: type parse error: %s", t) } + if typ.IsArray || typ.IsSlice { + sliceType, err := NewType(res[1]) + if err != nil { + return Type{}, err + } + typ.Elem = &sliceType + return typ, nil + } // parse the type and size of the abi-type. parsedType := typeRegex.FindAllStringSubmatch(res[1], -1)[0] @@ -109,21 +113,20 @@ func NewType(t string) (typ Type, err error) { switch varType { case "int": - typ.Kind = reflect.Int + typ.Kind = reflectIntKind(false, varSize) typ.Type = big_t typ.Size = varSize typ.T = IntTy case "uint": - typ.Kind = reflect.Uint + typ.Kind = reflectIntKind(true, varSize) typ.Type = ubig_t typ.Size = varSize typ.T = UintTy case "bool": typ.Kind = reflect.Bool typ.T = BoolTy - case "real": // TODO - typ.Kind = reflect.Invalid case "address": + typ.Kind = reflect.Array typ.Type = address_t typ.Size = 20 typ.T = AddressTy @@ -131,22 +134,17 @@ func NewType(t string) (typ Type, err error) { typ.Kind = reflect.String typ.Size = -1 typ.T = StringTy - if varSize > 0 { - typ.Size = 32 - } - case "hash": - typ.Kind = reflect.Array - typ.Size = 32 - typ.Type = hash_t - typ.T = HashTy case "bytes": - typ.Kind = reflect.Array - typ.Type = byte_ts - typ.Size = varSize + sliceType, _ := NewType("uint8") + typ.Elem = &sliceType if varSize == 0 { + typ.IsSlice = true typ.T = BytesTy + typ.SliceSize = -1 } else { + typ.IsArray = true typ.T = FixedBytesTy + typ.SliceSize = varSize } default: return Type{}, fmt.Errorf("unsupported arg type: %s", t) @@ -156,98 +154,30 @@ func NewType(t string) (typ Type, err error) { return } +// String implements Stringer func (t Type) String() (out string) { return t.stringKind } -// packBytesSlice packs the given bytes as [L, V] as the canonical representation -// bytes slice -func packBytesSlice(bytes []byte, l int) []byte { - len := packNum(reflect.ValueOf(l), UintTy) - return append(len, common.RightPadBytes(bytes, (l+31)/32*32)...) -} +func (t Type) pack(v reflect.Value) ([]byte, error) { + // dereference pointer first if it's a pointer + v = indirect(v) -// 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: - // check input is unsigned - if t.Type != ubig_t { - return nil, fmt.Errorf("abi: type mismatch: %s for %T", t.Type, v) - } - - // no implicit type casting - if int(value.Type().Size()*8) != t.Size { - return nil, fmt.Errorf("abi: cannot use type %T as type uint%d", v, t.Size) - } - - 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) - } - - // no implicit type casting - if int(value.Type().Size()*8) != t.Size { - return nil, fmt.Errorf("abi: cannot use type %T as type uint%d", v, t.Size) - } - 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 packBytesSlice([]byte(value.String()), value.Len()), nil - case reflect.Slice: - // Byte slice is a special case, it gets treated as a single value - if t.T == BytesTy { - return packBytesSlice(value.Bytes(), value.Len()), nil - } - - if t.SliceSize > -1 && value.Len() > t.SliceSize { - return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size) - } - - // Signed / Unsigned check - if value.Type() == big_t && (t.T != IntTy && isSigned(value)) || (t.T == UintTy && isSigned(value)) { - return nil, fmt.Errorf("slice of incompatible types.") - } + if err := typeCheck(t, v); err != nil { + return nil, err + } + if (t.IsSlice || t.IsArray) && t.T != BytesTy && t.T != FixedBytesTy { var packed []byte - for i := 0; i < value.Len(); i++ { - val, err := t.pack(value.Index(i).Interface()) + for i := 0; i < v.Len(); i++ { + val, err := t.Elem.pack(v.Index(i)) if err != nil { return nil, err } packed = append(packed, val...) } - return packBytesSlice(packed, value.Len()), nil - case reflect.Bool: - if value.Bool() { - return common.LeftPadBytes(common.Big1.Bytes(), 32), nil - } else { - return common.LeftPadBytes(common.Big0.Bytes(), 32), nil - } - case reflect.Array: - if v, ok := value.Interface().(common.Address); ok { - return common.LeftPadBytes(v[:], 32), nil - } else if v, ok := value.Interface().(common.Hash); ok { - return v[:], nil - } + return packBytesSlice(packed, v.Len()), nil } - return nil, fmt.Errorf("ABI: bad input given %v", value.Kind()) + return packElement(t, v), nil } From 87ae0df476cf6b413795ee54207e8ec86e178dbc Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 20 Apr 2016 23:17:00 +0200 Subject: [PATCH 03/22] cmd/geth, jsre: improve the js command geth js stopped the JS runtime after running the first input file and blocked for pending callbacks. This commit makes it process all files and enables quitting with Ctrl-C regardless of callbacks. Error reporting is also improved. If a script fails to load, the error is printed and includes the backtrace. package jsre now ensures that otto is aware of the filename, the backtrace will contain them. Before: $ geth js bad.js; echo "exit $?" ... log messages ... exit 0 After: $ geth js bad.js; echo "exit $?" ... log messages ... Fatal: JavaScript Error: Invalid number of input parameters at web3.js:3109:20 at web3.js:4917:15 at web3.js:4960:5 at web3.js:4984:23 at checkWork (bad.js:11:9) at bad.js:19:1 exit 1 --- cmd/geth/js.go | 16 +++++++--------- cmd/geth/main.go | 22 +++++++++++++++++++--- jsre/jsre.go | 9 ++++++++- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/cmd/geth/js.go b/cmd/geth/js.go index 767b513c1c..2b64303b2b 100644 --- a/cmd/geth/js.go +++ b/cmd/geth/js.go @@ -123,7 +123,7 @@ func (self *jsre) batch(statement string) { err := self.re.EvalAndPrettyPrint(statement) if err != nil { - fmt.Printf("error: %v", err) + fmt.Printf("%v", jsErrorString(err)) } if self.atexit != nil { @@ -301,21 +301,19 @@ func (self *jsre) preloadJSFiles(ctx *cli.Context) error { for _, file := range jsFiles { filename := common.AbsolutePath(assetPath, strings.TrimSpace(file)) if err := self.re.Exec(filename); err != nil { - return fmt.Errorf("%s: %v", file, err) + return fmt.Errorf("%s: %v", file, jsErrorString(err)) } } } return nil } -// exec executes the JS file with the given filename and stops the JSRE -func (self *jsre) exec(filename string) error { - if err := self.re.Exec(filename); err != nil { - self.re.Stop(false) - return fmt.Errorf("Javascript Error: %v", err) +// jsErrorString adds a backtrace to errors generated by otto. +func jsErrorString(err error) string { + if ottoErr, ok := err.(*otto.Error); ok { + return ottoErr.String() } - self.re.Stop(true) - return nil + return err.Error() } func (self *jsre) interactive() { diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 512a5f1839..4f3946200e 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -21,6 +21,7 @@ import ( "fmt" "io/ioutil" "os" + "os/signal" "path/filepath" "runtime" "strconv" @@ -353,7 +354,7 @@ func console(ctx *cli.Context) { // preload user defined JS files into the console err = repl.preloadJSFiles(ctx) if err != nil { - utils.Fatalf("unable to preload JS file %v", err) + utils.Fatalf("%v", err) } // in case the exec flag holds a JS statement execute it and return @@ -372,6 +373,7 @@ func execScripts(ctx *cli.Context) { // Create and start the node based on the CLI flags node := utils.MakeSystemNode(ClientIdentifier, nodeNameVersion, makeDefaultExtra(), ctx) startNode(ctx, node) + defer node.Stop() // Attach to the newly started node and execute the given scripts client, err := node.Attach() @@ -383,10 +385,24 @@ func execScripts(ctx *cli.Context) { ctx.GlobalString(utils.RPCCORSDomainFlag.Name), client, false) + // Run all given files. for _, file := range ctx.Args() { - repl.exec(file) + if err = repl.re.Exec(file); err != nil { + break + } } - node.Stop() + if err != nil { + utils.Fatalf("JavaScript Error: %v", jsErrorString(err)) + } + // JS files loaded successfully. + // Wait for pending callbacks, but stop for Ctrl-C. + abort := make(chan os.Signal, 1) + signal.Notify(abort, os.Interrupt) + go func() { + <-abort + repl.re.Stop(false) + }() + repl.re.Stop(true) } // startNode boots up the system node and all registered protocols, after which diff --git a/jsre/jsre.go b/jsre/jsre.go index 7df022cb18..59730bc0da 100644 --- a/jsre/jsre.go +++ b/jsre/jsre.go @@ -235,7 +235,14 @@ func (self *JSRE) Exec(file string) error { if err != nil { return err } - self.Do(func(vm *otto.Otto) { _, err = vm.Run(code) }) + var script *otto.Script + self.Do(func(vm *otto.Otto) { + script, err = vm.Compile(file, code) + if err != nil { + return + } + _, err = vm.Run(script) + }) return err } From b06f44ecc2b8f0c0506dd4239ee3d70eac4d1005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 21 Apr 2016 12:14:57 +0300 Subject: [PATCH 04/22] cmd: add a `--fakepow` flag to help benchmarking database changes --- cmd/geth/main.go | 1 + cmd/geth/usage.go | 7 +++++-- cmd/utils/flags.go | 13 ++++++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 6ab4ed45bf..c33e04f069 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -205,6 +205,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso utils.NetworkIdFlag, utils.RPCCORSDomainFlag, utils.MetricsEnabledFlag, + utils.FakePoWFlag, utils.SolcPathFlag, utils.GpoMinGasPriceFlag, utils.GpoMaxGasPriceFlag, diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 278a559809..90019d7b97 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -150,8 +150,11 @@ var AppHelpFlagGroups = []flagGroup{ }, }, { - Name: "LOGGING AND DEBUGGING", - Flags: append([]cli.Flag{utils.MetricsEnabledFlag}, debug.Flags...), + Name: "LOGGING AND DEBUGGING", + Flags: append([]cli.Flag{ + utils.MetricsEnabledFlag, + utils.FakePoWFlag, + }, debug.Flags...), }, { Name: "EXPERIMENTAL", diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 52060c7951..8d55ac8b94 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -47,6 +47,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/whisper" ) @@ -228,6 +229,10 @@ var ( Name: metrics.MetricsEnabledFlag, Usage: "Enable metrics collection and reporting", } + FakePoWFlag = cli.BoolFlag{ + Name: "fakepow", + Usage: "Disables proof-of-work verification", + } // RPC settings RPCEnabledFlag = cli.BoolFlag{ @@ -842,11 +847,13 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database glog.Fatalln(err) } } - chainConfig := MustMakeChainConfigFromDb(ctx, chainDb) - var eventMux event.TypeMux - chain, err = core.NewBlockChain(chainDb, chainConfig, ethash.New(), &eventMux) + pow := pow.PoW(core.FakePow{}) + if !ctx.GlobalBool(FakePoWFlag.Name) { + pow = ethash.New() + } + chain, err = core.NewBlockChain(chainDb, chainConfig, pow, new(event.TypeMux)) if err != nil { Fatalf("Could not start chainmanager: %v", err) } From c88c89fd9ec404fdff85b458188899fb90d974a9 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 21 Apr 2016 13:13:30 +0200 Subject: [PATCH 05/22] cmd/bootnode: fix -genkey, add logging options --- cmd/bootnode/main.go | 46 +++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go index 7f74e9c706..7d3f9fdb3f 100644 --- a/cmd/bootnode/main.go +++ b/cmd/bootnode/main.go @@ -19,15 +19,12 @@ package main import ( "crypto/ecdsa" - "encoding/hex" "flag" - "fmt" - "io/ioutil" - "log" "os" + "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/nat" ) @@ -43,50 +40,43 @@ func main() { nodeKey *ecdsa.PrivateKey err error ) + flag.Var(glog.GetVerbosity(), "verbosity", "log verbosity (0-9)") + flag.Var(glog.GetVModule(), "vmodule", "log verbosity pattern") + glog.SetToStderr(true) flag.Parse() - logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.DebugLevel)) if *genKey != "" { - writeKey(*genKey) + key, err := crypto.GenerateKey() + if err != nil { + utils.Fatalf("could not generate key: %v", err) + } + if err := crypto.SaveECDSA(*genKey, key); err != nil { + utils.Fatalf("%v", err) + } os.Exit(0) } natm, err := nat.Parse(*natdesc) if err != nil { - log.Fatalf("-nat: %v", err) + utils.Fatalf("-nat: %v", err) } switch { case *nodeKeyFile == "" && *nodeKeyHex == "": - log.Fatal("Use -nodekey or -nodekeyhex to specify a private key") + utils.Fatalf("Use -nodekey or -nodekeyhex to specify a private key") case *nodeKeyFile != "" && *nodeKeyHex != "": - log.Fatal("Options -nodekey and -nodekeyhex are mutually exclusive") + utils.Fatalf("Options -nodekey and -nodekeyhex are mutually exclusive") case *nodeKeyFile != "": if nodeKey, err = crypto.LoadECDSA(*nodeKeyFile); err != nil { - log.Fatalf("-nodekey: %v", err) + utils.Fatalf("-nodekey: %v", err) } case *nodeKeyHex != "": if nodeKey, err = crypto.HexToECDSA(*nodeKeyHex); err != nil { - log.Fatalf("-nodekeyhex: %v", err) + utils.Fatalf("-nodekeyhex: %v", err) } } if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil { - log.Fatal(err) + utils.Fatalf("%v", err) } select {} } - -func writeKey(target string) { - key, err := crypto.GenerateKey() - if err != nil { - log.Fatalf("could not generate key: %v", err) - } - b := crypto.FromECDSA(key) - if target == "-" { - fmt.Println(hex.EncodeToString(b)) - } else { - if err := ioutil.WriteFile(target, b, 0600); err != nil { - log.Fatal("write error: ", err) - } - } -} From c1a4dcfc871f21ff1d7b2ce928a8108891cc4820 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Mon, 25 Apr 2016 12:38:15 +0200 Subject: [PATCH 06/22] core, eth: added json tag field for proper unmarshalling According to our own instructions the genesis config attribute should be "config". The genesis definition in the go code, however, has a field called `ChainConfig`. This field now has a `json:"config"` struct tag so that the json is properly unmarshalled. This fixes #2482 --- core/genesis.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/genesis.go b/core/genesis.go index 5c69b216c4..40d7996219 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -43,7 +43,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, } var genesis struct { - ChainConfig *ChainConfig + ChainConfig *ChainConfig `json:"config"` Nonce string Timestamp string ParentHash string From 6a543607efe26df055f4a88310326260b9e72c9e Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 25 Apr 2016 13:30:28 +0200 Subject: [PATCH 07/22] accounts: disable file system watch on linux/arm64 --- accounts/watch.go | 2 +- accounts/watch_fallback.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accounts/watch.go b/accounts/watch.go index 19d304fcc2..309e4d458c 100644 --- a/accounts/watch.go +++ b/accounts/watch.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build darwin,!ios freebsd linux netbsd solaris windows +// +build darwin,!ios freebsd linux,!arm64 netbsd solaris windows package accounts diff --git a/accounts/watch_fallback.go b/accounts/watch_fallback.go index 0b70161673..7b5e221dfd 100644 --- a/accounts/watch_fallback.go +++ b/accounts/watch_fallback.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build ios !darwin,!freebsd,!linux,!netbsd,!solaris,!windows +// +build ios linux,arm64 !darwin,!freebsd,!linux,!netbsd,!solaris,!windows // This is the fallback implementation of directory watching. // It is used on unsupported platforms. From a20d3fc3625c8ccdd025e0dff96cde5d48586b08 Mon Sep 17 00:00:00 2001 From: Paulo L F Casaretto Date: Thu, 21 Apr 2016 23:33:24 -0300 Subject: [PATCH 08/22] common: Add tests for Address#UnmarshalJSON --- common/types.go | 2 +- common/types_test.go | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/common/types.go b/common/types.go index fec9861648..d008844847 100644 --- a/common/types.go +++ b/common/types.go @@ -167,7 +167,7 @@ func (a Address) MarshalJSON() ([]byte, error) { // Parse address from raw json data func (a *Address) UnmarshalJSON(data []byte) error { if len(data) > 2 && data[0] == '"' && data[len(data)-1] == '"' { - data = data[:len(data)-1][1:] + data = data[1 : len(data)-1] } if len(data) > 2 && data[0] == '0' && data[1] == 'x' { diff --git a/common/types_test.go b/common/types_test.go index f2dfbf0c95..de67cfcb5f 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -16,7 +16,10 @@ package common -import "testing" +import ( + "math/big" + "testing" +) func TestBytesConversion(t *testing.T) { bytes := []byte{5} @@ -47,7 +50,38 @@ func TestHashJsonValidation(t *testing.T) { } for i, test := range tests { if err := h.UnmarshalJSON(append([]byte(test.Prefix), make([]byte, test.Size)...)); err != test.Error { - t.Error(i, "expected", test.Error, "got", err) + t.Errorf("test #%d: error mismatch: have %v, want %v", i, err, test.Error) + } + } +} + +func TestAddressUnmarshalJSON(t *testing.T) { + var a Address + var tests = []struct { + Input string + ShouldErr bool + Output *big.Int + }{ + {"", true, nil}, + {`""`, true, nil}, + {`"0x"`, true, nil}, + {`"0x00"`, true, nil}, + {`"0xG000000000000000000000000000000000000000"`, true, nil}, + {`"0x0000000000000000000000000000000000000000"`, false, big.NewInt(0)}, + {`"0x0000000000000000000000000000000000000010"`, false, big.NewInt(16)}, + } + for i, test := range tests { + err := a.UnmarshalJSON([]byte(test.Input)) + if err != nil && !test.ShouldErr { + t.Errorf("test #%d: unexpected error: %v", i, err) + } + if err == nil { + if test.ShouldErr { + t.Errorf("test #%d: expected error, got none", i) + } + if a.Big().Cmp(test.Output) != 0 { + t.Errorf("test #%d: address mismatch: have %v, want %v", i, a.Big(), test.Output) + } } } } From cdcbb2f16014077597e5901c0f328920c904409e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Wed, 27 Apr 2016 16:29:13 +0300 Subject: [PATCH 09/22] accounts/abi/bind, eth: add contract non-existent error --- accounts/abi/bind/backend.go | 10 +++++++++ accounts/abi/bind/backends/remote.go | 15 ++++++++++--- accounts/abi/bind/backends/simulated.go | 11 +++++++++- accounts/abi/bind/bind_test.go | 28 +++++++++++++++++++++++++ eth/api.go | 15 +++++++++++++ 5 files changed, 75 insertions(+), 4 deletions(-) diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go index 328f9f3b71..7442557cce 100644 --- a/accounts/abi/bind/backend.go +++ b/accounts/abi/bind/backend.go @@ -17,12 +17,22 @@ package bind import ( + "errors" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) +// ErrNoCode is returned by call and transact operations for which the requested +// recipient contract to operate on does not exist in the state db or does not +// have any code associated with it (i.e. suicided). +// +// Please note, this error string is part of the RPC API and is expected by the +// native contract bindings to signal this particular error. Do not change this +// as it will break all dependent code! +var ErrNoCode = errors.New("no contract code at given address") + // ContractCaller defines the methods needed to allow operating with contract on a read // only basis. type ContractCaller interface { diff --git a/accounts/abi/bind/backends/remote.go b/accounts/abi/bind/backends/remote.go index 8e990f0763..9b3647192a 100644 --- a/accounts/abi/bind/backends/remote.go +++ b/accounts/abi/bind/backends/remote.go @@ -66,10 +66,16 @@ type request struct { type response struct { JSONRPC string `json:"jsonrpc"` // Version of the JSON RPC protocol, always set to 2.0 ID int `json:"id"` // Auto incrementing ID number for this request - Error json.RawMessage `json:"error"` // Any error returned by the remote side + Error *failure `json:"error"` // Any error returned by the remote side Result json.RawMessage `json:"result"` // Whatever the remote side sends us in reply } +// failure is a JSON RPC response error field sent back from the API server. +type failure struct { + Code int `json:"code"` // JSON RPC error code associated with the failure + Message string `json:"message"` // Specific error message of the failure +} + // request forwards an API request to the RPC server, and parses the response. // // This is currently painfully non-concurrent, but it will have to do until we @@ -96,8 +102,11 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa if err := b.client.Recv(res); err != nil { return nil, err } - if len(res.Error) > 0 { - return nil, fmt.Errorf("remote error: %s", string(res.Error)) + if res.Error != nil { + if res.Error.Message == bind.ErrNoCode.Error() { + return nil, bind.ErrNoCode + } + return nil, fmt.Errorf("remote error: %s", res.Error.Message) } return res.Result, nil } diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 6cdb9a0ccf..4866c4f588 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -92,6 +92,10 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe block = b.blockchain.CurrentBlock() statedb, _ = b.blockchain.State() } + // If there's no code to interact with, respond with an appropriate error + if code := statedb.GetCode(contract); len(code) == 0 { + return nil, bind.ErrNoCode + } // Set infinite balance to the a fake caller account from := statedb.GetOrNewStateObject(common.Address{}) from.SetBalance(common.MaxBig) @@ -134,7 +138,12 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com block = b.pendingBlock statedb = b.pendingState.Copy() ) - + // If there's no code to interact with, respond with an appropriate error + if contract != nil { + if code := statedb.GetCode(*contract); len(code) == 0 { + return nil, bind.ErrNoCode + } + } // Set infinite balance to the a fake caller account from := statedb.GetOrNewStateObject(sender) from.SetBalance(common.MaxBig) diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 5c36bc48f4..f9cc8aba47 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -303,6 +303,34 @@ var bindTests = []struct { } `, }, + // Tests that non-existent contracts are reported as such (though only simulator test) + { + `NonExistent`, + ` + contract NonExistent { + function String() constant returns(string) { + return "I don't exist"; + } + } + `, + `6060604052609f8060106000396000f3606060405260e060020a6000350463f97a60058114601a575b005b600060605260c0604052600d60809081527f4920646f6e27742065786973740000000000000000000000000000000000000060a052602060c0908152600d60e081905281906101009060a09080838184600060046012f15050815172ffffffffffffffffffffffffffffffffffffff1916909152505060405161012081900392509050f3`, + `[{"constant":true,"inputs":[],"name":"String","outputs":[{"name":"","type":"string"}],"type":"function"}]`, + ` + // Create a simulator and wrap a non-deployed contract + sim := backends.NewSimulatedBackend() + + nonexistent, err := NewNonExistent(common.Address{}, sim) + if err != nil { + t.Fatalf("Failed to access non-existent contract: %v", err) + } + // Ensure that contract calls fail with the appropriate error + if res, err := nonexistent.String(nil); err == nil { + t.Fatalf("Call succeeded on non-existent contract: %v", res) + } else if (err != bind.ErrNoCode) { + t.Fatalf("Error mismatch: have %v, want %v", err, bind.ErrNoCode) + } + `, + }, } // Tests that packages generated by the binder can be successfully compiled and diff --git a/eth/api.go b/eth/api.go index a0b1f8ac27..02b34541fa 100644 --- a/eth/api.go +++ b/eth/api.go @@ -51,6 +51,15 @@ import ( "golang.org/x/net/context" ) +// ErrNoCode is returned by call and transact operations for which the requested +// recipient contract to operate on does not exist in the state db or does not +// have any code associated with it (i.e. suicided). +// +// Please note, this error string is part of the RPC API and is expected by the +// native contract bindings to signal this particular error. Do not change this +// as it will break all dependent code! +var ErrNoCode = errors.New("no contract code at given address") + const defaultGas = uint64(90000) // blockByNumber is a commonly used helper function which retrieves and returns @@ -694,6 +703,12 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st } stateDb = stateDb.Copy() + // If there's no code to interact with, respond with an appropriate error + if args.To != nil { + if code := stateDb.GetCode(*args.To); len(code) == 0 { + return "0x", nil, ErrNoCode + } + } // Retrieve the account state object to interact with var from *state.StateObject if args.From == (common.Address{}) { From 48cc36ce83efeb08c1de01c943d4e522f9c3b7ff Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Thu, 28 Apr 2016 12:33:42 +0200 Subject: [PATCH 10/22] eth/filters: ignore logs that don't match filter criteria on chain reorg --- eth/filters/filter_system.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index 29968530a3..4343dfa21c 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -164,7 +164,7 @@ func (fs *FilterSystem) filterLoop() { fs.filterMu.RLock() for _, filter := range fs.logFilters { if filter.LogCallback != nil && !filter.created.After(event.Time) { - for _, removedLog := range ev.Logs { + for _, removedLog := range filter.FilterLogs(ev.Logs) { filter.LogCallback(removedLog, true) } } From e0dc45fce2276fcabae8dca61dc766f98dde23e2 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Wed, 20 Apr 2016 21:16:21 +0200 Subject: [PATCH 11/22] accounts/abi: fixed strict go-like unpacking --- accounts/abi/abi.go | 31 ++++++-- accounts/abi/abi_test.go | 154 +++++++++++++++++++++++++++++++++------ 2 files changed, 158 insertions(+), 27 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 82ea4a0d5c..e7cf4aac76 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -180,12 +180,33 @@ func toGoType(i int, t Argument, output []byte) (interface{}, error) { returnOutput = output[index : index+32] } - // cast bytes to abi return type + // convert the bytes to whatever is specified by the ABI. switch t.Type.T { - case IntTy: - return common.BytesToBig(returnOutput), nil - case UintTy: - return common.BytesToBig(returnOutput), nil + case IntTy, UintTy: + bigNum := common.BytesToBig(returnOutput) + + // If the type is a integer convert to the integer type + // specified by the ABI. + switch t.Type.Kind { + case reflect.Uint8: + return uint8(bigNum.Uint64()), nil + case reflect.Uint16: + return uint16(bigNum.Uint64()), nil + case reflect.Uint32: + return uint32(bigNum.Uint64()), nil + case reflect.Uint64: + return uint64(bigNum.Uint64()), nil + case reflect.Int8: + return uint8(bigNum.Int64()), nil + case reflect.Int16: + return uint16(bigNum.Int64()), nil + case reflect.Int32: + return uint32(bigNum.Int64()), nil + case reflect.Int64: + return uint64(bigNum.Int64()), nil + case reflect.Ptr: + return bigNum, nil + } case BoolTy: return common.BytesToBig(returnOutput).Uint64() > 0, nil case AddressTy: diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 323718a72b..249d37dca9 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -18,6 +18,7 @@ package abi import ( "bytes" + "errors" "fmt" "log" "math/big" @@ -103,6 +104,137 @@ func TestTypeCheck(t *testing.T) { } } +func TestSimpleMethodUnpack(t *testing.T) { + for i, test := range []struct { + def string // definition of the **output** ABI params + marshalledOutput []byte // evm return data + expectedOut interface{} // the expected output + outVar string // the output variable (e.g. uint32, *big.Int, etc) + err error // nil or error if expected + }{ + { + `[ { "type": "uint32" } ]`, + pad([]byte{1}, 32, true), + uint32(1), + "uint32", + nil, + }, + { + `[ { "type": "uint32" } ]`, + pad([]byte{1}, 32, true), + nil, + "uint16", + errors.New("abi: cannot unmarshal uint32 in to uint16"), + }, + { + `[ { "type": "uint17" } ]`, + pad([]byte{1}, 32, true), + nil, + "uint16", + errors.New("abi: cannot unmarshal *big.Int in to uint16"), + }, + { + `[ { "type": "uint17" } ]`, + pad([]byte{1}, 32, true), + big.NewInt(1), + "*big.Int", + nil, + }, + { + `[ { "type": "address" } ]`, + pad(pad([]byte{1}, 20, false), 32, true), + common.Address{1}, + "address", + nil, + }, + { + `[ { "type": "bytes32" } ]`, + pad([]byte{1}, 32, false), + pad([]byte{1}, 32, false), + "bytes", + nil, + }, + { + `[ { "type": "bytes32" } ]`, + pad([]byte{1}, 32, false), + pad([]byte{1}, 32, false), + "hash", + nil, + }, + } { + abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def) + abi, err := JSON(strings.NewReader(abiDefinition)) + if err != nil { + t.Errorf("%d failed. %v", i, err) + continue + } + + var outvar interface{} + switch test.outVar { + case "uint8": + var v uint8 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "uint16": + var v uint16 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "uint32": + var v uint32 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "uint64": + var v uint64 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "*big.Int": + var v *big.Int + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "address": + var v common.Address + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "bytes": + var v []byte + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "hash": + var v common.Hash + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + default: + t.Errorf("unsupported type '%v' please add it to the switch statement in this test", test.outVar) + continue + } + + if err != nil && test.err == nil { + t.Errorf("%d failed. Expected no err but got: %v", i, err) + continue + } + if err == nil && test.err != nil { + t.Errorf("%d failed. Expected err: %v but got none", i, test.err) + continue + } + if err != nil && test.err != nil && err.Error() != test.err.Error() { + t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err) + continue + } + + if err == nil { + // bit of an ugly hack for hash type but I don't feel like finding a proper solution + if test.outVar == "hash" { + tmp := outvar.(common.Hash) // without assignment it's unaddressable + outvar = tmp[:] + } + + if !reflect.DeepEqual(test.expectedOut, outvar) { + t.Errorf("%d failed. Output error: expected %v, got %v", i, test.expectedOut, outvar) + } + } + } +} + func TestPack(t *testing.T) { for i, test := range []struct { typ string @@ -354,28 +486,6 @@ func TestMethodSignature(t *testing.T) { } } -func TestOldPack(t *testing.T) { - abi, err := JSON(strings.NewReader(jsondata2)) - if err != nil { - t.Error(err) - t.FailNow() - } - - sig := crypto.Keccak256([]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 { From c3d5250473794e5b7732e0d06941a6736cff2fca Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Wed, 20 Apr 2016 21:25:19 +0200 Subject: [PATCH 12/22] accounts/abi: added unpacking "anything" in to interface{} --- accounts/abi/abi.go | 2 ++ accounts/abi/abi_test.go | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index e7cf4aac76..cce89de1b0 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -316,6 +316,8 @@ func set(dst, src reflect.Value, output Argument) error { return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.SliceSize, dst.Len()) } reflect.Copy(dst, src) + case dstType.Kind() == reflect.Interface: + dst.Set(src) default: return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type()) } diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 249d37dca9..4b41d2eee0 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -161,6 +161,13 @@ func TestSimpleMethodUnpack(t *testing.T) { "hash", nil, }, + { + `[ { "type": "bytes32" } ]`, + pad([]byte{1}, 32, false), + pad([]byte{1}, 32, false), + "interface", + nil, + }, } { abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def) abi, err := JSON(strings.NewReader(abiDefinition)) @@ -203,6 +210,8 @@ func TestSimpleMethodUnpack(t *testing.T) { var v common.Hash err = abi.Unpack(&v, "method", test.marshalledOutput) outvar = v + case "interface": + err = abi.Unpack(&outvar, "method", test.marshalledOutput) default: t.Errorf("unsupported type '%v' please add it to the switch statement in this test", test.outVar) continue From 4880868c88b8d82cda8ea615bf82548667a95da2 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Wed, 20 Apr 2016 21:30:02 +0200 Subject: [PATCH 13/22] accounts/abi: fixed string and fixed size bytes packing --- accounts/abi/abi.go | 8 +- accounts/abi/abi_test.go | 196 ++++++++++++++++++++++++--------------- accounts/abi/error.go | 7 +- accounts/abi/method.go | 2 +- accounts/abi/packing.go | 2 +- accounts/abi/type.go | 9 +- 6 files changed, 138 insertions(+), 86 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index cce89de1b0..32df6f19d7 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -197,13 +197,13 @@ func toGoType(i int, t Argument, output []byte) (interface{}, error) { case reflect.Uint64: return uint64(bigNum.Uint64()), nil case reflect.Int8: - return uint8(bigNum.Int64()), nil + return int8(bigNum.Int64()), nil case reflect.Int16: - return uint16(bigNum.Int64()), nil + return int16(bigNum.Int64()), nil case reflect.Int32: - return uint32(bigNum.Int64()), nil + return int32(bigNum.Int64()), nil case reflect.Int64: - return uint64(bigNum.Int64()), nil + return int64(bigNum.Int64()), nil case reflect.Ptr: return bigNum, nil } diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 4b41d2eee0..05535b3b50 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -18,7 +18,6 @@ package abi import ( "bytes" - "errors" "fmt" "log" "math/big" @@ -53,35 +52,35 @@ func TestTypeCheck(t *testing.T) { for i, test := range []struct { typ string input interface{} - err error + err string }{ - {"uint", big.NewInt(1), nil}, - {"int", big.NewInt(1), nil}, - {"uint30", big.NewInt(1), nil}, - {"uint30", uint8(1), varErr(reflect.Ptr, reflect.Uint8)}, - {"uint16", uint16(1), nil}, - {"uint16", uint8(1), varErr(reflect.Uint16, reflect.Uint8)}, - {"uint16[]", []uint16{1, 2, 3}, nil}, - {"uint16[]", [3]uint16{1, 2, 3}, nil}, - {"uint16[]", []uint32{1, 2, 3}, typeErr(formatSliceString(reflect.Uint16, -1), formatSliceString(reflect.Uint32, -1))}, - {"uint16[3]", [3]uint32{1, 2, 3}, typeErr(formatSliceString(reflect.Uint16, 3), formatSliceString(reflect.Uint32, 3))}, - {"uint16[3]", [4]uint16{1, 2, 3}, typeErr(formatSliceString(reflect.Uint16, 3), formatSliceString(reflect.Uint16, 4))}, - {"uint16[3]", []uint16{1, 2, 3}, nil}, - {"uint16[3]", []uint16{1, 2, 3, 4}, typeErr(formatSliceString(reflect.Uint16, 3), formatSliceString(reflect.Uint16, 4))}, - {"address[]", []common.Address{common.Address{1}}, nil}, - {"address[1]", []common.Address{common.Address{1}}, nil}, - {"address[1]", [1]common.Address{common.Address{1}}, nil}, - {"address[2]", [1]common.Address{common.Address{1}}, typeErr(formatSliceString(reflect.Array, 2), formatSliceString(reflect.Array, 1))}, - {"bytes32", [32]byte{}, nil}, - {"bytes32", [33]byte{}, typeErr(formatSliceString(reflect.Uint8, 32), formatSliceString(reflect.Uint8, 33))}, - {"bytes32", common.Hash{1}, nil}, - {"bytes31", [31]byte{}, nil}, - {"bytes31", [32]byte{}, typeErr(formatSliceString(reflect.Uint8, 31), formatSliceString(reflect.Uint8, 32))}, - {"bytes", []byte{0, 1}, nil}, - {"bytes", [2]byte{0, 1}, nil}, - {"bytes", common.Hash{1}, nil}, - {"string", "hello world", nil}, - {"bytes32[]", [][32]byte{[32]byte{}}, nil}, + {"uint", big.NewInt(1), ""}, + {"int", big.NewInt(1), ""}, + {"uint30", big.NewInt(1), ""}, + {"uint30", uint8(1), "abi: cannot use uint8 as type ptr as argument"}, + {"uint16", uint16(1), ""}, + {"uint16", uint8(1), "abi: cannot use uint8 as type uint16 as argument"}, + {"uint16[]", []uint16{1, 2, 3}, ""}, + {"uint16[]", [3]uint16{1, 2, 3}, ""}, + {"uint16[]", []uint32{1, 2, 3}, "abi: cannot use []uint32 as type []uint16 as argument"}, + {"uint16[3]", [3]uint32{1, 2, 3}, "abi: cannot use [3]uint32 as type [3]uint16 as argument"}, + {"uint16[3]", [4]uint16{1, 2, 3}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"}, + {"uint16[3]", []uint16{1, 2, 3}, ""}, + {"uint16[3]", []uint16{1, 2, 3, 4}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"}, + {"address[]", []common.Address{common.Address{1}}, ""}, + {"address[1]", []common.Address{common.Address{1}}, ""}, + {"address[1]", [1]common.Address{common.Address{1}}, ""}, + {"address[2]", [1]common.Address{common.Address{1}}, "abi: cannot use [1]array as type [2]array as argument"}, + {"bytes32", [32]byte{}, ""}, + {"bytes32", [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"}, + {"bytes32", common.Hash{1}, ""}, + {"bytes31", [31]byte{}, ""}, + {"bytes31", [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"}, + {"bytes", []byte{0, 1}, ""}, + {"bytes", [2]byte{0, 1}, ""}, + {"bytes", common.Hash{1}, ""}, + {"string", "hello world", ""}, + {"bytes32[]", [][32]byte{[32]byte{}}, ""}, } { typ, err := NewType(test.typ) if err != nil { @@ -89,16 +88,16 @@ func TestTypeCheck(t *testing.T) { } err = typeCheck(typ, reflect.ValueOf(test.input)) - if err != nil && test.err == nil { + if err != nil && len(test.err) == 0 { t.Errorf("%d failed. Expected no err but got: %v", i, err) continue } - if err == nil && test.err != nil { + if err == nil && len(test.err) != 0 { t.Errorf("%d failed. Expected err: %v but got none", i, test.err) continue } - if err != nil && test.err != nil && err.Error() != test.err.Error() { + if err != nil && len(test.err) != 0 && err.Error() != test.err { t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err) } } @@ -110,63 +109,93 @@ func TestSimpleMethodUnpack(t *testing.T) { marshalledOutput []byte // evm return data expectedOut interface{} // the expected output outVar string // the output variable (e.g. uint32, *big.Int, etc) - err error // nil or error if expected + err string // empty or error if expected }{ { `[ { "type": "uint32" } ]`, pad([]byte{1}, 32, true), uint32(1), "uint32", - nil, + "", }, { `[ { "type": "uint32" } ]`, pad([]byte{1}, 32, true), nil, "uint16", - errors.New("abi: cannot unmarshal uint32 in to uint16"), + "abi: cannot unmarshal uint32 in to uint16", }, { `[ { "type": "uint17" } ]`, pad([]byte{1}, 32, true), nil, "uint16", - errors.New("abi: cannot unmarshal *big.Int in to uint16"), + "abi: cannot unmarshal *big.Int in to uint16", }, { `[ { "type": "uint17" } ]`, pad([]byte{1}, 32, true), big.NewInt(1), "*big.Int", - nil, + "", }, + + { + `[ { "type": "int32" } ]`, + pad([]byte{1}, 32, true), + int32(1), + "int32", + "", + }, + { + `[ { "type": "int32" } ]`, + pad([]byte{1}, 32, true), + nil, + "int16", + "abi: cannot unmarshal int32 in to int16", + }, + { + `[ { "type": "int17" } ]`, + pad([]byte{1}, 32, true), + nil, + "int16", + "abi: cannot unmarshal *big.Int in to int16", + }, + { + `[ { "type": "int17" } ]`, + pad([]byte{1}, 32, true), + big.NewInt(1), + "*big.Int", + "", + }, + { `[ { "type": "address" } ]`, pad(pad([]byte{1}, 20, false), 32, true), common.Address{1}, "address", - nil, + "", }, { `[ { "type": "bytes32" } ]`, pad([]byte{1}, 32, false), pad([]byte{1}, 32, false), "bytes", - nil, + "", }, { `[ { "type": "bytes32" } ]`, pad([]byte{1}, 32, false), pad([]byte{1}, 32, false), "hash", - nil, + "", }, { `[ { "type": "bytes32" } ]`, pad([]byte{1}, 32, false), pad([]byte{1}, 32, false), "interface", - nil, + "", }, } { abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def) @@ -194,6 +223,22 @@ func TestSimpleMethodUnpack(t *testing.T) { var v uint64 err = abi.Unpack(&v, "method", test.marshalledOutput) outvar = v + case "int8": + var v int8 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "int16": + var v int16 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "int32": + var v int32 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "int64": + var v int64 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v case "*big.Int": var v *big.Int err = abi.Unpack(&v, "method", test.marshalledOutput) @@ -217,15 +262,15 @@ func TestSimpleMethodUnpack(t *testing.T) { continue } - if err != nil && test.err == nil { + if err != nil && len(test.err) == 0 { t.Errorf("%d failed. Expected no err but got: %v", i, err) continue } - if err == nil && test.err != nil { + if err == nil && len(test.err) != 0 { t.Errorf("%d failed. Expected err: %v but got none", i, test.err) continue } - if err != nil && test.err != nil && err.Error() != test.err.Error() { + if err != nil && len(test.err) != 0 && err.Error() != test.err { t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err) continue } @@ -253,6 +298,7 @@ func TestPack(t *testing.T) { }{ {"uint16", uint16(2), pad([]byte{2}, 32, true)}, {"uint16[]", []uint16{1, 2}, formatSliceOutput([]byte{1}, []byte{2})}, + {"bytes20", [20]byte{1}, pad([]byte{1}, 32, false)}, {"uint256[]", []*big.Int{big.NewInt(1), big.NewInt(2)}, formatSliceOutput([]byte{1}, []byte{2})}, {"address[]", []common.Address{common.Address{1}, common.Address{2}}, formatSliceOutput(pad([]byte{1}, 20, false), pad([]byte{2}, 20, false))}, {"bytes32[]", []common.Hash{common.Hash{1}, common.Hash{2}}, formatSliceOutput(pad([]byte{1}, 32, false), pad([]byte{2}, 32, false))}, @@ -346,26 +392,26 @@ func TestMethodPack(t *testing.T) { const jsondata = ` [ - { "type" : "function", "name" : "balance", "const" : true }, - { "type" : "function", "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] } + { "type" : "function", "name" : "balance", "constant" : true }, + { "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] } ]` const jsondata2 = ` [ - { "type" : "function", "name" : "balance", "const" : true }, - { "type" : "function", "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }, - { "type" : "function", "name" : "test", "const" : false, "inputs" : [ { "name" : "number", "type" : "uint32" } ] }, - { "type" : "function", "name" : "string", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "string" } ] }, - { "type" : "function", "name" : "bool", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" } ] }, - { "type" : "function", "name" : "address", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "address" } ] }, - { "type" : "function", "name" : "uint64[2]", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] }, - { "type" : "function", "name" : "uint64[]", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] }, - { "type" : "function", "name" : "foo", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] }, - { "type" : "function", "name" : "bar", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] }, - { "type" : "function", "name" : "slice", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] }, - { "type" : "function", "name" : "slice256", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] }, - { "type" : "function", "name" : "sliceAddress", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] }, - { "type" : "function", "name" : "sliceMultiAddress", "const" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] } + { "type" : "function", "name" : "balance", "constant" : true }, + { "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }, + { "type" : "function", "name" : "test", "constant" : false, "inputs" : [ { "name" : "number", "type" : "uint32" } ] }, + { "type" : "function", "name" : "string", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "string" } ] }, + { "type" : "function", "name" : "bool", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" } ] }, + { "type" : "function", "name" : "address", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address" } ] }, + { "type" : "function", "name" : "uint64[2]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] }, + { "type" : "function", "name" : "uint64[]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] }, + { "type" : "function", "name" : "foo", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] }, + { "type" : "function", "name" : "bar", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] }, + { "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] }, + { "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] }, + { "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] }, + { "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] } ]` func TestReader(t *testing.T) { @@ -537,9 +583,9 @@ func ExampleJSON() { func TestInputVariableInputLength(t *testing.T) { const definition = `[ - { "type" : "function", "name" : "strOne", "const" : true, "inputs" : [ { "name" : "str", "type" : "string" } ] }, - { "type" : "function", "name" : "bytesOne", "const" : true, "inputs" : [ { "name" : "str", "type" : "bytes" } ] }, - { "type" : "function", "name" : "strTwo", "const" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "str1", "type" : "string" } ] } + { "type" : "function", "name" : "strOne", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" } ] }, + { "type" : "function", "name" : "bytesOne", "constant" : true, "inputs" : [ { "name" : "str", "type" : "bytes" } ] }, + { "type" : "function", "name" : "strTwo", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "str1", "type" : "string" } ] } ]` abi, err := JSON(strings.NewReader(definition)) @@ -701,7 +747,7 @@ func TestBareEvents(t *testing.T) { func TestMultiReturnWithStruct(t *testing.T) { const definition = `[ - { "name" : "multi", "const" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]` + { "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]` abi, err := JSON(strings.NewReader(definition)) if err != nil { @@ -754,7 +800,7 @@ func TestMultiReturnWithStruct(t *testing.T) { func TestMultiReturnWithSlice(t *testing.T) { const definition = `[ - { "name" : "multi", "const" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]` + { "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]` abi, err := JSON(strings.NewReader(definition)) if err != nil { @@ -790,8 +836,8 @@ func TestMultiReturnWithSlice(t *testing.T) { func TestMarshalArrays(t *testing.T) { const definition = `[ - { "name" : "bytes32", "const" : false, "outputs": [ { "type": "bytes32" } ] }, - { "name" : "bytes10", "const" : false, "outputs": [ { "type": "bytes10" } ] } + { "name" : "bytes32", "constant" : false, "outputs": [ { "type": "bytes32" } ] }, + { "name" : "bytes10", "constant" : false, "outputs": [ { "type": "bytes10" } ] } ]` abi, err := JSON(strings.NewReader(definition)) @@ -849,14 +895,14 @@ func TestMarshalArrays(t *testing.T) { func TestUnmarshal(t *testing.T) { const definition = `[ - { "name" : "int", "const" : false, "outputs": [ { "type": "uint256" } ] }, - { "name" : "bool", "const" : false, "outputs": [ { "type": "bool" } ] }, - { "name" : "bytes", "const" : false, "outputs": [ { "type": "bytes" } ] }, - { "name" : "fixed", "const" : false, "outputs": [ { "type": "bytes32" } ] }, - { "name" : "multi", "const" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] }, - { "name" : "addressSliceSingle", "const" : false, "outputs": [ { "type": "address[]" } ] }, - { "name" : "addressSliceDouble", "const" : false, "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] }, - { "name" : "mixedBytes", "const" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]` + { "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] }, + { "name" : "bool", "constant" : false, "outputs": [ { "type": "bool" } ] }, + { "name" : "bytes", "constant" : false, "outputs": [ { "type": "bytes" } ] }, + { "name" : "fixed", "constant" : false, "outputs": [ { "type": "bytes32" } ] }, + { "name" : "multi", "constant" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] }, + { "name" : "addressSliceSingle", "constant" : false, "outputs": [ { "type": "address[]" } ] }, + { "name" : "addressSliceDouble", "constant" : false, "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] }, + { "name" : "mixedBytes", "constant" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]` abi, err := JSON(strings.NewReader(definition)) if err != nil { diff --git a/accounts/abi/error.go b/accounts/abi/error.go index 91a0374fdd..67739c21dc 100644 --- a/accounts/abi/error.go +++ b/accounts/abi/error.go @@ -33,7 +33,7 @@ func formatSliceString(kind reflect.Kind, sliceSize int) string { // sliceTypeCheck checks that the given slice can by assigned to the reflection // type in t. func sliceTypeCheck(t Type, val reflect.Value) error { - if !(val.Kind() == reflect.Slice || val.Kind() == reflect.Array) { + if val.Kind() != reflect.Slice && val.Kind() != reflect.Array { return typeErr(formatSliceString(t.Kind, t.SliceSize), val.Type()) } if t.IsArray && val.Len() != t.SliceSize { @@ -48,14 +48,13 @@ func sliceTypeCheck(t Type, val reflect.Value) error { return sliceTypeCheck(*t.Elem, val.Index(0)) } - elemKind := val.Type().Elem().Kind() - if elemKind != t.Elem.Kind { + if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.Kind { return typeErr(formatSliceString(t.Elem.Kind, t.SliceSize), val.Type()) } return nil } -// typeCheck checks that thet given reflection val can be assigned to the reflection +// typeCheck checks that the given reflection value can be assigned to the reflection // type in t. func typeCheck(t Type, value reflect.Value) error { if t.IsSlice || t.IsArray { diff --git a/accounts/abi/method.go b/accounts/abi/method.go index cad0cd27ff..f3d1a44b55 100644 --- a/accounts/abi/method.go +++ b/accounts/abi/method.go @@ -58,7 +58,7 @@ func (m Method) pack(method Method, args ...interface{}) ([]byte, error) { } // check for a slice type (string, bytes, slice) - if input.Type.T == StringTy || input.Type.T == BytesTy || input.Type.IsSlice || input.Type.IsArray { + if input.Type.requiresLengthPrefix() { // calculate the offset offset := len(method.Inputs)*32 + len(variableInput) // set the offset diff --git a/accounts/abi/packing.go b/accounts/abi/packing.go index 2a16d3be1c..c765dfdf30 100644 --- a/accounts/abi/packing.go +++ b/accounts/abi/packing.go @@ -59,7 +59,7 @@ func packElement(t Type, reflectValue reflect.Value) []byte { reflectValue = mustArrayToByteSlice(reflectValue) } - return common.LeftPadBytes(reflectValue.Bytes(), 32) + return common.RightPadBytes(reflectValue.Bytes(), 32) } panic("abi: fatal error") } diff --git a/accounts/abi/type.go b/accounts/abi/type.go index cad7b82123..2235bad611 100644 --- a/accounts/abi/type.go +++ b/accounts/abi/type.go @@ -89,6 +89,7 @@ func NewType(t string) (typ Type, err error) { return Type{}, err } typ.Elem = &sliceType + typ.stringKind = sliceType.stringKind + t[len(res[1]):] return typ, nil } @@ -110,6 +111,7 @@ func NewType(t string) (typ Type, err error) { varSize = 256 t += "256" } + typ.stringKind = t switch varType { case "int": @@ -149,7 +151,6 @@ func NewType(t string) (typ Type, err error) { default: return Type{}, fmt.Errorf("unsupported arg type: %s", t) } - typ.stringKind = t return } @@ -181,3 +182,9 @@ func (t Type) pack(v reflect.Value) ([]byte, error) { return packElement(t, v), nil } + +// requireLengthPrefix returns whether the type requires any sort of length +// prefixing. +func (t Type) requiresLengthPrefix() bool { + return t.T != FixedBytesTy && (t.T == StringTy || t.T == BytesTy || t.IsSlice || t.IsArray) +} From 572da73d4d475db0443f457d9383a3d513f189ee Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 25 Apr 2016 11:23:40 -0600 Subject: [PATCH 14/22] eth: add personal_importRawKey for runtime private key import --- accounts/account_manager.go | 7 ++++++- eth/api.go | 11 +++++++++++ internal/web3ext/web3ext.go | 27 +++++++++++++++++++++------ 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 56499672ed..3afadf6b22 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -284,7 +284,12 @@ func (am *Manager) Import(keyJSON []byte, passphrase, newPassphrase string) (Acc // ImportECDSA stores the given key into the key directory, encrypting it with the passphrase. func (am *Manager) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (Account, error) { - return am.importKey(newKeyFromECDSA(priv), passphrase) + key := newKeyFromECDSA(priv) + if am.cache.hasAddress(key.Address) { + return Account{}, fmt.Errorf("account already exists") + } + + return am.importKey(key, passphrase) } func (am *Manager) importKey(key *Key, passphrase string) (Account, error) { diff --git a/eth/api.go b/eth/api.go index a0b1f8ac27..4ebc9b2a0c 100644 --- a/eth/api.go +++ b/eth/api.go @@ -18,6 +18,7 @@ package eth import ( "bytes" + "encoding/hex" "encoding/json" "errors" "fmt" @@ -439,6 +440,16 @@ func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) return common.Address{}, err } +func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) { + hexkey, err := hex.DecodeString(privkey) + if err != nil { + return common.Address{}, err + } + + acc, err := s.am.ImportECDSA(crypto.ToECDSA(hexkey), password) + return acc.Address, err +} + // UnlockAccount will unlock the account associated with the given address with // the given password for duration seconds. If duration is nil it will use a // default of 300 seconds. It returns an indication if the account was unlocked. diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index bc1e469217..14700b05c6 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -18,12 +18,13 @@ package web3ext var Modules = map[string]string{ - "txpool": TxPool_JS, - "admin": Admin_JS, - "eth": Eth_JS, - "miner": Miner_JS, - "debug": Debug_JS, - "net": Net_JS, + "txpool": TxPool_JS, + "admin": Admin_JS, + "personal": Personal_JS, + "eth": Eth_JS, + "miner": Miner_JS, + "debug": Debug_JS, + "net": Net_JS, } const TxPool_JS = ` @@ -175,6 +176,20 @@ web3._extend({ }); ` +const Personal_JS = ` +web3._extend({ + property: 'personal', + methods: + [ + new web3._extend.Method({ + name: 'importRawKey', + call: 'personal_importRawKey', + params: 2 + }) + ] +}); +` + const Eth_JS = ` web3._extend({ property: 'eth', From c74a57572515543cf24b71cd9c275921ddf24075 Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Fri, 29 Apr 2016 12:02:54 +0100 Subject: [PATCH 15/22] core: Provide a public accessor for ChainConfig This is necessary for external users of the go-ethereum code who want to, for instance, build a custom node that plays back transactions, as core.ApplyTransaction requires a ChainConfig as a parameter. --- core/blockchain.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/blockchain.go b/core/blockchain.go index ecf8297cbf..4598800d54 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1213,3 +1213,6 @@ func (self *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []c func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header { return self.hc.GetHeaderByNumber(number) } + +// Config retrieves the blockchain's chain configuration. +func (self *BlockChain) Config() *ChainConfig { return self.config } From ecd7199c4367bbffd5000ab6ee9bad3ef88de5d2 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Tue, 24 Nov 2015 11:30:35 +0100 Subject: [PATCH 16/22] common/versions, cmd/utils: add geth version contract --- cmd/utils/flags.go | 7 ++ common/versions/version.sol | 152 +++++++++++++++++++++++++ common/versions/versions.go | 215 ++++++++++++++++++++++++++++++++++++ node/node.go | 1 + 4 files changed, 375 insertions(+) create mode 100644 common/versions/version.sol create mode 100644 common/versions/versions.go diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 8d55ac8b94..86e9e9b0d7 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/versions" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" @@ -773,6 +774,12 @@ func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node. } } + err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + return versions.NewVersionCheck(ctx) + }) + if err != nil { + Fatalf("Failed to register the Version Check service: %v", err) + } return stack } diff --git a/common/versions/version.sol b/common/versions/version.sol new file mode 100644 index 0000000000..68c883115d --- /dev/null +++ b/common/versions/version.sol @@ -0,0 +1,152 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// WARNING: WORK IN PROGRESS & UNTESTED +// +// contract tracking versions added by designated signers. +// designed to track versions of geth (go-ethereum) recommended by the +// go-ethereum team. geth client interfaces with contract through ABI by simply +// reading the full state and then deciding on recommended version based on +// some logic (e.g. version date & number of signers). +// +// to keep things simple, the contract does not use FSM for multisig +// but rather allows any designated signer to add a version or vote for an +// existing version. this avoids need to track voting-in-progress states and +// also provides history of all past versions. +// + +contract Versions { + struct V { + bytes32 v; + uint64 ts; + address[] signers; + } + + address[] public parties; // owners/signers + address[] public deleteAcks; // votes to suicide contract + uint public deleteAcksReq; // number of votes needed + V[] public versions; + + modifier canAccess(address addr) { + bool access = false; + for (uint i = 0; i < parties.length; i++) { + if (parties[i] == addr) { + access = true; + break; + } + } + if (access == false) { + throw; + } + _ + } + + function Versions(address[] addrs) { + if (addrs.length < 2) { + throw; + } + + parties = addrs; + deleteAcksReq = (addrs.length / 2) + 1; + } + + // TODO: use dynamic array when solidity adds proper support for returning them + function GetVersions() returns (bytes32[10], uint64[10], uint[10]) { + bytes32[10] memory vs; + uint64[10] memory ts; + uint[10] memory ss; + for (uint i = 0; i < versions.length; i++) { + vs[i] = versions[i].v; + ts[i] = versions[i].ts; + ss[i] = versions[i].signers.length; + } + return (vs, ts, ss); + } + + // either submit a new version or acknowledge an existing one + function AckVersion(bytes32 ver) + canAccess(msg.sender) + { + for (uint i = 0; i < versions.length; i++) { + if (versions[i].v == ver) { + for (uint j = 0; j < versions[i].signers.length; j++) { + if (versions[i].signers[j] == msg.sender) { + // already signed + throw; + } + } + // add sender as signer of existing version + versions[i].signers.push(msg.sender); + return; + } + } + + // version is new, add it + // due to dynamic array, push it first then set values + V memory v; + versions.push(v); + versions[versions.length - 1].v = ver; + // signers is dynamic array; have to extend size manually + versions[versions.length - 1].signers.length++; + versions[versions.length - 1].signers[0] = msg.sender; + versions[versions.length - 1].ts = uint64(block.timestamp); + } + + // remove vote for a version, if present + function NackVersion(bytes32 ver) + canAccess(msg.sender) + { + for (uint i = 0; i < versions.length; i++) { + if (versions[i].v == ver) { + for (uint j = 0; j < versions[i].signers.length; j++) { + if (versions[i].signers[j] == msg.sender) { + delete versions[i].signers[j]; + } + } + } + } + } + + // delete-this-contract vote, suicide if enough votes + function AckDelete() + canAccess(msg.sender) + { + for (uint i = 0; i < deleteAcks.length; i++) { + if (deleteAcks[i] == msg.sender) { + throw; // already acked delete + } + } + deleteAcks.push(msg.sender); + if (deleteAcks.length >= deleteAcksReq) { + suicide(msg.sender); + } + } + + // remove sender's delete-this-contract vote, if present + function NackDelete() + canAccess(msg.sender) + { + uint len = deleteAcks.length; + for (uint i = 0; i < len; i++) { + if (deleteAcks[i] == msg.sender) { + if (len > 1) { + deleteAcks[i] = deleteAcks[len-1]; + } + deleteAcks.length -= 1; + } + } + } +} diff --git a/common/versions/versions.go b/common/versions/versions.go new file mode 100644 index 0000000000..dc7681485b --- /dev/null +++ b/common/versions/versions.go @@ -0,0 +1,215 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package versions + +import ( + "fmt" + "math/big" + "strconv" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" +) + +var ( + jsonlogger = logger.NewJsonLogger() + // TODO: add Frontier address + GlobalVersionsAddr = common.HexToAddress("0x40bebcadbb4456db23fda39f261f3b2509096e9e") // test + dummySender = common.HexToAddress("0x16db48070243bc37a1c59cd5bb977ad7047618be") // test + getVersionsSignature = "GetVersions()" + firstCheckTime = time.Second * 4 + continousCheckTime = time.Second * 600 +) + +type VersionCheck struct { + serverName string + timer *time.Timer + e *eth.Ethereum + stop chan bool +} + +// Boilerplate to satisfy node.Service interface +func (v *VersionCheck) Protocols() []p2p.Protocol { + return []p2p.Protocol{} +} + +func (v *VersionCheck) APIs() []rpc.API { + return []rpc.API{} +} + +func (v *VersionCheck) Start(server *p2p.Server) error { + v.serverName = server.Name + // Check version first time after a few seconds so it shows after + // other startup messages + t := time.NewTimer(firstCheckTime) + v.timer = t + v.stop = make(chan bool) + versionCheck := func() { + for { + select { + case <-v.stop: + close(v.stop) + return + case <-v.timer.C: + _, err := get(v.e, v.serverName) + if err != nil { + glog.V(logger.Error).Infof("Could not query geth version contract: %s", err) + } + v.timer.Reset(continousCheckTime) + } + } + } + go versionCheck() + return nil +} + +func (v *VersionCheck) Stop() error { + v.stop <- true + select { + case <-v.stop: + } + return nil +} + +func NewVersionCheck(ctx *node.ServiceContext) (node.Service, error) { + var v VersionCheck + var e *eth.Ethereum + // sets e to the Ethereum instance previously started + // expects double pointer + ctx.Service(&e) + v.e = e + return &v, nil +} + +// query versions list from the (custom) accessor in the versions contract +func get(e *eth.Ethereum, clientVersion string) (string, error) { + // TODO: move common/registrar abiSignature to some util package + abi := crypto.Sha3([]byte(getVersionsSignature))[:4] + res, _, err := simulateCall( + e, + &dummySender, + &GlobalVersionsAddr, + big.NewInt(3000000), // gasLimit + big.NewInt(1), // gasPrice + big.NewInt(0), // value + abi) + if err != nil { + return "", err + } + + // TODO: we use static arrays of size versionCount as workaround + // until solidity has proper support for returning dynamic arrays + versionCount := 10 + + if len(res) != 2+(64*versionCount*3) { // 0x + three 32-byte fields per version + return "", fmt.Errorf("unexpected result length from GetVersions") + } + + // TODO: use ABI (after solidity supports returning arrays of arrays and/or structs) + var versions []string + var timestamps []uint64 + var signerCounts []uint64 + + // trim 0x + res = res[2:] + + // parse res + for i := 0; i < versionCount; i++ { + bytes := common.FromHex(res[:64]) + versions = append(versions, string(bytes)) + res = res[64:] + } + + for i := 0; i < versionCount; i++ { + ts, err := strconv.ParseUint(res[:64], 16, 64) + if err != nil { + return "", err + } + timestamps = append(timestamps, ts) + res = res[64:] + } + + for i := 0; i < versionCount; i++ { + sc, err := strconv.ParseUint(res[:64], 16, 64) + if err != nil { + return "", err + } + signerCounts = append(signerCounts, sc) + res = res[64:] + } + + // TODO: version matching logic (e.g. most votes / most recent) + if versions[0] != clientVersion { + glog.V(logger.Info).Infof("geth version %s does not match recommended version %s", clientVersion, versions[0]) + } + + return res, nil +} + +func simulateCall(e *eth.Ethereum, from0, to *common.Address, gas, gasPrice, value *big.Int, data []byte) (string, *big.Int, error) { + stateCopy, err := e.BlockChain().State() + if err != nil { + return "", nil, err + } + from := stateCopy.GetOrNewStateObject(*from0) + from.SetBalance(common.MaxBig) + + msg := callmsg{ + from: from, + to: to, + gas: gas, + gasPrice: gasPrice, + value: value, + data: data, + } + + // Execute the call and return + vmenv := core.NewEnv(stateCopy, e.BlockChain(), msg, e.BlockChain().CurrentHeader()) + gp := new(core.GasPool).AddGas(common.MaxBig) + + res, gas, err := core.ApplyMessage(vmenv, msg, gp) + return common.ToHex(res), gas, err + +} + +// TODO: consider moving to package common or accounts/abi as it's useful for anyone +// simulating EVM CALL +type callmsg struct { + from *state.StateObject + to *common.Address + gas, gasPrice *big.Int + value *big.Int + data []byte +} + +// accessor boilerplate to implement core.Message +func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil } +func (m callmsg) Nonce() uint64 { return m.from.Nonce() } +func (m callmsg) To() *common.Address { return m.to } +func (m callmsg) GasPrice() *big.Int { return m.gasPrice } +func (m callmsg) Gas() *big.Int { return m.gas } +func (m callmsg) Value() *big.Int { return m.value } +func (m callmsg) Data() []byte { return m.data } diff --git a/node/node.go b/node/node.go index 18c3f91d87..0864253f45 100644 --- a/node/node.go +++ b/node/node.go @@ -530,6 +530,7 @@ func (n *Node) Server() *p2p.Server { } // Service retrieves a currently running service registered of a specific type. +// NOTE: must be called with double pointer to service func (n *Node) Service(service interface{}) error { n.lock.RLock() defer n.lock.RUnlock() From d46da273c6731512b4114393856a96be06505797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 31 Mar 2016 12:20:24 +0300 Subject: [PATCH 17/22] common/releases: rewrite release version contract + use native dapps --- common/versions/version.sol | 152 --------- common/versions/versions.go | 215 ------------- contracts/release/contract.go | 474 +++++++++++++++++++++++++++++ contracts/release/contract.sol | 240 +++++++++++++++ contracts/release/contract_test.go | 374 +++++++++++++++++++++++ contracts/release/generator.go | 19 ++ 6 files changed, 1107 insertions(+), 367 deletions(-) delete mode 100644 common/versions/version.sol delete mode 100644 common/versions/versions.go create mode 100644 contracts/release/contract.go create mode 100644 contracts/release/contract.sol create mode 100644 contracts/release/contract_test.go create mode 100644 contracts/release/generator.go diff --git a/common/versions/version.sol b/common/versions/version.sol deleted file mode 100644 index 68c883115d..0000000000 --- a/common/versions/version.sol +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library 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. -// -// The go-ethereum library 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// WARNING: WORK IN PROGRESS & UNTESTED -// -// contract tracking versions added by designated signers. -// designed to track versions of geth (go-ethereum) recommended by the -// go-ethereum team. geth client interfaces with contract through ABI by simply -// reading the full state and then deciding on recommended version based on -// some logic (e.g. version date & number of signers). -// -// to keep things simple, the contract does not use FSM for multisig -// but rather allows any designated signer to add a version or vote for an -// existing version. this avoids need to track voting-in-progress states and -// also provides history of all past versions. -// - -contract Versions { - struct V { - bytes32 v; - uint64 ts; - address[] signers; - } - - address[] public parties; // owners/signers - address[] public deleteAcks; // votes to suicide contract - uint public deleteAcksReq; // number of votes needed - V[] public versions; - - modifier canAccess(address addr) { - bool access = false; - for (uint i = 0; i < parties.length; i++) { - if (parties[i] == addr) { - access = true; - break; - } - } - if (access == false) { - throw; - } - _ - } - - function Versions(address[] addrs) { - if (addrs.length < 2) { - throw; - } - - parties = addrs; - deleteAcksReq = (addrs.length / 2) + 1; - } - - // TODO: use dynamic array when solidity adds proper support for returning them - function GetVersions() returns (bytes32[10], uint64[10], uint[10]) { - bytes32[10] memory vs; - uint64[10] memory ts; - uint[10] memory ss; - for (uint i = 0; i < versions.length; i++) { - vs[i] = versions[i].v; - ts[i] = versions[i].ts; - ss[i] = versions[i].signers.length; - } - return (vs, ts, ss); - } - - // either submit a new version or acknowledge an existing one - function AckVersion(bytes32 ver) - canAccess(msg.sender) - { - for (uint i = 0; i < versions.length; i++) { - if (versions[i].v == ver) { - for (uint j = 0; j < versions[i].signers.length; j++) { - if (versions[i].signers[j] == msg.sender) { - // already signed - throw; - } - } - // add sender as signer of existing version - versions[i].signers.push(msg.sender); - return; - } - } - - // version is new, add it - // due to dynamic array, push it first then set values - V memory v; - versions.push(v); - versions[versions.length - 1].v = ver; - // signers is dynamic array; have to extend size manually - versions[versions.length - 1].signers.length++; - versions[versions.length - 1].signers[0] = msg.sender; - versions[versions.length - 1].ts = uint64(block.timestamp); - } - - // remove vote for a version, if present - function NackVersion(bytes32 ver) - canAccess(msg.sender) - { - for (uint i = 0; i < versions.length; i++) { - if (versions[i].v == ver) { - for (uint j = 0; j < versions[i].signers.length; j++) { - if (versions[i].signers[j] == msg.sender) { - delete versions[i].signers[j]; - } - } - } - } - } - - // delete-this-contract vote, suicide if enough votes - function AckDelete() - canAccess(msg.sender) - { - for (uint i = 0; i < deleteAcks.length; i++) { - if (deleteAcks[i] == msg.sender) { - throw; // already acked delete - } - } - deleteAcks.push(msg.sender); - if (deleteAcks.length >= deleteAcksReq) { - suicide(msg.sender); - } - } - - // remove sender's delete-this-contract vote, if present - function NackDelete() - canAccess(msg.sender) - { - uint len = deleteAcks.length; - for (uint i = 0; i < len; i++) { - if (deleteAcks[i] == msg.sender) { - if (len > 1) { - deleteAcks[i] = deleteAcks[len-1]; - } - deleteAcks.length -= 1; - } - } - } -} diff --git a/common/versions/versions.go b/common/versions/versions.go deleted file mode 100644 index dc7681485b..0000000000 --- a/common/versions/versions.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library 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. -// -// The go-ethereum library 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package versions - -import ( - "fmt" - "math/big" - "strconv" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/rpc" -) - -var ( - jsonlogger = logger.NewJsonLogger() - // TODO: add Frontier address - GlobalVersionsAddr = common.HexToAddress("0x40bebcadbb4456db23fda39f261f3b2509096e9e") // test - dummySender = common.HexToAddress("0x16db48070243bc37a1c59cd5bb977ad7047618be") // test - getVersionsSignature = "GetVersions()" - firstCheckTime = time.Second * 4 - continousCheckTime = time.Second * 600 -) - -type VersionCheck struct { - serverName string - timer *time.Timer - e *eth.Ethereum - stop chan bool -} - -// Boilerplate to satisfy node.Service interface -func (v *VersionCheck) Protocols() []p2p.Protocol { - return []p2p.Protocol{} -} - -func (v *VersionCheck) APIs() []rpc.API { - return []rpc.API{} -} - -func (v *VersionCheck) Start(server *p2p.Server) error { - v.serverName = server.Name - // Check version first time after a few seconds so it shows after - // other startup messages - t := time.NewTimer(firstCheckTime) - v.timer = t - v.stop = make(chan bool) - versionCheck := func() { - for { - select { - case <-v.stop: - close(v.stop) - return - case <-v.timer.C: - _, err := get(v.e, v.serverName) - if err != nil { - glog.V(logger.Error).Infof("Could not query geth version contract: %s", err) - } - v.timer.Reset(continousCheckTime) - } - } - } - go versionCheck() - return nil -} - -func (v *VersionCheck) Stop() error { - v.stop <- true - select { - case <-v.stop: - } - return nil -} - -func NewVersionCheck(ctx *node.ServiceContext) (node.Service, error) { - var v VersionCheck - var e *eth.Ethereum - // sets e to the Ethereum instance previously started - // expects double pointer - ctx.Service(&e) - v.e = e - return &v, nil -} - -// query versions list from the (custom) accessor in the versions contract -func get(e *eth.Ethereum, clientVersion string) (string, error) { - // TODO: move common/registrar abiSignature to some util package - abi := crypto.Sha3([]byte(getVersionsSignature))[:4] - res, _, err := simulateCall( - e, - &dummySender, - &GlobalVersionsAddr, - big.NewInt(3000000), // gasLimit - big.NewInt(1), // gasPrice - big.NewInt(0), // value - abi) - if err != nil { - return "", err - } - - // TODO: we use static arrays of size versionCount as workaround - // until solidity has proper support for returning dynamic arrays - versionCount := 10 - - if len(res) != 2+(64*versionCount*3) { // 0x + three 32-byte fields per version - return "", fmt.Errorf("unexpected result length from GetVersions") - } - - // TODO: use ABI (after solidity supports returning arrays of arrays and/or structs) - var versions []string - var timestamps []uint64 - var signerCounts []uint64 - - // trim 0x - res = res[2:] - - // parse res - for i := 0; i < versionCount; i++ { - bytes := common.FromHex(res[:64]) - versions = append(versions, string(bytes)) - res = res[64:] - } - - for i := 0; i < versionCount; i++ { - ts, err := strconv.ParseUint(res[:64], 16, 64) - if err != nil { - return "", err - } - timestamps = append(timestamps, ts) - res = res[64:] - } - - for i := 0; i < versionCount; i++ { - sc, err := strconv.ParseUint(res[:64], 16, 64) - if err != nil { - return "", err - } - signerCounts = append(signerCounts, sc) - res = res[64:] - } - - // TODO: version matching logic (e.g. most votes / most recent) - if versions[0] != clientVersion { - glog.V(logger.Info).Infof("geth version %s does not match recommended version %s", clientVersion, versions[0]) - } - - return res, nil -} - -func simulateCall(e *eth.Ethereum, from0, to *common.Address, gas, gasPrice, value *big.Int, data []byte) (string, *big.Int, error) { - stateCopy, err := e.BlockChain().State() - if err != nil { - return "", nil, err - } - from := stateCopy.GetOrNewStateObject(*from0) - from.SetBalance(common.MaxBig) - - msg := callmsg{ - from: from, - to: to, - gas: gas, - gasPrice: gasPrice, - value: value, - data: data, - } - - // Execute the call and return - vmenv := core.NewEnv(stateCopy, e.BlockChain(), msg, e.BlockChain().CurrentHeader()) - gp := new(core.GasPool).AddGas(common.MaxBig) - - res, gas, err := core.ApplyMessage(vmenv, msg, gp) - return common.ToHex(res), gas, err - -} - -// TODO: consider moving to package common or accounts/abi as it's useful for anyone -// simulating EVM CALL -type callmsg struct { - from *state.StateObject - to *common.Address - gas, gasPrice *big.Int - value *big.Int - data []byte -} - -// accessor boilerplate to implement core.Message -func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil } -func (m callmsg) Nonce() uint64 { return m.from.Nonce() } -func (m callmsg) To() *common.Address { return m.to } -func (m callmsg) GasPrice() *big.Int { return m.gasPrice } -func (m callmsg) Gas() *big.Int { return m.gas } -func (m callmsg) Value() *big.Int { return m.value } -func (m callmsg) Data() []byte { return m.data } diff --git a/contracts/release/contract.go b/contracts/release/contract.go new file mode 100644 index 0000000000..ffdcc14cd2 --- /dev/null +++ b/contracts/release/contract.go @@ -0,0 +1,474 @@ +// This file is an automatically generated Go binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package release + +import ( + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// ReleaseOracleABI is the input ABI used to generate the binding from. +const ReleaseOracleABI = `[{"constant":false,"inputs":[],"name":"Nuke","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"}],"name":"Release","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"AuthProposals","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"CurrentVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"time","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"ProposedVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"pass","type":"address[]"},{"name":"fail","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"Promote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"release","type":"bool"}],"name":"updateRelease","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"Demote","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"AuthVotes","outputs":[{"name":"promote","type":"address[]"},{"name":"demote","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"Signers","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"},{"name":"authorize","type":"bool"}],"name":"updateSigner","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"}]` + +// ReleaseOracleBin is the compiled bytecode used for deploying new contracts. +const ReleaseOracleBin = `0x6060604052600160a060020a0333166000908152602081905260409020805460ff1916600190811790915580548082018083558281838015829011606257818360005260206000209182019101606291905b808211156094576000815584016051565b505050919090600052602060002090016000508054600160a060020a0319163317905550611262806100986000396000f35b5090566060604052361561008d5760e060020a60003504630443b1ad811461008f5780630d618178146100a0578063282fe4e5146100bd5780632b225f291461012c5780634c327071146101515780636195db9c14610276578063645dce721461028757806380bbbd4a146102d6578063a29226f2146102e7578063f04c4758146103e1578063f460590b1461044d575b005b61008d61072060008080808061029a565b61008d60043560243560443560643561071a84848484600161029a565b6104d360408051602081810183526000825282516003805480840283018401909552848252929390929183018282801561044257602002820191906000526020600020908154600160a060020a0316815260019190910190602001808311610423575b5050505050905061044a565b61051d600060006000600060006000600860005080549050600014156106895761070a565b610551604080516020818101835260008083528351808301855281815260045460068054875181870281018701909852808852939687968796879691959463ffffffff818116956401000000008304821695604060020a840490921694606060020a93849004909302939092600792918491908301828280156101fe57602002820191906000526020600020905b8154600160a060020a03168152600191909101906020018083116101df575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561025b57602002820191906000526020600020905b8154600160a060020a031681526001919091019060200180831161023c575b50505050509050955095509550955095509550909192939495565b61008d600435610712816001610457565b61008d6004356024356044356064356084355b600160a060020a033316600090815260208190526040812054819060ff16156111f957821580156102cc575060065481145b15610c81576111f9565b61008d600435610712816000610457565b6106046004356040805160208181018352600080835283518083018552818152600160a060020a038616825260028352908490208054855181850281018501909652808652939491939092600184019291849183018282801561037457602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610355575b50505050509150808054806020026020016040519081016040528092919081815260200182805480156103d157602002820191906000526020600020905b8154600160a060020a03168152600191909101906020018083116103b2575b5050505050905091509150915091565b604080516020818101835260008252600180548451818402810184019095528085526104d3949283018282801561044257602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610423575b505050505090505b90565b61008d6004356024355b600160a060020a033316600090815260208190526040812054819060ff161561071a57600160a060020a038416815260026020526040812091505b8154811015610722578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a0316141561076d5761071a565b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b6040805163ffffffff9687168152948616602086015294909216838501526060830152608082015290519081900360a00190f35b604051808763ffffffff1681526020018663ffffffff1681526020018563ffffffff16815260200184815260200180602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019850505050505050505060405180910390f35b6040518080602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f15090500194505050505060405180910390f35b600880546000198101908110156100025760009182526004027ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30190508054600182015463ffffffff8281169950640100000000830481169850604060020a8304169650606060020a91829004909102945067ffffffffffffffff16925090505b509091929394565b50565b505050505b50505050565b565b5060005b60018201548110156107755733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a031614156107bf5761071a565b600101610492565b8154600014801561078a575060018201546000145b156107e757600380546001810180835582818380158290116107c7578183600052602060002091820191016107c7919061086d565b600101610726565b5050506000928352506020909120018054600160a060020a031916851790555b82156108855781546001810180845583919082818380158290116108ba576000838152602090206108ba91810190830161086d565b5050506000928352506020909120018054600160a060020a031916851790555b600160a060020a038416600090815260026020908152604082208054838255818452918320909291610b6991908101905b80821115610881576000815560010161086d565b5090565b81600101600050805480600101828181548183558181151161097657818360005260206000209182019101610976919061086d565b5050506000928352506020909120018054600160a060020a031916331790556001548254600290910490116108ee5761071a565b8280156109145750600160a060020a03841660009081526020819052604090205460ff16155b156109ad57600160a060020a0384166000908152602081905260409020805460ff191660019081179091558054808201808355828183801582901161081c57600083905261081c9060008051602061124283398151915290810190830161086d565b5050506000928352506020909120018054600160a060020a031916331790556001805490830154600290910490116108ee5761071a565b821580156109d35750600160a060020a03841660009081526020819052604090205460ff165b1561083c5750600160a060020a0383166000908152602081905260408120805460ff191690555b60015481101561083c5783600160a060020a03166001600050828154811015610002576000919091526000805160206112428339815191520154600160a060020a03161415610add576001805460001981019081101561000257600091825260008051602061124283398151915201909054906101000a9004600160a060020a0316600160005082815481101561000257600080516020611242833981519152018054600160a060020a03191690921790915580546000198101808355909190828015829011610ae557818360005260206000209182019101610ae5919061086d565b6001016109fa565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529194509192508290610b3f907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f9081019061086d565b5060018201805460008083559182526020909120610b5f9181019061086d565b505050505061083c565b5060018201805460008083559182526020909120610b899181019061086d565b506000925050505b60035481101561071a5783600160a060020a03166003600050828154811015610002576000919091526000805160206112228339815191520154600160a060020a03161415610c79576003805460001981019081101561000257600091825260008051602061122283398151915201909054906101000a9004600160a060020a0316600360005082815481101561000257600080516020611222833981519152018054600160a060020a03191690921790915580546000198101808355909190828015829011610715576107159060008051602061122283398151915290810190830161086d565b600101610b91565b60065460001415610cdf576004805463ffffffff1916881767ffffffff0000000019166401000000008802176bffffffff00000000000000001916604060020a8702176bffffffffffffffffffffffff16606060020a808704021790555b828015610d49575060045463ffffffff8881169116141580610d155750600454640100000000900463ffffffff90811690871614155b80610d32575060045463ffffffff808716604060020a9092041614155b80610d495750600454606060020a90819004028414155b15610d53576111f9565b506006905060005b8154811015610d9c578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610de7576111f9565b5060005b6001820154811015610def5733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610e26576111f9565b600101610d5b565b8215610e2e578154600181018084558391908281838015829011610e6357818360005260206000209182019101610e63919061086d565b600101610da0565b816001016000508054806001018281815481835581811511610ee657818360005260206000209182019101610ee6919061086d565b5050506000928352506020909120018054600160a060020a03191633179055600154825460029091049011610e97576111f9565b8215610f1d576005805467ffffffffffffffff19164217905560088054600181018083558281838015829011610f7257600402816004028360005260206000209182019101610f72919061108b565b5050506000928352506020909120018054600160a060020a03191633179055600180549083015460029091049011610e97576111f9565b600060048181556005805467ffffffffffffffff19169055600680548382558184529192918290611202907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f9081019061086d565b5050509190906000526020600020906004020160005060048054825463ffffffff191663ffffffff9182161780845582546401000000009081900483160267ffffffff000000001991909116178084558254604060020a908190049092169091026bffffffff00000000000000001991909116178083558154606060020a908190048102819004026bffffffffffffffffffffffff9190911617825560055460018301805467ffffffffffffffff191667ffffffffffffffff9092169190911790556006805460028401805482825560008281526020902094959491928392918201918582156110ea5760005260206000209182015b828111156110ea578254825591600101919060010190611068565b505050506001015b8082111561088157600080825560018201805467ffffffffffffffff191690556002820180548282558183526020832083916110ca919081019061086d565b50600182018054600080835591825260209091206110839181019061086d565b506111109291505b80821115610881578054600160a060020a03191681556001016110f2565b505060018181018054918401805480835560008381526020902092938301929091821561115e5760005260206000209182015b8281111561115e578254825591600101919060010190611143565b5061116a9291506110f2565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529197509195509093508492506111c991507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f9081019061086d565b50600182018054600080835591825260209091206111e99181019061086d565b50505050506111f9565b50505050505b50505050505050565b50600182018054600080835591825260209091206111f39181019061086d56c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85bb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6` + +// DeployReleaseOracle deploys a new Ethereum contract, binding an instance of ReleaseOracle to it. +func DeployReleaseOracle(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ReleaseOracle, error) { + parsed, err := abi.JSON(strings.NewReader(ReleaseOracleABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ReleaseOracleBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ReleaseOracle{ReleaseOracleCaller: ReleaseOracleCaller{contract: contract}, ReleaseOracleTransactor: ReleaseOracleTransactor{contract: contract}}, nil +} + +// ReleaseOracle is an auto generated Go binding around an Ethereum contract. +type ReleaseOracle struct { + ReleaseOracleCaller // Read-only binding to the contract + ReleaseOracleTransactor // Write-only binding to the contract +} + +// ReleaseOracleCaller is an auto generated read-only Go binding around an Ethereum contract. +type ReleaseOracleCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ReleaseOracleTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ReleaseOracleTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ReleaseOracleSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ReleaseOracleSession struct { + Contract *ReleaseOracle // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ReleaseOracleCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ReleaseOracleCallerSession struct { + Contract *ReleaseOracleCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ReleaseOracleTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ReleaseOracleTransactorSession struct { + Contract *ReleaseOracleTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ReleaseOracleRaw is an auto generated low-level Go binding around an Ethereum contract. +type ReleaseOracleRaw struct { + Contract *ReleaseOracle // Generic contract binding to access the raw methods on +} + +// ReleaseOracleCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ReleaseOracleCallerRaw struct { + Contract *ReleaseOracleCaller // Generic read-only contract binding to access the raw methods on +} + +// ReleaseOracleTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ReleaseOracleTransactorRaw struct { + Contract *ReleaseOracleTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewReleaseOracle creates a new instance of ReleaseOracle, bound to a specific deployed contract. +func NewReleaseOracle(address common.Address, backend bind.ContractBackend) (*ReleaseOracle, error) { + contract, err := bindReleaseOracle(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor)) + if err != nil { + return nil, err + } + return &ReleaseOracle{ReleaseOracleCaller: ReleaseOracleCaller{contract: contract}, ReleaseOracleTransactor: ReleaseOracleTransactor{contract: contract}}, nil +} + +// NewReleaseOracleCaller creates a new read-only instance of ReleaseOracle, bound to a specific deployed contract. +func NewReleaseOracleCaller(address common.Address, caller bind.ContractCaller) (*ReleaseOracleCaller, error) { + contract, err := bindReleaseOracle(address, caller, nil) + if err != nil { + return nil, err + } + return &ReleaseOracleCaller{contract: contract}, nil +} + +// NewReleaseOracleTransactor creates a new write-only instance of ReleaseOracle, bound to a specific deployed contract. +func NewReleaseOracleTransactor(address common.Address, transactor bind.ContractTransactor) (*ReleaseOracleTransactor, error) { + contract, err := bindReleaseOracle(address, nil, transactor) + if err != nil { + return nil, err + } + return &ReleaseOracleTransactor{contract: contract}, nil +} + +// bindReleaseOracle binds a generic wrapper to an already deployed contract. +func bindReleaseOracle(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ReleaseOracleABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ReleaseOracle *ReleaseOracleRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ReleaseOracle.Contract.ReleaseOracleCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ReleaseOracle *ReleaseOracleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ReleaseOracle.Contract.ReleaseOracleTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ReleaseOracle *ReleaseOracleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ReleaseOracle.Contract.ReleaseOracleTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ReleaseOracle *ReleaseOracleCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ReleaseOracle.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ReleaseOracle *ReleaseOracleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ReleaseOracle.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ReleaseOracle *ReleaseOracleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ReleaseOracle.Contract.contract.Transact(opts, method, params...) +} + +// AuthProposals is a free data retrieval call binding the contract method 0x282fe4e5. +// +// Solidity: function AuthProposals() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleCaller) AuthProposals(opts *bind.CallOpts) ([]common.Address, error) { + var ( + ret0 = new([]common.Address) + ) + out := ret0 + err := _ReleaseOracle.contract.Call(opts, out, "AuthProposals") + return *ret0, err +} + +// AuthProposals is a free data retrieval call binding the contract method 0x282fe4e5. +// +// Solidity: function AuthProposals() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleSession) AuthProposals() ([]common.Address, error) { + return _ReleaseOracle.Contract.AuthProposals(&_ReleaseOracle.CallOpts) +} + +// AuthProposals is a free data retrieval call binding the contract method 0x282fe4e5. +// +// Solidity: function AuthProposals() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleCallerSession) AuthProposals() ([]common.Address, error) { + return _ReleaseOracle.Contract.AuthProposals(&_ReleaseOracle.CallOpts) +} + +// AuthVotes is a free data retrieval call binding the contract method 0xa29226f2. +// +// Solidity: function AuthVotes(user address) constant returns(promote address[], demote address[]) +func (_ReleaseOracle *ReleaseOracleCaller) AuthVotes(opts *bind.CallOpts, user common.Address) (struct { + Promote []common.Address + Demote []common.Address +}, error) { + ret := new(struct { + Promote []common.Address + Demote []common.Address + }) + out := ret + err := _ReleaseOracle.contract.Call(opts, out, "AuthVotes", user) + return *ret, err +} + +// AuthVotes is a free data retrieval call binding the contract method 0xa29226f2. +// +// Solidity: function AuthVotes(user address) constant returns(promote address[], demote address[]) +func (_ReleaseOracle *ReleaseOracleSession) AuthVotes(user common.Address) (struct { + Promote []common.Address + Demote []common.Address +}, error) { + return _ReleaseOracle.Contract.AuthVotes(&_ReleaseOracle.CallOpts, user) +} + +// AuthVotes is a free data retrieval call binding the contract method 0xa29226f2. +// +// Solidity: function AuthVotes(user address) constant returns(promote address[], demote address[]) +func (_ReleaseOracle *ReleaseOracleCallerSession) AuthVotes(user common.Address) (struct { + Promote []common.Address + Demote []common.Address +}, error) { + return _ReleaseOracle.Contract.AuthVotes(&_ReleaseOracle.CallOpts, user) +} + +// CurrentVersion is a free data retrieval call binding the contract method 0x2b225f29. +// +// Solidity: function CurrentVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, time uint256) +func (_ReleaseOracle *ReleaseOracleCaller) CurrentVersion(opts *bind.CallOpts) (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Time *big.Int +}, error) { + ret := new(struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Time *big.Int + }) + out := ret + err := _ReleaseOracle.contract.Call(opts, out, "CurrentVersion") + return *ret, err +} + +// CurrentVersion is a free data retrieval call binding the contract method 0x2b225f29. +// +// Solidity: function CurrentVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, time uint256) +func (_ReleaseOracle *ReleaseOracleSession) CurrentVersion() (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Time *big.Int +}, error) { + return _ReleaseOracle.Contract.CurrentVersion(&_ReleaseOracle.CallOpts) +} + +// CurrentVersion is a free data retrieval call binding the contract method 0x2b225f29. +// +// Solidity: function CurrentVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, time uint256) +func (_ReleaseOracle *ReleaseOracleCallerSession) CurrentVersion() (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Time *big.Int +}, error) { + return _ReleaseOracle.Contract.CurrentVersion(&_ReleaseOracle.CallOpts) +} + +// ProposedVersion is a free data retrieval call binding the contract method 0x4c327071. +// +// Solidity: function ProposedVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, pass address[], fail address[]) +func (_ReleaseOracle *ReleaseOracleCaller) ProposedVersion(opts *bind.CallOpts) (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Pass []common.Address + Fail []common.Address +}, error) { + ret := new(struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Pass []common.Address + Fail []common.Address + }) + out := ret + err := _ReleaseOracle.contract.Call(opts, out, "ProposedVersion") + return *ret, err +} + +// ProposedVersion is a free data retrieval call binding the contract method 0x4c327071. +// +// Solidity: function ProposedVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, pass address[], fail address[]) +func (_ReleaseOracle *ReleaseOracleSession) ProposedVersion() (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Pass []common.Address + Fail []common.Address +}, error) { + return _ReleaseOracle.Contract.ProposedVersion(&_ReleaseOracle.CallOpts) +} + +// ProposedVersion is a free data retrieval call binding the contract method 0x4c327071. +// +// Solidity: function ProposedVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, pass address[], fail address[]) +func (_ReleaseOracle *ReleaseOracleCallerSession) ProposedVersion() (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Pass []common.Address + Fail []common.Address +}, error) { + return _ReleaseOracle.Contract.ProposedVersion(&_ReleaseOracle.CallOpts) +} + +// Signers is a free data retrieval call binding the contract method 0xf04c4758. +// +// Solidity: function Signers() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleCaller) Signers(opts *bind.CallOpts) ([]common.Address, error) { + var ( + ret0 = new([]common.Address) + ) + out := ret0 + err := _ReleaseOracle.contract.Call(opts, out, "Signers") + return *ret0, err +} + +// Signers is a free data retrieval call binding the contract method 0xf04c4758. +// +// Solidity: function Signers() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleSession) Signers() ([]common.Address, error) { + return _ReleaseOracle.Contract.Signers(&_ReleaseOracle.CallOpts) +} + +// Signers is a free data retrieval call binding the contract method 0xf04c4758. +// +// Solidity: function Signers() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleCallerSession) Signers() ([]common.Address, error) { + return _ReleaseOracle.Contract.Signers(&_ReleaseOracle.CallOpts) +} + +// Demote is a paid mutator transaction binding the contract method 0x80bbbd4a. +// +// Solidity: function Demote(user address) returns() +func (_ReleaseOracle *ReleaseOracleTransactor) Demote(opts *bind.TransactOpts, user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "Demote", user) +} + +// Demote is a paid mutator transaction binding the contract method 0x80bbbd4a. +// +// Solidity: function Demote(user address) returns() +func (_ReleaseOracle *ReleaseOracleSession) Demote(user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Demote(&_ReleaseOracle.TransactOpts, user) +} + +// Demote is a paid mutator transaction binding the contract method 0x80bbbd4a. +// +// Solidity: function Demote(user address) returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) Demote(user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Demote(&_ReleaseOracle.TransactOpts, user) +} + +// Nuke is a paid mutator transaction binding the contract method 0x0443b1ad. +// +// Solidity: function Nuke() returns() +func (_ReleaseOracle *ReleaseOracleTransactor) Nuke(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "Nuke") +} + +// Nuke is a paid mutator transaction binding the contract method 0x0443b1ad. +// +// Solidity: function Nuke() returns() +func (_ReleaseOracle *ReleaseOracleSession) Nuke() (*types.Transaction, error) { + return _ReleaseOracle.Contract.Nuke(&_ReleaseOracle.TransactOpts) +} + +// Nuke is a paid mutator transaction binding the contract method 0x0443b1ad. +// +// Solidity: function Nuke() returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) Nuke() (*types.Transaction, error) { + return _ReleaseOracle.Contract.Nuke(&_ReleaseOracle.TransactOpts) +} + +// Promote is a paid mutator transaction binding the contract method 0x6195db9c. +// +// Solidity: function Promote(user address) returns() +func (_ReleaseOracle *ReleaseOracleTransactor) Promote(opts *bind.TransactOpts, user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "Promote", user) +} + +// Promote is a paid mutator transaction binding the contract method 0x6195db9c. +// +// Solidity: function Promote(user address) returns() +func (_ReleaseOracle *ReleaseOracleSession) Promote(user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Promote(&_ReleaseOracle.TransactOpts, user) +} + +// Promote is a paid mutator transaction binding the contract method 0x6195db9c. +// +// Solidity: function Promote(user address) returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) Promote(user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Promote(&_ReleaseOracle.TransactOpts, user) +} + +// Release is a paid mutator transaction binding the contract method 0x0d618178. +// +// Solidity: function Release(major uint32, minor uint32, patch uint32, commit bytes20) returns() +func (_ReleaseOracle *ReleaseOracleTransactor) Release(opts *bind.TransactOpts, major uint32, minor uint32, patch uint32, commit [20]byte) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "Release", major, minor, patch, commit) +} + +// Release is a paid mutator transaction binding the contract method 0x0d618178. +// +// Solidity: function Release(major uint32, minor uint32, patch uint32, commit bytes20) returns() +func (_ReleaseOracle *ReleaseOracleSession) Release(major uint32, minor uint32, patch uint32, commit [20]byte) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Release(&_ReleaseOracle.TransactOpts, major, minor, patch, commit) +} + +// Release is a paid mutator transaction binding the contract method 0x0d618178. +// +// Solidity: function Release(major uint32, minor uint32, patch uint32, commit bytes20) returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) Release(major uint32, minor uint32, patch uint32, commit [20]byte) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Release(&_ReleaseOracle.TransactOpts, major, minor, patch, commit) +} + +// UpdateRelease is a paid mutator transaction binding the contract method 0x645dce72. +// +// Solidity: function updateRelease(major uint32, minor uint32, patch uint32, commit bytes20, release bool) returns() +func (_ReleaseOracle *ReleaseOracleTransactor) UpdateRelease(opts *bind.TransactOpts, major uint32, minor uint32, patch uint32, commit [20]byte, release bool) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "updateRelease", major, minor, patch, commit, release) +} + +// UpdateRelease is a paid mutator transaction binding the contract method 0x645dce72. +// +// Solidity: function updateRelease(major uint32, minor uint32, patch uint32, commit bytes20, release bool) returns() +func (_ReleaseOracle *ReleaseOracleSession) UpdateRelease(major uint32, minor uint32, patch uint32, commit [20]byte, release bool) (*types.Transaction, error) { + return _ReleaseOracle.Contract.UpdateRelease(&_ReleaseOracle.TransactOpts, major, minor, patch, commit, release) +} + +// UpdateRelease is a paid mutator transaction binding the contract method 0x645dce72. +// +// Solidity: function updateRelease(major uint32, minor uint32, patch uint32, commit bytes20, release bool) returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) UpdateRelease(major uint32, minor uint32, patch uint32, commit [20]byte, release bool) (*types.Transaction, error) { + return _ReleaseOracle.Contract.UpdateRelease(&_ReleaseOracle.TransactOpts, major, minor, patch, commit, release) +} + +// UpdateSigner is a paid mutator transaction binding the contract method 0xf460590b. +// +// Solidity: function updateSigner(user address, authorize bool) returns() +func (_ReleaseOracle *ReleaseOracleTransactor) UpdateSigner(opts *bind.TransactOpts, user common.Address, authorize bool) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "updateSigner", user, authorize) +} + +// UpdateSigner is a paid mutator transaction binding the contract method 0xf460590b. +// +// Solidity: function updateSigner(user address, authorize bool) returns() +func (_ReleaseOracle *ReleaseOracleSession) UpdateSigner(user common.Address, authorize bool) (*types.Transaction, error) { + return _ReleaseOracle.Contract.UpdateSigner(&_ReleaseOracle.TransactOpts, user, authorize) +} + +// UpdateSigner is a paid mutator transaction binding the contract method 0xf460590b. +// +// Solidity: function updateSigner(user address, authorize bool) returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) UpdateSigner(user common.Address, authorize bool) (*types.Transaction, error) { + return _ReleaseOracle.Contract.UpdateSigner(&_ReleaseOracle.TransactOpts, user, authorize) +} diff --git a/contracts/release/contract.sol b/contracts/release/contract.sol new file mode 100644 index 0000000000..7ec0fa8e7c --- /dev/null +++ b/contracts/release/contract.sol @@ -0,0 +1,240 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// ReleaseOracle is an Ethereum contract to store the current and previous +// versions of the go-ethereum implementation. Its goal is to allow Geth to +// check for new releases automatically without the need to consult a central +// repository. +// +// The contract takes a vote based approach on both assigning authorized signers +// as well as signing off on new Geth releases. +// +// Note, when a signer is demoted, the currently pending release is auto-nuked. +// The reason is to prevent suprises where a demotion actually tilts the votes +// in favor of one voter party and pushing out a new release as a consequence of +// a simple demotion. +contract ReleaseOracle { + // Votes is an internal data structure to count votes on a specific proposal + struct Votes { + address[] pass; // List of signers voting to pass a proposal + address[] fail; // List of signers voting to fail a proposal + } + + // Version is the version details of a particular Geth release + struct Version { + uint32 major; // Major version component of the release + uint32 minor; // Minor version component of the release + uint32 patch; // Patch version component of the release + bytes20 commit; // Git SHA1 commit hash of the release + + uint64 time; // Timestamp of the release approval + Votes votes; // Votes that passed this release + } + + // Oracle authorization details + mapping(address => bool) authorized; // Set of accounts allowed to vote on updating the contract + address[] signers; // List of addresses currently accepted as signers + + // Various proposals being voted on + mapping(address => Votes) authProps; // Currently running user authorization proposals + address[] authPend; // List of addresses being voted on (map indexes) + + Version verProp; // Currently proposed release being voted on + Version[] releases; // All the positively voted releases + + // isSigner is a modifier to authorize contract transactions. + modifier isSigner() { + if (authorized[msg.sender]) { + _ + } + } + + // Constructor to assign the creator as the sole valid signer. + function ReleaseOracle() { + authorized[msg.sender] = true; + signers.push(msg.sender); + } + + // Signers is an accessor method to retrieve all te signers (public accessor + // generates an indexed one, not a retreive-all version). + function Signers() constant returns(address[]) { + return signers; + } + + // AuthProposals retrieves the list of addresses that authorization proposals + // are currently being voted on. + function AuthProposals() constant returns(address[]) { + return authPend; + } + + // AuthVotes retrieves the current authorization votes for a particular user + // to promote him into the list of signers, or demote him from there. + function AuthVotes(address user) constant returns(address[] promote, address[] demote) { + return (authProps[user].pass, authProps[user].fail); + } + + // CurrentVersion retrieves the semantic version, commit hash and release time + // of the currently votec active release. + function CurrentVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, uint time) { + if (releases.length == 0) { + return (0, 0, 0, 0, 0); + } + var release = releases[releases.length - 1]; + + return (release.major, release.minor, release.patch, release.commit, release.time); + } + + // ProposedVersion retrieves the semantic version, commit hash and the current + // votes for the next proposed release. + function ProposedVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, address[] pass, address[] fail) { + return (verProp.major, verProp.minor, verProp.patch, verProp.commit, verProp.votes.pass, verProp.votes.fail); + } + + // Promote pitches in on a voting campaign to promote a new user to a signer + // position. + function Promote(address user) { + updateSigner(user, true); + } + + // Demote pitches in on a voting campaign to demote an authorized user from + // its signer position. + function Demote(address user) { + updateSigner(user, false); + } + + // Release votes for a particular version to be included as the next release. + function Release(uint32 major, uint32 minor, uint32 patch, bytes20 commit) { + updateRelease(major, minor, patch, commit, true); + } + + // Nuke votes for the currently proposed version to not be included as the next + // release. Nuking doesn't require a specific version number for simplicity. + function Nuke() { + updateRelease(0, 0, 0, 0, false); + } + + // updateSigner marks a vote for changing the status of an Ethereum user, either + // for or against the user being an authorized signer. + function updateSigner(address user, bool authorize) isSigner { + // Gather the current votes and ensure we don't double vote + Votes votes = authProps[user]; + for (uint i = 0; i < votes.pass.length; i++) { + if (votes.pass[i] == msg.sender) { + return; + } + } + for (i = 0; i < votes.fail.length; i++) { + if (votes.fail[i] == msg.sender) { + return; + } + } + // If no authorization proposal is open, add the user to the index for later lookups + if (votes.pass.length == 0 && votes.fail.length == 0) { + authPend.push(user); + } + // Cast the vote and return if the proposal cannot be resolved yet + if (authorize) { + votes.pass.push(msg.sender); + if (votes.pass.length <= signers.length / 2) { + return; + } + } else { + votes.fail.push(msg.sender); + if (votes.fail.length <= signers.length / 2) { + return; + } + } + // Proposal resolved in our favor, execute whatever we voted on + if (authorize && !authorized[user]) { + authorized[user] = true; + signers.push(user); + } else if (!authorize && authorized[user]) { + authorized[user] = false; + + for (i = 0; i < signers.length; i++) { + if (signers[i] == user) { + signers[i] = signers[signers.length - 1]; + signers.length--; + + delete verProp; // Nuke any version proposal (no suprise releases!) + break; + } + } + } + // Finally delete the resolved proposal, index and garbage collect + delete authProps[user]; + + for (i = 0; i < authPend.length; i++) { + if (authPend[i] == user) { + authPend[i] = authPend[authPend.length - 1]; + authPend.length--; + break; + } + } + } + + // updateRelease votes for a particular version to be included as the next release, + // or for the currently proposed release to be nuked out. + function updateRelease(uint32 major, uint32 minor, uint32 patch, bytes20 commit, bool release) isSigner { + // Skip nuke votes if no proposal is pending + if (!release && verProp.votes.pass.length == 0) { + return; + } + // Mark a new release if no proposal is pending + if (verProp.votes.pass.length == 0) { + verProp.major = major; + verProp.minor = minor; + verProp.patch = patch; + verProp.commit = commit; + } + // Make sure positive votes match the current proposal + if (release && (verProp.major != major || verProp.minor != minor || verProp.patch != patch || verProp.commit != commit)) { + return; + } + // Gather the current votes and ensure we don't double vote + Votes votes = verProp.votes; + for (uint i = 0; i < votes.pass.length; i++) { + if (votes.pass[i] == msg.sender) { + return; + } + } + for (i = 0; i < votes.fail.length; i++) { + if (votes.fail[i] == msg.sender) { + return; + } + } + // Cast the vote and return if the proposal cannot be resolved yet + if (release) { + votes.pass.push(msg.sender); + if (votes.pass.length <= signers.length / 2) { + return; + } + } else { + votes.fail.push(msg.sender); + if (votes.fail.length <= signers.length / 2) { + return; + } + } + // Proposal resolved in our favor, execute whatever we voted on + if (release) { + verProp.time = uint64(now); + releases.push(verProp); + delete verProp; + } else { + delete verProp; + } + } +} diff --git a/contracts/release/contract_test.go b/contracts/release/contract_test.go new file mode 100644 index 0000000000..7aa3a577db --- /dev/null +++ b/contracts/release/contract_test.go @@ -0,0 +1,374 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package release + +import ( + "crypto/ecdsa" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/crypto" +) + +// setupReleaseTest creates a blockchain simulator and deploys a version oracle +// contract for testing. +func setupReleaseTest(t *testing.T, prefund ...*ecdsa.PrivateKey) (*ecdsa.PrivateKey, *ReleaseOracle, *backends.SimulatedBackend) { + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() + auth := bind.NewKeyedTransactor(key) + + accounts := []core.GenesisAccount{{Address: auth.From, Balance: big.NewInt(10000000000)}} + for _, key := range prefund { + accounts = append(accounts, core.GenesisAccount{Address: crypto.PubkeyToAddress(key.PublicKey), Balance: big.NewInt(10000000000)}) + } + sim := backends.NewSimulatedBackend(accounts...) + + // Deploy a version oracle contract, commit and return + _, _, oracle, err := DeployReleaseOracle(auth, sim) + if err != nil { + t.Fatalf("Failed to deploy version contract: %v", err) + } + sim.Commit() + + return key, oracle, sim +} + +// Tests that the version contract can be deployed and the creator is assigned +// the sole authorized signer. +func TestContractCreation(t *testing.T) { + key, oracle, _ := setupReleaseTest(t) + + owner := crypto.PubkeyToAddress(key.PublicKey) + signers, err := oracle.Signers(nil) + if err != nil { + t.Fatalf("Failed to retrieve list of signers: %v", err) + } + if len(signers) != 1 || signers[0] != owner { + t.Fatalf("Initial signer mismatch: have %v, want %v", signers, owner) + } +} + +// Tests that subsequent signers can be promoted, each requiring half plus one +// votes for it to pass through. +func TestSignerPromotion(t *testing.T) { + // Prefund a few accounts to authorize with and create the oracle + keys := make([]*ecdsa.PrivateKey, 5) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + } + key, oracle, sim := setupReleaseTest(t, keys...) + + // Gradually promote the keys, until all are authorized + keys = append([]*ecdsa.PrivateKey{key}, keys...) + for i := 1; i < len(keys); i++ { + // Check that no votes are accepted from the not yet authed user + if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[i]), common.Address{}); err != nil { + t.Fatalf("Iter #%d: failed invalid promotion attempt: %v", i, err) + } + sim.Commit() + + pend, err := oracle.AuthProposals(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve active proposals: %v", i, err) + } + if len(pend) != 0 { + t.Fatalf("Iter #%d: proposal count mismatch: have %d, want 0", i, len(pend)) + } + // Promote with half - 1 voters and check that the user's not yet authorized + for j := 0; j < i/2; j++ { + if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) + } + } + sim.Commit() + + signers, err := oracle.Signers(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", i, err) + } + if len(signers) != i { + t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", i, len(signers), i) + } + // Promote with the last one needed to pass the promotion + if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[i/2]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid promotion completion attempt: %v", i, err) + } + sim.Commit() + + signers, err = oracle.Signers(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", i, err) + } + if len(signers) != i+1 { + t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", i, len(signers), i+1) + } + } +} + +// Tests that subsequent signers can be demoted, each requiring half plus one +// votes for it to pass through. +func TestSignerDemotion(t *testing.T) { + // Prefund a few accounts to authorize with and create the oracle + keys := make([]*ecdsa.PrivateKey, 5) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + } + key, oracle, sim := setupReleaseTest(t, keys...) + + // Authorize all the keys as valid signers and verify cardinality + keys = append([]*ecdsa.PrivateKey{key}, keys...) + for i := 1; i < len(keys); i++ { + for j := 0; j <= i/2; j++ { + if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) + } + } + sim.Commit() + } + signers, err := oracle.Signers(nil) + if err != nil { + t.Fatalf("Failed to retrieve list of signers: %v", err) + } + if len(signers) != len(keys) { + t.Fatalf("Signer count mismatch: have %v, want %v", len(signers), len(keys)) + } + // Gradually demote users until we run out of signers + for i := len(keys) - 1; i >= 0; i-- { + // Demote with half - 1 voters and check that the user's not yet dropped + for j := 0; j < (i+1)/2; j++ { + if _, err = oracle.Demote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid demotion attempt: %v", len(keys)-i, err) + } + } + sim.Commit() + + signers, err := oracle.Signers(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", len(keys)-i, err) + } + if len(signers) != i+1 { + t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", len(keys)-i, len(signers), i+1) + } + // Demote with the last one needed to pass the demotion + if _, err = oracle.Demote(bind.NewKeyedTransactor(keys[(i+1)/2]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid demotion completion attempt: %v", i, err) + } + sim.Commit() + + signers, err = oracle.Signers(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", len(keys)-i, err) + } + if len(signers) != i { + t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", len(keys)-i, len(signers), i) + } + // Check that no votes are accepted from the already demoted users + if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[i]), common.Address{}); err != nil { + t.Fatalf("Iter #%d: failed invalid promotion attempt: %v", i, err) + } + sim.Commit() + + pend, err := oracle.AuthProposals(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve active proposals: %v", i, err) + } + if len(pend) != 0 { + t.Fatalf("Iter #%d: proposal count mismatch: have %d, want 0", i, len(pend)) + } + } +} + +// Tests that new versions can be released, honouring both voting rights as well +// as the minimum required vote count. +func TestVersionRelease(t *testing.T) { + // Prefund a few accounts to authorize with and create the oracle + keys := make([]*ecdsa.PrivateKey, 5) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + } + key, oracle, sim := setupReleaseTest(t, keys...) + + // Track the "current release" + var ( + verMajor = uint32(0) + verMinor = uint32(0) + verPatch = uint32(0) + verCommit = [20]byte{} + ) + // Gradually push releases, always requiring more signers than previously + keys = append([]*ecdsa.PrivateKey{key}, keys...) + for i := 1; i < len(keys); i++ { + // Check that no votes are accepted from the not yet authed user + if _, err := oracle.Release(bind.NewKeyedTransactor(keys[i]), 0, 0, 0, [20]byte{0}); err != nil { + t.Fatalf("Iter #%d: failed invalid release attempt: %v", i, err) + } + sim.Commit() + + prop, err := oracle.ProposedVersion(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err) + } + if len(prop.Pass) != 0 { + t.Fatalf("Iter #%d: proposal vote count mismatch: have %d, want 0", i, len(prop.Pass)) + } + // Authorize the user to make releases + for j := 0; j <= i/2; j++ { + if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) + } + } + sim.Commit() + + // Propose release with half voters and check that the release does not yet go through + for j := 0; j < (i+1)/2; j++ { + if _, err = oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{}); err != nil { + t.Fatalf("Iter #%d: failed valid release attempt: %v", i, err) + } + } + sim.Commit() + + ver, err := oracle.CurrentVersion(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve current version: %v", i, err) + } + if ver.Major != verMajor || ver.Minor != verMinor || ver.Patch != verPatch || ver.Commit != verCommit { + t.Fatalf("Iter #%d: version mismatch: have %d.%d.%d-%x, want %d.%d.%d-%x", i, ver.Major, ver.Minor, ver.Patch, ver.Commit, verMajor, verMinor, verPatch, verCommit) + } + + // Pass the release and check that it became the next version + verMajor, verMinor, verPatch, verCommit = uint32(i), uint32(i+1), uint32(i+2), [20]byte{} + if _, err = oracle.Release(bind.NewKeyedTransactor(keys[(i+1)/2]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{}); err != nil { + t.Fatalf("Iter #%d: failed valid release completion attempt: %v", i, err) + } + sim.Commit() + + ver, err = oracle.CurrentVersion(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve current version: %v", i, err) + } + if ver.Major != verMajor || ver.Minor != verMinor || ver.Patch != verPatch || ver.Commit != verCommit { + t.Fatalf("Iter #%d: version mismatch: have %d.%d.%d-%x, want %d.%d.%d-%x", i, ver.Major, ver.Minor, ver.Patch, ver.Commit, verMajor, verMinor, verPatch, verCommit) + } + } +} + +// Tests that proposed versions can be nuked out of existence. +func TestVersionNuking(t *testing.T) { + // Prefund a few accounts to authorize with and create the oracle + keys := make([]*ecdsa.PrivateKey, 9) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + } + key, oracle, sim := setupReleaseTest(t, keys...) + + // Authorize all the keys as valid signers + keys = append([]*ecdsa.PrivateKey{key}, keys...) + for i := 1; i < len(keys); i++ { + for j := 0; j <= i/2; j++ { + if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) + } + } + sim.Commit() + } + // Propose releases with more and more keys, always retaining enough users to nuke the proposals + for i := 1; i < (len(keys)+1)/2; i++ { + // Propose release with an initial set of signers + for j := 0; j < i; j++ { + if _, err := oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{}); err != nil { + t.Fatalf("Iter #%d: failed valid proposal attempt: %v", i, err) + } + } + sim.Commit() + + prop, err := oracle.ProposedVersion(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err) + } + if len(prop.Pass) != i { + t.Fatalf("Iter #%d: proposal vote count mismatch: have %d, want %d", i, len(prop.Pass), i) + } + // Nuke the release with half+1 voters + for j := i; j <= i+(len(keys)+1)/2; j++ { + if _, err := oracle.Nuke(bind.NewKeyedTransactor(keys[j])); err != nil { + t.Fatalf("Iter #%d: failed valid nuke attempt: %v", i, err) + } + } + sim.Commit() + + prop, err = oracle.ProposedVersion(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err) + } + if len(prop.Pass) != 0 || len(prop.Fail) != 0 { + t.Fatalf("Iter #%d: proposal vote count mismatch: have %d/%d pass/fail, want 0/0", i, len(prop.Pass), len(prop.Fail)) + } + } +} + +// Tests that demoting a signer will auto-nuke the currently pending release. +func TestVersionAutoNuke(t *testing.T) { + // Prefund a few accounts to authorize with and create the oracle + keys := make([]*ecdsa.PrivateKey, 5) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + } + key, oracle, sim := setupReleaseTest(t, keys...) + + // Authorize all the keys as valid signers + keys = append([]*ecdsa.PrivateKey{key}, keys...) + for i := 1; i < len(keys); i++ { + for j := 0; j <= i/2; j++ { + if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) + } + } + sim.Commit() + } + // Make a release proposal and check it's existence + if _, err := oracle.Release(bind.NewKeyedTransactor(keys[0]), 1, 2, 3, [20]byte{}); err != nil { + t.Fatalf("Failed valid proposal attempt: %v", err) + } + sim.Commit() + + prop, err := oracle.ProposedVersion(nil) + if err != nil { + t.Fatalf("Failed to retrieve active proposal: %v", err) + } + if len(prop.Pass) != 1 { + t.Fatalf("Proposal vote count mismatch: have %d, want 1", len(prop.Pass)) + } + // Demote a signer and check release proposal deletion + for i := 0; i <= len(keys)/2; i++ { + if _, err := oracle.Demote(bind.NewKeyedTransactor(keys[i]), crypto.PubkeyToAddress(keys[len(keys)-1].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid demotion attempt: %v", i, err) + } + } + sim.Commit() + + prop, err = oracle.ProposedVersion(nil) + if err != nil { + t.Fatalf("Failed to retrieve active proposal: %v", err) + } + if len(prop.Pass) != 0 { + t.Fatalf("Proposal vote count mismatch: have %d, want 0", len(prop.Pass)) + } +} diff --git a/contracts/release/generator.go b/contracts/release/generator.go new file mode 100644 index 0000000000..1553e06127 --- /dev/null +++ b/contracts/release/generator.go @@ -0,0 +1,19 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:generate abigen --sol ./contract.sol --pkg release --out ./contract.go + +package release From 586eddfd09558bfd71f23c2e50c270d2ca665d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 28 Apr 2016 12:00:11 +0300 Subject: [PATCH 18/22] release, all: integrate the release service into geth --- accounts/abi/bind/backend.go | 5 +- cmd/geth/main.go | 59 ++- cmd/utils/flags.go | 14 +- contracts/release/contract.go | 474 ------------------ eth/api.go | 30 +- eth/backend.go | 22 +- eth/bind.go | 110 ++++ node/node.go | 3 +- node/service.go | 2 +- release/contract.go | 432 ++++++++++++++++ {contracts/release => release}/contract.sol | 95 ++-- .../release => release}/contract_test.go | 12 +- {contracts/release => release}/generator.go | 0 release/release.go | 147 ++++++ 14 files changed, 822 insertions(+), 583 deletions(-) delete mode 100644 contracts/release/contract.go create mode 100644 eth/bind.go create mode 100644 release/contract.go rename {contracts/release => release}/contract.sol (73%) rename {contracts/release => release}/contract_test.go (97%) rename {contracts/release => release}/generator.go (100%) create mode 100644 release/release.go diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go index 7442557cce..604e1ef267 100644 --- a/accounts/abi/bind/backend.go +++ b/accounts/abi/bind/backend.go @@ -47,7 +47,8 @@ type ContractCaller interface { // used when the user does not provide some needed values, but rather leaves it up // to the transactor to decide. type ContractTransactor interface { - // Nonce retrieves the current pending nonce associated with an account. + // PendingAccountNonce retrieves the current pending nonce associated with an + // account. PendingAccountNonce(account common.Address) (uint64, error) // SuggestGasPrice retrieves the currently suggested gas price to allow a timely @@ -62,7 +63,7 @@ type ContractTransactor interface { EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) // SendTransaction injects the transaction into the pending pool for execution. - SendTransaction(*types.Transaction) error + SendTransaction(tx *types.Transaction) error } // ContractBackend defines the methods needed to allow operating with contract diff --git a/cmd/geth/main.go b/cmd/geth/main.go index ffeb7c1e54..a023497a76 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -18,6 +18,7 @@ package main import ( + "encoding/hex" "fmt" "io/ioutil" "os" @@ -41,31 +42,48 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/release" "github.com/ethereum/go-ethereum/rlp" ) const ( - ClientIdentifier = "Geth" - Version = "1.5.0-unstable" - VersionMajor = 1 - VersionMinor = 5 - VersionPatch = 0 + clientIdentifier = "Geth" // Client identifier to advertise over the network + versionMajor = 1 // Major version component of the current release + versionMinor = 5 // Minor version component of the current release + versionPatch = 0 // Patch version component of the current release + versionMeta = "unstable" // Version metadata to append to the version string + + versionOracle = "0x48a117313039b73ab02fb9f73e04a66defe123ec" // Ethereum address of the Geth release oracle ) var ( - gitCommit string // set via linker flagg - nodeNameVersion string - app *cli.App + gitCommit string // Git SHA1 commit hash of the release (set via linker flags) + verString string // Combined textual representation of all the version components + relConfig release.Config // Structured version information and release oracle config + app *cli.App ) func init() { - if gitCommit == "" { - nodeNameVersion = Version - } else { - nodeNameVersion = Version + "-" + gitCommit[:8] + // Construct the textual version string from the individual components + verString = fmt.Sprintf("%d.%d.%d", versionMajor, versionMinor, versionPatch) + if versionMeta != "" { + verString += "-" + versionMeta } + if gitCommit != "" { + verString += "-" + gitCommit[:8] + } + // Construct the version release oracle configuration + relConfig.Oracle = common.HexToAddress(versionOracle) - app = utils.NewApp(Version, "the go-ethereum command line interface") + relConfig.Major = uint32(versionMajor) + relConfig.Minor = uint32(versionMinor) + relConfig.Patch = uint32(versionPatch) + + commit, _ := hex.DecodeString(gitCommit) + copy(relConfig.Commit[:], commit) + + // Initialize the CLI app and start Geth + app = utils.NewApp(verString, "the go-ethereum command line interface") app.Action = geth app.HideVersion = true // we have a command to print the version app.Commands = []cli.Command{ @@ -257,7 +275,7 @@ func makeDefaultExtra() []byte { Name string GoVersion string Os string - }{uint(VersionMajor<<16 | VersionMinor<<8 | VersionPatch), ClientIdentifier, runtime.Version(), runtime.GOOS} + }{uint(versionMajor<<16 | versionMinor<<8 | versionPatch), clientIdentifier, runtime.Version(), runtime.GOOS} extra, err := rlp.EncodeToBytes(clientInfo) if err != nil { glog.V(logger.Warn).Infoln("error setting canonical miner information:", err) @@ -275,7 +293,7 @@ func makeDefaultExtra() []byte { // It creates a default node based on the command line arguments and runs it in // blocking mode, waiting for it to be shut down. func geth(ctx *cli.Context) { - node := utils.MakeSystemNode(ClientIdentifier, nodeNameVersion, makeDefaultExtra(), ctx) + node := utils.MakeSystemNode(clientIdentifier, verString, relConfig, makeDefaultExtra(), ctx) startNode(ctx, node) node.Wait() } @@ -339,7 +357,7 @@ func initGenesis(ctx *cli.Context) { // same time. func console(ctx *cli.Context) { // Create and start the node based on the CLI flags - node := utils.MakeSystemNode(ClientIdentifier, nodeNameVersion, makeDefaultExtra(), ctx) + node := utils.MakeSystemNode(clientIdentifier, verString, relConfig, makeDefaultExtra(), ctx) startNode(ctx, node) // Attach to the newly started node, and either execute script or become interactive @@ -372,7 +390,7 @@ func console(ctx *cli.Context) { // of the JavaScript files specified as command arguments. func execScripts(ctx *cli.Context) { // Create and start the node based on the CLI flags - node := utils.MakeSystemNode(ClientIdentifier, nodeNameVersion, makeDefaultExtra(), ctx) + node := utils.MakeSystemNode(clientIdentifier, verString, relConfig, makeDefaultExtra(), ctx) startNode(ctx, node) defer node.Stop() @@ -488,11 +506,8 @@ func gpubench(ctx *cli.Context) { } func version(c *cli.Context) { - fmt.Println(ClientIdentifier) - fmt.Println("Version:", Version) - if gitCommit != "" { - fmt.Println("Git Commit:", gitCommit) - } + fmt.Println(clientIdentifier) + fmt.Println("Version:", version) fmt.Println("Protocol Versions:", eth.ProtocolVersions) fmt.Println("Network Id:", c.GlobalInt(utils.NetworkIdFlag.Name)) fmt.Println("Go Version:", runtime.Version()) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 86e9e9b0d7..43dbc37f74 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -34,7 +34,6 @@ import ( "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/versions" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" @@ -49,6 +48,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/release" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/whisper" ) @@ -642,7 +642,7 @@ func MakePasswordList(ctx *cli.Context) []string { // MakeSystemNode sets up a local node, configures the services to launch and // assembles the P2P protocol stack. -func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node.Node { +func MakeSystemNode(name, version string, relconf release.Config, extra []byte, ctx *cli.Context) *node.Node { // Avoid conflicting network flags networks, netFlags := 0, []cli.BoolFlag{DevModeFlag, TestNetFlag, OlympicFlag} for _, flag := range netFlags { @@ -773,12 +773,10 @@ func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node. Fatalf("Failed to register the Whisper service: %v", err) } } - - err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return versions.NewVersionCheck(ctx) - }) - if err != nil { - Fatalf("Failed to register the Version Check service: %v", err) + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + return release.NewReleaseService(ctx, relconf) + }); err != nil { + Fatalf("Failed to register the Geth release oracle service: %v", err) } return stack } diff --git a/contracts/release/contract.go b/contracts/release/contract.go deleted file mode 100644 index ffdcc14cd2..0000000000 --- a/contracts/release/contract.go +++ /dev/null @@ -1,474 +0,0 @@ -// This file is an automatically generated Go binding. Do not modify as any -// change will likely be lost upon the next re-generation! - -package release - -import ( - "math/big" - "strings" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" -) - -// ReleaseOracleABI is the input ABI used to generate the binding from. -const ReleaseOracleABI = `[{"constant":false,"inputs":[],"name":"Nuke","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"}],"name":"Release","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"AuthProposals","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"CurrentVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"time","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"ProposedVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"pass","type":"address[]"},{"name":"fail","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"Promote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"release","type":"bool"}],"name":"updateRelease","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"Demote","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"AuthVotes","outputs":[{"name":"promote","type":"address[]"},{"name":"demote","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"Signers","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"},{"name":"authorize","type":"bool"}],"name":"updateSigner","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"}]` - -// ReleaseOracleBin is the compiled bytecode used for deploying new contracts. -const ReleaseOracleBin = `0x6060604052600160a060020a0333166000908152602081905260409020805460ff1916600190811790915580548082018083558281838015829011606257818360005260206000209182019101606291905b808211156094576000815584016051565b505050919090600052602060002090016000508054600160a060020a0319163317905550611262806100986000396000f35b5090566060604052361561008d5760e060020a60003504630443b1ad811461008f5780630d618178146100a0578063282fe4e5146100bd5780632b225f291461012c5780634c327071146101515780636195db9c14610276578063645dce721461028757806380bbbd4a146102d6578063a29226f2146102e7578063f04c4758146103e1578063f460590b1461044d575b005b61008d61072060008080808061029a565b61008d60043560243560443560643561071a84848484600161029a565b6104d360408051602081810183526000825282516003805480840283018401909552848252929390929183018282801561044257602002820191906000526020600020908154600160a060020a0316815260019190910190602001808311610423575b5050505050905061044a565b61051d600060006000600060006000600860005080549050600014156106895761070a565b610551604080516020818101835260008083528351808301855281815260045460068054875181870281018701909852808852939687968796879691959463ffffffff818116956401000000008304821695604060020a840490921694606060020a93849004909302939092600792918491908301828280156101fe57602002820191906000526020600020905b8154600160a060020a03168152600191909101906020018083116101df575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561025b57602002820191906000526020600020905b8154600160a060020a031681526001919091019060200180831161023c575b50505050509050955095509550955095509550909192939495565b61008d600435610712816001610457565b61008d6004356024356044356064356084355b600160a060020a033316600090815260208190526040812054819060ff16156111f957821580156102cc575060065481145b15610c81576111f9565b61008d600435610712816000610457565b6106046004356040805160208181018352600080835283518083018552818152600160a060020a038616825260028352908490208054855181850281018501909652808652939491939092600184019291849183018282801561037457602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610355575b50505050509150808054806020026020016040519081016040528092919081815260200182805480156103d157602002820191906000526020600020905b8154600160a060020a03168152600191909101906020018083116103b2575b5050505050905091509150915091565b604080516020818101835260008252600180548451818402810184019095528085526104d3949283018282801561044257602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610423575b505050505090505b90565b61008d6004356024355b600160a060020a033316600090815260208190526040812054819060ff161561071a57600160a060020a038416815260026020526040812091505b8154811015610722578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a0316141561076d5761071a565b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b6040805163ffffffff9687168152948616602086015294909216838501526060830152608082015290519081900360a00190f35b604051808763ffffffff1681526020018663ffffffff1681526020018563ffffffff16815260200184815260200180602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019850505050505050505060405180910390f35b6040518080602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f15090500194505050505060405180910390f35b600880546000198101908110156100025760009182526004027ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30190508054600182015463ffffffff8281169950640100000000830481169850604060020a8304169650606060020a91829004909102945067ffffffffffffffff16925090505b509091929394565b50565b505050505b50505050565b565b5060005b60018201548110156107755733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a031614156107bf5761071a565b600101610492565b8154600014801561078a575060018201546000145b156107e757600380546001810180835582818380158290116107c7578183600052602060002091820191016107c7919061086d565b600101610726565b5050506000928352506020909120018054600160a060020a031916851790555b82156108855781546001810180845583919082818380158290116108ba576000838152602090206108ba91810190830161086d565b5050506000928352506020909120018054600160a060020a031916851790555b600160a060020a038416600090815260026020908152604082208054838255818452918320909291610b6991908101905b80821115610881576000815560010161086d565b5090565b81600101600050805480600101828181548183558181151161097657818360005260206000209182019101610976919061086d565b5050506000928352506020909120018054600160a060020a031916331790556001548254600290910490116108ee5761071a565b8280156109145750600160a060020a03841660009081526020819052604090205460ff16155b156109ad57600160a060020a0384166000908152602081905260409020805460ff191660019081179091558054808201808355828183801582901161081c57600083905261081c9060008051602061124283398151915290810190830161086d565b5050506000928352506020909120018054600160a060020a031916331790556001805490830154600290910490116108ee5761071a565b821580156109d35750600160a060020a03841660009081526020819052604090205460ff165b1561083c5750600160a060020a0383166000908152602081905260408120805460ff191690555b60015481101561083c5783600160a060020a03166001600050828154811015610002576000919091526000805160206112428339815191520154600160a060020a03161415610add576001805460001981019081101561000257600091825260008051602061124283398151915201909054906101000a9004600160a060020a0316600160005082815481101561000257600080516020611242833981519152018054600160a060020a03191690921790915580546000198101808355909190828015829011610ae557818360005260206000209182019101610ae5919061086d565b6001016109fa565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529194509192508290610b3f907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f9081019061086d565b5060018201805460008083559182526020909120610b5f9181019061086d565b505050505061083c565b5060018201805460008083559182526020909120610b899181019061086d565b506000925050505b60035481101561071a5783600160a060020a03166003600050828154811015610002576000919091526000805160206112228339815191520154600160a060020a03161415610c79576003805460001981019081101561000257600091825260008051602061122283398151915201909054906101000a9004600160a060020a0316600360005082815481101561000257600080516020611222833981519152018054600160a060020a03191690921790915580546000198101808355909190828015829011610715576107159060008051602061122283398151915290810190830161086d565b600101610b91565b60065460001415610cdf576004805463ffffffff1916881767ffffffff0000000019166401000000008802176bffffffff00000000000000001916604060020a8702176bffffffffffffffffffffffff16606060020a808704021790555b828015610d49575060045463ffffffff8881169116141580610d155750600454640100000000900463ffffffff90811690871614155b80610d32575060045463ffffffff808716604060020a9092041614155b80610d495750600454606060020a90819004028414155b15610d53576111f9565b506006905060005b8154811015610d9c578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610de7576111f9565b5060005b6001820154811015610def5733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610e26576111f9565b600101610d5b565b8215610e2e578154600181018084558391908281838015829011610e6357818360005260206000209182019101610e63919061086d565b600101610da0565b816001016000508054806001018281815481835581811511610ee657818360005260206000209182019101610ee6919061086d565b5050506000928352506020909120018054600160a060020a03191633179055600154825460029091049011610e97576111f9565b8215610f1d576005805467ffffffffffffffff19164217905560088054600181018083558281838015829011610f7257600402816004028360005260206000209182019101610f72919061108b565b5050506000928352506020909120018054600160a060020a03191633179055600180549083015460029091049011610e97576111f9565b600060048181556005805467ffffffffffffffff19169055600680548382558184529192918290611202907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f9081019061086d565b5050509190906000526020600020906004020160005060048054825463ffffffff191663ffffffff9182161780845582546401000000009081900483160267ffffffff000000001991909116178084558254604060020a908190049092169091026bffffffff00000000000000001991909116178083558154606060020a908190048102819004026bffffffffffffffffffffffff9190911617825560055460018301805467ffffffffffffffff191667ffffffffffffffff9092169190911790556006805460028401805482825560008281526020902094959491928392918201918582156110ea5760005260206000209182015b828111156110ea578254825591600101919060010190611068565b505050506001015b8082111561088157600080825560018201805467ffffffffffffffff191690556002820180548282558183526020832083916110ca919081019061086d565b50600182018054600080835591825260209091206110839181019061086d565b506111109291505b80821115610881578054600160a060020a03191681556001016110f2565b505060018181018054918401805480835560008381526020902092938301929091821561115e5760005260206000209182015b8281111561115e578254825591600101919060010190611143565b5061116a9291506110f2565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529197509195509093508492506111c991507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f9081019061086d565b50600182018054600080835591825260209091206111e99181019061086d565b50505050506111f9565b50505050505b50505050505050565b50600182018054600080835591825260209091206111f39181019061086d56c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85bb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6` - -// DeployReleaseOracle deploys a new Ethereum contract, binding an instance of ReleaseOracle to it. -func DeployReleaseOracle(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ReleaseOracle, error) { - parsed, err := abi.JSON(strings.NewReader(ReleaseOracleABI)) - if err != nil { - return common.Address{}, nil, nil, err - } - address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ReleaseOracleBin), backend) - if err != nil { - return common.Address{}, nil, nil, err - } - return address, tx, &ReleaseOracle{ReleaseOracleCaller: ReleaseOracleCaller{contract: contract}, ReleaseOracleTransactor: ReleaseOracleTransactor{contract: contract}}, nil -} - -// ReleaseOracle is an auto generated Go binding around an Ethereum contract. -type ReleaseOracle struct { - ReleaseOracleCaller // Read-only binding to the contract - ReleaseOracleTransactor // Write-only binding to the contract -} - -// ReleaseOracleCaller is an auto generated read-only Go binding around an Ethereum contract. -type ReleaseOracleCaller struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// ReleaseOracleTransactor is an auto generated write-only Go binding around an Ethereum contract. -type ReleaseOracleTransactor struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// ReleaseOracleSession is an auto generated Go binding around an Ethereum contract, -// with pre-set call and transact options. -type ReleaseOracleSession struct { - Contract *ReleaseOracle // Generic contract binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// ReleaseOracleCallerSession is an auto generated read-only Go binding around an Ethereum contract, -// with pre-set call options. -type ReleaseOracleCallerSession struct { - Contract *ReleaseOracleCaller // Generic contract caller binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session -} - -// ReleaseOracleTransactorSession is an auto generated write-only Go binding around an Ethereum contract, -// with pre-set transact options. -type ReleaseOracleTransactorSession struct { - Contract *ReleaseOracleTransactor // Generic contract transactor binding to set the session for - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// ReleaseOracleRaw is an auto generated low-level Go binding around an Ethereum contract. -type ReleaseOracleRaw struct { - Contract *ReleaseOracle // Generic contract binding to access the raw methods on -} - -// ReleaseOracleCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. -type ReleaseOracleCallerRaw struct { - Contract *ReleaseOracleCaller // Generic read-only contract binding to access the raw methods on -} - -// ReleaseOracleTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. -type ReleaseOracleTransactorRaw struct { - Contract *ReleaseOracleTransactor // Generic write-only contract binding to access the raw methods on -} - -// NewReleaseOracle creates a new instance of ReleaseOracle, bound to a specific deployed contract. -func NewReleaseOracle(address common.Address, backend bind.ContractBackend) (*ReleaseOracle, error) { - contract, err := bindReleaseOracle(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor)) - if err != nil { - return nil, err - } - return &ReleaseOracle{ReleaseOracleCaller: ReleaseOracleCaller{contract: contract}, ReleaseOracleTransactor: ReleaseOracleTransactor{contract: contract}}, nil -} - -// NewReleaseOracleCaller creates a new read-only instance of ReleaseOracle, bound to a specific deployed contract. -func NewReleaseOracleCaller(address common.Address, caller bind.ContractCaller) (*ReleaseOracleCaller, error) { - contract, err := bindReleaseOracle(address, caller, nil) - if err != nil { - return nil, err - } - return &ReleaseOracleCaller{contract: contract}, nil -} - -// NewReleaseOracleTransactor creates a new write-only instance of ReleaseOracle, bound to a specific deployed contract. -func NewReleaseOracleTransactor(address common.Address, transactor bind.ContractTransactor) (*ReleaseOracleTransactor, error) { - contract, err := bindReleaseOracle(address, nil, transactor) - if err != nil { - return nil, err - } - return &ReleaseOracleTransactor{contract: contract}, nil -} - -// bindReleaseOracle binds a generic wrapper to an already deployed contract. -func bindReleaseOracle(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(ReleaseOracleABI)) - if err != nil { - return nil, err - } - return bind.NewBoundContract(address, parsed, caller, transactor), nil -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_ReleaseOracle *ReleaseOracleRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { - return _ReleaseOracle.Contract.ReleaseOracleCaller.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_ReleaseOracle *ReleaseOracleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _ReleaseOracle.Contract.ReleaseOracleTransactor.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_ReleaseOracle *ReleaseOracleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _ReleaseOracle.Contract.ReleaseOracleTransactor.contract.Transact(opts, method, params...) -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_ReleaseOracle *ReleaseOracleCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { - return _ReleaseOracle.Contract.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_ReleaseOracle *ReleaseOracleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _ReleaseOracle.Contract.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_ReleaseOracle *ReleaseOracleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _ReleaseOracle.Contract.contract.Transact(opts, method, params...) -} - -// AuthProposals is a free data retrieval call binding the contract method 0x282fe4e5. -// -// Solidity: function AuthProposals() constant returns(address[]) -func (_ReleaseOracle *ReleaseOracleCaller) AuthProposals(opts *bind.CallOpts) ([]common.Address, error) { - var ( - ret0 = new([]common.Address) - ) - out := ret0 - err := _ReleaseOracle.contract.Call(opts, out, "AuthProposals") - return *ret0, err -} - -// AuthProposals is a free data retrieval call binding the contract method 0x282fe4e5. -// -// Solidity: function AuthProposals() constant returns(address[]) -func (_ReleaseOracle *ReleaseOracleSession) AuthProposals() ([]common.Address, error) { - return _ReleaseOracle.Contract.AuthProposals(&_ReleaseOracle.CallOpts) -} - -// AuthProposals is a free data retrieval call binding the contract method 0x282fe4e5. -// -// Solidity: function AuthProposals() constant returns(address[]) -func (_ReleaseOracle *ReleaseOracleCallerSession) AuthProposals() ([]common.Address, error) { - return _ReleaseOracle.Contract.AuthProposals(&_ReleaseOracle.CallOpts) -} - -// AuthVotes is a free data retrieval call binding the contract method 0xa29226f2. -// -// Solidity: function AuthVotes(user address) constant returns(promote address[], demote address[]) -func (_ReleaseOracle *ReleaseOracleCaller) AuthVotes(opts *bind.CallOpts, user common.Address) (struct { - Promote []common.Address - Demote []common.Address -}, error) { - ret := new(struct { - Promote []common.Address - Demote []common.Address - }) - out := ret - err := _ReleaseOracle.contract.Call(opts, out, "AuthVotes", user) - return *ret, err -} - -// AuthVotes is a free data retrieval call binding the contract method 0xa29226f2. -// -// Solidity: function AuthVotes(user address) constant returns(promote address[], demote address[]) -func (_ReleaseOracle *ReleaseOracleSession) AuthVotes(user common.Address) (struct { - Promote []common.Address - Demote []common.Address -}, error) { - return _ReleaseOracle.Contract.AuthVotes(&_ReleaseOracle.CallOpts, user) -} - -// AuthVotes is a free data retrieval call binding the contract method 0xa29226f2. -// -// Solidity: function AuthVotes(user address) constant returns(promote address[], demote address[]) -func (_ReleaseOracle *ReleaseOracleCallerSession) AuthVotes(user common.Address) (struct { - Promote []common.Address - Demote []common.Address -}, error) { - return _ReleaseOracle.Contract.AuthVotes(&_ReleaseOracle.CallOpts, user) -} - -// CurrentVersion is a free data retrieval call binding the contract method 0x2b225f29. -// -// Solidity: function CurrentVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, time uint256) -func (_ReleaseOracle *ReleaseOracleCaller) CurrentVersion(opts *bind.CallOpts) (struct { - Major uint32 - Minor uint32 - Patch uint32 - Commit [20]byte - Time *big.Int -}, error) { - ret := new(struct { - Major uint32 - Minor uint32 - Patch uint32 - Commit [20]byte - Time *big.Int - }) - out := ret - err := _ReleaseOracle.contract.Call(opts, out, "CurrentVersion") - return *ret, err -} - -// CurrentVersion is a free data retrieval call binding the contract method 0x2b225f29. -// -// Solidity: function CurrentVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, time uint256) -func (_ReleaseOracle *ReleaseOracleSession) CurrentVersion() (struct { - Major uint32 - Minor uint32 - Patch uint32 - Commit [20]byte - Time *big.Int -}, error) { - return _ReleaseOracle.Contract.CurrentVersion(&_ReleaseOracle.CallOpts) -} - -// CurrentVersion is a free data retrieval call binding the contract method 0x2b225f29. -// -// Solidity: function CurrentVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, time uint256) -func (_ReleaseOracle *ReleaseOracleCallerSession) CurrentVersion() (struct { - Major uint32 - Minor uint32 - Patch uint32 - Commit [20]byte - Time *big.Int -}, error) { - return _ReleaseOracle.Contract.CurrentVersion(&_ReleaseOracle.CallOpts) -} - -// ProposedVersion is a free data retrieval call binding the contract method 0x4c327071. -// -// Solidity: function ProposedVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, pass address[], fail address[]) -func (_ReleaseOracle *ReleaseOracleCaller) ProposedVersion(opts *bind.CallOpts) (struct { - Major uint32 - Minor uint32 - Patch uint32 - Commit [20]byte - Pass []common.Address - Fail []common.Address -}, error) { - ret := new(struct { - Major uint32 - Minor uint32 - Patch uint32 - Commit [20]byte - Pass []common.Address - Fail []common.Address - }) - out := ret - err := _ReleaseOracle.contract.Call(opts, out, "ProposedVersion") - return *ret, err -} - -// ProposedVersion is a free data retrieval call binding the contract method 0x4c327071. -// -// Solidity: function ProposedVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, pass address[], fail address[]) -func (_ReleaseOracle *ReleaseOracleSession) ProposedVersion() (struct { - Major uint32 - Minor uint32 - Patch uint32 - Commit [20]byte - Pass []common.Address - Fail []common.Address -}, error) { - return _ReleaseOracle.Contract.ProposedVersion(&_ReleaseOracle.CallOpts) -} - -// ProposedVersion is a free data retrieval call binding the contract method 0x4c327071. -// -// Solidity: function ProposedVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, pass address[], fail address[]) -func (_ReleaseOracle *ReleaseOracleCallerSession) ProposedVersion() (struct { - Major uint32 - Minor uint32 - Patch uint32 - Commit [20]byte - Pass []common.Address - Fail []common.Address -}, error) { - return _ReleaseOracle.Contract.ProposedVersion(&_ReleaseOracle.CallOpts) -} - -// Signers is a free data retrieval call binding the contract method 0xf04c4758. -// -// Solidity: function Signers() constant returns(address[]) -func (_ReleaseOracle *ReleaseOracleCaller) Signers(opts *bind.CallOpts) ([]common.Address, error) { - var ( - ret0 = new([]common.Address) - ) - out := ret0 - err := _ReleaseOracle.contract.Call(opts, out, "Signers") - return *ret0, err -} - -// Signers is a free data retrieval call binding the contract method 0xf04c4758. -// -// Solidity: function Signers() constant returns(address[]) -func (_ReleaseOracle *ReleaseOracleSession) Signers() ([]common.Address, error) { - return _ReleaseOracle.Contract.Signers(&_ReleaseOracle.CallOpts) -} - -// Signers is a free data retrieval call binding the contract method 0xf04c4758. -// -// Solidity: function Signers() constant returns(address[]) -func (_ReleaseOracle *ReleaseOracleCallerSession) Signers() ([]common.Address, error) { - return _ReleaseOracle.Contract.Signers(&_ReleaseOracle.CallOpts) -} - -// Demote is a paid mutator transaction binding the contract method 0x80bbbd4a. -// -// Solidity: function Demote(user address) returns() -func (_ReleaseOracle *ReleaseOracleTransactor) Demote(opts *bind.TransactOpts, user common.Address) (*types.Transaction, error) { - return _ReleaseOracle.contract.Transact(opts, "Demote", user) -} - -// Demote is a paid mutator transaction binding the contract method 0x80bbbd4a. -// -// Solidity: function Demote(user address) returns() -func (_ReleaseOracle *ReleaseOracleSession) Demote(user common.Address) (*types.Transaction, error) { - return _ReleaseOracle.Contract.Demote(&_ReleaseOracle.TransactOpts, user) -} - -// Demote is a paid mutator transaction binding the contract method 0x80bbbd4a. -// -// Solidity: function Demote(user address) returns() -func (_ReleaseOracle *ReleaseOracleTransactorSession) Demote(user common.Address) (*types.Transaction, error) { - return _ReleaseOracle.Contract.Demote(&_ReleaseOracle.TransactOpts, user) -} - -// Nuke is a paid mutator transaction binding the contract method 0x0443b1ad. -// -// Solidity: function Nuke() returns() -func (_ReleaseOracle *ReleaseOracleTransactor) Nuke(opts *bind.TransactOpts) (*types.Transaction, error) { - return _ReleaseOracle.contract.Transact(opts, "Nuke") -} - -// Nuke is a paid mutator transaction binding the contract method 0x0443b1ad. -// -// Solidity: function Nuke() returns() -func (_ReleaseOracle *ReleaseOracleSession) Nuke() (*types.Transaction, error) { - return _ReleaseOracle.Contract.Nuke(&_ReleaseOracle.TransactOpts) -} - -// Nuke is a paid mutator transaction binding the contract method 0x0443b1ad. -// -// Solidity: function Nuke() returns() -func (_ReleaseOracle *ReleaseOracleTransactorSession) Nuke() (*types.Transaction, error) { - return _ReleaseOracle.Contract.Nuke(&_ReleaseOracle.TransactOpts) -} - -// Promote is a paid mutator transaction binding the contract method 0x6195db9c. -// -// Solidity: function Promote(user address) returns() -func (_ReleaseOracle *ReleaseOracleTransactor) Promote(opts *bind.TransactOpts, user common.Address) (*types.Transaction, error) { - return _ReleaseOracle.contract.Transact(opts, "Promote", user) -} - -// Promote is a paid mutator transaction binding the contract method 0x6195db9c. -// -// Solidity: function Promote(user address) returns() -func (_ReleaseOracle *ReleaseOracleSession) Promote(user common.Address) (*types.Transaction, error) { - return _ReleaseOracle.Contract.Promote(&_ReleaseOracle.TransactOpts, user) -} - -// Promote is a paid mutator transaction binding the contract method 0x6195db9c. -// -// Solidity: function Promote(user address) returns() -func (_ReleaseOracle *ReleaseOracleTransactorSession) Promote(user common.Address) (*types.Transaction, error) { - return _ReleaseOracle.Contract.Promote(&_ReleaseOracle.TransactOpts, user) -} - -// Release is a paid mutator transaction binding the contract method 0x0d618178. -// -// Solidity: function Release(major uint32, minor uint32, patch uint32, commit bytes20) returns() -func (_ReleaseOracle *ReleaseOracleTransactor) Release(opts *bind.TransactOpts, major uint32, minor uint32, patch uint32, commit [20]byte) (*types.Transaction, error) { - return _ReleaseOracle.contract.Transact(opts, "Release", major, minor, patch, commit) -} - -// Release is a paid mutator transaction binding the contract method 0x0d618178. -// -// Solidity: function Release(major uint32, minor uint32, patch uint32, commit bytes20) returns() -func (_ReleaseOracle *ReleaseOracleSession) Release(major uint32, minor uint32, patch uint32, commit [20]byte) (*types.Transaction, error) { - return _ReleaseOracle.Contract.Release(&_ReleaseOracle.TransactOpts, major, minor, patch, commit) -} - -// Release is a paid mutator transaction binding the contract method 0x0d618178. -// -// Solidity: function Release(major uint32, minor uint32, patch uint32, commit bytes20) returns() -func (_ReleaseOracle *ReleaseOracleTransactorSession) Release(major uint32, minor uint32, patch uint32, commit [20]byte) (*types.Transaction, error) { - return _ReleaseOracle.Contract.Release(&_ReleaseOracle.TransactOpts, major, minor, patch, commit) -} - -// UpdateRelease is a paid mutator transaction binding the contract method 0x645dce72. -// -// Solidity: function updateRelease(major uint32, minor uint32, patch uint32, commit bytes20, release bool) returns() -func (_ReleaseOracle *ReleaseOracleTransactor) UpdateRelease(opts *bind.TransactOpts, major uint32, minor uint32, patch uint32, commit [20]byte, release bool) (*types.Transaction, error) { - return _ReleaseOracle.contract.Transact(opts, "updateRelease", major, minor, patch, commit, release) -} - -// UpdateRelease is a paid mutator transaction binding the contract method 0x645dce72. -// -// Solidity: function updateRelease(major uint32, minor uint32, patch uint32, commit bytes20, release bool) returns() -func (_ReleaseOracle *ReleaseOracleSession) UpdateRelease(major uint32, minor uint32, patch uint32, commit [20]byte, release bool) (*types.Transaction, error) { - return _ReleaseOracle.Contract.UpdateRelease(&_ReleaseOracle.TransactOpts, major, minor, patch, commit, release) -} - -// UpdateRelease is a paid mutator transaction binding the contract method 0x645dce72. -// -// Solidity: function updateRelease(major uint32, minor uint32, patch uint32, commit bytes20, release bool) returns() -func (_ReleaseOracle *ReleaseOracleTransactorSession) UpdateRelease(major uint32, minor uint32, patch uint32, commit [20]byte, release bool) (*types.Transaction, error) { - return _ReleaseOracle.Contract.UpdateRelease(&_ReleaseOracle.TransactOpts, major, minor, patch, commit, release) -} - -// UpdateSigner is a paid mutator transaction binding the contract method 0xf460590b. -// -// Solidity: function updateSigner(user address, authorize bool) returns() -func (_ReleaseOracle *ReleaseOracleTransactor) UpdateSigner(opts *bind.TransactOpts, user common.Address, authorize bool) (*types.Transaction, error) { - return _ReleaseOracle.contract.Transact(opts, "updateSigner", user, authorize) -} - -// UpdateSigner is a paid mutator transaction binding the contract method 0xf460590b. -// -// Solidity: function updateSigner(user address, authorize bool) returns() -func (_ReleaseOracle *ReleaseOracleSession) UpdateSigner(user common.Address, authorize bool) (*types.Transaction, error) { - return _ReleaseOracle.Contract.UpdateSigner(&_ReleaseOracle.TransactOpts, user, authorize) -} - -// UpdateSigner is a paid mutator transaction binding the contract method 0xf460590b. -// -// Solidity: function updateSigner(user address, authorize bool) returns() -func (_ReleaseOracle *ReleaseOracleTransactorSession) UpdateSigner(user common.Address, authorize bool) (*types.Transaction, error) { - return _ReleaseOracle.Contract.UpdateSigner(&_ReleaseOracle.TransactOpts, user, authorize) -} diff --git a/eth/api.go b/eth/api.go index 2c84cf471e..bd81799626 100644 --- a/eth/api.go +++ b/eth/api.go @@ -52,14 +52,14 @@ import ( "golang.org/x/net/context" ) -// ErrNoCode is returned by call and transact operations for which the requested +// errNoCode is returned by call and transact operations for which the requested // recipient contract to operate on does not exist in the state db or does not // have any code associated with it (i.e. suicided). // // Please note, this error string is part of the RPC API and is expected by the // native contract bindings to signal this particular error. Do not change this // as it will break all dependent code! -var ErrNoCode = errors.New("no contract code at given address") +var errNoCode = errors.New("no contract code at given address") const defaultGas = uint64(90000) @@ -107,8 +107,11 @@ type PublicEthereumAPI struct { } // NewPublicEthereumAPI creates a new Ethereum protocol API. -func NewPublicEthereumAPI(e *Ethereum, gpo *GasPriceOracle) *PublicEthereumAPI { - return &PublicEthereumAPI{e, gpo} +func NewPublicEthereumAPI(e *Ethereum) *PublicEthereumAPI { + return &PublicEthereumAPI{ + e: e, + gpo: e.gpo, + } } // GasPrice returns a suggestion for a gas price. @@ -717,7 +720,7 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st // If there's no code to interact with, respond with an appropriate error if args.To != nil { if code := stateDb.GetCode(*args.To); len(code) == 0 { - return "0x", nil, ErrNoCode + return "0x", nil, errNoCode } } // Retrieve the account state object to interact with @@ -914,18 +917,17 @@ type PublicTransactionPoolAPI struct { } // NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool. -func NewPublicTransactionPoolAPI(e *Ethereum, gpo *GasPriceOracle) *PublicTransactionPoolAPI { +func NewPublicTransactionPoolAPI(e *Ethereum) *PublicTransactionPoolAPI { api := &PublicTransactionPoolAPI{ - eventMux: e.EventMux(), - gpo: gpo, - chainDb: e.ChainDb(), - bc: e.BlockChain(), - am: e.AccountManager(), - txPool: e.TxPool(), - miner: e.Miner(), + eventMux: e.eventMux, + gpo: e.gpo, + chainDb: e.chainDb, + bc: e.blockchain, + am: e.accountManager, + txPool: e.txPool, + miner: e.miner, pendingTxSubs: make(map[string]rpc.Subscription), } - go api.subscriptionLoop() return api diff --git a/eth/backend.go b/eth/backend.go index 76cf8783b9..9722e96257 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -119,6 +119,7 @@ type Ethereum struct { protocolManager *ProtocolManager SolcPath string solc *compiler.Solidity + gpo *GasPriceOracle GpoMinGasPrice *big.Int GpoMaxGasPrice *big.Int @@ -260,6 +261,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } return nil, err } + eth.gpo = NewGasPriceOracle(eth) + newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) eth.txPool = newPool @@ -276,34 +279,31 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. func (s *Ethereum) APIs() []rpc.API { - // share gas price oracle in API's - gpo := NewGasPriceOracle(s) - return []rpc.API{ { Namespace: "eth", Version: "1.0", - Service: NewPublicEthereumAPI(s, gpo), + Service: NewPublicEthereumAPI(s), Public: true, }, { Namespace: "eth", Version: "1.0", - Service: NewPublicAccountAPI(s.AccountManager()), + Service: NewPublicAccountAPI(s.accountManager), Public: true, }, { Namespace: "personal", Version: "1.0", - Service: NewPrivateAccountAPI(s.AccountManager()), + Service: NewPrivateAccountAPI(s.accountManager), Public: false, }, { Namespace: "eth", Version: "1.0", - Service: NewPublicBlockChainAPI(s.chainConfig, s.BlockChain(), s.Miner(), s.ChainDb(), gpo, s.EventMux(), s.AccountManager()), + Service: NewPublicBlockChainAPI(s.chainConfig, s.blockchain, s.miner, s.chainDb, s.gpo, s.eventMux, s.accountManager), Public: true, }, { Namespace: "eth", Version: "1.0", - Service: NewPublicTransactionPoolAPI(s, gpo), + Service: NewPublicTransactionPoolAPI(s), Public: true, }, { Namespace: "eth", @@ -313,7 +313,7 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: downloader.NewPublicDownloaderAPI(s.Downloader(), s.EventMux()), + Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), Public: true, }, { Namespace: "miner", @@ -328,7 +328,7 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ChainDb(), s.EventMux()), + Service: filters.NewPublicFilterAPI(s.chainDb, s.eventMux), Public: true, }, { Namespace: "admin", @@ -351,7 +351,7 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "admin", Version: "1.0", - Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.BlockChain(), s.ChainDb(), s.TxPool(), s.AccountManager()), + Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.blockchain, s.chainDb, s.txPool, s.accountManager), }, } } diff --git a/eth/bind.go b/eth/bind.go new file mode 100644 index 0000000000..3a3eca0623 --- /dev/null +++ b/eth/bind.go @@ -0,0 +1,110 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" +) + +// ContractBackend implements bind.ContractBackend with direct calls to Ethereum +// internals to support operating on contracts within subprotocols like eth and +// swarm. +// +// Internally this backend uses the already exposed API endpoints of the Ethereum +// object. These should be rewritten to internal Go method calls when the Go API +// is refactored to support a clean library use. +type ContractBackend struct { + eapi *PublicEthereumAPI // Wrapper around the Ethereum object to access metadata + bcapi *PublicBlockChainAPI // Wrapper around the blockchain to access chain data + txapi *PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data +} + +// NewContractBackend creates a new native contract backend using an existing +// Etheruem object. +func NewContractBackend(eth *Ethereum) *ContractBackend { + return &ContractBackend{ + eapi: NewPublicEthereumAPI(eth), + bcapi: NewPublicBlockChainAPI(eth.chainConfig, eth.blockchain, eth.miner, eth.chainDb, eth.gpo, eth.eventMux, eth.accountManager), + txapi: NewPublicTransactionPoolAPI(eth), + } +} + +// ContractCall implements bind.ContractCaller executing an Ethereum contract +// call with the specified data as the input. The pending flag requests execution +// against the pending block, not the stable head of the chain. +func (b *ContractBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) { + // Convert the input args to the API spec + args := CallArgs{ + To: &contract, + Data: common.ToHex(data), + } + block := rpc.LatestBlockNumber + if pending { + block = rpc.PendingBlockNumber + } + // Execute the call and convert the output back to Go types + out, err := b.bcapi.Call(args, block) + if err == errNoCode { + err = bind.ErrNoCode + } + return common.FromHex(out), err +} + +// PendingAccountNonce implements bind.ContractTransactor retrieving the current +// pending nonce associated with an account. +func (b *ContractBackend) PendingAccountNonce(account common.Address) (uint64, error) { + out, err := b.txapi.GetTransactionCount(account, rpc.PendingBlockNumber) + return out.Uint64(), err +} + +// SuggestGasPrice implements bind.ContractTransactor retrieving the currently +// suggested gas price to allow a timely execution of a transaction. +func (b *ContractBackend) SuggestGasPrice() (*big.Int, error) { + return b.eapi.GasPrice(), nil +} + +// EstimateGasLimit implements bind.ContractTransactor triing to estimate the gas +// needed to execute a specific transaction based on the current pending state of +// the backend blockchain. There is no guarantee that this is the true gas limit +// requirement as other transactions may be added or removed by miners, but it +// should provide a basis for setting a reasonable default. +func (b *ContractBackend) EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { + out, err := b.bcapi.EstimateGas(CallArgs{ + From: sender, + To: contract, + Value: *rpc.NewHexNumber(value), + Data: common.ToHex(data), + }) + if err == errNoCode { + err = bind.ErrNoCode + } + return out.BigInt(), err +} + +// SendTransaction implements bind.ContractTransactor injects the transaction +// into the pending pool for execution. +func (b *ContractBackend) SendTransaction(tx *types.Transaction) error { + raw, _ := rlp.EncodeToBytes(tx) + _, err := b.txapi.SendRawTransaction(common.ToHex(raw)) + return err +} diff --git a/node/node.go b/node/node.go index 0864253f45..06a1b7aed7 100644 --- a/node/node.go +++ b/node/node.go @@ -311,7 +311,7 @@ func (n *Node) startIPC(apis []rpc.API) error { glog.V(logger.Error).Infof("IPC accept failed: %v", err) continue } - go handler.ServeCodec(rpc.NewJSONCodec(conn), rpc.OptionMethodInvocation | rpc.OptionSubscriptions) + go handler.ServeCodec(rpc.NewJSONCodec(conn), rpc.OptionMethodInvocation|rpc.OptionSubscriptions) } }() // All listeners booted successfully @@ -530,7 +530,6 @@ func (n *Node) Server() *p2p.Server { } // Service retrieves a currently running service registered of a specific type. -// NOTE: must be called with double pointer to service func (n *Node) Service(service interface{}) error { n.lock.RLock() defer n.lock.RUnlock() diff --git a/node/service.go b/node/service.go index 77b2ddc928..4d9a6e42cf 100644 --- a/node/service.go +++ b/node/service.go @@ -68,7 +68,7 @@ type ServiceConstructor func(ctx *ServiceContext) (Service, error) // - Restart logic is not required as the node will create a fresh instance // every time a service is started. type Service interface { - // Protocol retrieves the P2P protocols the service wishes to start. + // Protocols retrieves the P2P protocols the service wishes to start. Protocols() []p2p.Protocol // APIs retrieves the list of RPC descriptors the service provides diff --git a/release/contract.go b/release/contract.go new file mode 100644 index 0000000000..5daf7e4a40 --- /dev/null +++ b/release/contract.go @@ -0,0 +1,432 @@ +// This file is an automatically generated Go binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package release + +import ( + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// ReleaseOracleABI is the input ABI used to generate the binding from. +const ReleaseOracleABI = `[{"constant":true,"inputs":[],"name":"proposedVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"pass","type":"address[]"},{"name":"fail","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"signers","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"demote","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"authVotes","outputs":[{"name":"promote","type":"address[]"},{"name":"demote","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"currentVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"time","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"nuke","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"authProposals","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"promote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"}],"name":"release","outputs":[],"type":"function"},{"inputs":[{"name":"signers","type":"address[]"}],"type":"constructor"}]` + +// ReleaseOracleBin is the compiled bytecode used for deploying new contracts. +const ReleaseOracleBin = `0x60606040526040516114033803806114038339810160405280510160008151600014156100ce57600160a060020a0333168152602081905260408120805460ff191660019081179091558054808201808355828183801582901161015e5781836000526020600020918201910161015e91905b8082111561019957600081558401610072565b505050919090600052602060002090016000848481518110156100025790602001906020020151909190916101000a815481600160a060020a0302191690830217905550506001015b815181101561018957600160006000506000848481518110156100025790602001906020020151600160a060020a0316815260200190815260200160002060006101000a81548160ff0219169083021790555060016000508054806001018281815481835581811511610085576000839052610085906000805160206113e3833981519152908101908301610072565b5050506000929092526000805160206113e3833981519152018054600160a060020a03191633179055505b50506112458061019e6000396000f35b50905600606060405236156100775760e060020a600035046326db7648811461007957806346f0975a1461019e5780635c3d005d1461020a57806364ed31fe146102935780639d888e861461038d578063bc8fbbf8146103b2578063bf8ecf9c146103fb578063d0e0813a14610467578063d67cbec914610478575b005b610495604080516020818101835260008083528351808301855281815260045460068054875181870281018701909852808852939687968796879691959463ffffffff818116956401000000008304821695604060020a840490921694606060020a938490049093029390926007929184919083018282801561012657602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610107575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561018357602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610164575b50505050509050955095509550955095509550909192939495565b6040805160208181018352600082526001805484518184028101840190955280855261054894928301828280156101ff57602002820191906000526020600020905b8154600160a060020a03168152600191909101906020018083116101e0575b505050505090505b90565b6100776004356106d78160005b600160a060020a033316600090815260208190526040812054819060ff16156106df57600160a060020a038416815260026020526040812091505b81548110156106e7578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610732576106df565b6105926004356040805160208181018352600080835283518083018552818152600160a060020a038616825260028352908490208054855181850281018501909652808652939491939092600184019291849183018282801561032057602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610301575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561037d57602002820191906000526020600020905b8154600160a060020a031681526001919091019060200180831161035e575b5050505050905091509150915091565b6106176000600060006000600060006008600050805490506000141561064e576106cf565b6100776106e56000808080805b600160a060020a033316600090815260208190526040812054819060ff161561121c57821580156103f1575060065481145b15610ca55761121c565b6040805160208181018352600082526003805484518184028101840190955280855261054894928301828280156101ff57602002820191906000526020600020908154600160a060020a03168152600191909101906020018083116101e0575b50505050509050610207565b6100776004356106d7816001610217565b6100776004356024356044356064356106df8484848460016103bf565b604051808763ffffffff1681526020018663ffffffff1681526020018563ffffffff16815260200184815260200180602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019850505050505050505060405180910390f35b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b6040518080602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f15090500194505050505060405180910390f35b6040805163ffffffff9687168152948616602086015292909416838301526060830152608082019290925290519081900360a00190f35b600880546000198101908110156100025760009182526004027ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30190508054600182015463ffffffff8281169950640100000000830481169850604060020a8304169650606060020a91829004909102945067ffffffffffffffff16925090505b509091929394565b50565b505050505b50505050565b565b5060005b600182015481101561073a5733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610784576106df565b600101610252565b8154600014801561074f575060018201546000145b156107ac576003805460018101808355828183801582901161078c5781836000526020600020918201910161078c9190610834565b6001016106eb565b5050506000928352506020909120018054600160a060020a031916851790555b821561084c578154600181018084558391908281838015829011610881578183600052602060002091820191016108819190610834565b5050506000928352506020909120018054600160a060020a031916851790555b600160a060020a038416600090815260026020908152604082208054838255818452918320909291610b5c91908101905b808211156108485760008155600101610834565b5090565b816001016000508054806001018281815481835581811511610933578183600052602060002091820191016109339190610834565b5050506000928352506020909120018054600160a060020a031916331790556001548254600290910490116108b5576106df565b8280156108db5750600160a060020a03841660009081526020819052604090205460ff16155b1561096a57600160a060020a0384166000908152602081905260409020805460ff19166001908117909155805480820180835582818380158290116107e3578183600052602060002091820191016107e39190610834565b5050506000928352506020909120018054600160a060020a031916331790556001805490830154600290910490116108b5576106df565b821580156109905750600160a060020a03841660009081526020819052604090205460ff165b156108035750600160a060020a0383166000908152602081905260408120805460ff191690555b6001548110156108035783600160a060020a03166001600050828154811015610002576000919091527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60154600160a060020a03161415610ad057600180546000198101908110156100025760009182527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf601909054906101000a9004600160a060020a03166001600050828154811015610002577fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6018054600160a060020a03191690921790915580546000198101808355909190828015829011610ad857818360005260206000209182019101610ad89190610834565b6001016109b7565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529194509192508290610b32907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610834565b5060018201805460008083559182526020909120610b5291810190610834565b5050505050610803565b5060018201805460008083559182526020909120610b7c91810190610834565b506000925050505b6003548110156106df5783600160a060020a03166003600050828154811015610002576000919091527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0154600160a060020a03161415610c9d57600380546000198101908110156100025760009182527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01909054906101000a9004600160a060020a03166003600050828154811015610002577fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b018054600160a060020a031916909217909155805460001981018083559091908280158290116106da578183600052602060002091820191016106da9190610834565b600101610b84565b60065460001415610d03576004805463ffffffff1916881767ffffffff0000000019166401000000008802176bffffffff00000000000000001916604060020a8702176bffffffffffffffffffffffff16606060020a808704021790555b828015610d6d575060045463ffffffff8881169116141580610d395750600454640100000000900463ffffffff90811690871614155b80610d56575060045463ffffffff868116604060020a9092041614155b80610d6d5750600454606060020a90819004028414155b15610d775761121c565b506006905060005b8154811015610dc0578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610e0b5761121c565b5060005b6001820154811015610e135733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610e485761121c565b600101610d7f565b8215610e50578154600181018084558391908281838015829011610e8557600083815260209020610e85918101908301610834565b600101610dc4565b816001016000508054806001018281815481835581811511610f0857818360005260206000209182019101610f089190610834565b5050506000928352506020909120018054600160a060020a03191633179055600154825460029091049011610eb95761121c565b8215610f3f576005805467ffffffffffffffff19164217905560088054600181018083558281838015829011610f9457600402816004028360005260206000209182019101610f9491906110ae565b5050506000928352506020909120018054600160a060020a03191633179055600180549083015460029091049011610eb95761121c565b600060048181556005805467ffffffffffffffff19169055600680548382558184529192918290611225907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610834565b5050509190906000526020600020906004020160005060048054825463ffffffff191663ffffffff9182161780845582546401000000009081900483160267ffffffff000000001991909116178084558254604060020a908190049092169091026bffffffff00000000000000001991909116178083558154606060020a908190048102819004026bffffffffffffffffffffffff9190911617825560055460018301805467ffffffffffffffff191667ffffffffffffffff9290921691909117905560068054600284018054828255600082815260209020949594919283929182019185821561110d5760005260206000209182015b8281111561110d57825482559160010191906001019061108b565b505050506004015b8082111561084857600080825560018201805467ffffffffffffffff191690556002820180548282558183526020832083916110ed9190810190610834565b50600182018054600080835591825260209091206110a691810190610834565b506111339291505b80821115610848578054600160a060020a0319168155600101611115565b50506001818101805491840180548083556000838152602090209293830192909182156111815760005260206000209182015b82811115611181578254825591600101919060010190611166565b5061118d929150611115565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529197509195509093508492506111ec91507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610834565b506001820180546000808355918252602090912061120c91810190610834565b505050505061121c565b50505050505b50505050505050565b50600182018054600080835591825260209091206112169181019061083456b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6` + +// DeployReleaseOracle deploys a new Ethereum contract, binding an instance of ReleaseOracle to it. +func DeployReleaseOracle(auth *bind.TransactOpts, backend bind.ContractBackend, signers []common.Address) (common.Address, *types.Transaction, *ReleaseOracle, error) { + parsed, err := abi.JSON(strings.NewReader(ReleaseOracleABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ReleaseOracleBin), backend, signers) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ReleaseOracle{ReleaseOracleCaller: ReleaseOracleCaller{contract: contract}, ReleaseOracleTransactor: ReleaseOracleTransactor{contract: contract}}, nil +} + +// ReleaseOracle is an auto generated Go binding around an Ethereum contract. +type ReleaseOracle struct { + ReleaseOracleCaller // Read-only binding to the contract + ReleaseOracleTransactor // Write-only binding to the contract +} + +// ReleaseOracleCaller is an auto generated read-only Go binding around an Ethereum contract. +type ReleaseOracleCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ReleaseOracleTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ReleaseOracleTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ReleaseOracleSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ReleaseOracleSession struct { + Contract *ReleaseOracle // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ReleaseOracleCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ReleaseOracleCallerSession struct { + Contract *ReleaseOracleCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ReleaseOracleTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ReleaseOracleTransactorSession struct { + Contract *ReleaseOracleTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ReleaseOracleRaw is an auto generated low-level Go binding around an Ethereum contract. +type ReleaseOracleRaw struct { + Contract *ReleaseOracle // Generic contract binding to access the raw methods on +} + +// ReleaseOracleCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ReleaseOracleCallerRaw struct { + Contract *ReleaseOracleCaller // Generic read-only contract binding to access the raw methods on +} + +// ReleaseOracleTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ReleaseOracleTransactorRaw struct { + Contract *ReleaseOracleTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewReleaseOracle creates a new instance of ReleaseOracle, bound to a specific deployed contract. +func NewReleaseOracle(address common.Address, backend bind.ContractBackend) (*ReleaseOracle, error) { + contract, err := bindReleaseOracle(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor)) + if err != nil { + return nil, err + } + return &ReleaseOracle{ReleaseOracleCaller: ReleaseOracleCaller{contract: contract}, ReleaseOracleTransactor: ReleaseOracleTransactor{contract: contract}}, nil +} + +// NewReleaseOracleCaller creates a new read-only instance of ReleaseOracle, bound to a specific deployed contract. +func NewReleaseOracleCaller(address common.Address, caller bind.ContractCaller) (*ReleaseOracleCaller, error) { + contract, err := bindReleaseOracle(address, caller, nil) + if err != nil { + return nil, err + } + return &ReleaseOracleCaller{contract: contract}, nil +} + +// NewReleaseOracleTransactor creates a new write-only instance of ReleaseOracle, bound to a specific deployed contract. +func NewReleaseOracleTransactor(address common.Address, transactor bind.ContractTransactor) (*ReleaseOracleTransactor, error) { + contract, err := bindReleaseOracle(address, nil, transactor) + if err != nil { + return nil, err + } + return &ReleaseOracleTransactor{contract: contract}, nil +} + +// bindReleaseOracle binds a generic wrapper to an already deployed contract. +func bindReleaseOracle(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ReleaseOracleABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ReleaseOracle *ReleaseOracleRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ReleaseOracle.Contract.ReleaseOracleCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ReleaseOracle *ReleaseOracleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ReleaseOracle.Contract.ReleaseOracleTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ReleaseOracle *ReleaseOracleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ReleaseOracle.Contract.ReleaseOracleTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ReleaseOracle *ReleaseOracleCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ReleaseOracle.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ReleaseOracle *ReleaseOracleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ReleaseOracle.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ReleaseOracle *ReleaseOracleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ReleaseOracle.Contract.contract.Transact(opts, method, params...) +} + +// AuthProposals is a free data retrieval call binding the contract method 0xbf8ecf9c. +// +// Solidity: function authProposals() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleCaller) AuthProposals(opts *bind.CallOpts) ([]common.Address, error) { + var ( + ret0 = new([]common.Address) + ) + out := ret0 + err := _ReleaseOracle.contract.Call(opts, out, "authProposals") + return *ret0, err +} + +// AuthProposals is a free data retrieval call binding the contract method 0xbf8ecf9c. +// +// Solidity: function authProposals() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleSession) AuthProposals() ([]common.Address, error) { + return _ReleaseOracle.Contract.AuthProposals(&_ReleaseOracle.CallOpts) +} + +// AuthProposals is a free data retrieval call binding the contract method 0xbf8ecf9c. +// +// Solidity: function authProposals() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleCallerSession) AuthProposals() ([]common.Address, error) { + return _ReleaseOracle.Contract.AuthProposals(&_ReleaseOracle.CallOpts) +} + +// AuthVotes is a free data retrieval call binding the contract method 0x64ed31fe. +// +// Solidity: function authVotes(user address) constant returns(promote address[], demote address[]) +func (_ReleaseOracle *ReleaseOracleCaller) AuthVotes(opts *bind.CallOpts, user common.Address) (struct { + Promote []common.Address + Demote []common.Address +}, error) { + ret := new(struct { + Promote []common.Address + Demote []common.Address + }) + out := ret + err := _ReleaseOracle.contract.Call(opts, out, "authVotes", user) + return *ret, err +} + +// AuthVotes is a free data retrieval call binding the contract method 0x64ed31fe. +// +// Solidity: function authVotes(user address) constant returns(promote address[], demote address[]) +func (_ReleaseOracle *ReleaseOracleSession) AuthVotes(user common.Address) (struct { + Promote []common.Address + Demote []common.Address +}, error) { + return _ReleaseOracle.Contract.AuthVotes(&_ReleaseOracle.CallOpts, user) +} + +// AuthVotes is a free data retrieval call binding the contract method 0x64ed31fe. +// +// Solidity: function authVotes(user address) constant returns(promote address[], demote address[]) +func (_ReleaseOracle *ReleaseOracleCallerSession) AuthVotes(user common.Address) (struct { + Promote []common.Address + Demote []common.Address +}, error) { + return _ReleaseOracle.Contract.AuthVotes(&_ReleaseOracle.CallOpts, user) +} + +// CurrentVersion is a free data retrieval call binding the contract method 0x9d888e86. +// +// Solidity: function currentVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, time uint256) +func (_ReleaseOracle *ReleaseOracleCaller) CurrentVersion(opts *bind.CallOpts) (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Time *big.Int +}, error) { + ret := new(struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Time *big.Int + }) + out := ret + err := _ReleaseOracle.contract.Call(opts, out, "currentVersion") + return *ret, err +} + +// CurrentVersion is a free data retrieval call binding the contract method 0x9d888e86. +// +// Solidity: function currentVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, time uint256) +func (_ReleaseOracle *ReleaseOracleSession) CurrentVersion() (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Time *big.Int +}, error) { + return _ReleaseOracle.Contract.CurrentVersion(&_ReleaseOracle.CallOpts) +} + +// CurrentVersion is a free data retrieval call binding the contract method 0x9d888e86. +// +// Solidity: function currentVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, time uint256) +func (_ReleaseOracle *ReleaseOracleCallerSession) CurrentVersion() (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Time *big.Int +}, error) { + return _ReleaseOracle.Contract.CurrentVersion(&_ReleaseOracle.CallOpts) +} + +// ProposedVersion is a free data retrieval call binding the contract method 0x26db7648. +// +// Solidity: function proposedVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, pass address[], fail address[]) +func (_ReleaseOracle *ReleaseOracleCaller) ProposedVersion(opts *bind.CallOpts) (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Pass []common.Address + Fail []common.Address +}, error) { + ret := new(struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Pass []common.Address + Fail []common.Address + }) + out := ret + err := _ReleaseOracle.contract.Call(opts, out, "proposedVersion") + return *ret, err +} + +// ProposedVersion is a free data retrieval call binding the contract method 0x26db7648. +// +// Solidity: function proposedVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, pass address[], fail address[]) +func (_ReleaseOracle *ReleaseOracleSession) ProposedVersion() (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Pass []common.Address + Fail []common.Address +}, error) { + return _ReleaseOracle.Contract.ProposedVersion(&_ReleaseOracle.CallOpts) +} + +// ProposedVersion is a free data retrieval call binding the contract method 0x26db7648. +// +// Solidity: function proposedVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, pass address[], fail address[]) +func (_ReleaseOracle *ReleaseOracleCallerSession) ProposedVersion() (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Pass []common.Address + Fail []common.Address +}, error) { + return _ReleaseOracle.Contract.ProposedVersion(&_ReleaseOracle.CallOpts) +} + +// Signers is a free data retrieval call binding the contract method 0x46f0975a. +// +// Solidity: function signers() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleCaller) Signers(opts *bind.CallOpts) ([]common.Address, error) { + var ( + ret0 = new([]common.Address) + ) + out := ret0 + err := _ReleaseOracle.contract.Call(opts, out, "signers") + return *ret0, err +} + +// Signers is a free data retrieval call binding the contract method 0x46f0975a. +// +// Solidity: function signers() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleSession) Signers() ([]common.Address, error) { + return _ReleaseOracle.Contract.Signers(&_ReleaseOracle.CallOpts) +} + +// Signers is a free data retrieval call binding the contract method 0x46f0975a. +// +// Solidity: function signers() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleCallerSession) Signers() ([]common.Address, error) { + return _ReleaseOracle.Contract.Signers(&_ReleaseOracle.CallOpts) +} + +// Demote is a paid mutator transaction binding the contract method 0x5c3d005d. +// +// Solidity: function demote(user address) returns() +func (_ReleaseOracle *ReleaseOracleTransactor) Demote(opts *bind.TransactOpts, user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "demote", user) +} + +// Demote is a paid mutator transaction binding the contract method 0x5c3d005d. +// +// Solidity: function demote(user address) returns() +func (_ReleaseOracle *ReleaseOracleSession) Demote(user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Demote(&_ReleaseOracle.TransactOpts, user) +} + +// Demote is a paid mutator transaction binding the contract method 0x5c3d005d. +// +// Solidity: function demote(user address) returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) Demote(user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Demote(&_ReleaseOracle.TransactOpts, user) +} + +// Nuke is a paid mutator transaction binding the contract method 0xbc8fbbf8. +// +// Solidity: function nuke() returns() +func (_ReleaseOracle *ReleaseOracleTransactor) Nuke(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "nuke") +} + +// Nuke is a paid mutator transaction binding the contract method 0xbc8fbbf8. +// +// Solidity: function nuke() returns() +func (_ReleaseOracle *ReleaseOracleSession) Nuke() (*types.Transaction, error) { + return _ReleaseOracle.Contract.Nuke(&_ReleaseOracle.TransactOpts) +} + +// Nuke is a paid mutator transaction binding the contract method 0xbc8fbbf8. +// +// Solidity: function nuke() returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) Nuke() (*types.Transaction, error) { + return _ReleaseOracle.Contract.Nuke(&_ReleaseOracle.TransactOpts) +} + +// Promote is a paid mutator transaction binding the contract method 0xd0e0813a. +// +// Solidity: function promote(user address) returns() +func (_ReleaseOracle *ReleaseOracleTransactor) Promote(opts *bind.TransactOpts, user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "promote", user) +} + +// Promote is a paid mutator transaction binding the contract method 0xd0e0813a. +// +// Solidity: function promote(user address) returns() +func (_ReleaseOracle *ReleaseOracleSession) Promote(user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Promote(&_ReleaseOracle.TransactOpts, user) +} + +// Promote is a paid mutator transaction binding the contract method 0xd0e0813a. +// +// Solidity: function promote(user address) returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) Promote(user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Promote(&_ReleaseOracle.TransactOpts, user) +} + +// Release is a paid mutator transaction binding the contract method 0xd67cbec9. +// +// Solidity: function release(major uint32, minor uint32, patch uint32, commit bytes20) returns() +func (_ReleaseOracle *ReleaseOracleTransactor) Release(opts *bind.TransactOpts, major uint32, minor uint32, patch uint32, commit [20]byte) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "release", major, minor, patch, commit) +} + +// Release is a paid mutator transaction binding the contract method 0xd67cbec9. +// +// Solidity: function release(major uint32, minor uint32, patch uint32, commit bytes20) returns() +func (_ReleaseOracle *ReleaseOracleSession) Release(major uint32, minor uint32, patch uint32, commit [20]byte) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Release(&_ReleaseOracle.TransactOpts, major, minor, patch, commit) +} + +// Release is a paid mutator transaction binding the contract method 0xd67cbec9. +// +// Solidity: function release(major uint32, minor uint32, patch uint32, commit bytes20) returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) Release(major uint32, minor uint32, patch uint32, commit [20]byte) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Release(&_ReleaseOracle.TransactOpts, major, minor, patch, commit) +} diff --git a/contracts/release/contract.sol b/release/contract.sol similarity index 73% rename from contracts/release/contract.sol rename to release/contract.sol index 7ec0fa8e7c..fedf646c07 100644 --- a/contracts/release/contract.sol +++ b/release/contract.sol @@ -19,7 +19,7 @@ // check for new releases automatically without the need to consult a central // repository. // -// The contract takes a vote based approach on both assigning authorized signers +// The contract takes a vote based approach on both assigning authorised signers // as well as signing off on new Geth releases. // // Note, when a signer is demoted, the currently pending release is auto-nuked. @@ -45,8 +45,8 @@ contract ReleaseOracle { } // Oracle authorization details - mapping(address => bool) authorized; // Set of accounts allowed to vote on updating the contract - address[] signers; // List of addresses currently accepted as signers + mapping(address => bool) authorised; // Set of accounts allowed to vote on updating the contract + address[] voters; // List of addresses currently accepted as signers // Various proposals being voted on mapping(address => Votes) authProps; // Currently running user authorization proposals @@ -57,38 +57,47 @@ contract ReleaseOracle { // isSigner is a modifier to authorize contract transactions. modifier isSigner() { - if (authorized[msg.sender]) { + if (authorised[msg.sender]) { _ } } - // Constructor to assign the creator as the sole valid signer. - function ReleaseOracle() { - authorized[msg.sender] = true; - signers.push(msg.sender); + // Constructor to assign the initial set of signers. + function ReleaseOracle(address[] signers) { + // If no signers were specified, assign the creator as the sole signer + if (signers.length == 0) { + authorised[msg.sender] = true; + voters.push(msg.sender); + return; + } + // Otherwise assign the individual signers one by one + for (uint i = 0; i < signers.length; i++) { + authorised[signers[i]] = true; + voters.push(signers[i]); + } } - // Signers is an accessor method to retrieve all te signers (public accessor + // signers is an accessor method to retrieve all te signers (public accessor // generates an indexed one, not a retreive-all version). - function Signers() constant returns(address[]) { - return signers; + function signers() constant returns(address[]) { + return voters; } - // AuthProposals retrieves the list of addresses that authorization proposals + // authProposals retrieves the list of addresses that authorization proposals // are currently being voted on. - function AuthProposals() constant returns(address[]) { + function authProposals() constant returns(address[]) { return authPend; } - // AuthVotes retrieves the current authorization votes for a particular user + // authVotes retrieves the current authorization votes for a particular user // to promote him into the list of signers, or demote him from there. - function AuthVotes(address user) constant returns(address[] promote, address[] demote) { + function authVotes(address user) constant returns(address[] promote, address[] demote) { return (authProps[user].pass, authProps[user].fail); } - // CurrentVersion retrieves the semantic version, commit hash and release time + // currentVersion retrieves the semantic version, commit hash and release time // of the currently votec active release. - function CurrentVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, uint time) { + function currentVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, uint time) { if (releases.length == 0) { return (0, 0, 0, 0, 0); } @@ -97,38 +106,38 @@ contract ReleaseOracle { return (release.major, release.minor, release.patch, release.commit, release.time); } - // ProposedVersion retrieves the semantic version, commit hash and the current + // proposedVersion retrieves the semantic version, commit hash and the current // votes for the next proposed release. - function ProposedVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, address[] pass, address[] fail) { + function proposedVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, address[] pass, address[] fail) { return (verProp.major, verProp.minor, verProp.patch, verProp.commit, verProp.votes.pass, verProp.votes.fail); } - // Promote pitches in on a voting campaign to promote a new user to a signer + // promote pitches in on a voting campaign to promote a new user to a signer // position. - function Promote(address user) { + function promote(address user) { updateSigner(user, true); } - // Demote pitches in on a voting campaign to demote an authorized user from + // demote pitches in on a voting campaign to demote an authorised user from // its signer position. - function Demote(address user) { + function demote(address user) { updateSigner(user, false); } - // Release votes for a particular version to be included as the next release. - function Release(uint32 major, uint32 minor, uint32 patch, bytes20 commit) { + // release votes for a particular version to be included as the next release. + function release(uint32 major, uint32 minor, uint32 patch, bytes20 commit) { updateRelease(major, minor, patch, commit, true); } - // Nuke votes for the currently proposed version to not be included as the next + // nuke votes for the currently proposed version to not be included as the next // release. Nuking doesn't require a specific version number for simplicity. - function Nuke() { + function nuke() { updateRelease(0, 0, 0, 0, false); } // updateSigner marks a vote for changing the status of an Ethereum user, either - // for or against the user being an authorized signer. - function updateSigner(address user, bool authorize) isSigner { + // for or against the user being an authorised signer. + function updateSigner(address user, bool authorize) internal isSigner { // Gather the current votes and ensure we don't double vote Votes votes = authProps[user]; for (uint i = 0; i < votes.pass.length; i++) { @@ -148,26 +157,26 @@ contract ReleaseOracle { // Cast the vote and return if the proposal cannot be resolved yet if (authorize) { votes.pass.push(msg.sender); - if (votes.pass.length <= signers.length / 2) { + if (votes.pass.length <= voters.length / 2) { return; } } else { votes.fail.push(msg.sender); - if (votes.fail.length <= signers.length / 2) { + if (votes.fail.length <= voters.length / 2) { return; } } // Proposal resolved in our favor, execute whatever we voted on - if (authorize && !authorized[user]) { - authorized[user] = true; - signers.push(user); - } else if (!authorize && authorized[user]) { - authorized[user] = false; + if (authorize && !authorised[user]) { + authorised[user] = true; + voters.push(user); + } else if (!authorize && authorised[user]) { + authorised[user] = false; - for (i = 0; i < signers.length; i++) { - if (signers[i] == user) { - signers[i] = signers[signers.length - 1]; - signers.length--; + for (i = 0; i < voters.length; i++) { + if (voters[i] == user) { + voters[i] = voters[voters.length - 1]; + voters.length--; delete verProp; // Nuke any version proposal (no suprise releases!) break; @@ -188,7 +197,7 @@ contract ReleaseOracle { // updateRelease votes for a particular version to be included as the next release, // or for the currently proposed release to be nuked out. - function updateRelease(uint32 major, uint32 minor, uint32 patch, bytes20 commit, bool release) isSigner { + function updateRelease(uint32 major, uint32 minor, uint32 patch, bytes20 commit, bool release) internal isSigner { // Skip nuke votes if no proposal is pending if (!release && verProp.votes.pass.length == 0) { return; @@ -219,12 +228,12 @@ contract ReleaseOracle { // Cast the vote and return if the proposal cannot be resolved yet if (release) { votes.pass.push(msg.sender); - if (votes.pass.length <= signers.length / 2) { + if (votes.pass.length <= voters.length / 2) { return; } } else { votes.fail.push(msg.sender); - if (votes.fail.length <= signers.length / 2) { + if (votes.fail.length <= voters.length / 2) { return; } } diff --git a/contracts/release/contract_test.go b/release/contract_test.go similarity index 97% rename from contracts/release/contract_test.go rename to release/contract_test.go index 7aa3a577db..11a039992b 100644 --- a/contracts/release/contract_test.go +++ b/release/contract_test.go @@ -42,7 +42,7 @@ func setupReleaseTest(t *testing.T, prefund ...*ecdsa.PrivateKey) (*ecdsa.Privat sim := backends.NewSimulatedBackend(accounts...) // Deploy a version oracle contract, commit and return - _, _, oracle, err := DeployReleaseOracle(auth, sim) + _, _, oracle, err := DeployReleaseOracle(auth, sim, []common.Address{auth.From}) if err != nil { t.Fatalf("Failed to deploy version contract: %v", err) } @@ -239,7 +239,7 @@ func TestVersionRelease(t *testing.T) { // Propose release with half voters and check that the release does not yet go through for j := 0; j < (i+1)/2; j++ { - if _, err = oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{}); err != nil { + if _, err = oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil { t.Fatalf("Iter #%d: failed valid release attempt: %v", i, err) } } @@ -254,8 +254,8 @@ func TestVersionRelease(t *testing.T) { } // Pass the release and check that it became the next version - verMajor, verMinor, verPatch, verCommit = uint32(i), uint32(i+1), uint32(i+2), [20]byte{} - if _, err = oracle.Release(bind.NewKeyedTransactor(keys[(i+1)/2]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{}); err != nil { + verMajor, verMinor, verPatch, verCommit = uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)} + if _, err = oracle.Release(bind.NewKeyedTransactor(keys[(i+1)/2]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil { t.Fatalf("Iter #%d: failed valid release completion attempt: %v", i, err) } sim.Commit() @@ -293,7 +293,7 @@ func TestVersionNuking(t *testing.T) { for i := 1; i < (len(keys)+1)/2; i++ { // Propose release with an initial set of signers for j := 0; j < i; j++ { - if _, err := oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{}); err != nil { + if _, err := oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil { t.Fatalf("Iter #%d: failed valid proposal attempt: %v", i, err) } } @@ -344,7 +344,7 @@ func TestVersionAutoNuke(t *testing.T) { sim.Commit() } // Make a release proposal and check it's existence - if _, err := oracle.Release(bind.NewKeyedTransactor(keys[0]), 1, 2, 3, [20]byte{}); err != nil { + if _, err := oracle.Release(bind.NewKeyedTransactor(keys[0]), 1, 2, 3, [20]byte{4}); err != nil { t.Fatalf("Failed valid proposal attempt: %v", err) } sim.Commit() diff --git a/contracts/release/generator.go b/release/generator.go similarity index 100% rename from contracts/release/generator.go rename to release/generator.go diff --git a/release/release.go b/release/release.go new file mode 100644 index 0000000000..05b4885b50 --- /dev/null +++ b/release/release.go @@ -0,0 +1,147 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package release contains the node service that tracks client releases. +package release + +import ( + "fmt" + "strings" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" +) + +// Interval to check for new releases +const releaseRecheckInterval = time.Hour + +// Config contains the configurations of the release service. +type Config struct { + Oracle common.Address // Ethereum address of the release oracle + Major uint32 // Major version component of the release + Minor uint32 // Minor version component of the release + Patch uint32 // Patch version component of the release + Commit [20]byte // Git SHA1 commit hash of the release +} + +// ReleaseService is a node service that periodically checks the blockchain for +// newly released versions of the client being run and issues a warning to the +// user about it. +type ReleaseService struct { + config Config // Current version to check releases against + oracle *ReleaseOracle // Native binding to the release oracle contract + quit chan chan error // Quit channel to terminate the version checker +} + +// NewReleaseService creates a new service to periodically check for new client +// releases and notify the user of such. +func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) { + // Retrieve the Ethereum service dependency to access the blockchain + var ethereum *eth.Ethereum + if err := ctx.Service(ðereum); err != nil { + return nil, err + } + // Construct the release service + contract, err := NewReleaseOracle(config.Oracle, eth.NewContractBackend(ethereum)) + if err != nil { + return nil, err + } + return &ReleaseService{ + config: config, + oracle: contract, + quit: make(chan chan error), + }, nil +} + +// Protocols returns an empty list of P2P protocols as the release service does +// not have a networking component. +func (r *ReleaseService) Protocols() []p2p.Protocol { return nil } + +// APIs returns an empty list of RPC descriptors as the release service does not +// expose any functioanlity to the outside world. +func (r *ReleaseService) APIs() []rpc.API { return nil } + +// Start spawns the periodic version checker goroutine +func (r *ReleaseService) Start(server *p2p.Server) error { + go r.checker() + return nil +} + +// Stop terminates all goroutines belonging to the service, blocking until they +// are all terminated. +func (r *ReleaseService) Stop() error { + errc := make(chan error) + r.quit <- errc + return <-errc +} + +// checker runs indefinitely in the background, periodically checking for new +// client releases. +func (r *ReleaseService) checker() { + // Set up the timers to periodically check for releases + timer := time.NewTimer(0) // Immediately fire a version check + defer timer.Stop() + + for { + select { + // If the time arrived, check for a new release + case <-timer.C: + // Rechedule the timer before continuing + timer.Reset(releaseRecheckInterval) + + // Retrieve the current version, and handle missing contracts gracefully + version, err := r.oracle.CurrentVersion(nil) + if err != nil { + if err == bind.ErrNoCode { + glog.V(logger.Debug).Infof("Release oracle not found at %x", r.config.Oracle) + continue + } + glog.V(logger.Error).Infof("Failed to retrieve current release: %v", err) + continue + } + // Version was successfully retrieved, notify if newer than ours + if version.Major > r.config.Major || + (version.Major == r.config.Major && version.Minor > r.config.Minor) || + (version.Major == r.config.Major && version.Minor == r.config.Minor && version.Patch > r.config.Patch) { + + warning := fmt.Sprintf("Client v%d.%d.%d-%x seems older than the latest upstream release v%d.%d.%d-%x", + r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4], version.Major, version.Minor, version.Patch, version.Commit[:4]) + howtofix := fmt.Sprintf("Please check https://github.com/ethereum/go-ethereum/releases for new releases") + separator := strings.Repeat("-", len(warning)) + + glog.V(logger.Warn).Info(separator) + glog.V(logger.Warn).Info(warning) + glog.V(logger.Warn).Info(howtofix) + glog.V(logger.Warn).Info(separator) + } else { + glog.V(logger.Debug).Infof("Client v%d.%d.%d-%x seems up to date with upstream v%d.%d.%d-%x", + r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4], version.Major, version.Minor, version.Patch, version.Commit[:4]) + } + + // If termination was requested, return + case errc := <-r.quit: + errc <- nil + return + } + } +} From 4536b993ff6a5b3751f59b52744078e150296654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 2 May 2016 15:29:55 +0300 Subject: [PATCH 19/22] cmd/geth, release: polish and deploy live release contract --- cmd/geth/main.go | 2 +- release/contract.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index a023497a76..42ce761d8b 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -53,7 +53,7 @@ const ( versionPatch = 0 // Patch version component of the current release versionMeta = "unstable" // Version metadata to append to the version string - versionOracle = "0x48a117313039b73ab02fb9f73e04a66defe123ec" // Ethereum address of the Geth release oracle + versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle ) var ( diff --git a/release/contract.go b/release/contract.go index 5daf7e4a40..51c1443d48 100644 --- a/release/contract.go +++ b/release/contract.go @@ -14,7 +14,7 @@ import ( ) // ReleaseOracleABI is the input ABI used to generate the binding from. -const ReleaseOracleABI = `[{"constant":true,"inputs":[],"name":"proposedVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"pass","type":"address[]"},{"name":"fail","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"signers","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"demote","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"authVotes","outputs":[{"name":"promote","type":"address[]"},{"name":"demote","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"currentVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"time","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"nuke","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"authProposals","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"promote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"}],"name":"release","outputs":[],"type":"function"},{"inputs":[{"name":"signers","type":"address[]"}],"type":"constructor"}]` +const ReleaseOracleABI = `[{"constant":true,"inputs":[],"name":"currentVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"time","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"proposedVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"pass","type":"address[]"},{"name":"fail","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"signers","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"authProposals","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"authVotes","outputs":[{"name":"promote","type":"address[]"},{"name":"demote","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"promote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"demote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"}],"name":"release","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"nuke","outputs":[],"type":"function"},{"inputs":[{"name":"signers","type":"address[]"}],"type":"constructor"}]` // ReleaseOracleBin is the compiled bytecode used for deploying new contracts. const ReleaseOracleBin = `0x60606040526040516114033803806114038339810160405280510160008151600014156100ce57600160a060020a0333168152602081905260408120805460ff191660019081179091558054808201808355828183801582901161015e5781836000526020600020918201910161015e91905b8082111561019957600081558401610072565b505050919090600052602060002090016000848481518110156100025790602001906020020151909190916101000a815481600160a060020a0302191690830217905550506001015b815181101561018957600160006000506000848481518110156100025790602001906020020151600160a060020a0316815260200190815260200160002060006101000a81548160ff0219169083021790555060016000508054806001018281815481835581811511610085576000839052610085906000805160206113e3833981519152908101908301610072565b5050506000929092526000805160206113e3833981519152018054600160a060020a03191633179055505b50506112458061019e6000396000f35b50905600606060405236156100775760e060020a600035046326db7648811461007957806346f0975a1461019e5780635c3d005d1461020a57806364ed31fe146102935780639d888e861461038d578063bc8fbbf8146103b2578063bf8ecf9c146103fb578063d0e0813a14610467578063d67cbec914610478575b005b610495604080516020818101835260008083528351808301855281815260045460068054875181870281018701909852808852939687968796879691959463ffffffff818116956401000000008304821695604060020a840490921694606060020a938490049093029390926007929184919083018282801561012657602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610107575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561018357602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610164575b50505050509050955095509550955095509550909192939495565b6040805160208181018352600082526001805484518184028101840190955280855261054894928301828280156101ff57602002820191906000526020600020905b8154600160a060020a03168152600191909101906020018083116101e0575b505050505090505b90565b6100776004356106d78160005b600160a060020a033316600090815260208190526040812054819060ff16156106df57600160a060020a038416815260026020526040812091505b81548110156106e7578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610732576106df565b6105926004356040805160208181018352600080835283518083018552818152600160a060020a038616825260028352908490208054855181850281018501909652808652939491939092600184019291849183018282801561032057602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610301575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561037d57602002820191906000526020600020905b8154600160a060020a031681526001919091019060200180831161035e575b5050505050905091509150915091565b6106176000600060006000600060006008600050805490506000141561064e576106cf565b6100776106e56000808080805b600160a060020a033316600090815260208190526040812054819060ff161561121c57821580156103f1575060065481145b15610ca55761121c565b6040805160208181018352600082526003805484518184028101840190955280855261054894928301828280156101ff57602002820191906000526020600020908154600160a060020a03168152600191909101906020018083116101e0575b50505050509050610207565b6100776004356106d7816001610217565b6100776004356024356044356064356106df8484848460016103bf565b604051808763ffffffff1681526020018663ffffffff1681526020018563ffffffff16815260200184815260200180602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019850505050505050505060405180910390f35b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b6040518080602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f15090500194505050505060405180910390f35b6040805163ffffffff9687168152948616602086015292909416838301526060830152608082019290925290519081900360a00190f35b600880546000198101908110156100025760009182526004027ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30190508054600182015463ffffffff8281169950640100000000830481169850604060020a8304169650606060020a91829004909102945067ffffffffffffffff16925090505b509091929394565b50565b505050505b50505050565b565b5060005b600182015481101561073a5733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610784576106df565b600101610252565b8154600014801561074f575060018201546000145b156107ac576003805460018101808355828183801582901161078c5781836000526020600020918201910161078c9190610834565b6001016106eb565b5050506000928352506020909120018054600160a060020a031916851790555b821561084c578154600181018084558391908281838015829011610881578183600052602060002091820191016108819190610834565b5050506000928352506020909120018054600160a060020a031916851790555b600160a060020a038416600090815260026020908152604082208054838255818452918320909291610b5c91908101905b808211156108485760008155600101610834565b5090565b816001016000508054806001018281815481835581811511610933578183600052602060002091820191016109339190610834565b5050506000928352506020909120018054600160a060020a031916331790556001548254600290910490116108b5576106df565b8280156108db5750600160a060020a03841660009081526020819052604090205460ff16155b1561096a57600160a060020a0384166000908152602081905260409020805460ff19166001908117909155805480820180835582818380158290116107e3578183600052602060002091820191016107e39190610834565b5050506000928352506020909120018054600160a060020a031916331790556001805490830154600290910490116108b5576106df565b821580156109905750600160a060020a03841660009081526020819052604090205460ff165b156108035750600160a060020a0383166000908152602081905260408120805460ff191690555b6001548110156108035783600160a060020a03166001600050828154811015610002576000919091527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60154600160a060020a03161415610ad057600180546000198101908110156100025760009182527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf601909054906101000a9004600160a060020a03166001600050828154811015610002577fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6018054600160a060020a03191690921790915580546000198101808355909190828015829011610ad857818360005260206000209182019101610ad89190610834565b6001016109b7565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529194509192508290610b32907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610834565b5060018201805460008083559182526020909120610b5291810190610834565b5050505050610803565b5060018201805460008083559182526020909120610b7c91810190610834565b506000925050505b6003548110156106df5783600160a060020a03166003600050828154811015610002576000919091527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0154600160a060020a03161415610c9d57600380546000198101908110156100025760009182527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01909054906101000a9004600160a060020a03166003600050828154811015610002577fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b018054600160a060020a031916909217909155805460001981018083559091908280158290116106da578183600052602060002091820191016106da9190610834565b600101610b84565b60065460001415610d03576004805463ffffffff1916881767ffffffff0000000019166401000000008802176bffffffff00000000000000001916604060020a8702176bffffffffffffffffffffffff16606060020a808704021790555b828015610d6d575060045463ffffffff8881169116141580610d395750600454640100000000900463ffffffff90811690871614155b80610d56575060045463ffffffff868116604060020a9092041614155b80610d6d5750600454606060020a90819004028414155b15610d775761121c565b506006905060005b8154811015610dc0578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610e0b5761121c565b5060005b6001820154811015610e135733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610e485761121c565b600101610d7f565b8215610e50578154600181018084558391908281838015829011610e8557600083815260209020610e85918101908301610834565b600101610dc4565b816001016000508054806001018281815481835581811511610f0857818360005260206000209182019101610f089190610834565b5050506000928352506020909120018054600160a060020a03191633179055600154825460029091049011610eb95761121c565b8215610f3f576005805467ffffffffffffffff19164217905560088054600181018083558281838015829011610f9457600402816004028360005260206000209182019101610f9491906110ae565b5050506000928352506020909120018054600160a060020a03191633179055600180549083015460029091049011610eb95761121c565b600060048181556005805467ffffffffffffffff19169055600680548382558184529192918290611225907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610834565b5050509190906000526020600020906004020160005060048054825463ffffffff191663ffffffff9182161780845582546401000000009081900483160267ffffffff000000001991909116178084558254604060020a908190049092169091026bffffffff00000000000000001991909116178083558154606060020a908190048102819004026bffffffffffffffffffffffff9190911617825560055460018301805467ffffffffffffffff191667ffffffffffffffff9290921691909117905560068054600284018054828255600082815260209020949594919283929182019185821561110d5760005260206000209182015b8281111561110d57825482559160010191906001019061108b565b505050506004015b8082111561084857600080825560018201805467ffffffffffffffff191690556002820180548282558183526020832083916110ed9190810190610834565b50600182018054600080835591825260209091206110a691810190610834565b506111339291505b80821115610848578054600160a060020a0319168155600101611115565b50506001818101805491840180548083556000838152602090209293830192909182156111815760005260206000209182015b82811115611181578254825591600101919060010190611166565b5061118d929150611115565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529197509195509093508492506111ec91507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610834565b506001820180546000808355918252602090912061120c91810190610834565b505050505061121c565b50505050505b50505050505050565b50600182018054600080835591825260209091206112169181019061083456b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6` From 32bb280179a44b9ad1058766bf61cdbacea30a59 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 2 May 2016 17:01:13 +0200 Subject: [PATCH 20/22] p2p: improve readability of dial task scheduling code --- p2p/server.go | 57 +++++++++++++++++++++++----------------------- p2p/server_test.go | 50 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 29 deletions(-) diff --git a/p2p/server.go b/p2p/server.go index 52d1be6775..3b2f2b0786 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -398,12 +398,11 @@ type dialer interface { func (srv *Server) run(dialstate dialer) { defer srv.loopWG.Done() var ( - peers = make(map[discover.NodeID]*Peer) - trusted = make(map[discover.NodeID]bool, len(srv.TrustedNodes)) - - tasks []task - pendingTasks []task + peers = make(map[discover.NodeID]*Peer) + trusted = make(map[discover.NodeID]bool, len(srv.TrustedNodes)) taskdone = make(chan task, maxActiveDialTasks) + runningTasks []task + queuedTasks []task // tasks that can't run yet ) // Put trusted nodes into a map to speed up checks. // Trusted peers are loaded on startup and cannot be @@ -412,39 +411,39 @@ func (srv *Server) run(dialstate dialer) { trusted[n.ID] = true } - // Some task list helpers. + // removes t from runningTasks delTask := func(t task) { - for i := range tasks { - if tasks[i] == t { - tasks = append(tasks[:i], tasks[i+1:]...) + for i := range runningTasks { + if runningTasks[i] == t { + runningTasks = append(runningTasks[:i], runningTasks[i+1:]...) break } } } - scheduleTasks := func(new []task) { - pt := append(pendingTasks, new...) - start := maxActiveDialTasks - len(tasks) - if len(pt) < start { - start = len(pt) + // starts until max number of active tasks is satisfied + startTasks := func(ts []task) (rest []task) { + i := 0 + for ; len(runningTasks) < maxActiveDialTasks && i < len(ts); i++ { + t := ts[i] + glog.V(logger.Detail).Infoln("new task:", t) + go func() { t.Do(srv); taskdone <- t }() + runningTasks = append(runningTasks, t) } - if start > 0 { - tasks = append(tasks, pt[:start]...) - for _, t := range pt[:start] { - t := t - glog.V(logger.Detail).Infoln("new task:", t) - go func() { t.Do(srv); taskdone <- t }() - } - copy(pt, pt[start:]) - pendingTasks = pt[:len(pt)-start] + return ts[i:] + } + scheduleTasks := func() { + // Start from queue first. + queuedTasks = append(queuedTasks[:0], startTasks(queuedTasks)...) + // Query dialer for new tasks and start as many as possible now. + if len(runningTasks) < maxActiveDialTasks { + nt := dialstate.newTasks(len(runningTasks)+len(queuedTasks), peers, time.Now()) + queuedTasks = append(queuedTasks, startTasks(nt)...) } } running: for { - // Query the dialer for new tasks and launch them. - now := time.Now() - nt := dialstate.newTasks(len(pendingTasks)+len(tasks), peers, now) - scheduleTasks(nt) + scheduleTasks() select { case <-srv.quit: @@ -466,7 +465,7 @@ running: // can update its state and remove it from the active // tasks list. glog.V(logger.Detail).Infoln("<-taskdone:", t) - dialstate.taskDone(t, now) + dialstate.taskDone(t, time.Now()) delTask(t) case c := <-srv.posthandshake: // A connection has passed the encryption handshake so @@ -513,7 +512,7 @@ running: // Wait for peers to shut down. Pending connections and tasks are // not handled here and will terminate soon-ish because srv.quit // is closed. - glog.V(logger.Detail).Infof("ignoring %d pending tasks at spindown", len(tasks)) + glog.V(logger.Detail).Infof("ignoring %d pending tasks at spindown", len(runningTasks)) for len(peers) > 0 { p := <-srv.delpeer glog.V(logger.Detail).Infoln("<-delpeer (spindown):", p) diff --git a/p2p/server_test.go b/p2p/server_test.go index 02d1c8e010..b437ac3676 100644 --- a/p2p/server_test.go +++ b/p2p/server_test.go @@ -235,6 +235,56 @@ func TestServerTaskScheduling(t *testing.T) { } } +// This test checks that Server doesn't drop tasks, +// even if newTasks returns more than the maximum number of tasks. +func TestServerManyTasks(t *testing.T) { + alltasks := make([]task, 300) + for i := range alltasks { + alltasks[i] = &testTask{index: i} + } + + var ( + srv = &Server{quit: make(chan struct{}), ntab: fakeTable{}, running: true} + done = make(chan *testTask) + start, end = 0, 0 + ) + defer srv.Stop() + srv.loopWG.Add(1) + go srv.run(taskgen{ + newFunc: func(running int, peers map[discover.NodeID]*Peer) []task { + start, end = end, end+maxActiveDialTasks+10 + if end > len(alltasks) { + end = len(alltasks) + } + return alltasks[start:end] + }, + doneFunc: func(tt task) { + done <- tt.(*testTask) + }, + }) + + doneset := make(map[int]bool) + timeout := time.After(2 * time.Second) + for len(doneset) < len(alltasks) { + select { + case tt := <-done: + if doneset[tt.index] { + t.Errorf("task %d got done more than once", tt.index) + } else { + doneset[tt.index] = true + } + case <-timeout: + t.Errorf("%d of %d tasks got done within 2s", len(doneset), len(alltasks)) + for i := 0; i < len(alltasks); i++ { + if !doneset[i] { + t.Logf("task %d not done", i) + } + } + return + } + } +} + type taskgen struct { newFunc func(running int, peers map[discover.NodeID]*Peer) []task doneFunc func(task) From 81106719601c6eb0fa9e9421262569c16e3c2fde Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 2 May 2016 17:57:07 +0200 Subject: [PATCH 21/22] p2p/discover: prevent bonding self --- p2p/discover/table.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/p2p/discover/table.go b/p2p/discover/table.go index 1de045f047..ad0b5c8ca3 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -25,6 +25,7 @@ package discover import ( "crypto/rand" "encoding/binary" + "errors" "fmt" "net" "sort" @@ -457,6 +458,9 @@ func (tab *Table) bondall(nodes []*Node) (result []*Node) { // If pinged is true, the remote node has just pinged us and one half // of the process can be skipped. func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) (*Node, error) { + if id == tab.self.ID { + return nil, errors.New("is self") + } // Retrieve a previously known node and any recent findnode failures node, fails := tab.db.node(id), 0 if node != nil { From b4fbcd5060c950a18bff5e6310f2c600ac964140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Fri, 29 Apr 2016 17:40:19 +0300 Subject: [PATCH 22/22] README: Polish up exec section, rewrite contrib and add license. --- README.md | 69 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 2d60e0564d..aaf44997d6 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ master | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=L [![API Reference]( https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667 -)](https://godoc.org/github.com/ethereum/go-ethereum) +)](https://godoc.org/github.com/ethereum/go-ethereum) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/go-ethereum?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) ## Automated development builds @@ -38,36 +38,47 @@ Once the dependencies are installed, run ## Executables -Go Ethereum comes with several wrappers/executables found in -[the `cmd` directory](https://github.com/ethereum/go-ethereum/tree/develop/cmd): +The go-ethereum project comes with several wrappers/executables found in the `cmd` directory. - Command | | -----------|---------| -`geth` | Ethereum CLI (ethereum command line interface client) | -`bootnode` | runs a bootstrap node for the Discovery Protocol | -`ethtest` | test tool which runs with the [tests](https://github.com/ethereum/tests) suite: `/path/to/test.json > ethtest --test BlockTests --stdin`. -`evm` | is a generic Ethereum Virtual Machine: `evm -code 60ff60ff -gas 10000 -price 0 -dump`. See `-h` for a detailed description. | -`disasm` | disassembles EVM code: `echo "6001" | disasm` | -`rlpdump` | prints RLP structures | - -## Command line options - -`geth` can be configured via command line options, environment variables and config files. - -To get the options available: - - geth help - -For further details on options, see the [wiki](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) +| Command | Description | +|:----------:|-------------| +| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default) archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as an gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. Please see our [Command Line Options](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) wiki page for details. | +| `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) with expanded functionality if the contract bytecode is also available. However it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts) wiki page for details. | +| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. | +| `disasm` | Bytecode disassembler to convert EVM (Ethereum Virtual Machine) bytecode into more user friendly assembly-like opcodes (e.g. `echo "6001" | disasm`). For details on the individual opcodes, please see pages 22-30 of the [Ethereum Yellow Paper](http://gavwood.com/paper.pdf). | +| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow insolated, fine graned debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). | +| `gethrpctest` | Developer utility tool to support our [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) specs. Please see the [test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. | +| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ethereum/wiki/wiki/RLP)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). | ## Contribution -If you'd like to contribute to go-ethereum please fork, fix, commit and -send a pull request. Commits who do not comply with the coding standards -are ignored (use gofmt!). If you send pull requests make absolute sure that you -commit on the `develop` branch and that you do not merge to master. -Commits that are directly based on master are simply ignored. +Thank you for considering to help out with the source code! We welcome contributions from +anyone on the internet, and are grateful for even the smallest of fixes! -See [Developers' Guide](https://github.com/ethereum/go-ethereum/wiki/Developers'-Guide) -for more details on configuring your environment, testing, and -dependency management. +If you'd like to contribute to go-ethereum, please fork, fix, commit and send a pull request +for the maintainers to review and merge into the main code base. If you wish to submit more +complex changes though, please check up with the core devs first on [our gitter channel](https://gitter.im/ethereum/go-ethereum) +to ensure those changes are in line with the general philosopy of the project and/or get some +early feedback which can make both your efforts much lighter as well as our review and merge +procedures quick and simple. + +Please make sure your contributions adhere to our coding guidlines: + + * Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). + * Code must be documented adherign to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines. + * Pull requests need to be based on and opened against the `develop` branch. + * Commit messages should be prefixed with the package(s) they modify. + * E.g. "eth, rpc: make trace configs optional" + +Please see the [Developers' Guide](https://github.com/ethereum/go-ethereum/wiki/Developers'-Guide) +for more details on configuring your environment, managing project dependencies and testing procedures. + +## License + +The go-ethereum library (i.e. all code outside of the `cmd` directory) is licensed under the +[GNU Lesser General Public License v3.0](http://www.gnu.org/licenses/lgpl-3.0.en.html), also +included in our repository in the `COPYING.LESSER` file. + +The go-ethereum binaries (i.e. all code inside of the `cmd` directory) is licensed under the +[GNU General Public License v3.0](http://www.gnu.org/licenses/gpl-3.0.en.html), also included +in our repository in the `COPYING` file.