diff --git a/.travis.yml b/.travis.yml
index bc28ad0932..68fba06794 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,15 +13,15 @@ matrix:
go: 1.6.2
- os: linux
dist: trusty
- go: 1.7
+ go: 1.7.4
- os: osx
- go: 1.7
+ go: 1.7.4
# This builder does the Ubuntu PPA and Linux Azure uploads
- os: linux
dist: trusty
sudo: required
- go: 1.7
+ go: 1.7.4
env:
- ubuntu-ppa
- azure-linux
@@ -55,7 +55,7 @@ matrix:
# This builder does the OSX Azure, Android Maven and Azure and iOS CocoaPods and Azure uploads
- os: osx
- go: 1.7
+ go: 1.7.4
env:
- azure-osx
- mobile
@@ -90,7 +90,7 @@ install:
- go get golang.org/x/tools/cmd/cover
script:
- go run build/ci.go install
- - go run build/ci.go test -coverage -vet
+ - go run build/ci.go test -coverage -vet -misspell
notifications:
webhooks:
diff --git a/README.md b/README.md
index 957615197b..4803f08d13 100644
--- a/README.md
+++ b/README.md
@@ -39,9 +39,7 @@ The go-ubiq project comes with several wrappers/executables found in the `cmd` d
| `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-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). |
| `gubiqrpctest` | Developer utility tool to support our [ubiq/rpc-test](https://github.com/ubiq/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ubiq/wiki/wiki/JSON-RPC) specs. Please see the [test suite's readme](https://github.com/ubiq/rpc-tests/blob/master/README.md) for details. |
| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ubiq/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`). |
-| `bzzd` | swarm daemon. This is the entrypoint for the swarm network. `bzzd --help` for command line options. See https://swarm-guide.readthedocs.io for swarm documentation. |
-| `bzzup` | swarm command line file uploader. `bzzup --help` for command line options |
-| `bzzhash` | command to calculate the swarm hash of a file or directory. `bzzhash --help` for command line options |
+| `swarm` | swarm daemon and tools. This is the entrypoint for the swarm network. `swarm --help` for command line options and subcommands. See https://swarm-guide.readthedocs.io for swarm documentation. |
## Running gubiq
diff --git a/VERSION b/VERSION
index 9075be4951..f01291b87f 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.5.5
+1.5.7
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index 9cc4374916..46ec075dbd 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -91,8 +91,30 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
// first we need to create a slice of the type
var refSlice reflect.Value
switch elem.T {
- case IntTy, UintTy, BoolTy: // int, uint, bool can all be of type big int.
- refSlice = reflect.ValueOf([]*big.Int(nil))
+ case IntTy, UintTy, BoolTy:
+ // create a new reference slice matching the element type
+ switch t.Type.Kind {
+ case reflect.Bool:
+ refSlice = reflect.ValueOf([]bool(nil))
+ case reflect.Uint8:
+ refSlice = reflect.ValueOf([]uint8(nil))
+ case reflect.Uint16:
+ refSlice = reflect.ValueOf([]uint16(nil))
+ case reflect.Uint32:
+ refSlice = reflect.ValueOf([]uint32(nil))
+ case reflect.Uint64:
+ refSlice = reflect.ValueOf([]uint64(nil))
+ case reflect.Int8:
+ refSlice = reflect.ValueOf([]int8(nil))
+ case reflect.Int16:
+ refSlice = reflect.ValueOf([]int16(nil))
+ case reflect.Int32:
+ refSlice = reflect.ValueOf([]int32(nil))
+ case reflect.Int64:
+ refSlice = reflect.ValueOf([]int64(nil))
+ default:
+ 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
@@ -147,7 +169,27 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
// set inter to the correct type (cast)
switch elem.T {
case IntTy, UintTy:
- inter = common.BytesToBig(returnOutput)
+ bigNum := common.BytesToBig(returnOutput)
+ switch t.Type.Kind {
+ case reflect.Uint8:
+ inter = uint8(bigNum.Uint64())
+ case reflect.Uint16:
+ inter = uint16(bigNum.Uint64())
+ case reflect.Uint32:
+ inter = uint32(bigNum.Uint64())
+ case reflect.Uint64:
+ inter = bigNum.Uint64()
+ case reflect.Int8:
+ inter = int8(bigNum.Int64())
+ case reflect.Int16:
+ inter = int16(bigNum.Int64())
+ case reflect.Int32:
+ inter = int32(bigNum.Int64())
+ case reflect.Int64:
+ inter = bigNum.Int64()
+ default:
+ inter = common.BytesToBig(returnOutput)
+ }
case BoolTy:
inter = common.BytesToBig(returnOutput).Uint64() > 0
case AddressTy:
@@ -169,7 +211,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 || t.Type.IsArray) && t.Type.T != BytesTy && t.Type.T != StringTy && t.Type.T != FixedBytesTy {
+ if (t.Type.IsSlice || t.Type.IsArray) && t.Type.T != BytesTy && t.Type.T != StringTy && t.Type.T != FixedBytesTy && t.Type.T != FunctionTy {
return toGoSlice(i, t, output)
}
@@ -233,7 +275,7 @@ func toGoType(i int, t Argument, output []byte) (interface{}, error) {
return common.BytesToAddress(returnOutput), nil
case HashTy:
return common.BytesToHash(returnOutput), nil
- case BytesTy, FixedBytesTy:
+ case BytesTy, FixedBytesTy, FunctionTy:
return returnOutput, nil
case StringTy:
return string(returnOutput), nil
@@ -345,12 +387,13 @@ func (abi ABI) Unpack(v interface{}, name string, output []byte) error {
func (abi *ABI) UnmarshalJSON(data []byte) error {
var fields []struct {
- Type string
- Name string
- Constant bool
- Indexed bool
- Inputs []Argument
- Outputs []Argument
+ Type string
+ Name string
+ Constant bool
+ Indexed bool
+ Anonymous bool
+ Inputs []Argument
+ Outputs []Argument
}
if err := json.Unmarshal(data, &fields); err != nil {
@@ -375,8 +418,9 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
}
case "event":
abi.Events[field.Name] = Event{
- Name: field.Name,
- Inputs: field.Inputs,
+ Name: field.Name,
+ Anonymous: field.Anonymous,
+ Inputs: field.Inputs,
}
}
}
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index 1f9beb0ab6..94a9f5aa05 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -67,10 +67,10 @@ func TestTypeCheck(t *testing.T) {
{"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"},
+ {"address[]", []common.Address{{1}}, ""},
+ {"address[1]", []common.Address{{1}}, ""},
+ {"address[1]", [1]common.Address{{1}}, ""},
+ {"address[2]", [1]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}, ""},
@@ -80,7 +80,8 @@ func TestTypeCheck(t *testing.T) {
{"bytes", [2]byte{0, 1}, ""},
{"bytes", common.Hash{1}, ""},
{"string", "hello world", ""},
- {"bytes32[]", [][32]byte{[32]byte{}}, ""},
+ {"bytes32[]", [][32]byte{{}}, ""},
+ {"function", [24]byte{}, ""},
} {
typ, err := NewType(test.typ)
if err != nil {
@@ -197,6 +198,13 @@ func TestSimpleMethodUnpack(t *testing.T) {
"interface",
"",
},
+ {
+ `[ { "type": "function" } ]`,
+ pad([]byte{1}, 32, false),
+ [24]byte{1},
+ "function",
+ "",
+ },
} {
abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
abi, err := JSON(strings.NewReader(abiDefinition))
@@ -255,6 +263,10 @@ func TestSimpleMethodUnpack(t *testing.T) {
var v common.Hash
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
+ case "function":
+ var v [24]byte
+ err = abi.Unpack(&v, "method", test.marshalledOutput)
+ outvar = v
case "interface":
err = abi.Unpack(&outvar, "method", test.marshalledOutput)
default:
@@ -320,6 +332,30 @@ func TestUnpackSetInterfaceSlice(t *testing.T) {
}
}
+func TestUnpackSetInterfaceArrayOutput(t *testing.T) {
+ var (
+ var1 = new([1]uint32)
+ var2 = new([1]uint32)
+ )
+ out := []interface{}{var1, var2}
+ abi, err := JSON(strings.NewReader(`[{"type":"function", "name":"ints", "outputs":[{"type":"uint32[1]"}, {"type":"uint32[1]"}]}]`))
+ if err != nil {
+ t.Fatal(err)
+ }
+ marshalledReturn := append(pad([]byte{1}, 32, true), pad([]byte{2}, 32, true)...)
+ err = abi.Unpack(&out, "ints", marshalledReturn)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if *var1 != [1]uint32{1} {
+ t.Error("expected var1 to be [1], got", *var1)
+ }
+ if *var2 != [1]uint32{2} {
+ t.Error("expected var2 to be [2], got", *var2)
+ }
+}
+
func TestPack(t *testing.T) {
for i, test := range []struct {
typ string
@@ -331,8 +367,9 @@ func TestPack(t *testing.T) {
{"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))},
+ {"address[]", []common.Address{{1}, {2}}, formatSliceOutput(pad([]byte{1}, 20, false), pad([]byte{2}, 20, false))},
+ {"bytes32[]", []common.Hash{{1}, {2}}, formatSliceOutput(pad([]byte{1}, 32, false), pad([]byte{2}, 32, false))},
+ {"function", [24]byte{1}, pad([]byte{1}, 32, false)},
} {
typ, err := NewType(test.typ)
if err != nil {
@@ -445,12 +482,12 @@ func TestReader(t *testing.T) {
Uint256, _ := NewType("uint256")
exp := ABI{
Methods: map[string]Method{
- "balance": Method{
+ "balance": {
"balance", true, nil, nil,
},
- "send": Method{
+ "send": {
"send", false, []Argument{
- Argument{"amount", Uint256, false},
+ {"amount", Uint256, false},
}, nil,
},
},
@@ -549,7 +586,7 @@ func TestTestSlice(t *testing.T) {
func TestMethodSignature(t *testing.T) {
String, _ := NewType("string")
- m := Method{"foo", false, []Argument{Argument{"bar", String, false}, Argument{"baz", String, false}}, nil}
+ m := Method{"foo", false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil}
exp := "foo(string,string)"
if m.Sig() != exp {
t.Error("signature mismatch", exp, "!=", m.Sig())
@@ -561,7 +598,7 @@ func TestMethodSignature(t *testing.T) {
}
uintt, _ := NewType("uint")
- m = Method{"foo", false, []Argument{Argument{"bar", uintt, false}}, nil}
+ m = Method{"foo", false, []Argument{{"bar", uintt, false}}, nil}
exp = "foo(uint256)"
if m.Sig() != exp {
t.Error("signature mismatch", exp, "!=", m.Sig())
@@ -752,23 +789,58 @@ func TestDefaultFunctionParsing(t *testing.T) {
func TestBareEvents(t *testing.T) {
const definition = `[
{ "type" : "event", "name" : "balance" },
- { "type" : "event", "name" : "name" }]`
+ { "type" : "event", "name" : "anon", "anonymous" : true},
+ { "type" : "event", "name" : "args", "inputs" : [{ "indexed":false, "name":"arg0", "type":"uint256" }, { "indexed":true, "name":"arg1", "type":"address" }] }
+ ]`
+
+ arg0, _ := NewType("uint256")
+ arg1, _ := NewType("address")
+
+ expectedEvents := map[string]struct {
+ Anonymous bool
+ Args []Argument
+ }{
+ "balance": {false, nil},
+ "anon": {true, nil},
+ "args": {false, []Argument{
+ {Name: "arg0", Type: arg0, Indexed: false},
+ {Name: "arg1", Type: arg1, Indexed: true},
+ }},
+ }
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
- if len(abi.Events) != 2 {
- t.Error("expected 2 events")
+ if len(abi.Events) != len(expectedEvents) {
+ t.Fatalf("invalid number of events after parsing, want %d, got %d", len(expectedEvents), len(abi.Events))
}
- if _, ok := abi.Events["balance"]; !ok {
- t.Error("expected 'balance' event to be present")
- }
-
- if _, ok := abi.Events["name"]; !ok {
- t.Error("expected 'name' event to be present")
+ for name, exp := range expectedEvents {
+ got, ok := abi.Events[name]
+ if !ok {
+ t.Errorf("could not found event %s", name)
+ continue
+ }
+ if got.Anonymous != exp.Anonymous {
+ t.Errorf("invalid anonymous indication for event %s, want %v, got %v", name, exp.Anonymous, got.Anonymous)
+ }
+ if len(got.Inputs) != len(exp.Args) {
+ t.Errorf("invalid number of args, want %d, got %d", len(exp.Args), len(got.Inputs))
+ continue
+ }
+ for i, arg := range exp.Args {
+ if arg.Name != got.Inputs[i].Name {
+ t.Errorf("events[%s].Input[%d] has an invalid name, want %s, got %s", name, i, arg.Name, got.Inputs[i].Name)
+ }
+ if arg.Indexed != got.Inputs[i].Indexed {
+ t.Errorf("events[%s].Input[%d] has an invalid indexed indication, want %v, got %v", name, i, arg.Indexed, got.Inputs[i].Indexed)
+ }
+ if arg.Type.T != got.Inputs[i].Type.T {
+ t.Errorf("events[%s].Input[%d] has an invalid type, want %x, got %x", name, i, arg.Type.T, got.Inputs[i].Type.T)
+ }
+ }
}
}
diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go
index 188203a5d1..4691318ce7 100644
--- a/accounts/abi/argument.go
+++ b/accounts/abi/argument.go
@@ -31,8 +31,9 @@ type Argument struct {
func (a *Argument) UnmarshalJSON(data []byte) error {
var extarg struct {
- Name string
- Type string
+ Name string
+ Type string
+ Indexed bool
}
err := json.Unmarshal(data, &extarg)
if err != nil {
@@ -44,6 +45,7 @@ func (a *Argument) UnmarshalJSON(data []byte) error {
return err
}
a.Name = extarg.Name
+ a.Indexed = extarg.Indexed
return nil
}
diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go
index 8cef0ee7be..8a20d6a41c 100644
--- a/accounts/abi/bind/auth.go
+++ b/accounts/abi/bind/auth.go
@@ -52,7 +52,7 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
if address != keyAddr {
return nil, errors.New("not authorized to sign this account")
}
- signature, err := crypto.SignEthereum(signer.Hash(tx).Bytes(), key)
+ signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key)
if err != nil {
return nil, err
}
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 1c627ccd21..5a040743d0 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -225,7 +225,11 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
from.SetBalance(common.MaxBig)
// Execute the call.
msg := callmsg{call}
- vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
+
+ evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain)
+ // Create a new environment which holds all relevant information
+ // about the transaction and calling mechanisms.
+ vmenv := vm.NewEVM(evmContext, statedb, chainConfig, vm.Config{})
gaspool := new(core.GasPool).AddGas(common.MaxBig)
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
return ret, gasUsed, err
diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go
index de10dadc4d..9fcee75f24 100644
--- a/accounts/abi/bind/base.go
+++ b/accounts/abi/bind/base.go
@@ -170,7 +170,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
if value == nil {
value = new(big.Int)
}
- nonce := uint64(0)
+ var nonce uint64
if opts.Nonce == nil {
nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
if err != nil {
diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go
index 2516d9685e..e1da75c632 100644
--- a/accounts/abi/bind/bind.go
+++ b/accounts/abi/bind/bind.go
@@ -147,21 +147,21 @@ func bindTypeGo(kind abi.Type) string {
switch {
case strings.HasPrefix(stringKind, "address"):
- parts := regexp.MustCompile("address(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 2 {
return stringKind
}
return fmt.Sprintf("%scommon.Address", parts[1])
case strings.HasPrefix(stringKind, "bytes"):
- parts := regexp.MustCompile("bytes([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ parts := regexp.MustCompile(`bytes([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 3 {
return stringKind
}
return fmt.Sprintf("%s[%s]byte", parts[2], parts[1])
case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
- parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 4 {
return stringKind
}
@@ -172,7 +172,7 @@ func bindTypeGo(kind abi.Type) string {
return fmt.Sprintf("%s*big.Int", parts[3])
case strings.HasPrefix(stringKind, "bool") || strings.HasPrefix(stringKind, "string"):
- parts := regexp.MustCompile("([a-z]+)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ parts := regexp.MustCompile(`([a-z]+)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 3 {
return stringKind
}
@@ -191,7 +191,7 @@ func bindTypeJava(kind abi.Type) string {
switch {
case strings.HasPrefix(stringKind, "address"):
- parts := regexp.MustCompile("address(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 2 {
return stringKind
}
@@ -201,7 +201,7 @@ func bindTypeJava(kind abi.Type) string {
return fmt.Sprintf("Addresses")
case strings.HasPrefix(stringKind, "bytes"):
- parts := regexp.MustCompile("bytes([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ parts := regexp.MustCompile(`bytes([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 3 {
return stringKind
}
@@ -211,7 +211,7 @@ func bindTypeJava(kind abi.Type) string {
return "byte[]"
case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
- parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 4 {
return stringKind
}
@@ -230,7 +230,7 @@ func bindTypeJava(kind abi.Type) string {
return fmt.Sprintf("BigInts")
case strings.HasPrefix(stringKind, "bool"):
- parts := regexp.MustCompile("bool(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ parts := regexp.MustCompile(`bool(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 2 {
return stringKind
}
@@ -240,7 +240,7 @@ func bindTypeJava(kind abi.Type) string {
return fmt.Sprintf("bool[]")
case strings.HasPrefix(stringKind, "string"):
- parts := regexp.MustCompile("string(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
+ parts := regexp.MustCompile(`string(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 2 {
return stringKind
}
@@ -278,7 +278,7 @@ func namedTypeJava(javaKind string, solKind abi.Type) string {
case "bool[]":
return "Bools"
case "BigInt":
- parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(solKind.String())
+ parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
if len(parts) != 4 {
return javaKind
}
diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/util_test.go
index 97ea7fc0ad..f304dcbc38 100644
--- a/accounts/abi/bind/util_test.go
+++ b/accounts/abi/bind/util_test.go
@@ -60,7 +60,7 @@ func TestWaitDeployed(t *testing.T) {
// Create the transaction.
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code))
- tx, _ = tx.SignECDSA(types.HomesteadSigner{}, testKey)
+ tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
// Wait for it to get mined in the background.
var (
diff --git a/accounts/abi/event.go b/accounts/abi/event.go
index 71b2deddac..6613fd43d6 100644
--- a/accounts/abi/event.go
+++ b/accounts/abi/event.go
@@ -25,10 +25,12 @@ import (
)
// Event is an event potentially triggered by the EVM's LOG mechanism. The Event
-// holds type information (inputs) about the yielded output
+// holds type information (inputs) about the yielded output. Anonymous events
+// don't get the signature canonical representation as the first LOG topic.
type Event struct {
- Name string
- Inputs []Argument
+ Name string
+ Anonymous bool
+ Inputs []Argument
}
// Id returns the canonical representation of the event's signature used by the
diff --git a/accounts/abi/packing.go b/accounts/abi/packing.go
index f4c9c1e14d..a117a37ac9 100644
--- a/accounts/abi/packing.go
+++ b/accounts/abi/packing.go
@@ -54,7 +54,7 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
reflectValue = mustArrayToByteSlice(reflectValue)
}
return packBytesSlice(reflectValue.Bytes(), reflectValue.Len())
- case FixedBytesTy:
+ case FixedBytesTy, FunctionTy:
if reflectValue.Kind() == reflect.Array {
reflectValue = mustArrayToByteSlice(reflectValue)
}
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
index 39b843f817..f2832aef56 100644
--- a/accounts/abi/type.go
+++ b/accounts/abi/type.go
@@ -33,7 +33,8 @@ const (
FixedBytesTy
BytesTy
HashTy
- RealTy
+ FixedpointTy
+ FunctionTy
)
// Type is the reflection of the supported argument type
@@ -57,16 +58,16 @@ var (
// Types can be in the format of:
//
// Input = Type [ "[" [ Number ] "]" ] Name .
- // Type = [ "u" ] "int" [ Number ] .
+ // Type = [ "u" ] "int" [ Number ] [ x ] [ Number ].
//
// Examples:
//
- // string int uint real
+ // string int uint fixed
// string32 int8 uint8 uint[]
- // address int256 uint256 real[2]
- fullTypeRegex = regexp.MustCompile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?")
+ // address int256 uint256 fixed128x128[2]
+ fullTypeRegex = regexp.MustCompile(`([a-zA-Z0-9]+)(\[([0-9]*)\])?`)
// typeRegex parses the abi sub types
- typeRegex = regexp.MustCompile("([a-zA-Z]+)([0-9]*)?")
+ typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
)
// NewType creates a new reflection type of abi type given in t.
@@ -90,14 +91,19 @@ func NewType(t string) (typ Type, err error) {
}
typ.Elem = &sliceType
typ.stringKind = sliceType.stringKind + t[len(res[1]):]
- return typ, nil
+ // Although we know that this is an array, we cannot return
+ // as we don't know the type of the element, however, if it
+ // is still an array, then don't determine the type.
+ if typ.Elem.IsArray || typ.Elem.IsSlice {
+ return typ, nil
+ }
}
// parse the type and size of the abi-type.
parsedType := typeRegex.FindAllStringSubmatch(res[1], -1)[0]
// varSize is the size of the variable
var varSize int
- if len(parsedType[2]) > 0 {
+ if len(parsedType[3]) > 0 {
var err error
varSize, err = strconv.Atoi(parsedType[2])
if err != nil {
@@ -111,7 +117,12 @@ func NewType(t string) (typ Type, err error) {
varSize = 256
t += "256"
}
- typ.stringKind = t
+
+ // only set stringKind if not array or slice, as for those,
+ // the correct string type has been set
+ if !(typ.IsArray || typ.IsSlice) {
+ typ.stringKind = t
+ }
switch varType {
case "int":
@@ -148,6 +159,12 @@ func NewType(t string) (typ Type, err error) {
typ.T = FixedBytesTy
typ.SliceSize = varSize
}
+ case "function":
+ sliceType, _ := NewType("uint8")
+ typ.Elem = &sliceType
+ typ.IsArray = true
+ typ.T = FunctionTy
+ typ.SliceSize = 24
default:
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
}
@@ -168,7 +185,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
return nil, err
}
- if (t.IsSlice || t.IsArray) && t.T != BytesTy && t.T != FixedBytesTy {
+ if (t.IsSlice || t.IsArray) && t.T != BytesTy && t.T != FixedBytesTy && t.T != FunctionTy {
var packed []byte
for i := 0; i < v.Len(); i++ {
diff --git a/accounts/abi/type_test.go b/accounts/abi/type_test.go
new file mode 100644
index 0000000000..1557c2a41b
--- /dev/null
+++ b/accounts/abi/type_test.go
@@ -0,0 +1,78 @@
+// 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"
+ "testing"
+)
+
+// typeWithoutStringer is a alias for the Type type which simply doesn't implement
+// the stringer interface to allow printing type details in the tests below.
+type typeWithoutStringer Type
+
+// Tests that all allowed types get recognized by the type parser.
+func TestTypeRegexp(t *testing.T) {
+ tests := []struct {
+ blob string
+ kind Type
+ }{
+ {"int", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}},
+ {"int8", Type{Kind: reflect.Int8, Type: big_t, Size: 8, T: IntTy, stringKind: "int8"}},
+ {"int256", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}},
+ {"int[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
+ {"int[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
+ {"int32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
+ {"int32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
+ {"uint", Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}},
+ {"uint8", Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}},
+ {"uint256", Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}},
+ {"uint[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
+ {"uint[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
+ {"uint32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint32, Type: ubig_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: big_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
+ {"uint32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint32, Type: ubig_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: big_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
+ {"bytes", Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}},
+ {"bytes32", Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}},
+ {"bytes[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
+ {"bytes[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[2]"}},
+ {"bytes32[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
+ {"bytes32[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
+ {"string", Type{Kind: reflect.String, Size: -1, T: StringTy, stringKind: "string"}},
+ {"string[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[]"}},
+ {"string[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[2]"}},
+ {"address", Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}},
+ {"address[]", Type{IsSlice: true, SliceSize: -1,Kind: reflect.Array, Type:address_t, T: AddressTy, Size:20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
+ {"address[2]", Type{IsArray: true, SliceSize: 2,Kind: reflect.Array, Type:address_t, T: AddressTy, Size:20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
+
+ // TODO when fixed types are implemented properly
+ // {"fixed", Type{}},
+ // {"fixed128x128", Type{}},
+ // {"fixed[]", Type{}},
+ // {"fixed[2]", Type{}},
+ // {"fixed128x128[]", Type{}},
+ // {"fixed128x128[2]", Type{}},
+ }
+ for i, tt := range tests {
+ typ, err := NewType(tt.blob)
+ if err != nil {
+ t.Errorf("type %d: failed to parse type string: %v", i, err)
+ }
+ if !reflect.DeepEqual(typ, tt.kind) {
+ t.Errorf("type %d: parsed type mismatch:\n have %+v\n want %+v", i, typeWithoutStringer(typ), typeWithoutStringer(tt.kind))
+ }
+ }
+}
diff --git a/accounts/account_manager.go b/accounts/account_manager.go
index c3b340bcfb..a3bc57db8c 100644
--- a/accounts/account_manager.go
+++ b/accounts/account_manager.go
@@ -136,13 +136,12 @@ func (am *Manager) DeleteAccount(a Account, passphrase string) error {
return err
}
-// Sign calculates a ECDSA signature for the given hash.
-// Note, Ethereum signatures have a particular format as described in the
-// yellow paper. Use the SignEthereum function to calculate a signature
-// in Ethereum format.
+// Sign calculates a ECDSA signature for the given hash. The produced signature
+// is in the [R || S || V] format where V is 0 or 1.
func (am *Manager) Sign(addr common.Address, hash []byte) ([]byte, error) {
am.mu.RLock()
defer am.mu.RUnlock()
+
unlockedKey, found := am.unlocked[addr]
if !found {
return nil, ErrLocked
@@ -150,28 +149,16 @@ func (am *Manager) Sign(addr common.Address, hash []byte) ([]byte, error) {
return crypto.Sign(hash, unlockedKey.PrivateKey)
}
-// SignEthereum calculates a ECDSA signature for the given hash.
-// The signature has the format as described in the Ethereum yellow paper.
-func (am *Manager) SignEthereum(addr common.Address, hash []byte) ([]byte, error) {
- am.mu.RLock()
- defer am.mu.RUnlock()
- unlockedKey, found := am.unlocked[addr]
- if !found {
- return nil, ErrLocked
- }
- return crypto.SignEthereum(hash, unlockedKey.PrivateKey)
-}
-
-// SignWithPassphrase signs hash if the private key matching the given
-// address can be decrypted with the given passphrase.
-func (am *Manager) SignWithPassphrase(addr common.Address, passphrase string, hash []byte) (signature []byte, err error) {
- _, key, err := am.getDecryptedKey(Account{Address: addr}, passphrase)
+// SignWithPassphrase signs hash if the private key matching the given address
+// can be decrypted with the given passphrase. The produced signature is in the
+// [R || S || V] format where V is 0 or 1.
+func (am *Manager) SignWithPassphrase(a Account, passphrase string, hash []byte) (signature []byte, err error) {
+ _, key, err := am.getDecryptedKey(a, passphrase)
if err != nil {
return nil, err
}
-
defer zeroKey(key.PrivateKey)
- return crypto.SignEthereum(hash, key.PrivateKey)
+ return crypto.Sign(hash, key.PrivateKey)
}
// Unlock unlocks the given account indefinitely.
diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go
index 5ae3aa55c7..988ae1a4b3 100644
--- a/accounts/accounts_test.go
+++ b/accounts/accounts_test.go
@@ -95,7 +95,7 @@ func TestSignWithPassphrase(t *testing.T) {
t.Fatal("expected account to be locked")
}
- _, err = am.SignWithPassphrase(acc.Address, pass, testSigData)
+ _, err = am.SignWithPassphrase(acc, pass, testSigData)
if err != nil {
t.Fatal(err)
}
@@ -104,7 +104,7 @@ func TestSignWithPassphrase(t *testing.T) {
t.Fatal("expected account to be locked")
}
- if _, err = am.SignWithPassphrase(acc.Address, "invalid passwd", testSigData); err == nil {
+ if _, err = am.SignWithPassphrase(acc, "invalid passwd", testSigData); err == nil {
t.Fatal("expected SignHash to fail with invalid password")
}
}
@@ -115,6 +115,9 @@ func TestTimedUnlock(t *testing.T) {
pass := "foo"
a1, err := am.NewAccount(pass)
+ if err != nil {
+ t.Fatal(err)
+ }
// Signing without passphrase fails because account is locked
_, err = am.Sign(a1.Address, testSigData)
@@ -147,6 +150,9 @@ func TestOverrideUnlock(t *testing.T) {
pass := "foo"
a1, err := am.NewAccount(pass)
+ if err != nil {
+ t.Fatal(err)
+ }
// Unlock indefinitely.
if err = am.TimedUnlock(a1, pass, 5*time.Minute); err != nil {
diff --git a/accounts/presale.go b/accounts/presale.go
index 211a241564..17fa4f13d3 100644
--- a/accounts/presale.go
+++ b/accounts/presale.go
@@ -22,6 +22,7 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
+ "errors"
"fmt"
"github.com/ubiq/go-ubiq/crypto"
@@ -53,6 +54,9 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
return nil, err
}
encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
+ if err != nil {
+ return nil, errors.New("invalid hex in encSeed")
+ }
iv := encSeedBytes[:16]
cipherText := encSeedBytes[16:]
/*
diff --git a/appveyor.yml b/appveyor.yml
index 181ea0d5d6..3463e4e091 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -22,8 +22,8 @@ environment:
install:
- rmdir C:\go /s /q
- - appveyor DownloadFile https://storage.googleapis.com/golang/go1.7.3.windows-%GETH_ARCH%.zip
- - 7z x go1.7.3.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
+ - appveyor DownloadFile https://storage.googleapis.com/golang/go1.7.4.windows-%GETH_ARCH%.zip
+ - 7z x go1.7.4.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
- go version
- gcc --version
diff --git a/build/ci.go b/build/ci.go
index 3c24aba018..7826088cb1 100644
--- a/build/ci.go
+++ b/build/ci.go
@@ -24,7 +24,7 @@ Usage: go run ci.go
Available commands are:
install [-arch architecture] [ packages... ] -- builds packages and executables
- test [ -coverage ] [ -vet ] [ packages... ] -- runs the tests
+ test [ -coverage ] [ -vet ] [ -misspell ] [ packages... ] -- runs the tests
archive [-arch architecture] [ -type zip|tar ] [ -signer key-envvar ] [ -upload dest ] -- archives build artefacts
importkeys -- imports signing keys from env
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
@@ -72,9 +72,7 @@ var (
executablePath("abigen"),
executablePath("evm"),
executablePath("gubiq"),
- executablePath("bzzd"),
- executablePath("bzzhash"),
- executablePath("bzzup"),
+ executablePath("swarm"),
executablePath("rlpdump"),
}
@@ -93,16 +91,8 @@ var (
Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.",
},
{
- Name: "bzzd",
- Description: "Ethereum Swarm daemon",
- },
- {
- Name: "bzzup",
- Description: "Ethereum Swarm command line file/directory uploader",
- },
- {
- Name: "bzzhash",
- Description: "Ethereum Swarm file/directory hash calculator",
+ Name: "swarm",
+ Description: "Ethereum Swarm daemon and tools",
},
{
Name: "abigen",
@@ -112,7 +102,8 @@ var (
// Distros for which packages are created.
// Note: vivid is unsupported because there is no golang-1.6 package for it.
- debDistros = []string{"trusty", "wily", "xenial", "yakkety"}
+ // Note: wily is unsupported because it was officially deprecated on lanchpad.
+ debDistros = []string{"trusty", "xenial", "yakkety"}
)
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
@@ -204,7 +195,7 @@ func doInstall(cmdline []string) {
if err != nil {
log.Fatal(err)
}
- for name, _ := range pkgs {
+ for name := range pkgs {
if name == "main" {
gobuild := goToolArch(*arch, "build", buildFlags(env)...)
gobuild.Args = append(gobuild.Args, "-v")
@@ -271,6 +262,7 @@ func goToolArch(arch string, subcmd string, args ...string) *exec.Cmd {
func doTest(cmdline []string) {
var (
vet = flag.Bool("vet", false, "Whether to run go vet")
+ misspell = flag.Bool("misspell", false, "Whether to run the spell checker")
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
)
flag.CommandLine.Parse(cmdline)
@@ -296,7 +288,9 @@ func doTest(cmdline []string) {
if *vet {
build.MustRun(goTool("vet", packages...))
}
-
+ if *misspell {
+ spellcheck(packages)
+ }
// Run the actual tests.
gotest := goTool("test")
// Test a single package at a time. CI builders are slow
@@ -309,6 +303,34 @@ func doTest(cmdline []string) {
build.MustRun(gotest)
}
+// spellcheck runs the client9/misspell spellchecker package on all Go, Cgo and
+// test files in the requested packages.
+func spellcheck(packages []string) {
+ // Ensure the spellchecker is available
+ build.MustRun(goTool("get", "github.com/client9/misspell/cmd/misspell"))
+
+ // Windows chokes on long argument lists, check packages individualy
+ for _, pkg := range packages {
+ // The spell checker doesn't work on packages, gather all .go files for it
+ out, err := goTool("list", "-f", "{{.Dir}}{{range .GoFiles}}\n{{.}}{{end}}{{range .CgoFiles}}\n{{.}}{{end}}{{range .TestGoFiles}}\n{{.}}{{end}}", pkg).CombinedOutput()
+ if err != nil {
+ log.Fatalf("source file listing failed: %v\n%s", err, string(out))
+ }
+ // Retrieve the folder and assemble the source list
+ lines := strings.Split(string(out), "\n")
+ root := lines[0]
+
+ sources := make([]string, 0, len(lines)-1)
+ for _, line := range lines[1:] {
+ if line = strings.TrimSpace(line); line != "" {
+ sources = append(sources, filepath.Join(root, line))
+ }
+ }
+ // Run the spell checker for this particular package
+ build.MustRunCommand(filepath.Join(GOBIN, "misspell"), append([]string{"-error"}, sources...)...)
+ }
+}
+
// Release Packaging
func doArchive(cmdline []string) {
@@ -332,7 +354,7 @@ func doArchive(cmdline []string) {
var (
env = build.Env()
base = archiveBasename(*arch, env)
- gubiq = "gubiq-" + base + ext
+ gubiq = "gubiq-" + base + ext
alltools = "gubiq-alltools-" + base + ext
)
maybeSkipArchive(env)
@@ -612,8 +634,8 @@ func doWindowsInstaller(cmdline []string) {
// Aggregate binaries that are included in the installer
var (
- devTools []string
- allTools []string
+ devTools []string
+ allTools []string
gubiqTool string
)
for _, file := range allToolsArchiveFiles {
@@ -632,12 +654,13 @@ func doWindowsInstaller(cmdline []string) {
// first section contains the gubiq binary, second section holds the dev tools.
templateData := map[string]interface{}{
"License": "COPYING",
- "Gubiq": gubiqTool,
+ "Gubiq": gubiqTool,
"DevTools": devTools,
}
build.Render("build/nsis.gubiq.nsi", filepath.Join(*workdir, "gubiq.nsi"), 0644, nil)
build.Render("build/nsis.install.nsh", filepath.Join(*workdir, "install.nsh"), 0644, templateData)
build.Render("build/nsis.uninstall.nsh", filepath.Join(*workdir, "uninstall.nsh"), 0644, allTools)
+ build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil)
build.Render("build/nsis.envvarupdate.nsh", filepath.Join(*workdir, "EnvVarUpdate.nsh"), 0644, nil)
build.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll", 0755)
build.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING", 0755)
@@ -800,7 +823,7 @@ func doXCodeFramework(cmdline []string) {
// Build the iOS XCode framework
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile"))
build.MustRun(gomobileTool("init"))
- bind := gomobileTool("bind", "--target", "ios", "--tags", "ios", "--prefix", "GE", "-v", "github.com/ubiq/go-ubiq/mobile")
+ bind := gomobileTool("bind", "--target", "ios", "--tags", "ios", "-v", "github.com/ubiq/go-ubiq/mobile")
if *local {
// If we're building locally, use the build folder and stop afterwards
diff --git a/build/nsis.gubiq.nsi b/build/nsis.gubiq.nsi
index 1c5546e083..f7a2e1a32b 100644
--- a/build/nsis.gubiq.nsi
+++ b/build/nsis.gubiq.nsi
@@ -17,8 +17,12 @@
#
# Requirements:
# - NSIS, http://nsis.sourceforge.net/Main_Page
+# - NSIS Large Strings build, http://nsis.sourceforge.net/Special_Builds
# - SFP, http://nsis.sourceforge.net/NSIS_Simple_Firewall_Plugin (put dll in NSIS\Plugins\x86-ansi)
#
+# After intalling NSIS extra the NSIS Large Strings build zip and replace the makensis.exe and the
+# files found in Stub.
+#
# based on: http://nsis.sourceforge.net/A_simple_installer_with_start_menu_shortcut_and_uninstaller
#
# TODO:
@@ -37,6 +41,7 @@ RequestExecutionLevel admin
SetCompressor /SOLID lzma
!include LogicLib.nsh
+!include PathUpdate.nsh
!include EnvVarUpdate.nsh
!macro VerifyUserIsAdmin
diff --git a/build/nsis.install.nsh b/build/nsis.install.nsh
index 82f9153cb2..2664c73bb3 100644
--- a/build/nsis.install.nsh
+++ b/build/nsis.install.nsh
@@ -37,8 +37,9 @@ Section "Gubiq" GETH_IDX
${EnvVarUpdate} $0 "ETHEREUM_SOCKET" "R" "HKLM" "\\.\pipe\gubiq.ipc"
${EnvVarUpdate} $0 "ETHEREUM_SOCKET" "A" "HKLM" "\\.\pipe\gubiq.ipc"
- # Add gubiq to PATH
- ${EnvVarUpdate} $0 "PATH" "A" "HKLM" $INSTDIR
+ # Add instdir to PATH
+ Push "$INSTDIR"
+ Call AddToPath
SectionEnd
# Install optional develop tools.
diff --git a/build/nsis.pathupdate.nsh b/build/nsis.pathupdate.nsh
new file mode 100644
index 0000000000..f54b7e3e13
--- /dev/null
+++ b/build/nsis.pathupdate.nsh
@@ -0,0 +1,153 @@
+!include "WinMessages.nsh"
+
+; see https://support.microsoft.com/en-us/kb/104011
+!define Environ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
+; HKEY_LOCAL_MACHINE = 0x80000002
+
+; AddToPath - Appends dir to PATH
+; (does not work on Win9x/ME)
+;
+; Usage:
+; Push "dir"
+; Call AddToPath
+Function AddToPath
+ Exch $0
+ Push $1
+ Push $2
+ Push $3
+ Push $4
+
+ ; NSIS ReadRegStr returns empty string on string overflow
+ ; Native calls are used here to check actual length of PATH
+ ; $4 = RegOpenKey(HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Control\Session Manager\Environment", &$3)
+ System::Call "advapi32::RegOpenKey(i 0x80000002, t'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', *i.r3) i.r4"
+ IntCmp $4 0 0 done done
+
+ ; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2))
+ ; RegCloseKey($3)
+ System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4"
+ System::Call "advapi32::RegCloseKey(i $3)"
+
+ IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA
+ DetailPrint "AddToPath: original length $2 > ${NSIS_MAX_STRLEN}"
+ MessageBox MB_OK "PATH not updated, original length $2 > ${NSIS_MAX_STRLEN}"
+ Goto done
+
+ IntCmp $4 0 +5 ; $4 != NO_ERROR
+ IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND
+ DetailPrint "AddToPath: unexpected error code $4"
+ Goto done
+ StrCpy $1 ""
+
+ ; Check if already in PATH
+ Push "$1;"
+ Push "$0;"
+ Call StrStr
+ Pop $2
+ StrCmp $2 "" 0 done
+ Push "$1;"
+ Push "$0\;"
+ Call StrStr
+ Pop $2
+ StrCmp $2 "" 0 done
+
+ ; Prevent NSIS string overflow
+ StrLen $2 $0
+ StrLen $3 $1
+ IntOp $2 $2 + $3
+ IntOp $2 $2 + 2 ; $2 = strlen(dir) + strlen(PATH) + sizeof(";")
+ IntCmp $2 ${NSIS_MAX_STRLEN} +4 +4 0
+ DetailPrint "AddToPath: new length $2 > ${NSIS_MAX_STRLEN}"
+ MessageBox MB_OK "PATH not updated, new length $2 > ${NSIS_MAX_STRLEN}."
+ Goto done
+
+ ; Append dir to PATH
+ DetailPrint "Add to PATH: $0"
+ StrCpy $2 $1 1 -1
+ StrCmp $2 ";" 0 +2
+ StrCpy $1 $1 -1 ; remove trailing ';'
+ StrCmp $1 "" +2 ; no leading ';'
+ StrCpy $0 "$1;$0"
+
+ WriteRegExpandStr ${Environ} "PATH" $0
+ SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
+
+done:
+ Pop $4
+ Pop $3
+ Pop $2
+ Pop $1
+ Pop $0
+FunctionEnd
+
+
+; RemoveFromPath - Removes dir from PATH
+;
+; Usage:
+; Push "dir"
+; Call RemoveFromPath
+Function un.RemoveFromPath
+ Exch $0
+ Push $1
+ Push $2
+ Push $3
+ Push $4
+ Push $5
+ Push $6
+
+ ; NSIS ReadRegStr returns empty string on string overflow
+ ; Native calls are used here to check actual length of PATH
+ ; $4 = RegOpenKey(HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Control\Session Manager\Environment", &$3)
+ System::Call "advapi32::RegOpenKey(i 0x80000002, t'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', *i.r3) i.r4"
+ IntCmp $4 0 0 done done
+
+ ; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2))
+ ; RegCloseKey($3)
+ System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4"
+ System::Call "advapi32::RegCloseKey(i $3)"
+
+ IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA
+ DetailPrint "RemoveFromPath: original length $2 > ${NSIS_MAX_STRLEN}"
+ MessageBox MB_OK "PATH not updated, original length $2 > ${NSIS_MAX_STRLEN}"
+ Goto done
+
+ IntCmp $4 0 +5 ; $4 != NO_ERROR
+ IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND
+ DetailPrint "RemoveFromPath: unexpected error code $4"
+ Goto done
+ StrCpy $1 ""
+
+ ; length < ${NSIS_MAX_STRLEN} -> ReadRegStr can be used
+ ReadRegStr $1 ${Environ} "PATH"
+ StrCpy $5 $1 1 -1
+ StrCmp $5 ";" +2
+ StrCpy $1 "$1;" ; ensure trailing ';'
+ Push $1
+ Push "$0;"
+ Call un.StrStr
+ Pop $2 ; pos of our dir
+ StrCmp $2 "" done
+
+ DetailPrint "Remove from PATH: $0"
+ StrLen $3 "$0;"
+ StrLen $4 $2
+ StrCpy $5 $1 -$4 ; $5 is now the part before the path to remove
+ StrCpy $6 $2 "" $3 ; $6 is now the part after the path to remove
+ StrCpy $3 "$5$6"
+ StrCpy $5 $3 1 -1
+ StrCmp $5 ";" 0 +2
+ StrCpy $3 $3 -1 ; remove trailing ';'
+ WriteRegExpandStr ${Environ} "PATH" $3
+ SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
+
+done:
+ Pop $6
+ Pop $5
+ Pop $4
+ Pop $3
+ Pop $2
+ Pop $1
+ Pop $0
+FunctionEnd
+
+
diff --git a/build/nsis.uninstall.nsh b/build/nsis.uninstall.nsh
index 04a15d2820..c7fe292426 100644
--- a/build/nsis.uninstall.nsh
+++ b/build/nsis.uninstall.nsh
@@ -25,7 +25,8 @@ Section "Uninstall"
${un.EnvVarUpdate} $0 "ETHEREUM_SOCKET" "R" "HKLM" "\\.\pipe\gubiq.ipc"
# Remove install directory from PATH
- ${un.EnvVarUpdate} $0 "PATH" "R" "HKLM" $INSTDIR
+ Push "$INSTDIR"
+ Call un.RemoveFromPath
# Cleanup registry (deletes all sub keys)
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}"
diff --git a/build/update-license.go b/build/update-license.go
index f1ebd53bad..bc956ea136 100644
--- a/build/update-license.go
+++ b/build/update-license.go
@@ -185,7 +185,7 @@ func getFiles() []string {
files = append(files, line)
})
if err != nil {
- log.Fatalf("error getting files:", err)
+ log.Fatal("error getting files:", err)
}
return files
}
@@ -294,7 +294,7 @@ func getInfo(files <-chan string, out chan<- *info, wg *sync.WaitGroup) {
wg.Done()
}
-// fileInfo finds the lowest year in which the given file was commited.
+// fileInfo finds the lowest year in which the given file was committed.
func fileInfo(file string) (*info, error) {
info := &info{file: file, Year: int64(time.Now().Year())}
cmd := exec.Command("git", "log", "--follow", "--find-renames=80", "--find-copies=80", "--pretty=format:%ai", "--", file)
diff --git a/cmd/bzzup/main.go b/cmd/bzzup/main.go
deleted file mode 100644
index 7d251aadb0..0000000000
--- a/cmd/bzzup/main.go
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
-
-// Command bzzup uploads files to the swarm HTTP API.
-package main
-
-import (
- "bytes"
- "encoding/json"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "mime"
- "net/http"
- "os"
- "path/filepath"
- "strings"
-)
-
-func main() {
- var (
- bzzapiFlag = flag.String("bzzapi", "http://127.0.0.1:8500", "Swarm HTTP endpoint")
- recursiveFlag = flag.Bool("recursive", false, "Upload directories recursively")
- manifestFlag = flag.Bool("manifest", true, "Skip automatic manifest upload")
- )
- log.SetOutput(os.Stderr)
- log.SetFlags(0)
- flag.Parse()
- if flag.NArg() != 1 {
- log.Fatal("need filename as the first and only argument")
- }
-
- var (
- file = flag.Arg(0)
- client = &client{api: *bzzapiFlag}
- mroot manifest
- )
- fi, err := os.Stat(file)
- if err != nil {
- log.Fatal(err)
- }
- if fi.IsDir() {
- if !*recursiveFlag {
- log.Fatal("argument is a directory and recursive upload is disabled")
- }
- mroot, err = client.uploadDirectory(file)
- } else {
- mroot, err = client.uploadFile(file, fi)
- if *manifestFlag {
- // Wrap the raw file entry in a proper manifest so both hashes get printed.
- mroot = manifest{Entries: []manifest{mroot}}
- }
- }
- if err != nil {
- log.Fatalln("upload failed:", err)
- }
- if *manifestFlag {
- hash, err := client.uploadManifest(mroot)
- if err != nil {
- log.Fatalln("manifest upload failed:", err)
- }
- mroot.Hash = hash
- }
-
- // Print the manifest. This is the only output to stdout.
- mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
- fmt.Println(string(mrootJSON))
-}
-
-// client wraps interaction with the swarm HTTP gateway.
-type client struct {
- api string
-}
-
-// manifest is the JSON representation of a swarm manifest.
-type manifest struct {
- Hash string `json:"hash,omitempty"`
- ContentType string `json:"contentType,omitempty"`
- Path string `json:"path,omitempty"`
- Entries []manifest `json:"entries,omitempty"`
-}
-
-func (c *client) uploadFile(file string, fi os.FileInfo) (manifest, error) {
- hash, err := c.uploadFileContent(file, fi)
- m := manifest{
- Hash: hash,
- ContentType: mime.TypeByExtension(filepath.Ext(fi.Name())),
- }
- return m, err
-}
-
-func (c *client) uploadDirectory(dir string) (manifest, error) {
- dirm := manifest{}
- prefix := filepath.ToSlash(filepath.Clean(dir)) + "/"
- err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
- if err != nil || fi.IsDir() {
- return err
- }
- if !strings.HasPrefix(path, dir) {
- return fmt.Errorf("path %s outside directory %s", path, dir)
- }
- entry, err := c.uploadFile(path, fi)
- entry.Path = strings.TrimPrefix(filepath.ToSlash(filepath.Clean(path)), prefix)
- dirm.Entries = append(dirm.Entries, entry)
- return err
- })
- return dirm, err
-}
-
-func (c *client) uploadFileContent(file string, fi os.FileInfo) (string, error) {
- fd, err := os.Open(file)
- if err != nil {
- return "", err
- }
- defer fd.Close()
- log.Printf("uploading file %s (%d bytes)", file, fi.Size())
- return c.postRaw("application/octet-stream", fi.Size(), fd)
-}
-
-func (c *client) uploadManifest(m manifest) (string, error) {
- jsm, err := json.Marshal(m)
- if err != nil {
- panic(err)
- }
- log.Println("uploading manifest")
- return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm)))
-}
-
-func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (string, error) {
- req, err := http.NewRequest("POST", c.api+"/bzzr:/", body)
- if err != nil {
- return "", err
- }
- req.Header.Set("content-type", mimetype)
- req.ContentLength = size
- resp, err := http.DefaultClient.Do(req)
- if err != nil {
- return "", err
- }
- defer resp.Body.Close()
- if resp.StatusCode >= 400 {
- return "", fmt.Errorf("bad status: %s", resp.Status)
- }
- content, err := ioutil.ReadAll(resp.Body)
- return string(content), err
-}
diff --git a/cmd/disasm/main.go b/cmd/disasm/main.go
index 4e6d860d90..d2f5c1dfc0 100644
--- a/cmd/disasm/main.go
+++ b/cmd/disasm/main.go
@@ -18,11 +18,12 @@
package main
import (
+ "encoding/hex"
"fmt"
"io/ioutil"
"os"
+ "strings"
- "github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core/vm"
)
@@ -32,20 +33,28 @@ func main() {
fmt.Println(err)
os.Exit(1)
}
- code = common.Hex2Bytes(string(code[:len(code)-1]))
+ code, err = hex.DecodeString(strings.TrimSpace(string(code[:])))
+ if err != nil {
+ fmt.Printf("Error: %v\n", err)
+ return
+ }
fmt.Printf("%x\n", code)
for pc := uint64(0); pc < uint64(len(code)); pc++ {
op := vm.OpCode(code[pc])
- fmt.Printf("%-5d %v", pc, op)
switch op {
case vm.PUSH1, vm.PUSH2, vm.PUSH3, vm.PUSH4, vm.PUSH5, vm.PUSH6, vm.PUSH7, vm.PUSH8, vm.PUSH9, vm.PUSH10, vm.PUSH11, vm.PUSH12, vm.PUSH13, vm.PUSH14, vm.PUSH15, vm.PUSH16, vm.PUSH17, vm.PUSH18, vm.PUSH19, vm.PUSH20, vm.PUSH21, vm.PUSH22, vm.PUSH23, vm.PUSH24, vm.PUSH25, vm.PUSH26, vm.PUSH27, vm.PUSH28, vm.PUSH29, vm.PUSH30, vm.PUSH31, vm.PUSH32:
a := uint64(op) - uint64(vm.PUSH1) + 1
- fmt.Printf(" => %x", code[pc+1:pc+1+a])
-
+ u := pc + 1 + a
+ if uint64(len(code)) <= pc || uint64(len(code)) < u {
+ fmt.Printf("Error: incomplete push instruction at %v\n", pc)
+ return
+ }
+ fmt.Printf("%-5d %v => %x\n", pc, op, code[pc+1:u])
pc += a
+ default:
+ fmt.Printf("%-5d %v\n", pc, op)
}
- fmt.Println()
}
}
diff --git a/cmd/ethtest/main.go b/cmd/ethtest/main.go
index cc60113aed..dc3fc9238f 100644
--- a/cmd/ethtest/main.go
+++ b/cmd/ethtest/main.go
@@ -88,12 +88,7 @@ func runTestWithReader(test string, r io.Reader) error {
default:
err = fmt.Errorf("Invalid test type specified: %v", test)
}
-
- if err != nil {
- return err
- }
-
- return nil
+ return err
}
func getFiles(path string) ([]string, error) {
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index 085831d4dd..41bfca424e 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -20,21 +20,18 @@ package main
import (
"fmt"
"io/ioutil"
- "math/big"
"os"
- "runtime"
+ goruntime "runtime"
"time"
"github.com/ubiq/go-ubiq/cmd/utils"
"github.com/ubiq/go-ubiq/common"
- "github.com/ubiq/go-ubiq/core"
"github.com/ubiq/go-ubiq/core/state"
- "github.com/ubiq/go-ubiq/core/types"
"github.com/ubiq/go-ubiq/core/vm"
+ "github.com/ubiq/go-ubiq/core/vm/runtime"
"github.com/ubiq/go-ubiq/crypto"
"github.com/ubiq/go-ubiq/ethdb"
"github.com/ubiq/go-ubiq/logger/glog"
- "github.com/ubiq/go-ubiq/params"
"gopkg.in/urfave/cli.v1"
)
@@ -47,14 +44,6 @@ var (
Name: "debug",
Usage: "output full trace logs",
}
- ForceJitFlag = cli.BoolFlag{
- Name: "forcejit",
- Usage: "forces jit compilation",
- }
- DisableJitFlag = cli.BoolFlag{
- Name: "nojit",
- Usage: "disabled jit compilation",
- }
CodeFlag = cli.StringFlag{
Name: "code",
Usage: "EVM code",
@@ -98,6 +87,10 @@ var (
Name: "create",
Usage: "indicates the action should be create rather than call",
}
+ DisableGasMeteringFlag = cli.BoolFlag{
+ Name: "nogasmetering",
+ Usage: "disable gas metering",
+ }
)
func init() {
@@ -105,8 +98,6 @@ func init() {
CreateFlag,
DebugFlag,
VerbosityFlag,
- ForceJitFlag,
- DisableJitFlag,
SysStatFlag,
CodeFlag,
CodeFileFlag,
@@ -115,6 +106,7 @@ func init() {
ValueFlag,
DumpFlag,
InputFlag,
+ DisableGasMeteringFlag,
}
app.Action = run
}
@@ -129,13 +121,6 @@ func run(ctx *cli.Context) error {
logger := vm.NewStructLogger(nil)
- vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), vm.Config{
- Debug: ctx.GlobalBool(DebugFlag.Name),
- ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
- EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
- Tracer: logger,
- })
-
tstart := time.Now()
var (
@@ -168,25 +153,32 @@ func run(ctx *cli.Context) error {
if ctx.GlobalBool(CreateFlag.Name) {
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
- ret, _, err = vmenv.Create(
- sender,
- input,
- common.Big(ctx.GlobalString(GasFlag.Name)),
- common.Big(ctx.GlobalString(PriceFlag.Name)),
- common.Big(ctx.GlobalString(ValueFlag.Name)),
- )
+ ret, _, err = runtime.Create(input, &runtime.Config{
+ Origin: sender.Address(),
+ State: statedb,
+ GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
+ GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
+ Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
+ EVMConfig: vm.Config{
+ Tracer: logger,
+ DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
+ },
+ })
} else {
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
-
receiver.SetCode(crypto.Keccak256Hash(code), code)
- ret, err = vmenv.Call(
- sender,
- receiver.Address(),
- common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)),
- common.Big(ctx.GlobalString(GasFlag.Name)),
- common.Big(ctx.GlobalString(PriceFlag.Name)),
- common.Big(ctx.GlobalString(ValueFlag.Name)),
- )
+
+ ret, err = runtime.Call(receiver.Address(), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{
+ Origin: sender.Address(),
+ State: statedb,
+ GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
+ GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
+ Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
+ EVMConfig: vm.Config{
+ Tracer: logger,
+ DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
+ },
+ })
}
vmdone := time.Since(tstart)
@@ -197,8 +189,8 @@ func run(ctx *cli.Context) error {
vm.StdErrFormat(logger.StructLogs())
if ctx.GlobalBool(SysStatFlag.Name) {
- var mem runtime.MemStats
- runtime.ReadMemStats(&mem)
+ var mem goruntime.MemStats
+ goruntime.ReadMemStats(&mem)
fmt.Printf("vm took %v\n", vmdone)
fmt.Printf(`alloc: %d
tot alloc: %d
@@ -223,87 +215,3 @@ func main() {
os.Exit(1)
}
}
-
-type VMEnv struct {
- state *state.StateDB
- block *types.Block
-
- transactor *common.Address
- value *big.Int
-
- depth int
- Gas *big.Int
- time *big.Int
- logs []vm.StructLog
-
- evm *vm.EVM
-}
-
-func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg vm.Config) *VMEnv {
- env := &VMEnv{
- state: state,
- transactor: &transactor,
- value: value,
- time: big.NewInt(time.Now().Unix()),
- }
-
- env.evm = vm.New(env, cfg)
- return env
-}
-
-// ruleSet implements vm.ChainConfig and will always default to the homestead rule set.
-type ruleSet struct{}
-
-func (ruleSet) IsHomestead(*big.Int) bool { return true }
-func (ruleSet) GasTable(*big.Int) params.GasTable {
- return params.GasTableHomesteadGasRepriceFork
-}
-
-func (self *VMEnv) ChainConfig() *params.ChainConfig { return params.TestChainConfig }
-func (self *VMEnv) Vm() vm.Vm { return self.evm }
-func (self *VMEnv) Db() vm.Database { return self.state }
-func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() }
-func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) }
-func (self *VMEnv) Origin() common.Address { return *self.transactor }
-func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
-func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
-func (self *VMEnv) Time() *big.Int { return self.time }
-func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
-func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
-func (self *VMEnv) Value() *big.Int { return self.value }
-func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
-func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
-func (self *VMEnv) Depth() int { return 0 }
-func (self *VMEnv) SetDepth(i int) { self.depth = i }
-func (self *VMEnv) GetHash(n uint64) common.Hash {
- if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
- return self.block.Hash()
- }
- return common.Hash{}
-}
-func (self *VMEnv) AddLog(log *vm.Log) {
- self.state.AddLog(log)
-}
-func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
- return self.state.GetBalance(from).Cmp(balance) >= 0
-}
-func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
- core.Transfer(from, to, amount)
-}
-
-func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- self.Gas = gas
- return core.Call(self, caller, addr, data, gas, price, value)
-}
-
-func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return core.CallCode(self, caller, addr, data, gas, price, value)
-}
-
-func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- return core.DelegateCall(self, caller, addr, data, gas, price)
-}
-
-func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- return core.Create(self, caller, data, gas, price, value)
-}
diff --git a/cmd/gubiq/accountcmd_test.go b/cmd/gubiq/accountcmd_test.go
index de5d35e462..5ac21e42fb 100644
--- a/cmd/gubiq/accountcmd_test.go
+++ b/cmd/gubiq/accountcmd_test.go
@@ -148,7 +148,7 @@ Passphrase: {{.InputLine "foobar"}}
"Unlocked account f466859ead1932d743d622cb74fc058882e8648a",
}
for _, m := range wantMessages {
- if strings.Index(gubiq.stderrText(), m) == -1 {
+ if !strings.Contains(gubiq.stderrText(), m) {
t.Errorf("stderr text does not contain %q", m)
}
}
@@ -193,7 +193,7 @@ Passphrase: {{.InputLine "foobar"}}
"Unlocked account 289d485d9771714cce91d3393d764e1311907acc",
}
for _, m := range wantMessages {
- if strings.Index(gubiq.stderrText(), m) == -1 {
+ if !strings.Contains(gubiq.stderrText(), m) {
t.Errorf("stderr text does not contain %q", m)
}
}
@@ -212,7 +212,7 @@ func TestUnlockFlagPasswordFile(t *testing.T) {
"Unlocked account 289d485d9771714cce91d3393d764e1311907acc",
}
for _, m := range wantMessages {
- if strings.Index(gubiq.stderrText(), m) == -1 {
+ if !strings.Contains(gubiq.stderrText(), m) {
t.Errorf("stderr text does not contain %q", m)
}
}
@@ -260,7 +260,7 @@ In order to avoid this warning, you need to remove the following duplicate key f
"Unlocked account f466859ead1932d743d622cb74fc058882e8648a",
}
for _, m := range wantMessages {
- if strings.Index(gubiq.stderrText(), m) == -1 {
+ if !strings.Contains(gubiq.stderrText(), m) {
t.Errorf("stderr text does not contain %q", m)
}
}
diff --git a/cmd/gubiq/library.c b/cmd/gubiq/library.c
deleted file mode 100644
index f738621a82..0000000000
--- a/cmd/gubiq/library.c
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
-
-// Simple wrapper to translate the API exposed methods and types to inthernal
-// Go versions of the same types.
-
-#include "_cgo_export.h"
-
-int run(const char* args) {
- return doRun((char*)args);
-}
diff --git a/cmd/gubiq/library.go b/cmd/gubiq/library.go
deleted file mode 100644
index c7a8a44d31..0000000000
--- a/cmd/gubiq/library.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
-
-// Contains a simple library definition to allow creating a Gubiq instance from
-// straight C code.
-
-package main
-
-// #ifdef __cplusplus
-// extern "C" {
-// #endif
-//
-// extern int run(const char*);
-//
-// #ifdef __cplusplus
-// }
-// #endif
-import "C"
-import (
- "fmt"
- "os"
- "strings"
-)
-
-//export doRun
-func doRun(args *C.char) C.int {
- // This is equivalent to gubiq.main, just modified to handle the function arg passing
- if err := app.Run(strings.Split("gubiq "+C.GoString(args), " ")); err != nil {
- fmt.Fprintln(os.Stderr, err)
- return -1
- }
- return 0
-}
diff --git a/cmd/gubiq/library_android.go b/cmd/gubiq/library_android.go
deleted file mode 100644
index 17a0285d6c..0000000000
--- a/cmd/gubiq/library_android.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
-
-// Contains specialized code for running Gubiq on Android.
-
-package main
-
-// #include
-// #cgo LDFLAGS: -llog
-import "C"
-import (
- "bufio"
- "os"
-)
-
-func init() {
- // Redirect the standard output and error to logcat
- oldStdout, oldStderr := os.Stdout, os.Stderr
-
- outRead, outWrite, _ := os.Pipe()
- errRead, errWrite, _ := os.Pipe()
-
- os.Stdout = outWrite
- os.Stderr = errWrite
-
- go func() {
- scanner := bufio.NewScanner(outRead)
- for scanner.Scan() {
- line := scanner.Text()
- C.__android_log_write(C.ANDROID_LOG_INFO, C.CString("Stdout"), C.CString(line))
- oldStdout.WriteString(line + "\n")
- }
- }()
-
- go func() {
- scanner := bufio.NewScanner(errRead)
- for scanner.Scan() {
- line := scanner.Text()
- C.__android_log_write(C.ANDROID_LOG_INFO, C.CString("Stderr"), C.CString(line))
- oldStderr.WriteString(line + "\n")
- }
- }()
-}
diff --git a/cmd/gubiq/main.go b/cmd/gubiq/main.go
index 6394066ec2..0d9a91cd4f 100644
--- a/cmd/gubiq/main.go
+++ b/cmd/gubiq/main.go
@@ -88,7 +88,6 @@ func init() {
utils.BootnodesFlag,
utils.DataDirFlag,
utils.KeyStoreDirFlag,
- utils.OlympicFlag,
utils.FastSyncFlag,
utils.LightModeFlag,
utils.LightServFlag,
@@ -168,7 +167,6 @@ func init() {
}
app.After = func(ctx *cli.Context) error {
- logger.Flush()
debug.Exit()
console.Stdin.Close() // Resets terminal mode.
return nil
diff --git a/cmd/gubiq/monitorcmd.go b/cmd/gubiq/monitorcmd.go
index 0f65222c4c..6a9508063c 100644
--- a/cmd/gubiq/monitorcmd.go
+++ b/cmd/gubiq/monitorcmd.go
@@ -236,8 +236,9 @@ func expandMetrics(metrics map[string]interface{}, path string) []string {
// fetchMetric iterates over the metrics map and retrieves a specific one.
func fetchMetric(metrics map[string]interface{}, metric string) float64 {
- parts, found := strings.Split(metric, "/"), true
+ parts := strings.Split(metric, "/")
for _, part := range parts[:len(parts)-1] {
+ var found bool
metrics, found = metrics[part].(map[string]interface{})
if !found {
return 0
diff --git a/cmd/gubiq/usage.go b/cmd/gubiq/usage.go
index c4aaa8bcf9..4b1d5f746e 100644
--- a/cmd/gubiq/usage.go
+++ b/cmd/gubiq/usage.go
@@ -67,7 +67,6 @@ var AppHelpFlagGroups = []flagGroup{
utils.DataDirFlag,
utils.KeyStoreDirFlag,
utils.NetworkIdFlag,
- utils.OlympicFlag,
utils.TestNetFlag,
utils.DevModeFlag,
utils.IdentityFlag,
diff --git a/cmd/bzzhash/main.go b/cmd/swarm/hash.go
similarity index 80%
rename from cmd/bzzhash/main.go
rename to cmd/swarm/hash.go
index 8ebe059470..e1062300ab 100644
--- a/cmd/bzzhash/main.go
+++ b/cmd/swarm/hash.go
@@ -19,22 +19,21 @@ package main
import (
"fmt"
+ "log"
"os"
- "runtime"
"github.com/ubiq/go-ubiq/swarm/storage"
+ "gopkg.in/urfave/cli.v1"
)
-func main() {
- runtime.GOMAXPROCS(runtime.NumCPU())
-
- if len(os.Args) < 2 {
- fmt.Println("Usage: bzzhash ")
- os.Exit(0)
+func hash(ctx *cli.Context) {
+ args := ctx.Args()
+ if len(args) < 1 {
+ log.Fatal("Usage: swarm hash ")
}
- f, err := os.Open(os.Args[1])
+ f, err := os.Open(args[0])
if err != nil {
- fmt.Println("Error opening file " + os.Args[1])
+ fmt.Println("Error opening file " + args[1])
os.Exit(1)
}
@@ -42,7 +41,7 @@ func main() {
chunker := storage.NewTreeChunker(storage.NewChunkerParams())
key, err := chunker.Split(f, stat.Size(), nil, nil, nil)
if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
+ log.Fatalf("%v\n", err)
} else {
fmt.Printf("%v\n", key)
}
diff --git a/cmd/bzzd/main.go b/cmd/swarm/main.go
similarity index 68%
rename from cmd/bzzd/main.go
rename to cmd/swarm/main.go
index e63dad373e..8041605057 100644
--- a/cmd/bzzd/main.go
+++ b/cmd/swarm/main.go
@@ -43,11 +43,15 @@ import (
"gopkg.in/urfave/cli.v1"
)
-const clientIdentifier = "bzzd"
+const (
+ clientIdentifier = "swarm"
+ versionString = "0.2"
+)
var (
- gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
- app = utils.NewApp(gitCommit, "Ubiq Swarm server daemon")
+ gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
+ app = utils.NewApp(gitCommit, "Ubiq Swarm")
+ testbetBootNodes = []string{}
)
var (
@@ -65,30 +69,49 @@ var (
}
SwarmNetworkIdFlag = cli.IntFlag{
Name: "bzznetworkid",
- Usage: "Network identifier (integer, default 322=swarm testnet)",
+ Usage: "Network identifier (integer, default 3=swarm testnet)",
Value: network.NetworkId,
}
SwarmConfigPathFlag = cli.StringFlag{
Name: "bzzconfig",
Usage: "Swarm config file path (datadir/bzz)",
}
- SwarmSwapEnabled = cli.BoolFlag{
+ SwarmSwapEnabledFlag = cli.BoolFlag{
Name: "swap",
Usage: "Swarm SWAP enabled (default false)",
}
- SwarmSyncEnabled = cli.BoolTFlag{
+ SwarmSyncEnabledFlag = cli.BoolTFlag{
Name: "sync",
Usage: "Swarm Syncing enabled (default true)",
}
- EthAPI = cli.StringFlag{
+ EthAPIFlag = cli.StringFlag{
Name: "ethapi",
Usage: "URL of the Ethereum API provider",
Value: node.DefaultIPCEndpoint("gubiq"),
}
+ SwarmApiFlag = cli.StringFlag{
+ Name: "bzzapi",
+ Usage: "Swarm HTTP endpoint",
+ Value: "http://127.0.0.1:8500",
+ }
+ SwarmRecursiveUploadFlag = cli.BoolFlag{
+ Name: "recursive",
+ Usage: "Upload directories recursively",
+ }
+ SwarmWantManifestFlag = cli.BoolTFlag{
+ Name: "manifest",
+ Usage: "Automatic manifest upload",
+ }
+ SwarmUploadDefaultPath = cli.StringFlag{
+ Name: "defaultpath",
+ Usage: "path to file served for empty url path (none)",
+ }
+ CorsStringFlag = cli.StringFlag{
+ Name: "corsdomain",
+ Usage: "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')",
+ }
)
-var defaultBootnodes = []string{}
-
func init() {
// Override flag defaults so bzzd can run alongside gubiq.
utils.ListenPortFlag.Value = 30399
@@ -96,8 +119,39 @@ func init() {
utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, web3"
// Set up the cli app.
- app.Commands = nil
app.Action = bzzd
+ app.HideVersion = true // we have a command to print the version
+ app.Copyright = "Copyright 2013-2016 The go-ethereum Authors"
+ app.Commands = []cli.Command{
+ {
+ Action: version,
+ Name: "version",
+ Usage: "Print version numbers",
+ ArgsUsage: " ",
+ Description: `
+The output of this command is supposed to be machine-readable.
+`,
+ },
+ {
+ Action: upload,
+ Name: "up",
+ Usage: "upload a file or directory to swarm using the HTTP API",
+ ArgsUsage: " ",
+ Description: `
+"upload a file or directory to swarm using the HTTP API and prints the root hash",
+`,
+ },
+ {
+ Action: hash,
+ Name: "hash",
+ Usage: "print the swarm hash of a file or directory",
+ ArgsUsage: " ",
+ Description: `
+Prints the swarm hash of file or directory.
+`,
+ },
+ }
+
app.Flags = []cli.Flag{
utils.IdentityFlag,
utils.DataDirFlag,
@@ -115,14 +169,20 @@ func init() {
utils.IPCApiFlag,
utils.IPCPathFlag,
// bzzd-specific flags
- EthAPI,
+ CorsStringFlag,
+ EthAPIFlag,
SwarmConfigPathFlag,
- SwarmSwapEnabled,
- SwarmSyncEnabled,
+ SwarmSwapEnabledFlag,
+ SwarmSyncEnabledFlag,
SwarmPortFlag,
SwarmAccountFlag,
SwarmNetworkIdFlag,
ChequebookAddrFlag,
+ // upload flags
+ SwarmApiFlag,
+ SwarmRecursiveUploadFlag,
+ SwarmWantManifestFlag,
+ SwarmUploadDefaultPath,
}
app.Flags = append(app.Flags, debug.Flags...)
app.Before = func(ctx *cli.Context) error {
@@ -142,17 +202,33 @@ func main() {
}
}
+func version(ctx *cli.Context) error {
+ fmt.Println(strings.Title(clientIdentifier))
+ fmt.Println("Version:", versionString)
+ if gitCommit != "" {
+ fmt.Println("Git Commit:", gitCommit)
+ }
+ fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name))
+ fmt.Println("Go Version:", runtime.Version())
+ fmt.Println("OS:", runtime.GOOS)
+ fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
+ fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
+ return nil
+}
+
func bzzd(ctx *cli.Context) error {
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
registerBzzService(ctx, stack)
utils.StartNode(stack)
-
+ networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
// Add bootnodes as initial peers.
if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",")
injectBootnodes(stack.Server(), bootnodes)
} else {
- injectBootnodes(stack.Server(), defaultBootnodes)
+ if networkId == 3 {
+ injectBootnodes(stack.Server(), testbetBootNodes)
+ }
}
stack.Wait()
@@ -175,22 +251,21 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) {
if len(bzzport) > 0 {
bzzconfig.Port = bzzport
}
- swapEnabled := ctx.GlobalBool(SwarmSwapEnabled.Name)
- syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabled.Name)
+ swapEnabled := ctx.GlobalBool(SwarmSwapEnabledFlag.Name)
+ syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabledFlag.Name)
- ethapi := ctx.GlobalString(EthAPI.Name)
+ ethapi := ctx.GlobalString(EthAPIFlag.Name)
+ cors := ctx.GlobalString(CorsStringFlag.Name)
boot := func(ctx *node.ServiceContext) (node.Service, error) {
var client *ethclient.Client
- if ethapi == "" {
- err = fmt.Errorf("use ethapi flag to connect to a an eth client and talk to the blockchain")
- } else {
+ if len(ethapi) > 0 {
client, err = ethclient.Dial(ethapi)
+ if err != nil {
+ utils.Fatalf("Can't connect: %v", err)
+ }
}
- if err != nil {
- utils.Fatalf("Can't connect: %v", err)
- }
- return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled)
+ return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled, cors)
}
if err := stack.Register(boot); err != nil {
utils.Fatalf("Failed to register the Swarm service: %v", err)
diff --git a/cmd/swarm/upload.go b/cmd/swarm/upload.go
new file mode 100644
index 0000000000..d8039d45b5
--- /dev/null
+++ b/cmd/swarm/upload.go
@@ -0,0 +1,231 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+// Command bzzup uploads files to the swarm HTTP API.
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "mime"
+ "net/http"
+ "os"
+ "os/user"
+ "path"
+ "path/filepath"
+ "strings"
+
+ "gopkg.in/urfave/cli.v1"
+)
+
+func upload(ctx *cli.Context) {
+ args := ctx.Args()
+ var (
+ bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
+ recursive = ctx.GlobalBool(SwarmRecursiveUploadFlag.Name)
+ wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
+ defaultPath = ctx.GlobalString(SwarmUploadDefaultPath.Name)
+ )
+ if len(args) != 1 {
+ log.Fatal("need filename as the first and only argument")
+ }
+
+ var (
+ file = args[0]
+ client = &client{api: bzzapi}
+ )
+ fi, err := os.Stat(expandPath(file))
+ if err != nil {
+ log.Fatal(err)
+ }
+ if fi.IsDir() {
+ if !recursive {
+ log.Fatal("argument is a directory and recursive upload is disabled")
+ }
+ if !wantManifest {
+ log.Fatal("manifest is required for directory uploads")
+ }
+ mhash, err := client.uploadDirectory(file, defaultPath)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(mhash)
+ return
+ }
+ entry, err := client.uploadFile(file, fi)
+ if err != nil {
+ log.Fatalln("upload failed:", err)
+ }
+ mroot := manifest{[]manifestEntry{entry}}
+ if !wantManifest {
+ // Print the manifest. This is the only output to stdout.
+ mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
+ fmt.Println(string(mrootJSON))
+ return
+ }
+ hash, err := client.uploadManifest(mroot)
+ if err != nil {
+ log.Fatalln("manifest upload failed:", err)
+ }
+ fmt.Println(hash)
+}
+
+// Expands a file path
+// 1. replace tilde with users home dir
+// 2. expands embedded environment variables
+// 3. cleans the path, e.g. /a/b/../c -> /a/c
+// Note, it has limitations, e.g. ~someuser/tmp will not be expanded
+func expandPath(p string) string {
+ if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
+ if home := homeDir(); home != "" {
+ p = home + p[1:]
+ }
+ }
+ return path.Clean(os.ExpandEnv(p))
+}
+
+func homeDir() string {
+ if home := os.Getenv("HOME"); home != "" {
+ return home
+ }
+ if usr, err := user.Current(); err == nil {
+ return usr.HomeDir
+ }
+ return ""
+}
+
+// client wraps interaction with the swarm HTTP gateway.
+type client struct {
+ api string
+}
+
+// manifest is the JSON representation of a swarm manifest.
+type manifestEntry struct {
+ Hash string `json:"hash,omitempty"`
+ ContentType string `json:"contentType,omitempty"`
+ Path string `json:"path,omitempty"`
+}
+
+// manifest is the JSON representation of a swarm manifest.
+type manifest struct {
+ Entries []manifestEntry `json:"entries,omitempty"`
+}
+
+func (c *client) uploadDirectory(dir string, defaultPath string) (string, error) {
+ mhash, err := c.postRaw("application/json", 2, ioutil.NopCloser(bytes.NewReader([]byte("{}"))))
+ if err != nil {
+ return "", fmt.Errorf("failed to upload empty manifest")
+ }
+ if len(defaultPath) > 0 {
+ fi, err := os.Stat(defaultPath)
+ if err != nil {
+ return "", err
+ }
+ mhash, err = c.uploadToManifest(mhash, "", defaultPath, fi)
+ if err != nil {
+ return "", err
+ }
+ }
+ prefix := filepath.ToSlash(filepath.Clean(dir)) + "/"
+ err = filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
+ if err != nil || fi.IsDir() {
+ return err
+ }
+ if !strings.HasPrefix(path, dir) {
+ return fmt.Errorf("path %s outside directory %s", path, dir)
+ }
+ uripath := strings.TrimPrefix(filepath.ToSlash(filepath.Clean(path)), prefix)
+ mhash, err = c.uploadToManifest(mhash, uripath, path, fi)
+ return err
+ })
+ return mhash, err
+}
+
+func (c *client) uploadFile(file string, fi os.FileInfo) (manifestEntry, error) {
+ hash, err := c.uploadFileContent(file, fi)
+ m := manifestEntry{
+ Hash: hash,
+ ContentType: mime.TypeByExtension(filepath.Ext(fi.Name())),
+ }
+ return m, err
+}
+
+func (c *client) uploadFileContent(file string, fi os.FileInfo) (string, error) {
+ fd, err := os.Open(file)
+ if err != nil {
+ return "", err
+ }
+ defer fd.Close()
+ log.Printf("uploading file %s (%d bytes)", file, fi.Size())
+ return c.postRaw("application/octet-stream", fi.Size(), fd)
+}
+
+func (c *client) uploadManifest(m manifest) (string, error) {
+ jsm, err := json.Marshal(m)
+ if err != nil {
+ panic(err)
+ }
+ log.Println("uploading manifest")
+ return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm)))
+}
+
+func (c *client) uploadToManifest(mhash string, path string, fpath string, fi os.FileInfo) (string, error) {
+ fd, err := os.Open(fpath)
+ if err != nil {
+ return "", err
+ }
+ defer fd.Close()
+ log.Printf("uploading file %s (%d bytes) and adding path %v", fpath, fi.Size(), path)
+ req, err := http.NewRequest("PUT", c.api+"/bzz:/"+mhash+"/"+path, fd)
+ if err != nil {
+ return "", err
+ }
+ req.Header.Set("content-type", mime.TypeByExtension(filepath.Ext(fi.Name())))
+ req.ContentLength = fi.Size()
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode >= 400 {
+ return "", fmt.Errorf("bad status: %s", resp.Status)
+ }
+ content, err := ioutil.ReadAll(resp.Body)
+ return string(content), err
+}
+
+func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (string, error) {
+ req, err := http.NewRequest("POST", c.api+"/bzzr:/", body)
+ if err != nil {
+ return "", err
+ }
+ req.Header.Set("content-type", mimetype)
+ req.ContentLength = size
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode >= 400 {
+ return "", fmt.Errorf("bad status: %s", resp.Status)
+ }
+ content, err := ioutil.ReadAll(resp.Body)
+ return string(content), err
+}
diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go
index 8c4a9ec4ec..6e138e7d8a 100644
--- a/cmd/utils/cmd.go
+++ b/cmd/utils/cmd.go
@@ -18,12 +18,14 @@
package utils
import (
+ "compress/gzip"
"fmt"
"io"
"os"
"os/signal"
"regexp"
"runtime"
+ "strings"
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core"
@@ -65,7 +67,6 @@ func Fatalf(format string, args ...interface{}) {
}
}
fmt.Fprintf(w, "Fatal: "+format+"\n", args...)
- logger.Flush()
os.Exit(1)
}
@@ -93,7 +94,7 @@ func StartNode(stack *node.Node) {
func FormatTransactionData(data string) []byte {
d := common.StringToByteFunc(data, func(s string) (ret []byte) {
- slice := regexp.MustCompile("\\n|\\s").Split(s, 1000000000)
+ slice := regexp.MustCompile(`\n|\s`).Split(s, 1000000000)
for _, dataItem := range slice {
d := common.FormatData(dataItem)
ret = append(ret, d...)
@@ -133,7 +134,15 @@ func ImportChain(chain *core.BlockChain, fn string) error {
return err
}
defer fh.Close()
- stream := rlp.NewStream(fh, 0)
+
+ var reader io.Reader = fh
+ if strings.HasSuffix(fn, ".gz") {
+ if reader, err = gzip.NewReader(reader); err != nil {
+ return err
+ }
+ }
+
+ stream := rlp.NewStream(reader, 0)
// Run actual the import.
blocks := make(types.Blocks, importBatchSize)
@@ -195,10 +204,18 @@ func ExportChain(blockchain *core.BlockChain, fn string) error {
return err
}
defer fh.Close()
- if err := blockchain.Export(fh); err != nil {
+
+ var writer io.Writer = fh
+ if strings.HasSuffix(fn, ".gz") {
+ writer = gzip.NewWriter(writer)
+ defer writer.(*gzip.Writer).Close()
+ }
+
+ if err := blockchain.Export(writer); err != nil {
return err
}
glog.Infoln("Exported blockchain to ", fn)
+
return nil
}
@@ -210,7 +227,14 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
return err
}
defer fh.Close()
- if err := blockchain.ExportN(fh, first, last); err != nil {
+
+ var writer io.Writer = fh
+ if strings.HasSuffix(fn, ".gz") {
+ writer = gzip.NewWriter(writer)
+ defer writer.(*gzip.Writer).Close()
+ }
+
+ if err := blockchain.ExportN(writer, first, last); err != nil {
return err
}
glog.Infoln("Exported blockchain to ", fn)
diff --git a/cmd/utils/customflags.go b/cmd/utils/customflags.go
index 1c3ebc81ae..39a47031f5 100644
--- a/cmd/utils/customflags.go
+++ b/cmd/utils/customflags.go
@@ -23,8 +23,6 @@ import (
"os/user"
"path"
"strings"
-
- "gopkg.in/urfave/cli.v1"
)
// Custom type which is registered in the flags library which cli uses for
@@ -46,7 +44,6 @@ func (self *DirectoryString) Set(value string) error {
// Custom cli.Flag type which expand the received string to an absolute path.
// e.g. ~/.ubiq -> /home/username/.ubiq
type DirectoryFlag struct {
- cli.GenericFlag
Name string
Value DirectoryString
Usage string
@@ -54,15 +51,10 @@ type DirectoryFlag struct {
}
func (self DirectoryFlag) String() string {
- var fmtString string
- fmtString = "%s %v\t%v"
-
+ fmtString := "%s %v\t%v"
if len(self.Value.Value) > 0 {
fmtString = "%s \"%v\"\t%v"
- } else {
- fmtString = "%s %v\t%v"
}
-
return withEnvHint(self.EnvVar, fmt.Sprintf(fmtString, prefixedNames(self.Name), self.Value.Value, self.Usage))
}
@@ -122,7 +114,7 @@ func withEnvHint(envVar, str string) string {
return str + envText
}
-func (self DirectoryFlag) getName() string {
+func (self DirectoryFlag) GetName() string {
return self.Name
}
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 86c699de62..d3fc0fc88e 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -21,7 +21,6 @@ import (
"crypto/ecdsa"
"fmt"
"io/ioutil"
- "math"
"math/big"
"os"
"path/filepath"
@@ -116,13 +115,9 @@ var (
}
NetworkIdFlag = cli.IntFlag{
Name: "networkid",
- Usage: "Network identifier (integer, 0=Olympic (disused), 1=Frontier, 2=Morden (disused), 3=Ropsten)",
+ Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)",
Value: eth.NetworkId,
}
- OlympicFlag = cli.BoolFlag{
- Name: "olympic",
- Usage: "Olympic network: pre-configured pre-release test network",
- }
TestNetFlag = cli.BoolFlag{
Name: "testnet",
Usage: "Ropsten network: pre-configured test network",
@@ -485,17 +480,15 @@ func makeNodeUserIdent(ctx *cli.Context) string {
// MakeBootstrapNodes creates a list of bootstrap nodes from the command line
// flags, reverting to pre-configured ones if none have been specified.
func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node {
- // Return pre-configured nodes if none were manually requested
- if !ctx.GlobalIsSet(BootnodesFlag.Name) {
- if ctx.GlobalBool(TestNetFlag.Name) {
- return params.TestnetBootnodes
- }
- return params.MainnetBootnodes
+ urls := params.MainnetBootnodes
+ if ctx.GlobalIsSet(BootnodesFlag.Name) {
+ urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
+ } else if ctx.GlobalBool(TestNetFlag.Name) {
+ urls = params.TestnetBootnodes
}
- // Otherwise parse and use the CLI bootstrap nodes
- bootnodes := []*discover.Node{}
- for _, url := range strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") {
+ bootnodes := make([]*discover.Node, 0, len(urls))
+ for _, url := range urls {
node, err := discover.ParseNode(url)
if err != nil {
glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err)
@@ -509,14 +502,13 @@ func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node {
// MakeBootstrapNodesV5 creates a list of bootstrap nodes from the command line
// flags, reverting to pre-configured ones if none have been specified.
func MakeBootstrapNodesV5(ctx *cli.Context) []*discv5.Node {
- // Return pre-configured nodes if none were manually requested
- if !ctx.GlobalIsSet(BootnodesFlag.Name) {
- return params.DiscoveryV5Bootnodes
+ urls := params.DiscoveryV5Bootnodes
+ if ctx.GlobalIsSet(BootnodesFlag.Name) {
+ urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
}
- // Otherwise parse and use the CLI bootstrap nodes
- bootnodes := []*discv5.Node{}
- for _, url := range strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") {
+ bootnodes := make([]*discv5.Node, 0, len(urls))
+ for _, url := range urls {
node, err := discv5.ParseNode(url)
if err != nil {
glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err)
@@ -715,7 +707,7 @@ func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node {
// given node.
func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
// Avoid conflicting network flags
- networks, netFlags := 0, []cli.BoolFlag{DevModeFlag, TestNetFlag, OlympicFlag}
+ networks, netFlags := 0, []cli.BoolFlag{DevModeFlag, TestNetFlag}
for _, flag := range netFlags {
if ctx.GlobalBool(flag.Name) {
networks++
@@ -753,12 +745,6 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
// Override any default configs in dev mode or the test net
switch {
- case ctx.GlobalBool(OlympicFlag.Name):
- if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
- ethConf.NetworkId = 88
- }
- ethConf.Genesis = core.OlympicGenesisBlock()
-
case ctx.GlobalBool(TestNetFlag.Name):
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
ethConf.NetworkId = 3
@@ -766,7 +752,7 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
ethConf.Genesis = core.DefaultTestnetGenesisBlock()
case ctx.GlobalBool(DevModeFlag.Name):
- ethConf.Genesis = core.OlympicGenesisBlock()
+ ethConf.Genesis = core.DevGenesisBlock()
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
ethConf.GasPrice = new(big.Int)
}
@@ -823,16 +809,6 @@ func RegisterEthStatsService(stack *node.Node, url string) {
// SetupNetwork configures the system for either the main net or some test network.
func SetupNetwork(ctx *cli.Context) {
- switch {
- case ctx.GlobalBool(OlympicFlag.Name):
- params.DurationLimit = big.NewInt(8)
- params.GenesisGasLimit = big.NewInt(3141592)
- params.MinGasLimit = big.NewInt(125000)
- params.MaximumExtraDataSize = big.NewInt(1024)
- NetworkIdFlag.Value = 0
- core.BlockReward = big.NewInt(1.5e+18)
- core.ExpDiffPeriod = big.NewInt(math.MaxInt64)
- }
params.TargetGasLimit = common.String2Big(ctx.GlobalString(TargetGasLimitFlag.Name))
}
@@ -920,12 +896,13 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
var err error
chainDb = MakeChainDatabase(ctx, stack)
- if ctx.GlobalBool(OlympicFlag.Name) {
+ if ctx.GlobalBool(TestNetFlag.Name) {
_, err := core.WriteTestNetGenesisBlock(chainDb)
if err != nil {
glog.Fatalln(err)
}
}
+
chainConfig := MakeChainConfigFromDb(ctx, chainDb)
pow := pow.PoW(core.FakePow{})
diff --git a/common/big_test.go b/common/big_test.go
index 1eb0c0c1fd..4d04a8db36 100644
--- a/common/big_test.go
+++ b/common/big_test.go
@@ -27,7 +27,7 @@ func TestMisc(t *testing.T) {
c := []byte{1, 2, 3, 4}
z := BitTest(a, 1)
- if z != true {
+ if !z {
t.Error("Expected true got", z)
}
@@ -79,11 +79,11 @@ func TestBigCopy(t *testing.T) {
z := BigToBytes(c, 16)
zbytes := []byte{232, 212, 165, 16, 0}
- if bytes.Compare(y, ybytes) != 0 {
+ if !bytes.Equal(y, ybytes) {
t.Error("Got", ybytes)
}
- if bytes.Compare(z, zbytes) != 0 {
+ if !bytes.Equal(z, zbytes) {
t.Error("Got", zbytes)
}
}
diff --git a/common/bytes.go b/common/bytes.go
index b9fb3b2da6..cbceea8b5c 100644
--- a/common/bytes.go
+++ b/common/bytes.go
@@ -143,7 +143,7 @@ func Hex2BytesFixed(str string, flen int) []byte {
return h
} else {
if len(h) > flen {
- return h[len(h)-flen : len(h)]
+ return h[len(h)-flen:]
} else {
hh := make([]byte, flen)
copy(hh[flen-len(h):flen], h[:])
diff --git a/common/bytes_test.go b/common/bytes_test.go
index 2e52084777..98d402c489 100644
--- a/common/bytes_test.go
+++ b/common/bytes_test.go
@@ -181,7 +181,7 @@ func TestFromHex(t *testing.T) {
input := "0x01"
expected := []byte{1}
result := FromHex(input)
- if bytes.Compare(expected, result) != 0 {
+ if !bytes.Equal(expected, result) {
t.Errorf("Expected % x got % x", expected, result)
}
}
@@ -190,7 +190,7 @@ func TestFromHexOddLength(t *testing.T) {
input := "0x1"
expected := []byte{1}
result := FromHex(input)
- if bytes.Compare(expected, result) != 0 {
+ if !bytes.Equal(expected, result) {
t.Errorf("Expected % x got % x", expected, result)
}
}
diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go
index ca6cffce20..3442250503 100644
--- a/common/compiler/solidity.go
+++ b/common/compiler/solidity.go
@@ -22,9 +22,7 @@ import (
"encoding/json"
"errors"
"fmt"
- "io"
"io/ioutil"
- "os"
"os/exec"
"regexp"
"strings"
@@ -34,7 +32,7 @@ import (
)
var (
- versionRegexp = regexp.MustCompile("[0-9]+\\.[0-9]+\\.[0-9]+")
+ versionRegexp = regexp.MustCompile(`[0-9]+\.[0-9]+\.[0-9]+`)
solcParams = []string{
"--combined-json", "bin,abi,userdoc,devdoc",
"--add-std", // include standard lib contracts
@@ -96,27 +94,16 @@ func CompileSolidityString(solc, source string) (map[string]*Contract, error) {
if solc == "" {
solc = "solc"
}
- // Write source to a temporary file. Compiling stdin used to be supported
- // but seems to produce an exception with solc 0.3.5.
- infile, err := ioutil.TempFile("", "gubiq-compile-solidity")
- if err != nil {
- return nil, err
- }
- defer os.Remove(infile.Name())
- if _, err := io.WriteString(infile, source); err != nil {
- return nil, err
- }
- if err := infile.Close(); err != nil {
- return nil, err
- }
-
- return CompileSolidity(solc, infile.Name())
+ args := append(solcParams, "--")
+ cmd := exec.Command(solc, append(args, "-")...)
+ cmd.Stdin = strings.NewReader(source)
+ return runsolc(cmd, source)
}
// CompileSolidity compiles all given Solidity source files.
func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, error) {
if len(sourcefiles) == 0 {
- return nil, errors.New("solc: no source ")
+ return nil, errors.New("solc: no source files")
}
source, err := slurpFiles(sourcefiles)
if err != nil {
@@ -125,10 +112,13 @@ func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract,
if solc == "" {
solc = "solc"
}
-
- var stderr, stdout bytes.Buffer
args := append(solcParams, "--")
cmd := exec.Command(solc, append(args, sourcefiles...)...)
+ return runsolc(cmd, source)
+}
+
+func runsolc(cmd *exec.Cmd, source string) (map[string]*Contract, error) {
+ var stderr, stdout bytes.Buffer
cmd.Stderr = &stderr
cmd.Stdout = &stdout
if err := cmd.Run(); err != nil {
diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go
index d4a8c3cf5a..ba124632f7 100644
--- a/common/compiler/solidity_test.go
+++ b/common/compiler/solidity_test.go
@@ -20,6 +20,7 @@ import (
"encoding/json"
"io/ioutil"
"os"
+ "os/exec"
"path"
"testing"
@@ -27,8 +28,7 @@ import (
)
const (
- supportedSolcVersion = "0.3.5"
- testSource = `
+ testSource = `
contract test {
/// @notice Will multiply ` + "`a`" + ` by 7.
function multiply(uint a) returns(uint d) {
@@ -36,23 +36,18 @@ contract test {
}
}
`
- testCode = "0x6060604052602a8060106000396000f3606060405260e060020a6000350463c6888fa18114601a575b005b6007600435026060908152602090f3"
testInfo = `{"source":"\ncontract test {\n /// @notice Will multiply ` + "`a`" + ` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","language":"Solidity","languageVersion":"0.1.1","compilerVersion":"0.1.1","compilerOptions":"--binary file --json-abi file --natspec-user file --natspec-dev file --add-std 1","abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}},"developerDoc":{"methods":{}}}`
)
-func skipUnsupported(t *testing.T) {
- sol, err := SolidityVersion("")
- if err != nil {
+func skipWithoutSolc(t *testing.T) {
+ if _, err := exec.LookPath("solc"); err != nil {
t.Skip(err)
- return
- }
- if sol.Version != supportedSolcVersion {
- t.Skipf("unsupported version of solc found (%v, expect %v)", sol.Version, supportedSolcVersion)
}
}
func TestCompiler(t *testing.T) {
- skipUnsupported(t)
+ skipWithoutSolc(t)
+
contracts, err := CompileSolidityString("", testSource)
if err != nil {
t.Fatalf("error compiling source. result %v: %v", contracts, err)
@@ -64,19 +59,20 @@ func TestCompiler(t *testing.T) {
if !ok {
t.Fatal("info for contract 'test' not present in result")
}
- if c.Code != testCode {
- t.Errorf("wrong code: expected\n%s, got\n%s", testCode, c.Code)
+ if c.Code == "" {
+ t.Error("empty code")
}
if c.Info.Source != testSource {
t.Error("wrong source")
}
- if c.Info.CompilerVersion != supportedSolcVersion {
- t.Errorf("wrong version: expected %q, got %q", supportedSolcVersion, c.Info.CompilerVersion)
+ if c.Info.CompilerVersion == "" {
+ t.Error("empty version")
}
}
func TestCompileError(t *testing.T) {
- skipUnsupported(t)
+ skipWithoutSolc(t)
+
contracts, err := CompileSolidityString("", testSource[4:])
if err == nil {
t.Errorf("error expected compiling source. got none. result %v", contracts)
diff --git a/common/format.go b/common/format.go
index 119637d2e4..fccc299620 100644
--- a/common/format.go
+++ b/common/format.go
@@ -27,7 +27,7 @@ import (
// the unnecessary precision off from the formatted textual representation.
type PrettyDuration time.Duration
-var prettyDurationRe = regexp.MustCompile("\\.[0-9]+")
+var prettyDurationRe = regexp.MustCompile(`\.[0-9]+`)
// String implements the Stringer interface, allowing pretty printing of duration
// values rounded to three decimals.
diff --git a/common/hexutil/json.go b/common/hexutil/json.go
index cbbadbed6a..c36d862b5e 100644
--- a/common/hexutil/json.go
+++ b/common/hexutil/json.go
@@ -237,7 +237,7 @@ func checkJSON(input []byte) (raw []byte, err error) {
return nil, errNonString
}
if len(input) == 2 {
- return nil, ErrEmptyString
+ return nil, nil // empty strings are allowed
}
if !bytesHave0xPrefix(input[1:]) {
return nil, ErrMissingPrefix
@@ -255,7 +255,7 @@ func checkNumberJSON(input []byte) (raw []byte, err error) {
}
input = input[1 : len(input)-1]
if len(input) == 0 {
- return nil, ErrEmptyString
+ return nil, nil // empty strings are allowed
}
if !bytesHave0xPrefix(input) {
return nil, ErrMissingPrefix
diff --git a/common/hexutil/json_test.go b/common/hexutil/json_test.go
index 2d2e2fc0fd..290bf9ca24 100644
--- a/common/hexutil/json_test.go
+++ b/common/hexutil/json_test.go
@@ -60,13 +60,13 @@ var unmarshalBytesTests = []unmarshalTest{
{input: "", wantErr: errNonString},
{input: "null", wantErr: errNonString},
{input: "10", wantErr: errNonString},
- {input: `""`, wantErr: ErrEmptyString},
{input: `"0"`, wantErr: ErrMissingPrefix},
{input: `"0x0"`, wantErr: ErrOddLength},
{input: `"0xxx"`, wantErr: ErrSyntax},
{input: `"0x01zz01"`, wantErr: ErrSyntax},
// valid encoding
+ {input: `""`, want: referenceBytes("")},
{input: `"0x"`, want: referenceBytes("")},
{input: `"0x02"`, want: referenceBytes("02")},
{input: `"0X02"`, want: referenceBytes("02")},
@@ -125,7 +125,6 @@ var unmarshalBigTests = []unmarshalTest{
{input: "", wantErr: errNonString},
{input: "null", wantErr: errNonString},
{input: "10", wantErr: errNonString},
- {input: `""`, wantErr: ErrEmptyString},
{input: `"0"`, wantErr: ErrMissingPrefix},
{input: `"0x"`, wantErr: ErrEmptyNumber},
{input: `"0x01"`, wantErr: ErrLeadingZero},
@@ -133,6 +132,7 @@ var unmarshalBigTests = []unmarshalTest{
{input: `"0x1zz01"`, wantErr: ErrSyntax},
// valid encoding
+ {input: `""`, want: big.NewInt(0)},
{input: `"0x0"`, want: big.NewInt(0)},
{input: `"0x2"`, want: big.NewInt(0x2)},
{input: `"0x2F2"`, want: big.NewInt(0x2f2)},
@@ -198,7 +198,6 @@ var unmarshalUint64Tests = []unmarshalTest{
{input: "", wantErr: errNonString},
{input: "null", wantErr: errNonString},
{input: "10", wantErr: errNonString},
- {input: `""`, wantErr: ErrEmptyString},
{input: `"0"`, wantErr: ErrMissingPrefix},
{input: `"0x"`, wantErr: ErrEmptyNumber},
{input: `"0x01"`, wantErr: ErrLeadingZero},
@@ -207,6 +206,7 @@ var unmarshalUint64Tests = []unmarshalTest{
{input: `"0x1zz01"`, wantErr: ErrSyntax},
// valid encoding
+ {input: `""`, want: uint64(0)},
{input: `"0x0"`, want: uint64(0)},
{input: `"0x2"`, want: uint64(0x2)},
{input: `"0x2F2"`, want: uint64(0x2f2)},
diff --git a/common/math/dist_test.go b/common/math/dist_test.go
index 826faea8b3..f5857b6f80 100644
--- a/common/math/dist_test.go
+++ b/common/math/dist_test.go
@@ -41,24 +41,24 @@ func TestSum(t *testing.T) {
func TestDist(t *testing.T) {
var vectors = []Vector{
- Vector{big.NewInt(1000), big.NewInt(1234)},
- Vector{big.NewInt(500), big.NewInt(10023)},
- Vector{big.NewInt(1034), big.NewInt(1987)},
- Vector{big.NewInt(1034), big.NewInt(1987)},
- Vector{big.NewInt(8983), big.NewInt(1977)},
- Vector{big.NewInt(98382), big.NewInt(1887)},
- Vector{big.NewInt(12398), big.NewInt(1287)},
- Vector{big.NewInt(12398), big.NewInt(1487)},
- Vector{big.NewInt(12398), big.NewInt(1987)},
- Vector{big.NewInt(12398), big.NewInt(128)},
- Vector{big.NewInt(12398), big.NewInt(1987)},
- Vector{big.NewInt(1398), big.NewInt(187)},
- Vector{big.NewInt(12328), big.NewInt(1927)},
- Vector{big.NewInt(12398), big.NewInt(1987)},
- Vector{big.NewInt(22398), big.NewInt(1287)},
- Vector{big.NewInt(1370), big.NewInt(1981)},
- Vector{big.NewInt(12398), big.NewInt(1957)},
- Vector{big.NewInt(42198), big.NewInt(1987)},
+ {big.NewInt(1000), big.NewInt(1234)},
+ {big.NewInt(500), big.NewInt(10023)},
+ {big.NewInt(1034), big.NewInt(1987)},
+ {big.NewInt(1034), big.NewInt(1987)},
+ {big.NewInt(8983), big.NewInt(1977)},
+ {big.NewInt(98382), big.NewInt(1887)},
+ {big.NewInt(12398), big.NewInt(1287)},
+ {big.NewInt(12398), big.NewInt(1487)},
+ {big.NewInt(12398), big.NewInt(1987)},
+ {big.NewInt(12398), big.NewInt(128)},
+ {big.NewInt(12398), big.NewInt(1987)},
+ {big.NewInt(1398), big.NewInt(187)},
+ {big.NewInt(12328), big.NewInt(1927)},
+ {big.NewInt(12398), big.NewInt(1987)},
+ {big.NewInt(22398), big.NewInt(1287)},
+ {big.NewInt(1370), big.NewInt(1981)},
+ {big.NewInt(12398), big.NewInt(1957)},
+ {big.NewInt(42198), big.NewInt(1987)},
}
VectorsBy(GasSort).Sort(vectors)
diff --git a/compression/rle/read_write.go b/compression/rle/read_write.go
index 88ed7b05fc..b9bbe0a170 100644
--- a/compression/rle/read_write.go
+++ b/compression/rle/read_write.go
@@ -76,9 +76,9 @@ func compressChunk(dat []byte) (ret []byte, n int) {
}
return []byte{token, byte(j + 2)}, j
case len(dat) >= 32:
- if dat[0] == empty[0] && bytes.Compare(dat[:32], empty) == 0 {
+ if dat[0] == empty[0] && bytes.Equal(dat[:32], empty) {
return []byte{token, emptyShaToken}, 32
- } else if dat[0] == emptyList[0] && bytes.Compare(dat[:32], emptyList) == 0 {
+ } else if dat[0] == emptyList[0] && bytes.Equal(dat[:32], emptyList) {
return []byte{token, emptyListShaToken}, 32
}
fallthrough
diff --git a/console/bridge.go b/console/bridge.go
index 0e3d67effe..64c8d27a66 100644
--- a/console/bridge.go
+++ b/console/bridge.go
@@ -46,7 +46,7 @@ func newBridge(client *rpc.Client, prompter UserPrompter, printer io.Writer) *br
}
// NewAccount is a wrapper around the personal.newAccount RPC method that uses a
-// non-echoing password prompt to aquire the passphrase and executes the original
+// non-echoing password prompt to acquire the passphrase and executes the original
// RPC method (saved in jeth.newAccount) with it to actually execute the RPC call.
func (b *bridge) NewAccount(call otto.FunctionCall) (response otto.Value) {
var (
@@ -75,7 +75,7 @@ func (b *bridge) NewAccount(call otto.FunctionCall) (response otto.Value) {
default:
throwJSException("expected 0 or 1 string argument")
}
- // Password aquired, execute the call and return
+ // Password acquired, execute the call and return
ret, err := call.Otto.Call("jeth.newAccount", nil, password)
if err != nil {
throwJSException(err.Error())
@@ -84,7 +84,7 @@ func (b *bridge) NewAccount(call otto.FunctionCall) (response otto.Value) {
}
// UnlockAccount is a wrapper around the personal.unlockAccount RPC method that
-// uses a non-echoing password prompt to aquire the passphrase and executes the
+// uses a non-echoing password prompt to acquire the passphrase and executes the
// original RPC method (saved in jeth.unlockAccount) with it to actually execute
// the RPC call.
func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) {
@@ -127,7 +127,7 @@ func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) {
}
// Sign is a wrapper around the personal.sign RPC method that uses a non-echoing password
-// prompt to aquire the passphrase and executes the original RPC method (saved in
+// prompt to acquire the passphrase and executes the original RPC method (saved in
// jeth.sign) with it to actually execute the RPC call.
func (b *bridge) Sign(call otto.FunctionCall) (response otto.Value) {
var (
@@ -270,18 +270,15 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
} else {
resultVal, err := JSON.Call("parse", string(result))
if err != nil {
- resp = newErrorResponse(call, -32603, err.Error(), &req.Id).Object()
+ setError(resp, -32603, err.Error())
} else {
resp.Set("result", resultVal)
}
}
case rpc.Error:
- resp.Set("error", map[string]interface{}{
- "code": err.ErrorCode(),
- "message": err.Error(),
- })
+ setError(resp, err.ErrorCode(), err.Error())
default:
- resp = newErrorResponse(call, -32603, err.Error(), &req.Id).Object()
+ setError(resp, -32603, err.Error())
}
resps.Call("push", resp)
}
@@ -300,12 +297,8 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
return response
}
-func newErrorResponse(call otto.FunctionCall, code int, msg string, id interface{}) otto.Value {
- // Bundle the error into a JSON RPC call response
- m := map[string]interface{}{"version": "2.0", "id": id, "error": map[string]interface{}{"code": code, msg: msg}}
- res, _ := json.Marshal(m)
- val, _ := call.Otto.Run("(" + string(res) + ")")
- return val
+func setError(resp *otto.Object, code int, msg string) {
+ resp.Set("error", map[string]interface{}{"code": code, "message": msg})
}
// throwJSException panics on an otto.Value. The Otto VM will recover from the
diff --git a/console/console.go b/console/console.go
index 7b649a4bb3..0952f1d344 100644
--- a/console/console.go
+++ b/console/console.go
@@ -36,9 +36,9 @@ import (
)
var (
- passwordRegexp = regexp.MustCompile("personal.[nus]")
- onlyWhitespace = regexp.MustCompile("^\\s*$")
- exit = regexp.MustCompile("^\\s*exit\\s*;*\\s*$")
+ passwordRegexp = regexp.MustCompile(`personal.[nus]`)
+ onlyWhitespace = regexp.MustCompile(`^\s*$`)
+ exit = regexp.MustCompile(`^\s*exit\s*;*\s*$`)
)
// HistoryFile is the file within the data directory to store input scrollback.
@@ -226,8 +226,8 @@ func (c *Console) AutoCompleteInput(line string, pos int) (string, []string, str
}
// Chunck data to relevant part for autocompletion
// E.g. in case of nested lines eth.getBalance(eth.coinb
- start := 0
- for start = pos - 1; start > 0; start-- {
+ start := pos - 1
+ for ; start > 0; start-- {
// Skip all methods and namespaces (i.e. including te dot)
if line[start] == '.' || (line[start] >= 'a' && line[start] <= 'z') || (line[start] >= 'A' && line[start] <= 'Z') {
continue
@@ -275,10 +275,7 @@ func (c *Console) Evaluate(statement string) error {
fmt.Fprintf(c.printer, "[native] error: %v\n", r)
}
}()
- if err := c.jsre.Evaluate(statement, c.printer); err != nil {
- return err
- }
- return nil
+ return c.jsre.Evaluate(statement, c.printer)
}
// Interactive starts an interactive user session, where input is propted from
diff --git a/console/prompter.go b/console/prompter.go
index 5946d9ece3..6acbfb0e25 100644
--- a/console/prompter.go
+++ b/console/prompter.go
@@ -44,7 +44,7 @@ type UserPrompter interface {
PromptConfirm(prompt string) (bool, error)
// SetHistory sets the the input scrollback history that the prompter will allow
- // the user to scoll back to.
+ // the user to scroll back to.
SetHistory(history []string)
// AppendHistory appends an entry to the scrollback history. It should be called
@@ -147,7 +147,7 @@ func (p *terminalPrompter) PromptConfirm(prompt string) (bool, error) {
}
// SetHistory sets the the input scrollback history that the prompter will allow
-// the user to scoll back to.
+// the user to scroll back to.
func (p *terminalPrompter) SetHistory(history []string) {
p.State.ReadHistory(strings.NewReader(strings.Join(history, "\n")))
}
diff --git a/contracts/chequebook/cheque.go b/contracts/chequebook/cheque.go
index 08dbbdd71e..42ec391ca0 100644
--- a/contracts/chequebook/cheque.go
+++ b/contracts/chequebook/cheque.go
@@ -252,7 +252,7 @@ func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *
return nil, fmt.Errorf("amount must be greater than zero (%v)", amount)
}
if self.balance.Cmp(amount) < 0 {
- err = fmt.Errorf("insufficent funds to issue cheque for amount: %v. balance: %v", amount, self.balance)
+ err = fmt.Errorf("insufficient funds to issue cheque for amount: %v. balance: %v", amount, self.balance)
} else {
var sig []byte
sent, found := self.sent[beneficiary]
@@ -277,7 +277,7 @@ func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *
}
// auto deposit if threshold is set and balance is less then threshold
- // note this is called even if issueing cheque fails
+ // note this is called even if issuing cheque fails
// so we reattempt depositing
if self.threshold != nil {
if self.balance.Cmp(self.threshold) < 0 {
diff --git a/contracts/chequebook/cheque_test.go b/contracts/chequebook/cheque_test.go
index c87c3c7ce2..9185ce1ae3 100644
--- a/contracts/chequebook/cheque_test.go
+++ b/contracts/chequebook/cheque_test.go
@@ -73,8 +73,8 @@ func TestIssueAndReceive(t *testing.T) {
}
chbook.sent[addr1] = new(big.Int).SetUint64(42)
amount := common.Big1
- ch, err := chbook.Issue(addr1, amount)
- if err == nil {
+
+ if _, err = chbook.Issue(addr1, amount); err == nil {
t.Fatalf("expected insufficient funds error, got none")
}
@@ -83,7 +83,7 @@ func TestIssueAndReceive(t *testing.T) {
t.Fatalf("expected: %v, got %v", "0", chbook.Balance())
}
- ch, err = chbook.Issue(addr1, amount)
+ ch, err := chbook.Issue(addr1, amount)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
@@ -128,8 +128,8 @@ func TestCheckbookFile(t *testing.T) {
t.Errorf("expected: %v, got %v", "0", chbook.Balance())
}
- ch, err := chbook.Issue(addr1, common.Big1)
- if err != nil {
+ var ch *Cheque
+ if ch, err = chbook.Issue(addr1, common.Big1); err != nil {
t.Fatalf("expected no error, got %v", err)
}
if ch.Amount.Cmp(new(big.Int).SetUint64(43)) != 0 {
@@ -155,7 +155,7 @@ func TestVerifyErrors(t *testing.T) {
}
path1 := filepath.Join(os.TempDir(), "chequebook-test-1.json")
- contr1, err := deploy(key1, common.Big2, backend)
+ contr1, _ := deploy(key1, common.Big2, backend)
chbook1, err := NewChequebook(path1, contr1, key1, backend)
if err != nil {
t.Errorf("expected no error, got %v", err)
@@ -223,7 +223,8 @@ func TestVerifyErrors(t *testing.T) {
func TestDeposit(t *testing.T) {
path0 := filepath.Join(os.TempDir(), "chequebook-test-0.json")
backend := newTestBackend()
- contr0, err := deploy(key0, new(big.Int), backend)
+ contr0, _ := deploy(key0, new(big.Int), backend)
+
chbook, err := NewChequebook(path0, contr0, key0, backend)
if err != nil {
t.Errorf("expected no error, got %v", err)
@@ -361,7 +362,8 @@ func TestDeposit(t *testing.T) {
func TestCash(t *testing.T) {
path := filepath.Join(os.TempDir(), "chequebook-test.json")
backend := newTestBackend()
- contr0, err := deploy(key0, common.Big2, backend)
+ contr0, _ := deploy(key0, common.Big2, backend)
+
chbook, err := NewChequebook(path, contr0, key0, backend)
if err != nil {
t.Errorf("expected no error, got %v", err)
@@ -380,11 +382,12 @@ func TestCash(t *testing.T) {
}
// cashing latest cheque
- _, err = chbox.Receive(ch)
- if err != nil {
+ if _, err = chbox.Receive(ch); err != nil {
t.Fatalf("expected no error, got %v", err)
}
- _, err = ch.Cash(chbook.session)
+ if _, err = ch.Cash(chbook.session); err != nil {
+ t.Fatal("Cash failed:", err)
+ }
backend.Commit()
chbook.balance = new(big.Int).Set(common.Big3)
diff --git a/contracts/ens/ens_test.go b/contracts/ens/ens_test.go
index be90688f16..167e60a730 100644
--- a/contracts/ens/ens_test.go
+++ b/contracts/ens/ens_test.go
@@ -29,7 +29,7 @@ import (
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
name = "my name on ENS"
- hash = crypto.Sha3Hash([]byte("my content"))
+ hash = crypto.Keccak256Hash([]byte("my content"))
addr = crypto.PubkeyToAddress(key.PublicKey)
)
diff --git a/contracts/release/contract.sol b/contracts/release/contract.sol
index 70df41b55b..aaaa641390 100644
--- a/contracts/release/contract.sol
+++ b/contracts/release/contract.sol
@@ -78,7 +78,7 @@ contract ReleaseOracle {
}
// signers is an accessor method to retrieve all te signers (public accessor
- // generates an indexed one, not a retreive-all version).
+ // generates an indexed one, not a retrieve-all version).
function signers() constant returns(address[]) {
return voters;
}
@@ -178,7 +178,7 @@ contract ReleaseOracle {
voters[i] = voters[voters.length - 1];
voters.length--;
- delete verProp; // Nuke any version proposal (no suprise releases!)
+ delete verProp; // Nuke any version proposal (no surprise releases!)
break;
}
}
diff --git a/core/bench_test.go b/core/bench_test.go
index 7469f84651..deffec3e83 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -83,7 +83,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
gas := IntrinsicGas(data, false, false)
- tx, _ := types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data).SignECDSA(types.HomesteadSigner{}, benchRootKey)
+ tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey)
gen.AddTx(tx)
}
}
@@ -123,7 +123,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
nil,
nil,
)
- tx, _ = tx.SignECDSA(types.HomesteadSigner{}, ringKeys[from])
+ tx, _ = types.SignTx(tx, types.HomesteadSigner{}, ringKeys[from])
gen.AddTx(tx)
from = to
}
diff --git a/core/block_validator_test.go b/core/block_validator_test.go
index 1481ee4a1a..b70aa79a7e 100644
--- a/core/block_validator_test.go
+++ b/core/block_validator_test.go
@@ -24,11 +24,9 @@ import (
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core/state"
"github.com/ubiq/go-ubiq/core/types"
- "github.com/ubiq/go-ubiq/core/vm"
"github.com/ubiq/go-ubiq/ethdb"
"github.com/ubiq/go-ubiq/event"
"github.com/ubiq/go-ubiq/params"
- "github.com/ubiq/go-ubiq/pow/ezp"
)
func testChainConfig() *params.ChainConfig {
@@ -49,20 +47,19 @@ func proc() (Validator, *BlockChain) {
}
func TestNumber(t *testing.T) {
- pow := ezp.New()
_, chain := proc()
statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb)
cfg := testChainConfig()
header := makeHeader(cfg, chain.Genesis(), statedb)
header.Number = big.NewInt(3)
- err := ValidateHeader(cfg, pow, header, chain.Genesis().Header(), false, false)
+ err := ValidateHeader(cfg, FakePow{}, header, chain.Genesis().Header(), false, false)
if err != BlockNumberErr {
t.Errorf("expected block number error, got %q", err)
}
header = makeHeader(cfg, chain.Genesis(), statedb)
- err = ValidateHeader(cfg, pow, header, chain.Genesis().Header(), false, false)
+ err = ValidateHeader(cfg, FakePow{}, header, chain.Genesis().Header(), false, false)
if err == BlockNumberErr {
t.Errorf("didn't expect block number error")
}
@@ -77,7 +74,7 @@ func TestPutReceipt(t *testing.T) {
hash[0] = 2
receipt := new(types.Receipt)
- receipt.Logs = vm.Logs{&vm.Log{
+ receipt.Logs = []*types.Log{{
Address: addr,
Topics: []common.Hash{hash},
Data: []byte("hi"),
diff --git a/core/blockchain.go b/core/blockchain.go
index 49fae0bdbb..5db25be6cc 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -29,7 +29,9 @@ import (
"sync/atomic"
"time"
+ "github.com/hashicorp/golang-lru"
"github.com/ubiq/go-ubiq/common"
+ "github.com/ubiq/go-ubiq/common/mclock"
"github.com/ubiq/go-ubiq/core/state"
"github.com/ubiq/go-ubiq/core/types"
"github.com/ubiq/go-ubiq/core/vm"
@@ -43,13 +45,9 @@ import (
"github.com/ubiq/go-ubiq/pow"
"github.com/ubiq/go-ubiq/rlp"
"github.com/ubiq/go-ubiq/trie"
- "github.com/hashicorp/golang-lru"
)
var (
- chainlogger = logger.NewLogger("CHAIN")
- jsonlogger = logger.NewJsonLogger()
-
blockInsertTimer = metrics.NewTimer("chain/inserts")
ErrNoGenesis = errors.New("Genesis not found in chain")
@@ -152,7 +150,7 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.P
return nil, err
}
// Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain
- for hash, _ := range BadHashes {
+ for hash := range BadHashes {
if header := bc.GetHeaderByHash(hash); header != nil {
// get the canonical block corresponding to the offending header's number
headerByNumber := bc.GetHeaderByNumber(header.Number.Uint64())
@@ -404,10 +402,7 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) {
// Export writes the active chain to the given writer.
func (self *BlockChain) Export(w io.Writer) error {
- if err := self.ExportN(w, uint64(0), self.currentBlock.NumberU64()); err != nil {
- return err
- }
- return nil
+ return self.ExportN(w, uint64(0), self.currentBlock.NumberU64())
}
// ExportN writes a subset of the active chain to the given writer.
@@ -634,7 +629,11 @@ func (self *BlockChain) procFutureBlocks() {
}
if len(blocks) > 0 {
types.BlockBy(types.Number).Sort(blocks)
- self.InsertChain(blocks)
+
+ // Insert one by one as chain insertion needs contiguous ancestry between blocks
+ for i := range blocks {
+ self.InsertChain(blocks[i : i+1])
+ }
}
}
@@ -708,6 +707,18 @@ func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts ty
// transaction and receipt data.
// XXX should this be moved to the test?
func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
+ // Do a sanity check that the provided chain is actually ordered and linked
+ for i := 1; i < len(blockChain); i++ {
+ if blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() {
+ // Chain broke ancestry, log a messge (programming error) and skip insertion
+ failure := fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, blockChain[i-1].NumberU64(),
+ blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4])
+
+ glog.V(logger.Error).Info(failure.Error())
+ return 0, failure
+ }
+ }
+ // Pre-checks passed, start the block body and receipt imports
self.wg.Add(1)
defer self.wg.Done()
@@ -822,7 +833,7 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain
if stats.ignored > 0 {
ignored = fmt.Sprintf(" (%d ignored)", stats.ignored)
}
- glog.V(logger.Info).Infof("imported %d receipts in %9v. #%d [%x… / %x…]%s", stats.processed, common.PrettyDuration(time.Since(start)), last.Number(), first.Hash().Bytes()[:4], last.Hash().Bytes()[:4], ignored)
+ glog.V(logger.Info).Infof("imported %4d receipts in %9v. #%d [%x… / %x…]%s", stats.processed, common.PrettyDuration(time.Since(start)), last.Number(), first.Hash().Bytes()[:4], last.Hash().Bytes()[:4], ignored)
return 0, nil
}
@@ -876,6 +887,18 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err
// InsertChain will attempt to insert the given chain in to the canonical chain or, otherwise, create a fork. It an error is returned
// it will return the index number of the failing block as well an error describing what went wrong (for possible errors see core/errors.go).
func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
+ // Do a sanity check that the provided chain is actually ordered and linked
+ for i := 1; i < len(chain); i++ {
+ if chain[i].NumberU64() != chain[i-1].NumberU64()+1 || chain[i].ParentHash() != chain[i-1].Hash() {
+ // Chain broke ancestry, log a messge (programming error) and skip insertion
+ failure := fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])",
+ i-1, chain[i-1].NumberU64(), chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4])
+
+ glog.V(logger.Error).Info(failure.Error())
+ return 0, failure
+ }
+ }
+ // Pre-checks passed, start the full block imports
self.wg.Add(1)
defer self.wg.Done()
@@ -886,9 +909,9 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
// faster than direct delivery and requires much less mutex
// acquiring.
var (
- stats = insertStats{startTime: time.Now()}
+ stats = insertStats{startTime: mclock.Now()}
events = make([]interface{}, 0, len(chain))
- coalescedLogs vm.Logs
+ coalescedLogs []*types.Log
nonceChecked = make([]bool, len(chain))
)
@@ -949,10 +972,8 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
}
self.reportBlock(block, nil, err)
-
return i, err
}
-
// Create a new statedb using the parent block and report an
// error if it fails.
switch {
@@ -1021,7 +1042,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
glog.Infof("inserted forked block #%d [%x…] (TD=%v) in %9v: %3d txs %d uncles.", block.Number(), block.Hash().Bytes()[0:4], block.Difficulty(), common.PrettyDuration(time.Since(bstart)), len(block.Transactions()), len(block.Uncles()))
}
blockInsertTimer.UpdateSince(bstart)
- events = append(events, ChainSideEvent{block, logs})
+ events = append(events, ChainSideEvent{block})
case SplitStatTy:
events = append(events, ChainSplitEvent{block, logs})
@@ -1044,7 +1065,7 @@ type insertStats struct {
queued, processed, ignored int
usedGas uint64
lastIndex int
- startTime time.Time
+ startTime mclock.AbsTime
}
// statsReportLimit is the time limit during import after which we always print
@@ -1056,28 +1077,24 @@ const statsReportLimit = 8 * time.Second
func (st *insertStats) report(chain []*types.Block, index int) {
// Fetch the timings for the batch
var (
- now = time.Now()
- elapsed = now.Sub(st.startTime)
+ now = mclock.Now()
+ elapsed = time.Duration(now) - time.Duration(st.startTime)
)
- if elapsed == 0 { // Yes Windows, I'm looking at you
- elapsed = 1
- }
// If we're at the last block of the batch or report period reached, log
if index == len(chain)-1 || elapsed >= statsReportLimit {
start, end := chain[st.lastIndex], chain[index]
txcount := countTransactions(chain[st.lastIndex : index+1])
- extra := ""
+ var hashes, extra string
if st.queued > 0 || st.ignored > 0 {
extra = fmt.Sprintf(" (%d queued %d ignored)", st.queued, st.ignored)
}
- hashes := ""
if st.processed > 1 {
hashes = fmt.Sprintf("%x… / %x…", start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
} else {
hashes = fmt.Sprintf("%x…", end.Hash().Bytes()[:4])
}
- glog.Infof("imported %d blocks, %5d txs (%7.3f Mg) in %9v (%6.3f Mg/s). #%v [%s]%s", st.processed, txcount, float64(st.usedGas)/1000000, common.PrettyDuration(elapsed), float64(st.usedGas)*1000/float64(elapsed), end.Number(), hashes, extra)
+ glog.Infof("imported %4d blocks, %5d txs (%7.3f Mg) in %9v (%6.3f Mg/s). #%v [%s]%s", st.processed, txcount, float64(st.usedGas)/1000000, common.PrettyDuration(elapsed), float64(st.usedGas)*1000/float64(elapsed), end.Number(), hashes, extra)
*st = insertStats{startTime: now, lastIndex: index}
}
@@ -1095,24 +1112,25 @@ func countTransactions(chain []*types.Block) (c int) {
// event about them
func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
var (
- newChain types.Blocks
- oldChain types.Blocks
- commonBlock *types.Block
- oldStart = oldBlock
- newStart = newBlock
- deletedTxs types.Transactions
- deletedLogs vm.Logs
- deletedLogsByHash = make(map[common.Hash]vm.Logs)
+ newChain types.Blocks
+ oldChain types.Blocks
+ commonBlock *types.Block
+ oldStart = oldBlock
+ newStart = newBlock
+ deletedTxs types.Transactions
+ deletedLogs []*types.Log
// collectLogs collects the logs that were generated during the
// processing of the block that corresponds with the given hash.
// These logs are later announced as deleted.
collectLogs = func(h common.Hash) {
- // Coalesce logs
+ // Coalesce logs and set 'Removed'.
receipts := GetBlockReceipts(self.chainDb, h, self.hc.GetBlockNumber(h))
for _, receipt := range receipts {
- deletedLogs = append(deletedLogs, receipt.Logs...)
-
- deletedLogsByHash[h] = receipt.Logs
+ for _, log := range receipt.Logs {
+ del := *log
+ del.Removed = true
+ deletedLogs = append(deletedLogs, &del)
+ }
}
}
)
@@ -1206,7 +1224,7 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
if len(oldChain) > 0 {
go func() {
for _, block := range oldChain {
- self.eventMux.Post(ChainSideEvent{Block: block, Logs: deletedLogsByHash[block.Hash()]})
+ self.eventMux.Post(ChainSideEvent{Block: block})
}
}()
}
@@ -1216,7 +1234,7 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
// postChainEvents iterates over the events generated by a chain insertion and
// posts them into the event mux.
-func (self *BlockChain) postChainEvents(events []interface{}, logs vm.Logs) {
+func (self *BlockChain) postChainEvents(events []interface{}, logs []*types.Log) {
// post event logs for further processing
self.eventMux.Post(logs)
for _, event := range events {
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index aad9c48597..828b2bdc8f 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -435,7 +435,7 @@ func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return n
func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
return nil
}
-func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
+func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error) {
return nil, nil, new(big.Int), nil
}
@@ -719,7 +719,7 @@ func TestFastVsFullChains(t *testing.T) {
// If the block number is multiple of 3, send a few bonus transactions to the miner
if i%3 == 2 {
for j := 0; j < i%4+1; j++ {
- tx, err := types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key)
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key)
if err != nil {
panic(err)
}
@@ -883,8 +883,8 @@ func TestChainTxReorgs(t *testing.T) {
// Create two transactions shared between the chains:
// - postponed: transaction included at a later block in the forked chain
// - swapped: transaction included at the same block number in the forked chain
- postponed, _ := types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key1)
- swapped, _ := types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key1)
+ postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
+ swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
// Create two transactions that will be dropped by the forked chain:
// - pastDrop: transaction dropped retroactively from a past block
@@ -900,13 +900,13 @@ func TestChainTxReorgs(t *testing.T) {
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {
switch i {
case 0:
- pastDrop, _ = types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key2)
+ pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point
gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
case 2:
- freshDrop, _ = types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key2)
+ freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
gen.AddTx(swapped) // This transaction will be swapped out at the exact height
@@ -925,18 +925,18 @@ func TestChainTxReorgs(t *testing.T) {
chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 5, func(i int, gen *BlockGen) {
switch i {
case 0:
- pastAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key3)
+ pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
case 2:
gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain
- freshAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key3)
+ freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
case 3:
- futureAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key3)
+ futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
}
})
@@ -995,7 +995,7 @@ func TestLogReorgs(t *testing.T) {
subs := evmux.Subscribe(RemovedLogsEvent{})
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 2, func(i int, gen *BlockGen) {
if i == 1 {
- tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), code).SignECDSA(signer, key1)
+ tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), code), signer, key1)
if err != nil {
t.Fatalf("failed to create tx: %v", err)
}
@@ -1035,7 +1035,7 @@ func TestReorgSideEvent(t *testing.T) {
}
replacementBlocks, _ := GenerateChain(params.TestChainConfig, genesis, db, 4, func(i int, gen *BlockGen) {
- tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil).SignECDSA(signer, key1)
+ tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil), signer, key1)
if i == 2 {
gen.OffsetTime(-1)
}
@@ -1107,7 +1107,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *BlockGen) {})
- for i, _ := range chain {
+ for i := range chain {
go func(block *types.Block) {
// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
for {
@@ -1152,7 +1152,7 @@ func TestEIP155Transition(t *testing.T) {
tx *types.Transaction
err error
basicTx = func(signer types.Signer) (*types.Transaction, error) {
- return types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, key)
+ return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
}
)
switch i {
@@ -1215,7 +1215,7 @@ func TestEIP155Transition(t *testing.T) {
tx *types.Transaction
err error
basicTx = func(signer types.Signer) (*types.Transaction, error) {
- return types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, key)
+ return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
}
)
switch i {
@@ -1260,11 +1260,11 @@ func TestEIP161AccountRemoval(t *testing.T) {
)
switch i {
case 0:
- tx, err = types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, key)
+ tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
case 1:
- tx, err = types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, key)
+ tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
case 2:
- tx, err = types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, key)
+ tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
}
if err != nil {
t.Fatal(err)
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 63dbce3d3f..3cd32d7ae1 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -106,7 +106,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
b.SetCoinbase(common.Address{})
}
b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs))
- receipt, _, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
+ receipt, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
if err != nil {
panic(err)
}
diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go
index a124ebaa6b..0c194499f0 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -55,13 +55,13 @@ func ExampleGenerateChain() {
switch i {
case 0:
// In block 1, addr1 sends addr2 some ether.
- tx, _ := types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(signer, key1)
+ tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
gen.AddTx(tx)
case 1:
// In block 2, addr1 sends some more ether to addr2.
// addr2 passes it on to addr3.
- tx1, _ := types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key1)
- tx2, _ := types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key2)
+ tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
+ tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
gen.AddTx(tx1)
gen.AddTx(tx2)
case 2:
diff --git a/core/database_util.go b/core/database_util.go
index f779d9a72e..7ace490b59 100644
--- a/core/database_util.go
+++ b/core/database_util.go
@@ -23,6 +23,7 @@ import (
"errors"
"fmt"
"math/big"
+ "sync"
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core/types"
@@ -63,6 +64,8 @@ var (
oldBlockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually]
ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error
+
+ mipmapBloomMu sync.Mutex // protect against race condition when updating mipmap blooms
)
// encodeBlockNumber encodes a block number as big endian uint64
@@ -564,6 +567,9 @@ func mipmapKey(num, level uint64) []byte {
// WriteMapmapBloom writes each address included in the receipts' logs to the
// MIP bloom bin.
func WriteMipmapBloom(db ethdb.Database, number uint64, receipts types.Receipts) error {
+ mipmapBloomMu.Lock()
+ defer mipmapBloomMu.Unlock()
+
batch := db.NewBatch()
for _, level := range MIPMapLevels {
key := mipmapKey(number, level)
diff --git a/core/database_util_test.go b/core/database_util_test.go
index 105ef86a8b..18f4ba4837 100644
--- a/core/database_util_test.go
+++ b/core/database_util_test.go
@@ -26,7 +26,6 @@ import (
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core/types"
- "github.com/ubiq/go-ubiq/core/vm"
"github.com/ubiq/go-ubiq/crypto"
"github.com/ubiq/go-ubiq/crypto/sha3"
"github.com/ubiq/go-ubiq/ethdb"
@@ -393,9 +392,9 @@ func TestReceiptStorage(t *testing.T) {
receipt1 := &types.Receipt{
PostState: []byte{0x01},
CumulativeGasUsed: big.NewInt(1),
- Logs: vm.Logs{
- &vm.Log{Address: common.BytesToAddress([]byte{0x11})},
- &vm.Log{Address: common.BytesToAddress([]byte{0x01, 0x11})},
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x11})},
+ {Address: common.BytesToAddress([]byte{0x01, 0x11})},
},
TxHash: common.BytesToHash([]byte{0x11, 0x11}),
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
@@ -404,9 +403,9 @@ func TestReceiptStorage(t *testing.T) {
receipt2 := &types.Receipt{
PostState: []byte{0x02},
CumulativeGasUsed: big.NewInt(2),
- Logs: vm.Logs{
- &vm.Log{Address: common.BytesToAddress([]byte{0x22})},
- &vm.Log{Address: common.BytesToAddress([]byte{0x02, 0x22})},
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x22})},
+ {Address: common.BytesToAddress([]byte{0x02, 0x22})},
},
TxHash: common.BytesToHash([]byte{0x22, 0x22}),
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
@@ -431,7 +430,7 @@ func TestReceiptStorage(t *testing.T) {
rlpHave, _ := rlp.EncodeToBytes(r)
rlpWant, _ := rlp.EncodeToBytes(receipt)
- if bytes.Compare(rlpHave, rlpWant) != 0 {
+ if !bytes.Equal(rlpHave, rlpWant) {
t.Fatalf("receipt #%d [%x]: receipt mismatch: have %v, want %v", i, receipt.TxHash, r, receipt)
}
}
@@ -452,9 +451,9 @@ func TestBlockReceiptStorage(t *testing.T) {
receipt1 := &types.Receipt{
PostState: []byte{0x01},
CumulativeGasUsed: big.NewInt(1),
- Logs: vm.Logs{
- &vm.Log{Address: common.BytesToAddress([]byte{0x11})},
- &vm.Log{Address: common.BytesToAddress([]byte{0x01, 0x11})},
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x11})},
+ {Address: common.BytesToAddress([]byte{0x01, 0x11})},
},
TxHash: common.BytesToHash([]byte{0x11, 0x11}),
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
@@ -463,9 +462,9 @@ func TestBlockReceiptStorage(t *testing.T) {
receipt2 := &types.Receipt{
PostState: []byte{0x02},
CumulativeGasUsed: big.NewInt(2),
- Logs: vm.Logs{
- &vm.Log{Address: common.BytesToAddress([]byte{0x22})},
- &vm.Log{Address: common.BytesToAddress([]byte{0x02, 0x22})},
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x22})},
+ {Address: common.BytesToAddress([]byte{0x02, 0x22})},
},
TxHash: common.BytesToHash([]byte{0x22, 0x22}),
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
@@ -489,7 +488,7 @@ func TestBlockReceiptStorage(t *testing.T) {
rlpHave, _ := rlp.EncodeToBytes(rs[i])
rlpWant, _ := rlp.EncodeToBytes(receipts[i])
- if bytes.Compare(rlpHave, rlpWant) != 0 {
+ if !bytes.Equal(rlpHave, rlpWant) {
t.Fatalf("receipt #%d: receipt mismatch: have %v, want %v", i, rs[i], receipts[i])
}
}
@@ -505,14 +504,14 @@ func TestMipmapBloom(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
receipt1 := new(types.Receipt)
- receipt1.Logs = vm.Logs{
- &vm.Log{Address: common.BytesToAddress([]byte("test"))},
- &vm.Log{Address: common.BytesToAddress([]byte("address"))},
+ receipt1.Logs = []*types.Log{
+ {Address: common.BytesToAddress([]byte("test"))},
+ {Address: common.BytesToAddress([]byte("address"))},
}
receipt2 := new(types.Receipt)
- receipt2.Logs = vm.Logs{
- &vm.Log{Address: common.BytesToAddress([]byte("test"))},
- &vm.Log{Address: common.BytesToAddress([]byte("address1"))},
+ receipt2.Logs = []*types.Log{
+ {Address: common.BytesToAddress([]byte("test"))},
+ {Address: common.BytesToAddress([]byte("address1"))},
}
WriteMipmapBloom(db, 1, types.Receipts{receipt1})
@@ -528,14 +527,14 @@ func TestMipmapBloom(t *testing.T) {
// reset
db, _ = ethdb.NewMemDatabase()
receipt := new(types.Receipt)
- receipt.Logs = vm.Logs{
- &vm.Log{Address: common.BytesToAddress([]byte("test"))},
+ receipt.Logs = []*types.Log{
+ {Address: common.BytesToAddress([]byte("test"))},
}
WriteMipmapBloom(db, 999, types.Receipts{receipt1})
receipt = new(types.Receipt)
- receipt.Logs = vm.Logs{
- &vm.Log{Address: common.BytesToAddress([]byte("test 1"))},
+ receipt.Logs = []*types.Log{
+ {Address: common.BytesToAddress([]byte("test 1"))},
}
WriteMipmapBloom(db, 1000, types.Receipts{receipt})
@@ -568,17 +567,12 @@ func TestMipmapChain(t *testing.T) {
switch i {
case 1:
receipt := types.NewReceipt(nil, new(big.Int))
- receipt.Logs = vm.Logs{
- &vm.Log{
- Address: addr,
- Topics: []common.Hash{hash1},
- },
- }
+ receipt.Logs = []*types.Log{{Address: addr, Topics: []common.Hash{hash1}}}
gen.AddUncheckedReceipt(receipt)
receipts = types.Receipts{receipt}
case 1000:
receipt := types.NewReceipt(nil, new(big.Int))
- receipt.Logs = vm.Logs{&vm.Log{Address: addr2}}
+ receipt.Logs = []*types.Log{{Address: addr2}}
gen.AddUncheckedReceipt(receipt)
receipts = types.Receipts{receipt}
diff --git a/core/default_genesis.go b/core/default_genesis.go
index ce6c4f04e9..7f3692829a 100644
--- a/core/default_genesis.go
+++ b/core/default_genesis.go
@@ -20,4 +20,9 @@ package core
// genesis block.
const defaultGenesisBlock = "H4sIACZxSlgAA61Sy24bMQy8+ysMn3OgKJKiegvQQw/9CerBZgE/gngLuAjy75XjGEiRtnAX5UHAUuTMzoyeV+tRm/1hX/vm03oDJ3hfqrq5u4zM064fZ9s9XsZY6T4Bpev1oz31/fzFjg+/gVlQV9x+mp/ss832Bnvtf7Pj12k3zZe2/rrUJvepft/OPy7X/hdEssTSBMcZpaRITLy5O09exnfT6eG/q6qHaV/s+GZ5vLGu27bdHupYfX79fG0NHUm8xZ65t2ih556EsqbiHjFCGVEyeKN23ltvim3tLfLw579dv9y9p0ACtCJSA9YaqAg7YE2eWonmWaBCZaT4gYJvJOhiA7drYiIs7JkVWs0NzopsHA3RM5TFBOCdJRQ0RHMD6hW4uFZXwVZjGQ4lz+GjSbcScNco4iODTBULuVPSyooKgcgNBUIQDosJQiXmDmZlOM4t5NKFyZR7IfQhpKfQe11uUQSLKedm7EKgXc4UQTlWSnVwaonKQfNyBRIpAvL5RZbhR1JOBo4gjUNHHXJi0bjcIiQE1/HisWGuGag0hhiGLizqaNWrFMnLQ3Zz6S7ckqJpD1IoSBUzAiEeIacgiv9G8Ir/snpZ/QSJtNFBkgUAAA=="
+// defaultTestnetGenesisBlock is a gzip compressed dump of the official default Ethereum
+// test network genesis block (currently Ropsten).
const defaultTestnetGenesisBlock = "QlpoOTFBWSZTWezl5v8AGI59gHRQABBcBH/0FGQ/795qUAN+PcGHAoQMVIwm0zCSp+ihiAANB6gqp+mmp6eMmqo0AADRpoABgAGjTQBk0BoMgNAbSqNAyDQyaBoNAyAGBUkhNNNA0JGmgD0g0AekxJtVIgTtRupd52vqSSSVfG2ALQBhhdbCIgmAgUiIJnEQTviQF8kkN1IOKiRVVFVSVXHT1cHLHdiSQBlAAG2JtCtNgCS5IEB6dNtP782Z729Tbt3Z6+uFr7u3w79widyYSMAhtlL5XeGZWdwEBNdFm+Hh5gVRBoslgksAMkUKmYlSJRTxBsQAAKKlnd3WjPWrNd66NR7WAu6AQL40vqlny1We+p3l1QThy4gRd2lSVjEKISLDFrVJ1UtjPEPctVSZGjBTJWRzpdgRmCCprJWGe3DK9wvz4eGvdv1ZZcMvxGgdakeMAVEeFywVQWLWCrUWg60Y9ZLoAuZ0uSJJUELb8N2e7j05d3Tlz7vRwgk+LisedJALLVUgFjLGzfv5SEGPLlhhjpx2aaO8T87whcYDmgPLVDBI3RF1TLnzvymWtN732u93GiAGiBdm5s1q9Haru7+yCLJAk3wBDTtrGeMVh3iJmXHo+OBAsqoDOZwjKM3d3mZmcHdBJiSVouSKVkDg+L4vSzNRqs7u6NEaIiUKoYxERGfTWIhoiHwQFESGzta21qu1qtZ3cAXQshVAUybTLbWttWh3iHLoDJEgkACRVCzbeX774Z6558+mfDTdz058efTpDbXai+tddbQBbn/YA9fwxy8/Pq3HFW7deL8W447duuyI2RGqIAAA9SOqHeXzbRtGiXeHiYBMggAAIyQh/f5vW96Xvvu7b7777yMURRHFE6EkEnxRIiOlY2ijs7RV4gYIB0R4LmgB49xg0Oz0bVr2a95u4AZE6rTjl2w58Cntu1ZV6e3Lw3ne2RegHw7R58NdbtVlJEkuqRV2WKa/hpbPIx3bIyX43aNWmezrCBry2ePzkA8reSwB/xdyRThQkOzl5v8="
+
+// defaultDevnetGenesisBlockis a gzip compressed dump of a dev Ethereum network genesis block.
+const defaultDevnetGenesisBlock = "QlpoOTFBWSZTWb66siUAAmldgEERWAR/8AAEP6eOakACGu6ULVwyRNGKeBCbUaAGIJUDJpT2lP1BQAAANqp6jTUxMIxGmAJgFVTAp4mjSaoAxAB4cLqQ6aBgTRPcAECErBIZ9PyZJw5IYvQPWlcXDXnhaC9buNtJaAFApAgwkUIRBmBY6EhTY8/6ZXse6lLD1Eh3sfas7VHnxse2LZeXpME9fADujvYB4Wr9cgQA+Wq0gRnrfVmgVwIiIrgfON+c4EhrApvQT2LjUh6y3x41XXOxriA89W52MDId9R7vasE3pfWWATQF7nAEAWRb861EU6M9SmBTET1UUGuBmgxzewhmUe4Rj4jSvgRFRTYgI8WzzcQn3nNr6x3GW94Yp1xG4kOoC/MxDhbF8uMahuMTmKyEep0SwLaxquoT3F9X0NxmouMWhxTAlxHQoN872vbYJS/Wrls2LuptmbKr4R6BJ/oJfPNpq+H362F5ZyhNQBxXep4ZOr94ylFM6UfOeUq37kFeGzXOle3LKItkvJ70OKqEqzujic72rnpZHym6b3xSONoWjYzRzIUbOKSUgdVtEhtvUg24asA2cy1zFzsfnj8CXe0Bdi7kinChIX11ZEo="
diff --git a/core/events.go b/core/events.go
index 9ca414ecb3..8267f30456 100644
--- a/core/events.go
+++ b/core/events.go
@@ -21,7 +21,6 @@ import (
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core/types"
- "github.com/ubiq/go-ubiq/core/vm"
)
// TxPreEvent is posted when a transaction enters the transaction pool.
@@ -32,7 +31,7 @@ type TxPostEvent struct{ Tx *types.Transaction }
// PendingLogsEvent is posted pre mining and notifies of pending logs.
type PendingLogsEvent struct {
- Logs vm.Logs
+ Logs []*types.Log
}
// PendingStateEvent is posted pre mining and notifies of pending state changes.
@@ -45,28 +44,27 @@ type NewMinedBlockEvent struct{ Block *types.Block }
type RemovedTransactionEvent struct{ Txs types.Transactions }
// RemovedLogEvent is posted when a reorg happens
-type RemovedLogsEvent struct{ Logs vm.Logs }
+type RemovedLogsEvent struct{ Logs []*types.Log }
// ChainSplit is posted when a new head is detected
type ChainSplitEvent struct {
Block *types.Block
- Logs vm.Logs
+ Logs []*types.Log
}
type ChainEvent struct {
Block *types.Block
Hash common.Hash
- Logs vm.Logs
+ Logs []*types.Log
}
type ChainSideEvent struct {
Block *types.Block
- Logs vm.Logs
}
type PendingBlockEvent struct {
Block *types.Block
- Logs vm.Logs
+ Logs []*types.Log
}
type ChainUncleEvent struct {
diff --git a/core/evm.go b/core/evm.go
new file mode 100644
index 0000000000..e6868580c1
--- /dev/null
+++ b/core/evm.go
@@ -0,0 +1,73 @@
+// Copyright 2014 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 core
+
+import (
+ "math/big"
+
+ "github.com/ubiq/go-ubiq/common"
+ "github.com/ubiq/go-ubiq/core/types"
+ "github.com/ubiq/go-ubiq/core/vm"
+)
+
+// BlockFetcher retrieves headers by their hash
+type HeaderFetcher interface {
+ // GetHeader returns the hash corresponding to their hash
+ GetHeader(common.Hash, uint64) *types.Header
+}
+
+// NewEVMContext creates a new context for use in the EVM.
+func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Context {
+ return vm.Context{
+ CanTransfer: CanTransfer,
+ Transfer: Transfer,
+ GetHash: GetHashFn(header, chain),
+
+ Origin: msg.From(),
+ Coinbase: header.Coinbase,
+ BlockNumber: new(big.Int).Set(header.Number),
+ Time: new(big.Int).Set(header.Time),
+ Difficulty: new(big.Int).Set(header.Difficulty),
+ GasLimit: new(big.Int).Set(header.GasLimit),
+ GasPrice: new(big.Int).Set(msg.GasPrice()),
+ }
+}
+
+// GetHashFn returns a GetHashFunc which retrieves header hashes by number
+func GetHashFn(ref *types.Header, chain HeaderFetcher) func(n uint64) common.Hash {
+ return func(n uint64) common.Hash {
+ for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) {
+ if header.Number.Uint64() == n {
+ return header.Hash()
+ }
+ }
+
+ return common.Hash{}
+ }
+}
+
+// CanTransfer checks wether there are enough funds in the address' account to make a transfer.
+// This does not take the necessary gas in to account to make the transfer valid.
+func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
+ return db.GetBalance(addr).Cmp(amount) >= 0
+}
+
+// Transfer subtracts amount from sender and adds amount to recipient using the given Db
+func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
+ db.SubBalance(sender, amount)
+ db.AddBalance(recipient, amount)
+}
diff --git a/core/execution.go b/core/execution.go
deleted file mode 100644
index cbc9dfd196..0000000000
--- a/core/execution.go
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2014 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 core
-
-import (
- "math/big"
-
- "github.com/ubiq/go-ubiq/common"
- "github.com/ubiq/go-ubiq/core/vm"
- "github.com/ubiq/go-ubiq/crypto"
- "github.com/ubiq/go-ubiq/params"
-)
-
-// Call executes within the given contract
-func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
- // Depth check execution. Fail if we're trying to execute above the
- // limit.
- if env.Depth() > int(params.CallCreateDepth.Int64()) {
- caller.ReturnGas(gas, gasPrice)
-
- return nil, vm.DepthError
- }
- if !env.CanTransfer(caller.Address(), value) {
- caller.ReturnGas(gas, gasPrice)
-
- return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
- }
-
- snapshotPreTransfer := env.SnapshotDatabase()
- var (
- from = env.Db().GetAccount(caller.Address())
- to vm.Account
- )
- if !env.Db().Exist(addr) {
- if vm.Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber()) && value.BitLen() == 0 {
- caller.ReturnGas(gas, gasPrice)
- return nil, nil
- }
-
- to = env.Db().CreateAccount(addr)
- } else {
- to = env.Db().GetAccount(addr)
- }
- env.Transfer(from, to, value)
-
- // initialise a new contract and set the code that is to be used by the
- // EVM. The contract is a scoped environment for this execution context
- // only.
- contract := vm.NewContract(caller, to, value, gas, gasPrice)
- contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
- defer contract.Finalise()
-
- ret, err = env.Vm().Run(contract, input)
- // When an error was returned by the EVM or when setting the creation code
- // above we revert to the snapshot and consume any gas remaining. Additionally
- // when we're in homestead this also counts for code storage gas errors.
- if err != nil {
- contract.UseGas(contract.Gas)
-
- env.RevertToSnapshot(snapshotPreTransfer)
- }
- return ret, err
-}
-
-// CallCode executes the given address' code as the given contract address
-func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
- // Depth check execution. Fail if we're trying to execute above the
- // limit.
- if env.Depth() > int(params.CallCreateDepth.Int64()) {
- caller.ReturnGas(gas, gasPrice)
-
- return nil, vm.DepthError
- }
- if !env.CanTransfer(caller.Address(), value) {
- caller.ReturnGas(gas, gasPrice)
-
- return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
- }
-
- var (
- snapshotPreTransfer = env.SnapshotDatabase()
- to = env.Db().GetAccount(caller.Address())
- )
- // initialise a new contract and set the code that is to be used by the
- // EVM. The contract is a scoped environment for this execution context
- // only.
- contract := vm.NewContract(caller, to, value, gas, gasPrice)
- contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
- defer contract.Finalise()
-
- ret, err = env.Vm().Run(contract, input)
- if err != nil {
- contract.UseGas(contract.Gas)
-
- env.RevertToSnapshot(snapshotPreTransfer)
- }
-
- return ret, err
-}
-
-// Create creates a new contract with the given code
-func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) {
- // Depth check execution. Fail if we're trying to execute above the
- // limit.
- if env.Depth() > int(params.CallCreateDepth.Int64()) {
- caller.ReturnGas(gas, gasPrice)
-
- return nil, common.Address{}, vm.DepthError
- }
- if !env.CanTransfer(caller.Address(), value) {
- caller.ReturnGas(gas, gasPrice)
-
- return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
- }
-
- // Create a new account on the state
- nonce := env.Db().GetNonce(caller.Address())
- env.Db().SetNonce(caller.Address(), nonce+1)
-
- snapshotPreTransfer := env.SnapshotDatabase()
- var (
- addr = crypto.CreateAddress(caller.Address(), nonce)
- from = env.Db().GetAccount(caller.Address())
- to = env.Db().CreateAccount(addr)
- )
- if env.ChainConfig().IsEIP158(env.BlockNumber()) {
- env.Db().SetNonce(addr, 1)
- }
- env.Transfer(from, to, value)
-
- // initialise a new contract and set the code that is to be used by the
- // EVM. The contract is a scoped environment for this execution context
- // only.
- contract := vm.NewContract(caller, to, value, gas, gasPrice)
- contract.SetCallCode(&addr, crypto.Keccak256Hash(code), code)
- defer contract.Finalise()
-
- ret, err = env.Vm().Run(contract, nil)
- // check whether the max code size has been exceeded
- maxCodeSizeExceeded := len(ret) > params.MaxCodeSize
- // if the contract creation ran successfully and no errors were returned
- // calculate the gas required to store the code. If the code could not
- // be stored due to not enough gas set an error and let it be handled
- // by the error checking condition below.
- if err == nil && !maxCodeSizeExceeded {
- dataGas := big.NewInt(int64(len(ret)))
- dataGas.Mul(dataGas, params.CreateDataGas)
- if contract.UseGas(dataGas) {
- env.Db().SetCode(addr, ret)
- } else {
- err = vm.CodeStoreOutOfGasError
- }
- }
-
- // When an error was returned by the EVM or when setting the creation code
- // above we revert to the snapshot and consume any gas remaining. Additionally
- // when we're in homestead this also counts for code storage gas errors.
- if maxCodeSizeExceeded ||
- (err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError)) {
- contract.UseGas(contract.Gas)
- env.RevertToSnapshot(snapshotPreTransfer)
-
- // Nothing should be returned when an error is thrown.
- return nil, addr, err
- }
-
- return ret, addr, err
-}
-
-// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope
-func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) {
- // Depth check execution. Fail if we're trying to execute above the
- // limit.
- if env.Depth() > int(params.CallCreateDepth.Int64()) {
- caller.ReturnGas(gas, gasPrice)
- return nil, vm.DepthError
- }
-
- var (
- snapshot = env.SnapshotDatabase()
- to = env.Db().GetAccount(caller.Address())
- )
-
- // Iinitialise a new contract and make initialise the delegate values
- contract := vm.NewContract(caller, to, caller.Value(), gas, gasPrice).AsDelegate()
- contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
- defer contract.Finalise()
-
- ret, err = env.Vm().Run(contract, input)
- if err != nil {
- contract.UseGas(contract.Gas)
-
- env.RevertToSnapshot(snapshot)
- }
-
- return ret, err
-}
-
-// generic transfer method
-func Transfer(from, to vm.Account, amount *big.Int) {
- from.SubBalance(amount)
- to.AddBalance(amount)
-}
diff --git a/core/genesis.go b/core/genesis.go
index 3c21a919f4..29d1c74b09 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -172,18 +172,12 @@ func WriteDefaultGenesisBlock(chainDb ethdb.Database) (*types.Block, error) {
return WriteGenesisBlock(chainDb, strings.NewReader(DefaultGenesisBlock()))
}
-// WriteTestNetGenesisBlock assembles the Morden test network genesis block and
+// WriteTestNetGenesisBlock assembles the test network genesis block and
// writes it - along with all associated state - into a chain database.
func WriteTestNetGenesisBlock(chainDb ethdb.Database) (*types.Block, error) {
return WriteGenesisBlock(chainDb, strings.NewReader(DefaultTestnetGenesisBlock()))
}
-// WriteOlympicGenesisBlock assembles the Olympic genesis block and writes it
-// along with all associated state into a chain database.
-func WriteOlympicGenesisBlock(db ethdb.Database) (*types.Block, error) {
- return WriteGenesisBlock(db, strings.NewReader(OlympicGenesisBlock()))
-}
-
// DefaultGenesisBlock assembles a JSON string representing the default Ethereum
// genesis block.
func DefaultGenesisBlock() string {
@@ -198,6 +192,8 @@ func DefaultGenesisBlock() string {
return string(blob)
}
+// DefaultTestnetGenesisBlock assembles a JSON string representing the default Ethereum
+// test network genesis block.
func DefaultTestnetGenesisBlock() string {
reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultTestnetGenesisBlock)))
blob, err := ioutil.ReadAll(reader)
@@ -207,26 +203,12 @@ func DefaultTestnetGenesisBlock() string {
return string(blob)
}
-// OlympicGenesisBlock assembles a JSON string representing the Olympic genesis
-// block.
-func OlympicGenesisBlock() string {
- return fmt.Sprintf(`{
- "nonce":"0x%x",
- "gasLimit":"0x%x",
- "difficulty":"0x%x",
- "alloc": {
- "0000000000000000000000000000000000000001": {"balance": "1"},
- "0000000000000000000000000000000000000002": {"balance": "1"},
- "0000000000000000000000000000000000000003": {"balance": "1"},
- "0000000000000000000000000000000000000004": {"balance": "1"},
- "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
- "e4157b34ea9615cfbde6b4fda419828124b70c78": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
- "b9c015918bdaba24b4ff057a92a3873d6eb201be": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
- "6c386a4b26f73c802f34673f7248bb118f97424a": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
- "cd2a3d9f938e13cd947ec05abc7fe734df8dd826": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
- "2ef47100e0787b915105fd5e3f4ff6752079d5cb": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
- "e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
- "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}
- }
- }`, types.EncodeNonce(42), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes())
+// DevGenesisBlock assembles a JSON string representing a local dev genesis block.
+func DevGenesisBlock() string {
+ reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultDevnetGenesisBlock)))
+ blob, err := ioutil.ReadAll(reader)
+ if err != nil {
+ panic(fmt.Sprintf("failed to load dev genesis: %v", err))
+ }
+ return string(blob)
}
diff --git a/core/headerchain.go b/core/headerchain.go
index 0c742780b8..2cc7bbce6a 100644
--- a/core/headerchain.go
+++ b/core/headerchain.go
@@ -225,6 +225,17 @@ type WhCallback func(*types.Header) error
// of the header retrieval mechanisms already need to verfy nonces, as well as
// because nonces can be verified sparsely, not needing to check each.
func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, writeHeader WhCallback) (int, error) {
+ // Do a sanity check that the provided chain is actually ordered and linked
+ for i := 1; i < len(chain); i++ {
+ if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 || chain[i].ParentHash != chain[i-1].Hash() {
+ // Chain broke ancestry, log a messge (programming error) and skip insertion
+ failure := fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])",
+ i-1, chain[i-1].Number.Uint64(), chain[i-1].Hash().Bytes()[:4], i, chain[i].Number.Uint64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash.Bytes()[:4])
+
+ glog.V(logger.Error).Info(failure.Error())
+ return 0, failure
+ }
+ }
// Collect some import statistics to report on
stats := struct{ processed, ignored int }{}
start := time.Now()
@@ -329,7 +340,7 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, w
if stats.ignored > 0 {
ignored = fmt.Sprintf(" (%d ignored)", stats.ignored)
}
- glog.V(logger.Info).Infof("imported %d headers%s in %9v. #%v [%x… / %x…]", stats.processed, ignored, common.PrettyDuration(time.Since(start)), last.Number, first.Hash().Bytes()[:4], last.Hash().Bytes()[:4])
+ glog.V(logger.Info).Infof("imported %4d headers%s in %9v. #%v [%x… / %x…]", stats.processed, ignored, common.PrettyDuration(time.Since(start)), last.Number, first.Hash().Bytes()[:4], last.Hash().Bytes()[:4])
return 0, nil
}
diff --git a/core/state/iterator.go b/core/state/iterator.go
index 69c8fe750a..2a79858b7f 100644
--- a/core/state/iterator.go
+++ b/core/state/iterator.go
@@ -123,7 +123,7 @@ func (it *NodeIterator) step() error {
if !it.dataIt.Next() {
it.dataIt = nil
}
- if bytes.Compare(account.CodeHash, emptyCodeHash) != 0 {
+ if !bytes.Equal(account.CodeHash, emptyCodeHash) {
it.codeHash = common.BytesToHash(account.CodeHash)
it.code, err = it.state.db.Get(account.CodeHash)
if err != nil {
diff --git a/core/state/iterator_test.go b/core/state/iterator_test.go
index aa09c3d4d8..30f7a826ea 100644
--- a/core/state/iterator_test.go
+++ b/core/state/iterator_test.go
@@ -41,7 +41,7 @@ func TestNodeIteratorCoverage(t *testing.T) {
}
}
// Cross check the hashes and the database itself
- for hash, _ := range hashes {
+ for hash := range hashes {
if _, err := db.Get(hash.Bytes()); err != nil {
t.Errorf("failed to retrieve reported node %x: %v", hash, err)
}
diff --git a/core/state/managed_state_test.go b/core/state/managed_state_test.go
index 40eadfece2..c2e8c3e72e 100644
--- a/core/state/managed_state_test.go
+++ b/core/state/managed_state_test.go
@@ -52,7 +52,7 @@ func TestRemove(t *testing.T) {
ms, account := create()
nn := make([]bool, 10)
- for i, _ := range nn {
+ for i := range nn {
nn[i] = true
}
account.nonces = append(account.nonces, nn...)
@@ -68,7 +68,7 @@ func TestReuse(t *testing.T) {
ms, account := create()
nn := make([]bool, 10)
- for i, _ := range nn {
+ for i := range nn {
nn[i] = true
}
account.nonces = append(account.nonces, nn...)
@@ -84,16 +84,16 @@ func TestReuse(t *testing.T) {
func TestRemoteNonceChange(t *testing.T) {
ms, account := create()
nn := make([]bool, 10)
- for i, _ := range nn {
+ for i := range nn {
nn[i] = true
}
account.nonces = append(account.nonces, nn...)
- nonce := ms.NewNonce(addr)
+ ms.NewNonce(addr)
ms.StateDB.stateObjects[addr].data.Nonce = 200
- nonce = ms.NewNonce(addr)
+ nonce := ms.NewNonce(addr)
if nonce != 200 {
- t.Error("expected nonce after remote update to be", 201, "got", nonce)
+ t.Error("expected nonce after remote update to be", 200, "got", nonce)
}
ms.NewNonce(addr)
ms.NewNonce(addr)
@@ -101,7 +101,7 @@ func TestRemoteNonceChange(t *testing.T) {
ms.StateDB.stateObjects[addr].data.Nonce = 200
nonce = ms.NewNonce(addr)
if nonce != 204 {
- t.Error("expected nonce after remote update to be", 201, "got", nonce)
+ t.Error("expected nonce after remote update to be", 204, "got", nonce)
}
}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index 3aab4740cc..9b5993008d 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -288,7 +288,7 @@ func (self *StateObject) setBalance(amount *big.Int) {
}
// Return the gas back to the origin. Used by the Virtual machine or Closures
-func (c *StateObject) ReturnGas(gas, price *big.Int) {}
+func (c *StateObject) ReturnGas(gas *big.Int) {}
func (self *StateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *StateObject {
stateObject := newObject(db, self.address, self.data, onDirty)
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 83b9138f4d..8828a78b58 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -23,7 +23,9 @@ import (
"sort"
"sync"
+ lru "github.com/hashicorp/golang-lru"
"github.com/ubiq/go-ubiq/common"
+ "github.com/ubiq/go-ubiq/core/types"
"github.com/ubiq/go-ubiq/core/vm"
"github.com/ubiq/go-ubiq/crypto"
"github.com/ubiq/go-ubiq/ethdb"
@@ -31,7 +33,6 @@ import (
"github.com/ubiq/go-ubiq/logger/glog"
"github.com/ubiq/go-ubiq/rlp"
"github.com/ubiq/go-ubiq/trie"
- lru "github.com/hashicorp/golang-lru"
)
// Trie cache generation limit after which to evic trie nodes from memory.
@@ -71,7 +72,7 @@ type StateDB struct {
thash, bhash common.Hash
txIndex int
- logs map[common.Hash]vm.Logs
+ logs map[common.Hash][]*types.Log
logSize uint
// Journal of state modifications. This is the backbone of
@@ -97,7 +98,7 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
stateObjects: make(map[common.Address]*StateObject),
stateObjectsDirty: make(map[common.Address]struct{}),
refund: new(big.Int),
- logs: make(map[common.Hash]vm.Logs),
+ logs: make(map[common.Hash][]*types.Log),
}, nil
}
@@ -118,7 +119,7 @@ func (self *StateDB) New(root common.Hash) (*StateDB, error) {
stateObjects: make(map[common.Address]*StateObject),
stateObjectsDirty: make(map[common.Address]struct{}),
refund: new(big.Int),
- logs: make(map[common.Hash]vm.Logs),
+ logs: make(map[common.Hash][]*types.Log),
}, nil
}
@@ -138,7 +139,7 @@ func (self *StateDB) Reset(root common.Hash) error {
self.thash = common.Hash{}
self.bhash = common.Hash{}
self.txIndex = 0
- self.logs = make(map[common.Hash]vm.Logs)
+ self.logs = make(map[common.Hash][]*types.Log)
self.logSize = 0
self.clearJournalAndRefund()
@@ -175,7 +176,7 @@ func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
self.txIndex = ti
}
-func (self *StateDB) AddLog(log *vm.Log) {
+func (self *StateDB) AddLog(log *types.Log) {
self.journal = append(self.journal, addLogChange{txhash: self.thash})
log.TxHash = self.thash
@@ -186,12 +187,12 @@ func (self *StateDB) AddLog(log *vm.Log) {
self.logSize++
}
-func (self *StateDB) GetLogs(hash common.Hash) vm.Logs {
+func (self *StateDB) GetLogs(hash common.Hash) []*types.Log {
return self.logs[hash]
}
-func (self *StateDB) Logs() vm.Logs {
- var logs vm.Logs
+func (self *StateDB) Logs() []*types.Log {
+ var logs []*types.Log
for _, lgs := range self.logs {
logs = append(logs, lgs...)
}
@@ -209,7 +210,7 @@ func (self *StateDB) Exist(addr common.Address) bool {
return self.GetStateObject(addr) != nil
}
-// Empty returns whether the state object is either non-existant
+// Empty returns whether the state object is either non-existent
// or empty according to the EIP161 specification (balance = nonce = code = 0)
func (self *StateDB) Empty(addr common.Address) bool {
so := self.GetStateObject(addr)
@@ -293,6 +294,7 @@ func (self *StateDB) HasSuicided(addr common.Address) bool {
* SETTERS
*/
+// AddBalance adds amount to the account associated with addr
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
@@ -300,6 +302,14 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
}
}
+// SubBalance subtracts amount from the account associated with addr
+func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SubBalance(amount)
+ }
+}
+
func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
@@ -465,16 +475,16 @@ func (self *StateDB) Copy() *StateDB {
stateObjects: make(map[common.Address]*StateObject, len(self.stateObjectsDirty)),
stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)),
refund: new(big.Int).Set(self.refund),
- logs: make(map[common.Hash]vm.Logs, len(self.logs)),
+ logs: make(map[common.Hash][]*types.Log, len(self.logs)),
logSize: self.logSize,
}
// Copy the dirty states and logs
- for addr, _ := range self.stateObjectsDirty {
+ for addr := range self.stateObjectsDirty {
state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty)
state.stateObjectsDirty[addr] = struct{}{}
}
for hash, logs := range self.logs {
- state.logs[hash] = make(vm.Logs, len(logs))
+ state.logs[hash] = make([]*types.Log, len(logs))
copy(state.logs[hash], logs)
}
return state
@@ -520,7 +530,7 @@ func (self *StateDB) GetRefund() *big.Int {
// It is called in between transactions to get the root hash that
// goes into transaction receipts.
func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
- for addr, _ := range s.stateObjectsDirty {
+ for addr := range s.stateObjectsDirty {
stateObject := s.stateObjects[addr]
if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) {
s.deleteStateObject(stateObject)
@@ -543,7 +553,7 @@ func (s *StateDB) DeleteSuicides() {
// Reset refund so that any used-gas calculations can use this method.
s.clearJournalAndRefund()
- for addr, _ := range s.stateObjectsDirty {
+ for addr := range s.stateObjectsDirty {
stateObject := s.stateObjects[addr]
// If the object has been removed by a suicide
diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go
index 4053e8d5e5..e5532dc07c 100644
--- a/core/state/statedb_test.go
+++ b/core/state/statedb_test.go
@@ -29,7 +29,7 @@ import (
"testing/quick"
"github.com/ubiq/go-ubiq/common"
- "github.com/ubiq/go-ubiq/core/vm"
+ "github.com/ubiq/go-ubiq/core/types"
"github.com/ubiq/go-ubiq/ethdb"
)
@@ -221,7 +221,7 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
fn: func(a testAction, s *StateDB) {
data := make([]byte, 2)
binary.BigEndian.PutUint16(data, uint16(a.args[0]))
- s.AddLog(&vm.Log{Address: addr, Data: data})
+ s.AddLog(&types.Log{Address: addr, Data: data})
},
args: make([]int64, 1),
},
diff --git a/core/state/sync.go b/core/state/sync.go
index 5da6f2b85f..d9a4d1a45f 100644
--- a/core/state/sync.go
+++ b/core/state/sync.go
@@ -21,7 +21,6 @@ import (
"math/big"
"github.com/ubiq/go-ubiq/common"
- "github.com/ubiq/go-ubiq/ethdb"
"github.com/ubiq/go-ubiq/rlp"
"github.com/ubiq/go-ubiq/trie"
)
@@ -32,7 +31,7 @@ import (
type StateSync trie.TrieSync
// NewStateSync create a new state trie download scheduler.
-func NewStateSync(root common.Hash, database ethdb.Database) *StateSync {
+func NewStateSync(root common.Hash, database trie.DatabaseReader) *StateSync {
var syncer *trie.TrieSync
callback := func(leaf []byte, parent common.Hash) error {
@@ -62,8 +61,8 @@ func (s *StateSync) Missing(max int) []common.Hash {
// Process injects a batch of retrieved trie nodes data, returning if something
// was committed to the database and also the index of an entry if processing of
// it failed.
-func (s *StateSync) Process(list []trie.SyncResult) (bool, int, error) {
- return (*trie.TrieSync)(s).Process(list)
+func (s *StateSync) Process(list []trie.SyncResult, dbw trie.DatabaseWriter) (bool, int, error) {
+ return (*trie.TrieSync)(s).Process(list, dbw)
}
// Pending returns the number of state entries currently pending for download.
diff --git a/core/state/sync_test.go b/core/state/sync_test.go
index f2b8855a2d..8cc96c27c1 100644
--- a/core/state/sync_test.go
+++ b/core/state/sync_test.go
@@ -84,7 +84,7 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accou
if nonce := state.GetNonce(acc.address); nonce != acc.nonce {
t.Errorf("account %d: nonce mismatch: have %v, want %v", i, nonce, acc.nonce)
}
- if code := state.GetCode(acc.address); bytes.Compare(code, acc.code) != 0 {
+ if code := state.GetCode(acc.address); !bytes.Equal(code, acc.code) {
t.Errorf("account %d: code mismatch: have %x, want %x", i, code, acc.code)
}
}
@@ -138,7 +138,7 @@ func testIterativeStateSync(t *testing.T, batch int) {
}
results[i] = trie.SyncResult{Hash: hash, Data: data}
}
- if _, index, err := sched.Process(results); err != nil {
+ if _, index, err := sched.Process(results, dstDb); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
}
queue = append(queue[:0], sched.Missing(batch)...)
@@ -168,7 +168,7 @@ func TestIterativeDelayedStateSync(t *testing.T) {
}
results[i] = trie.SyncResult{Hash: hash, Data: data}
}
- if _, index, err := sched.Process(results); err != nil {
+ if _, index, err := sched.Process(results, dstDb); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
}
queue = append(queue[len(results):], sched.Missing(0)...)
@@ -198,7 +198,7 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
for len(queue) > 0 {
// Fetch all the queued nodes in a random order
results := make([]trie.SyncResult, 0, len(queue))
- for hash, _ := range queue {
+ for hash := range queue {
data, err := srcDb.Get(hash.Bytes())
if err != nil {
t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
@@ -206,7 +206,7 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
results = append(results, trie.SyncResult{Hash: hash, Data: data})
}
// Feed the retrieved results back and queue new tasks
- if _, index, err := sched.Process(results); err != nil {
+ if _, index, err := sched.Process(results, dstDb); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
}
queue = make(map[common.Hash]struct{})
@@ -235,7 +235,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
for len(queue) > 0 {
// Sync only half of the scheduled nodes, even those in random order
results := make([]trie.SyncResult, 0, len(queue)/2+1)
- for hash, _ := range queue {
+ for hash := range queue {
delete(queue, hash)
data, err := srcDb.Get(hash.Bytes())
@@ -249,7 +249,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
}
}
// Feed the retrieved results back and queue new tasks
- if _, index, err := sched.Process(results); err != nil {
+ if _, index, err := sched.Process(results, dstDb); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
}
for _, hash := range sched.Missing(0) {
@@ -283,7 +283,7 @@ func TestIncompleteStateSync(t *testing.T) {
results[i] = trie.SyncResult{Hash: hash, Data: data}
}
// Process each of the state nodes
- if _, index, err := sched.Process(results); err != nil {
+ if _, index, err := sched.Process(results, dstDb); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
}
for _, result := range results {
@@ -294,7 +294,7 @@ func TestIncompleteStateSync(t *testing.T) {
// Skim through the accounts and make sure the root hash is not a code node
codeHash := false
for _, acc := range srcAccounts {
- if bytes.Compare(root.Bytes(), crypto.Sha3(acc.code)) == 0 {
+ if root == crypto.Keccak256Hash(acc.code) {
codeHash = true
break
}
diff --git a/core/state_processor.go b/core/state_processor.go
index 409a38d476..31a54b13cc 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -57,25 +57,25 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain) *StateProcess
// Process returns the receipts and logs accumulated during the process and
// returns the amount of gas that was used in the process. If any of the
// transactions failed to execute due to insufficient gas it will return an error.
-func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
+func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error) {
var (
receipts types.Receipts
totalUsedGas = big.NewInt(0)
err error
header = block.Header()
- allLogs vm.Logs
+ allLogs []*types.Log
gp = new(GasPool).AddGas(block.GasLimit())
)
// Iterate over and process the individual transactions
for i, tx := range block.Transactions() {
//fmt.Println("tx:", i)
statedb.StartRecord(tx.Hash(), block.Hash(), i)
- receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
+ receipt, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
if err != nil {
return nil, nil, nil, err
}
receipts = append(receipts, receipt)
- allLogs = append(allLogs, logs...)
+ allLogs = append(allLogs, receipt.Logs...)
}
AccumulateRewards(statedb, header, block.Uncles())
@@ -83,37 +83,44 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
}
// ApplyTransaction attempts to apply a transaction to the given state database
-// and uses the input parameters for its environment.
-//
-// ApplyTransactions returns the generated receipts and vm logs during the
-// execution of the state transition phase.
-func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
+// and uses the input parameters for its environment. It returns the receipt
+// for the transaction, gas used and an error if the transaction failed,
+// indicating the block was invalid.
+func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) {
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
if err != nil {
- return nil, nil, nil, err
+ return nil, nil, err
}
-
- _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, msg, header, cfg), msg, gp)
+ // Create a new context to be used in the EVM environment
+ context := NewEVMContext(msg, header, bc)
+ // Create a new environment which holds all relevant information
+ // about the transaction and calling mechanisms.
+ vmenv := vm.NewEVM(context, statedb, config, vm.Config{})
+ // Apply the transaction to the current state (included in the env)
+ _, gas, err := ApplyMessage(vmenv, msg, gp)
if err != nil {
- return nil, nil, nil, err
+ return nil, nil, err
}
// Update the state with pending changes
usedGas.Add(usedGas, gas)
+ // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
+ // based on the eip phase, we're passing wether the root touch-delete accounts.
receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes(), usedGas)
receipt.TxHash = tx.Hash()
receipt.GasUsed = new(big.Int).Set(gas)
- if MessageCreatesContract(msg) {
- receipt.ContractAddress = crypto.CreateAddress(msg.From(), tx.Nonce())
+ // if the transaction created a contract, store the creation address in the receipt.
+ if msg.To() == nil {
+ receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
}
- logs := statedb.GetLogs(tx.Hash())
- receipt.Logs = logs
+ // Set the receipt logs and create a bloom for filtering
+ receipt.Logs = statedb.GetLogs(tx.Hash())
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
glog.V(logger.Debug).Infoln(receipt)
- return receipt, logs, gas, err
+ return receipt, gas, err
}
// AccumulateRewards credits the coinbase of the given block with the
diff --git a/core/state_transition.go b/core/state_transition.go
index 6c7fa2a38c..5204aca203 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -55,9 +55,9 @@ type StateTransition struct {
initialGas *big.Int
value *big.Int
data []byte
- state vm.Database
+ state vm.StateDB
- env vm.Environment
+ env *vm.EVM
}
// Message represents a message sent to a contract.
@@ -106,7 +106,7 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
}
// NewStateTransition initialises and returns a new state transition object.
-func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTransition {
+func NewStateTransition(env *vm.EVM, msg Message, gp *GasPool) *StateTransition {
return &StateTransition{
gp: gp,
env: env,
@@ -116,7 +116,7 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran
initialGas: new(big.Int),
value: msg.Value(),
data: msg.Data(),
- state: env.Db(),
+ state: env.StateDB,
}
}
@@ -127,7 +127,7 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran
// the gas used (which includes gas refunds) and an error if it failed. An error always
// indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block.
-func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
+func ApplyMessage(env *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
st := NewStateTransition(env, msg, gp)
ret, _, gasUsed, err := st.TransitionDb()
@@ -159,7 +159,7 @@ func (self *StateTransition) to() vm.Account {
func (self *StateTransition) useGas(amount *big.Int) error {
if self.gas.Cmp(amount) < 0 {
- return vm.OutOfGasError
+ return vm.ErrOutOfGas
}
self.gas.Sub(self.gas, amount)
@@ -217,47 +217,44 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
msg := self.msg
sender := self.from() // err checked in preCheck
- homestead := self.env.ChainConfig().IsHomestead(self.env.BlockNumber())
+ homestead := self.env.ChainConfig().IsHomestead(self.env.BlockNumber)
contractCreation := MessageCreatesContract(msg)
// Pay intrinsic gas
if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil {
return nil, nil, nil, InvalidTxError(err)
}
- vmenv := self.env
- //var addr common.Address
+ var (
+ vmenv = self.env
+ // vm errors do not effect consensus and are therefor
+ // not assigned to err, except for insufficient balance
+ // error.
+ vmerr error
+ )
if contractCreation {
- ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
- if homestead && err == vm.CodeStoreOutOfGasError {
+ ret, _, vmerr = vmenv.Create(sender, self.data, self.gas, self.value)
+ if homestead && err == vm.ErrCodeStoreOutOfGas {
self.gas = Big0
}
-
- if err != nil {
- ret = nil
- glog.V(logger.Core).Infoln("VM create err:", err)
- }
} else {
// Increment the nonce for the next transaction
self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1)
- ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value)
- if err != nil {
- glog.V(logger.Core).Infoln("VM call err:", err)
+ ret, vmerr = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.value)
+ }
+ if vmerr != nil {
+ glog.V(logger.Core).Infoln("vm returned with error:", err)
+ // The only possible consensus-error would be if there wasn't
+ // sufficient balance to make the transfer happen. The first
+ // balance transfer may never fail.
+ if vmerr == vm.ErrInsufficientBalance {
+ return nil, nil, nil, InvalidTxError(vmerr)
}
}
- if err != nil && IsValueTransferErr(err) {
- return nil, nil, nil, InvalidTxError(err)
- }
-
- // We aren't interested in errors here. Errors returned by the VM are non-consensus errors and therefor shouldn't bubble up
- if err != nil {
- err = nil
- }
-
requiredGas = new(big.Int).Set(self.gasUsed())
self.refundGas()
- self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice))
+ self.state.AddBalance(self.env.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice))
return ret, requiredGas, self.gasUsed(), err
}
diff --git a/core/tx_list.go b/core/tx_list.go
index 24b3e32132..88a2d3c176 100644
--- a/core/tx_list.go
+++ b/core/tx_list.go
@@ -110,7 +110,7 @@ func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transac
// If transactions were removed, the heap and cache are ruined
if len(removed) > 0 {
*m.index = make([]uint64, 0, len(m.items))
- for nonce, _ := range m.items {
+ for nonce := range m.items {
*m.index = append(*m.index, nonce)
}
heap.Init(m.index)
@@ -216,7 +216,7 @@ func (m *txSortedMap) Flatten() types.Transactions {
// txList is a "list" of transactions belonging to an account, sorted by account
// nonce. The same type can be used both for storing contiguous transactions for
// the executable/pending queue; and for storing gapped transactions for the non-
-// executable/future queue, with minor behavoiral changes.
+// executable/future queue, with minor behavioral changes.
type txList struct {
strict bool // Whether nonces are strictly continuous or not
txs *txSortedMap // Heap indexed sorted hash map of the transactions
diff --git a/core/tx_pool.go b/core/tx_pool.go
index 666835528f..b6a40e3cff 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -37,15 +37,14 @@ import (
var (
// Transaction Pool Errors
- ErrInvalidSender = errors.New("Invalid sender")
- ErrNonce = errors.New("Nonce too low")
- ErrCheap = errors.New("Gas price too low for acceptance")
- ErrBalance = errors.New("Insufficient balance")
- ErrNonExistentAccount = errors.New("Account does not exist or account balance too low")
- ErrInsufficientFunds = errors.New("Insufficient funds for gas * price + value")
- ErrIntrinsicGas = errors.New("Intrinsic gas too low")
- ErrGasLimit = errors.New("Exceeds block gas limit")
- ErrNegativeValue = errors.New("Negative value")
+ ErrInvalidSender = errors.New("Invalid sender")
+ ErrNonce = errors.New("Nonce too low")
+ ErrCheap = errors.New("Gas price too low for acceptance")
+ ErrBalance = errors.New("Insufficient balance")
+ ErrInsufficientFunds = errors.New("Insufficient funds for gas * price + value")
+ ErrIntrinsicGas = errors.New("Intrinsic gas too low")
+ ErrGasLimit = errors.New("Exceeds block gas limit")
+ ErrNegativeValue = errors.New("Negative value")
)
var (
@@ -124,6 +123,8 @@ func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentState
quit: make(chan struct{}),
}
+ pool.resetState()
+
pool.wg.Add(2)
go pool.eventLoop()
go pool.expirationLoop()
@@ -176,7 +177,7 @@ func (pool *TxPool) resetState() {
// any transactions that have been included in the block or
// have been invalidated because of another transaction (e.g.
// higher gas price)
- pool.demoteUnexecutables()
+ pool.demoteUnexecutables(currentState)
// Update all accounts to the latest known pending nonce
for addr, list := range pool.pending {
@@ -185,7 +186,7 @@ func (pool *TxPool) resetState() {
}
// Check the queue and move transactions over to the pending if possible
// or remove those that have become invalid
- pool.promoteExecutables()
+ pool.promoteExecutables(currentState)
}
func (pool *TxPool) Stop() {
@@ -237,21 +238,26 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common
// Pending retrieves all currently processable transactions, groupped by origin
// account and sorted by nonce. The returned transaction set is a copy and can be
// freely modified by calling code.
-func (pool *TxPool) Pending() map[common.Address]types.Transactions {
+func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) {
pool.mu.Lock()
defer pool.mu.Unlock()
+ state, err := pool.currentState()
+ if err != nil {
+ return nil, err
+ }
+
// check queue first
- pool.promoteExecutables()
+ pool.promoteExecutables(state)
// invalidate any txs
- pool.demoteUnexecutables()
+ pool.demoteUnexecutables(state)
pending := make(map[common.Address]types.Transactions)
for addr, list := range pool.pending {
pending[addr] = list.Flatten()
}
- return pending
+ return pending, nil
}
// SetLocal marks a transaction as local, skipping gas price
@@ -280,13 +286,6 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
if err != nil {
return ErrInvalidSender
}
-
- // Make sure the account exist. Non existent accounts
- // haven't got funds and well therefor never pass.
- if !currentState.Exist(from) {
- return ErrNonExistentAccount
- }
-
// Last but not least check for nonce errors
if currentState.GetNonce(from) > tx.Nonce() {
return ErrNonce
@@ -322,7 +321,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
// add validates a transaction and inserts it into the non-executable queue for
// later pending promotion and execution.
func (pool *TxPool) add(tx *types.Transaction) error {
- // If the transaction is alreayd known, discard it
+ // If the transaction is already known, discard it
hash := tx.Hash()
if pool.all[hash] != nil {
return fmt.Errorf("Known transaction: %x", hash[:4])
@@ -372,10 +371,6 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) {
//
// Note, this method assumes the pool lock is held!
func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) {
- // Init delayed since tx pool could have been started before any state sync
- if pool.pendingState == nil {
- pool.resetState()
- }
// Try to insert the transaction into the pending queue
if pool.pending[addr] == nil {
pool.pending[addr] = newTxList(true)
@@ -410,13 +405,19 @@ func (pool *TxPool) Add(tx *types.Transaction) error {
if err := pool.add(tx); err != nil {
return err
}
- pool.promoteExecutables()
+
+ state, err := pool.currentState()
+ if err != nil {
+ return err
+ }
+
+ pool.promoteExecutables(state)
return nil
}
// AddBatch attempts to queue a batch of transactions.
-func (pool *TxPool) AddBatch(txs []*types.Transaction) {
+func (pool *TxPool) AddBatch(txs []*types.Transaction) error {
pool.mu.Lock()
defer pool.mu.Unlock()
@@ -425,7 +426,15 @@ func (pool *TxPool) AddBatch(txs []*types.Transaction) {
glog.V(logger.Debug).Infoln("tx error:", err)
}
}
- pool.promoteExecutables()
+
+ state, err := pool.currentState()
+ if err != nil {
+ return err
+ }
+
+ pool.promoteExecutables(state)
+
+ return nil
}
// Get returns a transaction if it is contained in the pool
@@ -499,17 +508,7 @@ func (pool *TxPool) removeTx(hash common.Hash) {
// promoteExecutables moves transactions that have become processable from the
// future queue to the set of pending transactions. During this process, all
// invalidated transactions (low nonce, low balance) are deleted.
-func (pool *TxPool) promoteExecutables() {
- // Init delayed since tx pool could have been started before any state sync
- if pool.pendingState == nil {
- pool.resetState()
- }
- // Retrieve the current state to allow nonce and balance checking
- state, err := pool.currentState()
- if err != nil {
- glog.Errorf("Could not get current state: %v", err)
- return
- }
+func (pool *TxPool) promoteExecutables(state *state.StateDB) {
// Iterate over all accounts and promote any executable transactions
queued := uint64(0)
for addr, list := range pool.queue {
@@ -610,7 +609,7 @@ func (pool *TxPool) promoteExecutables() {
if queued > maxQueuedInTotal {
// Sort all accounts with queued transactions by heartbeat
addresses := make(addresssByHeartbeat, 0, len(pool.queue))
- for addr, _ := range pool.queue {
+ for addr := range pool.queue {
addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]})
}
sort.Sort(addresses)
@@ -645,13 +644,7 @@ func (pool *TxPool) promoteExecutables() {
// demoteUnexecutables removes invalid and processed transactions from the pools
// executable/pending queue and any subsequent transactions that become unexecutable
// are moved back into the future queue.
-func (pool *TxPool) demoteUnexecutables() {
- // Retrieve the current state to allow nonce and balance checking
- state, err := pool.currentState()
- if err != nil {
- glog.V(logger.Info).Infoln("failed to get current state: %v", err)
- return
- }
+func (pool *TxPool) demoteUnexecutables(state *state.StateDB) {
// Iterate over all accounts and demote any non-executable transactions
for addr, list := range pool.pending {
nonce := state.GetNonce(addr)
diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go
index 4d10b41941..d28d620bdf 100644
--- a/core/tx_pool_test.go
+++ b/core/tx_pool_test.go
@@ -32,7 +32,7 @@ import (
)
func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
- tx, _ := types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, big.NewInt(1), nil).SignECDSA(types.HomesteadSigner{}, key)
+ tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, big.NewInt(1), nil), types.HomesteadSigner{}, key)
return tx
}
@@ -51,14 +51,84 @@ func deriveSender(tx *types.Transaction) (common.Address, error) {
return types.Sender(types.HomesteadSigner{}, tx)
}
+// This test simulates a scenario where a new block is imported during a
+// state reset and tests whether the pending state is in sync with the
+// block head event that initiated the resetState().
+func TestStateChangeDuringPoolReset(t *testing.T) {
+ var (
+ db, _ = ethdb.NewMemDatabase()
+ key, _ = crypto.GenerateKey()
+ address = crypto.PubkeyToAddress(key.PublicKey)
+ mux = new(event.TypeMux)
+ statedb, _ = state.New(common.Hash{}, db)
+ trigger = false
+ )
+
+ // setup pool with 2 transaction in it
+ statedb.SetBalance(address, new(big.Int).Mul(common.Big1, common.Ether))
+
+ tx0 := transaction(0, big.NewInt(100000), key)
+ tx1 := transaction(1, big.NewInt(100000), key)
+
+ // stateFunc is used multiple times to reset the pending state.
+ // when simulate is true it will create a state that indicates
+ // that tx0 and tx1 are included in the chain.
+ stateFunc := func() (*state.StateDB, error) {
+ // delay "state change" by one. The tx pool fetches the
+ // state multiple times and by delaying it a bit we simulate
+ // a state change between those fetches.
+ stdb := statedb
+ if trigger {
+ statedb, _ = state.New(common.Hash{}, db)
+ // simulate that the new head block included tx0 and tx1
+ statedb.SetNonce(address, 2)
+ statedb.SetBalance(address, new(big.Int).Mul(common.Big1, common.Ether))
+ trigger = false
+ }
+ return stdb, nil
+ }
+
+ gasLimitFunc := func() *big.Int { return big.NewInt(1000000000) }
+
+ txpool := NewTxPool(testChainConfig(), mux, stateFunc, gasLimitFunc)
+ txpool.resetState()
+
+ nonce := txpool.State().GetNonce(address)
+ if nonce != 0 {
+ t.Fatalf("Invalid nonce, want 0, got %d", nonce)
+ }
+
+ txpool.AddBatch(types.Transactions{tx0, tx1})
+
+ nonce = txpool.State().GetNonce(address)
+ if nonce != 2 {
+ t.Fatalf("Invalid nonce, want 2, got %d", nonce)
+ }
+
+ // trigger state change in the background
+ trigger = true
+
+ txpool.resetState()
+
+ pendingTx, err := txpool.Pending()
+ if err != nil {
+ t.Fatalf("Could not fetch pending transactions: %v", err)
+ }
+
+ for addr, txs := range pendingTx {
+ t.Logf("%0x: %d\n", addr, len(txs))
+ }
+
+ nonce = txpool.State().GetNonce(address)
+ if nonce != 2 {
+ t.Fatalf("Invalid nonce, want 2, got %d", nonce)
+ }
+}
+
func TestInvalidTransactions(t *testing.T) {
pool, key := setupTxPool()
tx := transaction(0, big.NewInt(100), key)
- if err := pool.Add(tx); err != ErrNonExistentAccount {
- t.Error("expected", ErrNonExistentAccount)
- }
-
from, _ := deriveSender(tx)
currentState, _ := pool.currentState()
currentState.AddBalance(from, big.NewInt(1))
@@ -97,9 +167,10 @@ func TestTransactionQueue(t *testing.T) {
from, _ := deriveSender(tx)
currentState, _ := pool.currentState()
currentState.AddBalance(from, big.NewInt(1000))
+ pool.resetState()
pool.enqueueTx(tx.Hash(), tx)
- pool.promoteExecutables()
+ pool.promoteExecutables(currentState)
if len(pool.pending) != 1 {
t.Error("expected valid txs to be 1 is", len(pool.pending))
}
@@ -108,7 +179,7 @@ func TestTransactionQueue(t *testing.T) {
from, _ = deriveSender(tx)
currentState.SetNonce(from, 2)
pool.enqueueTx(tx.Hash(), tx)
- pool.promoteExecutables()
+ pool.promoteExecutables(currentState)
if _, ok := pool.pending[from].txs.items[tx.Nonce()]; ok {
t.Error("expected transaction to be in tx pool")
}
@@ -124,11 +195,13 @@ func TestTransactionQueue(t *testing.T) {
from, _ = deriveSender(tx1)
currentState, _ = pool.currentState()
currentState.AddBalance(from, big.NewInt(1000))
+ pool.resetState()
+
pool.enqueueTx(tx1.Hash(), tx1)
pool.enqueueTx(tx2.Hash(), tx2)
pool.enqueueTx(tx3.Hash(), tx3)
- pool.promoteExecutables()
+ pool.promoteExecutables(currentState)
if len(pool.pending) != 1 {
t.Error("expected tx pool to be 1, got", len(pool.pending))
@@ -165,7 +238,7 @@ func TestRemoveTx(t *testing.T) {
func TestNegativeValue(t *testing.T) {
pool, key := setupTxPool()
- tx, _ := types.NewTransaction(0, common.Address{}, big.NewInt(-1), big.NewInt(100), big.NewInt(1), nil).SignECDSA(types.HomesteadSigner{}, key)
+ tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), big.NewInt(100), big.NewInt(1), nil), types.HomesteadSigner{}, key)
from, _ := deriveSender(tx)
currentState, _ := pool.currentState()
currentState.AddBalance(from, big.NewInt(1))
@@ -214,9 +287,9 @@ func TestTransactionDoubleNonce(t *testing.T) {
resetState()
signer := types.HomesteadSigner{}
- tx1, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(100000), big.NewInt(1), nil).SignECDSA(signer, key)
- tx2, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(2), nil).SignECDSA(signer, key)
- tx3, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(1), nil).SignECDSA(signer, key)
+ tx1, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(100000), big.NewInt(1), nil), signer, key)
+ tx2, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(2), nil), signer, key)
+ tx3, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(1), nil), signer, key)
// Add the first two transaction, ensure higher priced stays only
if err := pool.add(tx1); err != nil {
@@ -225,7 +298,8 @@ func TestTransactionDoubleNonce(t *testing.T) {
if err := pool.add(tx2); err != nil {
t.Error("didn't expect error", err)
}
- pool.promoteExecutables()
+ state, _ := pool.currentState()
+ pool.promoteExecutables(state)
if pool.pending[addr].Len() != 1 {
t.Error("expected 1 pending transactions, got", pool.pending[addr].Len())
}
@@ -236,7 +310,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
if err := pool.add(tx3); err != nil {
t.Error("didn't expect error", err)
}
- pool.promoteExecutables()
+ pool.promoteExecutables(state)
if pool.pending[addr].Len() != 1 {
t.Error("expected 1 pending transactions, got", pool.pending[addr].Len())
}
@@ -295,6 +369,7 @@ func TestRemovedTxEvent(t *testing.T) {
from, _ := deriveSender(tx)
currentState, _ := pool.currentState()
currentState.AddBalance(from, big.NewInt(1000000000000))
+ pool.resetState()
pool.eventMux.Post(RemovedTransactionEvent{types.Transactions{tx}})
pool.eventMux.Post(ChainHeadEvent{nil})
if pool.pending[from].Len() != 1 {
@@ -452,6 +527,7 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000000))
+ pool.resetState()
// Keep queuing up transactions and make sure all above a limit are dropped
for i := uint64(1); i <= maxQueuedPerAccount+5; i++ {
@@ -564,6 +640,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000000))
+ pool.resetState()
// Keep queuing up transactions and make sure all above a limit are dropped
for i := uint64(0); i < maxQueuedPerAccount+5; i++ {
@@ -733,7 +810,7 @@ func benchmarkPendingDemotion(b *testing.B, size int) {
// Benchmark the speed of pool validation
b.ResetTimer()
for i := 0; i < b.N; i++ {
- pool.demoteUnexecutables()
+ pool.demoteUnexecutables(state)
}
}
@@ -757,7 +834,7 @@ func benchmarkFuturePromotion(b *testing.B, size int) {
// Benchmark the speed of pool validation
b.ResetTimer()
for i := 0; i < b.N; i++ {
- pool.promoteExecutables()
+ pool.promoteExecutables(state)
}
}
diff --git a/core/types.go b/core/types.go
index a6efaefe99..bf9d27ddc7 100644
--- a/core/types.go
+++ b/core/types.go
@@ -58,5 +58,5 @@ type HeaderValidator interface {
// of gas used in the process and return an error if any of the internal rules
// failed.
type Processor interface {
- Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error)
+ Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error)
}
diff --git a/core/types/block.go b/core/types/block.go
index c185ebf14b..9ead628441 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -423,9 +423,9 @@ func CalcUncleHash(uncles []*Header) common.Hash {
// WithMiningResult returns a new block with the data from b
// where nonce and mix digest are set to the provided values.
-func (b *Block) WithMiningResult(nonce uint64, mixDigest common.Hash) *Block {
+func (b *Block) WithMiningResult(nonce BlockNonce, mixDigest common.Hash) *Block {
cpy := *b.header
- binary.BigEndian.PutUint64(cpy.Nonce[:], nonce)
+ cpy.Nonce = nonce
cpy.MixDigest = mixDigest
return &Block{
header: &cpy,
diff --git a/core/types/block_test.go b/core/types/block_test.go
index 1b5cbc4337..8aab6bb7e9 100644
--- a/core/types/block_test.go
+++ b/core/types/block_test.go
@@ -53,7 +53,7 @@ func TestBlockEncoding(t *testing.T) {
tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil)
- tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b11b"))
+ tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
fmt.Println(block.Transactions()[0].Hash())
fmt.Println(tx1.data)
fmt.Println(tx1.Hash())
diff --git a/core/types/bloom9.go b/core/types/bloom9.go
index 57fdefddd5..67f2d82a60 100644
--- a/core/types/bloom9.go
+++ b/core/types/bloom9.go
@@ -22,7 +22,6 @@ import (
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/common/hexutil"
- "github.com/ubiq/go-ubiq/core/vm"
"github.com/ubiq/go-ubiq/crypto"
)
@@ -95,17 +94,11 @@ func CreateBloom(receipts Receipts) Bloom {
return BytesToBloom(bin.Bytes())
}
-func LogsBloom(logs vm.Logs) *big.Int {
+func LogsBloom(logs []*Log) *big.Int {
bin := new(big.Int)
for _, log := range logs {
- data := make([]common.Hash, len(log.Topics))
bin.Or(bin, bloom9(log.Address.Bytes()))
-
- for i, topic := range log.Topics {
- data[i] = topic
- }
-
- for _, b := range data {
+ for _, b := range log.Topics {
bin.Or(bin, bloom9(b[:]))
}
}
diff --git a/core/types/log.go b/core/types/log.go
new file mode 100644
index 0000000000..c8e9b9c5c8
--- /dev/null
+++ b/core/types/log.go
@@ -0,0 +1,184 @@
+// Copyright 2014 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 types
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+
+ "github.com/ubiq/go-ubiq/common"
+ "github.com/ubiq/go-ubiq/common/hexutil"
+ "github.com/ubiq/go-ubiq/rlp"
+)
+
+var errMissingLogFields = errors.New("missing required JSON log fields")
+
+// Log represents a contract log event. These events are generated by the LOG opcode and
+// stored/indexed by the node.
+type Log struct {
+ // Consensus fields.
+ Address common.Address // address of the contract that generated the event
+ Topics []common.Hash // list of topics provided by the contract.
+ Data []byte // supplied by the contract, usually ABI-encoded
+
+ // Derived fields. These fields are filled in by the node
+ // but not secured by consensus.
+ BlockNumber uint64 // block in which the transaction was included
+ TxHash common.Hash // hash of the transaction
+ TxIndex uint // index of the transaction in the block
+ BlockHash common.Hash // hash of the block in which the transaction was included
+ Index uint // index of the log in the receipt
+
+ // The Removed field is true if this log was reverted due to a chain reorganisation.
+ // You must pay attention to this field if you receive logs through a filter query.
+ Removed bool
+}
+
+type rlpLog struct {
+ Address common.Address
+ Topics []common.Hash
+ Data []byte
+}
+
+type rlpStorageLog struct {
+ Address common.Address
+ Topics []common.Hash
+ Data []byte
+ BlockNumber uint64
+ TxHash common.Hash
+ TxIndex uint
+ BlockHash common.Hash
+ Index uint
+}
+
+type jsonLog struct {
+ Address *common.Address `json:"address"`
+ Topics *[]common.Hash `json:"topics"`
+ Data *hexutil.Bytes `json:"data"`
+ BlockNumber *hexutil.Uint64 `json:"blockNumber"`
+ TxIndex *hexutil.Uint `json:"transactionIndex"`
+ TxHash *common.Hash `json:"transactionHash"`
+ BlockHash *common.Hash `json:"blockHash"`
+ Index *hexutil.Uint `json:"logIndex"`
+ Removed bool `json:"removed"`
+}
+
+// EncodeRLP implements rlp.Encoder.
+func (l *Log) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data})
+}
+
+// DecodeRLP implements rlp.Decoder.
+func (l *Log) DecodeRLP(s *rlp.Stream) error {
+ var dec rlpLog
+ err := s.Decode(&dec)
+ if err == nil {
+ l.Address, l.Topics, l.Data = dec.Address, dec.Topics, dec.Data
+ }
+ return err
+}
+
+func (l *Log) String() string {
+ return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index)
+}
+
+// MarshalJSON implements json.Marshaler.
+func (l *Log) MarshalJSON() ([]byte, error) {
+ jslog := &jsonLog{
+ Address: &l.Address,
+ Topics: &l.Topics,
+ Data: (*hexutil.Bytes)(&l.Data),
+ TxIndex: (*hexutil.Uint)(&l.TxIndex),
+ TxHash: &l.TxHash,
+ Index: (*hexutil.Uint)(&l.Index),
+ Removed: l.Removed,
+ }
+ // Set block information for mined logs.
+ if (l.BlockHash != common.Hash{}) {
+ jslog.BlockHash = &l.BlockHash
+ jslog.BlockNumber = (*hexutil.Uint64)(&l.BlockNumber)
+ }
+ return json.Marshal(jslog)
+}
+
+// UnmarshalJSON implements json.Umarshaler.
+func (l *Log) UnmarshalJSON(input []byte) error {
+ var dec jsonLog
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Address == nil || dec.Topics == nil || dec.Data == nil ||
+ dec.TxIndex == nil || dec.TxHash == nil || dec.Index == nil {
+ return errMissingLogFields
+ }
+ declog := Log{
+ Address: *dec.Address,
+ Topics: *dec.Topics,
+ Data: *dec.Data,
+ TxHash: *dec.TxHash,
+ TxIndex: uint(*dec.TxIndex),
+ Index: uint(*dec.Index),
+ Removed: dec.Removed,
+ }
+ // Block information may be missing if the log is received through
+ // the pending log filter, so it's handled specially here.
+ if dec.BlockHash != nil && dec.BlockNumber != nil {
+ declog.BlockHash = *dec.BlockHash
+ declog.BlockNumber = uint64(*dec.BlockNumber)
+ }
+ *l = declog
+ return nil
+}
+
+// LogForStorage is a wrapper around a Log that flattens and parses the entire content of
+// a log including non-consensus fields.
+type LogForStorage Log
+
+// EncodeRLP implements rlp.Encoder.
+func (l *LogForStorage) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, rlpStorageLog{
+ Address: l.Address,
+ Topics: l.Topics,
+ Data: l.Data,
+ BlockNumber: l.BlockNumber,
+ TxHash: l.TxHash,
+ TxIndex: l.TxIndex,
+ BlockHash: l.BlockHash,
+ Index: l.Index,
+ })
+}
+
+// DecodeRLP implements rlp.Decoder.
+func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error {
+ var dec rlpStorageLog
+ err := s.Decode(&dec)
+ if err == nil {
+ *l = LogForStorage{
+ Address: dec.Address,
+ Topics: dec.Topics,
+ Data: dec.Data,
+ BlockNumber: dec.BlockNumber,
+ TxHash: dec.TxHash,
+ TxIndex: dec.TxIndex,
+ BlockHash: dec.BlockHash,
+ Index: dec.Index,
+ }
+ }
+ return err
+}
diff --git a/core/types/log_test.go b/core/types/log_test.go
new file mode 100644
index 0000000000..97bb7a3831
--- /dev/null
+++ b/core/types/log_test.go
@@ -0,0 +1,131 @@
+// 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 types
+
+import (
+ "encoding/json"
+ "reflect"
+ "testing"
+
+ "github.com/davecgh/go-spew/spew"
+ "github.com/ubiq/go-ubiq/common"
+ "github.com/ubiq/go-ubiq/common/hexutil"
+)
+
+var unmarshalLogTests = map[string]struct {
+ input string
+ want *Log
+ wantError error
+}{
+ "ok": {
+ input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x000000000000000000000000000000000000000000000001a055690d9db80000","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
+ want: &Log{
+ Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"),
+ BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"),
+ BlockNumber: 2019236,
+ Data: hexutil.MustDecode("0x000000000000000000000000000000000000000000000001a055690d9db80000"),
+ Index: 2,
+ TxIndex: 3,
+ TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"),
+ Topics: []common.Hash{
+ common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
+ common.HexToHash("0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"),
+ },
+ },
+ },
+ "empty data": {
+ input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
+ want: &Log{
+ Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"),
+ BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"),
+ BlockNumber: 2019236,
+ Data: []byte{},
+ Index: 2,
+ TxIndex: 3,
+ TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"),
+ Topics: []common.Hash{
+ common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
+ common.HexToHash("0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"),
+ },
+ },
+ },
+ "missing block fields (pending logs)": {
+ input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","data":"0x","logIndex":"0x0","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
+ want: &Log{
+ Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"),
+ BlockHash: common.Hash{},
+ BlockNumber: 0,
+ Data: []byte{},
+ Index: 0,
+ TxIndex: 3,
+ TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"),
+ Topics: []common.Hash{
+ common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
+ },
+ },
+ },
+ "Removed: true": {
+ input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3","removed":true}`,
+ want: &Log{
+ Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"),
+ BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"),
+ BlockNumber: 2019236,
+ Data: []byte{},
+ Index: 2,
+ TxIndex: 3,
+ TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"),
+ Topics: []common.Hash{
+ common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
+ },
+ Removed: true,
+ },
+ },
+ "missing data": {
+ input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
+ wantError: errMissingLogFields,
+ },
+}
+
+func TestUnmarshalLog(t *testing.T) {
+ dumper := spew.ConfigState{DisableMethods: true, Indent: " "}
+ for name, test := range unmarshalLogTests {
+ var log *Log
+ err := json.Unmarshal([]byte(test.input), &log)
+ checkError(t, name, err, test.wantError)
+ if test.wantError == nil && err == nil {
+ if !reflect.DeepEqual(log, test.want) {
+ t.Errorf("test %q:\nGOT %sWANT %s", name, dumper.Sdump(log), dumper.Sdump(test.want))
+ }
+ }
+ }
+}
+
+func checkError(t *testing.T, testname string, got, want error) bool {
+ if got == nil {
+ if want != nil {
+ t.Errorf("test %q: got no error, want %q", testname, want)
+ return false
+ }
+ return true
+ }
+ if want == nil {
+ t.Errorf("test %q: unexpected error %q", testname, got)
+ } else if got.Error() != want.Error() {
+ t.Errorf("test %q: got error %q, want %q", testname, got, want)
+ }
+ return false
+}
diff --git a/core/types/receipt.go b/core/types/receipt.go
index 67018d7c43..572c226547 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -25,7 +25,6 @@ import (
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/common/hexutil"
- "github.com/ubiq/go-ubiq/core/vm"
"github.com/ubiq/go-ubiq/rlp"
)
@@ -40,7 +39,7 @@ type Receipt struct {
PostState []byte
CumulativeGasUsed *big.Int
Bloom Bloom
- Logs vm.Logs
+ Logs []*Log
// Implementation fields (don't reorder!)
TxHash common.Hash
@@ -52,7 +51,7 @@ type jsonReceipt struct {
PostState *common.Hash `json:"root"`
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed"`
Bloom *Bloom `json:"logsBloom"`
- Logs *vm.Logs `json:"logs"`
+ Logs []*Log `json:"logs"`
TxHash *common.Hash `json:"transactionHash"`
ContractAddress *common.Address `json:"contractAddress"`
GasUsed *hexutil.Big `json:"gasUsed"`
@@ -76,7 +75,7 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
PostState []byte
CumulativeGasUsed *big.Int
Bloom Bloom
- Logs vm.Logs
+ Logs []*Log
}
if err := s.Decode(&receipt); err != nil {
return err
@@ -93,7 +92,7 @@ func (r *Receipt) MarshalJSON() ([]byte, error) {
PostState: &root,
CumulativeGasUsed: (*hexutil.Big)(r.CumulativeGasUsed),
Bloom: &r.Bloom,
- Logs: &r.Logs,
+ Logs: r.Logs,
TxHash: &r.TxHash,
ContractAddress: &r.ContractAddress,
GasUsed: (*hexutil.Big)(r.GasUsed),
@@ -120,7 +119,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
PostState: (*dec.PostState)[:],
CumulativeGasUsed: (*big.Int)(dec.CumulativeGasUsed),
Bloom: *dec.Bloom,
- Logs: *dec.Logs,
+ Logs: dec.Logs,
TxHash: *dec.TxHash,
GasUsed: (*big.Int)(dec.GasUsed),
}
@@ -142,9 +141,9 @@ type ReceiptForStorage Receipt
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
// into an RLP stream.
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
- logs := make([]*vm.LogForStorage, len(r.Logs))
+ logs := make([]*LogForStorage, len(r.Logs))
for i, log := range r.Logs {
- logs[i] = (*vm.LogForStorage)(log)
+ logs[i] = (*LogForStorage)(log)
}
return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed})
}
@@ -158,7 +157,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
Bloom Bloom
TxHash common.Hash
ContractAddress common.Address
- Logs []*vm.LogForStorage
+ Logs []*LogForStorage
GasUsed *big.Int
}
if err := s.Decode(&receipt); err != nil {
@@ -166,9 +165,9 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
}
// Assign the consensus fields
r.PostState, r.CumulativeGasUsed, r.Bloom = receipt.PostState, receipt.CumulativeGasUsed, receipt.Bloom
- r.Logs = make(vm.Logs, len(receipt.Logs))
+ r.Logs = make([]*Log, len(receipt.Logs))
for i, log := range receipt.Logs {
- r.Logs[i] = (*vm.Log)(log)
+ r.Logs[i] = (*Log)(log)
}
// Assign the implementation fields
r.TxHash, r.ContractAddress, r.GasUsed = receipt.TxHash, receipt.ContractAddress, receipt.GasUsed
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 8cfa00f335..7de98045d2 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -18,7 +18,6 @@ package types
import (
"container/heap"
- "crypto/ecdsa"
"encoding/json"
"errors"
"fmt"
@@ -199,9 +198,9 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
var V byte
if isProtectedV((*big.Int)(dec.V)) {
- V = normaliseV(NewEIP155Signer(deriveChainId((*big.Int)(dec.V))), (*big.Int)(dec.V))
+ V = byte((new(big.Int).Sub((*big.Int)(dec.V), deriveChainId((*big.Int)(dec.V))).Uint64()) - 35)
} else {
- V = byte(((*big.Int)(dec.V)).Uint64())
+ V = byte(((*big.Int)(dec.V)).Uint64() - 27)
}
if !crypto.ValidateSignatureValues(V, (*big.Int)(dec.R), (*big.Int)(dec.S), false) {
return ErrInvalidSig
@@ -272,51 +271,6 @@ func (tx *Transaction) Size() common.StorageSize {
return common.StorageSize(c)
}
-/*
-// From returns the address derived from the signature (V, R, S) using secp256k1
-// elliptic curve and an error if it failed deriving or upon an incorrect
-// signature.
-//
-// From Uses the homestead consensus rules to determine whether the signature is
-// valid.
-//
-// From caches the address, allowing it to be used regardless of
-// Frontier / Homestead. however, the first time called it runs
-// signature validations, so we need two versions. This makes it
-// easier to ensure backwards compatibility of things like package rpc
-// where eth_getblockbynumber uses tx.From() and needs to work for
-// both txs before and after the first homestead block. Signatures
-// valid in homestead are a subset of valid ones in Frontier)
-func (tx *Transaction) From() (common.Address, error) {
- if tx.signer == nil {
- return common.Address{}, errNoSigner
- }
-
- if from := tx.from.Load(); from != nil {
- return from.(common.Address), nil
- }
-
- pubkey, err := tx.signer.PublicKey(tx)
- if err != nil {
- return common.Address{}, err
- }
- var addr common.Address
- copy(addr[:], crypto.Keccak256(pubkey[1:])[12:])
- tx.from.Store(addr)
- return addr, nil
-}
-
-// SignatureValues returns the ECDSA signature values contained in the transaction.
-func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int, err error) {
- if tx.signer == nil {
- return 0, nil, nil,errNoSigner
- }
-
- return normaliseV(tx.signer, tx.data.V), new(big.Int).Set(tx.data.R),new(big.Int).Set(tx.data.S), nil
-}
-
-*/
-
// AsMessage returns the transaction as a core.Message.
//
// AsMessage requires a signer to derive the sender.
@@ -338,14 +292,6 @@ func (tx *Transaction) AsMessage(s Signer) (Message, error) {
return msg, err
}
-// SignECDSA signs the transaction using the given signer and private key
-//
-// XXX This only makes for a nice API: NewTx(...).SignECDSA(signer, prv). Should
-// we keep this?
-func (tx *Transaction) SignECDSA(signer Signer, prv *ecdsa.PrivateKey) (*Transaction, error) {
- return signer.SignECDSA(tx, prv)
-}
-
// WithSignature returns a new transaction with the given signature.
// This signature needs to be formatted as described in the yellow paper (v+27).
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go
index 0429c8c306..0b4d2b9625 100644
--- a/core/types/transaction_signing.go
+++ b/core/types/transaction_signing.go
@@ -50,10 +50,10 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
return signer
}
-// SignECDSA signs the transaction using the given signer and private key
-func SignECDSA(s Signer, tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
+// SignTx signs the transaction using the given signer and private key
+func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) {
h := s.Hash(tx)
- sig, err := crypto.SignEthereum(h[:], prv)
+ sig, err := crypto.Sign(h[:], prv)
if err != nil {
return nil, err
}
@@ -91,19 +91,13 @@ func Sender(signer Signer, tx *Transaction) (common.Address, error) {
return addr, nil
}
-// SignatureValues returns the ECDSA signature values contained in the transaction.
-func SignatureValues(signer Signer, tx *Transaction) (v byte, r *big.Int, s *big.Int) {
- return normaliseV(signer, tx.data.V), new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S)
-}
-
type Signer interface {
// Hash returns the rlp encoded hash for signatures
Hash(tx *Transaction) common.Hash
// PubilcKey returns the public key derived from the signature
PublicKey(tx *Transaction) ([]byte, error)
- // SignECDSA signs the transaction with the given and returns a copy of the tx
- SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error)
- // WithSignature returns a copy of the transaction with the given signature
+ // WithSignature returns a copy of the transaction with the given signature.
+ // The signature must be encoded in [R || S || V] format where V is 0 or 1.
WithSignature(tx *Transaction, sig []byte) (*Transaction, error)
// Checks for equality on the signers
Equal(Signer) bool
@@ -129,10 +123,6 @@ func (s EIP155Signer) Equal(s2 Signer) bool {
return ok && eip155.chainId.Cmp(s.chainId) == 0
}
-func (s EIP155Signer) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
- return SignECDSA(s, tx, prv)
-}
-
func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) {
// if the transaction is not protected fall back to homestead signer
if !tx.Protected() {
@@ -143,17 +133,16 @@ func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) {
return nil, ErrInvalidChainId
}
- V := normaliseV(s, tx.data.V)
+ V := byte(new(big.Int).Sub(tx.data.V, s.chainIdMul).Uint64() - 35)
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) {
return nil, ErrInvalidSig
}
-
// encode the signature in uncompressed format
R, S := tx.data.R.Bytes(), tx.data.S.Bytes()
sig := make([]byte, 65)
copy(sig[32-len(R):32], R)
copy(sig[64-len(S):64], S)
- sig[64] = V - 27
+ sig[64] = V
// recover the public key from the signature
hash := s.Hash(tx)
@@ -167,8 +156,8 @@ func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) {
return pub, nil
}
-// WithSignature returns a new transaction with the given signature.
-// This signature needs to be formatted as described in the yellow paper (v+27).
+// WithSignature returns a new transaction with the given signature. This signature
+// needs to be in the [R || S || V] format where V is 0 or 1.
func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
if len(sig) != 65 {
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
@@ -179,7 +168,7 @@ func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction,
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]})
if s.chainId.BitLen() > 0 {
- cpy.data.V = big.NewInt(int64(sig[64] - 27 + 35))
+ cpy.data.V = big.NewInt(int64(sig[64] + 35))
cpy.data.V.Add(cpy.data.V, s.chainIdMul)
}
return cpy, nil
@@ -199,15 +188,6 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
})
}
-func (s EIP155Signer) SigECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
- h := s.Hash(tx)
- sig, err := crypto.SignEthereum(h[:], prv)
- if err != nil {
- return nil, err
- }
- return s.WithSignature(tx, sig)
-}
-
// HomesteadTransaction implements TransactionInterface using the
// homestead rules.
type HomesteadSigner struct{ FrontierSigner }
@@ -217,8 +197,8 @@ func (s HomesteadSigner) Equal(s2 Signer) bool {
return ok
}
-// WithSignature returns a new transaction with the given snature.
-// This snature needs to be formatted as described in the yellow paper (v+27).
+// WithSignature returns a new transaction with the given signature. This signature
+// needs to be in the [R || S || V] format where V is 0 or 1.
func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
if len(sig) != 65 {
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
@@ -226,24 +206,15 @@ func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transacti
cpy := &Transaction{data: tx.data}
cpy.data.R = new(big.Int).SetBytes(sig[:32])
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
- cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]})
+ cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27})
return cpy, nil
}
-func (hs HomesteadSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
- h := hs.Hash(tx)
- sig, err := crypto.SignEthereum(h[:], prv)
- if err != nil {
- return nil, err
- }
- return hs.WithSignature(tx, sig)
-}
-
func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) {
if tx.data.V.BitLen() > 8 {
return nil, ErrInvalidSig
}
- V := byte(tx.data.V.Uint64())
+ V := byte(tx.data.V.Uint64() - 27)
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) {
return nil, ErrInvalidSig
}
@@ -252,7 +223,7 @@ func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) {
sig := make([]byte, 65)
copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s)
- sig[64] = V - 27
+ sig[64] = V
// recover the public key from the snature
hash := hs.Hash(tx)
@@ -273,8 +244,8 @@ func (s FrontierSigner) Equal(s2 Signer) bool {
return ok
}
-// WithSignature returns a new transaction with the given snature.
-// This snature needs to be formatted as described in the yellow paper (v+27).
+// WithSignature returns a new transaction with the given signature. This signature
+// needs to be in the [R || S || V] format where V is 0 or 1.
func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
if len(sig) != 65 {
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
@@ -282,19 +253,10 @@ func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transactio
cpy := &Transaction{data: tx.data}
cpy.data.R = new(big.Int).SetBytes(sig[:32])
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
- cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]})
+ cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27})
return cpy, nil
}
-func (fs FrontierSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
- h := fs.Hash(tx)
- sig, err := crypto.SignEthereum(h[:], prv)
- if err != nil {
- return nil, err
- }
- return fs.WithSignature(tx, sig)
-}
-
// Hash returns the hash to be sned by the sender.
// It does not uniquely identify the transaction.
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
@@ -313,7 +275,7 @@ func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) {
return nil, ErrInvalidSig
}
- V := byte(tx.data.V.Uint64())
+ V := byte(tx.data.V.Uint64() - 27)
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, false) {
return nil, ErrInvalidSig
}
@@ -322,7 +284,7 @@ func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) {
sig := make([]byte, 65)
copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s)
- sig[64] = V - 27
+ sig[64] = V
// recover the public key from the snature
hash := fs.Hash(tx)
@@ -336,18 +298,6 @@ func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) {
return pub, nil
}
-// normaliseV returns the Ethereum version of the V parameter
-func normaliseV(s Signer, v *big.Int) byte {
- if s, ok := s.(EIP155Signer); ok {
- stdV := v.BitLen() <= 8 && (v.Uint64() == 27 || v.Uint64() == 28)
- if s.chainId.BitLen() > 0 && !stdV {
- nv := byte((new(big.Int).Sub(v, s.chainIdMul).Uint64()) - 35 + 27)
- return nv
- }
- }
- return byte(v.Uint64())
-}
-
// deriveChainId derives the chain id from the given v parameter
func deriveChainId(v *big.Int) *big.Int {
if v.BitLen() <= 64 {
diff --git a/core/types/transaction_signing_test.go b/core/types/transaction_signing_test.go
index e53202ced8..fef1d67fb6 100644
--- a/core/types/transaction_signing_test.go
+++ b/core/types/transaction_signing_test.go
@@ -30,7 +30,7 @@ func TestEIP155Signing(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey)
signer := NewEIP155Signer(big.NewInt(18))
- tx, err := NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil).SignECDSA(signer, key)
+ tx, err := SignTx(NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil), signer, key)
if err != nil {
t.Fatal(err)
}
@@ -49,7 +49,7 @@ func TestEIP155ChainId(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey)
signer := NewEIP155Signer(big.NewInt(18))
- tx, err := NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil).SignECDSA(signer, key)
+ tx, err := SignTx(NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil), signer, key)
if err != nil {
t.Fatal(err)
}
@@ -62,7 +62,7 @@ func TestEIP155ChainId(t *testing.T) {
}
tx = NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil)
- tx, err = tx.SignECDSA(HomesteadSigner{}, key)
+ tx, err = SignTx(tx, HomesteadSigner{}, key)
if err != nil {
t.Fatal(err)
}
@@ -121,7 +121,7 @@ func TestChainId(t *testing.T) {
tx := NewTransaction(0, common.Address{}, new(big.Int), new(big.Int), new(big.Int), nil)
var err error
- tx, err = tx.SignECDSA(NewEIP155Signer(big.NewInt(1)), key)
+ tx, err = SignTx(tx, NewEIP155Signer(big.NewInt(1)), key)
if err != nil {
t.Fatal(err)
}
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index 9e01c5c305..a481744754 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -47,7 +47,7 @@ var (
common.FromHex("5544"),
).WithSignature(
HomesteadSigner{},
- common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a31c"),
+ common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"),
)
)
@@ -138,7 +138,7 @@ func TestTransactionPriceNonceSort(t *testing.T) {
for start, key := range keys {
addr := crypto.PubkeyToAddress(key.PublicKey)
for i := 0; i < 25; i++ {
- tx, _ := NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(int64(start+i)), nil).SignECDSA(signer, key)
+ tx, _ := SignTx(NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(int64(start+i)), nil), signer, key)
groups[addr] = append(groups[addr], tx)
}
}
diff --git a/core/vm/contract.go b/core/vm/contract.go
index 771f7ce260..35b0e96df8 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -24,7 +24,7 @@ import (
// ContractRef is a reference to the contract's backing object
type ContractRef interface {
- ReturnGas(*big.Int, *big.Int)
+ ReturnGas(*big.Int)
Address() common.Address
Value() *big.Int
SetCode(common.Hash, []byte)
@@ -48,7 +48,7 @@ type Contract struct {
CodeAddr *common.Address
Input []byte
- value, Gas, UsedGas, Price *big.Int
+ value, Gas, UsedGas *big.Int
Args []byte
@@ -56,7 +56,7 @@ type Contract struct {
}
// NewContract returns a new contract environment for the execution of EVM.
-func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract {
+func NewContract(caller ContractRef, object ContractRef, value, gas *big.Int) *Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
if parent, ok := caller.(*Contract); ok {
@@ -70,9 +70,6 @@ func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.
// This pointer will be off the state transition
c.Gas = gas //new(big.Int).Set(gas)
c.value = new(big.Int).Set(value)
- // In most cases price and value are pointers to transaction objects
- // and we don't want the transaction's values to change.
- c.Price = new(big.Int).Set(price)
c.UsedGas = new(big.Int)
return c
@@ -114,7 +111,7 @@ func (c *Contract) Caller() common.Address {
// caller.
func (c *Contract) Finalise() {
// Return the remaining gas to the caller
- c.caller.ReturnGas(c.Gas, c.Price)
+ c.caller.ReturnGas(c.Gas)
}
// UseGas attempts the use gas and subtracts it and returns true on success
@@ -127,7 +124,7 @@ func (c *Contract) UseGas(gas *big.Int) (ok bool) {
}
// ReturnGas adds the given gas back to itself.
-func (c *Contract) ReturnGas(gas, price *big.Int) {
+func (c *Contract) ReturnGas(gas *big.Int) {
// Return the gas to the context
c.Gas.Add(c.Gas, gas)
c.UsedGas.Sub(c.UsedGas, gas)
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 48c3234758..63c0d57cc9 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -26,84 +26,59 @@ import (
"github.com/ubiq/go-ubiq/params"
)
-// PrecompiledAccount represents a native ethereum contract
-type PrecompiledAccount struct {
- Gas func(l int) *big.Int
- fn func(in []byte) []byte
-}
-
-// Call calls the native function
-func (self PrecompiledAccount) Call(in []byte) []byte {
- return self.fn(in)
+// Precompiled contract is the basic interface for native Go contracts. The implementation
+// requires a deterministic gas count based on the input size of the Run method of the
+// contract.
+type PrecompiledContract interface {
+ RequiredGas(inputSize int) *big.Int // RequiredPrice calculates the contract gas use
+ Run(input []byte) []byte // Run runs the precompiled contract
}
// Precompiled contains the default set of ethereum contracts
-var Precompiled = PrecompiledContracts()
+var PrecompiledContracts = map[common.Address]PrecompiledContract{
+ common.BytesToAddress([]byte{1}): &ecrecover{},
+ common.BytesToAddress([]byte{2}): &sha256{},
+ common.BytesToAddress([]byte{3}): &ripemd160{},
+ common.BytesToAddress([]byte{4}): &dataCopy{},
+}
-// PrecompiledContracts returns the default set of precompiled ethereum
-// contracts defined by the ethereum yellow paper.
-func PrecompiledContracts() map[string]*PrecompiledAccount {
- return map[string]*PrecompiledAccount{
- // ECRECOVER
- string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int {
- return params.EcrecoverGas
- }, ecrecoverFunc},
+// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
+func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
+ gas := p.RequiredGas(len(input))
+ if contract.UseGas(gas) {
+ ret = p.Run(input)
- // SHA256
- string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int {
- n := big.NewInt(int64(l+31) / 32)
- n.Mul(n, params.Sha256WordGas)
- return n.Add(n, params.Sha256Gas)
- }, sha256Func},
-
- // RIPEMD160
- string(common.LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int {
- n := big.NewInt(int64(l+31) / 32)
- n.Mul(n, params.Ripemd160WordGas)
- return n.Add(n, params.Ripemd160Gas)
- }, ripemd160Func},
-
- string(common.LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int {
- n := big.NewInt(int64(l+31) / 32)
- n.Mul(n, params.IdentityWordGas)
-
- return n.Add(n, params.IdentityGas)
- }, memCpy},
+ return ret, nil
+ } else {
+ return nil, ErrOutOfGas
}
}
-func sha256Func(in []byte) []byte {
- return crypto.Sha256(in)
+// ECRECOVER implemented as a native contract
+type ecrecover struct{}
+
+func (c *ecrecover) RequiredGas(inputSize int) *big.Int {
+ return params.EcrecoverGas
}
-func ripemd160Func(in []byte) []byte {
- return common.LeftPadBytes(crypto.Ripemd160(in), 32)
-}
+func (c *ecrecover) Run(in []byte) []byte {
+ const ecRecoverInputLength = 128
-const ecRecoverInputLength = 128
-
-func ecrecoverFunc(in []byte) []byte {
- in = common.RightPadBytes(in, 128)
+ in = common.RightPadBytes(in, ecRecoverInputLength)
// "in" is (hash, v, r, s), each 32 bytes
// but for ecrecover we want (r, s, v)
r := common.BytesToBig(in[64:96])
s := common.BytesToBig(in[96:128])
- // Treat V as a 256bit integer
- vbig := common.Bytes2Big(in[32:64])
- v := byte(vbig.Uint64())
+ v := in[63] - 27
// tighter sig s values in homestead only apply to tx sigs
- if !crypto.ValidateSignatureValues(v, r, s, false) {
+ if common.Bytes2Big(in[32:63]).BitLen() > 0 || !crypto.ValidateSignatureValues(v, r, s, false) {
glog.V(logger.Detail).Infof("ECRECOVER error: v, r or s value invalid")
return nil
}
-
- // v needs to be at the end and normalized for libsecp256k1
- vbignormal := new(big.Int).Sub(vbig, big.NewInt(27))
- vnormal := byte(vbignormal.Uint64())
- rsv := append(in[64:128], vnormal)
- pubKey, err := crypto.Ecrecover(in[:32], rsv)
+ // v needs to be at the end for libsecp256k1
+ pubKey, err := crypto.Ecrecover(in[:32], append(in[64:128], v))
// make sure the public key is a valid one
if err != nil {
glog.V(logger.Detail).Infoln("ECRECOVER error: ", err)
@@ -114,6 +89,39 @@ func ecrecoverFunc(in []byte) []byte {
return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32)
}
-func memCpy(in []byte) []byte {
+// SHA256 implemented as a native contract
+type sha256 struct{}
+
+func (c *sha256) RequiredGas(inputSize int) *big.Int {
+ n := big.NewInt(int64(inputSize+31) / 32)
+ n.Mul(n, params.Sha256WordGas)
+ return n.Add(n, params.Sha256Gas)
+}
+func (c *sha256) Run(in []byte) []byte {
+ return crypto.Sha256(in)
+}
+
+// RIPMED160 implemented as a native contract
+type ripemd160 struct{}
+
+func (c *ripemd160) RequiredGas(inputSize int) *big.Int {
+ n := big.NewInt(int64(inputSize+31) / 32)
+ n.Mul(n, params.Ripemd160WordGas)
+ return n.Add(n, params.Ripemd160Gas)
+}
+func (c *ripemd160) Run(in []byte) []byte {
+ return common.LeftPadBytes(crypto.Ripemd160(in), 32)
+}
+
+// data copy implemented as a native contract
+type dataCopy struct{}
+
+func (c *dataCopy) RequiredGas(inputSize int) *big.Int {
+ n := big.NewInt(int64(inputSize+31) / 32)
+ n.Mul(n, params.IdentityWordGas)
+
+ return n.Add(n, params.IdentityGas)
+}
+func (c *dataCopy) Run(in []byte) []byte {
return in
}
diff --git a/core/vm/environment.go b/core/vm/environment.go
index 7720a2adac..46050281bd 100644
--- a/core/vm/environment.go
+++ b/core/vm/environment.go
@@ -17,110 +17,312 @@
package vm
import (
+ "fmt"
"math/big"
+ "sync/atomic"
"github.com/ubiq/go-ubiq/common"
+ "github.com/ubiq/go-ubiq/crypto"
"github.com/ubiq/go-ubiq/params"
)
-// Environment is an EVM requirement and helper which allows access to outside
-// information such as states.
-type Environment interface {
- // The current ruleset
- ChainConfig() *params.ChainConfig
- // The state database
- Db() Database
- // Creates a restorable snapshot
- SnapshotDatabase() int
- // Set database to previous snapshot
- RevertToSnapshot(int)
- // Address of the original invoker (first occurrence of the VM invoker)
- Origin() common.Address
- // The block number this VM is invoked on
- BlockNumber() *big.Int
- // The n'th hash ago from this block number
- GetHash(uint64) common.Hash
- // The handler's address
- Coinbase() common.Address
- // The current time (block time)
- Time() *big.Int
- // Difficulty set on the current block
- Difficulty() *big.Int
- // The gas limit of the block
- GasLimit() *big.Int
- // Determines whether it's possible to transact
- CanTransfer(from common.Address, balance *big.Int) bool
- // Transfers amount from one account to the other
- Transfer(from, to Account, amount *big.Int)
- // Adds a LOG to the state
- AddLog(*Log)
- // Type of the VM
- Vm() Vm
- // Get the curret calling depth
- Depth() int
- // Set the current calling depth
- SetDepth(i int)
- // Call another contract
- Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
- // Take another's contract code and execute within our own context
- CallCode(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
- // Same as CallCode except sender and value is propagated from parent to child scope
- DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error)
- // Create a new contract
- Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error)
+type (
+ CanTransferFunc func(StateDB, common.Address, *big.Int) bool
+ TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
+ // GetHashFunc returns the nth block hash in the blockchain
+ // and is used by the BLOCKHASH EVM op code.
+ GetHashFunc func(uint64) common.Hash
+)
+
+// Context provides the EVM with auxiliary information. Once provided it shouldn't be modified.
+type Context struct {
+ // CanTransfer returns whether the account contains
+ // sufficient ether to transfer the value
+ CanTransfer CanTransferFunc
+ // Transfer transfers ether from one account to the other
+ Transfer TransferFunc
+ // GetHash returns the hash corresponding to n
+ GetHash GetHashFunc
+
+ // Message information
+ Origin common.Address // Provides information for ORIGIN
+ GasPrice *big.Int // Provides information for GASPRICE
+
+ // Block information
+ Coinbase common.Address // Provides information for COINBASE
+ GasLimit *big.Int // Provides information for GASLIMIT
+ BlockNumber *big.Int // Provides information for NUMBER
+ Time *big.Int // Provides information for TIME
+ Difficulty *big.Int // Provides information for DIFFICULTY
}
-// Vm is the basic interface for an implementation of the EVM.
-type Vm interface {
- // Run should execute the given contract with the input given in in
- // and return the contract execution return bytes or an error if it
- // failed.
- Run(c *Contract, in []byte) ([]byte, error)
+// EVM provides information about external sources for the EVM
+//
+// The EVM should never be reused and is not thread safe.
+type EVM struct {
+ // Context provides auxiliary blockchain related information
+ Context
+ // StateDB gives access to the underlying state
+ StateDB StateDB
+ // Depth is the current call stack
+ depth int
+
+ // chainConfig contains information about the current chain
+ chainConfig *params.ChainConfig
+ // virtual machine configuration options used to initialise the
+ // evm.
+ vmConfig Config
+ // global (to this context) ethereum virtual machine
+ // used throughout the execution of the tx.
+ interpreter *Interpreter
+ // abort is used to abort the EVM calling operations
+ // NOTE: must be set atomically
+ abort int32
}
-// Database is a EVM database for full state querying.
-type Database interface {
- GetAccount(common.Address) Account
- CreateAccount(common.Address) Account
+// NewEVM retutrns a new EVM evmironment.
+func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
+ evm := &EVM{
+ Context: ctx,
+ StateDB: statedb,
+ vmConfig: vmConfig,
+ chainConfig: chainConfig,
+ }
- AddBalance(common.Address, *big.Int)
- GetBalance(common.Address) *big.Int
-
- GetNonce(common.Address) uint64
- SetNonce(common.Address, uint64)
-
- GetCodeHash(common.Address) common.Hash
- GetCodeSize(common.Address) int
- GetCode(common.Address) []byte
- SetCode(common.Address, []byte)
-
- AddRefund(*big.Int)
- GetRefund() *big.Int
-
- GetState(common.Address, common.Hash) common.Hash
- SetState(common.Address, common.Hash, common.Hash)
-
- Suicide(common.Address) bool
- HasSuicided(common.Address) bool
-
- // Exist reports whether the given account exists in state.
- // Notably this should also return true for suicided accounts.
- Exist(common.Address) bool
- // Empty returns whether the given account is empty. Empty
- // is defined according to EIP161 (balance = nonce = code = 0).
- Empty(common.Address) bool
+ evm.interpreter = NewInterpreter(evm, vmConfig)
+ return evm
}
-// Account represents a contract or basic ethereum account.
-type Account interface {
- SubBalance(amount *big.Int)
- AddBalance(amount *big.Int)
- SetBalance(*big.Int)
- SetNonce(uint64)
- Balance() *big.Int
- Address() common.Address
- ReturnGas(*big.Int, *big.Int)
- SetCode(common.Hash, []byte)
- ForEachStorage(cb func(key, value common.Hash) bool)
- Value() *big.Int
+// Cancel cancels any running EVM operation. This may be called concurrently and it's safe to be
+// called multiple times.
+func (evm *EVM) Cancel() {
+ atomic.StoreInt32(&evm.abort, 1)
}
+
+// Call executes the contract associated with the addr with the given input as parameters. It also handles any
+// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in
+// case of an execution error or failed value transfer.
+func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
+ if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ caller.ReturnGas(gas)
+
+ return nil, nil
+ }
+
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if evm.depth > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas)
+
+ return nil, ErrDepth
+ }
+ if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
+ caller.ReturnGas(gas)
+
+ return nil, ErrInsufficientBalance
+ }
+
+ var (
+ to Account
+ snapshot = evm.StateDB.Snapshot()
+ )
+ if !evm.StateDB.Exist(addr) {
+ if PrecompiledContracts[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.BitLen() == 0 {
+ caller.ReturnGas(gas)
+ return nil, nil
+ }
+
+ to = evm.StateDB.CreateAccount(addr)
+ } else {
+ to = evm.StateDB.GetAccount(addr)
+ }
+ evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
+
+ // initialise a new contract and set the code that is to be used by the
+ // E The contract is a scoped evmironment for this execution context
+ // only.
+ contract := NewContract(caller, to, value, gas)
+ contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
+ defer contract.Finalise()
+
+ ret, err = evm.interpreter.Run(contract, input)
+ // When an error was returned by the EVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in homestead this also counts for code storage gas errors.
+ if err != nil {
+ contract.UseGas(contract.Gas)
+
+ evm.StateDB.RevertToSnapshot(snapshot)
+ }
+ return ret, err
+}
+
+// CallCode executes the contract associated with the addr with the given input as parameters. It also handles any
+// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in
+// case of an execution error or failed value transfer.
+//
+// CallCode differs from Call in the sense that it executes the given address' code with the caller as context.
+func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
+ if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ caller.ReturnGas(gas)
+
+ return nil, nil
+ }
+
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if evm.depth > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas)
+
+ return nil, ErrDepth
+ }
+ if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
+ caller.ReturnGas(gas)
+
+ return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, evm.StateDB.GetBalance(caller.Address()))
+ }
+
+ var (
+ snapshot = evm.StateDB.Snapshot()
+ to = evm.StateDB.GetAccount(caller.Address())
+ )
+ // initialise a new contract and set the code that is to be used by the
+ // E The contract is a scoped evmironment for this execution context
+ // only.
+ contract := NewContract(caller, to, value, gas)
+ contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
+ defer contract.Finalise()
+
+ ret, err = evm.interpreter.Run(contract, input)
+ if err != nil {
+ contract.UseGas(contract.Gas)
+
+ evm.StateDB.RevertToSnapshot(snapshot)
+ }
+
+ return ret, err
+}
+
+// DelegateCall executes the contract associated with the addr with the given input as parameters.
+// It reverses the state in case of an execution error.
+//
+// DelegateCall differs from CallCode in the sense that it executes the given address' code with the caller as context
+// and the caller is set to the caller of the caller.
+func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) {
+ if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ caller.ReturnGas(gas)
+
+ return nil, nil
+ }
+
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if evm.depth > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas)
+ return nil, ErrDepth
+ }
+
+ var (
+ snapshot = evm.StateDB.Snapshot()
+ to = evm.StateDB.GetAccount(caller.Address())
+ )
+
+ // Iinitialise a new contract and make initialise the delegate values
+ contract := NewContract(caller, to, caller.Value(), gas).AsDelegate()
+ contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
+ defer contract.Finalise()
+
+ ret, err = evm.interpreter.Run(contract, input)
+ if err != nil {
+ contract.UseGas(contract.Gas)
+
+ evm.StateDB.RevertToSnapshot(snapshot)
+ }
+
+ return ret, err
+}
+
+// Create creates a new contract using code as deployment code.
+func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (ret []byte, contractAddr common.Address, err error) {
+ if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ caller.ReturnGas(gas)
+
+ return nil, common.Address{}, nil
+ }
+
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if evm.depth > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas)
+
+ return nil, common.Address{}, ErrDepth
+ }
+ if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
+ caller.ReturnGas(gas)
+
+ return nil, common.Address{}, ErrInsufficientBalance
+ }
+
+ // Create a new account on the state
+ nonce := evm.StateDB.GetNonce(caller.Address())
+ evm.StateDB.SetNonce(caller.Address(), nonce+1)
+
+ snapshot := evm.StateDB.Snapshot()
+ contractAddr = crypto.CreateAddress(caller.Address(), nonce)
+ to := evm.StateDB.CreateAccount(contractAddr)
+ if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
+ evm.StateDB.SetNonce(contractAddr, 1)
+ }
+ evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
+
+ // initialise a new contract and set the code that is to be used by the
+ // E The contract is a scoped evmironment for this execution context
+ // only.
+ contract := NewContract(caller, to, value, gas)
+ contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)
+ defer contract.Finalise()
+
+ ret, err = evm.interpreter.Run(contract, nil)
+
+ // check whether the max code size has been exceeded
+ maxCodeSizeExceeded := len(ret) > params.MaxCodeSize
+ // if the contract creation ran successfully and no errors were returned
+ // calculate the gas required to store the code. If the code could not
+ // be stored due to not enough gas set an error and let it be handled
+ // by the error checking condition below.
+ if err == nil && !maxCodeSizeExceeded {
+ dataGas := big.NewInt(int64(len(ret)))
+ dataGas.Mul(dataGas, params.CreateDataGas)
+ if contract.UseGas(dataGas) {
+ evm.StateDB.SetCode(contractAddr, ret)
+ } else {
+ err = ErrCodeStoreOutOfGas
+ }
+ }
+
+ // When an error was returned by the EVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in homestead this also counts for code storage gas errors.
+ if maxCodeSizeExceeded ||
+ (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) {
+ contract.UseGas(contract.Gas)
+ evm.StateDB.RevertToSnapshot(snapshot)
+
+ // Nothing should be returned when an error is thrown.
+ return nil, contractAddr, err
+ }
+ // If the vm returned with an error the return value should be set to nil.
+ // This isn't consensus critical but merely to for behaviour reasons such as
+ // tests, RPC calls, etc.
+ if err != nil {
+ ret = nil
+ }
+
+ return ret, contractAddr, err
+}
+
+// ChainConfig returns the evmironment's chain configuration
+func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
+
+// Interpreter returns the EVM interpreter
+func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter }
diff --git a/core/vm/errors.go b/core/vm/errors.go
index 11e6f443e7..69c7d6a98c 100644
--- a/core/vm/errors.go
+++ b/core/vm/errors.go
@@ -16,14 +16,12 @@
package vm
-import (
- "errors"
- "fmt"
+import "errors"
- "github.com/ubiq/go-ubiq/params"
+var (
+ ErrOutOfGas = errors.New("out of gas")
+ ErrCodeStoreOutOfGas = errors.New("contract creation code storage out of gas")
+ ErrDepth = errors.New("max call depth exceeded")
+ ErrTraceLimitReached = errors.New("the number of logs reached the specified limit")
+ ErrInsufficientBalance = errors.New("insufficient balance for transfer")
)
-
-var OutOfGasError = errors.New("Out of gas")
-var CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas")
-var DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth)
-var TraceLimitReachedError = errors.New("The number of logs reached the specified limit")
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
new file mode 100644
index 0000000000..ec491a9b5a
--- /dev/null
+++ b/core/vm/gas_table.go
@@ -0,0 +1,246 @@
+package vm
+
+import (
+ "math/big"
+
+ "github.com/ubiq/go-ubiq/common"
+ "github.com/ubiq/go-ubiq/params"
+)
+
+func memoryGasCost(mem *Memory, newMemSize *big.Int) *big.Int {
+ gas := new(big.Int)
+ if newMemSize.Cmp(common.Big0) > 0 {
+ newMemSizeWords := toWordSize(newMemSize)
+
+ if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
+ // be careful reusing variables here when changing.
+ // The order has been optimised to reduce allocation
+ oldSize := toWordSize(big.NewInt(int64(mem.Len())))
+ pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
+ linCoef := oldSize.Mul(oldSize, params.MemoryGas)
+ quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
+ oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
+
+ pow.Exp(newMemSizeWords, common.Big2, Zero)
+ linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas)
+ quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv)
+ newTotalFee := linCoef.Add(linCoef, quadCoef)
+
+ fee := newTotalFee.Sub(newTotalFee, oldTotalFee)
+ gas.Add(gas, fee)
+ }
+ }
+ return gas
+}
+
+func constGasFunc(gas *big.Int) gasFunc {
+ return func(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ return gas
+ }
+}
+
+func gasCalldataCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ gas := memoryGasCost(mem, memorySize)
+ gas.Add(gas, GasFastestStep)
+ words := toWordSize(stack.Back(2))
+
+ return gas.Add(gas, words.Mul(words, params.CopyGas))
+}
+
+func gasSStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ var (
+ y, x = stack.Back(1), stack.Back(0)
+ val = env.StateDB.GetState(contract.Address(), common.BigToHash(x))
+ )
+ // This checks for 3 scenario's and calculates gas accordingly
+ // 1. From a zero-value address to a non-zero value (NEW VALUE)
+ // 2. From a non-zero value address to a zero-value address (DELETE)
+ // 3. From a non-zero to a non-zero (CHANGE)
+ if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
+ // 0 => non 0
+ return new(big.Int).Set(params.SstoreSetGas)
+ } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
+ env.StateDB.AddRefund(params.SstoreRefundGas)
+
+ return new(big.Int).Set(params.SstoreClearGas)
+ } else {
+ // non 0 => non 0 (or 0 => 0)
+ return new(big.Int).Set(params.SstoreResetGas)
+ }
+}
+
+func makeGasLog(n uint) gasFunc {
+ return func(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ mSize := stack.Back(1)
+
+ gas := new(big.Int).Add(memoryGasCost(mem, memorySize), params.LogGas)
+ gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), params.LogTopicGas))
+ gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas))
+ return gas
+ }
+}
+
+func gasSha3(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ gas := memoryGasCost(mem, memorySize)
+ gas.Add(gas, params.Sha3Gas)
+ words := toWordSize(stack.Back(1))
+ return gas.Add(gas, words.Mul(words, params.Sha3WordGas))
+}
+
+func gasCodeCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ gas := memoryGasCost(mem, memorySize)
+ gas.Add(gas, GasFastestStep)
+ words := toWordSize(stack.Back(2))
+
+ return gas.Add(gas, words.Mul(words, params.CopyGas))
+}
+
+func gasExtCodeCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ gas := memoryGasCost(mem, memorySize)
+ gas.Add(gas, gt.ExtcodeCopy)
+ words := toWordSize(stack.Back(3))
+
+ return gas.Add(gas, words.Mul(words, params.CopyGas))
+}
+
+func gasMLoad(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize))
+}
+
+func gasMStore8(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize))
+}
+
+func gasMStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize))
+}
+
+func gasCreate(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ return new(big.Int).Add(params.CreateGas, memoryGasCost(mem, memorySize))
+}
+
+func gasBalance(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ return gt.Balance
+}
+
+func gasExtCodeSize(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ return gt.ExtcodeSize
+}
+
+func gasSLoad(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ return gt.SLoad
+}
+
+func gasExp(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ expByteLen := int64((stack.data[stack.len()-2].BitLen() + 7) / 8)
+ gas := big.NewInt(expByteLen)
+ gas.Mul(gas, gt.ExpByte)
+ return gas.Add(gas, GasSlowStep)
+}
+
+func gasCall(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ gas := new(big.Int).Set(gt.Calls)
+
+ transfersValue := stack.Back(2).BitLen() > 0
+ var (
+ address = common.BigToAddress(stack.Back(1))
+ eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
+ )
+ if eip158 {
+ if env.StateDB.Empty(address) && transfersValue {
+ gas.Add(gas, params.CallNewAccountGas)
+ }
+ } else if !env.StateDB.Exist(address) {
+ gas.Add(gas, params.CallNewAccountGas)
+ }
+ if transfersValue {
+ gas.Add(gas, params.CallValueTransferGas)
+ }
+ gas.Add(gas, memoryGasCost(mem, memorySize))
+
+ cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1])
+ // Replace the stack item with the new gas calculation. This means that
+ // either the original item is left on the stack or the item is replaced by:
+ // (availableGas - gas) * 63 / 64
+ // We replace the stack item so that it's available when the opCall instruction is
+ // called. This information is otherwise lost due to the dependency on *current*
+ // available gas.
+ stack.data[stack.len()-1] = cg
+
+ return gas.Add(gas, cg)
+}
+
+func gasCallCode(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ gas := new(big.Int).Set(gt.Calls)
+ if stack.Back(2).BitLen() > 0 {
+ gas.Add(gas, params.CallValueTransferGas)
+ }
+ gas.Add(gas, memoryGasCost(mem, memorySize))
+
+ cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1])
+ // Replace the stack item with the new gas calculation. This means that
+ // either the original item is left on the stack or the item is replaced by:
+ // (availableGas - gas) * 63 / 64
+ // We replace the stack item so that it's available when the opCall instruction is
+ // called. This information is otherwise lost due to the dependency on *current*
+ // available gas.
+ stack.data[stack.len()-1] = cg
+
+ return gas.Add(gas, cg)
+}
+
+func gasReturn(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ return memoryGasCost(mem, memorySize)
+}
+
+func gasSuicide(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ gas := new(big.Int)
+ // EIP150 homestead gas reprice fork:
+ if env.ChainConfig().IsEIP150(env.BlockNumber) {
+ gas.Set(gt.Suicide)
+ var (
+ address = common.BigToAddress(stack.Back(0))
+ eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
+ )
+
+ if eip158 {
+ // if empty and transfers value
+ if env.StateDB.Empty(address) && env.StateDB.GetBalance(contract.Address()).BitLen() > 0 {
+ gas.Add(gas, gt.CreateBySuicide)
+ }
+ } else if !env.StateDB.Exist(address) {
+ gas.Add(gas, gt.CreateBySuicide)
+ }
+ }
+
+ if !env.StateDB.HasSuicided(contract.Address()) {
+ env.StateDB.AddRefund(params.SuicideRefundGas)
+ }
+ return gas
+}
+
+func gasDelegateCall(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ gas := new(big.Int).Add(gt.Calls, memoryGasCost(mem, memorySize))
+
+ cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1])
+ // Replace the stack item with the new gas calculation. This means that
+ // either the original item is left on the stack or the item is replaced by:
+ // (availableGas - gas) * 63 / 64
+ // We replace the stack item so that it's available when the opCall instruction is
+ // called.
+ stack.data[stack.len()-1] = cg
+
+ return gas.Add(gas, cg)
+}
+
+func gasPush(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ return GasFastestStep
+}
+
+func gasSwap(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ return GasFastestStep
+}
+
+func gasDup(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+ return GasFastestStep
+}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index e43660474f..fc63417d15 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -22,132 +22,44 @@ import (
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/common/math"
+ "github.com/ubiq/go-ubiq/core/types"
"github.com/ubiq/go-ubiq/crypto"
"github.com/ubiq/go-ubiq/params"
)
-type programInstruction interface {
- // executes the program instruction and allows the instruction to modify the state of the program
- do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
- // returns whether the program instruction halts the execution of the JIT
- halts() bool
- // Returns the current op code (debugging purposes)
- Op() OpCode
-}
-
-type instrFn func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack)
-
-type instruction struct {
- op OpCode
- pc uint64
- fn instrFn
- data *big.Int
-
- gas *big.Int
- spop int
- spush int
-
- returns bool
-}
-
-func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract *Contract, to *big.Int) (uint64, error) {
- if !validDest(destinations, to) {
- nop := contract.GetOp(to.Uint64())
- return 0, fmt.Errorf("invalid jump destination (%v) %v", nop, to)
- }
-
- return mapping[to.Uint64()], nil
-}
-
-func (instr instruction) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- // calculate the new memory size and gas price for the current executing opcode
- newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, env.Db(), memory, stack)
- if err != nil {
- return nil, err
- }
-
- // Use the calculated gas. When insufficient gas is present, use all gas and return an
- // Out Of Gas error
- if !contract.UseGas(cost) {
- return nil, OutOfGasError
- }
- // Resize the memory calculated previously
- memory.Resize(newMemSize.Uint64())
-
- // These opcodes return an argument and are therefor handled
- // differently from the rest of the opcodes
- switch instr.op {
- case JUMP:
- if pos, err := jump(program.mapping, program.destinations, contract, stack.pop()); err != nil {
- return nil, err
- } else {
- *pc = pos
- return nil, nil
- }
- case JUMPI:
- pos, cond := stack.pop(), stack.pop()
- if cond.Cmp(common.BigTrue) >= 0 {
- if pos, err := jump(program.mapping, program.destinations, contract, pos); err != nil {
- return nil, err
- } else {
- *pc = pos
- return nil, nil
- }
- }
- case RETURN:
- offset, size := stack.pop(), stack.pop()
- return memory.GetPtr(offset.Int64(), size.Int64()), nil
- default:
- if instr.fn == nil {
- return nil, fmt.Errorf("Invalid opcode 0x%x", instr.op)
- }
- instr.fn(instr, pc, env, contract, memory, stack)
- }
- *pc++
+func opAdd(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ x, y := stack.pop(), stack.pop()
+ stack.push(U256(x.Add(x, y)))
return nil, nil
}
-func (instr instruction) halts() bool {
- return instr.returns
-}
-
-func (instr instruction) Op() OpCode {
- return instr.op
-}
-
-func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- ret.Set(instr.data)
-}
-
-func opAdd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- x, y := stack.pop(), stack.pop()
- stack.push(U256(x.Add(x, y)))
-}
-
-func opSub(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSub(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
stack.push(U256(x.Sub(x, y)))
+ return nil, nil
}
-func opMul(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMul(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
stack.push(U256(x.Mul(x, y)))
+ return nil, nil
}
-func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opDiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
if y.Cmp(common.Big0) != 0 {
stack.push(U256(x.Div(x, y)))
} else {
stack.push(new(big.Int))
}
+ return nil, nil
}
-func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSdiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := S256(stack.pop()), S256(stack.pop())
if y.Cmp(common.Big0) == 0 {
stack.push(new(big.Int))
- return
+ return nil, nil
} else {
n := new(big.Int)
if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 {
@@ -161,18 +73,20 @@ func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract,
stack.push(U256(res))
}
+ return nil, nil
}
-func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
if y.Cmp(common.Big0) == 0 {
stack.push(new(big.Int))
} else {
stack.push(U256(x.Mod(x, y)))
}
+ return nil, nil
}
-func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := S256(stack.pop()), S256(stack.pop())
if y.Cmp(common.Big0) == 0 {
@@ -190,14 +104,16 @@ func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract,
stack.push(U256(res))
}
+ return nil, nil
}
-func opExp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opExp(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
base, exponent := stack.pop(), stack.pop()
stack.push(math.Exp(base, exponent))
+ return nil, nil
}
-func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSignExtend(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
back := stack.pop()
if back.Cmp(big.NewInt(31)) < 0 {
bit := uint(back.Uint64()*8 + 7)
@@ -212,80 +128,91 @@ func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Cont
stack.push(U256(num))
}
+ return nil, nil
}
-func opNot(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opNot(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x := stack.pop()
stack.push(U256(x.Not(x)))
+ return nil, nil
}
-func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opLt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
if x.Cmp(y) < 0 {
stack.push(big.NewInt(1))
} else {
stack.push(new(big.Int))
}
+ return nil, nil
}
-func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opGt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
if x.Cmp(y) > 0 {
stack.push(big.NewInt(1))
} else {
stack.push(new(big.Int))
}
+ return nil, nil
}
-func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSlt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := S256(stack.pop()), S256(stack.pop())
if x.Cmp(S256(y)) < 0 {
stack.push(big.NewInt(1))
} else {
stack.push(new(big.Int))
}
+ return nil, nil
}
-func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSgt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := S256(stack.pop()), S256(stack.pop())
if x.Cmp(y) > 0 {
stack.push(big.NewInt(1))
} else {
stack.push(new(big.Int))
}
+ return nil, nil
}
-func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opEq(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
if x.Cmp(y) == 0 {
stack.push(big.NewInt(1))
} else {
stack.push(new(big.Int))
}
+ return nil, nil
}
-func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opIszero(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x := stack.pop()
if x.Cmp(common.Big0) > 0 {
stack.push(new(big.Int))
} else {
stack.push(big.NewInt(1))
}
+ return nil, nil
}
-func opAnd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opAnd(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
stack.push(x.And(x, y))
+ return nil, nil
}
-func opOr(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opOr(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
stack.push(x.Or(x, y))
+ return nil, nil
}
-func opXor(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opXor(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
stack.push(x.Xor(x, y))
+ return nil, nil
}
-func opByte(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opByte(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
th, val := stack.pop(), stack.pop()
if th.Cmp(big.NewInt(32)) < 0 {
byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
@@ -293,8 +220,9 @@ func opByte(instr instruction, pc *uint64, env Environment, contract *Contract,
} else {
stack.push(new(big.Int))
}
+ return nil, nil
}
-func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opAddmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y, z := stack.pop(), stack.pop(), stack.pop()
if z.Cmp(Zero) > 0 {
add := x.Add(x, y)
@@ -303,8 +231,9 @@ func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract
} else {
stack.push(new(big.Int))
}
+ return nil, nil
}
-func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMulmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y, z := stack.pop(), stack.pop(), stack.pop()
if z.Cmp(Zero) > 0 {
mul := x.Mul(x, y)
@@ -313,67 +242,79 @@ func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract
} else {
stack.push(new(big.Int))
}
+ return nil, nil
}
-func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSha3(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
offset, size := stack.pop(), stack.pop()
hash := crypto.Keccak256(memory.Get(offset.Int64(), size.Int64()))
stack.push(common.BytesToBig(hash))
+ return nil, nil
}
-func opAddress(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opAddress(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(common.Bytes2Big(contract.Address().Bytes()))
+ return nil, nil
}
-func opBalance(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opBalance(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
addr := common.BigToAddress(stack.pop())
- balance := env.Db().GetBalance(addr)
+ balance := env.StateDB.GetBalance(addr)
stack.push(new(big.Int).Set(balance))
+ return nil, nil
}
-func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(env.Origin().Big())
+func opOrigin(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(env.Origin.Big())
+ return nil, nil
}
-func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCaller(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(contract.Caller().Big())
+ return nil, nil
}
-func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCallValue(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(new(big.Int).Set(contract.value))
+ return nil, nil
}
-func opCalldataLoad(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCalldataLoad(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32)))
+ return nil, nil
}
-func opCalldataSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCalldataSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(big.NewInt(int64(len(contract.Input))))
+ return nil, nil
}
-func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCalldataCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var (
mOff = stack.pop()
cOff = stack.pop()
l = stack.pop()
)
memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l))
+ return nil, nil
}
-func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opExtCodeSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
addr := common.BigToAddress(stack.pop())
- l := big.NewInt(int64(env.Db().GetCodeSize(addr)))
+ l := big.NewInt(int64(env.StateDB.GetCodeSize(addr)))
stack.push(l)
+ return nil, nil
}
-func opCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCodeSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
l := big.NewInt(int64(len(contract.Code)))
stack.push(l)
+ return nil, nil
}
-func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var (
mOff = stack.pop()
cOff = stack.pop()
@@ -382,160 +323,173 @@ func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contra
codeCopy := getData(contract.Code, cOff, l)
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
+ return nil, nil
}
-func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opExtCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var (
addr = common.BigToAddress(stack.pop())
mOff = stack.pop()
cOff = stack.pop()
l = stack.pop()
)
- codeCopy := getData(env.Db().GetCode(addr), cOff, l)
+ codeCopy := getData(env.StateDB.GetCode(addr), cOff, l)
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
+ return nil, nil
}
-func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(new(big.Int).Set(contract.Price))
+func opGasprice(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(new(big.Int).Set(env.GasPrice))
+ return nil, nil
}
-func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opBlockhash(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
num := stack.pop()
- n := new(big.Int).Sub(env.BlockNumber(), common.Big257)
- if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber()) < 0 {
+ n := new(big.Int).Sub(env.BlockNumber, common.Big257)
+ if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber) < 0 {
stack.push(env.GetHash(num.Uint64()).Big())
} else {
stack.push(new(big.Int))
}
+ return nil, nil
}
-func opCoinbase(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(env.Coinbase().Big())
+func opCoinbase(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(env.Coinbase.Big())
+ return nil, nil
}
-func opTimestamp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(U256(new(big.Int).Set(env.Time())))
+func opTimestamp(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(U256(new(big.Int).Set(env.Time)))
+ return nil, nil
}
-func opNumber(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(U256(new(big.Int).Set(env.BlockNumber())))
+func opNumber(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(U256(new(big.Int).Set(env.BlockNumber)))
+ return nil, nil
}
-func opDifficulty(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(U256(new(big.Int).Set(env.Difficulty())))
+func opDifficulty(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(U256(new(big.Int).Set(env.Difficulty)))
+ return nil, nil
}
-func opGasLimit(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(U256(new(big.Int).Set(env.GasLimit())))
+func opGasLimit(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(U256(new(big.Int).Set(env.GasLimit)))
+ return nil, nil
}
-func opPop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opPop(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.pop()
+ return nil, nil
}
-func opPush(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(new(big.Int).Set(instr.data))
-}
-
-func opDup(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.dup(int(instr.data.Int64()))
-}
-
-func opSwap(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.swap(int(instr.data.Int64()))
-}
-
-func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- n := int(instr.data.Int64())
- topics := make([]common.Hash, n)
- mStart, mSize := stack.pop(), stack.pop()
- for i := 0; i < n; i++ {
- topics[i] = common.BigToHash(stack.pop())
- }
-
- d := memory.Get(mStart.Int64(), mSize.Int64())
- log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
- env.AddLog(log)
-}
-
-func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMload(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
offset := stack.pop()
val := common.BigD(memory.Get(offset.Int64(), 32))
stack.push(val)
+ return nil, nil
}
-func opMstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMstore(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// pop value of the stack
mStart, val := stack.pop(), stack.pop()
memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256))
+ return nil, nil
}
-func opMstore8(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMstore8(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
off, val := stack.pop().Int64(), stack.pop().Int64()
memory.store[off] = byte(val & 0xff)
+ return nil, nil
}
-func opSload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSload(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
loc := common.BigToHash(stack.pop())
- val := env.Db().GetState(contract.Address(), loc).Big()
+ val := env.StateDB.GetState(contract.Address(), loc).Big()
stack.push(val)
+ return nil, nil
}
-func opSstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSstore(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
loc := common.BigToHash(stack.pop())
val := stack.pop()
- env.Db().SetState(contract.Address(), loc, common.BigToHash(val))
+ env.StateDB.SetState(contract.Address(), loc, common.BigToHash(val))
+ return nil, nil
}
-func opJump(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opJump(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ pos := stack.pop()
+ if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) {
+ nop := contract.GetOp(pos.Uint64())
+ return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
+ }
+ *pc = pos.Uint64()
+ return nil, nil
}
-func opJumpi(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opJumpi(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ pos, cond := stack.pop(), stack.pop()
+ if cond.Cmp(common.BigTrue) >= 0 {
+ if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) {
+ nop := contract.GetOp(pos.Uint64())
+ return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
+ }
+ *pc = pos.Uint64()
+ } else {
+ *pc++
+ }
+ return nil, nil
}
-func opJumpdest(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opJumpdest(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ return nil, nil
}
-func opPc(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(new(big.Int).Set(instr.data))
+func opPc(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(new(big.Int).SetUint64(*pc))
+ return nil, nil
}
-func opMsize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMsize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(big.NewInt(int64(memory.Len())))
+ return nil, nil
}
-func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opGas(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(new(big.Int).Set(contract.Gas))
+ return nil, nil
}
-func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCreate(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var (
value = stack.pop()
offset, size = stack.pop(), stack.pop()
input = memory.Get(offset.Int64(), size.Int64())
gas = new(big.Int).Set(contract.Gas)
)
- if env.ChainConfig().IsEIP150(env.BlockNumber()) {
+ if env.ChainConfig().IsEIP150(env.BlockNumber) {
gas.Div(gas, n64)
gas = gas.Sub(contract.Gas, gas)
}
contract.UseGas(gas)
- _, addr, suberr := env.Create(contract, input, gas, contract.Price, value)
+ _, addr, suberr := env.Create(contract, input, gas, value)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
// ignore this error and pretend the operation was successful.
- if env.ChainConfig().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
+ if env.ChainConfig().IsHomestead(env.BlockNumber) && suberr == ErrCodeStoreOutOfGas {
stack.push(new(big.Int))
- } else if suberr != nil && suberr != CodeStoreOutOfGasError {
+ } else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
stack.push(new(big.Int))
} else {
stack.push(addr.Big())
}
+ return nil, nil
}
-func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
gas := stack.pop()
// pop gas and value of the stack.
addr, value := stack.pop(), stack.pop()
@@ -554,7 +508,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract,
gas.Add(gas, params.CallStipend)
}
- ret, err := env.Call(contract, address, args, gas, contract.Price, value)
+ ret, err := env.Call(contract, address, args, gas, value)
if err != nil {
stack.push(new(big.Int))
@@ -564,9 +518,10 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract,
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
+ return nil, nil
}
-func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCallCode(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
gas := stack.pop()
// pop gas and value of the stack.
addr, value := stack.pop(), stack.pop()
@@ -585,7 +540,7 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra
gas.Add(gas, params.CallStipend)
}
- ret, err := env.CallCode(contract, address, args, gas, contract.Price, value)
+ ret, err := env.CallCode(contract, address, args, gas, value)
if err != nil {
stack.push(new(big.Int))
@@ -595,39 +550,55 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
+ return nil, nil
}
-func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opDelegateCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ // if not homestead return an error. DELEGATECALL is not supported
+ // during pre-homestead.
+ if !env.ChainConfig().IsHomestead(env.BlockNumber) {
+ return nil, fmt.Errorf("invalid opcode %x", DELEGATECALL)
+ }
+
gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.BigToAddress(to)
args := memory.Get(inOffset.Int64(), inSize.Int64())
- ret, err := env.DelegateCall(contract, toAddr, args, gas, contract.Price)
+ ret, err := env.DelegateCall(contract, toAddr, args, gas)
if err != nil {
stack.push(new(big.Int))
} else {
stack.push(big.NewInt(1))
memory.Set(outOffset.Uint64(), outSize.Uint64(), ret)
}
+ return nil, nil
}
-func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
-}
-func opStop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opReturn(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ offset, size := stack.pop(), stack.pop()
+ ret := memory.GetPtr(offset.Int64(), size.Int64())
+
+ return ret, nil
}
-func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- balance := env.Db().GetBalance(contract.Address())
- env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
+func opStop(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ return nil, nil
+}
- env.Db().Suicide(contract.Address())
+func opSuicide(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ balance := env.StateDB.GetBalance(contract.Address())
+ env.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance)
+
+ env.StateDB.Suicide(contract.Address())
+
+ return nil, nil
}
// following functions are used by the instruction jump table
// make log instruction function
-func makeLog(size int) instrFn {
- return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func makeLog(size int) executionFunc {
+ return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
topics := make([]common.Hash, size)
mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < size; i++ {
@@ -635,32 +606,42 @@ func makeLog(size int) instrFn {
}
d := memory.Get(mStart.Int64(), mSize.Int64())
- log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
- env.AddLog(log)
+ env.StateDB.AddLog(&types.Log{
+ Address: contract.Address(),
+ Topics: topics,
+ Data: d,
+ // This is a non-consensus field, but assigned here because
+ // core/state doesn't know the current block number.
+ BlockNumber: env.BlockNumber.Uint64(),
+ })
+ return nil, nil
}
}
// make push instruction function
-func makePush(size uint64, bsize *big.Int) instrFn {
- return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func makePush(size uint64, bsize *big.Int) executionFunc {
+ return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize)
stack.push(common.Bytes2Big(byts))
*pc += size
+ return nil, nil
}
}
// make push instruction function
-func makeDup(size int64) instrFn {
- return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func makeDup(size int64) executionFunc {
+ return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.dup(int(size))
+ return nil, nil
}
}
// make swap instruction function
-func makeSwap(size int64) instrFn {
+func makeSwap(size int64) executionFunc {
// switch n + 1 otherwise n would be swapped with n
size += 1
- return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+ return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.swap(int(size))
+ return nil, nil
}
}
diff --git a/core/vm/interface.go b/core/vm/interface.go
new file mode 100644
index 0000000000..e04bd4c3ff
--- /dev/null
+++ b/core/vm/interface.go
@@ -0,0 +1,90 @@
+// Copyright 2014 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 vm
+
+import (
+ "math/big"
+
+ "github.com/ubiq/go-ubiq/common"
+ "github.com/ubiq/go-ubiq/core/types"
+)
+
+// StateDB is an EVM database for full state querying.
+type StateDB interface {
+ GetAccount(common.Address) Account
+ CreateAccount(common.Address) Account
+
+ SubBalance(common.Address, *big.Int)
+ AddBalance(common.Address, *big.Int)
+ GetBalance(common.Address) *big.Int
+
+ GetNonce(common.Address) uint64
+ SetNonce(common.Address, uint64)
+
+ GetCodeHash(common.Address) common.Hash
+ GetCode(common.Address) []byte
+ SetCode(common.Address, []byte)
+ GetCodeSize(common.Address) int
+
+ AddRefund(*big.Int)
+ GetRefund() *big.Int
+
+ GetState(common.Address, common.Hash) common.Hash
+ SetState(common.Address, common.Hash, common.Hash)
+
+ Suicide(common.Address) bool
+ HasSuicided(common.Address) bool
+
+ // Exist reports whether the given account exists in state.
+ // Notably this should also return true for suicided accounts.
+ Exist(common.Address) bool
+ // Empty returns whether the given account is empty. Empty
+ // is defined according to EIP161 (balance = nonce = code = 0).
+ Empty(common.Address) bool
+
+ RevertToSnapshot(int)
+ Snapshot() int
+
+ AddLog(*types.Log)
+}
+
+// Account represents a contract or basic ethereum account.
+type Account interface {
+ SubBalance(amount *big.Int)
+ AddBalance(amount *big.Int)
+ SetBalance(*big.Int)
+ SetNonce(uint64)
+ Balance() *big.Int
+ Address() common.Address
+ ReturnGas(*big.Int)
+ SetCode(common.Hash, []byte)
+ ForEachStorage(cb func(key, value common.Hash) bool)
+ Value() *big.Int
+}
+
+// CallContext provides a basic interface for the EVM calling conventions. The EVM EVM
+// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
+type CallContext interface {
+ // Call another contract
+ Call(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
+ // Take another's contract code and execute within our own context
+ CallCode(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
+ // Same as CallCode except sender and value is propagated from parent to child scope
+ DelegateCall(env *EVM, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
+ // Create a new contract
+ Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
+}
diff --git a/core/vm/jit.go b/core/vm/jit.go
deleted file mode 100644
index ebbc58a359..0000000000
--- a/core/vm/jit.go
+++ /dev/null
@@ -1,512 +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 vm
-
-import (
- "fmt"
- "math/big"
- "sync/atomic"
- "time"
-
- "github.com/ubiq/go-ubiq/common"
- "github.com/ubiq/go-ubiq/crypto"
- "github.com/ubiq/go-ubiq/logger"
- "github.com/ubiq/go-ubiq/logger/glog"
- "github.com/ubiq/go-ubiq/params"
- "github.com/hashicorp/golang-lru"
-)
-
-// progStatus is the type for the JIT program status.
-type progStatus int32
-
-const (
- progUnknown progStatus = iota // unknown status
- progCompile // compile status
- progReady // ready for use status
- progError // error status (usually caused during compilation)
-
- defaultJitMaxCache int = 64 // maximum amount of jit cached programs
-)
-
-var MaxProgSize int // Max cache size for JIT programs
-
-var programs *lru.Cache // lru cache for the JIT programs.
-
-func init() {
- SetJITCacheSize(defaultJitMaxCache)
-}
-
-// SetJITCacheSize recreates the program cache with the max given size. Setting
-// a new cache is **not** thread safe. Use with caution.
-func SetJITCacheSize(size int) {
- programs, _ = lru.New(size)
-}
-
-// GetProgram returns the program by id or nil when non-existent
-func GetProgram(id common.Hash) *Program {
- if p, ok := programs.Get(id); ok {
- return p.(*Program)
- }
-
- return nil
-}
-
-// GenProgramStatus returns the status of the given program id
-func GetProgramStatus(id common.Hash) progStatus {
- program := GetProgram(id)
- if program != nil {
- return progStatus(atomic.LoadInt32(&program.status))
- }
-
- return progUnknown
-}
-
-// Program is a compiled program for the JIT VM and holds all required for
-// running a compiled JIT program.
-type Program struct {
- Id common.Hash // Id of the program
- status int32 // status should be accessed atomically
-
- contract *Contract
-
- instructions []programInstruction // instruction set
- mapping map[uint64]uint64 // real PC mapping to array indices
- destinations map[uint64]struct{} // cached jump destinations
-
- code []byte
-}
-
-// NewProgram returns a new JIT program
-func NewProgram(code []byte) *Program {
- program := &Program{
- Id: crypto.Keccak256Hash(code),
- mapping: make(map[uint64]uint64),
- destinations: make(map[uint64]struct{}),
- code: code,
- }
-
- programs.Add(program.Id, program)
- return program
-}
-
-func (p *Program) addInstr(op OpCode, pc uint64, fn instrFn, data *big.Int) {
- // PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit
- // PUSH is also allowed to calculate the same price for all PUSHes
- // DUP requirements are handled elsewhere (except for the stack limit check)
- baseOp := op
- if op >= PUSH1 && op <= PUSH32 {
- baseOp = PUSH1
- }
- if op >= DUP1 && op <= DUP16 {
- baseOp = DUP1
- }
- base := _baseCheck[baseOp]
-
- returns := op == RETURN || op == SUICIDE || op == STOP
- instr := instruction{op, pc, fn, data, base.gas, base.stackPop, base.stackPush, returns}
-
- p.instructions = append(p.instructions, instr)
- p.mapping[pc] = uint64(len(p.instructions) - 1)
-}
-
-// CompileProgram compiles the given program and return an error when it fails
-func CompileProgram(program *Program) (err error) {
- if progStatus(atomic.LoadInt32(&program.status)) == progCompile {
- return nil
- }
- atomic.StoreInt32(&program.status, int32(progCompile))
- defer func() {
- if err != nil {
- atomic.StoreInt32(&program.status, int32(progError))
- } else {
- atomic.StoreInt32(&program.status, int32(progReady))
- }
- }()
- if glog.V(logger.Debug) {
- glog.Infof("compiling %x\n", program.Id[:4])
- tstart := time.Now()
- defer func() {
- glog.Infof("compiled %x instrc: %d time: %v\n", program.Id[:4], len(program.instructions), time.Since(tstart))
- }()
- }
-
- // loop thru the opcodes and "compile" in to instructions
- for pc := uint64(0); pc < uint64(len(program.code)); pc++ {
- switch op := OpCode(program.code[pc]); op {
- case ADD:
- program.addInstr(op, pc, opAdd, nil)
- case SUB:
- program.addInstr(op, pc, opSub, nil)
- case MUL:
- program.addInstr(op, pc, opMul, nil)
- case DIV:
- program.addInstr(op, pc, opDiv, nil)
- case SDIV:
- program.addInstr(op, pc, opSdiv, nil)
- case MOD:
- program.addInstr(op, pc, opMod, nil)
- case SMOD:
- program.addInstr(op, pc, opSmod, nil)
- case EXP:
- program.addInstr(op, pc, opExp, nil)
- case SIGNEXTEND:
- program.addInstr(op, pc, opSignExtend, nil)
- case NOT:
- program.addInstr(op, pc, opNot, nil)
- case LT:
- program.addInstr(op, pc, opLt, nil)
- case GT:
- program.addInstr(op, pc, opGt, nil)
- case SLT:
- program.addInstr(op, pc, opSlt, nil)
- case SGT:
- program.addInstr(op, pc, opSgt, nil)
- case EQ:
- program.addInstr(op, pc, opEq, nil)
- case ISZERO:
- program.addInstr(op, pc, opIszero, nil)
- case AND:
- program.addInstr(op, pc, opAnd, nil)
- case OR:
- program.addInstr(op, pc, opOr, nil)
- case XOR:
- program.addInstr(op, pc, opXor, nil)
- case BYTE:
- program.addInstr(op, pc, opByte, nil)
- case ADDMOD:
- program.addInstr(op, pc, opAddmod, nil)
- case MULMOD:
- program.addInstr(op, pc, opMulmod, nil)
- case SHA3:
- program.addInstr(op, pc, opSha3, nil)
- case ADDRESS:
- program.addInstr(op, pc, opAddress, nil)
- case BALANCE:
- program.addInstr(op, pc, opBalance, nil)
- case ORIGIN:
- program.addInstr(op, pc, opOrigin, nil)
- case CALLER:
- program.addInstr(op, pc, opCaller, nil)
- case CALLVALUE:
- program.addInstr(op, pc, opCallValue, nil)
- case CALLDATALOAD:
- program.addInstr(op, pc, opCalldataLoad, nil)
- case CALLDATASIZE:
- program.addInstr(op, pc, opCalldataSize, nil)
- case CALLDATACOPY:
- program.addInstr(op, pc, opCalldataCopy, nil)
- case CODESIZE:
- program.addInstr(op, pc, opCodeSize, nil)
- case EXTCODESIZE:
- program.addInstr(op, pc, opExtCodeSize, nil)
- case CODECOPY:
- program.addInstr(op, pc, opCodeCopy, nil)
- case EXTCODECOPY:
- program.addInstr(op, pc, opExtCodeCopy, nil)
- case GASPRICE:
- program.addInstr(op, pc, opGasprice, nil)
- case BLOCKHASH:
- program.addInstr(op, pc, opBlockhash, nil)
- case COINBASE:
- program.addInstr(op, pc, opCoinbase, nil)
- case TIMESTAMP:
- program.addInstr(op, pc, opTimestamp, nil)
- case NUMBER:
- program.addInstr(op, pc, opNumber, nil)
- case DIFFICULTY:
- program.addInstr(op, pc, opDifficulty, nil)
- case GASLIMIT:
- program.addInstr(op, pc, opGasLimit, nil)
- case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
- size := uint64(op - PUSH1 + 1)
- bytes := getData([]byte(program.code), new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size))
-
- program.addInstr(op, pc, opPush, common.Bytes2Big(bytes))
-
- pc += size
-
- case POP:
- program.addInstr(op, pc, opPop, nil)
- case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
- program.addInstr(op, pc, opDup, big.NewInt(int64(op-DUP1+1)))
- case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
- program.addInstr(op, pc, opSwap, big.NewInt(int64(op-SWAP1+2)))
- case LOG0, LOG1, LOG2, LOG3, LOG4:
- program.addInstr(op, pc, opLog, big.NewInt(int64(op-LOG0)))
- case MLOAD:
- program.addInstr(op, pc, opMload, nil)
- case MSTORE:
- program.addInstr(op, pc, opMstore, nil)
- case MSTORE8:
- program.addInstr(op, pc, opMstore8, nil)
- case SLOAD:
- program.addInstr(op, pc, opSload, nil)
- case SSTORE:
- program.addInstr(op, pc, opSstore, nil)
- case JUMP:
- program.addInstr(op, pc, opJump, nil)
- case JUMPI:
- program.addInstr(op, pc, opJumpi, nil)
- case JUMPDEST:
- program.addInstr(op, pc, opJumpdest, nil)
- program.destinations[pc] = struct{}{}
- case PC:
- program.addInstr(op, pc, opPc, big.NewInt(int64(pc)))
- case MSIZE:
- program.addInstr(op, pc, opMsize, nil)
- case GAS:
- program.addInstr(op, pc, opGas, nil)
- case CREATE:
- program.addInstr(op, pc, opCreate, nil)
- case DELEGATECALL:
- // Instruction added regardless of homestead phase.
- // Homestead (and execution of the opcode) is checked during
- // runtime.
- program.addInstr(op, pc, opDelegateCall, nil)
- case CALL:
- program.addInstr(op, pc, opCall, nil)
- case CALLCODE:
- program.addInstr(op, pc, opCallCode, nil)
- case RETURN:
- program.addInstr(op, pc, opReturn, nil)
- case SUICIDE:
- program.addInstr(op, pc, opSuicide, nil)
- case STOP: // Stop the contract
- program.addInstr(op, pc, opStop, nil)
- default:
- program.addInstr(op, pc, nil, nil)
- }
- }
-
- optimiseProgram(program)
-
- return nil
-}
-
-// RunProgram runs the program given the environment and contract and returns an
-// error if the execution failed (non-consensus)
-func RunProgram(program *Program, env Environment, contract *Contract, input []byte) ([]byte, error) {
- return runProgram(program, 0, NewMemory(), newstack(), env, contract, input)
-}
-
-func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env Environment, contract *Contract, input []byte) ([]byte, error) {
- contract.Input = input
-
- var (
- pc uint64 = program.mapping[pcstart]
- instrCount = 0
- )
-
- if glog.V(logger.Debug) {
- glog.Infof("running JIT program %x\n", program.Id[:4])
- tstart := time.Now()
- defer func() {
- glog.Infof("JIT program %x done. time: %v instrc: %v\n", program.Id[:4], time.Since(tstart), instrCount)
- }()
- }
-
- homestead := env.ChainConfig().IsHomestead(env.BlockNumber())
- for pc < uint64(len(program.instructions)) {
- instrCount++
-
- instr := program.instructions[pc]
- if instr.Op() == DELEGATECALL && !homestead {
- return nil, fmt.Errorf("Invalid opcode 0x%x", instr.Op())
- }
-
- ret, err := instr.do(program, &pc, env, contract, mem, stack)
- if err != nil {
- return nil, err
- }
-
- if instr.halts() {
- return ret, nil
- }
- }
-
- contract.Input = nil
-
- return nil, nil
-}
-
-// validDest checks if the given destination is a valid one given the
-// destination table of the program
-func validDest(dests map[uint64]struct{}, dest *big.Int) bool {
- // PC cannot go beyond len(code) and certainly can't be bigger than 64bits.
- // Don't bother checking for JUMPDEST in that case.
- if dest.Cmp(bigMaxUint64) > 0 {
- return false
- }
- _, ok := dests[dest.Uint64()]
- return ok
-}
-
-// jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
-// the operation. This does not reduce gas or resizes the memory.
-func jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
- var (
- gas = new(big.Int)
- newMemSize *big.Int = new(big.Int)
- )
- err := jitBaseCheck(instr, stack, gas)
- if err != nil {
- return nil, nil, err
- }
-
- // stack Check, memory resize & gas phase
- switch op := instr.op; op {
- case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
- n := int(op - SWAP1 + 2)
- err := stack.require(n)
- if err != nil {
- return nil, nil, err
- }
- gas.Set(GasFastestStep)
- case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
- n := int(op - DUP1 + 1)
- err := stack.require(n)
- if err != nil {
- return nil, nil, err
- }
- gas.Set(GasFastestStep)
- case LOG0, LOG1, LOG2, LOG3, LOG4:
- n := int(op - LOG0)
- err := stack.require(n + 2)
- if err != nil {
- return nil, nil, err
- }
-
- mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1]
-
- add := new(big.Int)
- gas.Add(gas, params.LogGas)
- gas.Add(gas, add.Mul(big.NewInt(int64(n)), params.LogTopicGas))
- gas.Add(gas, add.Mul(mSize, params.LogDataGas))
-
- newMemSize = calcMemSize(mStart, mSize)
- case EXP:
- gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas))
- case SSTORE:
- err := stack.require(2)
- if err != nil {
- return nil, nil, err
- }
-
- var g *big.Int
- y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
- val := statedb.GetState(contract.Address(), common.BigToHash(x))
-
- // This checks for 3 scenario's and calculates gas accordingly
- // 1. From a zero-value address to a non-zero value (NEW VALUE)
- // 2. From a non-zero value address to a zero-value address (DELETE)
- // 3. From a non-zero to a non-zero (CHANGE)
- if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
- g = params.SstoreSetGas
- } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
- statedb.AddRefund(params.SstoreRefundGas)
-
- g = params.SstoreClearGas
- } else {
- g = params.SstoreResetGas
- }
- gas.Set(g)
- case SUICIDE:
- if !statedb.HasSuicided(contract.Address()) {
- statedb.AddRefund(params.SuicideRefundGas)
- }
- case MLOAD:
- newMemSize = calcMemSize(stack.peek(), u256(32))
- case MSTORE8:
- newMemSize = calcMemSize(stack.peek(), u256(1))
- case MSTORE:
- newMemSize = calcMemSize(stack.peek(), u256(32))
- case RETURN:
- newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
- case SHA3:
- newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
-
- words := toWordSize(stack.data[stack.len()-2])
- gas.Add(gas, words.Mul(words, params.Sha3WordGas))
- case CALLDATACOPY:
- newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
-
- words := toWordSize(stack.data[stack.len()-3])
- gas.Add(gas, words.Mul(words, params.CopyGas))
- case CODECOPY:
- newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
-
- words := toWordSize(stack.data[stack.len()-3])
- gas.Add(gas, words.Mul(words, params.CopyGas))
- case EXTCODECOPY:
- newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
-
- words := toWordSize(stack.data[stack.len()-4])
- gas.Add(gas, words.Mul(words, params.CopyGas))
-
- case CREATE:
- newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
- case CALL, CALLCODE:
- gas.Add(gas, stack.data[stack.len()-1])
-
- if op == CALL {
- if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
- gas.Add(gas, params.CallNewAccountGas)
- }
- }
-
- if len(stack.data[stack.len()-3].Bytes()) > 0 {
- gas.Add(gas, params.CallValueTransferGas)
- }
-
- x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
- y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
-
- newMemSize = common.BigMax(x, y)
- case DELEGATECALL:
- gas.Add(gas, stack.data[stack.len()-1])
-
- x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6])
- y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4])
-
- newMemSize = common.BigMax(x, y)
- }
- quadMemGas(mem, newMemSize, gas)
-
- return newMemSize, gas, nil
-}
-
-// jitBaseCheck is the same as baseCheck except it doesn't do the look up in the
-// gas table. This is done during compilation instead.
-func jitBaseCheck(instr instruction, stack *Stack, gas *big.Int) error {
- err := stack.require(instr.spop)
- if err != nil {
- return err
- }
-
- if instr.spush > 0 && stack.len()-instr.spop+instr.spush > int(params.StackLimit.Int64()) {
- return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64())
- }
-
- // nil on gas means no base calculation
- if instr.gas == nil {
- return nil
- }
-
- gas.Add(gas, instr.gas)
-
- return nil
-}
diff --git a/core/vm/jit_optimiser.go b/core/vm/jit_optimiser.go
deleted file mode 100644
index 9886eb4d7f..0000000000
--- a/core/vm/jit_optimiser.go
+++ /dev/null
@@ -1,123 +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 vm
-
-import (
- "math/big"
- "time"
-
- "github.com/ubiq/go-ubiq/logger"
- "github.com/ubiq/go-ubiq/logger/glog"
-)
-
-// optimeProgram optimises a JIT program creating segments out of program
-// instructions. Currently covered are multi-pushes and static jumps
-func optimiseProgram(program *Program) {
- var load []instruction
-
- var (
- statsJump = 0
- statsPush = 0
- )
-
- if glog.V(logger.Debug) {
- glog.Infof("optimising %x\n", program.Id[:4])
- tstart := time.Now()
- defer func() {
- glog.Infof("optimised %x done in %v with JMP: %d PSH: %d\n", program.Id[:4], time.Since(tstart), statsJump, statsPush)
- }()
- }
-
- /*
- code := Parse(program.code)
- for _, test := range [][]OpCode{
- []OpCode{PUSH, PUSH, ADD},
- []OpCode{PUSH, PUSH, SUB},
- []OpCode{PUSH, PUSH, MUL},
- []OpCode{PUSH, PUSH, DIV},
- } {
- matchCount := 0
- MatchFn(code, test, func(i int) bool {
- matchCount++
- return true
- })
- fmt.Printf("found %d match count on: %v\n", matchCount, test)
- }
- */
-
- for i := 0; i < len(program.instructions); i++ {
- instr := program.instructions[i].(instruction)
-
- switch {
- case instr.op.IsPush():
- load = append(load, instr)
- case instr.op.IsStaticJump():
- if len(load) == 0 {
- continue
- }
- // if the push load is greater than 1, finalise that
- // segment first
- if len(load) > 2 {
- seg, size := makePushSeg(load[:len(load)-1])
- program.instructions[i-size-1] = seg
- statsPush++
- }
- // create a segment consisting of a pre determined
- // jump, destination and validity.
- seg := makeStaticJumpSeg(load[len(load)-1].data, program)
- program.instructions[i-1] = seg
- statsJump++
-
- load = nil
- default:
- // create a new N pushes segment
- if len(load) > 1 {
- seg, size := makePushSeg(load)
- program.instructions[i-size] = seg
- statsPush++
- }
- load = nil
- }
- }
-}
-
-// makePushSeg creates a new push segment from N amount of push instructions
-func makePushSeg(instrs []instruction) (pushSeg, int) {
- var (
- data []*big.Int
- gas = new(big.Int)
- )
-
- for _, instr := range instrs {
- data = append(data, instr.data)
- gas.Add(gas, instr.gas)
- }
-
- return pushSeg{data, gas}, len(instrs)
-}
-
-// makeStaticJumpSeg creates a new static jump segment from a predefined
-// destination (PUSH, JUMP).
-func makeStaticJumpSeg(to *big.Int, program *Program) jumpSeg {
- gas := new(big.Int)
- gas.Add(gas, _baseCheck[PUSH1].gas)
- gas.Add(gas, _baseCheck[JUMP].gas)
-
- contract := &Contract{Code: program.code}
- pos, err := jump(program.mapping, program.destinations, contract, to)
- return jumpSeg{pos, err, gas}
-}
diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go
deleted file mode 100644
index 95aa6241c1..0000000000
--- a/core/vm/jit_test.go
+++ /dev/null
@@ -1,214 +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 vm
-
-import (
- "math/big"
- "testing"
- "time"
-
- "github.com/ubiq/go-ubiq/common"
- "github.com/ubiq/go-ubiq/crypto"
- "github.com/ubiq/go-ubiq/params"
-)
-
-const maxRun = 1000
-
-func TestSegmenting(t *testing.T) {
- prog := NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, 0x0})
- err := CompileProgram(prog)
- if err != nil {
- t.Fatal(err)
- }
-
- if instr, ok := prog.instructions[0].(pushSeg); ok {
- if len(instr.data) != 2 {
- t.Error("expected 2 element width pushSegment, got", len(instr.data))
- }
- } else {
- t.Errorf("expected instr[0] to be a pushSeg, got %T", prog.instructions[0])
- }
-
- prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
- err = CompileProgram(prog)
- if err != nil {
- t.Fatal(err)
- }
- if _, ok := prog.instructions[1].(jumpSeg); ok {
- } else {
- t.Errorf("expected instr[1] to be jumpSeg, got %T", prog.instructions[1])
- }
-
- prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
- err = CompileProgram(prog)
- if err != nil {
- t.Fatal(err)
- }
- if instr, ok := prog.instructions[0].(pushSeg); ok {
- if len(instr.data) != 2 {
- t.Error("expected 2 element width pushSegment, got", len(instr.data))
- }
- } else {
- t.Errorf("expected instr[0] to be a pushSeg, got %T", prog.instructions[0])
- }
- if _, ok := prog.instructions[2].(jumpSeg); ok {
- } else {
- t.Errorf("expected instr[1] to be jumpSeg, got %T", prog.instructions[1])
- }
-}
-
-func TestCompiling(t *testing.T) {
- prog := NewProgram([]byte{0x60, 0x10})
- err := CompileProgram(prog)
- if err != nil {
- t.Error("didn't expect compile error")
- }
-
- if len(prog.instructions) != 1 {
- t.Error("expected 1 compiled instruction, got", len(prog.instructions))
- }
-}
-
-func TestResetInput(t *testing.T) {
- var sender account
-
- env := NewEnv(&Config{EnableJit: true, ForceJit: true})
- contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
- contract.CodeAddr = &common.Address{}
-
- program := NewProgram([]byte{})
- RunProgram(program, env, contract, []byte{0xbe, 0xef})
- if contract.Input != nil {
- t.Errorf("expected input to be nil, got %x", contract.Input)
- }
-}
-
-func TestPcMappingToInstruction(t *testing.T) {
- program := NewProgram([]byte{byte(PUSH2), 0xbe, 0xef, byte(ADD)})
- CompileProgram(program)
- if program.mapping[3] != 1 {
- t.Error("expected mapping PC 4 to me instr no. 2, got", program.mapping[4])
- }
-}
-
-var benchmarks = map[string]vmBench{
- "pushes": vmBench{
- false, false, false,
- common.Hex2Bytes("600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01"), nil,
- },
-}
-
-func BenchmarkPushes(b *testing.B) {
- runVmBench(benchmarks["pushes"], b)
-}
-
-type vmBench struct {
- precompile bool // compile prior to executing
- nojit bool // ignore jit (sets DisbaleJit = true
- forcejit bool // forces the jit, precompile is ignored
-
- code []byte
- input []byte
-}
-
-type account struct{}
-
-func (account) SubBalance(amount *big.Int) {}
-func (account) AddBalance(amount *big.Int) {}
-func (account) SetAddress(common.Address) {}
-func (account) Value() *big.Int { return nil }
-func (account) SetBalance(*big.Int) {}
-func (account) SetNonce(uint64) {}
-func (account) Balance() *big.Int { return nil }
-func (account) Address() common.Address { return common.Address{} }
-func (account) ReturnGas(*big.Int, *big.Int) {}
-func (account) SetCode(common.Hash, []byte) {}
-func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
-
-func runVmBench(test vmBench, b *testing.B) {
- var sender account
-
- if test.precompile && !test.forcejit {
- NewProgram(test.code)
- }
- env := NewEnv(&Config{EnableJit: !test.nojit, ForceJit: test.forcejit})
-
- b.ResetTimer()
-
- for i := 0; i < b.N; i++ {
- context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
- context.Code = test.code
- context.CodeAddr = &common.Address{}
- _, err := env.Vm().Run(context, test.input)
- if err != nil {
- b.Error(err)
- b.FailNow()
- }
- }
-}
-
-type Env struct {
- gasLimit *big.Int
- depth int
- evm *EVM
-}
-
-func NewEnv(config *Config) *Env {
- env := &Env{gasLimit: big.NewInt(10000), depth: 0}
- env.evm = New(env, *config)
- return env
-}
-
-func (self *Env) ChainConfig() *params.ChainConfig {
- return params.TestChainConfig
-}
-func (self *Env) Vm() Vm { return self.evm }
-func (self *Env) Origin() common.Address { return common.Address{} }
-func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
-
-//func (self *Env) PrevHash() []byte { return self.parent }
-func (self *Env) Coinbase() common.Address { return common.Address{} }
-func (self *Env) SnapshotDatabase() int { return 0 }
-func (self *Env) RevertToSnapshot(int) {}
-func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) }
-func (self *Env) Difficulty() *big.Int { return big.NewInt(0) }
-func (self *Env) Db() Database { return nil }
-func (self *Env) GasLimit() *big.Int { return self.gasLimit }
-func (self *Env) VmType() Type { return StdVmTy }
-func (self *Env) GetHash(n uint64) common.Hash {
- return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
-}
-func (self *Env) AddLog(log *Log) {
-}
-func (self *Env) Depth() int { return self.depth }
-func (self *Env) SetDepth(i int) { self.depth = i }
-func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
- return true
-}
-func (self *Env) Transfer(from, to Account, amount *big.Int) {}
-func (self *Env) Call(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return nil, nil
-}
-func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return nil, nil
-}
-func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- return nil, common.Address{}, nil
-}
-func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- return nil, nil
-}
diff --git a/core/vm/jit_util.go b/core/vm/jit_util.go
deleted file mode 100644
index 947dae88f4..0000000000
--- a/core/vm/jit_util.go
+++ /dev/null
@@ -1,68 +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 vm
-
-// Parse parses all opcodes from the given code byte slice. This function
-// performs no error checking and may return non-existing opcodes.
-func Parse(code []byte) (opcodes []OpCode) {
- for pc := uint64(0); pc < uint64(len(code)); pc++ {
- op := OpCode(code[pc])
-
- switch op {
- case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
- a := uint64(op) - uint64(PUSH1) + 1
- pc += a
- opcodes = append(opcodes, PUSH)
- case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
- opcodes = append(opcodes, DUP)
- case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
- opcodes = append(opcodes, SWAP)
- default:
- opcodes = append(opcodes, op)
- }
- }
-
- return opcodes
-}
-
-// MatchFn searcher for match in the given input and calls matcheFn if it finds
-// an appropriate match. matcherFn yields the starting position in the input.
-// MatchFn will continue to search for a match until it reaches the end of the
-// buffer or if matcherFn return false.
-func MatchFn(input, match []OpCode, matcherFn func(int) bool) {
- // short circuit if either input or match is empty or if the match is
- // greater than the input
- if len(input) == 0 || len(match) == 0 || len(match) > len(input) {
- return
- }
-
-main:
- for i, op := range input[:len(input)+1-len(match)] {
- // match first opcode and continue search
- if op == match[0] {
- for j := 1; j < len(match); j++ {
- if input[i+j] != match[j] {
- continue main
- }
- }
- // check for abort instruction
- if !matcherFn(i) {
- return
- }
- }
- }
-}
diff --git a/core/vm/jit_util_test.go b/core/vm/jit_util_test.go
deleted file mode 100644
index 2123efe59e..0000000000
--- a/core/vm/jit_util_test.go
+++ /dev/null
@@ -1,84 +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 vm
-
-import "testing"
-
-type matchTest struct {
- input []OpCode
- match []OpCode
- matches int
-}
-
-func TestMatchFn(t *testing.T) {
- tests := []matchTest{
- matchTest{
- []OpCode{PUSH1, PUSH1, MSTORE, JUMP},
- []OpCode{PUSH1, MSTORE},
- 1,
- },
- matchTest{
- []OpCode{PUSH1, PUSH1, MSTORE, JUMP},
- []OpCode{PUSH1, MSTORE, PUSH1},
- 0,
- },
- matchTest{
- []OpCode{},
- []OpCode{PUSH1},
- 0,
- },
- }
-
- for i, test := range tests {
- var matchCount int
- MatchFn(test.input, test.match, func(i int) bool {
- matchCount++
- return true
- })
- if matchCount != test.matches {
- t.Errorf("match count failed on test[%d]: expected %d matches, got %d", i, test.matches, matchCount)
- }
- }
-}
-
-type parseTest struct {
- base OpCode
- size int
- output OpCode
-}
-
-func TestParser(t *testing.T) {
- tests := []parseTest{
- parseTest{PUSH1, 32, PUSH},
- parseTest{DUP1, 16, DUP},
- parseTest{SWAP1, 16, SWAP},
- parseTest{MSTORE, 1, MSTORE},
- }
-
- for _, test := range tests {
- for i := 0; i < test.size; i++ {
- code := append([]byte{byte(byte(test.base) + byte(i))}, make([]byte, i+1)...)
- output := Parse(code)
- if len(output) == 0 {
- t.Fatal("empty output")
- }
- if output[0] != test.output {
- t.Errorf("%v failed: expected %v but got %v", test.base+OpCode(i), test.output, output[0])
- }
- }
- }
-}
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index c6f234e6ea..c388572239 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -22,152 +22,838 @@ import (
"github.com/ubiq/go-ubiq/params"
)
-type jumpPtr struct {
- fn instrFn
+type (
+ executionFunc func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
+ gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, *big.Int) *big.Int
+ stackValidationFunc func(*Stack) error
+ memorySizeFunc func(*Stack) *big.Int
+)
+
+type operation struct {
+ // op is the operation function
+ execute executionFunc
+ // gasCost is the gas function and returns the gas required for execution
+ gasCost gasFunc
+ // validateStack validates the stack (size) for the operation
+ validateStack stackValidationFunc
+ // memorySize returns the memory size required for the operation
+ memorySize memorySizeFunc
+ // halts indicates whether the operation shoult halt further execution
+ // and return
+ halts bool
+ // jumps indicates whether operation made a jump. This prevents the program
+ // counter from further incrementing.
+ jumps bool
+ // valid is used to check whether the retrieved operation is valid and known
valid bool
}
-type vmJumpTable [256]jumpPtr
+var defaultJumpTable = NewJumpTable()
-func newJumpTable(ruleset *params.ChainConfig, blockNumber *big.Int) vmJumpTable {
- var jumpTable vmJumpTable
+func NewJumpTable() [256]operation {
+ return [256]operation{
+ ADD: {
+ execute: opAdd,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ SUB: {
+ execute: opSub,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ MUL: {
+ execute: opMul,
+ gasCost: constGasFunc(GasFastStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ DIV: {
+ execute: opDiv,
+ gasCost: constGasFunc(GasFastStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ SDIV: {
+ execute: opSdiv,
+ gasCost: constGasFunc(GasFastStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ MOD: {
+ execute: opMod,
+ gasCost: constGasFunc(GasFastStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ SMOD: {
+ execute: opSmod,
+ gasCost: constGasFunc(GasFastStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ EXP: {
+ execute: opExp,
+ gasCost: gasExp,
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ SIGNEXTEND: {
+ execute: opSignExtend,
+ gasCost: constGasFunc(GasFastStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ NOT: {
+ execute: opNot,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(1, 1),
+ valid: true,
+ },
+ LT: {
+ execute: opLt,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ GT: {
+ execute: opGt,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ SLT: {
+ execute: opSlt,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ SGT: {
+ execute: opSgt,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ EQ: {
+ execute: opEq,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ ISZERO: {
+ execute: opIszero,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(1, 1),
+ valid: true,
+ },
+ AND: {
+ execute: opAnd,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ OR: {
+ execute: opOr,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ XOR: {
+ execute: opXor,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ BYTE: {
+ execute: opByte,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ ADDMOD: {
+ execute: opAddmod,
+ gasCost: constGasFunc(GasMidStep),
+ validateStack: makeStackFunc(3, 1),
+ valid: true,
+ },
+ MULMOD: {
+ execute: opMulmod,
+ gasCost: constGasFunc(GasMidStep),
+ validateStack: makeStackFunc(3, 1),
+ valid: true,
+ },
+ SHA3: {
+ execute: opSha3,
+ gasCost: gasSha3,
+ validateStack: makeStackFunc(2, 1),
+ memorySize: memorySha3,
+ valid: true,
+ },
+ ADDRESS: {
+ execute: opAddress,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ BALANCE: {
+ execute: opBalance,
+ gasCost: gasBalance,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ ORIGIN: {
+ execute: opOrigin,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ CALLER: {
+ execute: opCaller,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ CALLVALUE: {
+ execute: opCallValue,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ CALLDATALOAD: {
+ execute: opCalldataLoad,
+ gasCost: constGasFunc(GasFastestStep),
+ validateStack: makeStackFunc(1, 1),
+ valid: true,
+ },
+ CALLDATASIZE: {
+ execute: opCalldataSize,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ CALLDATACOPY: {
+ execute: opCalldataCopy,
+ gasCost: gasCalldataCopy,
+ validateStack: makeStackFunc(3, 1),
+ memorySize: memoryCalldataCopy,
+ valid: true,
+ },
+ CODESIZE: {
+ execute: opCodeSize,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ EXTCODESIZE: {
+ execute: opExtCodeSize,
+ gasCost: gasExtCodeSize,
+ validateStack: makeStackFunc(1, 1),
+ valid: true,
+ },
+ CODECOPY: {
+ execute: opCodeCopy,
+ gasCost: gasCodeCopy,
+ validateStack: makeStackFunc(3, 0),
+ memorySize: memoryCodeCopy,
+ valid: true,
+ },
+ EXTCODECOPY: {
+ execute: opExtCodeCopy,
+ gasCost: gasExtCodeCopy,
+ validateStack: makeStackFunc(4, 0),
+ memorySize: memoryExtCodeCopy,
+ valid: true,
+ },
+ GASPRICE: {
+ execute: opGasprice,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ BLOCKHASH: {
+ execute: opBlockhash,
+ gasCost: constGasFunc(GasExtStep),
+ validateStack: makeStackFunc(1, 1),
+ valid: true,
+ },
+ COINBASE: {
+ execute: opCoinbase,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ TIMESTAMP: {
+ execute: opTimestamp,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ NUMBER: {
+ execute: opNumber,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ DIFFICULTY: {
+ execute: opDifficulty,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ GASLIMIT: {
+ execute: opGasLimit,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ POP: {
+ execute: opPop,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(1, 0),
+ valid: true,
+ },
+ MLOAD: {
+ execute: opMload,
+ gasCost: gasMLoad,
+ validateStack: makeStackFunc(1, 1),
+ memorySize: memoryMLoad,
+ valid: true,
+ },
+ MSTORE: {
+ execute: opMstore,
+ gasCost: gasMStore,
+ validateStack: makeStackFunc(2, 0),
+ memorySize: memoryMStore,
+ valid: true,
+ },
+ MSTORE8: {
+ execute: opMstore8,
+ gasCost: gasMStore8,
+ memorySize: memoryMStore8,
+ validateStack: makeStackFunc(2, 0),
- // when initialising a new VM execution we must first check the homestead
- // changes.
- if ruleset.IsHomestead(blockNumber) {
- jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true}
+ valid: true,
+ },
+ SLOAD: {
+ execute: opSload,
+ gasCost: gasSLoad,
+ validateStack: makeStackFunc(1, 1),
+ valid: true,
+ },
+ SSTORE: {
+ execute: opSstore,
+ gasCost: gasSStore,
+ validateStack: makeStackFunc(2, 0),
+ valid: true,
+ },
+ JUMPDEST: {
+ execute: opJumpdest,
+ gasCost: constGasFunc(params.JumpdestGas),
+ validateStack: makeStackFunc(0, 0),
+ valid: true,
+ },
+ PC: {
+ execute: opPc,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ MSIZE: {
+ execute: opMsize,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ GAS: {
+ execute: opGas,
+ gasCost: constGasFunc(GasQuickStep),
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ CREATE: {
+ execute: opCreate,
+ gasCost: gasCreate,
+ validateStack: makeStackFunc(3, 1),
+ memorySize: memoryCreate,
+ valid: true,
+ },
+ CALL: {
+ execute: opCall,
+ gasCost: gasCall,
+ validateStack: makeStackFunc(7, 1),
+ memorySize: memoryCall,
+ valid: true,
+ },
+ CALLCODE: {
+ execute: opCallCode,
+ gasCost: gasCallCode,
+ validateStack: makeStackFunc(7, 1),
+ memorySize: memoryCall,
+ valid: true,
+ },
+ DELEGATECALL: {
+ execute: opDelegateCall,
+ gasCost: gasDelegateCall,
+ validateStack: makeStackFunc(6, 1),
+ memorySize: memoryDelegateCall,
+ valid: true,
+ },
+ RETURN: {
+ execute: opReturn,
+ gasCost: gasReturn,
+ validateStack: makeStackFunc(2, 0),
+ memorySize: memoryReturn,
+ halts: true,
+ valid: true,
+ },
+ SUICIDE: {
+ execute: opSuicide,
+ gasCost: gasSuicide,
+ validateStack: makeStackFunc(1, 0),
+ halts: true,
+ valid: true,
+ },
+ JUMP: {
+ execute: opJump,
+ gasCost: constGasFunc(GasMidStep),
+ validateStack: makeStackFunc(1, 0),
+ jumps: true,
+ valid: true,
+ },
+ JUMPI: {
+ execute: opJumpi,
+ gasCost: constGasFunc(GasSlowStep),
+ validateStack: makeStackFunc(2, 0),
+ jumps: true,
+ valid: true,
+ },
+ STOP: {
+ execute: opStop,
+ gasCost: constGasFunc(Zero),
+ validateStack: makeStackFunc(0, 0),
+ halts: true,
+ valid: true,
+ },
+ LOG0: {
+ execute: makeLog(0),
+ gasCost: makeGasLog(0),
+ validateStack: makeStackFunc(2, 0),
+ memorySize: memoryLog,
+ valid: true,
+ },
+ LOG1: {
+ execute: makeLog(1),
+ gasCost: makeGasLog(1),
+ validateStack: makeStackFunc(3, 0),
+ memorySize: memoryLog,
+ valid: true,
+ },
+ LOG2: {
+ execute: makeLog(2),
+ gasCost: makeGasLog(2),
+ validateStack: makeStackFunc(4, 0),
+ memorySize: memoryLog,
+ valid: true,
+ },
+ LOG3: {
+ execute: makeLog(3),
+ gasCost: makeGasLog(3),
+ validateStack: makeStackFunc(5, 0),
+ memorySize: memoryLog,
+ valid: true,
+ },
+ LOG4: {
+ execute: makeLog(4),
+ gasCost: makeGasLog(4),
+ validateStack: makeStackFunc(6, 0),
+ memorySize: memoryLog,
+ valid: true,
+ },
+ SWAP1: {
+ execute: makeSwap(1),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(2, 0),
+ valid: true,
+ },
+ SWAP2: {
+ execute: makeSwap(2),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(3, 0),
+ valid: true,
+ },
+ SWAP3: {
+ execute: makeSwap(3),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(4, 0),
+ valid: true,
+ },
+ SWAP4: {
+ execute: makeSwap(4),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(5, 0),
+ valid: true,
+ },
+ SWAP5: {
+ execute: makeSwap(5),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(6, 0),
+ valid: true,
+ },
+ SWAP6: {
+ execute: makeSwap(6),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(7, 0),
+ valid: true,
+ },
+ SWAP7: {
+ execute: makeSwap(7),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(8, 0),
+ valid: true,
+ },
+ SWAP8: {
+ execute: makeSwap(8),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(9, 0),
+ valid: true,
+ },
+ SWAP9: {
+ execute: makeSwap(9),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(10, 0),
+ valid: true,
+ },
+ SWAP10: {
+ execute: makeSwap(10),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(11, 0),
+ valid: true,
+ },
+ SWAP11: {
+ execute: makeSwap(11),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(12, 0),
+ valid: true,
+ },
+ SWAP12: {
+ execute: makeSwap(12),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(13, 0),
+ valid: true,
+ },
+ SWAP13: {
+ execute: makeSwap(13),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(14, 0),
+ valid: true,
+ },
+ SWAP14: {
+ execute: makeSwap(14),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(15, 0),
+ valid: true,
+ },
+ SWAP15: {
+ execute: makeSwap(15),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(16, 0),
+ valid: true,
+ },
+ SWAP16: {
+ execute: makeSwap(16),
+ gasCost: gasSwap,
+ validateStack: makeStackFunc(17, 0),
+ valid: true,
+ },
+ PUSH1: {
+ execute: makePush(1, big.NewInt(1)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH2: {
+ execute: makePush(2, big.NewInt(2)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH3: {
+ execute: makePush(3, big.NewInt(3)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH4: {
+ execute: makePush(4, big.NewInt(4)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH5: {
+ execute: makePush(5, big.NewInt(5)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH6: {
+ execute: makePush(6, big.NewInt(6)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH7: {
+ execute: makePush(7, big.NewInt(7)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH8: {
+ execute: makePush(8, big.NewInt(8)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH9: {
+ execute: makePush(9, big.NewInt(9)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH10: {
+ execute: makePush(10, big.NewInt(10)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH11: {
+ execute: makePush(11, big.NewInt(11)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH12: {
+ execute: makePush(12, big.NewInt(12)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH13: {
+ execute: makePush(13, big.NewInt(13)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH14: {
+ execute: makePush(14, big.NewInt(14)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH15: {
+ execute: makePush(15, big.NewInt(15)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH16: {
+ execute: makePush(16, big.NewInt(16)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH17: {
+ execute: makePush(17, big.NewInt(17)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH18: {
+ execute: makePush(18, big.NewInt(18)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH19: {
+ execute: makePush(19, big.NewInt(19)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH20: {
+ execute: makePush(20, big.NewInt(20)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH21: {
+ execute: makePush(21, big.NewInt(21)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH22: {
+ execute: makePush(22, big.NewInt(22)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH23: {
+ execute: makePush(23, big.NewInt(23)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH24: {
+ execute: makePush(24, big.NewInt(24)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH25: {
+ execute: makePush(25, big.NewInt(25)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH26: {
+ execute: makePush(26, big.NewInt(26)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH27: {
+ execute: makePush(27, big.NewInt(27)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH28: {
+ execute: makePush(28, big.NewInt(28)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH29: {
+ execute: makePush(29, big.NewInt(29)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH30: {
+ execute: makePush(30, big.NewInt(30)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH31: {
+ execute: makePush(31, big.NewInt(31)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ PUSH32: {
+ execute: makePush(32, big.NewInt(32)),
+ gasCost: gasPush,
+ validateStack: makeStackFunc(0, 1),
+ valid: true,
+ },
+ DUP1: {
+ execute: makeDup(1),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(1, 1),
+ valid: true,
+ },
+ DUP2: {
+ execute: makeDup(2),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(2, 1),
+ valid: true,
+ },
+ DUP3: {
+ execute: makeDup(3),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(3, 1),
+ valid: true,
+ },
+ DUP4: {
+ execute: makeDup(4),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(4, 1),
+ valid: true,
+ },
+ DUP5: {
+ execute: makeDup(5),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(5, 1),
+ valid: true,
+ },
+ DUP6: {
+ execute: makeDup(6),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(6, 1),
+ valid: true,
+ },
+ DUP7: {
+ execute: makeDup(7),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(7, 1),
+ valid: true,
+ },
+ DUP8: {
+ execute: makeDup(8),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(8, 1),
+ valid: true,
+ },
+ DUP9: {
+ execute: makeDup(9),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(9, 1),
+ valid: true,
+ },
+ DUP10: {
+ execute: makeDup(10),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(10, 1),
+ valid: true,
+ },
+ DUP11: {
+ execute: makeDup(11),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(11, 1),
+ valid: true,
+ },
+ DUP12: {
+ execute: makeDup(12),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(12, 1),
+ valid: true,
+ },
+ DUP13: {
+ execute: makeDup(13),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(13, 1),
+ valid: true,
+ },
+ DUP14: {
+ execute: makeDup(14),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(14, 1),
+ valid: true,
+ },
+ DUP15: {
+ execute: makeDup(15),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(15, 1),
+ valid: true,
+ },
+ DUP16: {
+ execute: makeDup(16),
+ gasCost: gasDup,
+ validateStack: makeStackFunc(16, 1),
+ valid: true,
+ },
}
-
- jumpTable[ADD] = jumpPtr{opAdd, true}
- jumpTable[SUB] = jumpPtr{opSub, true}
- jumpTable[MUL] = jumpPtr{opMul, true}
- jumpTable[DIV] = jumpPtr{opDiv, true}
- jumpTable[SDIV] = jumpPtr{opSdiv, true}
- jumpTable[MOD] = jumpPtr{opMod, true}
- jumpTable[SMOD] = jumpPtr{opSmod, true}
- jumpTable[EXP] = jumpPtr{opExp, true}
- jumpTable[SIGNEXTEND] = jumpPtr{opSignExtend, true}
- jumpTable[NOT] = jumpPtr{opNot, true}
- jumpTable[LT] = jumpPtr{opLt, true}
- jumpTable[GT] = jumpPtr{opGt, true}
- jumpTable[SLT] = jumpPtr{opSlt, true}
- jumpTable[SGT] = jumpPtr{opSgt, true}
- jumpTable[EQ] = jumpPtr{opEq, true}
- jumpTable[ISZERO] = jumpPtr{opIszero, true}
- jumpTable[AND] = jumpPtr{opAnd, true}
- jumpTable[OR] = jumpPtr{opOr, true}
- jumpTable[XOR] = jumpPtr{opXor, true}
- jumpTable[BYTE] = jumpPtr{opByte, true}
- jumpTable[ADDMOD] = jumpPtr{opAddmod, true}
- jumpTable[MULMOD] = jumpPtr{opMulmod, true}
- jumpTable[SHA3] = jumpPtr{opSha3, true}
- jumpTable[ADDRESS] = jumpPtr{opAddress, true}
- jumpTable[BALANCE] = jumpPtr{opBalance, true}
- jumpTable[ORIGIN] = jumpPtr{opOrigin, true}
- jumpTable[CALLER] = jumpPtr{opCaller, true}
- jumpTable[CALLVALUE] = jumpPtr{opCallValue, true}
- jumpTable[CALLDATALOAD] = jumpPtr{opCalldataLoad, true}
- jumpTable[CALLDATASIZE] = jumpPtr{opCalldataSize, true}
- jumpTable[CALLDATACOPY] = jumpPtr{opCalldataCopy, true}
- jumpTable[CODESIZE] = jumpPtr{opCodeSize, true}
- jumpTable[EXTCODESIZE] = jumpPtr{opExtCodeSize, true}
- jumpTable[CODECOPY] = jumpPtr{opCodeCopy, true}
- jumpTable[EXTCODECOPY] = jumpPtr{opExtCodeCopy, true}
- jumpTable[GASPRICE] = jumpPtr{opGasprice, true}
- jumpTable[BLOCKHASH] = jumpPtr{opBlockhash, true}
- jumpTable[COINBASE] = jumpPtr{opCoinbase, true}
- jumpTable[TIMESTAMP] = jumpPtr{opTimestamp, true}
- jumpTable[NUMBER] = jumpPtr{opNumber, true}
- jumpTable[DIFFICULTY] = jumpPtr{opDifficulty, true}
- jumpTable[GASLIMIT] = jumpPtr{opGasLimit, true}
- jumpTable[POP] = jumpPtr{opPop, true}
- jumpTable[MLOAD] = jumpPtr{opMload, true}
- jumpTable[MSTORE] = jumpPtr{opMstore, true}
- jumpTable[MSTORE8] = jumpPtr{opMstore8, true}
- jumpTable[SLOAD] = jumpPtr{opSload, true}
- jumpTable[SSTORE] = jumpPtr{opSstore, true}
- jumpTable[JUMPDEST] = jumpPtr{opJumpdest, true}
- jumpTable[PC] = jumpPtr{nil, true}
- jumpTable[MSIZE] = jumpPtr{opMsize, true}
- jumpTable[GAS] = jumpPtr{opGas, true}
- jumpTable[CREATE] = jumpPtr{opCreate, true}
- jumpTable[CALL] = jumpPtr{opCall, true}
- jumpTable[CALLCODE] = jumpPtr{opCallCode, true}
- jumpTable[LOG0] = jumpPtr{makeLog(0), true}
- jumpTable[LOG1] = jumpPtr{makeLog(1), true}
- jumpTable[LOG2] = jumpPtr{makeLog(2), true}
- jumpTable[LOG3] = jumpPtr{makeLog(3), true}
- jumpTable[LOG4] = jumpPtr{makeLog(4), true}
- jumpTable[SWAP1] = jumpPtr{makeSwap(1), true}
- jumpTable[SWAP2] = jumpPtr{makeSwap(2), true}
- jumpTable[SWAP3] = jumpPtr{makeSwap(3), true}
- jumpTable[SWAP4] = jumpPtr{makeSwap(4), true}
- jumpTable[SWAP5] = jumpPtr{makeSwap(5), true}
- jumpTable[SWAP6] = jumpPtr{makeSwap(6), true}
- jumpTable[SWAP7] = jumpPtr{makeSwap(7), true}
- jumpTable[SWAP8] = jumpPtr{makeSwap(8), true}
- jumpTable[SWAP9] = jumpPtr{makeSwap(9), true}
- jumpTable[SWAP10] = jumpPtr{makeSwap(10), true}
- jumpTable[SWAP11] = jumpPtr{makeSwap(11), true}
- jumpTable[SWAP12] = jumpPtr{makeSwap(12), true}
- jumpTable[SWAP13] = jumpPtr{makeSwap(13), true}
- jumpTable[SWAP14] = jumpPtr{makeSwap(14), true}
- jumpTable[SWAP15] = jumpPtr{makeSwap(15), true}
- jumpTable[SWAP16] = jumpPtr{makeSwap(16), true}
- jumpTable[PUSH1] = jumpPtr{makePush(1, big.NewInt(1)), true}
- jumpTable[PUSH2] = jumpPtr{makePush(2, big.NewInt(2)), true}
- jumpTable[PUSH3] = jumpPtr{makePush(3, big.NewInt(3)), true}
- jumpTable[PUSH4] = jumpPtr{makePush(4, big.NewInt(4)), true}
- jumpTable[PUSH5] = jumpPtr{makePush(5, big.NewInt(5)), true}
- jumpTable[PUSH6] = jumpPtr{makePush(6, big.NewInt(6)), true}
- jumpTable[PUSH7] = jumpPtr{makePush(7, big.NewInt(7)), true}
- jumpTable[PUSH8] = jumpPtr{makePush(8, big.NewInt(8)), true}
- jumpTable[PUSH9] = jumpPtr{makePush(9, big.NewInt(9)), true}
- jumpTable[PUSH10] = jumpPtr{makePush(10, big.NewInt(10)), true}
- jumpTable[PUSH11] = jumpPtr{makePush(11, big.NewInt(11)), true}
- jumpTable[PUSH12] = jumpPtr{makePush(12, big.NewInt(12)), true}
- jumpTable[PUSH13] = jumpPtr{makePush(13, big.NewInt(13)), true}
- jumpTable[PUSH14] = jumpPtr{makePush(14, big.NewInt(14)), true}
- jumpTable[PUSH15] = jumpPtr{makePush(15, big.NewInt(15)), true}
- jumpTable[PUSH16] = jumpPtr{makePush(16, big.NewInt(16)), true}
- jumpTable[PUSH17] = jumpPtr{makePush(17, big.NewInt(17)), true}
- jumpTable[PUSH18] = jumpPtr{makePush(18, big.NewInt(18)), true}
- jumpTable[PUSH19] = jumpPtr{makePush(19, big.NewInt(19)), true}
- jumpTable[PUSH20] = jumpPtr{makePush(20, big.NewInt(20)), true}
- jumpTable[PUSH21] = jumpPtr{makePush(21, big.NewInt(21)), true}
- jumpTable[PUSH22] = jumpPtr{makePush(22, big.NewInt(22)), true}
- jumpTable[PUSH23] = jumpPtr{makePush(23, big.NewInt(23)), true}
- jumpTable[PUSH24] = jumpPtr{makePush(24, big.NewInt(24)), true}
- jumpTable[PUSH25] = jumpPtr{makePush(25, big.NewInt(25)), true}
- jumpTable[PUSH26] = jumpPtr{makePush(26, big.NewInt(26)), true}
- jumpTable[PUSH27] = jumpPtr{makePush(27, big.NewInt(27)), true}
- jumpTable[PUSH28] = jumpPtr{makePush(28, big.NewInt(28)), true}
- jumpTable[PUSH29] = jumpPtr{makePush(29, big.NewInt(29)), true}
- jumpTable[PUSH30] = jumpPtr{makePush(30, big.NewInt(30)), true}
- jumpTable[PUSH31] = jumpPtr{makePush(31, big.NewInt(31)), true}
- jumpTable[PUSH32] = jumpPtr{makePush(32, big.NewInt(32)), true}
- jumpTable[DUP1] = jumpPtr{makeDup(1), true}
- jumpTable[DUP2] = jumpPtr{makeDup(2), true}
- jumpTable[DUP3] = jumpPtr{makeDup(3), true}
- jumpTable[DUP4] = jumpPtr{makeDup(4), true}
- jumpTable[DUP5] = jumpPtr{makeDup(5), true}
- jumpTable[DUP6] = jumpPtr{makeDup(6), true}
- jumpTable[DUP7] = jumpPtr{makeDup(7), true}
- jumpTable[DUP8] = jumpPtr{makeDup(8), true}
- jumpTable[DUP9] = jumpPtr{makeDup(9), true}
- jumpTable[DUP10] = jumpPtr{makeDup(10), true}
- jumpTable[DUP11] = jumpPtr{makeDup(11), true}
- jumpTable[DUP12] = jumpPtr{makeDup(12), true}
- jumpTable[DUP13] = jumpPtr{makeDup(13), true}
- jumpTable[DUP14] = jumpPtr{makeDup(14), true}
- jumpTable[DUP15] = jumpPtr{makeDup(15), true}
- jumpTable[DUP16] = jumpPtr{makeDup(16), true}
-
- jumpTable[RETURN] = jumpPtr{nil, true}
- jumpTable[SUICIDE] = jumpPtr{nil, true}
- jumpTable[JUMP] = jumpPtr{nil, true}
- jumpTable[JUMPI] = jumpPtr{nil, true}
- jumpTable[STOP] = jumpPtr{nil, true}
-
- return jumpTable
}
diff --git a/core/vm/log.go b/core/vm/log.go
deleted file mode 100644
index 014f222bf7..0000000000
--- a/core/vm/log.go
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2014 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 vm
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "io"
-
- "github.com/ubiq/go-ubiq/common"
- "github.com/ubiq/go-ubiq/common/hexutil"
- "github.com/ubiq/go-ubiq/rlp"
-)
-
-var errMissingLogFields = errors.New("missing required JSON log fields")
-
-// Log represents a contract log event. These events are generated by the LOG
-// opcode and stored/indexed by the node.
-type Log struct {
- // Consensus fields.
- Address common.Address // address of the contract that generated the event
- Topics []common.Hash // list of topics provided by the contract.
- Data []byte // supplied by the contract, usually ABI-encoded
-
- // Derived fields (don't reorder!).
- BlockNumber uint64 // block in which the transaction was included
- TxHash common.Hash // hash of the transaction
- TxIndex uint // index of the transaction in the block
- BlockHash common.Hash // hash of the block in which the transaction was included
- Index uint // index of the log in the receipt
-}
-
-type jsonLog struct {
- Address *common.Address `json:"address"`
- Topics *[]common.Hash `json:"topics"`
- Data *hexutil.Bytes `json:"data"`
- BlockNumber *hexutil.Uint64 `json:"blockNumber"`
- TxIndex *hexutil.Uint `json:"transactionIndex"`
- TxHash *common.Hash `json:"transactionHash"`
- BlockHash *common.Hash `json:"blockHash"`
- Index *hexutil.Uint `json:"logIndex"`
-}
-
-func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log {
- return &Log{Address: address, Topics: topics, Data: data, BlockNumber: number}
-}
-
-func (l *Log) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, []interface{}{l.Address, l.Topics, l.Data})
-}
-
-func (l *Log) DecodeRLP(s *rlp.Stream) error {
- var log struct {
- Address common.Address
- Topics []common.Hash
- Data []byte
- }
- if err := s.Decode(&log); err != nil {
- return err
- }
- l.Address, l.Topics, l.Data = log.Address, log.Topics, log.Data
- return nil
-}
-
-func (l *Log) String() string {
- return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index)
-}
-
-// MarshalJSON implements json.Marshaler.
-func (r *Log) MarshalJSON() ([]byte, error) {
- return json.Marshal(&jsonLog{
- Address: &r.Address,
- Topics: &r.Topics,
- Data: (*hexutil.Bytes)(&r.Data),
- BlockNumber: (*hexutil.Uint64)(&r.BlockNumber),
- TxIndex: (*hexutil.Uint)(&r.TxIndex),
- TxHash: &r.TxHash,
- BlockHash: &r.BlockHash,
- Index: (*hexutil.Uint)(&r.Index),
- })
-}
-
-// UnmarshalJSON implements json.Umarshaler.
-func (r *Log) UnmarshalJSON(input []byte) error {
- var dec jsonLog
- if err := json.Unmarshal(input, &dec); err != nil {
- return err
- }
- if dec.Address == nil || dec.Topics == nil || dec.Data == nil || dec.BlockNumber == nil ||
- dec.TxIndex == nil || dec.TxHash == nil || dec.BlockHash == nil || dec.Index == nil {
- return errMissingLogFields
- }
- *r = Log{
- Address: *dec.Address,
- Topics: *dec.Topics,
- Data: *dec.Data,
- BlockNumber: uint64(*dec.BlockNumber),
- TxHash: *dec.TxHash,
- TxIndex: uint(*dec.TxIndex),
- BlockHash: *dec.BlockHash,
- Index: uint(*dec.Index),
- }
- return nil
-}
-
-type Logs []*Log
-
-// LogForStorage is a wrapper around a Log that flattens and parses the entire
-// content of a log, as opposed to only the consensus fields originally (by hiding
-// the rlp interface methods).
-type LogForStorage Log
diff --git a/core/vm/log_test.go b/core/vm/log_test.go
deleted file mode 100644
index 4d31895580..0000000000
--- a/core/vm/log_test.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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 vm
-
-import (
- "encoding/json"
- "testing"
-)
-
-var unmarshalLogTests = map[string]struct {
- input string
- wantError error
-}{
- "ok": {
- input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x000000000000000000000000000000000000000000000001a055690d9db80000","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
- },
- "empty data": {
- input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
- },
- "missing data": {
- input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
- wantError: errMissingLogFields,
- },
-}
-
-func TestUnmarshalLog(t *testing.T) {
- for name, test := range unmarshalLogTests {
- var log *Log
- err := json.Unmarshal([]byte(test.input), &log)
- checkError(t, name, err, test.wantError)
- }
-}
-
-func checkError(t *testing.T, testname string, got, want error) bool {
- if got == nil {
- if want != nil {
- t.Errorf("test %q: got no error, want %q", testname, want)
- return false
- }
- return true
- }
- if want == nil {
- t.Errorf("test %q: unexpected error %q", testname, got)
- } else if got.Error() != want.Error() {
- t.Errorf("test %q: got error %q, want %q", testname, got, want)
- }
- return false
-}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 1df003bc7e..db207109fa 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -45,7 +45,7 @@ type LogConfig struct {
Limit int // maximum length of output, but zero means unlimited
}
-// StructLog is emitted to the Environment each cycle and lists information about the current internal state
+// StructLog is emitted to the EVM each cycle and lists information about the current internal state
// prior to the execution of the statement.
type StructLog struct {
Pc uint64
@@ -65,7 +65,7 @@ type StructLog struct {
// Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call.
type Tracer interface {
- CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
+ CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
}
// StructLogger is an EVM state logger and implements Tracer.
@@ -94,10 +94,10 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
// captureState logs a new structured log message and pushes it out to the environment
//
// captureState also tracks SSTORE ops to track dirty values.
-func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
+func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
// check if already accumulated the specified number of logs
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
- return TraceLimitReachedError
+ return ErrTraceLimitReached
}
// initialise new changed values storage container for this contract
@@ -144,7 +144,7 @@ func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas,
storage = make(Storage)
// Get the contract account and loop over each storage entry. This may involve looping over
// the trie and is a very expensive process.
- env.Db().GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool {
+ env.StateDB.GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool {
storage[key] = value
// Return true, indicating we'd like to continue.
return true
@@ -155,7 +155,7 @@ func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas,
}
}
// create a new snaptshot of the EVM.
- log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth(), err}
+ log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.depth, err}
l.logs = append(l.logs, log)
return nil
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index 2f61841720..a5d7f233d4 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -21,16 +21,17 @@ import (
"testing"
"github.com/ubiq/go-ubiq/common"
+ "github.com/ubiq/go-ubiq/params"
)
type dummyContractRef struct {
calledForEach bool
}
-func (dummyContractRef) ReturnGas(*big.Int, *big.Int) {}
-func (dummyContractRef) Address() common.Address { return common.Address{} }
-func (dummyContractRef) Value() *big.Int { return new(big.Int) }
-func (dummyContractRef) SetCode(common.Hash, []byte) {}
+func (dummyContractRef) ReturnGas(*big.Int) {}
+func (dummyContractRef) Address() common.Address { return common.Address{} }
+func (dummyContractRef) Value() *big.Int { return new(big.Int) }
+func (dummyContractRef) SetCode(common.Hash, []byte) {}
func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
d.calledForEach = true
}
@@ -40,28 +41,22 @@ func (d *dummyContractRef) SetBalance(*big.Int) {}
func (d *dummyContractRef) SetNonce(uint64) {}
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
-type dummyEnv struct {
- *Env
+type dummyStateDB struct {
+ NoopStateDB
ref *dummyContractRef
}
-func newDummyEnv(ref *dummyContractRef) *dummyEnv {
- return &dummyEnv{
- Env: NewEnv(&Config{EnableJit: false, ForceJit: false}),
- ref: ref,
- }
-}
-func (d dummyEnv) GetAccount(common.Address) Account {
+func (d dummyStateDB) GetAccount(common.Address) Account {
return d.ref
}
func TestStoreCapture(t *testing.T) {
var (
- env = NewEnv(&Config{EnableJit: false, ForceJit: false})
+ env = NewEVM(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
logger = NewStructLogger(nil)
mem = NewMemory()
stack = newstack()
- contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int), new(big.Int))
+ contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int))
)
stack.push(big.NewInt(1))
stack.push(big.NewInt(0))
@@ -83,8 +78,8 @@ func TestStorageCapture(t *testing.T) {
t.Skip("implementing this function is difficult. it requires all sort of interfaces to be implemented which isn't trivial. The value (the actual test) isn't worth it")
var (
ref = &dummyContractRef{}
- contract = NewContract(ref, ref, new(big.Int), new(big.Int), new(big.Int))
- env = newDummyEnv(ref)
+ contract = NewContract(ref, ref, new(big.Int), new(big.Int))
+ env = NewEVM(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
logger = NewStructLogger(nil)
mem = NewMemory()
stack = newstack()
diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go
new file mode 100644
index 0000000000..2116001d7a
--- /dev/null
+++ b/core/vm/memory_table.go
@@ -0,0 +1,68 @@
+package vm
+
+import (
+ "math/big"
+
+ "github.com/ubiq/go-ubiq/common"
+)
+
+func memorySha3(stack *Stack) *big.Int {
+ return calcMemSize(stack.Back(0), stack.Back(1))
+}
+
+func memoryCalldataCopy(stack *Stack) *big.Int {
+ return calcMemSize(stack.Back(0), stack.Back(2))
+}
+
+func memoryCodeCopy(stack *Stack) *big.Int {
+ return calcMemSize(stack.Back(0), stack.Back(2))
+}
+
+func memoryExtCodeCopy(stack *Stack) *big.Int {
+ return calcMemSize(stack.Back(1), stack.Back(3))
+}
+
+func memoryMLoad(stack *Stack) *big.Int {
+ return calcMemSize(stack.Back(0), big.NewInt(32))
+}
+
+func memoryMStore8(stack *Stack) *big.Int {
+ return calcMemSize(stack.Back(0), big.NewInt(1))
+}
+
+func memoryMStore(stack *Stack) *big.Int {
+ return calcMemSize(stack.Back(0), big.NewInt(32))
+}
+
+func memoryCreate(stack *Stack) *big.Int {
+ return calcMemSize(stack.Back(1), stack.Back(2))
+}
+
+func memoryCall(stack *Stack) *big.Int {
+ x := calcMemSize(stack.Back(5), stack.Back(6))
+ y := calcMemSize(stack.Back(3), stack.Back(4))
+
+ return common.BigMax(x, y)
+}
+
+func memoryCallCode(stack *Stack) *big.Int {
+ x := calcMemSize(stack.Back(5), stack.Back(6))
+ y := calcMemSize(stack.Back(3), stack.Back(4))
+
+ return common.BigMax(x, y)
+}
+func memoryDelegateCall(stack *Stack) *big.Int {
+ x := calcMemSize(stack.Back(4), stack.Back(5))
+ y := calcMemSize(stack.Back(2), stack.Back(3))
+
+ return common.BigMax(x, y)
+}
+
+func memoryReturn(stack *Stack) *big.Int {
+ return calcMemSize(stack.Back(0), stack.Back(1))
+}
+
+func memoryLog(stack *Stack) *big.Int {
+ mSize, mStart := stack.Back(1), stack.Back(0)
+ return calcMemSize(mStart, mSize)
+}
diff --git a/core/vm/noop.go b/core/vm/noop.go
new file mode 100644
index 0000000000..fb3f6dd73d
--- /dev/null
+++ b/core/vm/noop.go
@@ -0,0 +1,69 @@
+// 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 vm
+
+import (
+ "math/big"
+
+ "github.com/ubiq/go-ubiq/common"
+ "github.com/ubiq/go-ubiq/core/types"
+)
+
+func NoopCanTransfer(db StateDB, from common.Address, balance *big.Int) bool {
+ return true
+}
+func NoopTransfer(db StateDB, from, to common.Address, amount *big.Int) {}
+
+type NoopEVMCallContext struct{}
+
+func (NoopEVMCallContext) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
+ return nil, nil
+}
+func (NoopEVMCallContext) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
+ return nil, nil
+}
+func (NoopEVMCallContext) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
+ return nil, common.Address{}, nil
+}
+func (NoopEVMCallContext) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
+ return nil, nil
+}
+
+type NoopStateDB struct{}
+
+func (NoopStateDB) GetAccount(common.Address) Account { return nil }
+func (NoopStateDB) CreateAccount(common.Address) Account { return nil }
+func (NoopStateDB) SubBalance(common.Address, *big.Int) {}
+func (NoopStateDB) AddBalance(common.Address, *big.Int) {}
+func (NoopStateDB) GetBalance(common.Address) *big.Int { return nil }
+func (NoopStateDB) GetNonce(common.Address) uint64 { return 0 }
+func (NoopStateDB) SetNonce(common.Address, uint64) {}
+func (NoopStateDB) GetCodeHash(common.Address) common.Hash { return common.Hash{} }
+func (NoopStateDB) GetCode(common.Address) []byte { return nil }
+func (NoopStateDB) SetCode(common.Address, []byte) {}
+func (NoopStateDB) GetCodeSize(common.Address) int { return 0 }
+func (NoopStateDB) AddRefund(*big.Int) {}
+func (NoopStateDB) GetRefund() *big.Int { return nil }
+func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} }
+func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {}
+func (NoopStateDB) Suicide(common.Address) bool { return false }
+func (NoopStateDB) HasSuicided(common.Address) bool { return false }
+func (NoopStateDB) Exist(common.Address) bool { return false }
+func (NoopStateDB) Empty(common.Address) bool { return false }
+func (NoopStateDB) RevertToSnapshot(int) {}
+func (NoopStateDB) Snapshot() int { return 0 }
+func (NoopStateDB) AddLog(*types.Log) {}
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index 5db661943a..c803cdab98 100644
--- a/core/vm/runtime/env.go
+++ b/core/vm/runtime/env.go
@@ -23,92 +23,22 @@ import (
"github.com/ubiq/go-ubiq/core"
"github.com/ubiq/go-ubiq/core/state"
"github.com/ubiq/go-ubiq/core/vm"
- "github.com/ubiq/go-ubiq/params"
)
-// Env is a basic runtime environment required for running the EVM.
-type Env struct {
- chainConfig *params.ChainConfig
- depth int
- state *state.StateDB
+func NewEnv(cfg *Config, state *state.StateDB) *vm.EVM {
+ context := vm.Context{
+ CanTransfer: core.CanTransfer,
+ Transfer: core.Transfer,
+ GetHash: func(uint64) common.Hash { return common.Hash{} },
- origin common.Address
- coinbase common.Address
-
- number *big.Int
- time *big.Int
- difficulty *big.Int
- gasLimit *big.Int
-
- getHashFn func(uint64) common.Hash
-
- evm *vm.EVM
-}
-
-// NewEnv returns a new vm.Environment
-func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
- env := &Env{
- chainConfig: cfg.ChainConfig,
- state: state,
- origin: cfg.Origin,
- coinbase: cfg.Coinbase,
- number: cfg.BlockNumber,
- time: cfg.Time,
- difficulty: cfg.Difficulty,
- gasLimit: cfg.GasLimit,
+ Origin: cfg.Origin,
+ Coinbase: cfg.Coinbase,
+ BlockNumber: cfg.BlockNumber,
+ Time: cfg.Time,
+ Difficulty: cfg.Difficulty,
+ GasLimit: cfg.GasLimit,
+ GasPrice: new(big.Int),
}
- env.evm = vm.New(env, vm.Config{
- Debug: cfg.Debug,
- EnableJit: !cfg.DisableJit,
- ForceJit: !cfg.DisableJit,
- })
- return env
-}
-
-func (self *Env) ChainConfig() *params.ChainConfig { return self.chainConfig }
-func (self *Env) Vm() vm.Vm { return self.evm }
-func (self *Env) Origin() common.Address { return self.origin }
-func (self *Env) BlockNumber() *big.Int { return self.number }
-func (self *Env) Coinbase() common.Address { return self.coinbase }
-func (self *Env) Time() *big.Int { return self.time }
-func (self *Env) Difficulty() *big.Int { return self.difficulty }
-func (self *Env) Db() vm.Database { return self.state }
-func (self *Env) GasLimit() *big.Int { return self.gasLimit }
-func (self *Env) VmType() vm.Type { return vm.StdVmTy }
-func (self *Env) GetHash(n uint64) common.Hash {
- return self.getHashFn(n)
-}
-func (self *Env) AddLog(log *vm.Log) {
- self.state.AddLog(log)
-}
-func (self *Env) Depth() int { return self.depth }
-func (self *Env) SetDepth(i int) { self.depth = i }
-func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
- return self.state.GetBalance(from).Cmp(balance) >= 0
-}
-func (self *Env) SnapshotDatabase() int {
- return self.state.Snapshot()
-}
-func (self *Env) RevertToSnapshot(snapshot int) {
- self.state.RevertToSnapshot(snapshot)
-}
-
-func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
- core.Transfer(from, to, amount)
-}
-
-func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return core.Call(self, caller, addr, data, gas, price, value)
-}
-func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return core.CallCode(self, caller, addr, data, gas, price, value)
-}
-
-func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- return core.DelegateCall(self, me, addr, data, gas, price)
-}
-
-func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- return core.Create(self, caller, data, gas, price, value)
+ return vm.NewEVM(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
}
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index d6452aa8be..c1c5261439 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -22,19 +22,12 @@ import (
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core/state"
+ "github.com/ubiq/go-ubiq/core/vm"
"github.com/ubiq/go-ubiq/crypto"
"github.com/ubiq/go-ubiq/ethdb"
"github.com/ubiq/go-ubiq/params"
)
-// The default, always homestead, rule set for the vm env
-type ruleSet struct{}
-
-func (ruleSet) IsHomestead(*big.Int) bool { return true }
-func (ruleSet) GasTable(*big.Int) params.GasTable {
- return params.GasTableHomesteadGasRepriceFork
-}
-
// Config is a basic type specifying certain configuration flags for running
// the EVM.
type Config struct {
@@ -49,6 +42,7 @@ type Config struct {
Value *big.Int
DisableJit bool // "disable" so it's enabled by default
Debug bool
+ EVMConfig vm.Config
State *state.StateDB
GetHashFn func(n uint64) common.Hash
@@ -121,13 +115,37 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
receiver.Address(),
input,
cfg.GasLimit,
- cfg.GasPrice,
cfg.Value,
)
return ret, cfg.State, err
}
+// Create executes the code using the EVM create method
+func Create(input []byte, cfg *Config) ([]byte, common.Address, error) {
+ if cfg == nil {
+ cfg = new(Config)
+ }
+ setDefaults(cfg)
+
+ if cfg.State == nil {
+ db, _ := ethdb.NewMemDatabase()
+ cfg.State, _ = state.New(common.Hash{}, db)
+ }
+ var (
+ vmenv = NewEnv(cfg, cfg.State)
+ sender = cfg.State.CreateAccount(cfg.Origin)
+ )
+
+ // Call the code with the given configuration.
+ return vmenv.Create(
+ sender,
+ input,
+ cfg.GasLimit,
+ cfg.Value,
+ )
+}
+
// Call executes the code given by the contract's address. It will return the
// EVM's return value or an error if it failed.
//
@@ -145,7 +163,6 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
address,
input,
cfg.GasLimit,
- cfg.GasPrice,
cfg.Value,
)
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index 0a65ef8ef3..1b281482c8 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -56,7 +56,7 @@ func TestDefaults(t *testing.T) {
}
}
-func TestEnvironment(t *testing.T) {
+func TestEVM(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Fatalf("crashed with: %v", r)
diff --git a/core/vm/segments.go b/core/vm/segments.go
deleted file mode 100644
index 648d8a04a1..0000000000
--- a/core/vm/segments.go
+++ /dev/null
@@ -1,60 +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 vm
-
-import "math/big"
-
-type jumpSeg struct {
- pos uint64
- err error
- gas *big.Int
-}
-
-func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- if !contract.UseGas(j.gas) {
- return nil, OutOfGasError
- }
- if j.err != nil {
- return nil, j.err
- }
- *pc = j.pos
- return nil, nil
-}
-func (s jumpSeg) halts() bool { return false }
-func (s jumpSeg) Op() OpCode { return 0 }
-
-type pushSeg struct {
- data []*big.Int
- gas *big.Int
-}
-
-func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- // Use the calculated gas. When insufficient gas is present, use all gas and return an
- // Out Of Gas error
- if !contract.UseGas(s.gas) {
- return nil, OutOfGasError
- }
-
- for _, d := range s.data {
- stack.push(new(big.Int).Set(d))
- }
- *pc += uint64(len(s.data))
- return nil, nil
-}
-
-func (s pushSeg) halts() bool { return false }
-func (s pushSeg) Op() OpCode { return 0 }
diff --git a/core/vm/stack.go b/core/vm/stack.go
index f6c1f76e45..2d1b7bb82d 100644
--- a/core/vm/stack.go
+++ b/core/vm/stack.go
@@ -68,6 +68,11 @@ func (st *Stack) peek() *big.Int {
return st.data[st.len()-1]
}
+// Back returns the n'th item in stack
+func (st *Stack) Back(n int) *big.Int {
+ return st.data[st.len()-n-1]
+}
+
func (st *Stack) require(n int) error {
if st.len() < n {
return fmt.Errorf("stack underflow (%d <=> %d)", len(st.data), n)
diff --git a/core/vm/stack_table.go b/core/vm/stack_table.go
new file mode 100644
index 0000000000..5ce3f2c489
--- /dev/null
+++ b/core/vm/stack_table.go
@@ -0,0 +1,20 @@
+package vm
+
+import (
+ "fmt"
+
+ "github.com/ubiq/go-ubiq/params"
+)
+
+func makeStackFunc(pop, push int) stackValidationFunc {
+ return func(stack *Stack) error {
+ if err := stack.require(pop); err != nil {
+ return err
+ }
+
+ if push > 0 && int64(stack.len()-pop+push) > params.StackLimit.Int64() {
+ return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64())
+ }
+ return nil
+ }
+}
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 64b5418197..0384897aa4 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -19,6 +19,7 @@ package vm
import (
"fmt"
"math/big"
+ "sync/atomic"
"time"
"github.com/ubiq/go-ubiq/common"
@@ -28,43 +29,61 @@ import (
"github.com/ubiq/go-ubiq/params"
)
-// Config are the configuration options for the EVM
+// Config are the configuration options for the Interpreter
type Config struct {
- Debug bool
+ // Debug enabled debugging Interpreter options
+ Debug bool
+ // EnableJit enabled the JIT VM
EnableJit bool
- ForceJit bool
- Tracer Tracer
+ // ForceJit forces the JIT VM
+ ForceJit bool
+ // Tracer is the op code logger
+ Tracer Tracer
+ // NoRecursion disabled Interpreter call, callcode,
+ // delegate call and create.
+ NoRecursion bool
+ // Disable gas metering
+ DisableGasMetering bool
+ // JumpTable contains the EVM instruction table. This
+ // may me left uninitialised and will be set the default
+ // table.
+ JumpTable [256]operation
}
-// EVM is used to run Ethereum based contracts and will utilise the
+// Interpreter is used to run Ethereum based contracts and will utilise the
// passed environment to query external sources for state information.
-// The EVM will run the byte code VM or JIT VM based on the passed
+// The Interpreter will run the byte code VM or JIT VM based on the passed
// configuration.
-type EVM struct {
- env Environment
- jumpTable vmJumpTable
- cfg Config
- gasTable params.GasTable
+type Interpreter struct {
+ env *EVM
+ cfg Config
+ gasTable params.GasTable
}
-// New returns a new instance of the EVM.
-func New(env Environment, cfg Config) *EVM {
- return &EVM{
- env: env,
- jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber()),
- cfg: cfg,
- gasTable: env.ChainConfig().GasTable(env.BlockNumber()),
+// NewInterpreter returns a new instance of the Interpreter.
+func NewInterpreter(env *EVM, cfg Config) *Interpreter {
+ // We use the STOP instruction whether to see
+ // the jump table was initialised. If it was not
+ // we'll set the default jump table.
+ if !cfg.JumpTable[STOP].valid {
+ cfg.JumpTable = defaultJumpTable
+ }
+
+ return &Interpreter{
+ env: env,
+ cfg: cfg,
+ gasTable: env.ChainConfig().GasTable(env.BlockNumber),
}
}
// Run loops and evaluates the contract's code with the given input data
-func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
- evm.env.SetDepth(evm.env.Depth() + 1)
- defer evm.env.SetDepth(evm.env.Depth() - 1)
+func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) {
+ evm.env.depth++
+ defer func() { evm.env.depth-- }()
if contract.CodeAddr != nil {
- if p := Precompiled[contract.CodeAddr.Str()]; p != nil {
- return evm.RunPrecompiled(p, input, contract)
+ if p := PrecompiledContracts[*contract.CodeAddr]; p != nil {
+ return RunPrecompiledContract(p, input, contract)
}
}
@@ -77,385 +96,91 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
if codehash == (common.Hash{}) {
codehash = crypto.Keccak256Hash(contract.Code)
}
- var program *Program
- if false {
- // JIT disabled due to JIT not being Homestead gas reprice ready.
-
- // If the JIT is enabled check the status of the JIT program,
- // if it doesn't exist compile a new program in a separate
- // goroutine or wait for compilation to finish if the JIT is
- // forced.
- switch GetProgramStatus(codehash) {
- case progReady:
- return RunProgram(GetProgram(codehash), evm.env, contract, input)
- case progUnknown:
- if evm.cfg.ForceJit {
- // Create and compile program
- program = NewProgram(contract.Code)
- perr := CompileProgram(program)
- if perr == nil {
- return RunProgram(program, evm.env, contract, input)
- }
- glog.V(logger.Info).Infoln("error compiling program", err)
- } else {
- // create and compile the program. Compilation
- // is done in a separate goroutine
- program = NewProgram(contract.Code)
- go func() {
- err := CompileProgram(program)
- if err != nil {
- glog.V(logger.Info).Infoln("error compiling program", err)
- return
- }
- }()
- }
- }
- }
var (
- caller = contract.caller
- code = contract.Code
- instrCount = 0
-
- op OpCode // current opcode
- mem = NewMemory() // bound memory
- stack = newstack() // local stack
- statedb = evm.env.Db() // current state
+ op OpCode // current opcode
+ mem = NewMemory() // bound memory
+ stack = newstack() // local stack
// For optimisation reason we're using uint64 as the program counter.
// It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Practically much less so feasible.
- pc = uint64(0) // program counter
-
- // jump evaluates and checks whether the given jump destination is a valid one
- // if valid move the `pc` otherwise return an error.
- jump = func(from uint64, to *big.Int) error {
- if !contract.jumpdests.has(codehash, code, to) {
- nop := contract.GetOp(to.Uint64())
- return fmt.Errorf("invalid jump destination (%v) %v", nop, to)
- }
-
- pc = to.Uint64()
-
- return nil
- }
-
- newMemSize *big.Int
- cost *big.Int
+ pc = uint64(0) // program counter
+ cost *big.Int
)
contract.Input = input
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
defer func() {
if err != nil && evm.cfg.Debug {
- evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), err)
+ evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err)
}
}()
if glog.V(logger.Debug) {
- glog.Infof("running byte VM %x\n", codehash[:4])
+ glog.Infof("evm running: %x\n", codehash[:4])
tstart := time.Now()
defer func() {
- glog.Infof("byte VM %x done. time: %v instrc: %v\n", codehash[:4], time.Since(tstart), instrCount)
+ glog.Infof("evm done: %x. time: %v\n", codehash[:4], time.Since(tstart))
}()
}
- for ; ; instrCount++ {
- /*
- if EnableJit && it%100 == 0 {
- if program != nil && progStatus(atomic.LoadInt32(&program.status)) == progReady {
- // move execution
- fmt.Println("moved", it)
- glog.V(logger.Info).Infoln("Moved execution to JIT")
- return runProgram(program, pc, mem, stack, evm.env, contract, input)
- }
- }
- */
-
+ // The Interpreter main run loop (contextual). This loop runs until either an
+ // explicit STOP, RETURN or SUICIDE is executed, an error accured during
+ // the execution of one of the operations or until the evm.done is set by
+ // the parent context.Context.
+ for atomic.LoadInt32(&evm.env.abort) == 0 {
// Get the memory location of pc
op = contract.GetOp(pc)
- //fmt.Printf("OP %d %v\n", op, op)
- // calculate the new memory size and gas price for the current executing opcode
- newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack)
- if err != nil {
+
+ // get the operation from the jump table matching the opcode
+ operation := evm.cfg.JumpTable[op]
+
+ // if the op is invalid abort the process and return an error
+ if !operation.valid {
+ return nil, fmt.Errorf("invalid opcode %x", op)
+ }
+
+ // validate the stack and make sure there enough stack items available
+ // to perform the operation
+ if err := operation.validateStack(stack); err != nil {
return nil, err
}
- // Use the calculated gas. When insufficient gas is present, use all gas and return an
- // Out Of Gas error
- if !contract.UseGas(cost) {
- return nil, OutOfGasError
+ var memorySize *big.Int
+ // calculate the new memory size and expand the memory to fit
+ // the operation
+ if operation.memorySize != nil {
+ memorySize = operation.memorySize(stack)
+ // memory is expanded in words of 32 bytes. Gas
+ // is also calculated in words.
+ memorySize.Mul(toWordSize(memorySize), big.NewInt(32))
+ }
+
+ if !evm.cfg.DisableGasMetering {
+ // consume the gas and return an error if not enough gas is available.
+ // cost is explicitly set so that the capture state defer method cas get the proper cost
+ cost = operation.gasCost(evm.gasTable, evm.env, contract, stack, mem, memorySize)
+ if !contract.UseGas(cost) {
+ return nil, ErrOutOfGas
+ }
+ }
+ if memorySize != nil {
+ mem.Resize(memorySize.Uint64())
}
- // Resize the memory calculated previously
- mem.Resize(newMemSize.Uint64())
- // Add a log message
if evm.cfg.Debug {
- err = evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil)
- if err != nil {
- return nil, err
- }
+ evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err)
}
- if opPtr := evm.jumpTable[op]; opPtr.valid {
- if opPtr.fn != nil {
- opPtr.fn(instruction{}, &pc, evm.env, contract, mem, stack)
- } else {
- switch op {
- case PC:
- opPc(instruction{data: new(big.Int).SetUint64(pc)}, &pc, evm.env, contract, mem, stack)
- case JUMP:
- if err := jump(pc, stack.pop()); err != nil {
- return nil, err
- }
-
- continue
- case JUMPI:
- pos, cond := stack.pop(), stack.pop()
-
- if cond.Cmp(common.BigTrue) >= 0 {
- if err := jump(pc, pos); err != nil {
- return nil, err
- }
-
- continue
- }
- case RETURN:
- offset, size := stack.pop(), stack.pop()
- ret := mem.GetPtr(offset.Int64(), size.Int64())
-
- return ret, nil
- case SUICIDE:
- opSuicide(instruction{}, nil, evm.env, contract, mem, stack)
-
- fallthrough
- case STOP: // Stop the contract
- return nil, nil
- }
- }
- } else {
- return nil, fmt.Errorf("Invalid opcode %x", op)
+ // execute the operation
+ res, err := operation.execute(&pc, evm.env, contract, mem, stack)
+ switch {
+ case err != nil:
+ return nil, err
+ case operation.halts:
+ return res, nil
+ case !operation.jumps:
+ pc++
}
-
- pc++
-
- }
-}
-
-// calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
-// the operation. This does not reduce gas or resizes the memory.
-func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
- var (
- gas = new(big.Int)
- newMemSize *big.Int = new(big.Int)
- )
- err := baseCheck(op, stack, gas)
- if err != nil {
- return nil, nil, err
- }
-
- // stack Check, memory resize & gas phase
- switch op {
- case SUICIDE:
- // EIP150 homestead gas reprice fork:
- if gasTable.CreateBySuicide != nil {
- gas.Set(gasTable.Suicide)
- var (
- address = common.BigToAddress(stack.data[len(stack.data)-1])
- eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
- )
-
- if eip158 {
- // if empty and transfers value
- if env.Db().Empty(address) && statedb.GetBalance(contract.Address()).BitLen() > 0 {
- gas.Add(gas, gasTable.CreateBySuicide)
- }
- } else if !env.Db().Exist(address) {
- gas.Add(gas, gasTable.CreateBySuicide)
- }
- }
-
- if !statedb.HasSuicided(contract.Address()) {
- statedb.AddRefund(params.SuicideRefundGas)
- }
- case EXTCODESIZE:
- gas.Set(gasTable.ExtcodeSize)
- case BALANCE:
- gas.Set(gasTable.Balance)
- case SLOAD:
- gas.Set(gasTable.SLoad)
- case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
- n := int(op - SWAP1 + 2)
- err := stack.require(n)
- if err != nil {
- return nil, nil, err
- }
- gas.Set(GasFastestStep)
- case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
- n := int(op - DUP1 + 1)
- err := stack.require(n)
- if err != nil {
- return nil, nil, err
- }
- gas.Set(GasFastestStep)
- case LOG0, LOG1, LOG2, LOG3, LOG4:
- n := int(op - LOG0)
- err := stack.require(n + 2)
- if err != nil {
- return nil, nil, err
- }
-
- mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1]
-
- gas.Add(gas, params.LogGas)
- gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), params.LogTopicGas))
- gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas))
-
- newMemSize = calcMemSize(mStart, mSize)
-
- quadMemGas(mem, newMemSize, gas)
- case EXP:
- expByteLen := int64((stack.data[stack.len()-2].BitLen() + 7) / 8)
- gas.Add(gas, new(big.Int).Mul(big.NewInt(expByteLen), gasTable.ExpByte))
- case SSTORE:
- err := stack.require(2)
- if err != nil {
- return nil, nil, err
- }
-
- var g *big.Int
- y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
- val := statedb.GetState(contract.Address(), common.BigToHash(x))
-
- // This checks for 3 scenario's and calculates gas accordingly
- // 1. From a zero-value address to a non-zero value (NEW VALUE)
- // 2. From a non-zero value address to a zero-value address (DELETE)
- // 3. From a non-zero to a non-zero (CHANGE)
- if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
- // 0 => non 0
- g = params.SstoreSetGas
- } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
- statedb.AddRefund(params.SstoreRefundGas)
-
- g = params.SstoreClearGas
- } else {
- // non 0 => non 0 (or 0 => 0)
- g = params.SstoreResetGas
- }
- gas.Set(g)
- case MLOAD:
- newMemSize = calcMemSize(stack.peek(), u256(32))
- quadMemGas(mem, newMemSize, gas)
- case MSTORE8:
- newMemSize = calcMemSize(stack.peek(), u256(1))
- quadMemGas(mem, newMemSize, gas)
- case MSTORE:
- newMemSize = calcMemSize(stack.peek(), u256(32))
- quadMemGas(mem, newMemSize, gas)
- case RETURN:
- newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
- quadMemGas(mem, newMemSize, gas)
- case SHA3:
- newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
-
- words := toWordSize(stack.data[stack.len()-2])
- gas.Add(gas, words.Mul(words, params.Sha3WordGas))
-
- quadMemGas(mem, newMemSize, gas)
- case CALLDATACOPY:
- newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
-
- words := toWordSize(stack.data[stack.len()-3])
- gas.Add(gas, words.Mul(words, params.CopyGas))
-
- quadMemGas(mem, newMemSize, gas)
- case CODECOPY:
- newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
-
- words := toWordSize(stack.data[stack.len()-3])
- gas.Add(gas, words.Mul(words, params.CopyGas))
-
- quadMemGas(mem, newMemSize, gas)
- case EXTCODECOPY:
- gas.Set(gasTable.ExtcodeCopy)
-
- newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
-
- words := toWordSize(stack.data[stack.len()-4])
- gas.Add(gas, words.Mul(words, params.CopyGas))
-
- quadMemGas(mem, newMemSize, gas)
- case CREATE:
- newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
-
- quadMemGas(mem, newMemSize, gas)
- case CALL, CALLCODE:
- gas.Set(gasTable.Calls)
-
- transfersValue := stack.data[len(stack.data)-3].BitLen() > 0
- if op == CALL {
- var (
- address = common.BigToAddress(stack.data[len(stack.data)-2])
- eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
- )
- if eip158 {
- if env.Db().Empty(address) && transfersValue {
- gas.Add(gas, params.CallNewAccountGas)
- }
- } else if !env.Db().Exist(address) {
- gas.Add(gas, params.CallNewAccountGas)
- }
- }
- if transfersValue {
- gas.Add(gas, params.CallValueTransferGas)
- }
- x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
- y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
-
- newMemSize = common.BigMax(x, y)
-
- quadMemGas(mem, newMemSize, gas)
-
- cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1])
- // Replace the stack item with the new gas calculation. This means that
- // either the original item is left on the stack or the item is replaced by:
- // (availableGas - gas) * 63 / 64
- // We replace the stack item so that it's available when the opCall instruction is
- // called. This information is otherwise lost due to the dependency on *current*
- // available gas.
- stack.data[stack.len()-1] = cg
- gas.Add(gas, cg)
-
- case DELEGATECALL:
- gas.Set(gasTable.Calls)
-
- x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6])
- y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4])
-
- newMemSize = common.BigMax(x, y)
-
- quadMemGas(mem, newMemSize, gas)
-
- cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1])
- // Replace the stack item with the new gas calculation. This means that
- // either the original item is left on the stack or the item is replaced by:
- // (availableGas - gas) * 63 / 64
- // We replace the stack item so that it's available when the opCall instruction is
- // called.
- stack.data[stack.len()-1] = cg
- gas.Add(gas, cg)
-
- }
-
- return newMemSize, gas, nil
-}
-
-// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
-func (evm *EVM) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) {
- gas := p.Gas(len(input))
- if contract.UseGas(gas) {
- ret = p.Call(input)
-
- return ret, nil
- } else {
- return nil, OutOfGasError
}
+ return nil, nil
}
diff --git a/core/vm/vm_jit.go b/core/vm/vm_jit.go
index 42590652a9..ae94ec8d04 100644
--- a/core/vm/vm_jit.go
+++ b/core/vm/vm_jit.go
@@ -44,7 +44,7 @@ import (
)
type JitVm struct {
- env Environment
+ env EVM
me ContextRef
callerAddr []byte
price *big.Int
@@ -161,7 +161,7 @@ func assert(condition bool, message string) {
}
}
-func NewJitVm(env Environment) *JitVm {
+func NewJitVm(env EVM) *JitVm {
return &JitVm{env: env}
}
@@ -235,7 +235,7 @@ func (self *JitVm) Endl() VirtualMachine {
return self
}
-func (self *JitVm) Env() Environment {
+func (self *JitVm) Env() EVM {
return self.env
}
diff --git a/core/vm/vm_jit_fake.go b/core/vm/vm_jit_fake.go
index 4fa98ccd9c..44b60abf6f 100644
--- a/core/vm/vm_jit_fake.go
+++ b/core/vm/vm_jit_fake.go
@@ -17,10 +17,3 @@
// +build !evmjit
package vm
-
-import "fmt"
-
-func NewJitVm(env Environment) VirtualMachine {
- fmt.Printf("Warning! EVM JIT not enabled.\n")
- return New(env, Config{})
-}
diff --git a/core/vm_env.go b/core/vm_env.go
deleted file mode 100644
index a4fb561d41..0000000000
--- a/core/vm_env.go
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2014 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 core
-
-import (
- "math/big"
-
- "github.com/ubiq/go-ubiq/common"
- "github.com/ubiq/go-ubiq/core/state"
- "github.com/ubiq/go-ubiq/core/types"
- "github.com/ubiq/go-ubiq/core/vm"
- "github.com/ubiq/go-ubiq/params"
-)
-
-// GetHashFn returns a function for which the VM env can query block hashes through
-// up to the limit defined by the Yellow Paper and uses the given block chain
-// to query for information.
-func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
- return func(n uint64) common.Hash {
- for block := chain.GetBlockByHash(ref); block != nil; block = chain.GetBlock(block.ParentHash(), block.NumberU64()-1) {
- if block.NumberU64() == n {
- return block.Hash()
- }
- }
-
- return common.Hash{}
- }
-}
-
-type VMEnv struct {
- chainConfig *params.ChainConfig // Chain configuration
- state *state.StateDB // State to use for executing
- evm *vm.EVM // The Ethereum Virtual Machine
- depth int // Current execution depth
- msg Message // Message appliod
-
- header *types.Header // Header information
- chain *BlockChain // Blockchain handle
- getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes
-}
-
-func NewEnv(state *state.StateDB, chainConfig *params.ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
- env := &VMEnv{
- chainConfig: chainConfig,
- chain: chain,
- state: state,
- header: header,
- msg: msg,
- getHashFn: GetHashFn(header.ParentHash, chain),
- }
-
- env.evm = vm.New(env, cfg)
- return env
-}
-
-func (self *VMEnv) ChainConfig() *params.ChainConfig { return self.chainConfig }
-func (self *VMEnv) Vm() vm.Vm { return self.evm }
-func (self *VMEnv) Origin() common.Address { return self.msg.From() }
-func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }
-func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }
-func (self *VMEnv) Time() *big.Int { return self.header.Time }
-func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
-func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
-func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
-func (self *VMEnv) Db() vm.Database { return self.state }
-func (self *VMEnv) Depth() int { return self.depth }
-func (self *VMEnv) SetDepth(i int) { self.depth = i }
-func (self *VMEnv) GetHash(n uint64) common.Hash {
- return self.getHashFn(n)
-}
-
-func (self *VMEnv) AddLog(log *vm.Log) {
- self.state.AddLog(log)
-}
-func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
- return self.state.GetBalance(from).Cmp(balance) >= 0
-}
-
-func (self *VMEnv) SnapshotDatabase() int {
- return self.state.Snapshot()
-}
-
-func (self *VMEnv) RevertToSnapshot(snapshot int) {
- self.state.RevertToSnapshot(snapshot)
-}
-
-func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
- Transfer(from, to, amount)
-}
-
-func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return Call(self, me, addr, data, gas, price, value)
-}
-func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return CallCode(self, me, addr, data, gas, price, value)
-}
-
-func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- return DelegateCall(self, me, addr, data, gas, price)
-}
-
-func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- return Create(self, me, data, gas, price, value)
-}
diff --git a/crypto/crypto.go b/crypto/crypto.go
index 7f8c935e2d..e01ed06a4d 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -167,25 +167,19 @@ func GenerateKey() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(secp256k1.S256(), rand.Reader)
}
+// ValidateSignatureValues verifies whether the signature values are valid with
+// the given chain rules. The v value is assumed to be either 0 or 1.
func ValidateSignatureValues(v byte, r, s *big.Int, homestead bool) bool {
if r.Cmp(common.Big1) < 0 || s.Cmp(common.Big1) < 0 {
return false
}
- vint := uint32(v)
// reject upper range of s values (ECDSA malleability)
// see discussion in secp256k1/libsecp256k1/include/secp256k1.h
if homestead && s.Cmp(secp256k1.HalfN) > 0 {
return false
}
// Frontier: allow s to be in full N range
- if s.Cmp(secp256k1.N) >= 0 {
- return false
- }
- if r.Cmp(secp256k1.N) < 0 && (vint == 27 || vint == 28) {
- return true
- } else {
- return false
- }
+ return r.Cmp(secp256k1.N) < 0 && s.Cmp(secp256k1.N) < 0 && (v == 0 || v == 1)
}
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
@@ -199,14 +193,13 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
}
// Sign calculates an ECDSA signature.
-// This function is susceptible to choosen plaintext attacks that can leak
+//
+// This function is susceptible to chosen plaintext attacks that can leak
// information about the private key that is used for signing. Callers must
-// be aware that the given hash cannot be choosen by an adversery. Common
+// be aware that the given hash cannot be chosen by an adversery. Common
// solution is to hash any input before calculating the signature.
//
-// Note: the calculated signature is not Ethereum compliant. The yellow paper
-// dictates Ethereum singature to have a V value with and offset of 27 v in [27,28].
-// Use SignEthereum to get an Ethereum compliant signature.
+// The produced signature is in the [R || S || V] format where V is 0 or 1.
func Sign(data []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
if len(data) != 32 {
return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(data))
@@ -218,20 +211,6 @@ func Sign(data []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
return
}
-// SignEthereum calculates an Ethereum ECDSA signature.
-// This function is susceptible to choosen plaintext attacks that can leak
-// information about the private key that is used for signing. Callers must
-// be aware that the given hash cannot be freely choosen by an adversery.
-// Common solution is to hash the message before calculating the signature.
-func SignEthereum(data []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
- sig, err := Sign(data, prv)
- if err != nil {
- return nil, err
- }
- sig[64] += 27 // as described in the yellow paper
- return sig, err
-}
-
func Encrypt(pub *ecdsa.PublicKey, message []byte) ([]byte, error) {
return ecies.Encrypt(rand.Reader, ecies.ImportECDSAPublic(pub), message, nil, nil)
}
diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go
index b44c63214a..f582971519 100644
--- a/crypto/crypto_test.go
+++ b/crypto/crypto_test.go
@@ -72,30 +72,15 @@ func BenchmarkSha3(b *testing.B) {
fmt.Println(amount, ":", time.Since(start))
}
-func Test0Key(t *testing.T) {
- key := common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
- _, err := secp256k1.GeneratePubKey(key)
- if err == nil {
- t.Errorf("expected error due to zero privkey")
- }
-}
-
-func testSign(signfn func([]byte, *ecdsa.PrivateKey) ([]byte, error), t *testing.T) {
+func TestSign(t *testing.T) {
key, _ := HexToECDSA(testPrivHex)
addr := common.HexToAddress(testAddrHex)
msg := Keccak256([]byte("foo"))
- sig, err := signfn(msg, key)
+ sig, err := Sign(msg, key)
if err != nil {
t.Errorf("Sign error: %s", err)
}
-
- // signfn can return a recover id of either [0,1] or [27,28].
- // In the latter case its an Ethereum signature, adjust recover id.
- if sig[64] == 27 || sig[64] == 28 {
- sig[64] -= 27
- }
-
recoveredPub, err := Ecrecover(msg, sig)
if err != nil {
t.Errorf("ECRecover error: %s", err)
@@ -117,34 +102,15 @@ func testSign(signfn func([]byte, *ecdsa.PrivateKey) ([]byte, error), t *testing
}
}
-func TestSign(t *testing.T) {
- testSign(Sign, t)
-}
-
-func TestSignEthereum(t *testing.T) {
- testSign(SignEthereum, t)
-}
-
-func testInvalidSign(signfn func([]byte, *ecdsa.PrivateKey) ([]byte, error), t *testing.T) {
- _, err := signfn(make([]byte, 1), nil)
- if err == nil {
+func TestInvalidSign(t *testing.T) {
+ if _, err := Sign(make([]byte, 1), nil); err == nil {
t.Errorf("expected sign with hash 1 byte to error")
}
-
- _, err = signfn(make([]byte, 33), nil)
- if err == nil {
+ if _, err := Sign(make([]byte, 33), nil); err == nil {
t.Errorf("expected sign with hash 33 byte to error")
}
}
-func TestInvalidSign(t *testing.T) {
- testInvalidSign(Sign, t)
-}
-
-func TestInvalidSignEthereum(t *testing.T) {
- testInvalidSign(SignEthereum, t)
-}
-
func TestNewContractAddress(t *testing.T) {
key, _ := HexToECDSA(testPrivHex)
addr := common.HexToAddress(testAddrHex)
@@ -207,43 +173,43 @@ func TestValidateSignatureValues(t *testing.T) {
secp256k1nMinus1 := new(big.Int).Sub(secp256k1.N, common.Big1)
// correct v,r,s
- check(true, 27, one, one)
- check(true, 28, one, one)
+ check(true, 0, one, one)
+ check(true, 1, one, one)
// incorrect v, correct r,s,
- check(false, 30, one, one)
- check(false, 26, one, one)
+ check(false, 2, one, one)
+ check(false, 3, one, one)
// incorrect v, combinations of incorrect/correct r,s at lower limit
+ check(false, 2, zero, zero)
+ check(false, 2, zero, one)
+ check(false, 2, one, zero)
+ check(false, 2, one, one)
+
+ // correct v for any combination of incorrect r,s
check(false, 0, zero, zero)
check(false, 0, zero, one)
check(false, 0, one, zero)
- check(false, 0, one, one)
- // correct v for any combination of incorrect r,s
- check(false, 27, zero, zero)
- check(false, 27, zero, one)
- check(false, 27, one, zero)
-
- check(false, 28, zero, zero)
- check(false, 28, zero, one)
- check(false, 28, one, zero)
+ check(false, 1, zero, zero)
+ check(false, 1, zero, one)
+ check(false, 1, one, zero)
// correct sig with max r,s
- check(true, 27, secp256k1nMinus1, secp256k1nMinus1)
+ check(true, 0, secp256k1nMinus1, secp256k1nMinus1)
// correct v, combinations of incorrect r,s at upper limit
- check(false, 27, secp256k1.N, secp256k1nMinus1)
- check(false, 27, secp256k1nMinus1, secp256k1.N)
- check(false, 27, secp256k1.N, secp256k1.N)
+ check(false, 0, secp256k1.N, secp256k1nMinus1)
+ check(false, 0, secp256k1nMinus1, secp256k1.N)
+ check(false, 0, secp256k1.N, secp256k1.N)
// current callers ensures r,s cannot be negative, but let's test for that too
// as crypto package could be used stand-alone
- check(false, 27, minusOne, one)
- check(false, 27, one, minusOne)
+ check(false, 0, minusOne, one)
+ check(false, 0, one, minusOne)
}
func checkhash(t *testing.T, name string, f func([]byte) []byte, msg, exp []byte) {
sum := f(msg)
- if bytes.Compare(exp, sum) != 0 {
+ if !bytes.Equal(exp, sum) {
t.Fatalf("hash %s mismatch: want: %x have: %x", name, exp, sum)
}
}
diff --git a/crypto/ecies/asn1.go b/crypto/ecies/asn1.go
index 187321e7fc..e96d4351f2 100644
--- a/crypto/ecies/asn1.go
+++ b/crypto/ecies/asn1.go
@@ -109,7 +109,7 @@ func (curve secgNamedCurve) Equal(curve2 secgNamedCurve) bool {
if len(curve) != len(curve2) {
return false
}
- for i, _ := range curve {
+ for i := range curve {
if curve[i] != curve2[i] {
return false
}
@@ -157,7 +157,7 @@ func (a asnAlgorithmIdentifier) Cmp(b asnAlgorithmIdentifier) bool {
if len(a.Algorithm) != len(b.Algorithm) {
return false
}
- for i, _ := range a.Algorithm {
+ for i := range a.Algorithm {
if a.Algorithm[i] != b.Algorithm[i] {
return false
}
@@ -306,7 +306,7 @@ func (a asnECDHAlgorithm) Cmp(b asnECDHAlgorithm) bool {
if len(a.Algorithm) != len(b.Algorithm) {
return false
}
- for i, _ := range a.Algorithm {
+ for i := range a.Algorithm {
if a.Algorithm[i] != b.Algorithm[i] {
return false
}
@@ -325,7 +325,7 @@ func (a asnKeyDerivationFunction) Cmp(b asnKeyDerivationFunction) bool {
if len(a.Algorithm) != len(b.Algorithm) {
return false
}
- for i, _ := range a.Algorithm {
+ for i := range a.Algorithm {
if a.Algorithm[i] != b.Algorithm[i] {
return false
}
@@ -360,7 +360,7 @@ func (a asnSymmetricEncryption) Cmp(b asnSymmetricEncryption) bool {
if len(a.Algorithm) != len(b.Algorithm) {
return false
}
- for i, _ := range a.Algorithm {
+ for i := range a.Algorithm {
if a.Algorithm[i] != b.Algorithm[i] {
return false
}
@@ -380,7 +380,7 @@ func (a asnMessageAuthenticationCode) Cmp(b asnMessageAuthenticationCode) bool {
if len(a.Algorithm) != len(b.Algorithm) {
return false
}
- for i, _ := range a.Algorithm {
+ for i := range a.Algorithm {
if a.Algorithm[i] != b.Algorithm[i] {
return false
}
diff --git a/crypto/ecies/ecies.go b/crypto/ecies/ecies.go
index 86a70261d2..2a16f20a2e 100644
--- a/crypto/ecies/ecies.go
+++ b/crypto/ecies/ecies.go
@@ -93,7 +93,7 @@ func ImportECDSA(prv *ecdsa.PrivateKey) *PrivateKey {
}
// Generate an elliptic curve public / private keypair. If params is nil,
-// the recommended default paramters for the key will be chosen.
+// the recommended default parameters for the key will be chosen.
func GenerateKey(rand io.Reader, curve elliptic.Curve, params *ECIESParams) (prv *PrivateKey, err error) {
pb, x, y, err := elliptic.GenerateKey(curve, rand)
if err != nil {
@@ -291,9 +291,8 @@ func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err e
// Decrypt decrypts an ECIES ciphertext.
func (prv *PrivateKey) Decrypt(rand io.Reader, c, s1, s2 []byte) (m []byte, err error) {
- if c == nil || len(c) == 0 {
- err = ErrInvalidMessage
- return
+ if len(c) == 0 {
+ return nil, ErrInvalidMessage
}
params := prv.PublicKey.Params
if params == nil {
diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go
index db2ecdc249..1315da8df1 100644
--- a/crypto/ecies/ecies_test.go
+++ b/crypto/ecies/ecies_test.go
@@ -492,17 +492,17 @@ type testCase struct {
}
var testCases = []testCase{
- testCase{
+ {
Curve: elliptic.P256(),
Name: "P256",
Expected: true,
},
- testCase{
+ {
Curve: elliptic.P384(),
Name: "P384",
Expected: true,
},
- testCase{
+ {
Curve: elliptic.P521(),
Name: "P521",
Expected: true,
diff --git a/crypto/secp256k1/curve.go b/crypto/secp256k1/curve.go
index 6e44a6771f..61cad54636 100644
--- a/crypto/secp256k1/curve.go
+++ b/crypto/secp256k1/curve.go
@@ -33,7 +33,6 @@ package secp256k1
import (
"crypto/elliptic"
- "io"
"math/big"
"sync"
"unsafe"
@@ -224,6 +223,7 @@ func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int,
if len(scalar) > 32 {
panic("can't handle scalars > 256 bits")
}
+ // NOTE: potential timing issue
padded := make([]byte, 32)
copy(padded[32-len(scalar):], scalar)
scalar = padded
@@ -257,31 +257,6 @@ func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k)
}
-var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f}
-
-//TODO: double check if it is okay
-// GenerateKey returns a public/private key pair. The private key is generated
-// using the given reader, which must return random data.
-func (BitCurve *BitCurve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err error) {
- byteLen := (BitCurve.BitSize + 7) >> 3
- priv = make([]byte, byteLen)
-
- for x == nil {
- _, err = io.ReadFull(rand, priv)
- if err != nil {
- return
- }
- // We have to mask off any excess bits in the case that the size of the
- // underlying field is not a whole number of bytes.
- priv[0] &= mask[BitCurve.BitSize%8]
- // This is because, in tests, rand will return all zeros and we don't
- // want to get the point at infinity and loop forever.
- priv[1] ^= 0x42
- x, y = BitCurve.ScalarBaseMult(priv)
- }
- return
-}
-
// Marshal converts a point into the form specified in section 4.3.6 of ANSI
// X9.62.
func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte {
diff --git a/crypto/secp256k1/ext.h b/crypto/secp256k1/ext.h
new file mode 100644
index 0000000000..ee759fde69
--- /dev/null
+++ b/crypto/secp256k1/ext.h
@@ -0,0 +1,87 @@
+// 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 .
+
+// secp256k1_context_create_sign_verify creates a context for signing and signature verification.
+static secp256k1_context* secp256k1_context_create_sign_verify() {
+ return secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+}
+
+// secp256k1_ecdsa_recover_pubkey recovers the public key of an encoded compact signature.
+//
+// Returns: 1: recovery was successful
+// 0: recovery was not successful
+// Args: ctx: pointer to a context object (cannot be NULL)
+// Out: pubkey_out: the serialized 65-byte public key of the signer (cannot be NULL)
+// In: sigdata: pointer to a 65-byte signature with the recovery id at the end (cannot be NULL)
+// msgdata: pointer to a 32-byte message (cannot be NULL)
+static int secp256k1_ecdsa_recover_pubkey(
+ const secp256k1_context* ctx,
+ unsigned char *pubkey_out,
+ const unsigned char *sigdata,
+ const unsigned char *msgdata
+) {
+ secp256k1_ecdsa_recoverable_signature sig;
+ secp256k1_pubkey pubkey;
+
+ if (!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &sig, sigdata, (int)sigdata[64])) {
+ return 0;
+ }
+ if (!secp256k1_ecdsa_recover(ctx, &pubkey, &sig, msgdata)) {
+ return 0;
+ }
+ size_t outputlen = 65;
+ return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
+}
+
+// secp256k1_pubkey_scalar_mul multiplies a point by a scalar in constant time.
+//
+// Returns: 1: multiplication was successful
+// 0: scalar was invalid (zero or overflow)
+// Args: ctx: pointer to a context object (cannot be NULL)
+// Out: point: the multiplied point (usually secret)
+// In: point: pointer to a 64-byte public point,
+// encoded as two 256bit big-endian numbers.
+// scalar: a 32-byte scalar with which to multiply the point
+int secp256k1_pubkey_scalar_mul(const secp256k1_context* ctx, unsigned char *point, const unsigned char *scalar) {
+ int ret = 0;
+ int overflow = 0;
+ secp256k1_fe feX, feY;
+ secp256k1_gej res;
+ secp256k1_ge ge;
+ secp256k1_scalar s;
+ ARG_CHECK(point != NULL);
+ ARG_CHECK(scalar != NULL);
+ (void)ctx;
+
+ secp256k1_fe_set_b32(&feX, point);
+ secp256k1_fe_set_b32(&feY, point+32);
+ secp256k1_ge_set_xy(&ge, &feX, &feY);
+ secp256k1_scalar_set_b32(&s, scalar, &overflow);
+ if (overflow || secp256k1_scalar_is_zero(&s)) {
+ ret = 0;
+ } else {
+ secp256k1_ecmult_const(&res, &ge, &s);
+ secp256k1_ge_set_gej(&ge, &res);
+ /* Note: can't use secp256k1_pubkey_save here because it is not constant time. */
+ secp256k1_fe_normalize(&ge.x);
+ secp256k1_fe_normalize(&ge.y);
+ secp256k1_fe_get_b32(point, &ge.x);
+ secp256k1_fe_get_b32(point+32, &ge.y);
+ ret = 1;
+ }
+ secp256k1_scalar_clear(&s);
+ return ret;
+}
diff --git a/crypto/secp256k1/libsecp256k1/.gitignore b/crypto/secp256k1/libsecp256k1/.gitignore
index e0b7b7a48a..87fea161ba 100644
--- a/crypto/secp256k1/libsecp256k1/.gitignore
+++ b/crypto/secp256k1/libsecp256k1/.gitignore
@@ -6,6 +6,7 @@ bench_schnorr_verify
bench_recover
bench_internal
tests
+exhaustive_tests
gen_context
*.exe
*.so
@@ -25,17 +26,24 @@ config.status
libtool
.deps/
.dirstamp
-build-aux/
*.lo
*.o
*~
src/libsecp256k1-config.h
src/libsecp256k1-config.h.in
src/ecmult_static_context.h
-m4/libtool.m4
-m4/ltoptions.m4
-m4/ltsugar.m4
-m4/ltversion.m4
-m4/lt~obsolete.m4
+build-aux/config.guess
+build-aux/config.sub
+build-aux/depcomp
+build-aux/install-sh
+build-aux/ltmain.sh
+build-aux/m4/libtool.m4
+build-aux/m4/lt~obsolete.m4
+build-aux/m4/ltoptions.m4
+build-aux/m4/ltsugar.m4
+build-aux/m4/ltversion.m4
+build-aux/missing
+build-aux/compile
+build-aux/test-driver
src/stamp-h1
libsecp256k1.pc
diff --git a/crypto/secp256k1/libsecp256k1/.travis.yml b/crypto/secp256k1/libsecp256k1/.travis.yml
index fba0892ddb..2439529242 100644
--- a/crypto/secp256k1/libsecp256k1/.travis.yml
+++ b/crypto/secp256k1/libsecp256k1/.travis.yml
@@ -6,25 +6,30 @@ addons:
compiler:
- clang
- gcc
+cache:
+ directories:
+ - src/java/guava/
env:
global:
- - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=NO RECOVERY=NO
+ - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no
+ - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar
matrix:
- SCALAR=32bit RECOVERY=yes
- - SCALAR=32bit FIELD=32bit ECDH=yes
+ - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes
- SCALAR=64bit
- FIELD=64bit RECOVERY=yes
- FIELD=64bit ENDOMORPHISM=yes
- - FIELD=64bit ENDOMORPHISM=yes ECDH=yes
+ - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes
- FIELD=64bit ASM=x86_64
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
- - FIELD=32bit SCHNORR=yes
- FIELD=32bit ENDOMORPHISM=yes
- BIGNUM=no
- - BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes
+ - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes
- BIGNUM=no STATICPRECOMPUTATION=no
- BUILD=distcheck
- - EXTRAFLAGS=CFLAGS=-DDETERMINISTIC
+ - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
+ - EXTRAFLAGS=CFLAGS=-O0
+ - BUILD=check-java ECDH=yes EXPERIMENTAL=yes
matrix:
fast_finish: true
include:
@@ -54,9 +59,11 @@ matrix:
packages:
- gcc-multilib
- libgmp-dev:i386
+before_install: mkdir -p `dirname $GUAVA_JAR`
+install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi
before_script: ./autogen.sh
script:
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
- - ./configure --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
+ - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
os: linux
diff --git a/crypto/secp256k1/libsecp256k1/Makefile.am b/crypto/secp256k1/libsecp256k1/Makefile.am
index 57524fab05..c071fbe275 100644
--- a/crypto/secp256k1/libsecp256k1/Makefile.am
+++ b/crypto/secp256k1/libsecp256k1/Makefile.am
@@ -1,14 +1,22 @@
ACLOCAL_AMFLAGS = -I build-aux/m4
lib_LTLIBRARIES = libsecp256k1.la
+if USE_JNI
+JNI_LIB = libsecp256k1_jni.la
+noinst_LTLIBRARIES = $(JNI_LIB)
+else
+JNI_LIB =
+endif
include_HEADERS = include/secp256k1.h
noinst_HEADERS =
noinst_HEADERS += src/scalar.h
noinst_HEADERS += src/scalar_4x64.h
noinst_HEADERS += src/scalar_8x32.h
+noinst_HEADERS += src/scalar_low.h
noinst_HEADERS += src/scalar_impl.h
noinst_HEADERS += src/scalar_4x64_impl.h
noinst_HEADERS += src/scalar_8x32_impl.h
+noinst_HEADERS += src/scalar_low_impl.h
noinst_HEADERS += src/group.h
noinst_HEADERS += src/group_impl.h
noinst_HEADERS += src/num_gmp.h
@@ -32,6 +40,7 @@ noinst_HEADERS += src/field_5x52_impl.h
noinst_HEADERS += src/field_5x52_int128_impl.h
noinst_HEADERS += src/field_5x52_asm_impl.h
noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h
+noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h
noinst_HEADERS += src/util.h
noinst_HEADERS += src/testrand.h
noinst_HEADERS += src/testrand_impl.h
@@ -40,41 +49,103 @@ noinst_HEADERS += src/hash_impl.h
noinst_HEADERS += src/field.h
noinst_HEADERS += src/field_impl.h
noinst_HEADERS += src/bench.h
+noinst_HEADERS += contrib/lax_der_parsing.h
+noinst_HEADERS += contrib/lax_der_parsing.c
+noinst_HEADERS += contrib/lax_der_privatekey_parsing.h
+noinst_HEADERS += contrib/lax_der_privatekey_parsing.c
+
+if USE_EXTERNAL_ASM
+COMMON_LIB = libsecp256k1_common.la
+noinst_LTLIBRARIES = $(COMMON_LIB)
+else
+COMMON_LIB =
+endif
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libsecp256k1.pc
-libsecp256k1_la_SOURCES = src/secp256k1.c
-libsecp256k1_la_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
-libsecp256k1_la_LIBADD = $(SECP_LIBS)
+if USE_EXTERNAL_ASM
+if USE_ASM_ARM
+libsecp256k1_common_la_SOURCES = src/asm/field_10x26_arm.s
+endif
+endif
+libsecp256k1_la_SOURCES = src/secp256k1.c
+libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
+libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB)
+
+libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c
+libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES)
noinst_PROGRAMS =
if USE_BENCHMARK
noinst_PROGRAMS += bench_verify bench_sign bench_internal
bench_verify_SOURCES = src/bench_verify.c
-bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
-bench_verify_LDFLAGS = -static
+bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_sign_SOURCES = src/bench_sign.c
-bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS)
-bench_sign_LDFLAGS = -static
+bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_internal_SOURCES = src/bench_internal.c
-bench_internal_LDADD = $(SECP_LIBS)
-bench_internal_LDFLAGS = -static
-bench_internal_CPPFLAGS = $(SECP_INCLUDES)
+bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB)
+bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
endif
+TESTS =
if USE_TESTS
noinst_PROGRAMS += tests
tests_SOURCES = src/tests.c
-tests_CPPFLAGS = -DVERIFY -I$(top_srcdir)/src $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
-tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS)
+tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
+if !ENABLE_COVERAGE
+tests_CPPFLAGS += -DVERIFY
+endif
+tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
tests_LDFLAGS = -static
-TESTS = tests
+TESTS += tests
+endif
+
+if USE_EXHAUSTIVE_TESTS
+noinst_PROGRAMS += exhaustive_tests
+exhaustive_tests_SOURCES = src/tests_exhaustive.c
+exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src $(SECP_INCLUDES)
+if !ENABLE_COVERAGE
+exhaustive_tests_CPPFLAGS += -DVERIFY
+endif
+exhaustive_tests_LDADD = $(SECP_LIBS)
+exhaustive_tests_LDFLAGS = -static
+TESTS += exhaustive_tests
+endif
+
+JAVAROOT=src/java
+JAVAORG=org/bitcoin
+JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar
+CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA)
+JAVA_FILES= \
+ $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \
+ $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \
+ $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \
+ $(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java
+
+if USE_JNI
+
+$(JAVA_GUAVA):
+ @echo Guava is missing. Fetch it via: \
+ wget https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar -O $(@)
+ @false
+
+.stamp-java: $(JAVA_FILES)
+ @echo Compiling $^
+ $(AM_V_at)$(CLASSPATH_ENV) javac $^
+ @touch $@
+
+if USE_TESTS
+
+check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java
+ $(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test
+
+endif
endif
if USE_ECMULT_STATIC_PRECOMPUTATION
-CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)/
+CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)
CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function
gen_context_OBJECTS = gen_context.o
@@ -92,19 +163,15 @@ $(bench_internal_OBJECTS): src/ecmult_static_context.h
src/ecmult_static_context.h: $(gen_context_BIN)
./$(gen_context_BIN)
-CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h
+CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java
endif
-EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h
+EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES)
if ENABLE_MODULE_ECDH
include src/modules/ecdh/Makefile.am.include
endif
-if ENABLE_MODULE_SCHNORR
-include src/modules/schnorr/Makefile.am.include
-endif
-
if ENABLE_MODULE_RECOVERY
include src/modules/recovery/Makefile.am.include
endif
diff --git a/crypto/secp256k1/libsecp256k1/README.md b/crypto/secp256k1/libsecp256k1/README.md
index 6095db4220..8cd344ea81 100644
--- a/crypto/secp256k1/libsecp256k1/README.md
+++ b/crypto/secp256k1/libsecp256k1/README.md
@@ -1,7 +1,7 @@
libsecp256k1
============
-[](https://travis-ci.org/bitcoin/secp256k1)
+[](https://travis-ci.org/bitcoin-core/secp256k1)
Optimized C library for EC operations on curve secp256k1.
diff --git a/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4 b/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4
new file mode 100644
index 0000000000..1fc3627614
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4
@@ -0,0 +1,140 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_JNI_INCLUDE_DIR
+#
+# DESCRIPTION
+#
+# AX_JNI_INCLUDE_DIR finds include directories needed for compiling
+# programs using the JNI interface.
+#
+# JNI include directories are usually in the Java distribution. This is
+# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in
+# that order. When this macro completes, a list of directories is left in
+# the variable JNI_INCLUDE_DIRS.
+#
+# Example usage follows:
+#
+# AX_JNI_INCLUDE_DIR
+#
+# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS
+# do
+# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR"
+# done
+#
+# If you want to force a specific compiler:
+#
+# - at the configure.in level, set JAVAC=yourcompiler before calling
+# AX_JNI_INCLUDE_DIR
+#
+# - at the configure level, setenv JAVAC
+#
+# Note: This macro can work with the autoconf M4 macros for Java programs.
+# This particular macro is not part of the original set of macros.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Don Anderson
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 10
+
+AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR])
+AC_DEFUN([AX_JNI_INCLUDE_DIR],[
+
+JNI_INCLUDE_DIRS=""
+
+if test "x$JAVA_HOME" != x; then
+ _JTOPDIR="$JAVA_HOME"
+else
+ if test "x$JAVAC" = x; then
+ JAVAC=javac
+ fi
+ AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no])
+ if test "x$_ACJNI_JAVAC" = xno; then
+ AC_MSG_WARN([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME])
+ fi
+ _ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC")
+ _JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'`
+fi
+
+case "$host_os" in
+ darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
+ _JINC="$_JTOPDIR/Headers";;
+ *) _JINC="$_JTOPDIR/include";;
+esac
+_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR])
+_AS_ECHO_LOG([_JINC=$_JINC])
+
+# On Mac OS X 10.6.4, jni.h is a symlink:
+# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h
+# -> ../../CurrentJDK/Headers/jni.h.
+
+AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path,
+[
+if test -f "$_JINC/jni.h"; then
+ ac_cv_jni_header_path="$_JINC"
+ JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
+else
+ _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
+ if test -f "$_JTOPDIR/include/jni.h"; then
+ ac_cv_jni_header_path="$_JTOPDIR/include"
+ JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
+ else
+ ac_cv_jni_header_path=none
+ fi
+fi
+])
+
+
+
+# get the likely subdirectories for system specific java includes
+case "$host_os" in
+bsdi*) _JNI_INC_SUBDIRS="bsdos";;
+darwin*) _JNI_INC_SUBDIRS="darwin";;
+freebsd*) _JNI_INC_SUBDIRS="freebsd";;
+linux*) _JNI_INC_SUBDIRS="linux genunix";;
+osf*) _JNI_INC_SUBDIRS="alpha";;
+solaris*) _JNI_INC_SUBDIRS="solaris";;
+mingw*) _JNI_INC_SUBDIRS="win32";;
+cygwin*) _JNI_INC_SUBDIRS="win32";;
+*) _JNI_INC_SUBDIRS="genunix";;
+esac
+
+if test "x$ac_cv_jni_header_path" != "xnone"; then
+ # add any subdirectories that are present
+ for JINCSUBDIR in $_JNI_INC_SUBDIRS
+ do
+ if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
+ JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
+ fi
+ done
+fi
+])
+
+# _ACJNI_FOLLOW_SYMLINKS
+# Follows symbolic links on ,
+# finally setting variable _ACJNI_FOLLOWED
+# ----------------------------------------
+AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[
+# find the include directory relative to the javac executable
+_cur="$1"
+while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do
+ AC_MSG_CHECKING([symlink for $_cur])
+ _slink=`ls -ld "$_cur" | sed 's/.* -> //'`
+ case "$_slink" in
+ /*) _cur="$_slink";;
+ # 'X' avoids triggering unwanted echo options.
+ *) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";;
+ esac
+ AC_MSG_RESULT([$_cur])
+done
+_ACJNI_FOLLOWED="$_cur"
+])# _ACJNI
diff --git a/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4 b/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4
new file mode 100644
index 0000000000..77fd346a79
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4
@@ -0,0 +1,125 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_PROG_CC_FOR_BUILD
+#
+# DESCRIPTION
+#
+# This macro searches for a C compiler that generates native executables,
+# that is a C compiler that surely is not a cross-compiler. This can be
+# useful if you have to generate source code at compile-time like for
+# example GCC does.
+#
+# The macro sets the CC_FOR_BUILD and CPP_FOR_BUILD macros to anything
+# needed to compile or link (CC_FOR_BUILD) and preprocess (CPP_FOR_BUILD).
+# The value of these variables can be overridden by the user by specifying
+# a compiler with an environment variable (like you do for standard CC).
+#
+# It also sets BUILD_EXEEXT and BUILD_OBJEXT to the executable and object
+# file extensions for the build platform, and GCC_FOR_BUILD to `yes' if
+# the compiler we found is GCC. All these variables but GCC_FOR_BUILD are
+# substituted in the Makefile.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Paolo Bonzini
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 8
+
+AU_ALIAS([AC_PROG_CC_FOR_BUILD], [AX_PROG_CC_FOR_BUILD])
+AC_DEFUN([AX_PROG_CC_FOR_BUILD], [dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_PROG_CPP])dnl
+AC_REQUIRE([AC_EXEEXT])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+
+dnl Use the standard macros, but make them use other variable names
+dnl
+pushdef([ac_cv_prog_CPP], ac_cv_build_prog_CPP)dnl
+pushdef([ac_cv_prog_gcc], ac_cv_build_prog_gcc)dnl
+pushdef([ac_cv_prog_cc_works], ac_cv_build_prog_cc_works)dnl
+pushdef([ac_cv_prog_cc_cross], ac_cv_build_prog_cc_cross)dnl
+pushdef([ac_cv_prog_cc_g], ac_cv_build_prog_cc_g)dnl
+pushdef([ac_cv_exeext], ac_cv_build_exeext)dnl
+pushdef([ac_cv_objext], ac_cv_build_objext)dnl
+pushdef([ac_exeext], ac_build_exeext)dnl
+pushdef([ac_objext], ac_build_objext)dnl
+pushdef([CC], CC_FOR_BUILD)dnl
+pushdef([CPP], CPP_FOR_BUILD)dnl
+pushdef([CFLAGS], CFLAGS_FOR_BUILD)dnl
+pushdef([CPPFLAGS], CPPFLAGS_FOR_BUILD)dnl
+pushdef([LDFLAGS], LDFLAGS_FOR_BUILD)dnl
+pushdef([host], build)dnl
+pushdef([host_alias], build_alias)dnl
+pushdef([host_cpu], build_cpu)dnl
+pushdef([host_vendor], build_vendor)dnl
+pushdef([host_os], build_os)dnl
+pushdef([ac_cv_host], ac_cv_build)dnl
+pushdef([ac_cv_host_alias], ac_cv_build_alias)dnl
+pushdef([ac_cv_host_cpu], ac_cv_build_cpu)dnl
+pushdef([ac_cv_host_vendor], ac_cv_build_vendor)dnl
+pushdef([ac_cv_host_os], ac_cv_build_os)dnl
+pushdef([ac_cpp], ac_build_cpp)dnl
+pushdef([ac_compile], ac_build_compile)dnl
+pushdef([ac_link], ac_build_link)dnl
+
+save_cross_compiling=$cross_compiling
+save_ac_tool_prefix=$ac_tool_prefix
+cross_compiling=no
+ac_tool_prefix=
+
+AC_PROG_CC
+AC_PROG_CPP
+AC_EXEEXT
+
+ac_tool_prefix=$save_ac_tool_prefix
+cross_compiling=$save_cross_compiling
+
+dnl Restore the old definitions
+dnl
+popdef([ac_link])dnl
+popdef([ac_compile])dnl
+popdef([ac_cpp])dnl
+popdef([ac_cv_host_os])dnl
+popdef([ac_cv_host_vendor])dnl
+popdef([ac_cv_host_cpu])dnl
+popdef([ac_cv_host_alias])dnl
+popdef([ac_cv_host])dnl
+popdef([host_os])dnl
+popdef([host_vendor])dnl
+popdef([host_cpu])dnl
+popdef([host_alias])dnl
+popdef([host])dnl
+popdef([LDFLAGS])dnl
+popdef([CPPFLAGS])dnl
+popdef([CFLAGS])dnl
+popdef([CPP])dnl
+popdef([CC])dnl
+popdef([ac_objext])dnl
+popdef([ac_exeext])dnl
+popdef([ac_cv_objext])dnl
+popdef([ac_cv_exeext])dnl
+popdef([ac_cv_prog_cc_g])dnl
+popdef([ac_cv_prog_cc_cross])dnl
+popdef([ac_cv_prog_cc_works])dnl
+popdef([ac_cv_prog_gcc])dnl
+popdef([ac_cv_prog_CPP])dnl
+
+dnl Finally, set Makefile variables
+dnl
+BUILD_EXEEXT=$ac_build_exeext
+BUILD_OBJEXT=$ac_build_objext
+AC_SUBST(BUILD_EXEEXT)dnl
+AC_SUBST(BUILD_OBJEXT)dnl
+AC_SUBST([CFLAGS_FOR_BUILD])dnl
+AC_SUBST([CPPFLAGS_FOR_BUILD])dnl
+AC_SUBST([LDFLAGS_FOR_BUILD])dnl
+])
diff --git a/crypto/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4 b/crypto/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4
new file mode 100644
index 0000000000..b74acb8c13
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4
@@ -0,0 +1,69 @@
+dnl libsecp25k1 helper checks
+AC_DEFUN([SECP_INT128_CHECK],[
+has_int128=$ac_cv_type___int128
+])
+
+dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell.
+AC_DEFUN([SECP_64BIT_ASM_CHECK],[
+AC_MSG_CHECKING(for x86_64 assembly availability)
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+ #include ]],[[
+ uint64_t a = 11, tmp;
+ __asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx");
+ ]])],[has_64bit_asm=yes],[has_64bit_asm=no])
+AC_MSG_RESULT([$has_64bit_asm])
+])
+
+dnl
+AC_DEFUN([SECP_OPENSSL_CHECK],[
+ has_libcrypto=no
+ m4_ifdef([PKG_CHECK_MODULES],[
+ PKG_CHECK_MODULES([CRYPTO], [libcrypto], [has_libcrypto=yes],[has_libcrypto=no])
+ if test x"$has_libcrypto" = x"yes"; then
+ TEMP_LIBS="$LIBS"
+ LIBS="$LIBS $CRYPTO_LIBS"
+ AC_CHECK_LIB(crypto, main,[AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed])],[has_libcrypto=no])
+ LIBS="$TEMP_LIBS"
+ fi
+ ])
+ if test x$has_libcrypto = xno; then
+ AC_CHECK_HEADER(openssl/crypto.h,[
+ AC_CHECK_LIB(crypto, main,[
+ has_libcrypto=yes
+ CRYPTO_LIBS=-lcrypto
+ AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed])
+ ])
+ ])
+ LIBS=
+ fi
+if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
+ AC_MSG_CHECKING(for EC functions in libcrypto)
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+ #include
+ #include
+ #include ]],[[
+ EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
+ ECDSA_sign(0, NULL, 0, NULL, NULL, eckey);
+ ECDSA_verify(0, NULL, 0, NULL, 0, eckey);
+ EC_KEY_free(eckey);
+ ECDSA_SIG *sig_openssl;
+ sig_openssl = ECDSA_SIG_new();
+ (void)sig_openssl->r;
+ ECDSA_SIG_free(sig_openssl);
+ ]])],[has_openssl_ec=yes],[has_openssl_ec=no])
+ AC_MSG_RESULT([$has_openssl_ec])
+fi
+])
+
+dnl
+AC_DEFUN([SECP_GMP_CHECK],[
+if test x"$has_gmp" != x"yes"; then
+ CPPFLAGS_TEMP="$CPPFLAGS"
+ CPPFLAGS="$GMP_CPPFLAGS $CPPFLAGS"
+ LIBS_TEMP="$LIBS"
+ LIBS="$GMP_LIBS $LIBS"
+ AC_CHECK_HEADER(gmp.h,[AC_CHECK_LIB(gmp, __gmpz_init,[has_gmp=yes; GMP_LIBS="$GMP_LIBS -lgmp"; AC_DEFINE(HAVE_LIBGMP,1,[Define this symbol if libgmp is installed])])])
+ CPPFLAGS="$CPPFLAGS_TEMP"
+ LIBS="$LIBS_TEMP"
+fi
+])
diff --git a/crypto/secp256k1/libsecp256k1/configure.ac b/crypto/secp256k1/libsecp256k1/configure.ac
index 786d8dcfb9..e5fcbcb4ed 100644
--- a/crypto/secp256k1/libsecp256k1/configure.ac
+++ b/crypto/secp256k1/libsecp256k1/configure.ac
@@ -20,7 +20,7 @@ AC_PATH_TOOL(STRIP, strip)
AX_PROG_CC_FOR_BUILD
if test "x$CFLAGS" = "x"; then
- CFLAGS="-O3 -g"
+ CFLAGS="-g"
fi
AM_PROG_CC_C_O
@@ -29,6 +29,7 @@ AC_PROG_CC_C89
if test x"$ac_cv_prog_cc_c89" = x"no"; then
AC_MSG_ERROR([c89 compiler support required])
fi
+AM_PROG_AS
case $host_os in
*darwin*)
@@ -88,36 +89,56 @@ AC_ARG_ENABLE(benchmark,
[use_benchmark=$enableval],
[use_benchmark=no])
+AC_ARG_ENABLE(coverage,
+ AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]),
+ [enable_coverage=$enableval],
+ [enable_coverage=no])
+
AC_ARG_ENABLE(tests,
AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]),
[use_tests=$enableval],
[use_tests=yes])
+AC_ARG_ENABLE(openssl_tests,
+ AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]),
+ [enable_openssl_tests=$enableval],
+ [enable_openssl_tests=auto])
+
+AC_ARG_ENABLE(experimental,
+ AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]),
+ [use_experimental=$enableval],
+ [use_experimental=no])
+
+AC_ARG_ENABLE(exhaustive_tests,
+ AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]),
+ [use_exhaustive_tests=$enableval],
+ [use_exhaustive_tests=yes])
+
AC_ARG_ENABLE(endomorphism,
AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]),
[use_endomorphism=$enableval],
[use_endomorphism=no])
-
+
AC_ARG_ENABLE(ecmult_static_precomputation,
AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]),
[use_ecmult_static_precomputation=$enableval],
- [use_ecmult_static_precomputation=yes])
+ [use_ecmult_static_precomputation=auto])
AC_ARG_ENABLE(module_ecdh,
- AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (default is no)]),
+ AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]),
[enable_module_ecdh=$enableval],
[enable_module_ecdh=no])
-AC_ARG_ENABLE(module_schnorr,
- AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (default is no)]),
- [enable_module_schnorr=$enableval],
- [enable_module_schnorr=no])
-
AC_ARG_ENABLE(module_recovery,
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]),
[enable_module_recovery=$enableval],
[enable_module_recovery=no])
+AC_ARG_ENABLE(jni,
+ AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]),
+ [use_jni=$enableval],
+ [use_jni=auto])
+
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto])
@@ -127,8 +148,8 @@ AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto],
[Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=auto])
-AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|no|auto]
-[Specify assembly optimizations to use. Default is auto])],[req_asm=$withval], [req_asm=auto])
+AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto]
+[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto])
AC_CHECK_TYPES([__int128])
@@ -138,6 +159,42 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])],
[ AC_MSG_RESULT([no])
])
+if test x"$enable_coverage" = x"yes"; then
+ AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code])
+ CFLAGS="$CFLAGS -O0 --coverage"
+ LDFLAGS="--coverage"
+else
+ CFLAGS="$CFLAGS -O3"
+fi
+
+if test x"$use_ecmult_static_precomputation" != x"no"; then
+ save_cross_compiling=$cross_compiling
+ cross_compiling=no
+ TEMP_CC="$CC"
+ CC="$CC_FOR_BUILD"
+ AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}])
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([], [return 0])],
+ [working_native_cc=yes],
+ [working_native_cc=no],[dnl])
+ CC="$TEMP_CC"
+ cross_compiling=$save_cross_compiling
+
+ if test x"$working_native_cc" = x"no"; then
+ set_precomp=no
+ if test x"$use_ecmult_static_precomputation" = x"yes"; then
+ AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
+ else
+ AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
+ fi
+ else
+ AC_MSG_RESULT([ok])
+ set_precomp=yes
+ fi
+else
+ set_precomp=no
+fi
+
if test x"$req_asm" = x"auto"; then
SECP_64BIT_ASM_CHECK
if test x"$has_64bit_asm" = x"yes"; then
@@ -155,6 +212,8 @@ else
AC_MSG_ERROR([x86_64 assembly optimization requested but not available])
fi
;;
+ arm)
+ ;;
no)
;;
*)
@@ -247,10 +306,15 @@ else
fi
# select assembly optimization
+use_external_asm=no
+
case $set_asm in
x86_64)
AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations])
;;
+arm)
+ use_external_asm=yes
+ ;;
no)
;;
*)
@@ -305,16 +369,48 @@ esac
if test x"$use_tests" = x"yes"; then
SECP_OPENSSL_CHECK
if test x"$has_openssl_ec" = x"yes"; then
- AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available])
- SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS"
- SECP_TEST_LIBS="$CRYPTO_LIBS"
+ if test x"$enable_openssl_tests" != x"no"; then
+ AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available])
+ SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS"
+ SECP_TEST_LIBS="$CRYPTO_LIBS"
- case $host in
- *mingw*)
- SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32"
- ;;
- esac
+ case $host in
+ *mingw*)
+ SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32"
+ ;;
+ esac
+ fi
+ else
+ if test x"$enable_openssl_tests" = x"yes"; then
+ AC_MSG_ERROR([OpenSSL tests requested but OpenSSL with EC support is not available])
+ fi
+ fi
+else
+ if test x"$enable_openssl_tests" = x"yes"; then
+ AC_MSG_ERROR([OpenSSL tests requested but tests are not enabled])
+ fi
+fi
+if test x"$use_jni" != x"no"; then
+ AX_JNI_INCLUDE_DIR
+ have_jni_dependencies=yes
+ if test x"$enable_module_ecdh" = x"no"; then
+ have_jni_dependencies=no
+ fi
+ if test "x$JNI_INCLUDE_DIRS" = "x"; then
+ have_jni_dependencies=no
+ fi
+ if test "x$have_jni_dependencies" = "xno"; then
+ if test x"$use_jni" = x"yes"; then
+ AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and try again.])
+ fi
+ AC_MSG_WARN([jni headers/dependencies not found. jni support disabled])
+ use_jni=no
+ else
+ use_jni=yes
+ for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do
+ JNI_INCLUDES="$JNI_INCLUDES -I$JNI_INCLUDE_DIR"
+ done
fi
fi
@@ -327,7 +423,7 @@ if test x"$use_endomorphism" = x"yes"; then
AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization])
fi
-if test x"$use_ecmult_static_precomputation" = x"yes"; then
+if test x"$set_precomp" = x"yes"; then
AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table])
fi
@@ -335,38 +431,59 @@ if test x"$enable_module_ecdh" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module])
fi
-if test x"$enable_module_schnorr" = x"yes"; then
- AC_DEFINE(ENABLE_MODULE_SCHNORR, 1, [Define this symbol to enable the Schnorr signature module])
-fi
-
if test x"$enable_module_recovery" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
fi
AC_C_BIGENDIAN()
+if test x"$use_external_asm" = x"yes"; then
+ AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
+fi
+
+AC_MSG_NOTICE([Using static precomputation: $set_precomp])
AC_MSG_NOTICE([Using assembly optimizations: $set_asm])
AC_MSG_NOTICE([Using field implementation: $set_field])
AC_MSG_NOTICE([Using bignum implementation: $set_bignum])
AC_MSG_NOTICE([Using scalar implementation: $set_scalar])
AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism])
+AC_MSG_NOTICE([Building for coverage analysis: $enable_coverage])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
-
-AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery])
+AC_MSG_NOTICE([Using jni: $use_jni])
+
+if test x"$enable_experimental" = x"yes"; then
+ AC_MSG_NOTICE([******])
+ AC_MSG_NOTICE([WARNING: experimental build])
+ AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
+ AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
+ AC_MSG_NOTICE([******])
+else
+ if test x"$enable_module_ecdh" = x"yes"; then
+ AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
+ fi
+ if test x"$set_asm" = x"arm"; then
+ AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
+ fi
+fi
AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
+AC_SUBST(JNI_INCLUDES)
AC_SUBST(SECP_INCLUDES)
AC_SUBST(SECP_LIBS)
AC_SUBST(SECP_TEST_LIBS)
AC_SUBST(SECP_TEST_INCLUDES)
+AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"])
AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"])
+AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"])
AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
-AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$use_ecmult_static_precomputation" = x"yes"])
+AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
-AM_CONDITIONAL([ENABLE_MODULE_SCHNORR], [test x"$enable_module_schnorr" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
+AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
+AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
+AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
dnl make sure nothing new is exported so that we don't break the cache
PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"
diff --git a/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.c b/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.c
new file mode 100644
index 0000000000..5b141a9948
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.c
@@ -0,0 +1,150 @@
+/**********************************************************************
+ * Copyright (c) 2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#include
+#include
+
+#include "lax_der_parsing.h"
+
+int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
+ size_t rpos, rlen, spos, slen;
+ size_t pos = 0;
+ size_t lenbyte;
+ unsigned char tmpsig[64] = {0};
+ int overflow = 0;
+
+ /* Hack to initialize sig with a correctly-parsed but invalid signature. */
+ secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
+
+ /* Sequence tag byte */
+ if (pos == inputlen || input[pos] != 0x30) {
+ return 0;
+ }
+ pos++;
+
+ /* Sequence length bytes */
+ if (pos == inputlen) {
+ return 0;
+ }
+ lenbyte = input[pos++];
+ if (lenbyte & 0x80) {
+ lenbyte -= 0x80;
+ if (pos + lenbyte > inputlen) {
+ return 0;
+ }
+ pos += lenbyte;
+ }
+
+ /* Integer tag byte for R */
+ if (pos == inputlen || input[pos] != 0x02) {
+ return 0;
+ }
+ pos++;
+
+ /* Integer length for R */
+ if (pos == inputlen) {
+ return 0;
+ }
+ lenbyte = input[pos++];
+ if (lenbyte & 0x80) {
+ lenbyte -= 0x80;
+ if (pos + lenbyte > inputlen) {
+ return 0;
+ }
+ while (lenbyte > 0 && input[pos] == 0) {
+ pos++;
+ lenbyte--;
+ }
+ if (lenbyte >= sizeof(size_t)) {
+ return 0;
+ }
+ rlen = 0;
+ while (lenbyte > 0) {
+ rlen = (rlen << 8) + input[pos];
+ pos++;
+ lenbyte--;
+ }
+ } else {
+ rlen = lenbyte;
+ }
+ if (rlen > inputlen - pos) {
+ return 0;
+ }
+ rpos = pos;
+ pos += rlen;
+
+ /* Integer tag byte for S */
+ if (pos == inputlen || input[pos] != 0x02) {
+ return 0;
+ }
+ pos++;
+
+ /* Integer length for S */
+ if (pos == inputlen) {
+ return 0;
+ }
+ lenbyte = input[pos++];
+ if (lenbyte & 0x80) {
+ lenbyte -= 0x80;
+ if (pos + lenbyte > inputlen) {
+ return 0;
+ }
+ while (lenbyte > 0 && input[pos] == 0) {
+ pos++;
+ lenbyte--;
+ }
+ if (lenbyte >= sizeof(size_t)) {
+ return 0;
+ }
+ slen = 0;
+ while (lenbyte > 0) {
+ slen = (slen << 8) + input[pos];
+ pos++;
+ lenbyte--;
+ }
+ } else {
+ slen = lenbyte;
+ }
+ if (slen > inputlen - pos) {
+ return 0;
+ }
+ spos = pos;
+ pos += slen;
+
+ /* Ignore leading zeroes in R */
+ while (rlen > 0 && input[rpos] == 0) {
+ rlen--;
+ rpos++;
+ }
+ /* Copy R value */
+ if (rlen > 32) {
+ overflow = 1;
+ } else {
+ memcpy(tmpsig + 32 - rlen, input + rpos, rlen);
+ }
+
+ /* Ignore leading zeroes in S */
+ while (slen > 0 && input[spos] == 0) {
+ slen--;
+ spos++;
+ }
+ /* Copy S value */
+ if (slen > 32) {
+ overflow = 1;
+ } else {
+ memcpy(tmpsig + 64 - slen, input + spos, slen);
+ }
+
+ if (!overflow) {
+ overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
+ }
+ if (overflow) {
+ memset(tmpsig, 0, 64);
+ secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
+ }
+ return 1;
+}
+
diff --git a/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.h b/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.h
new file mode 100644
index 0000000000..6d27871a7c
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.h
@@ -0,0 +1,91 @@
+/**********************************************************************
+ * Copyright (c) 2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+/****
+ * Please do not link this file directly. It is not part of the libsecp256k1
+ * project and does not promise any stability in its API, functionality or
+ * presence. Projects which use this code should instead copy this header
+ * and its accompanying .c file directly into their codebase.
+ ****/
+
+/* This file defines a function that parses DER with various errors and
+ * violations. This is not a part of the library itself, because the allowed
+ * violations are chosen arbitrarily and do not follow or establish any
+ * standard.
+ *
+ * In many places it matters that different implementations do not only accept
+ * the same set of valid signatures, but also reject the same set of signatures.
+ * The only means to accomplish that is by strictly obeying a standard, and not
+ * accepting anything else.
+ *
+ * Nonetheless, sometimes there is a need for compatibility with systems that
+ * use signatures which do not strictly obey DER. The snippet below shows how
+ * certain violations are easily supported. You may need to adapt it.
+ *
+ * Do not use this for new systems. Use well-defined DER or compact signatures
+ * instead if you have the choice (see secp256k1_ecdsa_signature_parse_der and
+ * secp256k1_ecdsa_signature_parse_compact).
+ *
+ * The supported violations are:
+ * - All numbers are parsed as nonnegative integers, even though X.609-0207
+ * section 8.3.3 specifies that integers are always encoded as two's
+ * complement.
+ * - Integers can have length 0, even though section 8.3.1 says they can't.
+ * - Integers with overly long padding are accepted, violation section
+ * 8.3.2.
+ * - 127-byte long length descriptors are accepted, even though section
+ * 8.1.3.5.c says that they are not.
+ * - Trailing garbage data inside or after the signature is ignored.
+ * - The length descriptor of the sequence is ignored.
+ *
+ * Compared to for example OpenSSL, many violations are NOT supported:
+ * - Using overly long tag descriptors for the sequence or integers inside,
+ * violating section 8.1.2.2.
+ * - Encoding primitive integers as constructed values, violating section
+ * 8.3.1.
+ */
+
+#ifndef _SECP256K1_CONTRIB_LAX_DER_PARSING_H_
+#define _SECP256K1_CONTRIB_LAX_DER_PARSING_H_
+
+#include
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+/** Parse a signature in "lax DER" format
+ *
+ * Returns: 1 when the signature could be parsed, 0 otherwise.
+ * Args: ctx: a secp256k1 context object
+ * Out: sig: a pointer to a signature object
+ * In: input: a pointer to the signature to be parsed
+ * inputlen: the length of the array pointed to be input
+ *
+ * This function will accept any valid DER encoded signature, even if the
+ * encoded numbers are out of range. In addition, it will accept signatures
+ * which violate the DER spec in various ways. Its purpose is to allow
+ * validation of the Bitcoin blockchain, which includes non-DER signatures
+ * from before the network rules were updated to enforce DER. Note that
+ * the set of supported violations is a strict subset of what OpenSSL will
+ * accept.
+ *
+ * After the call, sig will always be initialized. If parsing failed or the
+ * encoded numbers are out of range, signature validation with it is
+ * guaranteed to fail for every message and public key.
+ */
+int ecdsa_signature_parse_der_lax(
+ const secp256k1_context* ctx,
+ secp256k1_ecdsa_signature* sig,
+ const unsigned char *input,
+ size_t inputlen
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c b/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c
new file mode 100644
index 0000000000..c2e63b4b8d
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c
@@ -0,0 +1,113 @@
+/**********************************************************************
+ * Copyright (c) 2014, 2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#include
+#include
+
+#include "lax_der_privatekey_parsing.h"
+
+int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *privkey, size_t privkeylen) {
+ const unsigned char *end = privkey + privkeylen;
+ int lenb = 0;
+ int len = 0;
+ memset(out32, 0, 32);
+ /* sequence header */
+ if (end < privkey+1 || *privkey != 0x30) {
+ return 0;
+ }
+ privkey++;
+ /* sequence length constructor */
+ if (end < privkey+1 || !(*privkey & 0x80)) {
+ return 0;
+ }
+ lenb = *privkey & ~0x80; privkey++;
+ if (lenb < 1 || lenb > 2) {
+ return 0;
+ }
+ if (end < privkey+lenb) {
+ return 0;
+ }
+ /* sequence length */
+ len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0);
+ privkey += lenb;
+ if (end < privkey+len) {
+ return 0;
+ }
+ /* sequence element 0: version number (=1) */
+ if (end < privkey+3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) {
+ return 0;
+ }
+ privkey += 3;
+ /* sequence element 1: octet string, up to 32 bytes */
+ if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) {
+ return 0;
+ }
+ memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]);
+ if (!secp256k1_ec_seckey_verify(ctx, out32)) {
+ memset(out32, 0, 32);
+ return 0;
+ }
+ return 1;
+}
+
+int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, int compressed) {
+ secp256k1_pubkey pubkey;
+ size_t pubkeylen = 0;
+ if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) {
+ *privkeylen = 0;
+ return 0;
+ }
+ if (compressed) {
+ static const unsigned char begin[] = {
+ 0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20
+ };
+ static const unsigned char middle[] = {
+ 0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
+ 0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04,
+ 0x21,0x02,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87,
+ 0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8,
+ 0x17,0x98,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
+ 0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,0x00
+ };
+ unsigned char *ptr = privkey;
+ memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
+ memcpy(ptr, key32, 32); ptr += 32;
+ memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
+ pubkeylen = 33;
+ secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED);
+ ptr += pubkeylen;
+ *privkeylen = ptr - privkey;
+ } else {
+ static const unsigned char begin[] = {
+ 0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20
+ };
+ static const unsigned char middle[] = {
+ 0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
+ 0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04,
+ 0x41,0x04,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87,
+ 0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8,
+ 0x17,0x98,0x48,0x3A,0xDA,0x77,0x26,0xA3,0xC4,0x65,0x5D,0xA4,0xFB,0xFC,0x0E,0x11,
+ 0x08,0xA8,0xFD,0x17,0xB4,0x48,0xA6,0x85,0x54,0x19,0x9C,0x47,0xD0,0x8F,0xFB,0x10,
+ 0xD4,0xB8,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
+ 0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,0x00
+ };
+ unsigned char *ptr = privkey;
+ memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
+ memcpy(ptr, key32, 32); ptr += 32;
+ memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
+ pubkeylen = 65;
+ secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
+ ptr += pubkeylen;
+ *privkeylen = ptr - privkey;
+ }
+ return 1;
+}
diff --git a/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h b/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h
new file mode 100644
index 0000000000..2fd088f8ab
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h
@@ -0,0 +1,90 @@
+/**********************************************************************
+ * Copyright (c) 2014, 2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+/****
+ * Please do not link this file directly. It is not part of the libsecp256k1
+ * project and does not promise any stability in its API, functionality or
+ * presence. Projects which use this code should instead copy this header
+ * and its accompanying .c file directly into their codebase.
+ ****/
+
+/* This file contains code snippets that parse DER private keys with
+ * various errors and violations. This is not a part of the library
+ * itself, because the allowed violations are chosen arbitrarily and
+ * do not follow or establish any standard.
+ *
+ * It also contains code to serialize private keys in a compatible
+ * manner.
+ *
+ * These functions are meant for compatibility with applications
+ * that require BER encoded keys. When working with secp256k1-specific
+ * code, the simple 32-byte private keys normally used by the
+ * library are sufficient.
+ */
+
+#ifndef _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_
+#define _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_
+
+#include
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+/** Export a private key in DER format.
+ *
+ * Returns: 1 if the private key was valid.
+ * Args: ctx: pointer to a context object, initialized for signing (cannot
+ * be NULL)
+ * Out: privkey: pointer to an array for storing the private key in BER.
+ * Should have space for 279 bytes, and cannot be NULL.
+ * privkeylen: Pointer to an int where the length of the private key in
+ * privkey will be stored.
+ * In: seckey: pointer to a 32-byte secret key to export.
+ * compressed: 1 if the key should be exported in
+ * compressed format, 0 otherwise
+ *
+ * This function is purely meant for compatibility with applications that
+ * require BER encoded keys. When working with secp256k1-specific code, the
+ * simple 32-byte private keys are sufficient.
+ *
+ * Note that this function does not guarantee correct DER output. It is
+ * guaranteed to be parsable by secp256k1_ec_privkey_import_der
+ */
+SECP256K1_WARN_UNUSED_RESULT int ec_privkey_export_der(
+ const secp256k1_context* ctx,
+ unsigned char *privkey,
+ size_t *privkeylen,
+ const unsigned char *seckey,
+ int compressed
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+
+/** Import a private key in DER format.
+ * Returns: 1 if a private key was extracted.
+ * Args: ctx: pointer to a context object (cannot be NULL).
+ * Out: seckey: pointer to a 32-byte array for storing the private key.
+ * (cannot be NULL).
+ * In: privkey: pointer to a private key in DER format (cannot be NULL).
+ * privkeylen: length of the DER private key pointed to be privkey.
+ *
+ * This function will accept more than just strict DER, and even allow some BER
+ * violations. The public key stored inside the DER-encoded private key is not
+ * verified for correctness, nor are the curve parameters. Use this function
+ * only if you know in advance it is supposed to contain a secp256k1 private
+ * key.
+ */
+SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der(
+ const secp256k1_context* ctx,
+ unsigned char *seckey,
+ const unsigned char *privkey,
+ size_t privkeylen
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/crypto/secp256k1/libsecp256k1/include/secp256k1.h b/crypto/secp256k1/libsecp256k1/include/secp256k1.h
index 23378de1fd..f268e309d0 100644
--- a/crypto/secp256k1/libsecp256k1/include/secp256k1.h
+++ b/crypto/secp256k1/libsecp256k1/include/secp256k1.h
@@ -47,11 +47,8 @@ typedef struct secp256k1_context_struct secp256k1_context;
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
- * If you need to convert to a format suitable for storage or transmission, use
- * secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse.
- *
- * Furthermore, it is guaranteed that identical public keys (ignoring
- * compression) will have identical representation, so they can be memcmp'ed.
+ * If you need to convert to a format suitable for storage, transmission, or
+ * comparison, use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse.
*/
typedef struct {
unsigned char data[64];
@@ -62,12 +59,9 @@ typedef struct {
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
- * If you need to convert to a format suitable for storage or transmission, use
- * the secp256k1_ecdsa_signature_serialize_* and
+ * If you need to convert to a format suitable for storage, transmission, or
+ * comparison, use the secp256k1_ecdsa_signature_serialize_* and
* secp256k1_ecdsa_signature_serialize_* functions.
- *
- * Furthermore, it is guaranteed to identical signatures will have identical
- * representation, so they can be memcmp'ed.
*/
typedef struct {
unsigned char data[64];
@@ -147,12 +141,23 @@ typedef int (*secp256k1_nonce_function)(
# define SECP256K1_ARG_NONNULL(_x)
# endif
+/** All flags' lower 8 bits indicate what they're for. Do not use directly. */
+#define SECP256K1_FLAGS_TYPE_MASK ((1 << 8) - 1)
+#define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0)
+#define SECP256K1_FLAGS_TYPE_COMPRESSION (1 << 1)
+/** The higher bits contain the actual data. Do not use directly. */
+#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8)
+#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9)
+#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8)
+
/** Flags to pass to secp256k1_context_create. */
-# define SECP256K1_CONTEXT_VERIFY (1 << 0)
-# define SECP256K1_CONTEXT_SIGN (1 << 1)
+#define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
+#define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
+#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT)
/** Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export. */
-# define SECP256K1_EC_COMPRESSED (1 << 0)
+#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
+#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION)
/** Create a secp256k1 context object.
*
@@ -218,7 +223,7 @@ SECP256K1_API void secp256k1_context_set_illegal_callback(
* crashing.
*
* Args: ctx: an existing context object (cannot be NULL)
- * In: fun: a pointer to a function to call when an interal error occurs,
+ * In: fun: a pointer to a function to call when an internal error occurs,
* taking a message and an opaque pointer (NULL restores a default
* handler that calls abort).
* data: the opaque pointer to pass to fun above.
@@ -253,15 +258,17 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse(
/** Serialize a pubkey object into a serialized byte sequence.
*
* Returns: 1 always.
- * Args: ctx: a secp256k1 context object.
- * Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if
- * compressed==1) byte array to place the serialized key in.
- * outputlen: a pointer to an integer which will contain the serialized
- * size.
- * In: pubkey: a pointer to a secp256k1_pubkey containing an initialized
- * public key.
- * flags: SECP256K1_EC_COMPRESSED if serialization should be in
- * compressed format.
+ * Args: ctx: a secp256k1 context object.
+ * Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if
+ * compressed==1) byte array to place the serialized key
+ * in.
+ * In/Out: outputlen: a pointer to an integer which is initially set to the
+ * size of output, and is overwritten with the written
+ * size.
+ * In: pubkey: a pointer to a secp256k1_pubkey containing an
+ * initialized public key.
+ * flags: SECP256K1_EC_COMPRESSED if serialization should be in
+ * compressed format, otherwise SECP256K1_EC_UNCOMPRESSED.
*/
SECP256K1_API int secp256k1_ec_pubkey_serialize(
const secp256k1_context* ctx,
@@ -271,6 +278,27 @@ SECP256K1_API int secp256k1_ec_pubkey_serialize(
unsigned int flags
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+/** Parse an ECDSA signature in compact (64 bytes) format.
+ *
+ * Returns: 1 when the signature could be parsed, 0 otherwise.
+ * Args: ctx: a secp256k1 context object
+ * Out: sig: a pointer to a signature object
+ * In: input64: a pointer to the 64-byte array to parse
+ *
+ * The signature must consist of a 32-byte big endian R value, followed by a
+ * 32-byte big endian S value. If R or S fall outside of [0..order-1], the
+ * encoding is invalid. R and S with value 0 are allowed in the encoding.
+ *
+ * After the call, sig will always be initialized. If parsing failed or R or
+ * S are zero, the resulting sig value is guaranteed to fail validation for any
+ * message and public key.
+ */
+SECP256K1_API int secp256k1_ecdsa_signature_parse_compact(
+ const secp256k1_context* ctx,
+ secp256k1_ecdsa_signature* sig,
+ const unsigned char *input64
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
/** Parse a DER ECDSA signature.
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
@@ -279,7 +307,12 @@ SECP256K1_API int secp256k1_ec_pubkey_serialize(
* In: input: a pointer to the signature to be parsed
* inputlen: the length of the array pointed to be input
*
- * Note that this function also supports some violations of DER and even BER.
+ * This function will accept any valid DER encoded signature, even if the
+ * encoded numbers are out of range.
+ *
+ * After the call, sig will always be initialized. If parsing failed or the
+ * encoded numbers are out of range, signature validation with it is
+ * guaranteed to fail for every message and public key.
*/
SECP256K1_API int secp256k1_ecdsa_signature_parse_der(
const secp256k1_context* ctx,
@@ -306,6 +339,21 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_der(
const secp256k1_ecdsa_signature* sig
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+/** Serialize an ECDSA signature in compact (64 byte) format.
+ *
+ * Returns: 1
+ * Args: ctx: a secp256k1 context object
+ * Out: output64: a pointer to a 64-byte array to store the compact serialization
+ * In: sig: a pointer to an initialized signature object
+ *
+ * See secp256k1_ecdsa_signature_parse_compact for details about the encoding.
+ */
+SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact(
+ const secp256k1_context* ctx,
+ unsigned char *output64,
+ const secp256k1_ecdsa_signature* sig
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
/** Verify an ECDSA signature.
*
* Returns: 1: correct signature
@@ -314,6 +362,15 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_der(
* In: sig: the signature being verified (cannot be NULL)
* msg32: the 32-byte message hash being verified (cannot be NULL)
* pubkey: pointer to an initialized public key to verify with (cannot be NULL)
+ *
+ * To avoid accepting malleable signatures, only ECDSA signatures in lower-S
+ * form are accepted.
+ *
+ * If you need to accept ECDSA signatures from sources that do not obey this
+ * rule, apply secp256k1_ecdsa_signature_normalize to the signature prior to
+ * validation, but be aware that doing so results in malleable signatures.
+ *
+ * For details, see the comments for that function.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
const secp256k1_context* ctx,
@@ -322,14 +379,62 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
const secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+/** Convert a signature to a normalized lower-S form.
+ *
+ * Returns: 1 if sigin was not normalized, 0 if it already was.
+ * Args: ctx: a secp256k1 context object
+ * Out: sigout: a pointer to a signature to fill with the normalized form,
+ * or copy if the input was already normalized. (can be NULL if
+ * you're only interested in whether the input was already
+ * normalized).
+ * In: sigin: a pointer to a signature to check/normalize (cannot be NULL,
+ * can be identical to sigout)
+ *
+ * With ECDSA a third-party can forge a second distinct signature of the same
+ * message, given a single initial signature, but without knowing the key. This
+ * is done by negating the S value modulo the order of the curve, 'flipping'
+ * the sign of the random point R which is not included in the signature.
+ *
+ * Forgery of the same message isn't universally problematic, but in systems
+ * where message malleability or uniqueness of signatures is important this can
+ * cause issues. This forgery can be blocked by all verifiers forcing signers
+ * to use a normalized form.
+ *
+ * The lower-S form reduces the size of signatures slightly on average when
+ * variable length encodings (such as DER) are used and is cheap to verify,
+ * making it a good choice. Security of always using lower-S is assured because
+ * anyone can trivially modify a signature after the fact to enforce this
+ * property anyway.
+ *
+ * The lower S value is always between 0x1 and
+ * 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
+ * inclusive.
+ *
+ * No other forms of ECDSA malleability are known and none seem likely, but
+ * there is no formal proof that ECDSA, even with this additional restriction,
+ * is free of other malleability. Commonly used serialization schemes will also
+ * accept various non-unique encodings, so care should be taken when this
+ * property is required for an application.
+ *
+ * The secp256k1_ecdsa_sign function will by default create signatures in the
+ * lower-S form, and secp256k1_ecdsa_verify will not accept others. In case
+ * signatures come from a system that cannot enforce this property,
+ * secp256k1_ecdsa_signature_normalize must be called before verification.
+ */
+SECP256K1_API int secp256k1_ecdsa_signature_normalize(
+ const secp256k1_context* ctx,
+ secp256k1_ecdsa_signature *sigout,
+ const secp256k1_ecdsa_signature *sigin
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3);
+
/** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function.
* If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
* extra entropy.
*/
-extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979;
+SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979;
/** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */
-extern const secp256k1_nonce_function secp256k1_nonce_function_default;
+SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default;
/** Create an ECDSA signature.
*
@@ -342,32 +447,8 @@ extern const secp256k1_nonce_function secp256k1_nonce_function_default;
* noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
* ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
*
- * The sig always has an s value in the lower half of the range (From 0x1
- * to 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
- * inclusive), unlike many other implementations.
- *
- * With ECDSA a third-party can can forge a second distinct signature
- * of the same message given a single initial signature without knowing
- * the key by setting s to its additive inverse mod-order, 'flipping' the
- * sign of the random point R which is not included in the signature.
- * Since the forgery is of the same message this isn't universally
- * problematic, but in systems where message malleability or uniqueness
- * of signatures is important this can cause issues. This forgery can be
- * blocked by all verifiers forcing signers to use a canonical form. The
- * lower-S form reduces the size of signatures slightly on average when
- * variable length encodings (such as DER) are used and is cheap to
- * verify, making it a good choice. Security of always using lower-S is
- * assured because anyone can trivially modify a signature after the
- * fact to enforce this property. Adjusting it inside the signing
- * function avoids the need to re-serialize or have curve specific
- * constants outside of the library. By always using a canonical form
- * even in applications where it isn't needed it becomes possible to
- * impose a requirement later if a need is discovered.
- * No other forms of ECDSA malleability are known and none seem likely,
- * but there is no formal proof that ECDSA, even with this additional
- * restriction, is free of other malleability. Commonly used serialization
- * schemes will also accept various non-unique encodings, so care should
- * be taken when this property is required for an application.
+ * The created signature is always in lower-S form. See
+ * secp256k1_ecdsa_signature_normalize for more details.
*/
SECP256K1_API int secp256k1_ecdsa_sign(
const secp256k1_context* ctx,
@@ -404,55 +485,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
-/** Export a private key in BER format.
- *
- * Returns: 1 if the private key was valid.
- * Args: ctx: pointer to a context object, initialized for signing (cannot
- * be NULL)
- * Out: privkey: pointer to an array for storing the private key in BER.
- * Should have space for 279 bytes, and cannot be NULL.
- * privkeylen: Pointer to an int where the length of the private key in
- * privkey will be stored.
- * In: seckey: pointer to a 32-byte secret key to export.
- * flags: SECP256K1_EC_COMPRESSED if the key should be exported in
- * compressed format.
- *
- * This function is purely meant for compatibility with applications that
- * require BER encoded keys. When working with secp256k1-specific code, the
- * simple 32-byte private keys are sufficient.
- *
- * Note that this function does not guarantee correct DER output. It is
- * guaranteed to be parsable by secp256k1_ec_privkey_import.
- */
-SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_export(
- const secp256k1_context* ctx,
- unsigned char *privkey,
- size_t *privkeylen,
- const unsigned char *seckey,
- unsigned int flags
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
-
-/** Import a private key in DER format.
- * Returns: 1 if a private key was extracted.
- * Args: ctx: pointer to a context object (cannot be NULL).
- * Out: seckey: pointer to a 32-byte array for storing the private key.
- * (cannot be NULL).
- * In: privkey: pointer to a private key in DER format (cannot be NULL).
- * privkeylen: length of the DER private key pointed to be privkey.
- *
- * This function will accept more than just strict DER, and even allow some BER
- * violations. The public key stored inside the DER-encoded private key is not
- * verified for correctness, nor are the curve parameters. Use this function
- * only if you know in advance it is supposed to contain a secp256k1 private
- * key.
- */
-SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_import(
- const secp256k1_context* ctx,
- unsigned char *seckey,
- const unsigned char *privkey,
- size_t privkeylen
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
-
/** Tweak a private key by adding tweak to it.
* Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
* uniformly random 32-byte arrays, or if the resulting private key
@@ -526,18 +558,16 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
* Returns: 1: the sum of the public keys is valid.
* 0: the sum of the public keys is not valid.
* Args: ctx: pointer to a context object
- * Out: out: pointer to pubkey for placing the resulting public key
+ * Out: out: pointer to a public key object for placing the resulting public key
* (cannot be NULL)
* In: ins: pointer to array of pointers to public keys (cannot be NULL)
* n: the number of public keys to add together (must be at least 1)
- * Use secp256k1_ec_pubkey_compress and secp256k1_ec_pubkey_decompress if the
- * uncompressed format is needed.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine(
const secp256k1_context* ctx,
secp256k1_pubkey *out,
const secp256k1_pubkey * const * ins,
- int n
+ size_t n
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
# ifdef __cplusplus
diff --git a/crypto/secp256k1/libsecp256k1/include/secp256k1_ecdh.h b/crypto/secp256k1/libsecp256k1/include/secp256k1_ecdh.h
index db520f4467..4b84d7a963 100644
--- a/crypto/secp256k1/libsecp256k1/include/secp256k1_ecdh.h
+++ b/crypto/secp256k1/libsecp256k1/include/secp256k1_ecdh.h
@@ -10,17 +10,18 @@ extern "C" {
/** Compute an EC Diffie-Hellman secret in constant time
* Returns: 1: exponentiation was successful
* 0: scalar was invalid (zero or overflow)
- * Args: ctx: pointer to a context object (cannot be NULL)
- * Out: result: a 32-byte array which will be populated by an ECDH
- * secret computed from the point and scalar
- * In: point: pointer to a public point
- * scalar: a 32-byte scalar with which to multiply the point
+ * Args: ctx: pointer to a context object (cannot be NULL)
+ * Out: result: a 32-byte array which will be populated by an ECDH
+ * secret computed from the point and scalar
+ * In: pubkey: a pointer to a secp256k1_pubkey containing an
+ * initialized public key
+ * privkey: a 32-byte scalar with which to multiply the point
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh(
const secp256k1_context* ctx,
unsigned char *result,
- const secp256k1_pubkey *point,
- const unsigned char *scalar
+ const secp256k1_pubkey *pubkey,
+ const unsigned char *privkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
# ifdef __cplusplus
diff --git a/crypto/secp256k1/libsecp256k1/include/secp256k1_recovery.h b/crypto/secp256k1/libsecp256k1/include/secp256k1_recovery.h
index c9b8c0a306..0553797253 100644
--- a/crypto/secp256k1/libsecp256k1/include/secp256k1_recovery.h
+++ b/crypto/secp256k1/libsecp256k1/include/secp256k1_recovery.h
@@ -65,7 +65,7 @@ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact(
unsigned char *output64,
int *recid,
const secp256k1_ecdsa_recoverable_signature* sig
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Create a recoverable ECDSA signature.
*
@@ -92,7 +92,7 @@ SECP256K1_API int secp256k1_ecdsa_sign_recoverable(
* Returns: 1: public key successfully recovered (which guarantees a correct signature).
* 0: otherwise.
* Args: ctx: pointer to a context object, initialized for verification (cannot be NULL)
- * Out: pubkey: pointer to the recoved public key (cannot be NULL)
+ * Out: pubkey: pointer to the recovered public key (cannot be NULL)
* In: sig: pointer to initialized signature that supports pubkey recovery (cannot be NULL)
* msg32: the 32-byte message hash assumed to be signed (cannot be NULL)
*/
diff --git a/crypto/secp256k1/libsecp256k1/include/secp256k1_schnorr.h b/crypto/secp256k1/libsecp256k1/include/secp256k1_schnorr.h
deleted file mode 100644
index 49354933da..0000000000
--- a/crypto/secp256k1/libsecp256k1/include/secp256k1_schnorr.h
+++ /dev/null
@@ -1,173 +0,0 @@
-#ifndef _SECP256K1_SCHNORR_
-# define _SECP256K1_SCHNORR_
-
-# include "secp256k1.h"
-
-# ifdef __cplusplus
-extern "C" {
-# endif
-
-/** Create a signature using a custom EC-Schnorr-SHA256 construction. It
- * produces non-malleable 64-byte signatures which support public key recovery
- * batch validation, and multiparty signing.
- * Returns: 1: signature created
- * 0: the nonce generation function failed, or the private key was
- * invalid.
- * Args: ctx: pointer to a context object, initialized for signing
- * (cannot be NULL)
- * Out: sig64: pointer to a 64-byte array where the signature will be
- * placed (cannot be NULL)
- * In: msg32: the 32-byte message hash being signed (cannot be NULL)
- * seckey: pointer to a 32-byte secret key (cannot be NULL)
- * noncefp:pointer to a nonce generation function. If NULL,
- * secp256k1_nonce_function_default is used
- * ndata: pointer to arbitrary data used by the nonce generation
- * function (can be NULL)
- */
-SECP256K1_API int secp256k1_schnorr_sign(
- const secp256k1_context* ctx,
- unsigned char *sig64,
- const unsigned char *msg32,
- const unsigned char *seckey,
- secp256k1_nonce_function noncefp,
- const void *ndata
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
-
-/** Verify a signature created by secp256k1_schnorr_sign.
- * Returns: 1: correct signature
- * 0: incorrect signature
- * Args: ctx: a secp256k1 context object, initialized for verification.
- * In: sig64: the 64-byte signature being verified (cannot be NULL)
- * msg32: the 32-byte message hash being verified (cannot be NULL)
- * pubkey: the public key to verify with (cannot be NULL)
- */
-SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_verify(
- const secp256k1_context* ctx,
- const unsigned char *sig64,
- const unsigned char *msg32,
- const secp256k1_pubkey *pubkey
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
-
-/** Recover an EC public key from a Schnorr signature created using
- * secp256k1_schnorr_sign.
- * Returns: 1: public key successfully recovered (which guarantees a correct
- * signature).
- * 0: otherwise.
- * Args: ctx: pointer to a context object, initialized for
- * verification (cannot be NULL)
- * Out: pubkey: pointer to a pubkey to set to the recovered public key
- * (cannot be NULL).
- * In: sig64: signature as 64 byte array (cannot be NULL)
- * msg32: the 32-byte message hash assumed to be signed (cannot
- * be NULL)
- */
-SECP256K1_API int secp256k1_schnorr_recover(
- const secp256k1_context* ctx,
- secp256k1_pubkey *pubkey,
- const unsigned char *sig64,
- const unsigned char *msg32
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
-
-/** Generate a nonce pair deterministically for use with
- * secp256k1_schnorr_partial_sign.
- * Returns: 1: valid nonce pair was generated.
- * 0: otherwise (nonce generation function failed)
- * Args: ctx: pointer to a context object, initialized for signing
- * (cannot be NULL)
- * Out: pubnonce: public side of the nonce (cannot be NULL)
- * privnonce32: private side of the nonce (32 byte) (cannot be NULL)
- * In: msg32: the 32-byte message hash assumed to be signed (cannot
- * be NULL)
- * sec32: the 32-byte private key (cannot be NULL)
- * noncefp: pointer to a nonce generation function. If NULL,
- * secp256k1_nonce_function_default is used
- * noncedata: pointer to arbitrary data used by the nonce generation
- * function (can be NULL)
- *
- * Do not use the output as a private/public key pair for signing/validation.
- */
-SECP256K1_API int secp256k1_schnorr_generate_nonce_pair(
- const secp256k1_context* ctx,
- secp256k1_pubkey *pubnonce,
- unsigned char *privnonce32,
- const unsigned char *msg32,
- const unsigned char *sec32,
- secp256k1_nonce_function noncefp,
- const void* noncedata
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
-
-/** Produce a partial Schnorr signature, which can be combined using
- * secp256k1_schnorr_partial_combine, to end up with a full signature that is
- * verifiable using secp256k1_schnorr_verify.
- * Returns: 1: signature created succesfully.
- * 0: no valid signature exists with this combination of keys, nonces
- * and message (chance around 1 in 2^128)
- * -1: invalid private key, nonce, or public nonces.
- * Args: ctx: pointer to context object, initialized for signing (cannot
- * be NULL)
- * Out: sig64: pointer to 64-byte array to put partial signature in
- * In: msg32: pointer to 32-byte message to sign
- * sec32: pointer to 32-byte private key
- * pubnonce_others: pointer to pubkey containing the sum of the other's
- * nonces (see secp256k1_ec_pubkey_combine)
- * secnonce32: pointer to 32-byte array containing our nonce
- *
- * The intended procedure for creating a multiparty signature is:
- * - Each signer S[i] with private key x[i] and public key Q[i] runs
- * secp256k1_schnorr_generate_nonce_pair to produce a pair (k[i],R[i]) of
- * private/public nonces.
- * - All signers communicate their public nonces to each other (revealing your
- * private nonce can lead to discovery of your private key, so it should be
- * considered secret).
- * - All signers combine all the public nonces they received (excluding their
- * own) using secp256k1_ec_pubkey_combine to obtain an
- * Rall[i] = sum(R[0..i-1,i+1..n]).
- * - All signers produce a partial signature using
- * secp256k1_schnorr_partial_sign, passing in their own private key x[i],
- * their own private nonce k[i], and the sum of the others' public nonces
- * Rall[i].
- * - All signers communicate their partial signatures to each other.
- * - Someone combines all partial signatures using
- * secp256k1_schnorr_partial_combine, to obtain a full signature.
- * - The resulting signature is validatable using secp256k1_schnorr_verify, with
- * public key equal to the result of secp256k1_ec_pubkey_combine of the
- * signers' public keys (sum(Q[0..n])).
- *
- * Note that secp256k1_schnorr_partial_combine and secp256k1_ec_pubkey_combine
- * function take their arguments in any order, and it is possible to
- * pre-combine several inputs already with one call, and add more inputs later
- * by calling the function again (they are commutative and associative).
- */
-SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_sign(
- const secp256k1_context* ctx,
- unsigned char *sig64,
- const unsigned char *msg32,
- const unsigned char *sec32,
- const secp256k1_pubkey *pubnonce_others,
- const unsigned char *secnonce32
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
-
-/** Combine multiple Schnorr partial signatures.
- * Returns: 1: the passed signatures were succesfully combined.
- * 0: the resulting signature is not valid (chance of 1 in 2^256)
- * -1: some inputs were invalid, or the signatures were not created
- * using the same set of nonces
- * Args: ctx: pointer to a context object
- * Out: sig64: pointer to a 64-byte array to place the combined signature
- * (cannot be NULL)
- * In: sig64sin: pointer to an array of n pointers to 64-byte input
- * signatures
- * n: the number of signatures to combine (at least 1)
- */
-SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_combine(
- const secp256k1_context* ctx,
- unsigned char *sig64,
- const unsigned char * const * sig64sin,
- int n
-) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
-
-# ifdef __cplusplus
-}
-# endif
-
-#endif
diff --git a/crypto/secp256k1/libsecp256k1/libsecp256k1.pc.in b/crypto/secp256k1/libsecp256k1/libsecp256k1.pc.in
index 1c72dd0003..a0d006f113 100644
--- a/crypto/secp256k1/libsecp256k1/libsecp256k1.pc.in
+++ b/crypto/secp256k1/libsecp256k1/libsecp256k1.pc.in
@@ -5,7 +5,7 @@ includedir=@includedir@
Name: libsecp256k1
Description: Optimized C library for EC operations on curve secp256k1
-URL: https://github.com/bitcoin/secp256k1
+URL: https://github.com/bitcoin-core/secp256k1
Version: @PACKAGE_VERSION@
Cflags: -I${includedir}
Libs.private: @SECP_LIBS@
diff --git a/crypto/secp256k1/libsecp256k1/sage/group_prover.sage b/crypto/secp256k1/libsecp256k1/sage/group_prover.sage
new file mode 100644
index 0000000000..ab580c5b23
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/sage/group_prover.sage
@@ -0,0 +1,322 @@
+# This code supports verifying group implementations which have branches
+# or conditional statements (like cmovs), by allowing each execution path
+# to independently set assumptions on input or intermediary variables.
+#
+# The general approach is:
+# * A constraint is a tuple of two sets of of symbolic expressions:
+# the first of which are required to evaluate to zero, the second of which
+# are required to evaluate to nonzero.
+# - A constraint is said to be conflicting if any of its nonzero expressions
+# is in the ideal with basis the zero expressions (in other words: when the
+# zero expressions imply that one of the nonzero expressions are zero).
+# * There is a list of laws that describe the intended behaviour, including
+# laws for addition and doubling. Each law is called with the symbolic point
+# coordinates as arguments, and returns:
+# - A constraint describing the assumptions under which it is applicable,
+# called "assumeLaw"
+# - A constraint describing the requirements of the law, called "require"
+# * Implementations are transliterated into functions that operate as well on
+# algebraic input points, and are called once per combination of branches
+# exectured. Each execution returns:
+# - A constraint describing the assumptions this implementation requires
+# (such as Z1=1), called "assumeFormula"
+# - A constraint describing the assumptions this specific branch requires,
+# but which is by construction guaranteed to cover the entire space by
+# merging the results from all branches, called "assumeBranch"
+# - The result of the computation
+# * All combinations of laws with implementation branches are tried, and:
+# - If the combination of assumeLaw, assumeFormula, and assumeBranch results
+# in a conflict, it means this law does not apply to this branch, and it is
+# skipped.
+# - For others, we try to prove the require constraints hold, assuming the
+# information in assumeLaw + assumeFormula + assumeBranch, and if this does
+# not succeed, we fail.
+# + To prove an expression is zero, we check whether it belongs to the
+# ideal with the assumed zero expressions as basis. This test is exact.
+# + To prove an expression is nonzero, we check whether each of its
+# factors is contained in the set of nonzero assumptions' factors.
+# This test is not exact, so various combinations of original and
+# reduced expressions' factors are tried.
+# - If we succeed, we print out the assumptions from assumeFormula that
+# weren't implied by assumeLaw already. Those from assumeBranch are skipped,
+# as we assume that all constraints in it are complementary with each other.
+#
+# Based on the sage verification scripts used in the Explicit-Formulas Database
+# by Tanja Lange and others, see http://hyperelliptic.org/EFD
+
+class fastfrac:
+ """Fractions over rings."""
+
+ def __init__(self,R,top,bot=1):
+ """Construct a fractional, given a ring, a numerator, and denominator."""
+ self.R = R
+ if parent(top) == ZZ or parent(top) == R:
+ self.top = R(top)
+ self.bot = R(bot)
+ elif top.__class__ == fastfrac:
+ self.top = top.top
+ self.bot = top.bot * bot
+ else:
+ self.top = R(numerator(top))
+ self.bot = R(denominator(top)) * bot
+
+ def iszero(self,I):
+ """Return whether this fraction is zero given an ideal."""
+ return self.top in I and self.bot not in I
+
+ def reduce(self,assumeZero):
+ zero = self.R.ideal(map(numerator, assumeZero))
+ return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot))
+
+ def __add__(self,other):
+ """Add two fractions."""
+ if parent(other) == ZZ:
+ return fastfrac(self.R,self.top + self.bot * other,self.bot)
+ if other.__class__ == fastfrac:
+ return fastfrac(self.R,self.top * other.bot + self.bot * other.top,self.bot * other.bot)
+ return NotImplemented
+
+ def __sub__(self,other):
+ """Subtract two fractions."""
+ if parent(other) == ZZ:
+ return fastfrac(self.R,self.top - self.bot * other,self.bot)
+ if other.__class__ == fastfrac:
+ return fastfrac(self.R,self.top * other.bot - self.bot * other.top,self.bot * other.bot)
+ return NotImplemented
+
+ def __neg__(self):
+ """Return the negation of a fraction."""
+ return fastfrac(self.R,-self.top,self.bot)
+
+ def __mul__(self,other):
+ """Multiply two fractions."""
+ if parent(other) == ZZ:
+ return fastfrac(self.R,self.top * other,self.bot)
+ if other.__class__ == fastfrac:
+ return fastfrac(self.R,self.top * other.top,self.bot * other.bot)
+ return NotImplemented
+
+ def __rmul__(self,other):
+ """Multiply something else with a fraction."""
+ return self.__mul__(other)
+
+ def __div__(self,other):
+ """Divide two fractions."""
+ if parent(other) == ZZ:
+ return fastfrac(self.R,self.top,self.bot * other)
+ if other.__class__ == fastfrac:
+ return fastfrac(self.R,self.top * other.bot,self.bot * other.top)
+ return NotImplemented
+
+ def __pow__(self,other):
+ """Compute a power of a fraction."""
+ if parent(other) == ZZ:
+ if other < 0:
+ # Negative powers require flipping top and bottom
+ return fastfrac(self.R,self.bot ^ (-other),self.top ^ (-other))
+ else:
+ return fastfrac(self.R,self.top ^ other,self.bot ^ other)
+ return NotImplemented
+
+ def __str__(self):
+ return "fastfrac((" + str(self.top) + ") / (" + str(self.bot) + "))"
+ def __repr__(self):
+ return "%s" % self
+
+ def numerator(self):
+ return self.top
+
+class constraints:
+ """A set of constraints, consisting of zero and nonzero expressions.
+
+ Constraints can either be used to express knowledge or a requirement.
+
+ Both the fields zero and nonzero are maps from expressions to description
+ strings. The expressions that are the keys in zero are required to be zero,
+ and the expressions that are the keys in nonzero are required to be nonzero.
+
+ Note that (a != 0) and (b != 0) is the same as (a*b != 0), so all keys in
+ nonzero could be multiplied into a single key. This is often much less
+ efficient to work with though, so we keep them separate inside the
+ constraints. This allows higher-level code to do fast checks on the individual
+ nonzero elements, or combine them if needed for stronger checks.
+
+ We can't multiply the different zero elements, as it would suffice for one of
+ the factors to be zero, instead of all of them. Instead, the zero elements are
+ typically combined into an ideal first.
+ """
+
+ def __init__(self, **kwargs):
+ if 'zero' in kwargs:
+ self.zero = dict(kwargs['zero'])
+ else:
+ self.zero = dict()
+ if 'nonzero' in kwargs:
+ self.nonzero = dict(kwargs['nonzero'])
+ else:
+ self.nonzero = dict()
+
+ def negate(self):
+ return constraints(zero=self.nonzero, nonzero=self.zero)
+
+ def __add__(self, other):
+ zero = self.zero.copy()
+ zero.update(other.zero)
+ nonzero = self.nonzero.copy()
+ nonzero.update(other.nonzero)
+ return constraints(zero=zero, nonzero=nonzero)
+
+ def __str__(self):
+ return "constraints(zero=%s,nonzero=%s)" % (self.zero, self.nonzero)
+
+ def __repr__(self):
+ return "%s" % self
+
+
+def conflicts(R, con):
+ """Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""
+ zero = R.ideal(map(numerator, con.zero))
+ if 1 in zero:
+ return True
+ # First a cheap check whether any of the individual nonzero terms conflict on
+ # their own.
+ for nonzero in con.nonzero:
+ if nonzero.iszero(zero):
+ return True
+ # It can be the case that entries in the nonzero set do not individually
+ # conflict with the zero set, but their combination does. For example, knowing
+ # that either x or y is zero is equivalent to having x*y in the zero set.
+ # Having x or y individually in the nonzero set is not a conflict, but both
+ # simultaneously is, so that is the right thing to check for.
+ if reduce(lambda a,b: a * b, con.nonzero, fastfrac(R, 1)).iszero(zero):
+ return True
+ return False
+
+
+def get_nonzero_set(R, assume):
+ """Calculate a simple set of nonzero expressions"""
+ zero = R.ideal(map(numerator, assume.zero))
+ nonzero = set()
+ for nz in map(numerator, assume.nonzero):
+ for (f,n) in nz.factor():
+ nonzero.add(f)
+ rnz = zero.reduce(nz)
+ for (f,n) in rnz.factor():
+ nonzero.add(f)
+ return nonzero
+
+
+def prove_nonzero(R, exprs, assume):
+ """Check whether an expression is provably nonzero, given assumptions"""
+ zero = R.ideal(map(numerator, assume.zero))
+ nonzero = get_nonzero_set(R, assume)
+ expl = set()
+ ok = True
+ for expr in exprs:
+ if numerator(expr) in zero:
+ return (False, [exprs[expr]])
+ allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1)
+ for (f, n) in allexprs.factor():
+ if f not in nonzero:
+ ok = False
+ if ok:
+ return (True, None)
+ ok = True
+ for (f, n) in zero.reduce(numerator(allexprs)).factor():
+ if f not in nonzero:
+ ok = False
+ if ok:
+ return (True, None)
+ ok = True
+ for expr in exprs:
+ for (f,n) in numerator(expr).factor():
+ if f not in nonzero:
+ ok = False
+ if ok:
+ return (True, None)
+ ok = True
+ for expr in exprs:
+ for (f,n) in zero.reduce(numerator(expr)).factor():
+ if f not in nonzero:
+ expl.add(exprs[expr])
+ if expl:
+ return (False, list(expl))
+ else:
+ return (True, None)
+
+
+def prove_zero(R, exprs, assume):
+ """Check whether all of the passed expressions are provably zero, given assumptions"""
+ r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume)
+ if not r:
+ return (False, map(lambda x: "Possibly zero denominator: %s" % x, e))
+ zero = R.ideal(map(numerator, assume.zero))
+ nonzero = prod(x for x in assume.nonzero)
+ expl = []
+ for expr in exprs:
+ if not expr.iszero(zero):
+ expl.append(exprs[expr])
+ if not expl:
+ return (True, None)
+ return (False, expl)
+
+
+def describe_extra(R, assume, assumeExtra):
+ """Describe what assumptions are added, given existing assumptions"""
+ zerox = assume.zero.copy()
+ zerox.update(assumeExtra.zero)
+ zero = R.ideal(map(numerator, assume.zero))
+ zeroextra = R.ideal(map(numerator, zerox))
+ nonzero = get_nonzero_set(R, assume)
+ ret = set()
+ # Iterate over the extra zero expressions
+ for base in assumeExtra.zero:
+ if base not in zero:
+ add = []
+ for (f, n) in numerator(base).factor():
+ if f not in nonzero:
+ add += ["%s" % f]
+ if add:
+ ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base])
+ # Iterate over the extra nonzero expressions
+ for nz in assumeExtra.nonzero:
+ nzr = zeroextra.reduce(numerator(nz))
+ if nzr not in zeroextra:
+ for (f,n) in nzr.factor():
+ if zeroextra.reduce(f) not in nonzero:
+ ret.add("%s != 0" % zeroextra.reduce(f))
+ return ", ".join(x for x in ret)
+
+
+def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require):
+ """Check a set of zero and nonzero requirements, given a set of zero and nonzero assumptions"""
+ assume = assumeLaw + assumeAssert + assumeBranch
+
+ if conflicts(R, assume):
+ # This formula does not apply
+ return None
+
+ describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert)
+
+ ok, msg = prove_zero(R, require.zero, assume)
+ if not ok:
+ return "FAIL, %s fails (assuming %s)" % (str(msg), describe)
+
+ res, expl = prove_nonzero(R, require.nonzero, assume)
+ if not res:
+ return "FAIL, %s fails (assuming %s)" % (str(expl), describe)
+
+ if describe != "":
+ return "OK (assuming %s)" % describe
+ else:
+ return "OK"
+
+
+def concrete_verify(c):
+ for k in c.zero:
+ if k != 0:
+ return (False, c.zero[k])
+ for k in c.nonzero:
+ if k == 0:
+ return (False, c.nonzero[k])
+ return (True, None)
diff --git a/crypto/secp256k1/libsecp256k1/sage/secp256k1.sage b/crypto/secp256k1/libsecp256k1/sage/secp256k1.sage
new file mode 100644
index 0000000000..a97e732f7f
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/sage/secp256k1.sage
@@ -0,0 +1,306 @@
+# Test libsecp256k1' group operation implementations using prover.sage
+
+import sys
+
+load("group_prover.sage")
+load("weierstrass_prover.sage")
+
+def formula_secp256k1_gej_double_var(a):
+ """libsecp256k1's secp256k1_gej_double_var, used by various addition functions"""
+ rz = a.Z * a.Y
+ rz = rz * 2
+ t1 = a.X^2
+ t1 = t1 * 3
+ t2 = t1^2
+ t3 = a.Y^2
+ t3 = t3 * 2
+ t4 = t3^2
+ t4 = t4 * 2
+ t3 = t3 * a.X
+ rx = t3
+ rx = rx * 4
+ rx = -rx
+ rx = rx + t2
+ t2 = -t2
+ t3 = t3 * 6
+ t3 = t3 + t2
+ ry = t1 * t3
+ t2 = -t4
+ ry = ry + t2
+ return jacobianpoint(rx, ry, rz)
+
+def formula_secp256k1_gej_add_var(branch, a, b):
+ """libsecp256k1's secp256k1_gej_add_var"""
+ if branch == 0:
+ return (constraints(), constraints(nonzero={a.Infinity : 'a_infinite'}), b)
+ if branch == 1:
+ return (constraints(), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a)
+ z22 = b.Z^2
+ z12 = a.Z^2
+ u1 = a.X * z22
+ u2 = b.X * z12
+ s1 = a.Y * z22
+ s1 = s1 * b.Z
+ s2 = b.Y * z12
+ s2 = s2 * a.Z
+ h = -u1
+ h = h + u2
+ i = -s1
+ i = i + s2
+ if branch == 2:
+ r = formula_secp256k1_gej_double_var(a)
+ return (constraints(), constraints(zero={h : 'h=0', i : 'i=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}), r)
+ if branch == 3:
+ return (constraints(), constraints(zero={h : 'h=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={i : 'i!=0'}), point_at_infinity())
+ i2 = i^2
+ h2 = h^2
+ h3 = h2 * h
+ h = h * b.Z
+ rz = a.Z * h
+ t = u1 * h2
+ rx = t
+ rx = rx * 2
+ rx = rx + h3
+ rx = -rx
+ rx = rx + i2
+ ry = -rx
+ ry = ry + t
+ ry = ry * i
+ h3 = h3 * s1
+ h3 = -h3
+ ry = ry + h3
+ return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz))
+
+def formula_secp256k1_gej_add_ge_var(branch, a, b):
+ """libsecp256k1's secp256k1_gej_add_ge_var, which assume bz==1"""
+ if branch == 0:
+ return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(nonzero={a.Infinity : 'a_infinite'}), b)
+ if branch == 1:
+ return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a)
+ z12 = a.Z^2
+ u1 = a.X
+ u2 = b.X * z12
+ s1 = a.Y
+ s2 = b.Y * z12
+ s2 = s2 * a.Z
+ h = -u1
+ h = h + u2
+ i = -s1
+ i = i + s2
+ if (branch == 2):
+ r = formula_secp256k1_gej_double_var(a)
+ return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r)
+ if (branch == 3):
+ return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity())
+ i2 = i^2
+ h2 = h^2
+ h3 = h * h2
+ rz = a.Z * h
+ t = u1 * h2
+ rx = t
+ rx = rx * 2
+ rx = rx + h3
+ rx = -rx
+ rx = rx + i2
+ ry = -rx
+ ry = ry + t
+ ry = ry * i
+ h3 = h3 * s1
+ h3 = -h3
+ ry = ry + h3
+ return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz))
+
+def formula_secp256k1_gej_add_zinv_var(branch, a, b):
+ """libsecp256k1's secp256k1_gej_add_zinv_var"""
+ bzinv = b.Z^(-1)
+ if branch == 0:
+ return (constraints(), constraints(nonzero={b.Infinity : 'b_infinite'}), a)
+ if branch == 1:
+ bzinv2 = bzinv^2
+ bzinv3 = bzinv2 * bzinv
+ rx = b.X * bzinv2
+ ry = b.Y * bzinv3
+ rz = 1
+ return (constraints(), constraints(zero={b.Infinity : 'b_finite'}, nonzero={a.Infinity : 'a_infinite'}), jacobianpoint(rx, ry, rz))
+ azz = a.Z * bzinv
+ z12 = azz^2
+ u1 = a.X
+ u2 = b.X * z12
+ s1 = a.Y
+ s2 = b.Y * z12
+ s2 = s2 * azz
+ h = -u1
+ h = h + u2
+ i = -s1
+ i = i + s2
+ if branch == 2:
+ r = formula_secp256k1_gej_double_var(a)
+ return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r)
+ if branch == 3:
+ return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity())
+ i2 = i^2
+ h2 = h^2
+ h3 = h * h2
+ rz = a.Z
+ rz = rz * h
+ t = u1 * h2
+ rx = t
+ rx = rx * 2
+ rx = rx + h3
+ rx = -rx
+ rx = rx + i2
+ ry = -rx
+ ry = ry + t
+ ry = ry * i
+ h3 = h3 * s1
+ h3 = -h3
+ ry = ry + h3
+ return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz))
+
+def formula_secp256k1_gej_add_ge(branch, a, b):
+ """libsecp256k1's secp256k1_gej_add_ge"""
+ zeroes = {}
+ nonzeroes = {}
+ a_infinity = False
+ if (branch & 4) != 0:
+ nonzeroes.update({a.Infinity : 'a_infinite'})
+ a_infinity = True
+ else:
+ zeroes.update({a.Infinity : 'a_finite'})
+ zz = a.Z^2
+ u1 = a.X
+ u2 = b.X * zz
+ s1 = a.Y
+ s2 = b.Y * zz
+ s2 = s2 * a.Z
+ t = u1
+ t = t + u2
+ m = s1
+ m = m + s2
+ rr = t^2
+ m_alt = -u2
+ tt = u1 * m_alt
+ rr = rr + tt
+ degenerate = (branch & 3) == 3
+ if (branch & 1) != 0:
+ zeroes.update({m : 'm_zero'})
+ else:
+ nonzeroes.update({m : 'm_nonzero'})
+ if (branch & 2) != 0:
+ zeroes.update({rr : 'rr_zero'})
+ else:
+ nonzeroes.update({rr : 'rr_nonzero'})
+ rr_alt = s1
+ rr_alt = rr_alt * 2
+ m_alt = m_alt + u1
+ if not degenerate:
+ rr_alt = rr
+ m_alt = m
+ n = m_alt^2
+ q = n * t
+ n = n^2
+ if degenerate:
+ n = m
+ t = rr_alt^2
+ rz = a.Z * m_alt
+ infinity = False
+ if (branch & 8) != 0:
+ if not a_infinity:
+ infinity = True
+ zeroes.update({rz : 'r.z=0'})
+ else:
+ nonzeroes.update({rz : 'r.z!=0'})
+ rz = rz * 2
+ q = -q
+ t = t + q
+ rx = t
+ t = t * 2
+ t = t + q
+ t = t * rr_alt
+ t = t + n
+ ry = -t
+ rx = rx * 4
+ ry = ry * 4
+ if a_infinity:
+ rx = b.X
+ ry = b.Y
+ rz = 1
+ if infinity:
+ return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), point_at_infinity())
+ return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), jacobianpoint(rx, ry, rz))
+
+def formula_secp256k1_gej_add_ge_old(branch, a, b):
+ """libsecp256k1's old secp256k1_gej_add_ge, which fails when ay+by=0 but ax!=bx"""
+ a_infinity = (branch & 1) != 0
+ zero = {}
+ nonzero = {}
+ if a_infinity:
+ nonzero.update({a.Infinity : 'a_infinite'})
+ else:
+ zero.update({a.Infinity : 'a_finite'})
+ zz = a.Z^2
+ u1 = a.X
+ u2 = b.X * zz
+ s1 = a.Y
+ s2 = b.Y * zz
+ s2 = s2 * a.Z
+ z = a.Z
+ t = u1
+ t = t + u2
+ m = s1
+ m = m + s2
+ n = m^2
+ q = n * t
+ n = n^2
+ rr = t^2
+ t = u1 * u2
+ t = -t
+ rr = rr + t
+ t = rr^2
+ rz = m * z
+ infinity = False
+ if (branch & 2) != 0:
+ if not a_infinity:
+ infinity = True
+ else:
+ return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(nonzero={z : 'conflict_a'}, zero={z : 'conflict_b'}), point_at_infinity())
+ zero.update({rz : 'r.z=0'})
+ else:
+ nonzero.update({rz : 'r.z!=0'})
+ rz = rz * (0 if a_infinity else 2)
+ rx = t
+ q = -q
+ rx = rx + q
+ q = q * 3
+ t = t * 2
+ t = t + q
+ t = t * rr
+ t = t + n
+ ry = -t
+ rx = rx * (0 if a_infinity else 4)
+ ry = ry * (0 if a_infinity else 4)
+ t = b.X
+ t = t * (1 if a_infinity else 0)
+ rx = rx + t
+ t = b.Y
+ t = t * (1 if a_infinity else 0)
+ ry = ry + t
+ t = (1 if a_infinity else 0)
+ rz = rz + t
+ if infinity:
+ return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), point_at_infinity())
+ return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz))
+
+if __name__ == "__main__":
+ check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var)
+ check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var)
+ check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var)
+ check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge)
+ check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old)
+
+ if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive":
+ check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43)
+ check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43)
+ check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43)
+ check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43)
+ check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43)
diff --git a/crypto/secp256k1/libsecp256k1/sage/weierstrass_prover.sage b/crypto/secp256k1/libsecp256k1/sage/weierstrass_prover.sage
new file mode 100644
index 0000000000..03ef2ec901
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/sage/weierstrass_prover.sage
@@ -0,0 +1,264 @@
+# Prover implementation for Weierstrass curves of the form
+# y^2 = x^3 + A * x + B, specifically with a = 0 and b = 7, with group laws
+# operating on affine and Jacobian coordinates, including the point at infinity
+# represented by a 4th variable in coordinates.
+
+load("group_prover.sage")
+
+
+class affinepoint:
+ def __init__(self, x, y, infinity=0):
+ self.x = x
+ self.y = y
+ self.infinity = infinity
+ def __str__(self):
+ return "affinepoint(x=%s,y=%s,inf=%s)" % (self.x, self.y, self.infinity)
+
+
+class jacobianpoint:
+ def __init__(self, x, y, z, infinity=0):
+ self.X = x
+ self.Y = y
+ self.Z = z
+ self.Infinity = infinity
+ def __str__(self):
+ return "jacobianpoint(X=%s,Y=%s,Z=%s,inf=%s)" % (self.X, self.Y, self.Z, self.Infinity)
+
+
+def point_at_infinity():
+ return jacobianpoint(1, 1, 1, 1)
+
+
+def negate(p):
+ if p.__class__ == affinepoint:
+ return affinepoint(p.x, -p.y)
+ if p.__class__ == jacobianpoint:
+ return jacobianpoint(p.X, -p.Y, p.Z)
+ assert(False)
+
+
+def on_weierstrass_curve(A, B, p):
+ """Return a set of zero-expressions for an affine point to be on the curve"""
+ return constraints(zero={p.x^3 + A*p.x + B - p.y^2: 'on_curve'})
+
+
+def tangential_to_weierstrass_curve(A, B, p12, p3):
+ """Return a set of zero-expressions for ((x12,y12),(x3,y3)) to be a line that is tangential to the curve at (x12,y12)"""
+ return constraints(zero={
+ (p12.y - p3.y) * (p12.y * 2) - (p12.x^2 * 3 + A) * (p12.x - p3.x): 'tangential_to_curve'
+ })
+
+
+def colinear(p1, p2, p3):
+ """Return a set of zero-expressions for ((x1,y1),(x2,y2),(x3,y3)) to be collinear"""
+ return constraints(zero={
+ (p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x): 'colinear_1',
+ (p2.y - p3.y) * (p2.x - p1.x) - (p2.y - p1.y) * (p2.x - p3.x): 'colinear_2',
+ (p3.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p3.x - p1.x): 'colinear_3'
+ })
+
+
+def good_affine_point(p):
+ return constraints(nonzero={p.x : 'nonzero_x', p.y : 'nonzero_y'})
+
+
+def good_jacobian_point(p):
+ return constraints(nonzero={p.X : 'nonzero_X', p.Y : 'nonzero_Y', p.Z^6 : 'nonzero_Z'})
+
+
+def good_point(p):
+ return constraints(nonzero={p.Z^6 : 'nonzero_X'})
+
+
+def finite(p, *affine_fns):
+ con = good_point(p) + constraints(zero={p.Infinity : 'finite_point'})
+ if p.Z != 0:
+ return con + reduce(lambda a, b: a + b, (f(affinepoint(p.X / p.Z^2, p.Y / p.Z^3)) for f in affine_fns), con)
+ else:
+ return con
+
+def infinite(p):
+ return constraints(nonzero={p.Infinity : 'infinite_point'})
+
+
+def law_jacobian_weierstrass_add(A, B, pa, pb, pA, pB, pC):
+ """Check whether the passed set of coordinates is a valid Jacobian add, given assumptions"""
+ assumeLaw = (good_affine_point(pa) +
+ good_affine_point(pb) +
+ good_jacobian_point(pA) +
+ good_jacobian_point(pB) +
+ on_weierstrass_curve(A, B, pa) +
+ on_weierstrass_curve(A, B, pb) +
+ finite(pA) +
+ finite(pB) +
+ constraints(nonzero={pa.x - pb.x : 'different_x'}))
+ require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) +
+ colinear(pa, pb, negate(pc))))
+ return (assumeLaw, require)
+
+
+def law_jacobian_weierstrass_double(A, B, pa, pb, pA, pB, pC):
+ """Check whether the passed set of coordinates is a valid Jacobian doubling, given assumptions"""
+ assumeLaw = (good_affine_point(pa) +
+ good_affine_point(pb) +
+ good_jacobian_point(pA) +
+ good_jacobian_point(pB) +
+ on_weierstrass_curve(A, B, pa) +
+ on_weierstrass_curve(A, B, pb) +
+ finite(pA) +
+ finite(pB) +
+ constraints(zero={pa.x - pb.x : 'equal_x', pa.y - pb.y : 'equal_y'}))
+ require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) +
+ tangential_to_weierstrass_curve(A, B, pa, negate(pc))))
+ return (assumeLaw, require)
+
+
+def law_jacobian_weierstrass_add_opposites(A, B, pa, pb, pA, pB, pC):
+ assumeLaw = (good_affine_point(pa) +
+ good_affine_point(pb) +
+ good_jacobian_point(pA) +
+ good_jacobian_point(pB) +
+ on_weierstrass_curve(A, B, pa) +
+ on_weierstrass_curve(A, B, pb) +
+ finite(pA) +
+ finite(pB) +
+ constraints(zero={pa.x - pb.x : 'equal_x', pa.y + pb.y : 'opposite_y'}))
+ require = infinite(pC)
+ return (assumeLaw, require)
+
+
+def law_jacobian_weierstrass_add_infinite_a(A, B, pa, pb, pA, pB, pC):
+ assumeLaw = (good_affine_point(pa) +
+ good_affine_point(pb) +
+ good_jacobian_point(pA) +
+ good_jacobian_point(pB) +
+ on_weierstrass_curve(A, B, pb) +
+ infinite(pA) +
+ finite(pB))
+ require = finite(pC, lambda pc: constraints(zero={pc.x - pb.x : 'c.x=b.x', pc.y - pb.y : 'c.y=b.y'}))
+ return (assumeLaw, require)
+
+
+def law_jacobian_weierstrass_add_infinite_b(A, B, pa, pb, pA, pB, pC):
+ assumeLaw = (good_affine_point(pa) +
+ good_affine_point(pb) +
+ good_jacobian_point(pA) +
+ good_jacobian_point(pB) +
+ on_weierstrass_curve(A, B, pa) +
+ infinite(pB) +
+ finite(pA))
+ require = finite(pC, lambda pc: constraints(zero={pc.x - pa.x : 'c.x=a.x', pc.y - pa.y : 'c.y=a.y'}))
+ return (assumeLaw, require)
+
+
+def law_jacobian_weierstrass_add_infinite_ab(A, B, pa, pb, pA, pB, pC):
+ assumeLaw = (good_affine_point(pa) +
+ good_affine_point(pb) +
+ good_jacobian_point(pA) +
+ good_jacobian_point(pB) +
+ infinite(pA) +
+ infinite(pB))
+ require = infinite(pC)
+ return (assumeLaw, require)
+
+
+laws_jacobian_weierstrass = {
+ 'add': law_jacobian_weierstrass_add,
+ 'double': law_jacobian_weierstrass_double,
+ 'add_opposite': law_jacobian_weierstrass_add_opposites,
+ 'add_infinite_a': law_jacobian_weierstrass_add_infinite_a,
+ 'add_infinite_b': law_jacobian_weierstrass_add_infinite_b,
+ 'add_infinite_ab': law_jacobian_weierstrass_add_infinite_ab
+}
+
+
+def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
+ """Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field"""
+ F = Integers(p)
+ print "Formula %s on Z%i:" % (name, p)
+ points = []
+ for x in xrange(0, p):
+ for y in xrange(0, p):
+ point = affinepoint(F(x), F(y))
+ r, e = concrete_verify(on_weierstrass_curve(A, B, point))
+ if r:
+ points.append(point)
+
+ for za in xrange(1, p):
+ for zb in xrange(1, p):
+ for pa in points:
+ for pb in points:
+ for ia in xrange(2):
+ for ib in xrange(2):
+ pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia)
+ pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib)
+ for branch in xrange(0, branches):
+ assumeAssert, assumeBranch, pC = formula(branch, pA, pB)
+ pC.X = F(pC.X)
+ pC.Y = F(pC.Y)
+ pC.Z = F(pC.Z)
+ pC.Infinity = F(pC.Infinity)
+ r, e = concrete_verify(assumeAssert + assumeBranch)
+ if r:
+ match = False
+ for key in laws_jacobian_weierstrass:
+ assumeLaw, require = laws_jacobian_weierstrass[key](A, B, pa, pb, pA, pB, pC)
+ r, e = concrete_verify(assumeLaw)
+ if r:
+ if match:
+ print " multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity)
+ else:
+ match = True
+ r, e = concrete_verify(require)
+ if not r:
+ print " failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e)
+ print
+
+
+def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC):
+ assumeLaw, require = f(A, B, pa, pb, pA, pB, pC)
+ return check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require)
+
+def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
+ """Verify an implementation of addition of Jacobian points on a Weierstrass curve symbolically"""
+ R. = PolynomialRing(QQ,8,order='invlex')
+ lift = lambda x: fastfrac(R,x)
+ ax = lift(ax)
+ ay = lift(ay)
+ Az = lift(Az)
+ bx = lift(bx)
+ by = lift(by)
+ Bz = lift(Bz)
+ Ai = lift(Ai)
+ Bi = lift(Bi)
+
+ pa = affinepoint(ax, ay, Ai)
+ pb = affinepoint(bx, by, Bi)
+ pA = jacobianpoint(ax * Az^2, ay * Az^3, Az, Ai)
+ pB = jacobianpoint(bx * Bz^2, by * Bz^3, Bz, Bi)
+
+ res = {}
+
+ for key in laws_jacobian_weierstrass:
+ res[key] = []
+
+ print ("Formula " + name + ":")
+ count = 0
+ for branch in xrange(branches):
+ assumeFormula, assumeBranch, pC = formula(branch, pA, pB)
+ pC.X = lift(pC.X)
+ pC.Y = lift(pC.Y)
+ pC.Z = lift(pC.Z)
+ pC.Infinity = lift(pC.Infinity)
+
+ for key in laws_jacobian_weierstrass:
+ res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch))
+
+ for key in res:
+ print " %s:" % key
+ val = res[key]
+ for x in val:
+ if x[0] is not None:
+ print " branch %i: %s" % (x[1], x[0])
+
+ print
diff --git a/crypto/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s b/crypto/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s
new file mode 100644
index 0000000000..5df561f2fc
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s
@@ -0,0 +1,919 @@
+@ vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab syntax=armasm:
+/**********************************************************************
+ * Copyright (c) 2014 Wladimir J. van der Laan *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+/*
+ARM implementation of field_10x26 inner loops.
+
+Note:
+
+- To avoid unnecessary loads and make use of available registers, two
+ 'passes' have every time been interleaved, with the odd passes accumulating c' and d'
+ which will be added to c and d respectively in the the even passes
+
+*/
+
+ .syntax unified
+ .arch armv7-a
+ @ eabi attributes - see readelf -A
+ .eabi_attribute 8, 1 @ Tag_ARM_ISA_use = yes
+ .eabi_attribute 9, 0 @ Tag_Thumb_ISA_use = no
+ .eabi_attribute 10, 0 @ Tag_FP_arch = none
+ .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte
+ .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP
+ .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed
+ .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6
+ .text
+
+ @ Field constants
+ .set field_R0, 0x3d10
+ .set field_R1, 0x400
+ .set field_not_M, 0xfc000000 @ ~M = ~0x3ffffff
+
+ .align 2
+ .global secp256k1_fe_mul_inner
+ .type secp256k1_fe_mul_inner, %function
+ @ Arguments:
+ @ r0 r Restrict: can overlap with a, not with b
+ @ r1 a
+ @ r2 b
+ @ Stack (total 4+10*4 = 44)
+ @ sp + #0 saved 'r' pointer
+ @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9
+secp256k1_fe_mul_inner:
+ stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14}
+ sub sp, sp, #48 @ frame=44 + alignment
+ str r0, [sp, #0] @ save result address, we need it only at the end
+
+ /******************************************
+ * Main computation code.
+ ******************************************
+
+ Allocation:
+ r0,r14,r7,r8 scratch
+ r1 a (pointer)
+ r2 b (pointer)
+ r3:r4 c
+ r5:r6 d
+ r11:r12 c'
+ r9:r10 d'
+
+ Note: do not write to r[] here, it may overlap with a[]
+ */
+
+ /* A - interleaved with B */
+ ldr r7, [r1, #0*4] @ a[0]
+ ldr r8, [r2, #9*4] @ b[9]
+ ldr r0, [r1, #1*4] @ a[1]
+ umull r5, r6, r7, r8 @ d = a[0] * b[9]
+ ldr r14, [r2, #8*4] @ b[8]
+ umull r9, r10, r0, r8 @ d' = a[1] * b[9]
+ ldr r7, [r1, #2*4] @ a[2]
+ umlal r5, r6, r0, r14 @ d += a[1] * b[8]
+ ldr r8, [r2, #7*4] @ b[7]
+ umlal r9, r10, r7, r14 @ d' += a[2] * b[8]
+ ldr r0, [r1, #3*4] @ a[3]
+ umlal r5, r6, r7, r8 @ d += a[2] * b[7]
+ ldr r14, [r2, #6*4] @ b[6]
+ umlal r9, r10, r0, r8 @ d' += a[3] * b[7]
+ ldr r7, [r1, #4*4] @ a[4]
+ umlal r5, r6, r0, r14 @ d += a[3] * b[6]
+ ldr r8, [r2, #5*4] @ b[5]
+ umlal r9, r10, r7, r14 @ d' += a[4] * b[6]
+ ldr r0, [r1, #5*4] @ a[5]
+ umlal r5, r6, r7, r8 @ d += a[4] * b[5]
+ ldr r14, [r2, #4*4] @ b[4]
+ umlal r9, r10, r0, r8 @ d' += a[5] * b[5]
+ ldr r7, [r1, #6*4] @ a[6]
+ umlal r5, r6, r0, r14 @ d += a[5] * b[4]
+ ldr r8, [r2, #3*4] @ b[3]
+ umlal r9, r10, r7, r14 @ d' += a[6] * b[4]
+ ldr r0, [r1, #7*4] @ a[7]
+ umlal r5, r6, r7, r8 @ d += a[6] * b[3]
+ ldr r14, [r2, #2*4] @ b[2]
+ umlal r9, r10, r0, r8 @ d' += a[7] * b[3]
+ ldr r7, [r1, #8*4] @ a[8]
+ umlal r5, r6, r0, r14 @ d += a[7] * b[2]
+ ldr r8, [r2, #1*4] @ b[1]
+ umlal r9, r10, r7, r14 @ d' += a[8] * b[2]
+ ldr r0, [r1, #9*4] @ a[9]
+ umlal r5, r6, r7, r8 @ d += a[8] * b[1]
+ ldr r14, [r2, #0*4] @ b[0]
+ umlal r9, r10, r0, r8 @ d' += a[9] * b[1]
+ ldr r7, [r1, #0*4] @ a[0]
+ umlal r5, r6, r0, r14 @ d += a[9] * b[0]
+ @ r7,r14 used in B
+
+ bic r0, r5, field_not_M @ t9 = d & M
+ str r0, [sp, #4 + 4*9]
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+
+ /* B */
+ umull r3, r4, r7, r14 @ c = a[0] * b[0]
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u0 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u0 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t0 = c & M
+ str r14, [sp, #4 + 0*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u0 * R1
+ umlal r3, r4, r0, r14
+
+ /* C - interleaved with D */
+ ldr r7, [r1, #0*4] @ a[0]
+ ldr r8, [r2, #2*4] @ b[2]
+ ldr r14, [r2, #1*4] @ b[1]
+ umull r11, r12, r7, r8 @ c' = a[0] * b[2]
+ ldr r0, [r1, #1*4] @ a[1]
+ umlal r3, r4, r7, r14 @ c += a[0] * b[1]
+ ldr r8, [r2, #0*4] @ b[0]
+ umlal r11, r12, r0, r14 @ c' += a[1] * b[1]
+ ldr r7, [r1, #2*4] @ a[2]
+ umlal r3, r4, r0, r8 @ c += a[1] * b[0]
+ ldr r14, [r2, #9*4] @ b[9]
+ umlal r11, r12, r7, r8 @ c' += a[2] * b[0]
+ ldr r0, [r1, #3*4] @ a[3]
+ umlal r5, r6, r7, r14 @ d += a[2] * b[9]
+ ldr r8, [r2, #8*4] @ b[8]
+ umull r9, r10, r0, r14 @ d' = a[3] * b[9]
+ ldr r7, [r1, #4*4] @ a[4]
+ umlal r5, r6, r0, r8 @ d += a[3] * b[8]
+ ldr r14, [r2, #7*4] @ b[7]
+ umlal r9, r10, r7, r8 @ d' += a[4] * b[8]
+ ldr r0, [r1, #5*4] @ a[5]
+ umlal r5, r6, r7, r14 @ d += a[4] * b[7]
+ ldr r8, [r2, #6*4] @ b[6]
+ umlal r9, r10, r0, r14 @ d' += a[5] * b[7]
+ ldr r7, [r1, #6*4] @ a[6]
+ umlal r5, r6, r0, r8 @ d += a[5] * b[6]
+ ldr r14, [r2, #5*4] @ b[5]
+ umlal r9, r10, r7, r8 @ d' += a[6] * b[6]
+ ldr r0, [r1, #7*4] @ a[7]
+ umlal r5, r6, r7, r14 @ d += a[6] * b[5]
+ ldr r8, [r2, #4*4] @ b[4]
+ umlal r9, r10, r0, r14 @ d' += a[7] * b[5]
+ ldr r7, [r1, #8*4] @ a[8]
+ umlal r5, r6, r0, r8 @ d += a[7] * b[4]
+ ldr r14, [r2, #3*4] @ b[3]
+ umlal r9, r10, r7, r8 @ d' += a[8] * b[4]
+ ldr r0, [r1, #9*4] @ a[9]
+ umlal r5, r6, r7, r14 @ d += a[8] * b[3]
+ ldr r8, [r2, #2*4] @ b[2]
+ umlal r9, r10, r0, r14 @ d' += a[9] * b[3]
+ umlal r5, r6, r0, r8 @ d += a[9] * b[2]
+
+ bic r0, r5, field_not_M @ u1 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u1 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t1 = c & M
+ str r14, [sp, #4 + 1*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u1 * R1
+ umlal r3, r4, r0, r14
+
+ /* D */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u2 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u2 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t2 = c & M
+ str r14, [sp, #4 + 2*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u2 * R1
+ umlal r3, r4, r0, r14
+
+ /* E - interleaved with F */
+ ldr r7, [r1, #0*4] @ a[0]
+ ldr r8, [r2, #4*4] @ b[4]
+ umull r11, r12, r7, r8 @ c' = a[0] * b[4]
+ ldr r8, [r2, #3*4] @ b[3]
+ umlal r3, r4, r7, r8 @ c += a[0] * b[3]
+ ldr r7, [r1, #1*4] @ a[1]
+ umlal r11, r12, r7, r8 @ c' += a[1] * b[3]
+ ldr r8, [r2, #2*4] @ b[2]
+ umlal r3, r4, r7, r8 @ c += a[1] * b[2]
+ ldr r7, [r1, #2*4] @ a[2]
+ umlal r11, r12, r7, r8 @ c' += a[2] * b[2]
+ ldr r8, [r2, #1*4] @ b[1]
+ umlal r3, r4, r7, r8 @ c += a[2] * b[1]
+ ldr r7, [r1, #3*4] @ a[3]
+ umlal r11, r12, r7, r8 @ c' += a[3] * b[1]
+ ldr r8, [r2, #0*4] @ b[0]
+ umlal r3, r4, r7, r8 @ c += a[3] * b[0]
+ ldr r7, [r1, #4*4] @ a[4]
+ umlal r11, r12, r7, r8 @ c' += a[4] * b[0]
+ ldr r8, [r2, #9*4] @ b[9]
+ umlal r5, r6, r7, r8 @ d += a[4] * b[9]
+ ldr r7, [r1, #5*4] @ a[5]
+ umull r9, r10, r7, r8 @ d' = a[5] * b[9]
+ ldr r8, [r2, #8*4] @ b[8]
+ umlal r5, r6, r7, r8 @ d += a[5] * b[8]
+ ldr r7, [r1, #6*4] @ a[6]
+ umlal r9, r10, r7, r8 @ d' += a[6] * b[8]
+ ldr r8, [r2, #7*4] @ b[7]
+ umlal r5, r6, r7, r8 @ d += a[6] * b[7]
+ ldr r7, [r1, #7*4] @ a[7]
+ umlal r9, r10, r7, r8 @ d' += a[7] * b[7]
+ ldr r8, [r2, #6*4] @ b[6]
+ umlal r5, r6, r7, r8 @ d += a[7] * b[6]
+ ldr r7, [r1, #8*4] @ a[8]
+ umlal r9, r10, r7, r8 @ d' += a[8] * b[6]
+ ldr r8, [r2, #5*4] @ b[5]
+ umlal r5, r6, r7, r8 @ d += a[8] * b[5]
+ ldr r7, [r1, #9*4] @ a[9]
+ umlal r9, r10, r7, r8 @ d' += a[9] * b[5]
+ ldr r8, [r2, #4*4] @ b[4]
+ umlal r5, r6, r7, r8 @ d += a[9] * b[4]
+
+ bic r0, r5, field_not_M @ u3 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u3 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t3 = c & M
+ str r14, [sp, #4 + 3*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u3 * R1
+ umlal r3, r4, r0, r14
+
+ /* F */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u4 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u4 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t4 = c & M
+ str r14, [sp, #4 + 4*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u4 * R1
+ umlal r3, r4, r0, r14
+
+ /* G - interleaved with H */
+ ldr r7, [r1, #0*4] @ a[0]
+ ldr r8, [r2, #6*4] @ b[6]
+ ldr r14, [r2, #5*4] @ b[5]
+ umull r11, r12, r7, r8 @ c' = a[0] * b[6]
+ ldr r0, [r1, #1*4] @ a[1]
+ umlal r3, r4, r7, r14 @ c += a[0] * b[5]
+ ldr r8, [r2, #4*4] @ b[4]
+ umlal r11, r12, r0, r14 @ c' += a[1] * b[5]
+ ldr r7, [r1, #2*4] @ a[2]
+ umlal r3, r4, r0, r8 @ c += a[1] * b[4]
+ ldr r14, [r2, #3*4] @ b[3]
+ umlal r11, r12, r7, r8 @ c' += a[2] * b[4]
+ ldr r0, [r1, #3*4] @ a[3]
+ umlal r3, r4, r7, r14 @ c += a[2] * b[3]
+ ldr r8, [r2, #2*4] @ b[2]
+ umlal r11, r12, r0, r14 @ c' += a[3] * b[3]
+ ldr r7, [r1, #4*4] @ a[4]
+ umlal r3, r4, r0, r8 @ c += a[3] * b[2]
+ ldr r14, [r2, #1*4] @ b[1]
+ umlal r11, r12, r7, r8 @ c' += a[4] * b[2]
+ ldr r0, [r1, #5*4] @ a[5]
+ umlal r3, r4, r7, r14 @ c += a[4] * b[1]
+ ldr r8, [r2, #0*4] @ b[0]
+ umlal r11, r12, r0, r14 @ c' += a[5] * b[1]
+ ldr r7, [r1, #6*4] @ a[6]
+ umlal r3, r4, r0, r8 @ c += a[5] * b[0]
+ ldr r14, [r2, #9*4] @ b[9]
+ umlal r11, r12, r7, r8 @ c' += a[6] * b[0]
+ ldr r0, [r1, #7*4] @ a[7]
+ umlal r5, r6, r7, r14 @ d += a[6] * b[9]
+ ldr r8, [r2, #8*4] @ b[8]
+ umull r9, r10, r0, r14 @ d' = a[7] * b[9]
+ ldr r7, [r1, #8*4] @ a[8]
+ umlal r5, r6, r0, r8 @ d += a[7] * b[8]
+ ldr r14, [r2, #7*4] @ b[7]
+ umlal r9, r10, r7, r8 @ d' += a[8] * b[8]
+ ldr r0, [r1, #9*4] @ a[9]
+ umlal r5, r6, r7, r14 @ d += a[8] * b[7]
+ ldr r8, [r2, #6*4] @ b[6]
+ umlal r9, r10, r0, r14 @ d' += a[9] * b[7]
+ umlal r5, r6, r0, r8 @ d += a[9] * b[6]
+
+ bic r0, r5, field_not_M @ u5 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u5 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t5 = c & M
+ str r14, [sp, #4 + 5*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u5 * R1
+ umlal r3, r4, r0, r14
+
+ /* H */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u6 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u6 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t6 = c & M
+ str r14, [sp, #4 + 6*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u6 * R1
+ umlal r3, r4, r0, r14
+
+ /* I - interleaved with J */
+ ldr r8, [r2, #8*4] @ b[8]
+ ldr r7, [r1, #0*4] @ a[0]
+ ldr r14, [r2, #7*4] @ b[7]
+ umull r11, r12, r7, r8 @ c' = a[0] * b[8]
+ ldr r0, [r1, #1*4] @ a[1]
+ umlal r3, r4, r7, r14 @ c += a[0] * b[7]
+ ldr r8, [r2, #6*4] @ b[6]
+ umlal r11, r12, r0, r14 @ c' += a[1] * b[7]
+ ldr r7, [r1, #2*4] @ a[2]
+ umlal r3, r4, r0, r8 @ c += a[1] * b[6]
+ ldr r14, [r2, #5*4] @ b[5]
+ umlal r11, r12, r7, r8 @ c' += a[2] * b[6]
+ ldr r0, [r1, #3*4] @ a[3]
+ umlal r3, r4, r7, r14 @ c += a[2] * b[5]
+ ldr r8, [r2, #4*4] @ b[4]
+ umlal r11, r12, r0, r14 @ c' += a[3] * b[5]
+ ldr r7, [r1, #4*4] @ a[4]
+ umlal r3, r4, r0, r8 @ c += a[3] * b[4]
+ ldr r14, [r2, #3*4] @ b[3]
+ umlal r11, r12, r7, r8 @ c' += a[4] * b[4]
+ ldr r0, [r1, #5*4] @ a[5]
+ umlal r3, r4, r7, r14 @ c += a[4] * b[3]
+ ldr r8, [r2, #2*4] @ b[2]
+ umlal r11, r12, r0, r14 @ c' += a[5] * b[3]
+ ldr r7, [r1, #6*4] @ a[6]
+ umlal r3, r4, r0, r8 @ c += a[5] * b[2]
+ ldr r14, [r2, #1*4] @ b[1]
+ umlal r11, r12, r7, r8 @ c' += a[6] * b[2]
+ ldr r0, [r1, #7*4] @ a[7]
+ umlal r3, r4, r7, r14 @ c += a[6] * b[1]
+ ldr r8, [r2, #0*4] @ b[0]
+ umlal r11, r12, r0, r14 @ c' += a[7] * b[1]
+ ldr r7, [r1, #8*4] @ a[8]
+ umlal r3, r4, r0, r8 @ c += a[7] * b[0]
+ ldr r14, [r2, #9*4] @ b[9]
+ umlal r11, r12, r7, r8 @ c' += a[8] * b[0]
+ ldr r0, [r1, #9*4] @ a[9]
+ umlal r5, r6, r7, r14 @ d += a[8] * b[9]
+ ldr r8, [r2, #8*4] @ b[8]
+ umull r9, r10, r0, r14 @ d' = a[9] * b[9]
+ umlal r5, r6, r0, r8 @ d += a[9] * b[8]
+
+ bic r0, r5, field_not_M @ u7 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u7 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t7 = c & M
+ str r14, [sp, #4 + 7*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u7 * R1
+ umlal r3, r4, r0, r14
+
+ /* J */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u8 = d & M
+ str r0, [sp, #4 + 8*4]
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u8 * R0
+ umlal r3, r4, r0, r14
+
+ /******************************************
+ * compute and write back result
+ ******************************************
+ Allocation:
+ r0 r
+ r3:r4 c
+ r5:r6 d
+ r7 t0
+ r8 t1
+ r9 t2
+ r11 u8
+ r12 t9
+ r1,r2,r10,r14 scratch
+
+ Note: do not read from a[] after here, it may overlap with r[]
+ */
+ ldr r0, [sp, #0]
+ add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9
+ ldmia r1, {r2,r7,r8,r9,r10,r11,r12}
+ add r1, r0, #3*4
+ stmia r1, {r2,r7,r8,r9,r10}
+
+ bic r2, r3, field_not_M @ r[8] = c & M
+ str r2, [r0, #8*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u8 * R1
+ umlal r3, r4, r11, r14
+ movw r14, field_R0 @ c += d * R0
+ umlal r3, r4, r5, r14
+ adds r3, r3, r12 @ c += t9
+ adc r4, r4, #0
+
+ add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2
+ ldmia r1, {r7,r8,r9}
+
+ ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4)
+ str r2, [r0, #9*4]
+ mov r3, r3, lsr #22 @ c >>= 22
+ orr r3, r3, r4, asl #10
+ mov r4, r4, lsr #22
+ movw r14, field_R1 << 4 @ c += d * (R1 << 4)
+ umlal r3, r4, r5, r14
+
+ movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add)
+ umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4)
+ adds r5, r5, r7 @ d.lo += t0
+ mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4)
+ adc r6, r6, 0 @ d.hi += carry
+
+ bic r2, r5, field_not_M @ r[0] = d & M
+ str r2, [r0, #0*4]
+
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+
+ movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add)
+ umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4)
+ adds r5, r5, r8 @ d.lo += t1
+ adc r6, r6, #0 @ d.hi += carry
+ adds r5, r5, r1 @ d.lo += tmp.lo
+ mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4)
+ adc r6, r6, r2 @ d.hi += carry + tmp.hi
+
+ bic r2, r5, field_not_M @ r[1] = d & M
+ str r2, [r0, #1*4]
+ mov r5, r5, lsr #26 @ d >>= 26 (ignore hi)
+ orr r5, r5, r6, asl #6
+
+ add r5, r5, r9 @ d += t2
+ str r5, [r0, #2*4] @ r[2] = d
+
+ add sp, sp, #48
+ ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}
+ .size secp256k1_fe_mul_inner, .-secp256k1_fe_mul_inner
+
+ .align 2
+ .global secp256k1_fe_sqr_inner
+ .type secp256k1_fe_sqr_inner, %function
+ @ Arguments:
+ @ r0 r Can overlap with a
+ @ r1 a
+ @ Stack (total 4+10*4 = 44)
+ @ sp + #0 saved 'r' pointer
+ @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9
+secp256k1_fe_sqr_inner:
+ stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14}
+ sub sp, sp, #48 @ frame=44 + alignment
+ str r0, [sp, #0] @ save result address, we need it only at the end
+ /******************************************
+ * Main computation code.
+ ******************************************
+
+ Allocation:
+ r0,r14,r2,r7,r8 scratch
+ r1 a (pointer)
+ r3:r4 c
+ r5:r6 d
+ r11:r12 c'
+ r9:r10 d'
+
+ Note: do not write to r[] here, it may overlap with a[]
+ */
+ /* A interleaved with B */
+ ldr r0, [r1, #1*4] @ a[1]*2
+ ldr r7, [r1, #0*4] @ a[0]
+ mov r0, r0, asl #1
+ ldr r14, [r1, #9*4] @ a[9]
+ umull r3, r4, r7, r7 @ c = a[0] * a[0]
+ ldr r8, [r1, #8*4] @ a[8]
+ mov r7, r7, asl #1
+ umull r5, r6, r7, r14 @ d = a[0]*2 * a[9]
+ ldr r7, [r1, #2*4] @ a[2]*2
+ umull r9, r10, r0, r14 @ d' = a[1]*2 * a[9]
+ ldr r14, [r1, #7*4] @ a[7]
+ umlal r5, r6, r0, r8 @ d += a[1]*2 * a[8]
+ mov r7, r7, asl #1
+ ldr r0, [r1, #3*4] @ a[3]*2
+ umlal r9, r10, r7, r8 @ d' += a[2]*2 * a[8]
+ ldr r8, [r1, #6*4] @ a[6]
+ umlal r5, r6, r7, r14 @ d += a[2]*2 * a[7]
+ mov r0, r0, asl #1
+ ldr r7, [r1, #4*4] @ a[4]*2
+ umlal r9, r10, r0, r14 @ d' += a[3]*2 * a[7]
+ ldr r14, [r1, #5*4] @ a[5]
+ mov r7, r7, asl #1
+ umlal r5, r6, r0, r8 @ d += a[3]*2 * a[6]
+ umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[6]
+ umlal r5, r6, r7, r14 @ d += a[4]*2 * a[5]
+ umlal r9, r10, r14, r14 @ d' += a[5] * a[5]
+
+ bic r0, r5, field_not_M @ t9 = d & M
+ str r0, [sp, #4 + 9*4]
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+
+ /* B */
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u0 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u0 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t0 = c & M
+ str r14, [sp, #4 + 0*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u0 * R1
+ umlal r3, r4, r0, r14
+
+ /* C interleaved with D */
+ ldr r0, [r1, #0*4] @ a[0]*2
+ ldr r14, [r1, #1*4] @ a[1]
+ mov r0, r0, asl #1
+ ldr r8, [r1, #2*4] @ a[2]
+ umlal r3, r4, r0, r14 @ c += a[0]*2 * a[1]
+ mov r7, r8, asl #1 @ a[2]*2
+ umull r11, r12, r14, r14 @ c' = a[1] * a[1]
+ ldr r14, [r1, #9*4] @ a[9]
+ umlal r11, r12, r0, r8 @ c' += a[0]*2 * a[2]
+ ldr r0, [r1, #3*4] @ a[3]*2
+ ldr r8, [r1, #8*4] @ a[8]
+ umlal r5, r6, r7, r14 @ d += a[2]*2 * a[9]
+ mov r0, r0, asl #1
+ ldr r7, [r1, #4*4] @ a[4]*2
+ umull r9, r10, r0, r14 @ d' = a[3]*2 * a[9]
+ ldr r14, [r1, #7*4] @ a[7]
+ umlal r5, r6, r0, r8 @ d += a[3]*2 * a[8]
+ mov r7, r7, asl #1
+ ldr r0, [r1, #5*4] @ a[5]*2
+ umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[8]
+ ldr r8, [r1, #6*4] @ a[6]
+ mov r0, r0, asl #1
+ umlal r5, r6, r7, r14 @ d += a[4]*2 * a[7]
+ umlal r9, r10, r0, r14 @ d' += a[5]*2 * a[7]
+ umlal r5, r6, r0, r8 @ d += a[5]*2 * a[6]
+ umlal r9, r10, r8, r8 @ d' += a[6] * a[6]
+
+ bic r0, r5, field_not_M @ u1 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u1 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t1 = c & M
+ str r14, [sp, #4 + 1*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u1 * R1
+ umlal r3, r4, r0, r14
+
+ /* D */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u2 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u2 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t2 = c & M
+ str r14, [sp, #4 + 2*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u2 * R1
+ umlal r3, r4, r0, r14
+
+ /* E interleaved with F */
+ ldr r7, [r1, #0*4] @ a[0]*2
+ ldr r0, [r1, #1*4] @ a[1]*2
+ ldr r14, [r1, #2*4] @ a[2]
+ mov r7, r7, asl #1
+ ldr r8, [r1, #3*4] @ a[3]
+ ldr r2, [r1, #4*4]
+ umlal r3, r4, r7, r8 @ c += a[0]*2 * a[3]
+ mov r0, r0, asl #1
+ umull r11, r12, r7, r2 @ c' = a[0]*2 * a[4]
+ mov r2, r2, asl #1 @ a[4]*2
+ umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[3]
+ ldr r8, [r1, #9*4] @ a[9]
+ umlal r3, r4, r0, r14 @ c += a[1]*2 * a[2]
+ ldr r0, [r1, #5*4] @ a[5]*2
+ umlal r11, r12, r14, r14 @ c' += a[2] * a[2]
+ ldr r14, [r1, #8*4] @ a[8]
+ mov r0, r0, asl #1
+ umlal r5, r6, r2, r8 @ d += a[4]*2 * a[9]
+ ldr r7, [r1, #6*4] @ a[6]*2
+ umull r9, r10, r0, r8 @ d' = a[5]*2 * a[9]
+ mov r7, r7, asl #1
+ ldr r8, [r1, #7*4] @ a[7]
+ umlal r5, r6, r0, r14 @ d += a[5]*2 * a[8]
+ umlal r9, r10, r7, r14 @ d' += a[6]*2 * a[8]
+ umlal r5, r6, r7, r8 @ d += a[6]*2 * a[7]
+ umlal r9, r10, r8, r8 @ d' += a[7] * a[7]
+
+ bic r0, r5, field_not_M @ u3 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u3 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t3 = c & M
+ str r14, [sp, #4 + 3*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u3 * R1
+ umlal r3, r4, r0, r14
+
+ /* F */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u4 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u4 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t4 = c & M
+ str r14, [sp, #4 + 4*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u4 * R1
+ umlal r3, r4, r0, r14
+
+ /* G interleaved with H */
+ ldr r7, [r1, #0*4] @ a[0]*2
+ ldr r0, [r1, #1*4] @ a[1]*2
+ mov r7, r7, asl #1
+ ldr r8, [r1, #5*4] @ a[5]
+ ldr r2, [r1, #6*4] @ a[6]
+ umlal r3, r4, r7, r8 @ c += a[0]*2 * a[5]
+ ldr r14, [r1, #4*4] @ a[4]
+ mov r0, r0, asl #1
+ umull r11, r12, r7, r2 @ c' = a[0]*2 * a[6]
+ ldr r7, [r1, #2*4] @ a[2]*2
+ umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[5]
+ mov r7, r7, asl #1
+ ldr r8, [r1, #3*4] @ a[3]
+ umlal r3, r4, r0, r14 @ c += a[1]*2 * a[4]
+ mov r0, r2, asl #1 @ a[6]*2
+ umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[4]
+ ldr r14, [r1, #9*4] @ a[9]
+ umlal r3, r4, r7, r8 @ c += a[2]*2 * a[3]
+ ldr r7, [r1, #7*4] @ a[7]*2
+ umlal r11, r12, r8, r8 @ c' += a[3] * a[3]
+ mov r7, r7, asl #1
+ ldr r8, [r1, #8*4] @ a[8]
+ umlal r5, r6, r0, r14 @ d += a[6]*2 * a[9]
+ umull r9, r10, r7, r14 @ d' = a[7]*2 * a[9]
+ umlal r5, r6, r7, r8 @ d += a[7]*2 * a[8]
+ umlal r9, r10, r8, r8 @ d' += a[8] * a[8]
+
+ bic r0, r5, field_not_M @ u5 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u5 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t5 = c & M
+ str r14, [sp, #4 + 5*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u5 * R1
+ umlal r3, r4, r0, r14
+
+ /* H */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u6 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u6 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t6 = c & M
+ str r14, [sp, #4 + 6*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u6 * R1
+ umlal r3, r4, r0, r14
+
+ /* I interleaved with J */
+ ldr r7, [r1, #0*4] @ a[0]*2
+ ldr r0, [r1, #1*4] @ a[1]*2
+ mov r7, r7, asl #1
+ ldr r8, [r1, #7*4] @ a[7]
+ ldr r2, [r1, #8*4] @ a[8]
+ umlal r3, r4, r7, r8 @ c += a[0]*2 * a[7]
+ ldr r14, [r1, #6*4] @ a[6]
+ mov r0, r0, asl #1
+ umull r11, r12, r7, r2 @ c' = a[0]*2 * a[8]
+ ldr r7, [r1, #2*4] @ a[2]*2
+ umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[7]
+ ldr r8, [r1, #5*4] @ a[5]
+ umlal r3, r4, r0, r14 @ c += a[1]*2 * a[6]
+ ldr r0, [r1, #3*4] @ a[3]*2
+ mov r7, r7, asl #1
+ umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[6]
+ ldr r14, [r1, #4*4] @ a[4]
+ mov r0, r0, asl #1
+ umlal r3, r4, r7, r8 @ c += a[2]*2 * a[5]
+ mov r2, r2, asl #1 @ a[8]*2
+ umlal r11, r12, r0, r8 @ c' += a[3]*2 * a[5]
+ umlal r3, r4, r0, r14 @ c += a[3]*2 * a[4]
+ umlal r11, r12, r14, r14 @ c' += a[4] * a[4]
+ ldr r8, [r1, #9*4] @ a[9]
+ umlal r5, r6, r2, r8 @ d += a[8]*2 * a[9]
+ @ r8 will be used in J
+
+ bic r0, r5, field_not_M @ u7 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u7 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t7 = c & M
+ str r14, [sp, #4 + 7*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u7 * R1
+ umlal r3, r4, r0, r14
+
+ /* J */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ umlal r5, r6, r8, r8 @ d += a[9] * a[9]
+
+ bic r0, r5, field_not_M @ u8 = d & M
+ str r0, [sp, #4 + 8*4]
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u8 * R0
+ umlal r3, r4, r0, r14
+
+ /******************************************
+ * compute and write back result
+ ******************************************
+ Allocation:
+ r0 r
+ r3:r4 c
+ r5:r6 d
+ r7 t0
+ r8 t1
+ r9 t2
+ r11 u8
+ r12 t9
+ r1,r2,r10,r14 scratch
+
+ Note: do not read from a[] after here, it may overlap with r[]
+ */
+ ldr r0, [sp, #0]
+ add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9
+ ldmia r1, {r2,r7,r8,r9,r10,r11,r12}
+ add r1, r0, #3*4
+ stmia r1, {r2,r7,r8,r9,r10}
+
+ bic r2, r3, field_not_M @ r[8] = c & M
+ str r2, [r0, #8*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u8 * R1
+ umlal r3, r4, r11, r14
+ movw r14, field_R0 @ c += d * R0
+ umlal r3, r4, r5, r14
+ adds r3, r3, r12 @ c += t9
+ adc r4, r4, #0
+
+ add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2
+ ldmia r1, {r7,r8,r9}
+
+ ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4)
+ str r2, [r0, #9*4]
+ mov r3, r3, lsr #22 @ c >>= 22
+ orr r3, r3, r4, asl #10
+ mov r4, r4, lsr #22
+ movw r14, field_R1 << 4 @ c += d * (R1 << 4)
+ umlal r3, r4, r5, r14
+
+ movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add)
+ umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4)
+ adds r5, r5, r7 @ d.lo += t0
+ mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4)
+ adc r6, r6, 0 @ d.hi += carry
+
+ bic r2, r5, field_not_M @ r[0] = d & M
+ str r2, [r0, #0*4]
+
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+
+ movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add)
+ umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4)
+ adds r5, r5, r8 @ d.lo += t1
+ adc r6, r6, #0 @ d.hi += carry
+ adds r5, r5, r1 @ d.lo += tmp.lo
+ mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4)
+ adc r6, r6, r2 @ d.hi += carry + tmp.hi
+
+ bic r2, r5, field_not_M @ r[1] = d & M
+ str r2, [r0, #1*4]
+ mov r5, r5, lsr #26 @ d >>= 26 (ignore hi)
+ orr r5, r5, r6, asl #6
+
+ add r5, r5, r9 @ d += t2
+ str r5, [r0, #2*4] @ r[2] = d
+
+ add sp, sp, #48
+ ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}
+ .size secp256k1_fe_sqr_inner, .-secp256k1_fe_sqr_inner
+
diff --git a/crypto/secp256k1/libsecp256k1/src/bench_ecdh.c b/crypto/secp256k1/libsecp256k1/src/bench_ecdh.c
index 5a7c6376e0..cde5e2dbb4 100644
--- a/crypto/secp256k1/libsecp256k1/src/bench_ecdh.c
+++ b/crypto/secp256k1/libsecp256k1/src/bench_ecdh.c
@@ -28,7 +28,8 @@ static void bench_ecdh_setup(void* arg) {
0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f
};
- data->ctx = secp256k1_context_create(0);
+ /* create a context with no capabilities */
+ data->ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT);
for (i = 0; i < 32; i++) {
data->scalar[i] = i + 1;
}
diff --git a/crypto/secp256k1/libsecp256k1/src/bench_internal.c b/crypto/secp256k1/libsecp256k1/src/bench_internal.c
index 7809f5f8cf..0809f77bda 100644
--- a/crypto/secp256k1/libsecp256k1/src/bench_internal.c
+++ b/crypto/secp256k1/libsecp256k1/src/bench_internal.c
@@ -181,12 +181,12 @@ void bench_field_inverse_var(void* arg) {
}
}
-void bench_field_sqrt_var(void* arg) {
+void bench_field_sqrt(void* arg) {
int i;
bench_inv_t *data = (bench_inv_t*)arg;
for (i = 0; i < 20000; i++) {
- secp256k1_fe_sqrt_var(&data->fe_x, &data->fe_x);
+ secp256k1_fe_sqrt(&data->fe_x, &data->fe_x);
secp256k1_fe_add(&data->fe_x, &data->fe_y);
}
}
@@ -227,6 +227,15 @@ void bench_group_add_affine_var(void* arg) {
}
}
+void bench_group_jacobi_var(void* arg) {
+ int i;
+ bench_inv_t *data = (bench_inv_t*)arg;
+
+ for (i = 0; i < 20000; i++) {
+ secp256k1_gej_has_quad_y_var(&data->gej_x);
+ }
+}
+
void bench_ecmult_wnaf(void* arg) {
int i;
bench_inv_t *data = (bench_inv_t*)arg;
@@ -299,6 +308,21 @@ void bench_context_sign(void* arg) {
}
}
+#ifndef USE_NUM_NONE
+void bench_num_jacobi(void* arg) {
+ int i;
+ bench_inv_t *data = (bench_inv_t*)arg;
+ secp256k1_num nx, norder;
+
+ secp256k1_scalar_get_num(&nx, &data->scalar_x);
+ secp256k1_scalar_order_get_num(&norder);
+ secp256k1_scalar_get_num(&norder, &data->scalar_y);
+
+ for (i = 0; i < 200000; i++) {
+ secp256k1_num_jacobi(&nx, &norder);
+ }
+}
+#endif
int have_flag(int argc, char** argv, char *flag) {
char** argm = argv + argc;
@@ -333,12 +357,13 @@ int main(int argc, char **argv) {
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, 200000);
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, 20000);
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, 20000);
- if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt_var", bench_field_sqrt_var, bench_setup, NULL, &data, 10, 20000);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, 20000);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, 200000);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, 200000);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, 200000);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, 200000);
+ if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, 20000);
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, 20000);
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, 20000);
@@ -350,5 +375,8 @@ int main(int argc, char **argv) {
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 20);
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 200);
+#ifndef USE_NUM_NONE
+ if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, 200000);
+#endif
return 0;
}
diff --git a/crypto/secp256k1/libsecp256k1/src/bench_verify.c b/crypto/secp256k1/libsecp256k1/src/bench_verify.c
index 0cafbdc4e6..418defa0aa 100644
--- a/crypto/secp256k1/libsecp256k1/src/bench_verify.c
+++ b/crypto/secp256k1/libsecp256k1/src/bench_verify.c
@@ -11,6 +11,12 @@
#include "util.h"
#include "bench.h"
+#ifdef ENABLE_OPENSSL_TESTS
+#include
+#include
+#include
+#endif
+
typedef struct {
secp256k1_context *ctx;
unsigned char msg[32];
@@ -19,6 +25,9 @@ typedef struct {
size_t siglen;
unsigned char pubkey[33];
size_t pubkeylen;
+#ifdef ENABLE_OPENSSL_TESTS
+ EC_GROUP* ec_group;
+#endif
} benchmark_verify_t;
static void benchmark_verify(void* arg) {
@@ -40,6 +49,36 @@ static void benchmark_verify(void* arg) {
}
}
+#ifdef ENABLE_OPENSSL_TESTS
+static void benchmark_verify_openssl(void* arg) {
+ int i;
+ benchmark_verify_t* data = (benchmark_verify_t*)arg;
+
+ for (i = 0; i < 20000; i++) {
+ data->sig[data->siglen - 1] ^= (i & 0xFF);
+ data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF);
+ data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF);
+ {
+ EC_KEY *pkey = EC_KEY_new();
+ const unsigned char *pubkey = &data->pubkey[0];
+ int result;
+
+ CHECK(pkey != NULL);
+ result = EC_KEY_set_group(pkey, data->ec_group);
+ CHECK(result);
+ result = (o2i_ECPublicKey(&pkey, &pubkey, data->pubkeylen)) != NULL;
+ CHECK(result);
+ result = ECDSA_verify(0, &data->msg[0], sizeof(data->msg), &data->sig[0], data->siglen, pkey) == (i == 0);
+ CHECK(result);
+ EC_KEY_free(pkey);
+ }
+ data->sig[data->siglen - 1] ^= (i & 0xFF);
+ data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF);
+ data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF);
+ }
+}
+#endif
+
int main(void) {
int i;
secp256k1_pubkey pubkey;
@@ -58,9 +97,15 @@ int main(void) {
CHECK(secp256k1_ecdsa_sign(data.ctx, &sig, data.msg, data.key, NULL, NULL));
CHECK(secp256k1_ecdsa_signature_serialize_der(data.ctx, data.sig, &data.siglen, &sig));
CHECK(secp256k1_ec_pubkey_create(data.ctx, &pubkey, data.key));
+ data.pubkeylen = 33;
CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, 20000);
+#ifdef ENABLE_OPENSSL_TESTS
+ data.ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1);
+ run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, 20000);
+ EC_GROUP_free(data.ec_group);
+#endif
secp256k1_context_destroy(data.ctx);
return 0;
diff --git a/crypto/secp256k1/libsecp256k1/src/ecdsa.h b/crypto/secp256k1/libsecp256k1/src/ecdsa.h
index 4c0a4a89e0..54ae101b92 100644
--- a/crypto/secp256k1/libsecp256k1/src/ecdsa.h
+++ b/crypto/secp256k1/libsecp256k1/src/ecdsa.h
@@ -17,6 +17,5 @@ static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *r, secp256k1_scalar *s, c
static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar *r, const secp256k1_scalar *s);
static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message);
static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid);
-static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, const secp256k1_scalar* r, const secp256k1_scalar* s, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid);
#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/ecdsa_impl.h b/crypto/secp256k1/libsecp256k1/src/ecdsa_impl.h
index 4a172b3c51..453bb11880 100644
--- a/crypto/secp256k1/libsecp256k1/src/ecdsa_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/ecdsa_impl.h
@@ -1,5 +1,5 @@
/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Copyright (c) 2013-2015 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
@@ -46,66 +46,133 @@ static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CON
0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL
);
+static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned char *sigend) {
+ int lenleft, b1;
+ size_t ret = 0;
+ if (*sigp >= sigend) {
+ return -1;
+ }
+ b1 = *((*sigp)++);
+ if (b1 == 0xFF) {
+ /* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */
+ return -1;
+ }
+ if ((b1 & 0x80) == 0) {
+ /* X.690-0207 8.1.3.4 short form length octets */
+ return b1;
+ }
+ if (b1 == 0x80) {
+ /* Indefinite length is not allowed in DER. */
+ return -1;
+ }
+ /* X.690-207 8.1.3.5 long form length octets */
+ lenleft = b1 & 0x7F;
+ if (lenleft > sigend - *sigp) {
+ return -1;
+ }
+ if (**sigp == 0) {
+ /* Not the shortest possible length encoding. */
+ return -1;
+ }
+ if ((size_t)lenleft > sizeof(size_t)) {
+ /* The resulting length would exceed the range of a size_t, so
+ * certainly longer than the passed array size.
+ */
+ return -1;
+ }
+ while (lenleft > 0) {
+ if ((ret >> ((sizeof(size_t) - 1) * 8)) != 0) {
+ }
+ ret = (ret << 8) | **sigp;
+ if (ret + lenleft > (size_t)(sigend - *sigp)) {
+ /* Result exceeds the length of the passed array. */
+ return -1;
+ }
+ (*sigp)++;
+ lenleft--;
+ }
+ if (ret < 128) {
+ /* Not the shortest possible length encoding. */
+ return -1;
+ }
+ return ret;
+}
+
+static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) {
+ int overflow = 0;
+ unsigned char ra[32] = {0};
+ int rlen;
+
+ if (*sig == sigend || **sig != 0x02) {
+ /* Not a primitive integer (X.690-0207 8.3.1). */
+ return 0;
+ }
+ (*sig)++;
+ rlen = secp256k1_der_read_len(sig, sigend);
+ if (rlen <= 0 || (*sig) + rlen > sigend) {
+ /* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */
+ return 0;
+ }
+ if (**sig == 0x00 && rlen > 1 && (((*sig)[1]) & 0x80) == 0x00) {
+ /* Excessive 0x00 padding. */
+ return 0;
+ }
+ if (**sig == 0xFF && rlen > 1 && (((*sig)[1]) & 0x80) == 0x80) {
+ /* Excessive 0xFF padding. */
+ return 0;
+ }
+ if ((**sig & 0x80) == 0x80) {
+ /* Negative. */
+ overflow = 1;
+ }
+ while (rlen > 0 && **sig == 0) {
+ /* Skip leading zero bytes */
+ rlen--;
+ (*sig)++;
+ }
+ if (rlen > 32) {
+ overflow = 1;
+ }
+ if (!overflow) {
+ memcpy(ra + 32 - rlen, *sig, rlen);
+ secp256k1_scalar_set_b32(r, ra, &overflow);
+ }
+ if (overflow) {
+ secp256k1_scalar_set_int(r, 0);
+ }
+ (*sig) += rlen;
+ return 1;
+}
+
static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) {
- unsigned char ra[32] = {0}, sa[32] = {0};
- const unsigned char *rp;
- const unsigned char *sp;
- size_t lenr;
- size_t lens;
- int overflow;
- if (sig[0] != 0x30) {
+ const unsigned char *sigend = sig + size;
+ int rlen;
+ if (sig == sigend || *(sig++) != 0x30) {
+ /* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */
return 0;
}
- lenr = sig[3];
- if (5+lenr >= size) {
+ rlen = secp256k1_der_read_len(&sig, sigend);
+ if (rlen < 0 || sig + rlen > sigend) {
+ /* Tuple exceeds bounds */
return 0;
}
- lens = sig[lenr+5];
- if (sig[1] != lenr+lens+4) {
+ if (sig + rlen != sigend) {
+ /* Garbage after tuple. */
return 0;
}
- if (lenr+lens+6 > size) {
+
+ if (!secp256k1_der_parse_integer(rr, &sig, sigend)) {
return 0;
}
- if (sig[2] != 0x02) {
+ if (!secp256k1_der_parse_integer(rs, &sig, sigend)) {
return 0;
}
- if (lenr == 0) {
- return 0;
- }
- if (sig[lenr+4] != 0x02) {
- return 0;
- }
- if (lens == 0) {
- return 0;
- }
- sp = sig + 6 + lenr;
- while (lens > 0 && sp[0] == 0) {
- lens--;
- sp++;
- }
- if (lens > 32) {
- return 0;
- }
- rp = sig + 4;
- while (lenr > 0 && rp[0] == 0) {
- lenr--;
- rp++;
- }
- if (lenr > 32) {
- return 0;
- }
- memcpy(ra + 32 - lenr, rp, lenr);
- memcpy(sa + 32 - lens, sp, lens);
- overflow = 0;
- secp256k1_scalar_set_b32(rr, ra, &overflow);
- if (overflow) {
- return 0;
- }
- secp256k1_scalar_set_b32(rs, sa, &overflow);
- if (overflow) {
+
+ if (sig != sigend) {
+ /* Trailing garbage inside tuple. */
return 0;
}
+
return 1;
}
@@ -136,7 +203,9 @@ static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const
static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) {
unsigned char c[32];
secp256k1_scalar sn, u1, u2;
+#if !defined(EXHAUSTIVE_TEST_ORDER)
secp256k1_fe xr;
+#endif
secp256k1_gej pubkeyj;
secp256k1_gej pr;
@@ -152,6 +221,19 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const
if (secp256k1_gej_is_infinity(&pr)) {
return 0;
}
+
+#if defined(EXHAUSTIVE_TEST_ORDER)
+{
+ secp256k1_scalar computed_r;
+ secp256k1_ge pr_ge;
+ secp256k1_ge_set_gej(&pr_ge, &pr);
+ secp256k1_fe_normalize(&pr_ge.x);
+
+ secp256k1_fe_get_b32(c, &pr_ge.x);
+ secp256k1_scalar_set_b32(&computed_r, c, NULL);
+ return secp256k1_scalar_eq(sigr, &computed_r);
+}
+#else
secp256k1_scalar_get_b32(c, sigr);
secp256k1_fe_set_b32(&xr, c);
@@ -172,11 +254,11 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const
* secp256k1_gej_eq_x implements the (xr * pr.z^2 mod p == pr.x) test.
*/
if (secp256k1_gej_eq_x_var(&xr, &pr)) {
- /* xr.x == xr * xr.z^2 mod p, so the signature is valid. */
+ /* xr * pr.z^2 mod p == pr.x, so the signature is valid. */
return 1;
}
if (secp256k1_fe_cmp_var(&xr, &secp256k1_ecdsa_const_p_minus_order) >= 0) {
- /* xr + p >= n, so we can skip testing the second case. */
+ /* xr + n >= p, so we can skip testing the second case. */
return 0;
}
secp256k1_fe_add(&xr, &secp256k1_ecdsa_const_order_as_fe);
@@ -185,39 +267,7 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const
return 1;
}
return 0;
-}
-
-static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar* sigs, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid) {
- unsigned char brx[32];
- secp256k1_fe fx;
- secp256k1_ge x;
- secp256k1_gej xj;
- secp256k1_scalar rn, u1, u2;
- secp256k1_gej qj;
-
- if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) {
- return 0;
- }
-
- secp256k1_scalar_get_b32(brx, sigr);
- VERIFY_CHECK(secp256k1_fe_set_b32(&fx, brx)); /* brx comes from a scalar, so is less than the order; certainly less than p */
- if (recid & 2) {
- if (secp256k1_fe_cmp_var(&fx, &secp256k1_ecdsa_const_p_minus_order) >= 0) {
- return 0;
- }
- secp256k1_fe_add(&fx, &secp256k1_ecdsa_const_order_as_fe);
- }
- if (!secp256k1_ge_set_xo_var(&x, &fx, recid & 1)) {
- return 0;
- }
- secp256k1_gej_set_ge(&xj, &x);
- secp256k1_scalar_inverse_var(&rn, sigr);
- secp256k1_scalar_mul(&u1, &rn, message);
- secp256k1_scalar_negate(&u1, &u1);
- secp256k1_scalar_mul(&u2, &rn, sigs);
- secp256k1_ecmult(ctx, &qj, &xj, &u2, &u1);
- secp256k1_ge_set_gej_var(pubkey, &qj);
- return !secp256k1_gej_is_infinity(&qj);
+#endif
}
static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) {
@@ -233,13 +283,14 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
secp256k1_fe_normalize(&r.y);
secp256k1_fe_get_b32(b, &r.x);
secp256k1_scalar_set_b32(sigr, b, &overflow);
- if (secp256k1_scalar_is_zero(sigr)) {
- /* P.x = order is on the curve, so technically sig->r could end up zero, which would be an invalid signature. */
- secp256k1_gej_clear(&rp);
- secp256k1_ge_clear(&r);
- return 0;
- }
+ /* These two conditions should be checked before calling */
+ VERIFY_CHECK(!secp256k1_scalar_is_zero(sigr));
+ VERIFY_CHECK(overflow == 0);
+
if (recid) {
+ /* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log
+ * of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria.
+ */
*recid = (overflow ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0);
}
secp256k1_scalar_mul(&n, sigr, seckey);
diff --git a/crypto/secp256k1/libsecp256k1/src/eckey.h b/crypto/secp256k1/libsecp256k1/src/eckey.h
index 71c4096dfb..42739a3bea 100644
--- a/crypto/secp256k1/libsecp256k1/src/eckey.h
+++ b/crypto/secp256k1/libsecp256k1/src/eckey.h
@@ -15,10 +15,7 @@
#include "ecmult_gen.h"
static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size);
-static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, unsigned int flags);
-
-static int secp256k1_eckey_privkey_parse(secp256k1_scalar *key, const unsigned char *privkey, size_t privkeylen);
-static int secp256k1_eckey_privkey_serialize(const secp256k1_ecmult_gen_context *ctx, unsigned char *privkey, size_t *privkeylen, const secp256k1_scalar *key, unsigned int flags);
+static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed);
static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak);
static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak);
diff --git a/crypto/secp256k1/libsecp256k1/src/eckey_impl.h b/crypto/secp256k1/libsecp256k1/src/eckey_impl.h
index ae44240152..ce38071ac2 100644
--- a/crypto/secp256k1/libsecp256k1/src/eckey_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/eckey_impl.h
@@ -33,14 +33,14 @@ static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char
}
}
-static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, unsigned int flags) {
+static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed) {
if (secp256k1_ge_is_infinity(elem)) {
return 0;
}
secp256k1_fe_normalize_var(&elem->x);
secp256k1_fe_normalize_var(&elem->y);
secp256k1_fe_get_b32(&pub[1], &elem->x);
- if (flags & SECP256K1_EC_COMPRESSED) {
+ if (compressed) {
*size = 33;
pub[0] = 0x02 | (secp256k1_fe_is_odd(&elem->y) ? 0x01 : 0x00);
} else {
@@ -51,109 +51,6 @@ static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *p
return 1;
}
-static int secp256k1_eckey_privkey_parse(secp256k1_scalar *key, const unsigned char *privkey, size_t privkeylen) {
- unsigned char c[32] = {0};
- const unsigned char *end = privkey + privkeylen;
- int lenb = 0;
- int len = 0;
- int overflow = 0;
- /* sequence header */
- if (end < privkey+1 || *privkey != 0x30) {
- return 0;
- }
- privkey++;
- /* sequence length constructor */
- if (end < privkey+1 || !(*privkey & 0x80)) {
- return 0;
- }
- lenb = *privkey & ~0x80; privkey++;
- if (lenb < 1 || lenb > 2) {
- return 0;
- }
- if (end < privkey+lenb) {
- return 0;
- }
- /* sequence length */
- len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0);
- privkey += lenb;
- if (end < privkey+len) {
- return 0;
- }
- /* sequence element 0: version number (=1) */
- if (end < privkey+3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) {
- return 0;
- }
- privkey += 3;
- /* sequence element 1: octet string, up to 32 bytes */
- if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) {
- return 0;
- }
- memcpy(c + 32 - privkey[1], privkey + 2, privkey[1]);
- secp256k1_scalar_set_b32(key, c, &overflow);
- memset(c, 0, 32);
- return !overflow;
-}
-
-static int secp256k1_eckey_privkey_serialize(const secp256k1_ecmult_gen_context *ctx, unsigned char *privkey, size_t *privkeylen, const secp256k1_scalar *key, unsigned int flags) {
- secp256k1_gej rp;
- secp256k1_ge r;
- size_t pubkeylen = 0;
- secp256k1_ecmult_gen(ctx, &rp, key);
- secp256k1_ge_set_gej(&r, &rp);
- if (flags & SECP256K1_EC_COMPRESSED) {
- static const unsigned char begin[] = {
- 0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20
- };
- static const unsigned char middle[] = {
- 0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
- 0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04,
- 0x21,0x02,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87,
- 0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8,
- 0x17,0x98,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
- 0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,0x00
- };
- unsigned char *ptr = privkey;
- memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
- secp256k1_scalar_get_b32(ptr, key); ptr += 32;
- memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
- if (!secp256k1_eckey_pubkey_serialize(&r, ptr, &pubkeylen, 1)) {
- return 0;
- }
- ptr += pubkeylen;
- *privkeylen = ptr - privkey;
- } else {
- static const unsigned char begin[] = {
- 0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20
- };
- static const unsigned char middle[] = {
- 0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
- 0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04,
- 0x41,0x04,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87,
- 0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8,
- 0x17,0x98,0x48,0x3A,0xDA,0x77,0x26,0xA3,0xC4,0x65,0x5D,0xA4,0xFB,0xFC,0x0E,0x11,
- 0x08,0xA8,0xFD,0x17,0xB4,0x48,0xA6,0x85,0x54,0x19,0x9C,0x47,0xD0,0x8F,0xFB,0x10,
- 0xD4,0xB8,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
- 0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,0x00
- };
- unsigned char *ptr = privkey;
- memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
- secp256k1_scalar_get_b32(ptr, key); ptr += 32;
- memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
- if (!secp256k1_eckey_pubkey_serialize(&r, ptr, &pubkeylen, 0)) {
- return 0;
- }
- ptr += pubkeylen;
- *privkeylen = ptr - privkey;
- }
- return 1;
-}
-
static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak) {
secp256k1_scalar_add(key, key, tweak);
if (secp256k1_scalar_is_zero(key)) {
diff --git a/crypto/secp256k1/libsecp256k1/src/ecmult_const_impl.h b/crypto/secp256k1/libsecp256k1/src/ecmult_const_impl.h
index 90ac94770e..0db314c48e 100644
--- a/crypto/secp256k1/libsecp256k1/src/ecmult_const_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/ecmult_const_impl.h
@@ -58,25 +58,27 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
int global_sign;
int skew = 0;
int word = 0;
+
/* 1 2 3 */
int u_last;
int u;
-#ifdef USE_ENDOMORPHISM
int flip;
int bit;
secp256k1_scalar neg_s;
int not_neg_one;
- /* If we are using the endomorphism, we cannot handle even numbers by negating
- * them, since we are working with 128-bit numbers whose negations would be 256
- * bits, eliminating the performance advantage. Instead we use a technique from
+ /* Note that we cannot handle even numbers by negating them to be odd, as is
+ * done in other implementations, since if our scalars were specified to have
+ * width < 256 for performance reasons, their negations would have width 256
+ * and we'd lose any performance benefit. Instead, we use a technique from
* Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even)
- * or 2 (for odd) to the number we are encoding, then compensating after the
- * multiplication. */
- /* Negative 128-bit numbers will be negated, since otherwise they are 256-bit */
+ * or 2 (for odd) to the number we are encoding, returning a skew value indicating
+ * this, and having the caller compensate after doing the multiplication. */
+
+ /* Negative numbers will be negated to keep their bit representation below the maximum width */
flip = secp256k1_scalar_is_high(&s);
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
- bit = flip ^ (s.d[0] & 1);
+ bit = flip ^ !secp256k1_scalar_is_even(&s);
/* We check for negative one, since adding 2 to it will cause an overflow */
secp256k1_scalar_negate(&neg_s, &s);
not_neg_one = !secp256k1_scalar_is_one(&neg_s);
@@ -89,11 +91,6 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
global_sign = secp256k1_scalar_cond_negate(&s, flip);
global_sign *= not_neg_one * 2 - 1;
skew = 1 << bit;
-#else
- /* Otherwise, we just negate to force oddness */
- int is_even = secp256k1_scalar_is_even(&s);
- global_sign = secp256k1_scalar_cond_negate(&s, is_even);
-#endif
/* 4 */
u_last = secp256k1_scalar_shr_int(&s, w);
@@ -127,15 +124,13 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
secp256k1_ge tmpa;
secp256k1_fe Z;
+ int skew_1;
+ int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
#ifdef USE_ENDOMORPHISM
secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
- int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)];
- int skew_1;
int skew_lam;
secp256k1_scalar q_1, q_lam;
-#else
- int wnaf[1 + WNAF_SIZE(WINDOW_A - 1)];
#endif
int i;
@@ -145,18 +140,10 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
#ifdef USE_ENDOMORPHISM
/* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */
secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc);
- /* no need for zero correction when using endomorphism since even
- * numbers have one added to them anyway */
skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1);
skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1);
#else
- int is_zero = secp256k1_scalar_is_zero(scalar);
- /* the wNAF ladder cannot handle zero, so bump this to one .. we will
- * correct the result after the fact */
- sc.d[0] += is_zero;
- VERIFY_CHECK(!secp256k1_scalar_is_zero(&sc));
-
- secp256k1_wnaf_const(wnaf, sc, WINDOW_A - 1);
+ skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1);
#endif
/* Calculate odd multiples of a.
@@ -179,21 +166,15 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
/* first loop iteration (separated out so we can directly set r, rather
* than having it start at infinity, get doubled several times, then have
* its new value added to it) */
-#ifdef USE_ENDOMORPHISM
i = wnaf_1[WNAF_SIZE(WINDOW_A - 1)];
VERIFY_CHECK(i != 0);
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A);
secp256k1_gej_set_ge(r, &tmpa);
-
+#ifdef USE_ENDOMORPHISM
i = wnaf_lam[WNAF_SIZE(WINDOW_A - 1)];
VERIFY_CHECK(i != 0);
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A);
secp256k1_gej_add_ge(r, r, &tmpa);
-#else
- i = wnaf[WNAF_SIZE(WINDOW_A - 1)];
- VERIFY_CHECK(i != 0);
- ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A);
- secp256k1_gej_set_ge(r, &tmpa);
#endif
/* remaining loop iterations */
for (i = WNAF_SIZE(WINDOW_A - 1) - 1; i >= 0; i--) {
@@ -202,59 +183,57 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
for (j = 0; j < WINDOW_A - 1; ++j) {
secp256k1_gej_double_nonzero(r, r, NULL);
}
-#ifdef USE_ENDOMORPHISM
+
n = wnaf_1[i];
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A);
VERIFY_CHECK(n != 0);
secp256k1_gej_add_ge(r, r, &tmpa);
-
+#ifdef USE_ENDOMORPHISM
n = wnaf_lam[i];
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
VERIFY_CHECK(n != 0);
secp256k1_gej_add_ge(r, r, &tmpa);
-#else
- n = wnaf[i];
- VERIFY_CHECK(n != 0);
- ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A);
- secp256k1_gej_add_ge(r, r, &tmpa);
#endif
}
secp256k1_fe_mul(&r->z, &r->z, &Z);
-#ifdef USE_ENDOMORPHISM
{
/* Correct for wNAF skew */
secp256k1_ge correction = *a;
secp256k1_ge_storage correction_1_stor;
+#ifdef USE_ENDOMORPHISM
secp256k1_ge_storage correction_lam_stor;
+#endif
secp256k1_ge_storage a2_stor;
secp256k1_gej tmpj;
secp256k1_gej_set_ge(&tmpj, &correction);
secp256k1_gej_double_var(&tmpj, &tmpj, NULL);
secp256k1_ge_set_gej(&correction, &tmpj);
secp256k1_ge_to_storage(&correction_1_stor, a);
+#ifdef USE_ENDOMORPHISM
secp256k1_ge_to_storage(&correction_lam_stor, a);
+#endif
secp256k1_ge_to_storage(&a2_stor, &correction);
/* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */
secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2);
+#ifdef USE_ENDOMORPHISM
secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2);
+#endif
/* Apply the correction */
secp256k1_ge_from_storage(&correction, &correction_1_stor);
secp256k1_ge_neg(&correction, &correction);
secp256k1_gej_add_ge(r, r, &correction);
+#ifdef USE_ENDOMORPHISM
secp256k1_ge_from_storage(&correction, &correction_lam_stor);
secp256k1_ge_neg(&correction, &correction);
secp256k1_ge_mul_lambda(&correction, &correction);
secp256k1_gej_add_ge(r, r, &correction);
- }
-#else
- /* correct for zero */
- r->infinity |= is_zero;
#endif
+ }
}
#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/ecmult_gen_impl.h b/crypto/secp256k1/libsecp256k1/src/ecmult_gen_impl.h
index 2ee27377f1..35f2546077 100644
--- a/crypto/secp256k1/libsecp256k1/src/ecmult_gen_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/ecmult_gen_impl.h
@@ -40,8 +40,13 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
static const unsigned char nums_b32[33] = "The scalar for this x is unknown";
secp256k1_fe nums_x;
secp256k1_ge nums_ge;
- VERIFY_CHECK(secp256k1_fe_set_b32(&nums_x, nums_b32));
- VERIFY_CHECK(secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0));
+ int r;
+ r = secp256k1_fe_set_b32(&nums_x, nums_b32);
+ (void)r;
+ VERIFY_CHECK(r);
+ r = secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0);
+ (void)r;
+ VERIFY_CHECK(r);
secp256k1_gej_set_ge(&nums_gej, &nums_ge);
/* Add G to make the bits in x uniformly distributed. */
secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, &secp256k1_ge_const_g, NULL);
@@ -72,7 +77,7 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL);
}
}
- secp256k1_ge_set_all_gej_var(1024, prec, precj, cb);
+ secp256k1_ge_set_all_gej_var(prec, precj, 1024, cb);
}
for (j = 0; j < 64; j++) {
for (i = 0; i < 16; i++) {
@@ -182,7 +187,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
retry = !secp256k1_fe_set_b32(&s, nonce32);
retry |= secp256k1_fe_is_zero(&s);
- } while (retry);
+ } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */
/* Randomize the projection to defend against multiplier sidechannels. */
secp256k1_gej_rescale(&ctx->initial, &s);
secp256k1_fe_clear(&s);
@@ -191,7 +196,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
secp256k1_scalar_set_b32(&b, nonce32, &retry);
/* A blinding value of 0 works, but would undermine the projection hardening. */
retry |= secp256k1_scalar_is_zero(&b);
- } while (retry);
+ } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
memset(nonce32, 0, 32);
secp256k1_ecmult_gen(ctx, &gb, &b);
diff --git a/crypto/secp256k1/libsecp256k1/src/ecmult_impl.h b/crypto/secp256k1/libsecp256k1/src/ecmult_impl.h
index e6e5f47188..4e40104ad4 100644
--- a/crypto/secp256k1/libsecp256k1/src/ecmult_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/ecmult_impl.h
@@ -7,13 +7,29 @@
#ifndef _SECP256K1_ECMULT_IMPL_H_
#define _SECP256K1_ECMULT_IMPL_H_
+#include
+
#include "group.h"
#include "scalar.h"
#include "ecmult.h"
+#if defined(EXHAUSTIVE_TEST_ORDER)
+/* We need to lower these values for exhaustive tests because
+ * the tables cannot have infinities in them (this breaks the
+ * affine-isomorphism stuff which tracks z-ratios) */
+# if EXHAUSTIVE_TEST_ORDER > 128
+# define WINDOW_A 5
+# define WINDOW_G 8
+# elif EXHAUSTIVE_TEST_ORDER > 8
+# define WINDOW_A 4
+# define WINDOW_G 4
+# else
+# define WINDOW_A 2
+# define WINDOW_G 2
+# endif
+#else
/* optimal for 128-bit and 256-bit exponents. */
#define WINDOW_A 5
-
/** larger numbers may result in slightly better performance, at the cost of
exponentially larger precomputed tables. */
#ifdef USE_ENDOMORPHISM
@@ -23,6 +39,7 @@
/** One table for window size 16: 1.375 MiB. */
#define WINDOW_G 16
#endif
+#endif
/** The number of entries a table with precomputed multiples needs to have. */
#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2))
@@ -101,7 +118,7 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge
/* Compute the odd multiples in Jacobian form. */
secp256k1_ecmult_odd_multiples_table(n, prej, zr, a);
/* Convert them in batch to affine coordinates. */
- secp256k1_ge_set_table_gej_var(n, prea, prej, zr);
+ secp256k1_ge_set_table_gej_var(prea, prej, zr, n);
/* Convert them to compact storage form. */
for (i = 0; i < n; i++) {
secp256k1_ge_to_storage(&pre[i], &prea[i]);
diff --git a/crypto/secp256k1/libsecp256k1/src/field.h b/crypto/secp256k1/libsecp256k1/src/field.h
index 311329b927..bbb1ee866c 100644
--- a/crypto/secp256k1/libsecp256k1/src/field.h
+++ b/crypto/secp256k1/libsecp256k1/src/field.h
@@ -10,7 +10,7 @@
/** Field element module.
*
* Field elements can be represented in several ways, but code accessing
- * it (and implementations) need to take certain properaties into account:
+ * it (and implementations) need to take certain properties into account:
* - Each field element can be normalized or not.
* - Each field element has a magnitude, which represents how far away
* its representation is away from normalization. Normalized elements
@@ -30,6 +30,8 @@
#error "Please select field implementation"
#endif
+#include "util.h"
+
/** Normalize a field element. */
static void secp256k1_fe_normalize(secp256k1_fe *r);
@@ -50,6 +52,9 @@ static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r);
/** Set a field element equal to a small integer. Resulting field element is normalized. */
static void secp256k1_fe_set_int(secp256k1_fe *r, int a);
+/** Sets a field element equal to zero, initializing all fields. */
+static void secp256k1_fe_clear(secp256k1_fe *a);
+
/** Verify whether a field element is zero. Requires the input to be normalized. */
static int secp256k1_fe_is_zero(const secp256k1_fe *a);
@@ -57,6 +62,9 @@ static int secp256k1_fe_is_zero(const secp256k1_fe *a);
static int secp256k1_fe_is_odd(const secp256k1_fe *a);
/** Compare two field elements. Requires magnitude-1 inputs. */
+static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b);
+
+/** Same as secp256k1_fe_equal, but may be variable time. */
static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b);
/** Compare two field elements. Requires both inputs to be normalized */
@@ -87,10 +95,15 @@ static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp2
* The output magnitude is 1 (but not guaranteed to be normalized). */
static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a);
-/** Sets a field element to be the (modular) square root (if any exist) of another. Requires the
- * input's magnitude to be at most 8. The output magnitude is 1 (but not guaranteed to be
- * normalized). Return value indicates whether a square root was found. */
-static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a);
+/** If a has a square root, it is computed in r and 1 is returned. If a does not
+ * have a square root, the root of its negation is computed and 0 is returned.
+ * The input's magnitude can be at most 8. The output magnitude is 1 (but not
+ * guaranteed to be normalized). The result in r will always be a square
+ * itself. */
+static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a);
+
+/** Checks whether a field element is a quadratic residue. */
+static int secp256k1_fe_is_quad_var(const secp256k1_fe *a);
/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be
* at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */
@@ -102,7 +115,7 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a);
/** Calculate the (modular) inverses of a batch of field elements. Requires the inputs' magnitudes to be
* at most 8. The output magnitudes are 1 (but not guaranteed to be normalized). The inputs and
* outputs must not overlap in memory. */
-static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe *r, const secp256k1_fe *a);
+static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len);
/** Convert a field element to the storage type. */
static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a);
diff --git a/crypto/secp256k1/libsecp256k1/src/field_10x26_impl.h b/crypto/secp256k1/libsecp256k1/src/field_10x26_impl.h
index 212cc5396a..5fb092f1be 100644
--- a/crypto/secp256k1/libsecp256k1/src/field_10x26_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/field_10x26_impl.h
@@ -7,8 +7,6 @@
#ifndef _SECP256K1_FIELD_REPR_IMPL_H_
#define _SECP256K1_FIELD_REPR_IMPL_H_
-#include
-#include
#include "util.h"
#include "num.h"
#include "field.h"
@@ -40,10 +38,6 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) {
}
VERIFY_CHECK(r == 1);
}
-#else
-static void secp256k1_fe_verify(const secp256k1_fe *a) {
- (void)a;
-}
#endif
static void secp256k1_fe_normalize(secp256k1_fe *r) {
@@ -429,6 +423,14 @@ SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_f
#endif
}
+#if defined(USE_EXTERNAL_ASM)
+
+/* External assembler implementation */
+void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b);
+void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a);
+
+#else
+
#ifdef VERIFY
#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0)
#else
@@ -1037,7 +1039,7 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t
VERIFY_BITS(r[2], 27);
/* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
}
-
+#endif
static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) {
#ifdef VERIFY
diff --git a/crypto/secp256k1/libsecp256k1/src/field_5x52_impl.h b/crypto/secp256k1/libsecp256k1/src/field_5x52_impl.h
index b31e24ab81..dd88f38c77 100644
--- a/crypto/secp256k1/libsecp256k1/src/field_5x52_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/field_5x52_impl.h
@@ -11,7 +11,6 @@
#include "libsecp256k1-config.h"
#endif
-#include
#include "util.h"
#include "num.h"
#include "field.h"
@@ -50,10 +49,6 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) {
}
VERIFY_CHECK(r == 1);
}
-#else
-static void secp256k1_fe_verify(const secp256k1_fe *a) {
- (void)a;
-}
#endif
static void secp256k1_fe_normalize(secp256k1_fe *r) {
diff --git a/crypto/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h b/crypto/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h
index 9280bb5ea2..0bf22bdd3e 100644
--- a/crypto/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h
@@ -137,7 +137,7 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t
VERIFY_BITS(r[2], 52);
VERIFY_BITS(c, 63);
/* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
- c += d * R + t3;;
+ c += d * R + t3;
VERIFY_BITS(c, 100);
/* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
r[3] = c & M; c >>= 52;
@@ -259,7 +259,7 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t
VERIFY_BITS(c, 63);
/* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
- c += d * R + t3;;
+ c += d * R + t3;
VERIFY_BITS(c, 100);
/* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
r[3] = c & M; c >>= 52;
diff --git a/crypto/secp256k1/libsecp256k1/src/field_impl.h b/crypto/secp256k1/libsecp256k1/src/field_impl.h
index 551a6243e2..5127b279bc 100644
--- a/crypto/secp256k1/libsecp256k1/src/field_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/field_impl.h
@@ -21,6 +21,13 @@
#error "Please select field implementation"
#endif
+SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
+ secp256k1_fe na;
+ secp256k1_fe_negate(&na, a, 1);
+ secp256k1_fe_add(&na, b);
+ return secp256k1_fe_normalizes_to_zero(&na);
+}
+
SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b) {
secp256k1_fe na;
secp256k1_fe_negate(&na, a, 1);
@@ -28,7 +35,16 @@ SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const
return secp256k1_fe_normalizes_to_zero_var(&na);
}
-static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a) {
+static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) {
+ /** Given that p is congruent to 3 mod 4, we can compute the square root of
+ * a mod p as the (p+1)/4'th power of a.
+ *
+ * As (p+1)/4 is an even number, it will have the same result for a and for
+ * (-a). Only one of these two numbers actually has a square root however,
+ * so we test at the end by squaring and comparing to the input.
+ * Also because (p+1)/4 is an even number, the computed square root is
+ * itself always a square (a ** ((p+1)/4) is the square of a ** ((p+1)/8)).
+ */
secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1;
int j;
@@ -114,7 +130,7 @@ static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a) {
/* Check that a square root was actually calculated */
secp256k1_fe_sqr(&t1, r);
- return secp256k1_fe_equal_var(&t1, a);
+ return secp256k1_fe_equal(&t1, a);
}
static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a) {
@@ -224,6 +240,7 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) {
0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F
};
unsigned char b[32];
+ int res;
secp256k1_fe c = *a;
secp256k1_fe_normalize_var(&c);
secp256k1_fe_get_b32(b, &c);
@@ -231,7 +248,9 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) {
secp256k1_num_set_bin(&m, prime, 32);
secp256k1_num_mod_inverse(&n, &n, &m);
secp256k1_num_get_bin(b, 32, &n);
- VERIFY_CHECK(secp256k1_fe_set_b32(r, b));
+ res = secp256k1_fe_set_b32(r, b);
+ (void)res;
+ VERIFY_CHECK(res);
/* Verify the result is the (unique) valid inverse using non-GMP code. */
secp256k1_fe_mul(&c, &c, r);
secp256k1_fe_add(&c, &negone);
@@ -241,7 +260,7 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) {
#endif
}
-static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe *r, const secp256k1_fe *a) {
+static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len) {
secp256k1_fe u;
size_t i;
if (len < 1) {
@@ -268,4 +287,29 @@ static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe *r, const secp256k
r[0] = u;
}
+static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) {
+#ifndef USE_NUM_NONE
+ unsigned char b[32];
+ secp256k1_num n;
+ secp256k1_num m;
+ /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */
+ static const unsigned char prime[32] = {
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F
+ };
+
+ secp256k1_fe c = *a;
+ secp256k1_fe_normalize_var(&c);
+ secp256k1_fe_get_b32(b, &c);
+ secp256k1_num_set_bin(&n, b, 32);
+ secp256k1_num_set_bin(&m, prime, 32);
+ return secp256k1_num_jacobi(&n, &m) >= 0;
+#else
+ secp256k1_fe r;
+ return secp256k1_fe_sqrt(&r, a);
+#endif
+}
+
#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/group.h b/crypto/secp256k1/libsecp256k1/src/group.h
index 89b079d5c6..4957b248fe 100644
--- a/crypto/secp256k1/libsecp256k1/src/group.h
+++ b/crypto/secp256k1/libsecp256k1/src/group.h
@@ -40,12 +40,15 @@ typedef struct {
#define SECP256K1_GE_STORAGE_CONST_GET(t) SECP256K1_FE_STORAGE_CONST_GET(t.x), SECP256K1_FE_STORAGE_CONST_GET(t.y)
-/** Set a group element equal to the point at infinity */
-static void secp256k1_ge_set_infinity(secp256k1_ge *r);
-
/** Set a group element equal to the point with given X and Y coordinates */
static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y);
+/** Set a group element (affine) equal to the point with the given X coordinate
+ * and a Y coordinate that is a quadratic residue modulo p. The return value
+ * is true iff a coordinate with the given X coordinate exists.
+ */
+static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x);
+
/** Set a group element (affine) equal to the point with the given X coordinate, and given oddness
* for Y. Return value indicates whether the result is valid. */
static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd);
@@ -62,12 +65,12 @@ static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a);
static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a);
/** Set a batch of group elements equal to the inputs given in jacobian coordinates */
-static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_callback *cb);
+static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb);
/** Set a batch of group elements equal to the inputs given in jacobian
* coordinates (with known z-ratios). zr must contain the known z-ratios such
* that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */
-static void secp256k1_ge_set_table_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr);
+static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len);
/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to
* the same global z "denominator". zr must contain the known z-ratios such
@@ -79,9 +82,6 @@ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp
/** Set a group element (jacobian) equal to the point at infinity. */
static void secp256k1_gej_set_infinity(secp256k1_gej *r);
-/** Set a group element (jacobian) equal to the point with given X and Y coordinates. */
-static void secp256k1_gej_set_xy(secp256k1_gej *r, const secp256k1_fe *x, const secp256k1_fe *y);
-
/** Set a group element (jacobian) equal to another which is given in affine coordinates. */
static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a);
@@ -94,6 +94,9 @@ static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a);
/** Check whether a group element is the point at infinity. */
static int secp256k1_gej_is_infinity(const secp256k1_gej *a);
+/** Check whether a group element's y coordinate is a quadratic residue. */
+static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a);
+
/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0).
* a may not be zero. Constant time. */
static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);
diff --git a/crypto/secp256k1/libsecp256k1/src/group_impl.h b/crypto/secp256k1/libsecp256k1/src/group_impl.h
index fe0a35929c..7d723532ff 100644
--- a/crypto/secp256k1/libsecp256k1/src/group_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/group_impl.h
@@ -7,12 +7,57 @@
#ifndef _SECP256K1_GROUP_IMPL_H_
#define _SECP256K1_GROUP_IMPL_H_
-#include
-
#include "num.h"
#include "field.h"
#include "group.h"
+/* These points can be generated in sage as follows:
+ *
+ * 0. Setup a worksheet with the following parameters.
+ * b = 4 # whatever CURVE_B will be set to
+ * F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F)
+ * C = EllipticCurve ([F (0), F (b)])
+ *
+ * 1. Determine all the small orders available to you. (If there are
+ * no satisfactory ones, go back and change b.)
+ * print C.order().factor(limit=1000)
+ *
+ * 2. Choose an order as one of the prime factors listed in the above step.
+ * (You can also multiply some to get a composite order, though the
+ * tests will crash trying to invert scalars during signing.) We take a
+ * random point and scale it to drop its order to the desired value.
+ * There is some probability this won't work; just try again.
+ * order = 199
+ * P = C.random_point()
+ * P = (int(P.order()) / int(order)) * P
+ * assert(P.order() == order)
+ *
+ * 3. Print the values. You'll need to use a vim macro or something to
+ * split the hex output into 4-byte chunks.
+ * print "%x %x" % P.xy()
+ */
+#if defined(EXHAUSTIVE_TEST_ORDER)
+# if EXHAUSTIVE_TEST_ORDER == 199
+const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
+ 0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069,
+ 0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18,
+ 0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868,
+ 0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED
+);
+
+const int CURVE_B = 4;
+# elif EXHAUSTIVE_TEST_ORDER == 13
+const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
+ 0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0,
+ 0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15,
+ 0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e,
+ 0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac
+);
+const int CURVE_B = 2;
+# else
+# error No known generator for the specified exhaustive test group order.
+# endif
+#else
/** Generator for secp256k1, value 'g' defined in
* "Standards for Efficient Cryptography" (SEC2) 2.7.1.
*/
@@ -23,8 +68,11 @@ static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL
);
+const int CURVE_B = 7;
+#endif
+
static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
- secp256k1_fe zi2;
+ secp256k1_fe zi2;
secp256k1_fe zi3;
secp256k1_fe_sqr(&zi2, zi);
secp256k1_fe_mul(&zi3, &zi2, zi);
@@ -33,10 +81,6 @@ static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, c
r->infinity = a->infinity;
}
-static void secp256k1_ge_set_infinity(secp256k1_ge *r) {
- r->infinity = 1;
-}
-
static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y) {
r->infinity = 0;
r->x = *x;
@@ -82,7 +126,7 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) {
r->y = a->y;
}
-static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_callback *cb) {
+static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb) {
secp256k1_fe *az;
secp256k1_fe *azi;
size_t i;
@@ -95,7 +139,7 @@ static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp
}
azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count);
- secp256k1_fe_inv_all_var(count, azi, az);
+ secp256k1_fe_inv_all_var(azi, az, count);
free(az);
count = 0;
@@ -108,7 +152,7 @@ static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp
free(azi);
}
-static void secp256k1_ge_set_table_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr) {
+static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len) {
size_t i = len - 1;
secp256k1_fe zi;
@@ -151,16 +195,9 @@ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp
static void secp256k1_gej_set_infinity(secp256k1_gej *r) {
r->infinity = 1;
- secp256k1_fe_set_int(&r->x, 0);
- secp256k1_fe_set_int(&r->y, 0);
- secp256k1_fe_set_int(&r->z, 0);
-}
-
-static void secp256k1_gej_set_xy(secp256k1_gej *r, const secp256k1_fe *x, const secp256k1_fe *y) {
- r->infinity = 0;
- r->x = *x;
- r->y = *y;
- secp256k1_fe_set_int(&r->z, 1);
+ secp256k1_fe_clear(&r->x);
+ secp256k1_fe_clear(&r->y);
+ secp256k1_fe_clear(&r->z);
}
static void secp256k1_gej_clear(secp256k1_gej *r) {
@@ -176,15 +213,19 @@ static void secp256k1_ge_clear(secp256k1_ge *r) {
secp256k1_fe_clear(&r->y);
}
-static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
+static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) {
secp256k1_fe x2, x3, c;
r->x = *x;
secp256k1_fe_sqr(&x2, x);
secp256k1_fe_mul(&x3, x, &x2);
r->infinity = 0;
- secp256k1_fe_set_int(&c, 7);
+ secp256k1_fe_set_int(&c, CURVE_B);
secp256k1_fe_add(&c, &x3);
- if (!secp256k1_fe_sqrt_var(&r->y, &c)) {
+ return secp256k1_fe_sqrt(&r->y, &c);
+}
+
+static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
+ if (!secp256k1_ge_set_xquad(r, x)) {
return 0;
}
secp256k1_fe_normalize_var(&r->y);
@@ -192,6 +233,7 @@ static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int o
secp256k1_fe_negate(&r->y, &r->y, 1);
}
return 1;
+
}
static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a) {
@@ -236,7 +278,7 @@ static int secp256k1_gej_is_valid_var(const secp256k1_gej *a) {
secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x);
secp256k1_fe_sqr(&z2, &a->z);
secp256k1_fe_sqr(&z6, &z2); secp256k1_fe_mul(&z6, &z6, &z2);
- secp256k1_fe_mul_int(&z6, 7);
+ secp256k1_fe_mul_int(&z6, CURVE_B);
secp256k1_fe_add(&x3, &z6);
secp256k1_fe_normalize_weak(&x3);
return secp256k1_fe_equal_var(&y2, &x3);
@@ -250,18 +292,30 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) {
/* y^2 = x^3 + 7 */
secp256k1_fe_sqr(&y2, &a->y);
secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x);
- secp256k1_fe_set_int(&c, 7);
+ secp256k1_fe_set_int(&c, CURVE_B);
secp256k1_fe_add(&x3, &c);
secp256k1_fe_normalize_weak(&x3);
return secp256k1_fe_equal_var(&y2, &x3);
}
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
- /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate */
+ /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate.
+ *
+ * Note that there is an implementation described at
+ * https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
+ * which trades a multiply for a square, but in practice this is actually slower,
+ * mainly because it requires more normalizations.
+ */
secp256k1_fe t1,t2,t3,t4;
/** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity,
* Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have
* y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p.
+ *
+ * Having said this, if this function receives a point on a sextic twist, e.g. by
+ * a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6,
+ * since -6 does have a cube root mod p. For this point, this function will not set
+ * the infinity flag even though the point doubles to infinity, and the result
+ * point will be gibberish (z = 0 but infinity = 0).
*/
r->infinity = a->infinity;
if (r->infinity) {
@@ -629,4 +683,18 @@ static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) {
}
#endif
+static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) {
+ secp256k1_fe yz;
+
+ if (a->infinity) {
+ return 0;
+ }
+
+ /* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as
+ * that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z
+ is */
+ secp256k1_fe_mul(&yz, &a->y, &a->z);
+ return secp256k1_fe_is_quad_var(&yz);
+}
+
#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/hash.h b/crypto/secp256k1/libsecp256k1/src/hash.h
index 0ff01e63fa..fca98cab9f 100644
--- a/crypto/secp256k1/libsecp256k1/src/hash.h
+++ b/crypto/secp256k1/libsecp256k1/src/hash.h
@@ -11,7 +11,7 @@
#include
typedef struct {
- uint32_t s[32];
+ uint32_t s[8];
uint32_t buf[16]; /* In big endian */
size_t bytes;
} secp256k1_sha256_t;
diff --git a/crypto/secp256k1/libsecp256k1/src/hash_impl.h b/crypto/secp256k1/libsecp256k1/src/hash_impl.h
index ae55df6d8a..b47e65f830 100644
--- a/crypto/secp256k1/libsecp256k1/src/hash_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/hash_impl.h
@@ -269,15 +269,13 @@ static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256
rng->retry = 0;
}
-
+#undef BE32
#undef Round
-#undef sigma0
#undef sigma1
-#undef Sigma0
+#undef sigma0
#undef Sigma1
-#undef Ch
+#undef Sigma0
#undef Maj
-#undef ReadBE32
-#undef WriteBE32
+#undef Ch
#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java
index 90a498eaa2..1c67802fba 100644
--- a/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java
+++ b/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java
@@ -1,60 +1,446 @@
+/*
+ * Copyright 2013 Google Inc.
+ * Copyright 2014-2016 the libsecp256k1 contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package org.bitcoin;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.math.BigInteger;
import com.google.common.base.Preconditions;
-
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import static org.bitcoin.NativeSecp256k1Util.*;
/**
- * This class holds native methods to handle ECDSA verification.
- * You can find an example library that can be used for this at
- * https://github.com/sipa/secp256k1
+ *
This class holds native methods to handle ECDSA verification.
+ *
+ *
You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1
+ *
+ *
To build secp256k1 for use with bitcoinj, run
+ * `./configure --enable-jni --enable-experimental --enable-module-ecdh`
+ * and `make` then copy `.libs/libsecp256k1.so` to your system library path
+ * or point the JVM to the folder containing it with -Djava.library.path
+ *
*/
public class NativeSecp256k1 {
- public static final boolean enabled;
- static {
- boolean isEnabled = true;
- try {
- System.loadLibrary("javasecp256k1");
- } catch (UnsatisfiedLinkError e) {
- isEnabled = false;
- }
- enabled = isEnabled;
- }
-
+
+ private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+ private static final Lock r = rwl.readLock();
+ private static final Lock w = rwl.writeLock();
private static ThreadLocal nativeECDSABuffer = new ThreadLocal();
/**
* Verifies the given secp256k1 signature in native code.
* Calling when enabled == false is undefined (probably library not loaded)
- *
+ *
* @param data The data which was signed, must be exactly 32 bytes
* @param signature The signature
* @param pub The public key which did the signing
*/
- public static boolean verify(byte[] data, byte[] signature, byte[] pub) {
+ public static boolean verify(byte[] data, byte[] signature, byte[] pub) throws AssertFailException{
Preconditions.checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520);
ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null) {
- byteBuff = ByteBuffer.allocateDirect(32 + 8 + 520 + 520);
+ if (byteBuff == null || byteBuff.capacity() < 520) {
+ byteBuff = ByteBuffer.allocateDirect(520);
byteBuff.order(ByteOrder.nativeOrder());
nativeECDSABuffer.set(byteBuff);
}
byteBuff.rewind();
byteBuff.put(data);
- byteBuff.putInt(signature.length);
- byteBuff.putInt(pub.length);
byteBuff.put(signature);
byteBuff.put(pub);
- return secp256k1_ecdsa_verify(byteBuff) == 1;
+
+ byte[][] retByteArray;
+
+ r.lock();
+ try {
+ return secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(), signature.length, pub.length) == 1;
+ } finally {
+ r.unlock();
+ }
}
/**
- * @param byteBuff signature format is byte[32] data,
- * native-endian int signatureLength, native-endian int pubkeyLength,
- * byte[signatureLength] signature, byte[pubkeyLength] pub
- * @returns 1 for valid signature, anything else for invalid
+ * libsecp256k1 Create an ECDSA signature.
+ *
+ * @param data Message hash, 32 bytes
+ * @param key Secret key, 32 bytes
+ *
+ * Return values
+ * @param sig byte array of signature
*/
- private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff);
+ public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{
+ Preconditions.checkArgument(data.length == 32 && sec.length <= 32);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < 32 + 32) {
+ byteBuff = ByteBuffer.allocateDirect(32 + 32);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(data);
+ byteBuff.put(sec);
+
+ byte[][] retByteArray;
+
+ r.lock();
+ try {
+ retByteArray = secp256k1_ecdsa_sign(byteBuff, Secp256k1Context.getContext());
+ } finally {
+ r.unlock();
+ }
+
+ byte[] sigArr = retByteArray[0];
+ int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
+ int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
+
+ assertEquals(sigArr.length, sigLen, "Got bad signature length.");
+
+ return retVal == 0 ? new byte[0] : sigArr;
+ }
+
+ /**
+ * libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid
+ *
+ * @param seckey ECDSA Secret key, 32 bytes
+ */
+ public static boolean secKeyVerify(byte[] seckey) {
+ Preconditions.checkArgument(seckey.length == 32);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < seckey.length) {
+ byteBuff = ByteBuffer.allocateDirect(seckey.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(seckey);
+
+ r.lock();
+ try {
+ return secp256k1_ec_seckey_verify(byteBuff,Secp256k1Context.getContext()) == 1;
+ } finally {
+ r.unlock();
+ }
+ }
+
+
+ /**
+ * libsecp256k1 Compute Pubkey - computes public key from secret key
+ *
+ * @param seckey ECDSA Secret key, 32 bytes
+ *
+ * Return values
+ * @param pubkey ECDSA Public key, 33 or 65 bytes
+ */
+ //TODO add a 'compressed' arg
+ public static byte[] computePubkey(byte[] seckey) throws AssertFailException{
+ Preconditions.checkArgument(seckey.length == 32);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < seckey.length) {
+ byteBuff = ByteBuffer.allocateDirect(seckey.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(seckey);
+
+ byte[][] retByteArray;
+
+ r.lock();
+ try {
+ retByteArray = secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext());
+ } finally {
+ r.unlock();
+ }
+
+ byte[] pubArr = retByteArray[0];
+ int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
+ int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
+
+ assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
+
+ return retVal == 0 ? new byte[0]: pubArr;
+ }
+
+ /**
+ * libsecp256k1 Cleanup - This destroys the secp256k1 context object
+ * This should be called at the end of the program for proper cleanup of the context.
+ */
+ public static synchronized void cleanup() {
+ w.lock();
+ try {
+ secp256k1_destroy_context(Secp256k1Context.getContext());
+ } finally {
+ w.unlock();
+ }
+ }
+
+ public static long cloneContext() {
+ r.lock();
+ try {
+ return secp256k1_ctx_clone(Secp256k1Context.getContext());
+ } finally { r.unlock(); }
+ }
+
+ /**
+ * libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it
+ *
+ * @param tweak some bytes to tweak with
+ * @param seckey 32-byte seckey
+ */
+ public static byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{
+ Preconditions.checkArgument(privkey.length == 32);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) {
+ byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(privkey);
+ byteBuff.put(tweak);
+
+ byte[][] retByteArray;
+ r.lock();
+ try {
+ retByteArray = secp256k1_privkey_tweak_mul(byteBuff,Secp256k1Context.getContext());
+ } finally {
+ r.unlock();
+ }
+
+ byte[] privArr = retByteArray[0];
+
+ int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
+ int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
+
+ assertEquals(privArr.length, privLen, "Got bad pubkey length.");
+
+ assertEquals(retVal, 1, "Failed return value check.");
+
+ return privArr;
+ }
+
+ /**
+ * libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it
+ *
+ * @param tweak some bytes to tweak with
+ * @param seckey 32-byte seckey
+ */
+ public static byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{
+ Preconditions.checkArgument(privkey.length == 32);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) {
+ byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(privkey);
+ byteBuff.put(tweak);
+
+ byte[][] retByteArray;
+ r.lock();
+ try {
+ retByteArray = secp256k1_privkey_tweak_add(byteBuff,Secp256k1Context.getContext());
+ } finally {
+ r.unlock();
+ }
+
+ byte[] privArr = retByteArray[0];
+
+ int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
+ int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
+
+ assertEquals(privArr.length, privLen, "Got bad pubkey length.");
+
+ assertEquals(retVal, 1, "Failed return value check.");
+
+ return privArr;
+ }
+
+ /**
+ * libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it
+ *
+ * @param tweak some bytes to tweak with
+ * @param pubkey 32-byte seckey
+ */
+ public static byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak) throws AssertFailException{
+ Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) {
+ byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(pubkey);
+ byteBuff.put(tweak);
+
+ byte[][] retByteArray;
+ r.lock();
+ try {
+ retByteArray = secp256k1_pubkey_tweak_add(byteBuff,Secp256k1Context.getContext(), pubkey.length);
+ } finally {
+ r.unlock();
+ }
+
+ byte[] pubArr = retByteArray[0];
+
+ int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
+ int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
+
+ assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
+
+ assertEquals(retVal, 1, "Failed return value check.");
+
+ return pubArr;
+ }
+
+ /**
+ * libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it
+ *
+ * @param tweak some bytes to tweak with
+ * @param pubkey 32-byte seckey
+ */
+ public static byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak) throws AssertFailException{
+ Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) {
+ byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(pubkey);
+ byteBuff.put(tweak);
+
+ byte[][] retByteArray;
+ r.lock();
+ try {
+ retByteArray = secp256k1_pubkey_tweak_mul(byteBuff,Secp256k1Context.getContext(), pubkey.length);
+ } finally {
+ r.unlock();
+ }
+
+ byte[] pubArr = retByteArray[0];
+
+ int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
+ int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
+
+ assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
+
+ assertEquals(retVal, 1, "Failed return value check.");
+
+ return pubArr;
+ }
+
+ /**
+ * libsecp256k1 create ECDH secret - constant time ECDH calculation
+ *
+ * @param seckey byte array of secret key used in exponentiaion
+ * @param pubkey byte array of public key used in exponentiaion
+ */
+ public static byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{
+ Preconditions.checkArgument(seckey.length <= 32 && pubkey.length <= 65);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) {
+ byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(seckey);
+ byteBuff.put(pubkey);
+
+ byte[][] retByteArray;
+ r.lock();
+ try {
+ retByteArray = secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.length);
+ } finally {
+ r.unlock();
+ }
+
+ byte[] resArr = retByteArray[0];
+ int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
+
+ assertEquals(resArr.length, 32, "Got bad result length.");
+ assertEquals(retVal, 1, "Failed return value check.");
+
+ return resArr;
+ }
+
+ /**
+ * libsecp256k1 randomize - updates the context randomization
+ *
+ * @param seed 32-byte random seed
+ */
+ public static synchronized boolean randomize(byte[] seed) throws AssertFailException{
+ Preconditions.checkArgument(seed.length == 32 || seed == null);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < seed.length) {
+ byteBuff = ByteBuffer.allocateDirect(seed.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(seed);
+
+ w.lock();
+ try {
+ return secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1;
+ } finally {
+ w.unlock();
+ }
+ }
+
+ private static native long secp256k1_ctx_clone(long context);
+
+ private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context);
+
+ private static native byte[][] secp256k1_privkey_tweak_add(ByteBuffer byteBuff, long context);
+
+ private static native byte[][] secp256k1_privkey_tweak_mul(ByteBuffer byteBuff, long context);
+
+ private static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen);
+
+ private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen);
+
+ private static native void secp256k1_destroy_context(long context);
+
+ private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen);
+
+ private static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context);
+
+ private static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context);
+
+ private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context);
+
+ private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen);
+
+ private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen);
+
}
diff --git a/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
new file mode 100644
index 0000000000..c00d08899b
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
@@ -0,0 +1,226 @@
+package org.bitcoin;
+
+import com.google.common.io.BaseEncoding;
+import java.util.Arrays;
+import java.math.BigInteger;
+import javax.xml.bind.DatatypeConverter;
+import static org.bitcoin.NativeSecp256k1Util.*;
+
+/**
+ * This class holds test cases defined for testing this library.
+ */
+public class NativeSecp256k1Test {
+
+ //TODO improve comments/add more tests
+ /**
+ * This tests verify() for a valid signature
+ */
+ public static void testVerifyPos() throws AssertFailException{
+ boolean result = false;
+ byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
+ byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase());
+ byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
+
+ result = NativeSecp256k1.verify( data, sig, pub);
+ assertEquals( result, true , "testVerifyPos");
+ }
+
+ /**
+ * This tests verify() for a non-valid signature
+ */
+ public static void testVerifyNeg() throws AssertFailException{
+ boolean result = false;
+ byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()); //sha256hash of "testing"
+ byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase());
+ byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
+
+ result = NativeSecp256k1.verify( data, sig, pub);
+ //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
+ assertEquals( result, false , "testVerifyNeg");
+ }
+
+ /**
+ * This tests secret key verify() for a valid secretkey
+ */
+ public static void testSecKeyVerifyPos() throws AssertFailException{
+ boolean result = false;
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+
+ result = NativeSecp256k1.secKeyVerify( sec );
+ //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
+ assertEquals( result, true , "testSecKeyVerifyPos");
+ }
+
+ /**
+ * This tests secret key verify() for a invalid secretkey
+ */
+ public static void testSecKeyVerifyNeg() throws AssertFailException{
+ boolean result = false;
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
+
+ result = NativeSecp256k1.secKeyVerify( sec );
+ //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
+ assertEquals( result, false , "testSecKeyVerifyNeg");
+ }
+
+ /**
+ * This tests public key create() for a valid secretkey
+ */
+ public static void testPubKeyCreatePos() throws AssertFailException{
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+
+ byte[] resultArr = NativeSecp256k1.computePubkey( sec);
+ String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( pubkeyString , "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6" , "testPubKeyCreatePos");
+ }
+
+ /**
+ * This tests public key create() for a invalid secretkey
+ */
+ public static void testPubKeyCreateNeg() throws AssertFailException{
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
+
+ byte[] resultArr = NativeSecp256k1.computePubkey( sec);
+ String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( pubkeyString, "" , "testPubKeyCreateNeg");
+ }
+
+ /**
+ * This tests sign() for a valid secretkey
+ */
+ public static void testSignPos() throws AssertFailException{
+
+ byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+
+ byte[] resultArr = NativeSecp256k1.sign(data, sec);
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString, "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos");
+ }
+
+ /**
+ * This tests sign() for a invalid secretkey
+ */
+ public static void testSignNeg() throws AssertFailException{
+ byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
+
+ byte[] resultArr = NativeSecp256k1.sign(data, sec);
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString, "" , "testSignNeg");
+ }
+
+ /**
+ * This tests private key tweak-add
+ */
+ public static void testPrivKeyTweakAdd_1() throws AssertFailException {
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+ byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
+
+ byte[] resultArr = NativeSecp256k1.privKeyTweakAdd( sec , data );
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString , "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3" , "testPrivKeyAdd_1");
+ }
+
+ /**
+ * This tests private key tweak-mul
+ */
+ public static void testPrivKeyTweakMul_1() throws AssertFailException {
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+ byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
+
+ byte[] resultArr = NativeSecp256k1.privKeyTweakMul( sec , data );
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString , "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC" , "testPrivKeyMul_1");
+ }
+
+ /**
+ * This tests private key tweak-add uncompressed
+ */
+ public static void testPrivKeyTweakAdd_2() throws AssertFailException {
+ byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
+ byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
+
+ byte[] resultArr = NativeSecp256k1.pubKeyTweakAdd( pub , data );
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString , "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF" , "testPrivKeyAdd_2");
+ }
+
+ /**
+ * This tests private key tweak-mul uncompressed
+ */
+ public static void testPrivKeyTweakMul_2() throws AssertFailException {
+ byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
+ byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
+
+ byte[] resultArr = NativeSecp256k1.pubKeyTweakMul( pub , data );
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString , "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589" , "testPrivKeyMul_2");
+ }
+
+ /**
+ * This tests seed randomization
+ */
+ public static void testRandomize() throws AssertFailException {
+ byte[] seed = BaseEncoding.base16().lowerCase().decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()); //sha256hash of "random"
+ boolean result = NativeSecp256k1.randomize(seed);
+ assertEquals( result, true, "testRandomize");
+ }
+
+ public static void testCreateECDHSecret() throws AssertFailException{
+
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+ byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
+
+ byte[] resultArr = NativeSecp256k1.createECDHSecret(sec, pub);
+ String ecdhString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( ecdhString, "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043" , "testCreateECDHSecret");
+ }
+
+ public static void main(String[] args) throws AssertFailException{
+
+
+ System.out.println("\n libsecp256k1 enabled: " + Secp256k1Context.isEnabled() + "\n");
+
+ assertEquals( Secp256k1Context.isEnabled(), true, "isEnabled" );
+
+ //Test verify() success/fail
+ testVerifyPos();
+ testVerifyNeg();
+
+ //Test secKeyVerify() success/fail
+ testSecKeyVerifyPos();
+ testSecKeyVerifyNeg();
+
+ //Test computePubkey() success/fail
+ testPubKeyCreatePos();
+ testPubKeyCreateNeg();
+
+ //Test sign() success/fail
+ testSignPos();
+ testSignNeg();
+
+ //Test privKeyTweakAdd() 1
+ testPrivKeyTweakAdd_1();
+
+ //Test privKeyTweakMul() 2
+ testPrivKeyTweakMul_1();
+
+ //Test privKeyTweakAdd() 3
+ testPrivKeyTweakAdd_2();
+
+ //Test privKeyTweakMul() 4
+ testPrivKeyTweakMul_2();
+
+ //Test randomize()
+ testRandomize();
+
+ //Test ECDH
+ testCreateECDHSecret();
+
+ NativeSecp256k1.cleanup();
+
+ System.out.println(" All tests passed." );
+
+ }
+}
diff --git a/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java b/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java
new file mode 100644
index 0000000000..04732ba044
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014-2016 the libsecp256k1 contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.bitcoin;
+
+public class NativeSecp256k1Util{
+
+ public static void assertEquals( int val, int val2, String message ) throws AssertFailException{
+ if( val != val2 )
+ throw new AssertFailException("FAIL: " + message);
+ }
+
+ public static void assertEquals( boolean val, boolean val2, String message ) throws AssertFailException{
+ if( val != val2 )
+ throw new AssertFailException("FAIL: " + message);
+ else
+ System.out.println("PASS: " + message);
+ }
+
+ public static void assertEquals( String val, String val2, String message ) throws AssertFailException{
+ if( !val.equals(val2) )
+ throw new AssertFailException("FAIL: " + message);
+ else
+ System.out.println("PASS: " + message);
+ }
+
+ public static class AssertFailException extends Exception {
+ public AssertFailException(String message) {
+ super( message );
+ }
+ }
+}
diff --git a/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java b/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java
new file mode 100644
index 0000000000..216c986a8b
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014-2016 the libsecp256k1 contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.bitcoin;
+
+/**
+ * This class holds the context reference used in native methods
+ * to handle ECDSA operations.
+ */
+public class Secp256k1Context {
+ private static final boolean enabled; //true if the library is loaded
+ private static final long context; //ref to pointer to context obj
+
+ static { //static initializer
+ boolean isEnabled = true;
+ long contextRef = -1;
+ try {
+ System.loadLibrary("secp256k1");
+ contextRef = secp256k1_init_context();
+ } catch (UnsatisfiedLinkError e) {
+ System.out.println("UnsatisfiedLinkError: " + e.toString());
+ isEnabled = false;
+ }
+ enabled = isEnabled;
+ context = contextRef;
+ }
+
+ public static boolean isEnabled() {
+ return enabled;
+ }
+
+ public static long getContext() {
+ if(!enabled) return -1; //sanity check
+ return context;
+ }
+
+ private static native long secp256k1_init_context();
+}
diff --git a/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c
index bb4cd70728..bcef7b32ce 100644
--- a/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c
+++ b/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c
@@ -1,23 +1,377 @@
+#include
+#include
+#include
#include "org_bitcoin_NativeSecp256k1.h"
#include "include/secp256k1.h"
+#include "include/secp256k1_ecdh.h"
+#include "include/secp256k1_recovery.h"
-JNIEXPORT jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
- (JNIEnv* env, jclass classObject, jobject byteBufferObject)
+
+SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
+ (JNIEnv* env, jclass classObject, jlong ctx_l)
{
- unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
- int sigLen = *((int*)(data + 32));
- int pubLen = *((int*)(data + 32 + 4));
+ const secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+
+ jlong ctx_clone_l = (uintptr_t) secp256k1_context_clone(ctx);
+
+ (void)classObject;(void)env;
+
+ return ctx_clone_l;
- return secp256k1_ecdsa_verify(data, 32, data+32+8, sigLen, data+32+8+sigLen, pubLen);
}
-static void __javasecp256k1_attach(void) __attribute__((constructor));
-static void __javasecp256k1_detach(void) __attribute__((destructor));
+SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+
+ const unsigned char* seed = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+
+ (void)classObject;
+
+ return secp256k1_context_randomize(ctx, seed);
-static void __javasecp256k1_attach(void) {
- secp256k1_start(SECP256K1_START_VERIFY);
}
-static void __javasecp256k1_detach(void) {
- secp256k1_stop();
+SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context
+ (JNIEnv* env, jclass classObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+
+ secp256k1_context_destroy(ctx);
+
+ (void)classObject;(void)env;
+}
+
+SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint siglen, jint publen)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+
+ unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ const unsigned char* sigdata = { (unsigned char*) (data + 32) };
+ const unsigned char* pubdata = { (unsigned char*) (data + siglen + 32) };
+
+ secp256k1_ecdsa_signature sig;
+ secp256k1_pubkey pubkey;
+
+ int ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen);
+
+ if( ret ) {
+ ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen);
+
+ if( ret ) {
+ ret = secp256k1_ecdsa_verify(ctx, &sig, data, &pubkey);
+ }
+ }
+
+ (void)classObject;
+
+ return ret;
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ unsigned char* secKey = (unsigned char*) (data + 32);
+
+ jobjectArray retArray;
+ jbyteArray sigArray, intsByteArray;
+ unsigned char intsarray[2];
+
+ secp256k1_ecdsa_signature sig[72];
+
+ int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL );
+
+ unsigned char outputSer[72];
+ size_t outputLen = 72;
+
+ if( ret ) {
+ int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, sig ); (void)ret2;
+ }
+
+ intsarray[0] = outputLen;
+ intsarray[1] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ sigArray = (*env)->NewByteArray(env, outputLen);
+ (*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer);
+ (*env)->SetObjectArrayElement(env, retArray, 0, sigArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 2);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
+}
+
+SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+
+ (void)classObject;
+
+ return secp256k1_ec_seckey_verify(ctx, secKey);
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+
+ secp256k1_pubkey pubkey;
+
+ jobjectArray retArray;
+ jbyteArray pubkeyArray, intsByteArray;
+ unsigned char intsarray[2];
+
+ int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey);
+
+ unsigned char outputSer[65];
+ size_t outputLen = 65;
+
+ if( ret ) {
+ int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
+ }
+
+ intsarray[0] = outputLen;
+ intsarray[1] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ pubkeyArray = (*env)->NewByteArray(env, outputLen);
+ (*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer);
+ (*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 2);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
+
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ const unsigned char* tweak = (unsigned char*) (privkey + 32);
+
+ jobjectArray retArray;
+ jbyteArray privArray, intsByteArray;
+ unsigned char intsarray[2];
+
+ int privkeylen = 32;
+
+ int ret = secp256k1_ec_privkey_tweak_add(ctx, privkey, tweak);
+
+ intsarray[0] = privkeylen;
+ intsarray[1] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ privArray = (*env)->NewByteArray(env, privkeylen);
+ (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey);
+ (*env)->SetObjectArrayElement(env, retArray, 0, privArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 2);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ const unsigned char* tweak = (unsigned char*) (privkey + 32);
+
+ jobjectArray retArray;
+ jbyteArray privArray, intsByteArray;
+ unsigned char intsarray[2];
+
+ int privkeylen = 32;
+
+ int ret = secp256k1_ec_privkey_tweak_mul(ctx, privkey, tweak);
+
+ intsarray[0] = privkeylen;
+ intsarray[1] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ privArray = (*env)->NewByteArray(env, privkeylen);
+ (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey);
+ (*env)->SetObjectArrayElement(env, retArray, 0, privArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 2);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+/* secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (*env)->GetDirectBufferAddress(env, byteBufferObject);*/
+ unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ const unsigned char* tweak = (unsigned char*) (pkey + publen);
+
+ jobjectArray retArray;
+ jbyteArray pubArray, intsByteArray;
+ unsigned char intsarray[2];
+ unsigned char outputSer[65];
+ size_t outputLen = 65;
+
+ secp256k1_pubkey pubkey;
+ int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
+
+ if( ret ) {
+ ret = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, tweak);
+ }
+
+ if( ret ) {
+ int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
+ }
+
+ intsarray[0] = outputLen;
+ intsarray[1] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ pubArray = (*env)->NewByteArray(env, outputLen);
+ (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer);
+ (*env)->SetObjectArrayElement(env, retArray, 0, pubArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 2);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ const unsigned char* tweak = (unsigned char*) (pkey + publen);
+
+ jobjectArray retArray;
+ jbyteArray pubArray, intsByteArray;
+ unsigned char intsarray[2];
+ unsigned char outputSer[65];
+ size_t outputLen = 65;
+
+ secp256k1_pubkey pubkey;
+ int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
+
+ if ( ret ) {
+ ret = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, tweak);
+ }
+
+ if( ret ) {
+ int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
+ }
+
+ intsarray[0] = outputLen;
+ intsarray[1] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ pubArray = (*env)->NewByteArray(env, outputLen);
+ (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer);
+ (*env)->SetObjectArrayElement(env, retArray, 0, pubArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 2);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
+}
+
+SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1pubkey_1combine
+ (JNIEnv * env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint numkeys)
+{
+ (void)classObject;(void)env;(void)byteBufferObject;(void)ctx_l;(void)numkeys;
+
+ return 0;
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ const unsigned char* secdata = (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ const unsigned char* pubdata = (const unsigned char*) (secdata + 32);
+
+ jobjectArray retArray;
+ jbyteArray outArray, intsByteArray;
+ unsigned char intsarray[1];
+ secp256k1_pubkey pubkey;
+ unsigned char nonce_res[32];
+ size_t outputLen = 32;
+
+ int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen);
+
+ if (ret) {
+ ret = secp256k1_ecdh(
+ ctx,
+ nonce_res,
+ &pubkey,
+ secdata
+ );
+ }
+
+ intsarray[0] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ outArray = (*env)->NewByteArray(env, outputLen);
+ (*env)->SetByteArrayRegion(env, outArray, 0, 32, (jbyte*)nonce_res);
+ (*env)->SetObjectArrayElement(env, retArray, 0, outArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 1);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
}
diff --git a/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h b/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h
index d7fb004fa8..fe613c9e9e 100644
--- a/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h
+++ b/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h
@@ -1,5 +1,6 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
+#include "include/secp256k1.h"
/* Header for class org_bitcoin_NativeSecp256k1 */
#ifndef _Included_org_bitcoin_NativeSecp256k1
@@ -9,11 +10,108 @@ extern "C" {
#endif
/*
* Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_ecdsa_verify
- * Signature: (Ljava/nio/ByteBuffer;)I
+ * Method: secp256k1_ctx_clone
+ * Signature: (J)J
*/
-JNIEXPORT jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
- (JNIEnv *, jclass, jobject);
+SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
+ (JNIEnv *, jclass, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_context_randomize
+ * Signature: (Ljava/nio/ByteBuffer;J)I
+ */
+SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize
+ (JNIEnv *, jclass, jobject, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_privkey_tweak_add
+ * Signature: (Ljava/nio/ByteBuffer;J)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add
+ (JNIEnv *, jclass, jobject, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_privkey_tweak_mul
+ * Signature: (Ljava/nio/ByteBuffer;J)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul
+ (JNIEnv *, jclass, jobject, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_pubkey_tweak_add
+ * Signature: (Ljava/nio/ByteBuffer;JI)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add
+ (JNIEnv *, jclass, jobject, jlong, jint);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_pubkey_tweak_mul
+ * Signature: (Ljava/nio/ByteBuffer;JI)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul
+ (JNIEnv *, jclass, jobject, jlong, jint);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_destroy_context
+ * Signature: (J)V
+ */
+SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context
+ (JNIEnv *, jclass, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_ecdsa_verify
+ * Signature: (Ljava/nio/ByteBuffer;JII)I
+ */
+SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
+ (JNIEnv *, jclass, jobject, jlong, jint, jint);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_ecdsa_sign
+ * Signature: (Ljava/nio/ByteBuffer;J)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
+ (JNIEnv *, jclass, jobject, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_ec_seckey_verify
+ * Signature: (Ljava/nio/ByteBuffer;J)I
+ */
+SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
+ (JNIEnv *, jclass, jobject, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_ec_pubkey_create
+ * Signature: (Ljava/nio/ByteBuffer;J)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
+ (JNIEnv *, jclass, jobject, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_ec_pubkey_parse
+ * Signature: (Ljava/nio/ByteBuffer;JI)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse
+ (JNIEnv *, jclass, jobject, jlong, jint);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_ecdh
+ * Signature: (Ljava/nio/ByteBuffer;JI)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen);
+
#ifdef __cplusplus
}
diff --git a/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c b/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c
new file mode 100644
index 0000000000..a52939e7e7
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c
@@ -0,0 +1,15 @@
+#include
+#include
+#include "org_bitcoin_Secp256k1Context.h"
+#include "include/secp256k1.h"
+
+SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context
+ (JNIEnv* env, jclass classObject)
+{
+ secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+
+ (void)classObject;(void)env;
+
+ return (uintptr_t)ctx;
+}
+
diff --git a/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h b/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h
new file mode 100644
index 0000000000..0d2bc84b7f
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h
@@ -0,0 +1,22 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include
+#include "include/secp256k1.h"
+/* Header for class org_bitcoin_Secp256k1Context */
+
+#ifndef _Included_org_bitcoin_Secp256k1Context
+#define _Included_org_bitcoin_Secp256k1Context
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_bitcoin_Secp256k1Context
+ * Method: secp256k1_init_context
+ * Signature: ()J
+ */
+SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context
+ (JNIEnv *, jclass);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include b/crypto/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include
index 8ef3aff92f..e3088b4697 100644
--- a/crypto/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include
+++ b/crypto/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include
@@ -4,6 +4,5 @@ noinst_HEADERS += src/modules/ecdh/tests_impl.h
if USE_BENCHMARK
noinst_PROGRAMS += bench_ecdh
bench_ecdh_SOURCES = src/bench_ecdh.c
-bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS)
-bench_ecdh_LDFLAGS = -static
+bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
endif
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h b/crypto/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h
index c23e4f82f7..9e30fb73dd 100644
--- a/crypto/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h
@@ -16,10 +16,10 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const se
secp256k1_gej res;
secp256k1_ge pt;
secp256k1_scalar s;
+ VERIFY_CHECK(ctx != NULL);
ARG_CHECK(result != NULL);
ARG_CHECK(point != NULL);
ARG_CHECK(scalar != NULL);
- (void)ctx;
secp256k1_pubkey_load(ctx, &pt, point);
secp256k1_scalar_set_b32(&s, scalar, &overflow);
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h b/crypto/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h
index 7badc9033f..85a5d0a9a6 100644
--- a/crypto/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h
@@ -7,6 +7,35 @@
#ifndef _SECP256K1_MODULE_ECDH_TESTS_
#define _SECP256K1_MODULE_ECDH_TESTS_
+void test_ecdh_api(void) {
+ /* Setup context that just counts errors */
+ secp256k1_context *tctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
+ secp256k1_pubkey point;
+ unsigned char res[32];
+ unsigned char s_one[32] = { 0 };
+ int32_t ecount = 0;
+ s_one[31] = 1;
+
+ secp256k1_context_set_error_callback(tctx, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(tctx, counting_illegal_callback_fn, &ecount);
+ CHECK(secp256k1_ec_pubkey_create(tctx, &point, s_one) == 1);
+
+ /* Check all NULLs are detected */
+ CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1);
+ CHECK(ecount == 0);
+ CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ecdh(tctx, res, NULL, s_one) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ecdh(tctx, res, &point, NULL) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1);
+ CHECK(ecount == 3);
+
+ /* Cleanup */
+ secp256k1_context_destroy(tctx);
+}
+
void test_ecdh_generator_basepoint(void) {
unsigned char s_one[32] = { 0 };
secp256k1_pubkey point[2];
@@ -68,6 +97,7 @@ void test_bad_scalar(void) {
}
void run_ecdh_tests(void) {
+ test_ecdh_api();
test_ecdh_generator_basepoint();
test_bad_scalar();
}
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include b/crypto/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include
index 754469eeb2..bf23c26e71 100644
--- a/crypto/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include
+++ b/crypto/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include
@@ -4,6 +4,5 @@ noinst_HEADERS += src/modules/recovery/tests_impl.h
if USE_BENCHMARK
noinst_PROGRAMS += bench_recover
bench_recover_SOURCES = src/bench_recover.c
-bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS)
-bench_recover_LDFLAGS = -static
+bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
endif
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h b/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h
old mode 100644
new mode 100755
index 75b6958940..c6fbe23981
--- a/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h
@@ -63,6 +63,7 @@ int secp256k1_ecdsa_recoverable_signature_serialize_compact(const secp256k1_cont
(void)ctx;
ARG_CHECK(output64 != NULL);
ARG_CHECK(sig != NULL);
+ ARG_CHECK(recid != NULL);
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, recid, sig);
secp256k1_scalar_get_b32(&output64[0], &r);
@@ -83,6 +84,42 @@ int secp256k1_ecdsa_recoverable_signature_convert(const secp256k1_context* ctx,
return 1;
}
+static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar* sigs, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid) {
+ unsigned char brx[32];
+ secp256k1_fe fx;
+ secp256k1_ge x;
+ secp256k1_gej xj;
+ secp256k1_scalar rn, u1, u2;
+ secp256k1_gej qj;
+ int r;
+
+ if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) {
+ return 0;
+ }
+
+ secp256k1_scalar_get_b32(brx, sigr);
+ r = secp256k1_fe_set_b32(&fx, brx);
+ (void)r;
+ VERIFY_CHECK(r); /* brx comes from a scalar, so is less than the order; certainly less than p */
+ if (recid & 2) {
+ if (secp256k1_fe_cmp_var(&fx, &secp256k1_ecdsa_const_p_minus_order) >= 0) {
+ return 0;
+ }
+ secp256k1_fe_add(&fx, &secp256k1_ecdsa_const_order_as_fe);
+ }
+ if (!secp256k1_ge_set_xo_var(&x, &fx, recid & 1)) {
+ return 0;
+ }
+ secp256k1_gej_set_ge(&xj, &x);
+ secp256k1_scalar_inverse_var(&rn, sigr);
+ secp256k1_scalar_mul(&u1, &rn, message);
+ secp256k1_scalar_negate(&u1, &u1);
+ secp256k1_scalar_mul(&u2, &rn, sigs);
+ secp256k1_ecmult(ctx, &qj, &xj, &u2, &u1);
+ secp256k1_ge_set_gej_var(pubkey, &qj);
+ return !secp256k1_gej_is_infinity(&qj);
+}
+
int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
secp256k1_scalar r, s;
secp256k1_scalar sec, non, msg;
@@ -101,16 +138,15 @@ int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecd
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
/* Fail if the secret key is invalid. */
if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
+ unsigned char nonce32[32];
unsigned int count = 0;
secp256k1_scalar_set_b32(&msg, msg32, NULL);
while (1) {
- unsigned char nonce32[32];
- ret = noncefp(nonce32, seckey, msg32, NULL, (void*)noncedata, count);
+ ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
if (!ret) {
break;
}
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
- memset(nonce32, 0, 32);
if (!secp256k1_scalar_is_zero(&non) && !overflow) {
if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) {
break;
@@ -118,6 +154,7 @@ int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecd
}
count++;
}
+ memset(nonce32, 0, 32);
secp256k1_scalar_clear(&msg);
secp256k1_scalar_clear(&non);
secp256k1_scalar_clear(&sec);
@@ -142,7 +179,7 @@ int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubk
ARG_CHECK(pubkey != NULL);
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, signature);
- ARG_CHECK(recid >= 0 && recid < 4);
+ VERIFY_CHECK(recid >= 0 && recid < 4); /* should have been caught in parse_compact */
secp256k1_scalar_set_b32(&m, msg32, NULL);
if (secp256k1_ecdsa_sig_recover(&ctx->ecmult_ctx, &r, &s, &q, &m, recid)) {
secp256k1_pubkey_save(pubkey, &q);
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h b/crypto/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h
index 5a78fae921..765c7dd81e 100644
--- a/crypto/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h
@@ -7,6 +7,146 @@
#ifndef _SECP256K1_MODULE_RECOVERY_TESTS_
#define _SECP256K1_MODULE_RECOVERY_TESTS_
+static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) {
+ (void) msg32;
+ (void) key32;
+ (void) algo16;
+ (void) data;
+
+ /* On the first run, return 0 to force a second run */
+ if (counter == 0) {
+ memset(nonce32, 0, 32);
+ return 1;
+ }
+ /* On the second run, return an overflow to force a third run */
+ if (counter == 1) {
+ memset(nonce32, 0xff, 32);
+ return 1;
+ }
+ /* On the next run, return a valid nonce, but flip a coin as to whether or not to fail signing. */
+ memset(nonce32, 1, 32);
+ return secp256k1_rand_bits(1);
+}
+
+void test_ecdsa_recovery_api(void) {
+ /* Setup contexts that just count errors */
+ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
+ secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
+ secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
+ secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ secp256k1_pubkey pubkey;
+ secp256k1_pubkey recpubkey;
+ secp256k1_ecdsa_signature normal_sig;
+ secp256k1_ecdsa_recoverable_signature recsig;
+ unsigned char privkey[32] = { 1 };
+ unsigned char message[32] = { 2 };
+ int32_t ecount = 0;
+ int recid = 0;
+ unsigned char sig[74];
+ unsigned char zero_privkey[32] = { 0 };
+ unsigned char over_privkey[32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount);
+
+ /* Construct and verify corresponding public key. */
+ CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1);
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1);
+
+ /* Check bad contexts and NULLs for signing */
+ ecount = 0;
+ CHECK(secp256k1_ecdsa_sign_recoverable(none, &recsig, message, privkey, NULL, NULL) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ecdsa_sign_recoverable(sign, &recsig, message, privkey, NULL, NULL) == 1);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ecdsa_sign_recoverable(vrfy, &recsig, message, privkey, NULL, NULL) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ecdsa_sign_recoverable(both, NULL, message, privkey, NULL, NULL) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, NULL, privkey, NULL, NULL) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, NULL, NULL, NULL) == 0);
+ CHECK(ecount == 5);
+ /* This will fail or succeed randomly, and in either case will not ARG_CHECK failure */
+ secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, recovery_test_nonce_function, NULL);
+ CHECK(ecount == 5);
+ /* These will all fail, but not in ARG_CHECK way */
+ CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, zero_privkey, NULL, NULL) == 0);
+ CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, over_privkey, NULL, NULL) == 0);
+ /* This one will succeed. */
+ CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1);
+ CHECK(ecount == 5);
+
+ /* Check signing with a goofy nonce function */
+
+ /* Check bad contexts and NULLs for recovery */
+ ecount = 0;
+ CHECK(secp256k1_ecdsa_recover(none, &recpubkey, &recsig, message) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ecdsa_recover(sign, &recpubkey, &recsig, message) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ecdsa_recover(vrfy, &recpubkey, &recsig, message) == 1);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ecdsa_recover(both, &recpubkey, &recsig, message) == 1);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ecdsa_recover(both, NULL, &recsig, message) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_ecdsa_recover(both, &recpubkey, NULL, message) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_ecdsa_recover(both, &recpubkey, &recsig, NULL) == 0);
+ CHECK(ecount == 5);
+
+ /* Check NULLs for conversion */
+ CHECK(secp256k1_ecdsa_sign(both, &normal_sig, message, privkey, NULL, NULL) == 1);
+ ecount = 0;
+ CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, NULL, &recsig) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, &normal_sig, NULL) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, &normal_sig, &recsig) == 1);
+
+ /* Check NULLs for de/serialization */
+ CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1);
+ ecount = 0;
+ CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, NULL, &recid, &recsig) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, NULL, &recsig) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, &recid, NULL) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, &recid, &recsig) == 1);
+
+ CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, NULL, sig, recid) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, NULL, recid) == 0);
+ CHECK(ecount == 5);
+ CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, -1) == 0);
+ CHECK(ecount == 6);
+ CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, 5) == 0);
+ CHECK(ecount == 7);
+ /* overflow in signature will fail but not affect ecount */
+ memcpy(sig, over_privkey, 32);
+ CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, recid) == 0);
+ CHECK(ecount == 7);
+
+ /* cleanup */
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(vrfy);
+ secp256k1_context_destroy(both);
+}
+
void test_ecdsa_recovery_end_to_end(void) {
unsigned char extra[32] = {0x00};
unsigned char privkey[32];
@@ -34,6 +174,7 @@ void test_ecdsa_recovery_end_to_end(void) {
/* Serialize/parse compact and verify/recover. */
extra[0] = 0;
CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[0], message, privkey, NULL, NULL) == 1);
+ CHECK(secp256k1_ecdsa_sign(ctx, &signature[0], message, privkey, NULL, NULL) == 1);
CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[4], message, privkey, NULL, NULL) == 1);
CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[1], message, privkey, NULL, extra) == 1);
extra[31] = 1;
@@ -43,6 +184,7 @@ void test_ecdsa_recovery_end_to_end(void) {
CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[3], message, privkey, NULL, extra) == 1);
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1);
CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1);
+ CHECK(memcmp(&signature[4], &signature[0], 64) == 0);
CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1);
memset(&rsignature[4], 0, sizeof(rsignature[4]));
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1);
@@ -54,7 +196,7 @@ void test_ecdsa_recovery_end_to_end(void) {
CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0);
/* Serialize/destroy/parse signature and verify again. */
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1);
- sig[secp256k1_rand32() % 64] += 1 + (secp256k1_rand32() % 255);
+ sig[secp256k1_rand_bits(6)] += 1 + secp256k1_rand_int(255);
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1);
CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1);
CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 0);
@@ -161,25 +303,24 @@ void test_ecdsa_recovery_edge_cases(void) {
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigb64, recid2) == 1);
CHECK(secp256k1_ecdsa_recover(ctx, &pubkey2b, &rsig, msg32) == 1);
/* Verifying with (order + r,4) should always fail. */
- CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderlong, sizeof(sigbderlong)) == 0);
+ CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderlong, sizeof(sigbderlong)) == 1);
+ CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0);
}
/* DER parsing tests. */
/* Zero length r/s. */
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zr, sizeof(sigcder_zr)) == 0);
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zs, sizeof(sigcder_zs)) == 0);
/* Leading zeros. */
- CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt1, sizeof(sigbderalt1)) == 1);
- CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1);
- CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt2, sizeof(sigbderalt2)) == 1);
- CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1);
- CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 1);
- CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1);
- CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 1);
- CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1);
- sigbderalt3[4] = 1;
+ CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt1, sizeof(sigbderalt1)) == 0);
+ CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt2, sizeof(sigbderalt2)) == 0);
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 0);
- sigbderalt4[7] = 1;
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 0);
+ sigbderalt3[4] = 1;
+ CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 1);
+ CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0);
+ sigbderalt4[7] = 1;
+ CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 1);
+ CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0);
/* Damage signature. */
sigbder[7]++;
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 1);
@@ -240,6 +381,9 @@ void test_ecdsa_recovery_edge_cases(void) {
void run_recovery_tests(void) {
int i;
+ for (i = 0; i < count; i++) {
+ test_ecdsa_recovery_api();
+ }
for (i = 0; i < 64*count; i++) {
test_ecdsa_recovery_end_to_end();
}
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/schnorr/Makefile.am.include b/crypto/secp256k1/libsecp256k1/src/modules/schnorr/Makefile.am.include
deleted file mode 100644
index bad4cb7c5b..0000000000
--- a/crypto/secp256k1/libsecp256k1/src/modules/schnorr/Makefile.am.include
+++ /dev/null
@@ -1,11 +0,0 @@
-include_HEADERS += include/secp256k1_schnorr.h
-noinst_HEADERS += src/modules/schnorr/main_impl.h
-noinst_HEADERS += src/modules/schnorr/schnorr.h
-noinst_HEADERS += src/modules/schnorr/schnorr_impl.h
-noinst_HEADERS += src/modules/schnorr/tests_impl.h
-if USE_BENCHMARK
-noinst_PROGRAMS += bench_schnorr_verify
-bench_schnorr_verify_SOURCES = src/bench_schnorr_verify.c
-bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
-bench_schnorr_verify_LDFLAGS = -static
-endif
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/schnorr/main_impl.h b/crypto/secp256k1/libsecp256k1/src/modules/schnorr/main_impl.h
deleted file mode 100644
index c10fd259f2..0000000000
--- a/crypto/secp256k1/libsecp256k1/src/modules/schnorr/main_impl.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/**********************************************************************
- * Copyright (c) 2014-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef SECP256K1_MODULE_SCHNORR_MAIN
-#define SECP256K1_MODULE_SCHNORR_MAIN
-
-#include "include/secp256k1_schnorr.h"
-#include "modules/schnorr/schnorr_impl.h"
-
-static void secp256k1_schnorr_msghash_sha256(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32) {
- secp256k1_sha256_t sha;
- secp256k1_sha256_initialize(&sha);
- secp256k1_sha256_write(&sha, r32, 32);
- secp256k1_sha256_write(&sha, msg32, 32);
- secp256k1_sha256_finalize(&sha, h32);
-}
-
-static const unsigned char secp256k1_schnorr_algo16[17] = "Schnorr+SHA256 ";
-
-int secp256k1_schnorr_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
- secp256k1_scalar sec, non;
- int ret = 0;
- int overflow = 0;
- unsigned int count = 0;
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
- ARG_CHECK(msg32 != NULL);
- ARG_CHECK(sig64 != NULL);
- ARG_CHECK(seckey != NULL);
- if (noncefp == NULL) {
- noncefp = secp256k1_nonce_function_default;
- }
-
- secp256k1_scalar_set_b32(&sec, seckey, NULL);
- while (1) {
- unsigned char nonce32[32];
- ret = noncefp(nonce32, msg32, seckey, secp256k1_schnorr_algo16, (void*)noncedata, count);
- if (!ret) {
- break;
- }
- secp256k1_scalar_set_b32(&non, nonce32, &overflow);
- memset(nonce32, 0, 32);
- if (!secp256k1_scalar_is_zero(&non) && !overflow) {
- if (secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64, &sec, &non, NULL, secp256k1_schnorr_msghash_sha256, msg32)) {
- break;
- }
- }
- count++;
- }
- if (!ret) {
- memset(sig64, 0, 64);
- }
- secp256k1_scalar_clear(&non);
- secp256k1_scalar_clear(&sec);
- return ret;
-}
-
-int secp256k1_schnorr_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg32, const secp256k1_pubkey *pubkey) {
- secp256k1_ge q;
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
- ARG_CHECK(msg32 != NULL);
- ARG_CHECK(sig64 != NULL);
- ARG_CHECK(pubkey != NULL);
-
- secp256k1_pubkey_load(ctx, &q, pubkey);
- return secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32);
-}
-
-int secp256k1_schnorr_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *sig64, const unsigned char *msg32) {
- secp256k1_ge q;
-
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
- ARG_CHECK(msg32 != NULL);
- ARG_CHECK(sig64 != NULL);
- ARG_CHECK(pubkey != NULL);
-
- if (secp256k1_schnorr_sig_recover(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32)) {
- secp256k1_pubkey_save(pubkey, &q);
- return 1;
- } else {
- memset(pubkey, 0, sizeof(*pubkey));
- return 0;
- }
-}
-
-int secp256k1_schnorr_generate_nonce_pair(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, unsigned char *privnonce32, const unsigned char *sec32, const unsigned char *msg32, secp256k1_nonce_function noncefp, const void* noncedata) {
- int count = 0;
- int ret = 1;
- secp256k1_gej Qj;
- secp256k1_ge Q;
- secp256k1_scalar sec;
-
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
- ARG_CHECK(msg32 != NULL);
- ARG_CHECK(sec32 != NULL);
- ARG_CHECK(pubnonce != NULL);
- ARG_CHECK(privnonce32 != NULL);
-
- if (noncefp == NULL) {
- noncefp = secp256k1_nonce_function_default;
- }
-
- do {
- int overflow;
- ret = noncefp(privnonce32, sec32, msg32, secp256k1_schnorr_algo16, (void*)noncedata, count++);
- if (!ret) {
- break;
- }
- secp256k1_scalar_set_b32(&sec, privnonce32, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&sec)) {
- continue;
- }
- secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sec);
- secp256k1_ge_set_gej(&Q, &Qj);
-
- secp256k1_pubkey_save(pubnonce, &Q);
- break;
- } while(1);
-
- secp256k1_scalar_clear(&sec);
- if (!ret) {
- memset(pubnonce, 0, sizeof(*pubnonce));
- }
- return ret;
-}
-
-int secp256k1_schnorr_partial_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const unsigned char *sec32, const secp256k1_pubkey *pubnonce_others, const unsigned char *secnonce32) {
- int overflow = 0;
- secp256k1_scalar sec, non;
- secp256k1_ge pubnon;
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
- ARG_CHECK(msg32 != NULL);
- ARG_CHECK(sig64 != NULL);
- ARG_CHECK(sec32 != NULL);
- ARG_CHECK(secnonce32 != NULL);
- ARG_CHECK(pubnonce_others != NULL);
-
- secp256k1_scalar_set_b32(&sec, sec32, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&sec)) {
- return -1;
- }
- secp256k1_scalar_set_b32(&non, secnonce32, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&non)) {
- return -1;
- }
- secp256k1_pubkey_load(ctx, &pubnon, pubnonce_others);
- return secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64, &sec, &non, &pubnon, secp256k1_schnorr_msghash_sha256, msg32);
-}
-
-int secp256k1_schnorr_partial_combine(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char * const *sig64sin, int n) {
- ARG_CHECK(sig64 != NULL);
- ARG_CHECK(n >= 1);
- ARG_CHECK(sig64sin != NULL);
- return secp256k1_schnorr_sig_combine(sig64, n, sig64sin);
-}
-
-#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/schnorr/schnorr.h b/crypto/secp256k1/libsecp256k1/src/modules/schnorr/schnorr.h
deleted file mode 100644
index d227433d48..0000000000
--- a/crypto/secp256k1/libsecp256k1/src/modules/schnorr/schnorr.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/***********************************************************************
- * Copyright (c) 2014-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php. *
- ***********************************************************************/
-
-#ifndef _SECP256K1_MODULE_SCHNORR_H_
-#define _SECP256K1_MODULE_SCHNORR_H_
-
-#include "scalar.h"
-#include "group.h"
-
-typedef void (*secp256k1_schnorr_msghash)(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32);
-
-static int secp256k1_schnorr_sig_sign(const secp256k1_ecmult_gen_context* ctx, unsigned char *sig64, const secp256k1_scalar *key, const secp256k1_scalar *nonce, const secp256k1_ge *pubnonce, secp256k1_schnorr_msghash hash, const unsigned char *msg32);
-static int secp256k1_schnorr_sig_verify(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, const secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32);
-static int secp256k1_schnorr_sig_recover(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32);
-static int secp256k1_schnorr_sig_combine(unsigned char *sig64, int n, const unsigned char * const *sig64ins);
-
-#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/schnorr/schnorr_impl.h b/crypto/secp256k1/libsecp256k1/src/modules/schnorr/schnorr_impl.h
deleted file mode 100644
index ed70390bba..0000000000
--- a/crypto/secp256k1/libsecp256k1/src/modules/schnorr/schnorr_impl.h
+++ /dev/null
@@ -1,207 +0,0 @@
-/***********************************************************************
- * Copyright (c) 2014-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php. *
- ***********************************************************************/
-
-#ifndef _SECP256K1_SCHNORR_IMPL_H_
-#define _SECP256K1_SCHNORR_IMPL_H_
-
-#include
-
-#include "schnorr.h"
-#include "num.h"
-#include "field.h"
-#include "group.h"
-#include "ecmult.h"
-#include "ecmult_gen.h"
-
-/**
- * Custom Schnorr-based signature scheme. They support multiparty signing, public key
- * recovery and batch validation.
- *
- * Rationale for verifying R's y coordinate:
- * In order to support batch validation and public key recovery, the full R point must
- * be known to verifiers, rather than just its x coordinate. In order to not risk
- * being more strict in batch validation than normal validation, validators must be
- * required to reject signatures with incorrect y coordinate. This is only possible
- * by including a (relatively slow) field inverse, or a field square root. However,
- * batch validation offers potentially much higher benefits than this cost.
- *
- * Rationale for having an implicit y coordinate oddness:
- * If we commit to having the full R point known to verifiers, there are two mechanism.
- * Either include its oddness in the signature, or give it an implicit fixed value.
- * As the R y coordinate can be flipped by a simple negation of the nonce, we choose the
- * latter, as it comes with nearly zero impact on signing or validation performance, and
- * saves a byte in the signature.
- *
- * Signing:
- * Inputs: 32-byte message m, 32-byte scalar key x (!=0), 32-byte scalar nonce k (!=0)
- *
- * Compute point R = k * G. Reject nonce if R's y coordinate is odd (or negate nonce).
- * Compute 32-byte r, the serialization of R's x coordinate.
- * Compute scalar h = Hash(r || m). Reject nonce if h == 0 or h >= order.
- * Compute scalar s = k - h * x.
- * The signature is (r, s).
- *
- *
- * Verification:
- * Inputs: 32-byte message m, public key point Q, signature: (32-byte r, scalar s)
- *
- * Signature is invalid if s >= order.
- * Signature is invalid if r >= p.
- * Compute scalar h = Hash(r || m). Signature is invalid if h == 0 or h >= order.
- * Option 1 (faster for single verification):
- * Compute point R = h * Q + s * G. Signature is invalid if R is infinity or R's y coordinate is odd.
- * Signature is valid if the serialization of R's x coordinate equals r.
- * Option 2 (allows batch validation and pubkey recovery):
- * Decompress x coordinate r into point R, with odd y coordinate. Fail if R is not on the curve.
- * Signature is valid if R + h * Q + s * G == 0.
- */
-
-static int secp256k1_schnorr_sig_sign(const secp256k1_ecmult_gen_context* ctx, unsigned char *sig64, const secp256k1_scalar *key, const secp256k1_scalar *nonce, const secp256k1_ge *pubnonce, secp256k1_schnorr_msghash hash, const unsigned char *msg32) {
- secp256k1_gej Rj;
- secp256k1_ge Ra;
- unsigned char h32[32];
- secp256k1_scalar h, s;
- int overflow;
- secp256k1_scalar n;
-
- if (secp256k1_scalar_is_zero(key) || secp256k1_scalar_is_zero(nonce)) {
- return 0;
- }
- n = *nonce;
-
- secp256k1_ecmult_gen(ctx, &Rj, &n);
- if (pubnonce != NULL) {
- secp256k1_gej_add_ge(&Rj, &Rj, pubnonce);
- }
- secp256k1_ge_set_gej(&Ra, &Rj);
- secp256k1_fe_normalize(&Ra.y);
- if (secp256k1_fe_is_odd(&Ra.y)) {
- /* R's y coordinate is odd, which is not allowed (see rationale above).
- Force it to be even by negating the nonce. Note that this even works
- for multiparty signing, as the R point is known to all participants,
- which can all decide to flip the sign in unison, resulting in the
- overall R point to be negated too. */
- secp256k1_scalar_negate(&n, &n);
- }
- secp256k1_fe_normalize(&Ra.x);
- secp256k1_fe_get_b32(sig64, &Ra.x);
- hash(h32, sig64, msg32);
- overflow = 0;
- secp256k1_scalar_set_b32(&h, h32, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&h)) {
- secp256k1_scalar_clear(&n);
- return 0;
- }
- secp256k1_scalar_mul(&s, &h, key);
- secp256k1_scalar_negate(&s, &s);
- secp256k1_scalar_add(&s, &s, &n);
- secp256k1_scalar_clear(&n);
- secp256k1_scalar_get_b32(sig64 + 32, &s);
- return 1;
-}
-
-static int secp256k1_schnorr_sig_verify(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, const secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32) {
- secp256k1_gej Qj, Rj;
- secp256k1_ge Ra;
- secp256k1_fe Rx;
- secp256k1_scalar h, s;
- unsigned char hh[32];
- int overflow;
-
- if (secp256k1_ge_is_infinity(pubkey)) {
- return 0;
- }
- hash(hh, sig64, msg32);
- overflow = 0;
- secp256k1_scalar_set_b32(&h, hh, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&h)) {
- return 0;
- }
- overflow = 0;
- secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
- if (overflow) {
- return 0;
- }
- if (!secp256k1_fe_set_b32(&Rx, sig64)) {
- return 0;
- }
- secp256k1_gej_set_ge(&Qj, pubkey);
- secp256k1_ecmult(ctx, &Rj, &Qj, &h, &s);
- if (secp256k1_gej_is_infinity(&Rj)) {
- return 0;
- }
- secp256k1_ge_set_gej_var(&Ra, &Rj);
- secp256k1_fe_normalize_var(&Ra.y);
- if (secp256k1_fe_is_odd(&Ra.y)) {
- return 0;
- }
- return secp256k1_fe_equal_var(&Rx, &Ra.x);
-}
-
-static int secp256k1_schnorr_sig_recover(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32) {
- secp256k1_gej Qj, Rj;
- secp256k1_ge Ra;
- secp256k1_fe Rx;
- secp256k1_scalar h, s;
- unsigned char hh[32];
- int overflow;
-
- hash(hh, sig64, msg32);
- overflow = 0;
- secp256k1_scalar_set_b32(&h, hh, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&h)) {
- return 0;
- }
- overflow = 0;
- secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
- if (overflow) {
- return 0;
- }
- if (!secp256k1_fe_set_b32(&Rx, sig64)) {
- return 0;
- }
- if (!secp256k1_ge_set_xo_var(&Ra, &Rx, 0)) {
- return 0;
- }
- secp256k1_gej_set_ge(&Rj, &Ra);
- secp256k1_scalar_inverse_var(&h, &h);
- secp256k1_scalar_negate(&s, &s);
- secp256k1_scalar_mul(&s, &s, &h);
- secp256k1_ecmult(ctx, &Qj, &Rj, &h, &s);
- if (secp256k1_gej_is_infinity(&Qj)) {
- return 0;
- }
- secp256k1_ge_set_gej(pubkey, &Qj);
- return 1;
-}
-
-static int secp256k1_schnorr_sig_combine(unsigned char *sig64, int n, const unsigned char * const *sig64ins) {
- secp256k1_scalar s = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
- int i;
- for (i = 0; i < n; i++) {
- secp256k1_scalar si;
- int overflow;
- secp256k1_scalar_set_b32(&si, sig64ins[i] + 32, &overflow);
- if (overflow) {
- return -1;
- }
- if (i) {
- if (memcmp(sig64ins[i - 1], sig64ins[i], 32) != 0) {
- return -1;
- }
- }
- secp256k1_scalar_add(&s, &s, &si);
- }
- if (secp256k1_scalar_is_zero(&s)) {
- return 0;
- }
- memcpy(sig64, sig64ins[0], 32);
- secp256k1_scalar_get_b32(sig64 + 32, &s);
- secp256k1_scalar_clear(&s);
- return 1;
-}
-
-#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/schnorr/tests_impl.h b/crypto/secp256k1/libsecp256k1/src/modules/schnorr/tests_impl.h
deleted file mode 100644
index 79737f7487..0000000000
--- a/crypto/secp256k1/libsecp256k1/src/modules/schnorr/tests_impl.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/**********************************************************************
- * Copyright (c) 2014-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef SECP256K1_MODULE_SCHNORR_TESTS
-#define SECP256K1_MODULE_SCHNORR_TESTS
-
-#include "include/secp256k1_schnorr.h"
-
-void test_schnorr_end_to_end(void) {
- unsigned char privkey[32];
- unsigned char message[32];
- unsigned char schnorr_signature[64];
- secp256k1_pubkey pubkey, recpubkey;
-
- /* Generate a random key and message. */
- {
- secp256k1_scalar key;
- random_scalar_order_test(&key);
- secp256k1_scalar_get_b32(privkey, &key);
- secp256k1_rand256_test(message);
- }
-
- /* Construct and verify corresponding public key. */
- CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1);
- CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1);
-
- /* Schnorr sign. */
- CHECK(secp256k1_schnorr_sign(ctx, schnorr_signature, message, privkey, NULL, NULL) == 1);
- CHECK(secp256k1_schnorr_verify(ctx, schnorr_signature, message, &pubkey) == 1);
- CHECK(secp256k1_schnorr_recover(ctx, &recpubkey, schnorr_signature, message) == 1);
- CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0);
- /* Destroy signature and verify again. */
- schnorr_signature[secp256k1_rand32() % 64] += 1 + (secp256k1_rand32() % 255);
- CHECK(secp256k1_schnorr_verify(ctx, schnorr_signature, message, &pubkey) == 0);
- CHECK(secp256k1_schnorr_recover(ctx, &recpubkey, schnorr_signature, message) != 1 ||
- memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0);
-}
-
-/** Horribly broken hash function. Do not use for anything but tests. */
-void test_schnorr_hash(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32) {
- int i;
- for (i = 0; i < 32; i++) {
- h32[i] = r32[i] ^ msg32[i];
- }
-}
-
-void test_schnorr_sign_verify(void) {
- unsigned char msg32[32];
- unsigned char sig64[3][64];
- secp256k1_gej pubkeyj[3];
- secp256k1_ge pubkey[3];
- secp256k1_scalar nonce[3], key[3];
- int i = 0;
- int k;
-
- secp256k1_rand256_test(msg32);
-
- for (k = 0; k < 3; k++) {
- random_scalar_order_test(&key[k]);
-
- do {
- random_scalar_order_test(&nonce[k]);
- if (secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64[k], &key[k], &nonce[k], NULL, &test_schnorr_hash, msg32)) {
- break;
- }
- } while(1);
-
- secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubkeyj[k], &key[k]);
- secp256k1_ge_set_gej_var(&pubkey[k], &pubkeyj[k]);
- CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64[k], &pubkey[k], &test_schnorr_hash, msg32));
-
- for (i = 0; i < 4; i++) {
- int pos = secp256k1_rand32() % 64;
- int mod = 1 + (secp256k1_rand32() % 255);
- sig64[k][pos] ^= mod;
- CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64[k], &pubkey[k], &test_schnorr_hash, msg32) == 0);
- sig64[k][pos] ^= mod;
- }
- }
-}
-
-void test_schnorr_threshold(void) {
- unsigned char msg[32];
- unsigned char sec[5][32];
- secp256k1_pubkey pub[5];
- unsigned char nonce[5][32];
- secp256k1_pubkey pubnonce[5];
- unsigned char sig[5][64];
- const unsigned char* sigs[5];
- unsigned char allsig[64];
- const secp256k1_pubkey* pubs[5];
- secp256k1_pubkey allpub;
- int n, i;
- int damage;
- int ret = 0;
-
- damage = (secp256k1_rand32() % 2) ? (1 + (secp256k1_rand32() % 4)) : 0;
- secp256k1_rand256_test(msg);
- n = 2 + (secp256k1_rand32() % 4);
- for (i = 0; i < n; i++) {
- do {
- secp256k1_rand256_test(sec[i]);
- } while (!secp256k1_ec_seckey_verify(ctx, sec[i]));
- CHECK(secp256k1_ec_pubkey_create(ctx, &pub[i], sec[i]));
- CHECK(secp256k1_schnorr_generate_nonce_pair(ctx, &pubnonce[i], nonce[i], msg, sec[i], NULL, NULL));
- pubs[i] = &pub[i];
- }
- if (damage == 1) {
- nonce[secp256k1_rand32() % n][secp256k1_rand32() % 32] ^= 1 + (secp256k1_rand32() % 255);
- } else if (damage == 2) {
- sec[secp256k1_rand32() % n][secp256k1_rand32() % 32] ^= 1 + (secp256k1_rand32() % 255);
- }
- for (i = 0; i < n; i++) {
- secp256k1_pubkey allpubnonce;
- const secp256k1_pubkey *pubnonces[4];
- int j;
- for (j = 0; j < i; j++) {
- pubnonces[j] = &pubnonce[j];
- }
- for (j = i + 1; j < n; j++) {
- pubnonces[j - 1] = &pubnonce[j];
- }
- CHECK(secp256k1_ec_pubkey_combine(ctx, &allpubnonce, pubnonces, n - 1));
- ret |= (secp256k1_schnorr_partial_sign(ctx, sig[i], msg, sec[i], &allpubnonce, nonce[i]) != 1) * 1;
- sigs[i] = sig[i];
- }
- if (damage == 3) {
- sig[secp256k1_rand32() % n][secp256k1_rand32() % 64] ^= 1 + (secp256k1_rand32() % 255);
- }
- ret |= (secp256k1_ec_pubkey_combine(ctx, &allpub, pubs, n) != 1) * 2;
- if ((ret & 1) == 0) {
- ret |= (secp256k1_schnorr_partial_combine(ctx, allsig, sigs, n) != 1) * 4;
- }
- if (damage == 4) {
- allsig[secp256k1_rand32() % 32] ^= 1 + (secp256k1_rand32() % 255);
- }
- if ((ret & 7) == 0) {
- ret |= (secp256k1_schnorr_verify(ctx, allsig, msg, &allpub) != 1) * 8;
- }
- CHECK((ret == 0) == (damage == 0));
-}
-
-void test_schnorr_recovery(void) {
- unsigned char msg32[32];
- unsigned char sig64[64];
- secp256k1_ge Q;
-
- secp256k1_rand256_test(msg32);
- secp256k1_rand256_test(sig64);
- secp256k1_rand256_test(sig64 + 32);
- if (secp256k1_schnorr_sig_recover(&ctx->ecmult_ctx, sig64, &Q, &test_schnorr_hash, msg32) == 1) {
- CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &Q, &test_schnorr_hash, msg32) == 1);
- }
-}
-
-void run_schnorr_tests(void) {
- int i;
- for (i = 0; i < 32*count; i++) {
- test_schnorr_end_to_end();
- }
- for (i = 0; i < 32 * count; i++) {
- test_schnorr_sign_verify();
- }
- for (i = 0; i < 16 * count; i++) {
- test_schnorr_recovery();
- }
- for (i = 0; i < 10 * count; i++) {
- test_schnorr_threshold();
- }
-}
-
-#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/num.h b/crypto/secp256k1/libsecp256k1/src/num.h
index ebfa71eb44..7bb9c5be8c 100644
--- a/crypto/secp256k1/libsecp256k1/src/num.h
+++ b/crypto/secp256k1/libsecp256k1/src/num.h
@@ -32,6 +32,9 @@ static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsi
/** Compute a modular inverse. The input must be less than the modulus. */
static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m);
+/** Compute the jacobi symbol (a|b). b must be positive and odd. */
+static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b);
+
/** Compare the absolute value of two numbers. */
static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b);
@@ -57,6 +60,9 @@ static void secp256k1_num_shift(secp256k1_num *r, int bits);
/** Check whether a number is zero. */
static int secp256k1_num_is_zero(const secp256k1_num *a);
+/** Check whether a number is one. */
+static int secp256k1_num_is_one(const secp256k1_num *a);
+
/** Check whether a number is strictly negative. */
static int secp256k1_num_is_neg(const secp256k1_num *a);
diff --git a/crypto/secp256k1/libsecp256k1/src/num_gmp_impl.h b/crypto/secp256k1/libsecp256k1/src/num_gmp_impl.h
index f43e7a56cc..3a46495eea 100644
--- a/crypto/secp256k1/libsecp256k1/src/num_gmp_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/num_gmp_impl.h
@@ -70,6 +70,7 @@ static void secp256k1_num_add_abs(secp256k1_num *r, const secp256k1_num *a, cons
static void secp256k1_num_sub_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) {
mp_limb_t c = mpn_sub(r->data, a->data, a->limbs, b->data, b->limbs);
+ (void)c;
VERIFY_CHECK(c == 0);
r->limbs = a->limbs;
while (r->limbs > 1 && r->data[r->limbs-1]==0) {
@@ -125,6 +126,7 @@ static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a,
}
sn = NUM_LIMBS+1;
gn = mpn_gcdext(g, r->data, &sn, u, m->limbs, v, m->limbs);
+ (void)gn;
VERIFY_CHECK(gn == 1);
VERIFY_CHECK(g[0] == 1);
r->neg = a->neg ^ m->neg;
@@ -142,6 +144,32 @@ static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a,
memset(v, 0, sizeof(v));
}
+static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b) {
+ int ret;
+ mpz_t ga, gb;
+ secp256k1_num_sanity(a);
+ secp256k1_num_sanity(b);
+ VERIFY_CHECK(!b->neg && (b->limbs > 0) && (b->data[0] & 1));
+
+ mpz_inits(ga, gb, NULL);
+
+ mpz_import(gb, b->limbs, -1, sizeof(mp_limb_t), 0, 0, b->data);
+ mpz_import(ga, a->limbs, -1, sizeof(mp_limb_t), 0, 0, a->data);
+ if (a->neg) {
+ mpz_neg(ga, ga);
+ }
+
+ ret = mpz_jacobi(ga, gb);
+
+ mpz_clears(ga, gb, NULL);
+
+ return ret;
+}
+
+static int secp256k1_num_is_one(const secp256k1_num *a) {
+ return (a->limbs == 1 && a->data[0] == 1);
+}
+
static int secp256k1_num_is_zero(const secp256k1_num *a) {
return (a->limbs == 1 && a->data[0] == 0);
}
diff --git a/crypto/secp256k1/libsecp256k1/src/scalar.h b/crypto/secp256k1/libsecp256k1/src/scalar.h
index b590ccd6dd..27e9d8375e 100644
--- a/crypto/secp256k1/libsecp256k1/src/scalar.h
+++ b/crypto/secp256k1/libsecp256k1/src/scalar.h
@@ -13,7 +13,9 @@
#include "libsecp256k1-config.h"
#endif
-#if defined(USE_SCALAR_4X64)
+#if defined(EXHAUSTIVE_TEST_ORDER)
+#include "scalar_low.h"
+#elif defined(USE_SCALAR_4X64)
#include "scalar_4x64.h"
#elif defined(USE_SCALAR_8X32)
#include "scalar_8x32.h"
diff --git a/crypto/secp256k1/libsecp256k1/src/scalar_4x64_impl.h b/crypto/secp256k1/libsecp256k1/src/scalar_4x64_impl.h
index cbec34d716..56e7bd82af 100644
--- a/crypto/secp256k1/libsecp256k1/src/scalar_4x64_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/scalar_4x64_impl.h
@@ -282,8 +282,8 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"movq 56(%%rsi), %%r14\n"
/* Initialize r8,r9,r10 */
"movq 0(%%rsi), %%r8\n"
- "movq $0, %%r9\n"
- "movq $0, %%r10\n"
+ "xorq %%r9, %%r9\n"
+ "xorq %%r10, %%r10\n"
/* (r8,r9) += n0 * c0 */
"movq %8, %%rax\n"
"mulq %%r11\n"
@@ -291,7 +291,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq %%rdx, %%r9\n"
/* extract m0 */
"movq %%r8, %q0\n"
- "movq $0, %%r8\n"
+ "xorq %%r8, %%r8\n"
/* (r9,r10) += l1 */
"addq 8(%%rsi), %%r9\n"
"adcq $0, %%r10\n"
@@ -309,7 +309,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r8\n"
/* extract m1 */
"movq %%r9, %q1\n"
- "movq $0, %%r9\n"
+ "xorq %%r9, %%r9\n"
/* (r10,r8,r9) += l2 */
"addq 16(%%rsi), %%r10\n"
"adcq $0, %%r8\n"
@@ -332,7 +332,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r9\n"
/* extract m2 */
"movq %%r10, %q2\n"
- "movq $0, %%r10\n"
+ "xorq %%r10, %%r10\n"
/* (r8,r9,r10) += l3 */
"addq 24(%%rsi), %%r8\n"
"adcq $0, %%r9\n"
@@ -355,7 +355,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r10\n"
/* extract m3 */
"movq %%r8, %q3\n"
- "movq $0, %%r8\n"
+ "xorq %%r8, %%r8\n"
/* (r9,r10,r8) += n3 * c1 */
"movq %9, %%rax\n"
"mulq %%r14\n"
@@ -387,8 +387,8 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"movq %q11, %%r13\n"
/* Initialize (r8,r9,r10) */
"movq %q5, %%r8\n"
- "movq $0, %%r9\n"
- "movq $0, %%r10\n"
+ "xorq %%r9, %%r9\n"
+ "xorq %%r10, %%r10\n"
/* (r8,r9) += m4 * c0 */
"movq %12, %%rax\n"
"mulq %%r11\n"
@@ -396,7 +396,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq %%rdx, %%r9\n"
/* extract p0 */
"movq %%r8, %q0\n"
- "movq $0, %%r8\n"
+ "xorq %%r8, %%r8\n"
/* (r9,r10) += m1 */
"addq %q6, %%r9\n"
"adcq $0, %%r10\n"
@@ -414,7 +414,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r8\n"
/* extract p1 */
"movq %%r9, %q1\n"
- "movq $0, %%r9\n"
+ "xorq %%r9, %%r9\n"
/* (r10,r8,r9) += m2 */
"addq %q7, %%r10\n"
"adcq $0, %%r8\n"
@@ -472,7 +472,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"movq %%rax, 0(%q6)\n"
/* Move to (r8,r9) */
"movq %%rdx, %%r8\n"
- "movq $0, %%r9\n"
+ "xorq %%r9, %%r9\n"
/* (r8,r9) += p1 */
"addq %q2, %%r8\n"
"adcq $0, %%r9\n"
@@ -483,7 +483,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq %%rdx, %%r9\n"
/* Extract r1 */
"movq %%r8, 8(%q6)\n"
- "movq $0, %%r8\n"
+ "xorq %%r8, %%r8\n"
/* (r9,r8) += p4 */
"addq %%r10, %%r9\n"
"adcq $0, %%r8\n"
@@ -492,7 +492,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r8\n"
/* Extract r2 */
"movq %%r9, 16(%q6)\n"
- "movq $0, %%r9\n"
+ "xorq %%r9, %%r9\n"
/* (r8,r9) += p3 */
"addq %q4, %%r8\n"
"adcq $0, %%r9\n"
@@ -912,6 +912,7 @@ static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a)
secp256k1_scalar_reduce_512(r, l);
}
+#ifdef USE_ENDOMORPHISM
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
r1->d[0] = a->d[0];
r1->d[1] = a->d[1];
@@ -922,6 +923,7 @@ static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r
r2->d[2] = 0;
r2->d[3] = 0;
}
+#endif
SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) {
return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3])) == 0;
diff --git a/crypto/secp256k1/libsecp256k1/src/scalar_impl.h b/crypto/secp256k1/libsecp256k1/src/scalar_impl.h
index 88ea97de86..f5b2376407 100644
--- a/crypto/secp256k1/libsecp256k1/src/scalar_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/scalar_impl.h
@@ -7,8 +7,6 @@
#ifndef _SECP256K1_SCALAR_IMPL_H_
#define _SECP256K1_SCALAR_IMPL_H_
-#include
-
#include "group.h"
#include "scalar.h"
@@ -16,7 +14,9 @@
#include "libsecp256k1-config.h"
#endif
-#if defined(USE_SCALAR_4X64)
+#if defined(EXHAUSTIVE_TEST_ORDER)
+#include "scalar_low_impl.h"
+#elif defined(USE_SCALAR_4X64)
#include "scalar_4x64_impl.h"
#elif defined(USE_SCALAR_8X32)
#include "scalar_8x32_impl.h"
@@ -33,17 +33,37 @@ static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a
/** secp256k1 curve order, see secp256k1_ecdsa_const_order_as_fe in ecdsa_impl.h */
static void secp256k1_scalar_order_get_num(secp256k1_num *r) {
+#if defined(EXHAUSTIVE_TEST_ORDER)
+ static const unsigned char order[32] = {
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,EXHAUSTIVE_TEST_ORDER
+ };
+#else
static const unsigned char order[32] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41
};
+#endif
secp256k1_num_set_bin(r, order, 32);
}
#endif
static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
+#if defined(EXHAUSTIVE_TEST_ORDER)
+ int i;
+ *r = 0;
+ for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++)
+ if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1)
+ *r = i;
+ /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus
+ * have a composite group order; fix it in exhaustive_tests.c). */
+ VERIFY_CHECK(*r != 0);
+}
+#else
secp256k1_scalar *t;
int i;
/* First compute x ^ (2^N - 1) for some values of N. */
@@ -235,9 +255,9 @@ static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar
}
SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
- /* d[0] is present and is the lowest word for all representations */
return !(a->d[0] & 1);
}
+#endif
static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
#if defined(USE_SCALAR_INV_BUILTIN)
@@ -261,6 +281,18 @@ static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_sc
}
#ifdef USE_ENDOMORPHISM
+#if defined(EXHAUSTIVE_TEST_ORDER)
+/**
+ * Find k1 and k2 given k, such that k1 + k2 * lambda == k mod n; unlike in the
+ * full case we don't bother making k1 and k2 be small, we just want them to be
+ * nontrivial to get full test coverage for the exhaustive tests. We therefore
+ * (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda.
+ */
+static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
+ *r2 = (*a + 5) % EXHAUSTIVE_TEST_ORDER;
+ *r1 = (*a + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER;
+}
+#else
/**
* The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where
* lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a,
@@ -333,5 +365,6 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar
secp256k1_scalar_add(r1, r1, a);
}
#endif
+#endif
#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/scalar_low.h b/crypto/secp256k1/libsecp256k1/src/scalar_low.h
new file mode 100644
index 0000000000..5574c44c7a
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/src/scalar_low.h
@@ -0,0 +1,15 @@
+/**********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_SCALAR_REPR_
+#define _SECP256K1_SCALAR_REPR_
+
+#include
+
+/** A scalar modulo the group order of the secp256k1 curve. */
+typedef uint32_t secp256k1_scalar;
+
+#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/scalar_low_impl.h b/crypto/secp256k1/libsecp256k1/src/scalar_low_impl.h
new file mode 100644
index 0000000000..4f94441f49
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/src/scalar_low_impl.h
@@ -0,0 +1,114 @@
+/**********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_
+#define _SECP256K1_SCALAR_REPR_IMPL_H_
+
+#include "scalar.h"
+
+#include
+
+SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
+ return !(*a & 1);
+}
+
+SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { *r = 0; }
+SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { *r = v; }
+
+SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
+ if (offset < 32)
+ return ((*a >> offset) & ((((uint32_t)1) << count) - 1));
+ else
+ return 0;
+}
+
+SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
+ return secp256k1_scalar_get_bits(a, offset, count);
+}
+
+SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { return *a >= EXHAUSTIVE_TEST_ORDER; }
+
+static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) {
+ *r = (*a + *b) % EXHAUSTIVE_TEST_ORDER;
+ return *r < *b;
+}
+
+static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) {
+ if (flag && bit < 32)
+ *r += (1 << bit);
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0);
+#endif
+}
+
+static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) {
+ const int base = 0x100 % EXHAUSTIVE_TEST_ORDER;
+ int i;
+ *r = 0;
+ for (i = 0; i < 32; i++) {
+ *r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER;
+ }
+ /* just deny overflow, it basically always happens */
+ if (overflow) *overflow = 0;
+}
+
+static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) {
+ memset(bin, 0, 32);
+ bin[28] = *a >> 24; bin[29] = *a >> 16; bin[30] = *a >> 8; bin[31] = *a;
+}
+
+SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) {
+ return *a == 0;
+}
+
+static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) {
+ if (*a == 0) {
+ *r = 0;
+ } else {
+ *r = EXHAUSTIVE_TEST_ORDER - *a;
+ }
+}
+
+SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) {
+ return *a == 1;
+}
+
+static int secp256k1_scalar_is_high(const secp256k1_scalar *a) {
+ return *a > EXHAUSTIVE_TEST_ORDER / 2;
+}
+
+static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
+ if (flag) secp256k1_scalar_negate(r, r);
+ return flag ? -1 : 1;
+}
+
+static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) {
+ *r = (*a * *b) % EXHAUSTIVE_TEST_ORDER;
+}
+
+static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) {
+ int ret;
+ VERIFY_CHECK(n > 0);
+ VERIFY_CHECK(n < 16);
+ ret = *r & ((1 << n) - 1);
+ *r >>= n;
+ return ret;
+}
+
+static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) {
+ *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER;
+}
+
+static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
+ *r1 = *a;
+ *r2 = 0;
+}
+
+SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) {
+ return *a == *b;
+}
+
+#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/secp256k1.c b/crypto/secp256k1/libsecp256k1/src/secp256k1.c
old mode 100644
new mode 100755
index 203f880af4..fb8b882faa
--- a/crypto/secp256k1/libsecp256k1/src/secp256k1.c
+++ b/crypto/secp256k1/libsecp256k1/src/secp256k1.c
@@ -4,8 +4,6 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#define SECP256K1_BUILD (1)
-
#include "include/secp256k1.h"
#include "util.h"
@@ -62,13 +60,20 @@ secp256k1_context* secp256k1_context_create(unsigned int flags) {
ret->illegal_callback = default_illegal_callback;
ret->error_callback = default_error_callback;
+ if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) {
+ secp256k1_callback_call(&ret->illegal_callback,
+ "Invalid flags");
+ free(ret);
+ return NULL;
+ }
+
secp256k1_ecmult_context_init(&ret->ecmult_ctx);
secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx);
- if (flags & SECP256K1_CONTEXT_SIGN) {
+ if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) {
secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback);
}
- if (flags & SECP256K1_CONTEXT_VERIFY) {
+ if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) {
secp256k1_ecmult_context_build(&ret->ecmult_ctx, &ret->error_callback);
}
@@ -145,9 +150,11 @@ static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) {
int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) {
secp256k1_ge Q;
- (void)ctx;
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(pubkey != NULL);
+ memset(pubkey, 0, sizeof(*pubkey));
+ ARG_CHECK(input != NULL);
if (!secp256k1_eckey_pubkey_parse(&Q, input, inputlen)) {
- memset(pubkey, 0, sizeof(*pubkey));
return 0;
}
secp256k1_pubkey_save(pubkey, &Q);
@@ -157,10 +164,25 @@ int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pu
int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey* pubkey, unsigned int flags) {
secp256k1_ge Q;
+ size_t len;
+ int ret = 0;
- (void)ctx;
- return (secp256k1_pubkey_load(ctx, &Q, pubkey) &&
- secp256k1_eckey_pubkey_serialize(&Q, output, outputlen, flags));
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(outputlen != NULL);
+ ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33 : 65));
+ len = *outputlen;
+ *outputlen = 0;
+ ARG_CHECK(output != NULL);
+ memset(output, 0, len);
+ ARG_CHECK(pubkey != NULL);
+ ARG_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_COMPRESSION);
+ if (secp256k1_pubkey_load(ctx, &Q, pubkey)) {
+ ret = secp256k1_eckey_pubkey_serialize(&Q, output, &len, flags & SECP256K1_FLAGS_BIT_COMPRESSION);
+ if (ret) {
+ *outputlen = len;
+ }
+ }
+ return ret;
}
static void secp256k1_ecdsa_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_ecdsa_signature* sig) {
@@ -190,7 +212,7 @@ static void secp256k1_ecdsa_signature_save(secp256k1_ecdsa_signature* sig, const
int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
secp256k1_scalar r, s;
- (void)ctx;
+ VERIFY_CHECK(ctx != NULL);
ARG_CHECK(sig != NULL);
ARG_CHECK(input != NULL);
@@ -203,10 +225,31 @@ int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_
}
}
+int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input64) {
+ secp256k1_scalar r, s;
+ int ret = 1;
+ int overflow = 0;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(sig != NULL);
+ ARG_CHECK(input64 != NULL);
+
+ secp256k1_scalar_set_b32(&r, &input64[0], &overflow);
+ ret &= !overflow;
+ secp256k1_scalar_set_b32(&s, &input64[32], &overflow);
+ ret &= !overflow;
+ if (ret) {
+ secp256k1_ecdsa_signature_save(sig, &r, &s);
+ } else {
+ memset(sig, 0, sizeof(*sig));
+ }
+ return ret;
+}
+
int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig) {
secp256k1_scalar r, s;
- (void)ctx;
+ VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL);
ARG_CHECK(outputlen != NULL);
ARG_CHECK(sig != NULL);
@@ -215,6 +258,38 @@ int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsign
return secp256k1_ecdsa_sig_serialize(output, outputlen, &r, &s);
}
+int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, const secp256k1_ecdsa_signature* sig) {
+ secp256k1_scalar r, s;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(output64 != NULL);
+ ARG_CHECK(sig != NULL);
+
+ secp256k1_ecdsa_signature_load(ctx, &r, &s, sig);
+ secp256k1_scalar_get_b32(&output64[0], &r);
+ secp256k1_scalar_get_b32(&output64[32], &s);
+ return 1;
+}
+
+int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sigout, const secp256k1_ecdsa_signature *sigin) {
+ secp256k1_scalar r, s;
+ int ret = 0;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(sigin != NULL);
+
+ secp256k1_ecdsa_signature_load(ctx, &r, &s, sigin);
+ ret = secp256k1_scalar_is_high(&s);
+ if (sigout != NULL) {
+ if (ret) {
+ secp256k1_scalar_negate(&s, &s);
+ }
+ secp256k1_ecdsa_signature_save(sigout, &r, &s);
+ }
+
+ return ret;
+}
+
int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey) {
secp256k1_ge q;
secp256k1_scalar r, s;
@@ -227,7 +302,8 @@ int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_s
secp256k1_scalar_set_b32(&m, msg32, NULL);
secp256k1_ecdsa_signature_load(ctx, &r, &s, sig);
- return (secp256k1_pubkey_load(ctx, &q, pubkey) &&
+ return (!secp256k1_scalar_is_high(&s) &&
+ secp256k1_pubkey_load(ctx, &q, pubkey) &&
secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &r, &s, &q, &m));
}
@@ -239,8 +315,10 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m
/* We feed a byte array to the PRNG as input, consisting of:
* - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d.
* - optionally 32 extra bytes of data, see RFC 6979 3.6 Additional Data.
- * - optionally 16 extra bytes with the algorithm name (the extra data bytes
- * are set to zeroes when not present, while the algorithm name is).
+ * - optionally 16 extra bytes with the algorithm name.
+ * Because the arguments have distinct fixed lengths it is not possible for
+ * different argument mixtures to emulate each other and result in the same
+ * nonces.
*/
memcpy(keydata, key32, 32);
memcpy(keydata + 32, msg32, 32);
@@ -249,9 +327,8 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m
keylen = 96;
}
if (algo16 != NULL) {
- memset(keydata + keylen, 0, 96 - keylen);
- memcpy(keydata + 96, algo16, 16);
- keylen = 112;
+ memcpy(keydata + keylen, algo16, 16);
+ keylen += 16;
}
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen);
memset(keydata, 0, sizeof(keydata));
@@ -282,16 +359,15 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
/* Fail if the secret key is invalid. */
if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
+ unsigned char nonce32[32];
unsigned int count = 0;
secp256k1_scalar_set_b32(&msg, msg32, NULL);
while (1) {
- unsigned char nonce32[32];
ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
if (!ret) {
break;
}
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
- memset(nonce32, 0, 32);
if (!overflow && !secp256k1_scalar_is_zero(&non)) {
if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) {
break;
@@ -299,6 +375,7 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature
}
count++;
}
+ memset(nonce32, 0, 32);
secp256k1_scalar_clear(&msg);
secp256k1_scalar_clear(&non);
secp256k1_scalar_clear(&sec);
@@ -317,7 +394,6 @@ int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char
int overflow;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
- (void)ctx;
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
ret = !overflow && !secp256k1_scalar_is_zero(&sec);
@@ -332,19 +408,19 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p
int overflow;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(pubkey != NULL);
+ memset(pubkey, 0, sizeof(*pubkey));
+ ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(seckey != NULL);
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
ret = (!overflow) & (!secp256k1_scalar_is_zero(&sec));
- secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec);
- secp256k1_ge_set_gej(&p, &pj);
- secp256k1_pubkey_save(pubkey, &p);
- secp256k1_scalar_clear(&sec);
- if (!ret) {
- memset(pubkey, 0, sizeof(*pubkey));
+ if (ret) {
+ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec);
+ secp256k1_ge_set_gej(&p, &pj);
+ secp256k1_pubkey_save(pubkey, &p);
}
+ secp256k1_scalar_clear(&sec);
return ret;
}
@@ -356,12 +432,12 @@ int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
ARG_CHECK(tweak != NULL);
- (void)ctx;
secp256k1_scalar_set_b32(&term, tweak, &overflow);
secp256k1_scalar_set_b32(&sec, seckey, NULL);
ret = !overflow && secp256k1_eckey_privkey_tweak_add(&sec, &term);
+ memset(seckey, 0, 32);
if (ret) {
secp256k1_scalar_get_b32(seckey, &sec);
}
@@ -382,12 +458,13 @@ int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey
ARG_CHECK(tweak != NULL);
secp256k1_scalar_set_b32(&term, tweak, &overflow);
- if (!overflow && secp256k1_pubkey_load(ctx, &p, pubkey)) {
- ret = secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term);
- if (ret) {
+ ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey);
+ memset(pubkey, 0, sizeof(*pubkey));
+ if (ret) {
+ if (secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term)) {
secp256k1_pubkey_save(pubkey, &p);
} else {
- memset(pubkey, 0, sizeof(*pubkey));
+ ret = 0;
}
}
@@ -402,11 +479,11 @@ int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
ARG_CHECK(tweak != NULL);
- (void)ctx;
secp256k1_scalar_set_b32(&factor, tweak, &overflow);
secp256k1_scalar_set_b32(&sec, seckey, NULL);
ret = !overflow && secp256k1_eckey_privkey_tweak_mul(&sec, &factor);
+ memset(seckey, 0, 32);
if (ret) {
secp256k1_scalar_get_b32(seckey, &sec);
}
@@ -427,48 +504,19 @@ int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey
ARG_CHECK(tweak != NULL);
secp256k1_scalar_set_b32(&factor, tweak, &overflow);
- if (!overflow && secp256k1_pubkey_load(ctx, &p, pubkey)) {
- ret = secp256k1_eckey_pubkey_tweak_mul(&ctx->ecmult_ctx, &p, &factor);
- if (ret) {
+ ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey);
+ memset(pubkey, 0, sizeof(*pubkey));
+ if (ret) {
+ if (secp256k1_eckey_pubkey_tweak_mul(&ctx->ecmult_ctx, &p, &factor)) {
secp256k1_pubkey_save(pubkey, &p);
} else {
- memset(pubkey, 0, sizeof(*pubkey));
+ ret = 0;
}
}
return ret;
}
-int secp256k1_ec_privkey_export(const secp256k1_context* ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *seckey, unsigned int flags) {
- secp256k1_scalar key;
- int ret = 0;
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(seckey != NULL);
- ARG_CHECK(privkey != NULL);
- ARG_CHECK(privkeylen != NULL);
- ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
-
- secp256k1_scalar_set_b32(&key, seckey, NULL);
- ret = secp256k1_eckey_privkey_serialize(&ctx->ecmult_gen_ctx, privkey, privkeylen, &key, flags);
- secp256k1_scalar_clear(&key);
- return ret;
-}
-
-int secp256k1_ec_privkey_import(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *privkey, size_t privkeylen) {
- secp256k1_scalar key;
- int ret = 0;
- ARG_CHECK(seckey != NULL);
- ARG_CHECK(privkey != NULL);
- (void)ctx;
-
- ret = secp256k1_eckey_privkey_parse(&key, privkey, privkeylen);
- if (ret) {
- secp256k1_scalar_get_b32(seckey, &key);
- }
- secp256k1_scalar_clear(&key);
- return ret;
-}
-
int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
@@ -476,12 +524,13 @@ int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *see
return 1;
}
-int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, const secp256k1_pubkey * const *pubnonces, int n) {
- int i;
+int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, const secp256k1_pubkey * const *pubnonces, size_t n) {
+ size_t i;
secp256k1_gej Qj;
secp256k1_ge Q;
ARG_CHECK(pubnonce != NULL);
+ memset(pubnonce, 0, sizeof(*pubnonce));
ARG_CHECK(n >= 1);
ARG_CHECK(pubnonces != NULL);
@@ -492,7 +541,6 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *
secp256k1_gej_add_ge(&Qj, &Qj, &Q);
}
if (secp256k1_gej_is_infinity(&Qj)) {
- memset(pubnonce, 0, sizeof(*pubnonce));
return 0;
}
secp256k1_ge_set_gej(&Q, &Qj);
diff --git a/crypto/secp256k1/libsecp256k1/src/testrand.h b/crypto/secp256k1/libsecp256k1/src/testrand.h
index 041bb92c47..f8efa93c7c 100644
--- a/crypto/secp256k1/libsecp256k1/src/testrand.h
+++ b/crypto/secp256k1/libsecp256k1/src/testrand.h
@@ -16,13 +16,23 @@
/** Seed the pseudorandom number generator for testing. */
SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16);
-/** Generate a pseudorandom 32-bit number. */
+/** Generate a pseudorandom number in the range [0..2**32-1]. */
static uint32_t secp256k1_rand32(void);
+/** Generate a pseudorandom number in the range [0..2**bits-1]. Bits must be 1 or
+ * more. */
+static uint32_t secp256k1_rand_bits(int bits);
+
+/** Generate a pseudorandom number in the range [0..range-1]. */
+static uint32_t secp256k1_rand_int(uint32_t range);
+
/** Generate a pseudorandom 32-byte array. */
static void secp256k1_rand256(unsigned char *b32);
/** Generate a pseudorandom 32-byte array with long sequences of zero and one bits. */
static void secp256k1_rand256_test(unsigned char *b32);
+/** Generate pseudorandom bytes with long sequences of zero and one bits. */
+static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len);
+
#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/testrand_impl.h b/crypto/secp256k1/libsecp256k1/src/testrand_impl.h
index 7c35542662..15c7b9f12d 100644
--- a/crypto/secp256k1/libsecp256k1/src/testrand_impl.h
+++ b/crypto/secp256k1/libsecp256k1/src/testrand_impl.h
@@ -1,5 +1,5 @@
/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Copyright (c) 2013-2015 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
@@ -16,6 +16,8 @@
static secp256k1_rfc6979_hmac_sha256_t secp256k1_test_rng;
static uint32_t secp256k1_test_rng_precomputed[8];
static int secp256k1_test_rng_precomputed_used = 8;
+static uint64_t secp256k1_test_rng_integer;
+static int secp256k1_test_rng_integer_bits_left = 0;
SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16) {
secp256k1_rfc6979_hmac_sha256_initialize(&secp256k1_test_rng, seed16, 16);
@@ -29,32 +31,80 @@ SECP256K1_INLINE static uint32_t secp256k1_rand32(void) {
return secp256k1_test_rng_precomputed[secp256k1_test_rng_precomputed_used++];
}
+static uint32_t secp256k1_rand_bits(int bits) {
+ uint32_t ret;
+ if (secp256k1_test_rng_integer_bits_left < bits) {
+ secp256k1_test_rng_integer |= (((uint64_t)secp256k1_rand32()) << secp256k1_test_rng_integer_bits_left);
+ secp256k1_test_rng_integer_bits_left += 32;
+ }
+ ret = secp256k1_test_rng_integer;
+ secp256k1_test_rng_integer >>= bits;
+ secp256k1_test_rng_integer_bits_left -= bits;
+ ret &= ((~((uint32_t)0)) >> (32 - bits));
+ return ret;
+}
+
+static uint32_t secp256k1_rand_int(uint32_t range) {
+ /* We want a uniform integer between 0 and range-1, inclusive.
+ * B is the smallest number such that range <= 2**B.
+ * two mechanisms implemented here:
+ * - generate B bits numbers until one below range is found, and return it
+ * - find the largest multiple M of range that is <= 2**(B+A), generate B+A
+ * bits numbers until one below M is found, and return it modulo range
+ * The second mechanism consumes A more bits of entropy in every iteration,
+ * but may need fewer iterations due to M being closer to 2**(B+A) then
+ * range is to 2**B. The array below (indexed by B) contains a 0 when the
+ * first mechanism is to be used, and the number A otherwise.
+ */
+ static const int addbits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0};
+ uint32_t trange, mult;
+ int bits = 0;
+ if (range <= 1) {
+ return 0;
+ }
+ trange = range - 1;
+ while (trange > 0) {
+ trange >>= 1;
+ bits++;
+ }
+ if (addbits[bits]) {
+ bits = bits + addbits[bits];
+ mult = ((~((uint32_t)0)) >> (32 - bits)) / range;
+ trange = range * mult;
+ } else {
+ trange = range;
+ mult = 1;
+ }
+ while(1) {
+ uint32_t x = secp256k1_rand_bits(bits);
+ if (x < trange) {
+ return (mult == 1) ? x : (x % range);
+ }
+ }
+}
+
static void secp256k1_rand256(unsigned char *b32) {
secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, b32, 32);
}
-static void secp256k1_rand256_test(unsigned char *b32) {
- int bits=0;
- uint64_t ent = 0;
- int entleft = 0;
- memset(b32, 0, 32);
- while (bits < 256) {
+static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len) {
+ size_t bits = 0;
+ memset(bytes, 0, len);
+ while (bits < len * 8) {
int now;
uint32_t val;
- if (entleft < 12) {
- ent |= ((uint64_t)secp256k1_rand32()) << entleft;
- entleft += 32;
- }
- now = 1 + ((ent % 64)*((ent >> 6) % 32)+16)/31;
- val = 1 & (ent >> 11);
- ent >>= 12;
- entleft -= 12;
- while (now > 0 && bits < 256) {
- b32[bits / 8] |= val << (bits % 8);
+ now = 1 + (secp256k1_rand_bits(6) * secp256k1_rand_bits(5) + 16) / 31;
+ val = secp256k1_rand_bits(1);
+ while (now > 0 && bits < len * 8) {
+ bytes[bits / 8] |= val << (bits % 8);
now--;
bits++;
}
}
}
+static void secp256k1_rand256_test(unsigned char *b32) {
+ secp256k1_rand_bytes_test(b32, 32);
+}
+
#endif
diff --git a/crypto/secp256k1/libsecp256k1/src/tests.c b/crypto/secp256k1/libsecp256k1/src/tests.c
index 3366d90fca..9ae7d30281 100644
--- a/crypto/secp256k1/libsecp256k1/src/tests.c
+++ b/crypto/secp256k1/libsecp256k1/src/tests.c
@@ -13,8 +13,8 @@
#include
-#include "include/secp256k1.h"
#include "secp256k1.c"
+#include "include/secp256k1.h"
#include "testrand_impl.h"
#ifdef ENABLE_OPENSSL_TESTS
@@ -24,9 +24,39 @@
#include "openssl/obj_mac.h"
#endif
+#include "contrib/lax_der_parsing.c"
+#include "contrib/lax_der_privatekey_parsing.c"
+
+#if !defined(VG_CHECK)
+# if defined(VALGRIND)
+# include
+# define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y))
+# define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y))
+# else
+# define VG_UNDEF(x,y)
+# define VG_CHECK(x,y)
+# endif
+#endif
+
static int count = 64;
static secp256k1_context *ctx = NULL;
+static void counting_illegal_callback_fn(const char* str, void* data) {
+ /* Dummy callback function that just counts. */
+ int32_t *p;
+ (void)str;
+ p = data;
+ (*p)++;
+}
+
+static void uncounting_illegal_callback_fn(const char* str, void* data) {
+ /* Dummy callback function that just counts (backwards). */
+ int32_t *p;
+ (void)str;
+ p = data;
+ (*p)--;
+}
+
void random_field_element_test(secp256k1_fe *fe) {
do {
unsigned char b32[32];
@@ -39,7 +69,7 @@ void random_field_element_test(secp256k1_fe *fe) {
void random_field_element_magnitude(secp256k1_fe *fe) {
secp256k1_fe zero;
- int n = secp256k1_rand32() % 9;
+ int n = secp256k1_rand_int(9);
secp256k1_fe_normalize(fe);
if (n == 0) {
return;
@@ -55,7 +85,7 @@ void random_group_element_test(secp256k1_ge *ge) {
secp256k1_fe fe;
do {
random_field_element_test(&fe);
- if (secp256k1_ge_set_xo_var(ge, &fe, secp256k1_rand32() & 1)) {
+ if (secp256k1_ge_set_xo_var(ge, &fe, secp256k1_rand_bits(1))) {
secp256k1_fe_normalize(&ge->y);
break;
}
@@ -104,7 +134,12 @@ void random_scalar_order(secp256k1_scalar *num) {
}
void run_context_tests(void) {
- secp256k1_context *none = secp256k1_context_create(0);
+ secp256k1_pubkey pubkey;
+ secp256k1_ecdsa_signature sig;
+ unsigned char ctmp[32];
+ int32_t ecount;
+ int32_t ecount2;
+ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
@@ -114,6 +149,13 @@ void run_context_tests(void) {
secp256k1_scalar msg, key, nonce;
secp256k1_scalar sigr, sigs;
+ ecount = 0;
+ ecount2 = 10;
+ secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount2);
+ secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, NULL);
+ CHECK(vrfy->error_callback.fn != sign->error_callback.fn);
+
/*** clone and destroy all of them to make sure cloning was complete ***/
{
secp256k1_context *ctx_tmp;
@@ -124,12 +166,54 @@ void run_context_tests(void) {
ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_destroy(ctx_tmp);
}
+ /* Verify that the error callback makes it across the clone. */
+ CHECK(vrfy->error_callback.fn != sign->error_callback.fn);
+ /* And that it resets back to default. */
+ secp256k1_context_set_error_callback(sign, NULL, NULL);
+ CHECK(vrfy->error_callback.fn == sign->error_callback.fn);
+
/*** attempt to use them ***/
random_scalar_order_test(&msg);
random_scalar_order_test(&key);
secp256k1_ecmult_gen(&both->ecmult_gen_ctx, &pubj, &key);
secp256k1_ge_set_gej(&pub, &pubj);
+ /* Verify context-type checking illegal-argument errors. */
+ memset(ctmp, 1, 32);
+ CHECK(secp256k1_ec_pubkey_create(vrfy, &pubkey, ctmp) == 0);
+ CHECK(ecount == 1);
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_create(sign, &pubkey, ctmp) == 1);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ecdsa_sign(vrfy, &sig, ctmp, ctmp, NULL, NULL) == 0);
+ CHECK(ecount == 2);
+ VG_UNDEF(&sig, sizeof(sig));
+ CHECK(secp256k1_ecdsa_sign(sign, &sig, ctmp, ctmp, NULL, NULL) == 1);
+ VG_CHECK(&sig, sizeof(sig));
+ CHECK(ecount2 == 10);
+ CHECK(secp256k1_ecdsa_verify(sign, &sig, ctmp, &pubkey) == 0);
+ CHECK(ecount2 == 11);
+ CHECK(secp256k1_ecdsa_verify(vrfy, &sig, ctmp, &pubkey) == 1);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ec_pubkey_tweak_add(sign, &pubkey, ctmp) == 0);
+ CHECK(ecount2 == 12);
+ CHECK(secp256k1_ec_pubkey_tweak_add(vrfy, &pubkey, ctmp) == 1);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ec_pubkey_tweak_mul(sign, &pubkey, ctmp) == 0);
+ CHECK(ecount2 == 13);
+ CHECK(secp256k1_ec_pubkey_tweak_mul(vrfy, &pubkey, ctmp) == 1);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_context_randomize(vrfy, ctmp) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_context_randomize(sign, NULL) == 1);
+ CHECK(ecount2 == 13);
+ secp256k1_context_set_illegal_callback(vrfy, NULL, NULL);
+ secp256k1_context_set_illegal_callback(sign, NULL, NULL);
+
+ /* This shouldn't leak memory, due to already-set tests. */
+ secp256k1_ecmult_gen_context_build(&sign->ecmult_gen_ctx, NULL);
+ secp256k1_ecmult_context_build(&vrfy->ecmult_ctx, NULL);
+
/* obtain a working nonce */
do {
random_scalar_order_test(&nonce);
@@ -148,6 +232,8 @@ void run_context_tests(void) {
secp256k1_context_destroy(sign);
secp256k1_context_destroy(vrfy);
secp256k1_context_destroy(both);
+ /* Defined as no-op. */
+ secp256k1_context_destroy(NULL);
}
/***** HASH TESTS *****/
@@ -178,7 +264,7 @@ void run_sha256_tests(void) {
secp256k1_sha256_finalize(&hasher, out);
CHECK(memcmp(out, outputs[i], 32) == 0);
if (strlen(inputs[i]) > 0) {
- int split = secp256k1_rand32() % strlen(inputs[i]);
+ int split = secp256k1_rand_int(strlen(inputs[i]));
secp256k1_sha256_initialize(&hasher);
secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split);
secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split);
@@ -222,7 +308,7 @@ void run_hmac_sha256_tests(void) {
secp256k1_hmac_sha256_finalize(&hasher, out);
CHECK(memcmp(out, outputs[i], 32) == 0);
if (strlen(inputs[i]) > 0) {
- int split = secp256k1_rand32() % strlen(inputs[i]);
+ int split = secp256k1_rand_int(strlen(inputs[i]));
secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i]));
secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split);
secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split);
@@ -273,11 +359,83 @@ void run_rfc6979_hmac_sha256_tests(void) {
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
}
+/***** RANDOM TESTS *****/
+
+void test_rand_bits(int rand32, int bits) {
+ /* (1-1/2^B)^rounds[B] < 1/10^9, so rounds is the number of iterations to
+ * get a false negative chance below once in a billion */
+ static const unsigned int rounds[7] = {1, 30, 73, 156, 322, 653, 1316};
+ /* We try multiplying the results with various odd numbers, which shouldn't
+ * influence the uniform distribution modulo a power of 2. */
+ static const uint32_t mults[6] = {1, 3, 21, 289, 0x9999, 0x80402011};
+ /* We only select up to 6 bits from the output to analyse */
+ unsigned int usebits = bits > 6 ? 6 : bits;
+ unsigned int maxshift = bits - usebits;
+ /* For each of the maxshift+1 usebits-bit sequences inside a bits-bit
+ number, track all observed outcomes, one per bit in a uint64_t. */
+ uint64_t x[6][27] = {{0}};
+ unsigned int i, shift, m;
+ /* Multiply the output of all rand calls with the odd number m, which
+ should not change the uniformity of its distribution. */
+ for (i = 0; i < rounds[usebits]; i++) {
+ uint32_t r = (rand32 ? secp256k1_rand32() : secp256k1_rand_bits(bits));
+ CHECK((((uint64_t)r) >> bits) == 0);
+ for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) {
+ uint32_t rm = r * mults[m];
+ for (shift = 0; shift <= maxshift; shift++) {
+ x[m][shift] |= (((uint64_t)1) << ((rm >> shift) & ((1 << usebits) - 1)));
+ }
+ }
+ }
+ for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) {
+ for (shift = 0; shift <= maxshift; shift++) {
+ /* Test that the lower usebits bits of x[shift] are 1 */
+ CHECK(((~x[m][shift]) << (64 - (1 << usebits))) == 0);
+ }
+ }
+}
+
+/* Subrange must be a whole divisor of range, and at most 64 */
+void test_rand_int(uint32_t range, uint32_t subrange) {
+ /* (1-1/subrange)^rounds < 1/10^9 */
+ int rounds = (subrange * 2073) / 100;
+ int i;
+ uint64_t x = 0;
+ CHECK((range % subrange) == 0);
+ for (i = 0; i < rounds; i++) {
+ uint32_t r = secp256k1_rand_int(range);
+ CHECK(r < range);
+ r = r % subrange;
+ x |= (((uint64_t)1) << r);
+ }
+ /* Test that the lower subrange bits of x are 1. */
+ CHECK(((~x) << (64 - subrange)) == 0);
+}
+
+void run_rand_bits(void) {
+ size_t b;
+ test_rand_bits(1, 32);
+ for (b = 1; b <= 32; b++) {
+ test_rand_bits(0, b);
+ }
+}
+
+void run_rand_int(void) {
+ static const uint32_t ms[] = {1, 3, 17, 1000, 13771, 999999, 33554432};
+ static const uint32_t ss[] = {1, 3, 6, 9, 13, 31, 64};
+ unsigned int m, s;
+ for (m = 0; m < sizeof(ms) / sizeof(ms[0]); m++) {
+ for (s = 0; s < sizeof(ss) / sizeof(ss[0]); s++) {
+ test_rand_int(ms[m] * ss[s], ss[s]);
+ }
+ }
+}
+
/***** NUM TESTS *****/
#ifndef USE_NUM_NONE
void random_num_negate(secp256k1_num *num) {
- if (secp256k1_rand32() & 1) {
+ if (secp256k1_rand_bits(1)) {
secp256k1_num_negate(num);
}
}
@@ -315,16 +473,17 @@ void test_num_negate(void) {
}
void test_num_add_sub(void) {
+ int i;
+ secp256k1_scalar s;
secp256k1_num n1;
secp256k1_num n2;
secp256k1_num n1p2, n2p1, n1m2, n2m1;
- int r = secp256k1_rand32();
random_num_order_test(&n1); /* n1 = R1 */
- if (r & 1) {
+ if (secp256k1_rand_bits(1)) {
random_num_negate(&n1);
}
random_num_order_test(&n2); /* n2 = R2 */
- if (r & 2) {
+ if (secp256k1_rand_bits(1)) {
random_num_negate(&n2);
}
secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = R1 + R2 */
@@ -341,6 +500,110 @@ void test_num_add_sub(void) {
CHECK(!secp256k1_num_eq(&n2p1, &n1));
secp256k1_num_sub(&n2p1, &n2p1, &n2); /* n2p1 = R2 + R1 - R2 = R1 */
CHECK(secp256k1_num_eq(&n2p1, &n1));
+
+ /* check is_one */
+ secp256k1_scalar_set_int(&s, 1);
+ secp256k1_scalar_get_num(&n1, &s);
+ CHECK(secp256k1_num_is_one(&n1));
+ /* check that 2^n + 1 is never 1 */
+ secp256k1_scalar_get_num(&n2, &s);
+ for (i = 0; i < 250; ++i) {
+ secp256k1_num_add(&n1, &n1, &n1); /* n1 *= 2 */
+ secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = n1 + 1 */
+ CHECK(!secp256k1_num_is_one(&n1p2));
+ }
+}
+
+void test_num_mod(void) {
+ int i;
+ secp256k1_scalar s;
+ secp256k1_num order, n;
+
+ /* check that 0 mod anything is 0 */
+ random_scalar_order_test(&s);
+ secp256k1_scalar_get_num(&order, &s);
+ secp256k1_scalar_set_int(&s, 0);
+ secp256k1_scalar_get_num(&n, &s);
+ secp256k1_num_mod(&n, &order);
+ CHECK(secp256k1_num_is_zero(&n));
+
+ /* check that anything mod 1 is 0 */
+ secp256k1_scalar_set_int(&s, 1);
+ secp256k1_scalar_get_num(&order, &s);
+ secp256k1_scalar_get_num(&n, &s);
+ secp256k1_num_mod(&n, &order);
+ CHECK(secp256k1_num_is_zero(&n));
+
+ /* check that increasing the number past 2^256 does not break this */
+ random_scalar_order_test(&s);
+ secp256k1_scalar_get_num(&n, &s);
+ /* multiply by 2^8, which'll test this case with high probability */
+ for (i = 0; i < 8; ++i) {
+ secp256k1_num_add(&n, &n, &n);
+ }
+ secp256k1_num_mod(&n, &order);
+ CHECK(secp256k1_num_is_zero(&n));
+}
+
+void test_num_jacobi(void) {
+ secp256k1_scalar sqr;
+ secp256k1_scalar small;
+ secp256k1_scalar five; /* five is not a quadratic residue */
+ secp256k1_num order, n;
+ int i;
+ /* squares mod 5 are 1, 4 */
+ const int jacobi5[10] = { 0, 1, -1, -1, 1, 0, 1, -1, -1, 1 };
+
+ /* check some small values with 5 as the order */
+ secp256k1_scalar_set_int(&five, 5);
+ secp256k1_scalar_get_num(&order, &five);
+ for (i = 0; i < 10; ++i) {
+ secp256k1_scalar_set_int(&small, i);
+ secp256k1_scalar_get_num(&n, &small);
+ CHECK(secp256k1_num_jacobi(&n, &order) == jacobi5[i]);
+ }
+
+ /** test large values with 5 as group order */
+ secp256k1_scalar_get_num(&order, &five);
+ /* we first need a scalar which is not a multiple of 5 */
+ do {
+ secp256k1_num fiven;
+ random_scalar_order_test(&sqr);
+ secp256k1_scalar_get_num(&fiven, &five);
+ secp256k1_scalar_get_num(&n, &sqr);
+ secp256k1_num_mod(&n, &fiven);
+ } while (secp256k1_num_is_zero(&n));
+ /* next force it to be a residue. 2 is a nonresidue mod 5 so we can
+ * just multiply by two, i.e. add the number to itself */
+ if (secp256k1_num_jacobi(&n, &order) == -1) {
+ secp256k1_num_add(&n, &n, &n);
+ }
+
+ /* test residue */
+ CHECK(secp256k1_num_jacobi(&n, &order) == 1);
+ /* test nonresidue */
+ secp256k1_num_add(&n, &n, &n);
+ CHECK(secp256k1_num_jacobi(&n, &order) == -1);
+
+ /** test with secp group order as order */
+ secp256k1_scalar_order_get_num(&order);
+ random_scalar_order_test(&sqr);
+ secp256k1_scalar_sqr(&sqr, &sqr);
+ /* test residue */
+ secp256k1_scalar_get_num(&n, &sqr);
+ CHECK(secp256k1_num_jacobi(&n, &order) == 1);
+ /* test nonresidue */
+ secp256k1_scalar_mul(&sqr, &sqr, &five);
+ secp256k1_scalar_get_num(&n, &sqr);
+ CHECK(secp256k1_num_jacobi(&n, &order) == -1);
+ /* test multiple of the order*/
+ CHECK(secp256k1_num_jacobi(&order, &order) == 0);
+
+ /* check one less than the order */
+ secp256k1_scalar_set_int(&small, 1);
+ secp256k1_scalar_get_num(&n, &small);
+ secp256k1_num_sub(&n, &order, &n);
+ CHECK(secp256k1_num_jacobi(&n, &order) == 1); /* sage confirms this is 1 */
}
void run_num_smalltests(void) {
@@ -348,6 +611,8 @@ void run_num_smalltests(void) {
for (i = 0; i < 100*count; i++) {
test_num_negate();
test_num_add_sub();
+ test_num_mod();
+ test_num_jacobi();
}
}
#endif
@@ -409,7 +674,7 @@ void scalar_test(void) {
while (i < 256) {
secp256k1_scalar t;
int j;
- int now = (secp256k1_rand32() % 15) + 1;
+ int now = secp256k1_rand_int(15) + 1;
if (now + i > 256) {
now = 256 - i;
}
@@ -437,7 +702,7 @@ void scalar_test(void) {
}
{
- /* Test that multipying the scalars is equal to multiplying their numbers modulo the order. */
+ /* Test that multiplying the scalars is equal to multiplying their numbers modulo the order. */
secp256k1_scalar r;
secp256k1_num r2num;
secp256k1_num rnum;
@@ -486,7 +751,7 @@ void scalar_test(void) {
secp256k1_num rnum;
secp256k1_num rnum2;
unsigned char cone[1] = {0x01};
- unsigned int shift = 256 + (secp256k1_rand32() % 257);
+ unsigned int shift = 256 + secp256k1_rand_int(257);
secp256k1_scalar_mul_shift_var(&r, &s1, &s2, shift);
secp256k1_num_mul(&rnum, &s1num, &s2num);
secp256k1_num_shift(&rnum, shift - 1);
@@ -504,7 +769,7 @@ void scalar_test(void) {
random_scalar_order_test(&r);
for (i = 0; i < 100; ++i) {
int low;
- int shift = 1 + (secp256k1_rand32() % 15);
+ int shift = 1 + secp256k1_rand_int(15);
int expected = r.d[0] % (1 << shift);
low = secp256k1_scalar_shr_int(&r, shift);
CHECK(expected == low);
@@ -532,6 +797,10 @@ void scalar_test(void) {
secp256k1_scalar_inverse(&inv, &inv);
/* Inverting one must result in one. */
CHECK(secp256k1_scalar_is_one(&inv));
+#ifndef USE_NUM_NONE
+ secp256k1_scalar_get_num(&invnum, &inv);
+ CHECK(secp256k1_num_is_one(&invnum));
+#endif
}
}
@@ -548,7 +817,7 @@ void scalar_test(void) {
secp256k1_scalar b;
int i;
/* Test add_bit. */
- int bit = secp256k1_rand32() % 256;
+ int bit = secp256k1_rand_bits(8);
secp256k1_scalar_set_int(&b, 1);
CHECK(secp256k1_scalar_is_one(&b));
for (i = 0; i < bit; i++) {
@@ -671,6 +940,600 @@ void run_scalar_tests(void) {
CHECK(secp256k1_scalar_is_zero(&zero));
}
#endif
+
+ {
+ /* Does check_overflow check catch all ones? */
+ static const secp256k1_scalar overflowed = SECP256K1_SCALAR_CONST(
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL
+ );
+ CHECK(secp256k1_scalar_check_overflow(&overflowed));
+ }
+
+ {
+ /* Static test vectors.
+ * These were reduced from ~10^12 random vectors based on comparison-decision
+ * and edge-case coverage on 32-bit and 64-bit implementations.
+ * The responses were generated with Sage 5.9.
+ */
+ secp256k1_scalar x;
+ secp256k1_scalar y;
+ secp256k1_scalar z;
+ secp256k1_scalar zz;
+ secp256k1_scalar one;
+ secp256k1_scalar r1;
+ secp256k1_scalar r2;
+#if defined(USE_SCALAR_INV_NUM)
+ secp256k1_scalar zzv;
+#endif
+ int overflow;
+ unsigned char chal[33][2][32] = {
+ {{0xff, 0xff, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff,
+ 0xff, 0xff, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff},
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff}},
+ {{0xef, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0,
+ 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x80, 0xff}},
+ {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0xff, 0x3f, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x00},
+ {0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x80,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xe0,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff}},
+ {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0x1e, 0xf8, 0xff, 0xff, 0xff, 0xfd, 0xff},
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f,
+ 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xe0,
+ 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff,
+ 0xf3, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0x1c, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff},
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00,
+ 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1f, 0x00, 0x00, 0x80, 0xff, 0xff, 0x3f,
+ 0x00, 0xfe, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff}},
+ {{0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xfc, 0x9f,
+ 0xff, 0xff, 0xff, 0x00, 0x80, 0x00, 0x00, 0x80,
+ 0xff, 0x0f, 0xfc, 0xff, 0x7f, 0x00, 0x00, 0x00,
+ 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00},
+ {0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0xf8, 0xff, 0x0f, 0xc0, 0xff, 0xff,
+ 0xff, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff}},
+ {{0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff,
+ 0xf7, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0x00,
+ 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf0},
+ {0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+ {{0x00, 0xf8, 0xff, 0x03, 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x03, 0xc0, 0xff, 0x0f, 0xfc, 0xff},
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff,
+ 0xff, 0x01, 0x00, 0x00, 0x00, 0x3f, 0x00, 0xc0,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+ {{0x8f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00},
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x03, 0x00, 0x80, 0x00, 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0xff, 0x7f},
+ {0xff, 0xcf, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff,
+ 0xbf, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00}},
+ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x00, 0x80, 0x00, 0x00, 0x80,
+ 0xff, 0x01, 0xfc, 0xff, 0x01, 0x00, 0xfe, 0xff},
+ {0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00}},
+ {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x7f, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xf8, 0xff, 0x01, 0x00, 0xf0, 0xff, 0xff,
+ 0xe0, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x00},
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0xfc, 0xff, 0xff, 0x3f, 0xf0, 0xff, 0xff, 0x3f,
+ 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x0f, 0x7e, 0x00, 0x00}},
+ {{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x1f, 0x00, 0x00, 0xfe, 0x07, 0x00},
+ {0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xfb, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60}},
+ {{0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00,
+ 0x80, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0xff, 0xff, 0x1f, 0x00, 0xf0, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00}},
+ {{0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03,
+ 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff}},
+ {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0xff, 0xff, 0xcf, 0xff, 0x1f, 0x00, 0x00,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x7e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00},
+ {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0xff, 0xff, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff}},
+ {{0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00},
+ {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x80,
+ 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
+ 0xff, 0x7f, 0xf8, 0xff, 0xff, 0x1f, 0x00, 0xfe}},
+ {{0xff, 0xff, 0xff, 0x3f, 0xf8, 0xff, 0xff, 0xff,
+ 0xff, 0x03, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07},
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0xff, 0x01, 0x80, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}},
+ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+ 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
+ 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40}},
+ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+ {{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc0,
+ 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f},
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00,
+ 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff}},
+ {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}},
+ {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+ 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
+ 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}},
+ {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x7e, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x07, 0x00,
+ 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0xff, 0x01, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+ {{0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01,
+ 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff,
+ 0xff, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xf1, 0x7f, 0x00}},
+ {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x00},
+ {0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff,
+ 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f,
+ 0x00, 0x00, 0xfc, 0xff, 0xff, 0x01, 0xff, 0xff}},
+ {{0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0xff, 0x03, 0xe0, 0x01,
+ 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00},
+ {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0xff, 0xff, 0xf0, 0x07, 0x00, 0x3c, 0x80,
+ 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x07, 0xe0, 0xff, 0x00, 0x00, 0x00}},
+ {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80},
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0xc0, 0x7f, 0xfe, 0xff, 0x1f,
+ 0x00, 0xfe, 0xff, 0x03, 0x00, 0x00, 0xfe, 0xff}},
+ {{0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83,
+ 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80,
+ 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xf0},
+ {0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00,
+ 0xf8, 0x07, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xc7, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff}},
+ {{0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00,
+ 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb,
+ 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03},
+ {0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00,
+ 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb,
+ 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}}
+ };
+ unsigned char res[33][2][32] = {
+ {{0x0c, 0x3b, 0x0a, 0xca, 0x8d, 0x1a, 0x2f, 0xb9,
+ 0x8a, 0x7b, 0x53, 0x5a, 0x1f, 0xc5, 0x22, 0xa1,
+ 0x07, 0x2a, 0x48, 0xea, 0x02, 0xeb, 0xb3, 0xd6,
+ 0x20, 0x1e, 0x86, 0xd0, 0x95, 0xf6, 0x92, 0x35},
+ {0xdc, 0x90, 0x7a, 0x07, 0x2e, 0x1e, 0x44, 0x6d,
+ 0xf8, 0x15, 0x24, 0x5b, 0x5a, 0x96, 0x37, 0x9c,
+ 0x37, 0x7b, 0x0d, 0xac, 0x1b, 0x65, 0x58, 0x49,
+ 0x43, 0xb7, 0x31, 0xbb, 0xa7, 0xf4, 0x97, 0x15}},
+ {{0xf1, 0xf7, 0x3a, 0x50, 0xe6, 0x10, 0xba, 0x22,
+ 0x43, 0x4d, 0x1f, 0x1f, 0x7c, 0x27, 0xca, 0x9c,
+ 0xb8, 0xb6, 0xa0, 0xfc, 0xd8, 0xc0, 0x05, 0x2f,
+ 0xf7, 0x08, 0xe1, 0x76, 0xdd, 0xd0, 0x80, 0xc8},
+ {0xe3, 0x80, 0x80, 0xb8, 0xdb, 0xe3, 0xa9, 0x77,
+ 0x00, 0xb0, 0xf5, 0x2e, 0x27, 0xe2, 0x68, 0xc4,
+ 0x88, 0xe8, 0x04, 0xc1, 0x12, 0xbf, 0x78, 0x59,
+ 0xe6, 0xa9, 0x7c, 0xe1, 0x81, 0xdd, 0xb9, 0xd5}},
+ {{0x96, 0xe2, 0xee, 0x01, 0xa6, 0x80, 0x31, 0xef,
+ 0x5c, 0xd0, 0x19, 0xb4, 0x7d, 0x5f, 0x79, 0xab,
+ 0xa1, 0x97, 0xd3, 0x7e, 0x33, 0xbb, 0x86, 0x55,
+ 0x60, 0x20, 0x10, 0x0d, 0x94, 0x2d, 0x11, 0x7c},
+ {0xcc, 0xab, 0xe0, 0xe8, 0x98, 0x65, 0x12, 0x96,
+ 0x38, 0x5a, 0x1a, 0xf2, 0x85, 0x23, 0x59, 0x5f,
+ 0xf9, 0xf3, 0xc2, 0x81, 0x70, 0x92, 0x65, 0x12,
+ 0x9c, 0x65, 0x1e, 0x96, 0x00, 0xef, 0xe7, 0x63}},
+ {{0xac, 0x1e, 0x62, 0xc2, 0x59, 0xfc, 0x4e, 0x5c,
+ 0x83, 0xb0, 0xd0, 0x6f, 0xce, 0x19, 0xf6, 0xbf,
+ 0xa4, 0xb0, 0xe0, 0x53, 0x66, 0x1f, 0xbf, 0xc9,
+ 0x33, 0x47, 0x37, 0xa9, 0x3d, 0x5d, 0xb0, 0x48},
+ {0x86, 0xb9, 0x2a, 0x7f, 0x8e, 0xa8, 0x60, 0x42,
+ 0x26, 0x6d, 0x6e, 0x1c, 0xa2, 0xec, 0xe0, 0xe5,
+ 0x3e, 0x0a, 0x33, 0xbb, 0x61, 0x4c, 0x9f, 0x3c,
+ 0xd1, 0xdf, 0x49, 0x33, 0xcd, 0x72, 0x78, 0x18}},
+ {{0xf7, 0xd3, 0xcd, 0x49, 0x5c, 0x13, 0x22, 0xfb,
+ 0x2e, 0xb2, 0x2f, 0x27, 0xf5, 0x8a, 0x5d, 0x74,
+ 0xc1, 0x58, 0xc5, 0xc2, 0x2d, 0x9f, 0x52, 0xc6,
+ 0x63, 0x9f, 0xba, 0x05, 0x76, 0x45, 0x7a, 0x63},
+ {0x8a, 0xfa, 0x55, 0x4d, 0xdd, 0xa3, 0xb2, 0xc3,
+ 0x44, 0xfd, 0xec, 0x72, 0xde, 0xef, 0xc0, 0x99,
+ 0xf5, 0x9f, 0xe2, 0x52, 0xb4, 0x05, 0x32, 0x58,
+ 0x57, 0xc1, 0x8f, 0xea, 0xc3, 0x24, 0x5b, 0x94}},
+ {{0x05, 0x83, 0xee, 0xdd, 0x64, 0xf0, 0x14, 0x3b,
+ 0xa0, 0x14, 0x4a, 0x3a, 0x41, 0x82, 0x7c, 0xa7,
+ 0x2c, 0xaa, 0xb1, 0x76, 0xbb, 0x59, 0x64, 0x5f,
+ 0x52, 0xad, 0x25, 0x29, 0x9d, 0x8f, 0x0b, 0xb0},
+ {0x7e, 0xe3, 0x7c, 0xca, 0xcd, 0x4f, 0xb0, 0x6d,
+ 0x7a, 0xb2, 0x3e, 0xa0, 0x08, 0xb9, 0xa8, 0x2d,
+ 0xc2, 0xf4, 0x99, 0x66, 0xcc, 0xac, 0xd8, 0xb9,
+ 0x72, 0x2a, 0x4a, 0x3e, 0x0f, 0x7b, 0xbf, 0xf4}},
+ {{0x8c, 0x9c, 0x78, 0x2b, 0x39, 0x61, 0x7e, 0xf7,
+ 0x65, 0x37, 0x66, 0x09, 0x38, 0xb9, 0x6f, 0x70,
+ 0x78, 0x87, 0xff, 0xcf, 0x93, 0xca, 0x85, 0x06,
+ 0x44, 0x84, 0xa7, 0xfe, 0xd3, 0xa4, 0xe3, 0x7e},
+ {0xa2, 0x56, 0x49, 0x23, 0x54, 0xa5, 0x50, 0xe9,
+ 0x5f, 0xf0, 0x4d, 0xe7, 0xdc, 0x38, 0x32, 0x79,
+ 0x4f, 0x1c, 0xb7, 0xe4, 0xbb, 0xf8, 0xbb, 0x2e,
+ 0x40, 0x41, 0x4b, 0xcc, 0xe3, 0x1e, 0x16, 0x36}},
+ {{0x0c, 0x1e, 0xd7, 0x09, 0x25, 0x40, 0x97, 0xcb,
+ 0x5c, 0x46, 0xa8, 0xda, 0xef, 0x25, 0xd5, 0xe5,
+ 0x92, 0x4d, 0xcf, 0xa3, 0xc4, 0x5d, 0x35, 0x4a,
+ 0xe4, 0x61, 0x92, 0xf3, 0xbf, 0x0e, 0xcd, 0xbe},
+ {0xe4, 0xaf, 0x0a, 0xb3, 0x30, 0x8b, 0x9b, 0x48,
+ 0x49, 0x43, 0xc7, 0x64, 0x60, 0x4a, 0x2b, 0x9e,
+ 0x95, 0x5f, 0x56, 0xe8, 0x35, 0xdc, 0xeb, 0xdc,
+ 0xc7, 0xc4, 0xfe, 0x30, 0x40, 0xc7, 0xbf, 0xa4}},
+ {{0xd4, 0xa0, 0xf5, 0x81, 0x49, 0x6b, 0xb6, 0x8b,
+ 0x0a, 0x69, 0xf9, 0xfe, 0xa8, 0x32, 0xe5, 0xe0,
+ 0xa5, 0xcd, 0x02, 0x53, 0xf9, 0x2c, 0xe3, 0x53,
+ 0x83, 0x36, 0xc6, 0x02, 0xb5, 0xeb, 0x64, 0xb8},
+ {0x1d, 0x42, 0xb9, 0xf9, 0xe9, 0xe3, 0x93, 0x2c,
+ 0x4c, 0xee, 0x6c, 0x5a, 0x47, 0x9e, 0x62, 0x01,
+ 0x6b, 0x04, 0xfe, 0xa4, 0x30, 0x2b, 0x0d, 0x4f,
+ 0x71, 0x10, 0xd3, 0x55, 0xca, 0xf3, 0x5e, 0x80}},
+ {{0x77, 0x05, 0xf6, 0x0c, 0x15, 0x9b, 0x45, 0xe7,
+ 0xb9, 0x11, 0xb8, 0xf5, 0xd6, 0xda, 0x73, 0x0c,
+ 0xda, 0x92, 0xea, 0xd0, 0x9d, 0xd0, 0x18, 0x92,
+ 0xce, 0x9a, 0xaa, 0xee, 0x0f, 0xef, 0xde, 0x30},
+ {0xf1, 0xf1, 0xd6, 0x9b, 0x51, 0xd7, 0x77, 0x62,
+ 0x52, 0x10, 0xb8, 0x7a, 0x84, 0x9d, 0x15, 0x4e,
+ 0x07, 0xdc, 0x1e, 0x75, 0x0d, 0x0c, 0x3b, 0xdb,
+ 0x74, 0x58, 0x62, 0x02, 0x90, 0x54, 0x8b, 0x43}},
+ {{0xa6, 0xfe, 0x0b, 0x87, 0x80, 0x43, 0x67, 0x25,
+ 0x57, 0x5d, 0xec, 0x40, 0x50, 0x08, 0xd5, 0x5d,
+ 0x43, 0xd7, 0xe0, 0xaa, 0xe0, 0x13, 0xb6, 0xb0,
+ 0xc0, 0xd4, 0xe5, 0x0d, 0x45, 0x83, 0xd6, 0x13},
+ {0x40, 0x45, 0x0a, 0x92, 0x31, 0xea, 0x8c, 0x60,
+ 0x8c, 0x1f, 0xd8, 0x76, 0x45, 0xb9, 0x29, 0x00,
+ 0x26, 0x32, 0xd8, 0xa6, 0x96, 0x88, 0xe2, 0xc4,
+ 0x8b, 0xdb, 0x7f, 0x17, 0x87, 0xcc, 0xc8, 0xf2}},
+ {{0xc2, 0x56, 0xe2, 0xb6, 0x1a, 0x81, 0xe7, 0x31,
+ 0x63, 0x2e, 0xbb, 0x0d, 0x2f, 0x81, 0x67, 0xd4,
+ 0x22, 0xe2, 0x38, 0x02, 0x25, 0x97, 0xc7, 0x88,
+ 0x6e, 0xdf, 0xbe, 0x2a, 0xa5, 0x73, 0x63, 0xaa},
+ {0x50, 0x45, 0xe2, 0xc3, 0xbd, 0x89, 0xfc, 0x57,
+ 0xbd, 0x3c, 0xa3, 0x98, 0x7e, 0x7f, 0x36, 0x38,
+ 0x92, 0x39, 0x1f, 0x0f, 0x81, 0x1a, 0x06, 0x51,
+ 0x1f, 0x8d, 0x6a, 0xff, 0x47, 0x16, 0x06, 0x9c}},
+ {{0x33, 0x95, 0xa2, 0x6f, 0x27, 0x5f, 0x9c, 0x9c,
+ 0x64, 0x45, 0xcb, 0xd1, 0x3c, 0xee, 0x5e, 0x5f,
+ 0x48, 0xa6, 0xaf, 0xe3, 0x79, 0xcf, 0xb1, 0xe2,
+ 0xbf, 0x55, 0x0e, 0xa2, 0x3b, 0x62, 0xf0, 0xe4},
+ {0x14, 0xe8, 0x06, 0xe3, 0xbe, 0x7e, 0x67, 0x01,
+ 0xc5, 0x21, 0x67, 0xd8, 0x54, 0xb5, 0x7f, 0xa4,
+ 0xf9, 0x75, 0x70, 0x1c, 0xfd, 0x79, 0xdb, 0x86,
+ 0xad, 0x37, 0x85, 0x83, 0x56, 0x4e, 0xf0, 0xbf}},
+ {{0xbc, 0xa6, 0xe0, 0x56, 0x4e, 0xef, 0xfa, 0xf5,
+ 0x1d, 0x5d, 0x3f, 0x2a, 0x5b, 0x19, 0xab, 0x51,
+ 0xc5, 0x8b, 0xdd, 0x98, 0x28, 0x35, 0x2f, 0xc3,
+ 0x81, 0x4f, 0x5c, 0xe5, 0x70, 0xb9, 0xeb, 0x62},
+ {0xc4, 0x6d, 0x26, 0xb0, 0x17, 0x6b, 0xfe, 0x6c,
+ 0x12, 0xf8, 0xe7, 0xc1, 0xf5, 0x2f, 0xfa, 0x91,
+ 0x13, 0x27, 0xbd, 0x73, 0xcc, 0x33, 0x31, 0x1c,
+ 0x39, 0xe3, 0x27, 0x6a, 0x95, 0xcf, 0xc5, 0xfb}},
+ {{0x30, 0xb2, 0x99, 0x84, 0xf0, 0x18, 0x2a, 0x6e,
+ 0x1e, 0x27, 0xed, 0xa2, 0x29, 0x99, 0x41, 0x56,
+ 0xe8, 0xd4, 0x0d, 0xef, 0x99, 0x9c, 0xf3, 0x58,
+ 0x29, 0x55, 0x1a, 0xc0, 0x68, 0xd6, 0x74, 0xa4},
+ {0x07, 0x9c, 0xe7, 0xec, 0xf5, 0x36, 0x73, 0x41,
+ 0xa3, 0x1c, 0xe5, 0x93, 0x97, 0x6a, 0xfd, 0xf7,
+ 0x53, 0x18, 0xab, 0xaf, 0xeb, 0x85, 0xbd, 0x92,
+ 0x90, 0xab, 0x3c, 0xbf, 0x30, 0x82, 0xad, 0xf6}},
+ {{0xc6, 0x87, 0x8a, 0x2a, 0xea, 0xc0, 0xa9, 0xec,
+ 0x6d, 0xd3, 0xdc, 0x32, 0x23, 0xce, 0x62, 0x19,
+ 0xa4, 0x7e, 0xa8, 0xdd, 0x1c, 0x33, 0xae, 0xd3,
+ 0x4f, 0x62, 0x9f, 0x52, 0xe7, 0x65, 0x46, 0xf4},
+ {0x97, 0x51, 0x27, 0x67, 0x2d, 0xa2, 0x82, 0x87,
+ 0x98, 0xd3, 0xb6, 0x14, 0x7f, 0x51, 0xd3, 0x9a,
+ 0x0b, 0xd0, 0x76, 0x81, 0xb2, 0x4f, 0x58, 0x92,
+ 0xa4, 0x86, 0xa1, 0xa7, 0x09, 0x1d, 0xef, 0x9b}},
+ {{0xb3, 0x0f, 0x2b, 0x69, 0x0d, 0x06, 0x90, 0x64,
+ 0xbd, 0x43, 0x4c, 0x10, 0xe8, 0x98, 0x1c, 0xa3,
+ 0xe1, 0x68, 0xe9, 0x79, 0x6c, 0x29, 0x51, 0x3f,
+ 0x41, 0xdc, 0xdf, 0x1f, 0xf3, 0x60, 0xbe, 0x33},
+ {0xa1, 0x5f, 0xf7, 0x1d, 0xb4, 0x3e, 0x9b, 0x3c,
+ 0xe7, 0xbd, 0xb6, 0x06, 0xd5, 0x60, 0x06, 0x6d,
+ 0x50, 0xd2, 0xf4, 0x1a, 0x31, 0x08, 0xf2, 0xea,
+ 0x8e, 0xef, 0x5f, 0x7d, 0xb6, 0xd0, 0xc0, 0x27}},
+ {{0x62, 0x9a, 0xd9, 0xbb, 0x38, 0x36, 0xce, 0xf7,
+ 0x5d, 0x2f, 0x13, 0xec, 0xc8, 0x2d, 0x02, 0x8a,
+ 0x2e, 0x72, 0xf0, 0xe5, 0x15, 0x9d, 0x72, 0xae,
+ 0xfc, 0xb3, 0x4f, 0x02, 0xea, 0xe1, 0x09, 0xfe},
+ {0x00, 0x00, 0x00, 0x00, 0xfa, 0x0a, 0x3d, 0xbc,
+ 0xad, 0x16, 0x0c, 0xb6, 0xe7, 0x7c, 0x8b, 0x39,
+ 0x9a, 0x43, 0xbb, 0xe3, 0xc2, 0x55, 0x15, 0x14,
+ 0x75, 0xac, 0x90, 0x9b, 0x7f, 0x9a, 0x92, 0x00}},
+ {{0x8b, 0xac, 0x70, 0x86, 0x29, 0x8f, 0x00, 0x23,
+ 0x7b, 0x45, 0x30, 0xaa, 0xb8, 0x4c, 0xc7, 0x8d,
+ 0x4e, 0x47, 0x85, 0xc6, 0x19, 0xe3, 0x96, 0xc2,
+ 0x9a, 0xa0, 0x12, 0xed, 0x6f, 0xd7, 0x76, 0x16},
+ {0x45, 0xaf, 0x7e, 0x33, 0xc7, 0x7f, 0x10, 0x6c,
+ 0x7c, 0x9f, 0x29, 0xc1, 0xa8, 0x7e, 0x15, 0x84,
+ 0xe7, 0x7d, 0xc0, 0x6d, 0xab, 0x71, 0x5d, 0xd0,
+ 0x6b, 0x9f, 0x97, 0xab, 0xcb, 0x51, 0x0c, 0x9f}},
+ {{0x9e, 0xc3, 0x92, 0xb4, 0x04, 0x9f, 0xc8, 0xbb,
+ 0xdd, 0x9e, 0xc6, 0x05, 0xfd, 0x65, 0xec, 0x94,
+ 0x7f, 0x2c, 0x16, 0xc4, 0x40, 0xac, 0x63, 0x7b,
+ 0x7d, 0xb8, 0x0c, 0xe4, 0x5b, 0xe3, 0xa7, 0x0e},
+ {0x43, 0xf4, 0x44, 0xe8, 0xcc, 0xc8, 0xd4, 0x54,
+ 0x33, 0x37, 0x50, 0xf2, 0x87, 0x42, 0x2e, 0x00,
+ 0x49, 0x60, 0x62, 0x02, 0xfd, 0x1a, 0x7c, 0xdb,
+ 0x29, 0x6c, 0x6d, 0x54, 0x53, 0x08, 0xd1, 0xc8}},
+ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}},
+ {{0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1,
+ 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0,
+ 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59,
+ 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92},
+ {0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1,
+ 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0,
+ 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59,
+ 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}},
+ {{0x28, 0x56, 0xac, 0x0e, 0x4f, 0x98, 0x09, 0xf0,
+ 0x49, 0xfa, 0x7f, 0x84, 0xac, 0x7e, 0x50, 0x5b,
+ 0x17, 0x43, 0x14, 0x89, 0x9c, 0x53, 0xa8, 0x94,
+ 0x30, 0xf2, 0x11, 0x4d, 0x92, 0x14, 0x27, 0xe8},
+ {0x39, 0x7a, 0x84, 0x56, 0x79, 0x9d, 0xec, 0x26,
+ 0x2c, 0x53, 0xc1, 0x94, 0xc9, 0x8d, 0x9e, 0x9d,
+ 0x32, 0x1f, 0xdd, 0x84, 0x04, 0xe8, 0xe2, 0x0a,
+ 0x6b, 0xbe, 0xbb, 0x42, 0x40, 0x67, 0x30, 0x6c}},
+ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4,
+ 0x40, 0x2d, 0xa1, 0x73, 0x2f, 0xc9, 0xbe, 0xbd},
+ {0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1,
+ 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0,
+ 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59,
+ 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}},
+ {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+ 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
+ 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}},
+ {{0x1c, 0xc4, 0xf7, 0xda, 0x0f, 0x65, 0xca, 0x39,
+ 0x70, 0x52, 0x92, 0x8e, 0xc3, 0xc8, 0x15, 0xea,
+ 0x7f, 0x10, 0x9e, 0x77, 0x4b, 0x6e, 0x2d, 0xdf,
+ 0xe8, 0x30, 0x9d, 0xda, 0xe8, 0x9a, 0x65, 0xae},
+ {0x02, 0xb0, 0x16, 0xb1, 0x1d, 0xc8, 0x57, 0x7b,
+ 0xa2, 0x3a, 0xa2, 0xa3, 0x38, 0x5c, 0x8f, 0xeb,
+ 0x66, 0x37, 0x91, 0xa8, 0x5f, 0xef, 0x04, 0xf6,
+ 0x59, 0x75, 0xe1, 0xee, 0x92, 0xf6, 0x0e, 0x30}},
+ {{0x8d, 0x76, 0x14, 0xa4, 0x14, 0x06, 0x9f, 0x9a,
+ 0xdf, 0x4a, 0x85, 0xa7, 0x6b, 0xbf, 0x29, 0x6f,
+ 0xbc, 0x34, 0x87, 0x5d, 0xeb, 0xbb, 0x2e, 0xa9,
+ 0xc9, 0x1f, 0x58, 0xd6, 0x9a, 0x82, 0xa0, 0x56},
+ {0xd4, 0xb9, 0xdb, 0x88, 0x1d, 0x04, 0xe9, 0x93,
+ 0x8d, 0x3f, 0x20, 0xd5, 0x86, 0xa8, 0x83, 0x07,
+ 0xdb, 0x09, 0xd8, 0x22, 0x1f, 0x7f, 0xf1, 0x71,
+ 0xc8, 0xe7, 0x5d, 0x47, 0xaf, 0x8b, 0x72, 0xe9}},
+ {{0x83, 0xb9, 0x39, 0xb2, 0xa4, 0xdf, 0x46, 0x87,
+ 0xc2, 0xb8, 0xf1, 0xe6, 0x4c, 0xd1, 0xe2, 0xa9,
+ 0xe4, 0x70, 0x30, 0x34, 0xbc, 0x52, 0x7c, 0x55,
+ 0xa6, 0xec, 0x80, 0xa4, 0xe5, 0xd2, 0xdc, 0x73},
+ {0x08, 0xf1, 0x03, 0xcf, 0x16, 0x73, 0xe8, 0x7d,
+ 0xb6, 0x7e, 0x9b, 0xc0, 0xb4, 0xc2, 0xa5, 0x86,
+ 0x02, 0x77, 0xd5, 0x27, 0x86, 0xa5, 0x15, 0xfb,
+ 0xae, 0x9b, 0x8c, 0xa9, 0xf9, 0xf8, 0xa8, 0x4a}},
+ {{0x8b, 0x00, 0x49, 0xdb, 0xfa, 0xf0, 0x1b, 0xa2,
+ 0xed, 0x8a, 0x9a, 0x7a, 0x36, 0x78, 0x4a, 0xc7,
+ 0xf7, 0xad, 0x39, 0xd0, 0x6c, 0x65, 0x7a, 0x41,
+ 0xce, 0xd6, 0xd6, 0x4c, 0x20, 0x21, 0x6b, 0xc7},
+ {0xc6, 0xca, 0x78, 0x1d, 0x32, 0x6c, 0x6c, 0x06,
+ 0x91, 0xf2, 0x1a, 0xe8, 0x43, 0x16, 0xea, 0x04,
+ 0x3c, 0x1f, 0x07, 0x85, 0xf7, 0x09, 0x22, 0x08,
+ 0xba, 0x13, 0xfd, 0x78, 0x1e, 0x3f, 0x6f, 0x62}},
+ {{0x25, 0x9b, 0x7c, 0xb0, 0xac, 0x72, 0x6f, 0xb2,
+ 0xe3, 0x53, 0x84, 0x7a, 0x1a, 0x9a, 0x98, 0x9b,
+ 0x44, 0xd3, 0x59, 0xd0, 0x8e, 0x57, 0x41, 0x40,
+ 0x78, 0xa7, 0x30, 0x2f, 0x4c, 0x9c, 0xb9, 0x68},
+ {0xb7, 0x75, 0x03, 0x63, 0x61, 0xc2, 0x48, 0x6e,
+ 0x12, 0x3d, 0xbf, 0x4b, 0x27, 0xdf, 0xb1, 0x7a,
+ 0xff, 0x4e, 0x31, 0x07, 0x83, 0xf4, 0x62, 0x5b,
+ 0x19, 0xa5, 0xac, 0xa0, 0x32, 0x58, 0x0d, 0xa7}},
+ {{0x43, 0x4f, 0x10, 0xa4, 0xca, 0xdb, 0x38, 0x67,
+ 0xfa, 0xae, 0x96, 0xb5, 0x6d, 0x97, 0xff, 0x1f,
+ 0xb6, 0x83, 0x43, 0xd3, 0xa0, 0x2d, 0x70, 0x7a,
+ 0x64, 0x05, 0x4c, 0xa7, 0xc1, 0xa5, 0x21, 0x51},
+ {0xe4, 0xf1, 0x23, 0x84, 0xe1, 0xb5, 0x9d, 0xf2,
+ 0xb8, 0x73, 0x8b, 0x45, 0x2b, 0x35, 0x46, 0x38,
+ 0x10, 0x2b, 0x50, 0xf8, 0x8b, 0x35, 0xcd, 0x34,
+ 0xc8, 0x0e, 0xf6, 0xdb, 0x09, 0x35, 0xf0, 0xda}},
+ {{0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34,
+ 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13,
+ 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46,
+ 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5},
+ {0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34,
+ 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13,
+ 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46,
+ 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}}
+ };
+ secp256k1_scalar_set_int(&one, 1);
+ for (i = 0; i < 33; i++) {
+ secp256k1_scalar_set_b32(&x, chal[i][0], &overflow);
+ CHECK(!overflow);
+ secp256k1_scalar_set_b32(&y, chal[i][1], &overflow);
+ CHECK(!overflow);
+ secp256k1_scalar_set_b32(&r1, res[i][0], &overflow);
+ CHECK(!overflow);
+ secp256k1_scalar_set_b32(&r2, res[i][1], &overflow);
+ CHECK(!overflow);
+ secp256k1_scalar_mul(&z, &x, &y);
+ CHECK(!secp256k1_scalar_check_overflow(&z));
+ CHECK(secp256k1_scalar_eq(&r1, &z));
+ if (!secp256k1_scalar_is_zero(&y)) {
+ secp256k1_scalar_inverse(&zz, &y);
+ CHECK(!secp256k1_scalar_check_overflow(&zz));
+#if defined(USE_SCALAR_INV_NUM)
+ secp256k1_scalar_inverse_var(&zzv, &y);
+ CHECK(secp256k1_scalar_eq(&zzv, &zz));
+#endif
+ secp256k1_scalar_mul(&z, &z, &zz);
+ CHECK(!secp256k1_scalar_check_overflow(&z));
+ CHECK(secp256k1_scalar_eq(&x, &z));
+ secp256k1_scalar_mul(&zz, &zz, &y);
+ CHECK(!secp256k1_scalar_check_overflow(&zz));
+ CHECK(secp256k1_scalar_eq(&one, &zz));
+ }
+ secp256k1_scalar_mul(&z, &x, &x);
+ CHECK(!secp256k1_scalar_check_overflow(&z));
+ secp256k1_scalar_sqr(&zz, &x);
+ CHECK(!secp256k1_scalar_check_overflow(&zz));
+ CHECK(secp256k1_scalar_eq(&zz, &z));
+ CHECK(secp256k1_scalar_eq(&r2, &zz));
+ }
+ }
}
/***** FIELD TESTS *****/
@@ -685,6 +1548,16 @@ void random_fe(secp256k1_fe *x) {
} while(1);
}
+void random_fe_test(secp256k1_fe *x) {
+ unsigned char bin[32];
+ do {
+ secp256k1_rand256_test(bin);
+ if (secp256k1_fe_set_b32(x, bin)) {
+ return;
+ }
+ } while(1);
+}
+
void random_fe_non_zero(secp256k1_fe *nz) {
int tries = 10;
while (--tries >= 0) {
@@ -701,7 +1574,7 @@ void random_fe_non_zero(secp256k1_fe *nz) {
void random_fe_non_square(secp256k1_fe *ns) {
secp256k1_fe r;
random_fe_non_zero(ns);
- if (secp256k1_fe_sqrt_var(&r, ns)) {
+ if (secp256k1_fe_sqrt(&r, ns)) {
secp256k1_fe_negate(ns, ns, 1);
}
}
@@ -860,18 +1733,18 @@ void run_field_inv_all_var(void) {
secp256k1_fe x[16], xi[16], xii[16];
int i;
/* Check it's safe to call for 0 elements */
- secp256k1_fe_inv_all_var(0, xi, x);
+ secp256k1_fe_inv_all_var(xi, x, 0);
for (i = 0; i < count; i++) {
size_t j;
- size_t len = (secp256k1_rand32() & 15) + 1;
+ size_t len = secp256k1_rand_int(15) + 1;
for (j = 0; j < len; j++) {
random_fe_non_zero(&x[j]);
}
- secp256k1_fe_inv_all_var(len, xi, x);
+ secp256k1_fe_inv_all_var(xi, x, len);
for (j = 0; j < len; j++) {
CHECK(check_fe_inverse(&x[j], &xi[j]));
}
- secp256k1_fe_inv_all_var(len, xii, xi);
+ secp256k1_fe_inv_all_var(xii, xi, len);
for (j = 0; j < len; j++) {
CHECK(check_fe_equal(&x[j], &xii[j]));
}
@@ -896,7 +1769,7 @@ void run_sqr(void) {
void test_sqrt(const secp256k1_fe *a, const secp256k1_fe *k) {
secp256k1_fe r1, r2;
- int v = secp256k1_fe_sqrt_var(&r1, a);
+ int v = secp256k1_fe_sqrt(&r1, a);
CHECK((v == 0) == (k == NULL));
if (k != NULL) {
@@ -1002,7 +1875,7 @@ void test_ge(void) {
/* Points: (infinity, p1, p1, -p1, -p1, p2, p2, -p2, -p2, p3, p3, -p3, -p3, p4, p4, -p4, -p4).
* The second in each pair of identical points uses a random Z coordinate in the Jacobian form.
* All magnitudes are randomized.
- * All 17*17 combinations of points are added to eachother, using all applicable methods.
+ * All 17*17 combinations of points are added to each other, using all applicable methods.
*
* When the endomorphism code is compiled in, p5 = lambda*p1 and p6 = lambda^2*p1 are added as well.
*/
@@ -1057,7 +1930,7 @@ void test_ge(void) {
zs[i] = gej[i].z;
}
}
- secp256k1_fe_inv_all_var(4 * runs + 1, zinv, zs);
+ secp256k1_fe_inv_all_var(zinv, zs, 4 * runs + 1);
free(zs);
}
@@ -1152,7 +2025,7 @@ void test_ge(void) {
gej_shuffled[i] = gej[i];
}
for (i = 0; i < 4 * runs + 1; i++) {
- int swap = i + secp256k1_rand32() % (4 * runs + 1 - i);
+ int swap = i + secp256k1_rand_int(4 * runs + 1 - i);
if (swap != i) {
secp256k1_gej t = gej_shuffled[i];
gej_shuffled[i] = gej_shuffled[swap];
@@ -1177,8 +2050,8 @@ void test_ge(void) {
secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z);
}
}
- secp256k1_ge_set_table_gej_var(4 * runs + 1, ge_set_table, gej, zr);
- secp256k1_ge_set_all_gej_var(4 * runs + 1, ge_set_all, gej, &ctx->error_callback);
+ secp256k1_ge_set_table_gej_var(ge_set_table, gej, zr, 4 * runs + 1);
+ secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1, &ctx->error_callback);
for (i = 0; i < 4 * runs + 1; i++) {
secp256k1_fe s;
random_fe_non_zero(&s);
@@ -1206,8 +2079,8 @@ void test_add_neg_y_diff_x(void) {
* of the sum to be wrong (since infinity has no xy coordinates).
* HOWEVER, if the x-coordinates are different, infinity is the
* wrong answer, and such degeneracies are exposed. This is the
- * root of https://github.com/bitcoin/secp256k1/issues/257 which
- * this test is a regression test for.
+ * root of https://github.com/bitcoin-core/secp256k1/issues/257
+ * which this test is a regression test for.
*
* These points were generated in sage as
* # secp256k1 params
@@ -1303,6 +2176,79 @@ void run_ec_combine(void) {
}
}
+void test_group_decompress(const secp256k1_fe* x) {
+ /* The input itself, normalized. */
+ secp256k1_fe fex = *x;
+ secp256k1_fe fez;
+ /* Results of set_xquad_var, set_xo_var(..., 0), set_xo_var(..., 1). */
+ secp256k1_ge ge_quad, ge_even, ge_odd;
+ secp256k1_gej gej_quad;
+ /* Return values of the above calls. */
+ int res_quad, res_even, res_odd;
+
+ secp256k1_fe_normalize_var(&fex);
+
+ res_quad = secp256k1_ge_set_xquad(&ge_quad, &fex);
+ res_even = secp256k1_ge_set_xo_var(&ge_even, &fex, 0);
+ res_odd = secp256k1_ge_set_xo_var(&ge_odd, &fex, 1);
+
+ CHECK(res_quad == res_even);
+ CHECK(res_quad == res_odd);
+
+ if (res_quad) {
+ secp256k1_fe_normalize_var(&ge_quad.x);
+ secp256k1_fe_normalize_var(&ge_odd.x);
+ secp256k1_fe_normalize_var(&ge_even.x);
+ secp256k1_fe_normalize_var(&ge_quad.y);
+ secp256k1_fe_normalize_var(&ge_odd.y);
+ secp256k1_fe_normalize_var(&ge_even.y);
+
+ /* No infinity allowed. */
+ CHECK(!ge_quad.infinity);
+ CHECK(!ge_even.infinity);
+ CHECK(!ge_odd.infinity);
+
+ /* Check that the x coordinates check out. */
+ CHECK(secp256k1_fe_equal_var(&ge_quad.x, x));
+ CHECK(secp256k1_fe_equal_var(&ge_even.x, x));
+ CHECK(secp256k1_fe_equal_var(&ge_odd.x, x));
+
+ /* Check that the Y coordinate result in ge_quad is a square. */
+ CHECK(secp256k1_fe_is_quad_var(&ge_quad.y));
+
+ /* Check odd/even Y in ge_odd, ge_even. */
+ CHECK(secp256k1_fe_is_odd(&ge_odd.y));
+ CHECK(!secp256k1_fe_is_odd(&ge_even.y));
+
+ /* Check secp256k1_gej_has_quad_y_var. */
+ secp256k1_gej_set_ge(&gej_quad, &ge_quad);
+ CHECK(secp256k1_gej_has_quad_y_var(&gej_quad));
+ do {
+ random_fe_test(&fez);
+ } while (secp256k1_fe_is_zero(&fez));
+ secp256k1_gej_rescale(&gej_quad, &fez);
+ CHECK(secp256k1_gej_has_quad_y_var(&gej_quad));
+ secp256k1_gej_neg(&gej_quad, &gej_quad);
+ CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad));
+ do {
+ random_fe_test(&fez);
+ } while (secp256k1_fe_is_zero(&fez));
+ secp256k1_gej_rescale(&gej_quad, &fez);
+ CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad));
+ secp256k1_gej_neg(&gej_quad, &gej_quad);
+ CHECK(secp256k1_gej_has_quad_y_var(&gej_quad));
+ }
+}
+
+void run_group_decompress(void) {
+ int i;
+ for (i = 0; i < count * 4; i++) {
+ secp256k1_fe fe;
+ random_fe_test(&fe);
+ test_group_decompress(&fe);
+ }
+}
+
/***** ECMULT TESTS *****/
void run_ecmult_chain(void) {
@@ -1582,9 +2528,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
secp256k1_scalar x, shift;
int wnaf[256] = {0};
int i;
-#ifdef USE_ENDOMORPHISM
int skew;
-#endif
secp256k1_scalar num = *number;
secp256k1_scalar_set_int(&x, 0);
@@ -1594,10 +2538,8 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
for (i = 0; i < 16; ++i) {
secp256k1_scalar_shr_int(&num, 8);
}
- skew = secp256k1_wnaf_const(wnaf, num, w);
-#else
- secp256k1_wnaf_const(wnaf, num, w);
#endif
+ skew = secp256k1_wnaf_const(wnaf, num, w);
for (i = WNAF_SIZE(w); i >= 0; --i) {
secp256k1_scalar t;
@@ -1616,10 +2558,8 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
}
secp256k1_scalar_add(&x, &x, &t);
}
-#ifdef USE_ENDOMORPHISM
- /* Skew num because when encoding 128-bit numbers as odd we use an offset */
+ /* Skew num because when encoding numbers as odd we use an offset */
secp256k1_scalar_cadd_bit(&num, skew == 2, 1);
-#endif
CHECK(secp256k1_scalar_eq(&x, &num));
}
@@ -1640,6 +2580,11 @@ void run_wnaf(void) {
test_constant_wnaf_negate(&n);
test_constant_wnaf(&n, 4 + (i % 10));
}
+ secp256k1_scalar_set_int(&n, 0);
+ CHECK(secp256k1_scalar_cond_negate(&n, 1) == -1);
+ CHECK(secp256k1_scalar_is_zero(&n));
+ CHECK(secp256k1_scalar_cond_negate(&n, 0) == 1);
+ CHECK(secp256k1_scalar_is_zero(&n));
}
void test_ecmult_constants(void) {
@@ -1680,7 +2625,7 @@ void run_ecmult_constants(void) {
}
void test_ecmult_gen_blind(void) {
- /* Test ecmult_gen() blinding and confirm that the blinding changes, the affline points match, and the z's don't match. */
+ /* Test ecmult_gen() blinding and confirm that the blinding changes, the affine points match, and the z's don't match. */
secp256k1_scalar key;
secp256k1_scalar b;
unsigned char seed32[32];
@@ -1752,6 +2697,644 @@ void run_endomorphism_tests(void) {
}
#endif
+void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvalid) {
+ unsigned char pubkeyc[65];
+ secp256k1_pubkey pubkey;
+ secp256k1_ge ge;
+ size_t pubkeyclen;
+ int32_t ecount;
+ ecount = 0;
+ secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount);
+ for (pubkeyclen = 3; pubkeyclen <= 65; pubkeyclen++) {
+ /* Smaller sizes are tested exhaustively elsewhere. */
+ int32_t i;
+ memcpy(&pubkeyc[1], input, 64);
+ VG_UNDEF(&pubkeyc[pubkeyclen], 65 - pubkeyclen);
+ for (i = 0; i < 256; i++) {
+ /* Try all type bytes. */
+ int xpass;
+ int ypass;
+ int ysign;
+ pubkeyc[0] = i;
+ /* What sign does this point have? */
+ ysign = (input[63] & 1) + 2;
+ /* For the current type (i) do we expect parsing to work? Handled all of compressed/uncompressed/hybrid. */
+ xpass = xvalid && (pubkeyclen == 33) && ((i & 254) == 2);
+ /* Do we expect a parse and re-serialize as uncompressed to give a matching y? */
+ ypass = xvalid && yvalid && ((i & 4) == ((pubkeyclen == 65) << 2)) &&
+ ((i == 4) || ((i & 251) == ysign)) && ((pubkeyclen == 33) || (pubkeyclen == 65));
+ if (xpass || ypass) {
+ /* These cases must parse. */
+ unsigned char pubkeyo[65];
+ size_t outl;
+ memset(&pubkey, 0, sizeof(pubkey));
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ ecount = 0;
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ outl = 65;
+ VG_UNDEF(pubkeyo, 65);
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
+ VG_CHECK(pubkeyo, outl);
+ CHECK(outl == 33);
+ CHECK(memcmp(&pubkeyo[1], &pubkeyc[1], 32) == 0);
+ CHECK((pubkeyclen != 33) || (pubkeyo[0] == pubkeyc[0]));
+ if (ypass) {
+ /* This test isn't always done because we decode with alternative signs, so the y won't match. */
+ CHECK(pubkeyo[0] == ysign);
+ CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 1);
+ memset(&pubkey, 0, sizeof(pubkey));
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ secp256k1_pubkey_save(&pubkey, &ge);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ outl = 65;
+ VG_UNDEF(pubkeyo, 65);
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1);
+ VG_CHECK(pubkeyo, outl);
+ CHECK(outl == 65);
+ CHECK(pubkeyo[0] == 4);
+ CHECK(memcmp(&pubkeyo[1], input, 64) == 0);
+ }
+ CHECK(ecount == 0);
+ } else {
+ /* These cases must fail to parse. */
+ memset(&pubkey, 0xfe, sizeof(pubkey));
+ ecount = 0;
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 0);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(ecount == 0);
+ CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0);
+ CHECK(ecount == 1);
+ }
+ }
+ }
+ secp256k1_context_set_illegal_callback(ctx, NULL, NULL);
+}
+
+void run_ec_pubkey_parse_test(void) {
+#define SECP256K1_EC_PARSE_TEST_NVALID (12)
+ const unsigned char valid[SECP256K1_EC_PARSE_TEST_NVALID][64] = {
+ {
+ /* Point with leading and trailing zeros in x and y serialization. */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x52,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x64, 0xef, 0xa1, 0x7b, 0x77, 0x61, 0xe1, 0xe4, 0x27, 0x06, 0x98, 0x9f, 0xb4, 0x83,
+ 0xb8, 0xd2, 0xd4, 0x9b, 0xf7, 0x8f, 0xae, 0x98, 0x03, 0xf0, 0x99, 0xb8, 0x34, 0xed, 0xeb, 0x00
+ },
+ {
+ /* Point with x equal to a 3rd root of unity.*/
+ 0x7a, 0xe9, 0x6a, 0x2b, 0x65, 0x7c, 0x07, 0x10, 0x6e, 0x64, 0x47, 0x9e, 0xac, 0x34, 0x34, 0xe9,
+ 0x9c, 0xf0, 0x49, 0x75, 0x12, 0xf5, 0x89, 0x95, 0xc1, 0x39, 0x6c, 0x28, 0x71, 0x95, 0x01, 0xee,
+ 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14,
+ 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee,
+ },
+ {
+ /* Point with largest x. (1/2) */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2c,
+ 0x0e, 0x99, 0x4b, 0x14, 0xea, 0x72, 0xf8, 0xc3, 0xeb, 0x95, 0xc7, 0x1e, 0xf6, 0x92, 0x57, 0x5e,
+ 0x77, 0x50, 0x58, 0x33, 0x2d, 0x7e, 0x52, 0xd0, 0x99, 0x5c, 0xf8, 0x03, 0x88, 0x71, 0xb6, 0x7d,
+ },
+ {
+ /* Point with largest x. (2/2) */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2c,
+ 0xf1, 0x66, 0xb4, 0xeb, 0x15, 0x8d, 0x07, 0x3c, 0x14, 0x6a, 0x38, 0xe1, 0x09, 0x6d, 0xa8, 0xa1,
+ 0x88, 0xaf, 0xa7, 0xcc, 0xd2, 0x81, 0xad, 0x2f, 0x66, 0xa3, 0x07, 0xfb, 0x77, 0x8e, 0x45, 0xb2,
+ },
+ {
+ /* Point with smallest x. (1/2) */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14,
+ 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee,
+ },
+ {
+ /* Point with smallest x. (2/2) */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0xbd, 0xe7, 0x0d, 0xf5, 0x19, 0x39, 0xb9, 0x4c, 0x9c, 0x24, 0x97, 0x9f, 0xa7, 0xdd, 0x04, 0xeb,
+ 0xd9, 0xb3, 0x57, 0x2d, 0xa7, 0x80, 0x22, 0x90, 0x43, 0x8a, 0xf2, 0xa6, 0x81, 0x89, 0x54, 0x41,
+ },
+ {
+ /* Point with largest y. (1/3) */
+ 0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6,
+ 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e,
+ },
+ {
+ /* Point with largest y. (2/3) */
+ 0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c,
+ 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e,
+ },
+ {
+ /* Point with largest y. (3/3) */
+ 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc,
+ 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e,
+ },
+ {
+ /* Point with smallest y. (1/3) */
+ 0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6,
+ 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ },
+ {
+ /* Point with smallest y. (2/3) */
+ 0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c,
+ 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ },
+ {
+ /* Point with smallest y. (3/3) */
+ 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc,
+ 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+ }
+ };
+#define SECP256K1_EC_PARSE_TEST_NXVALID (4)
+ const unsigned char onlyxvalid[SECP256K1_EC_PARSE_TEST_NXVALID][64] = {
+ {
+ /* Valid if y overflow ignored (y = 1 mod p). (1/3) */
+ 0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6,
+ 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30,
+ },
+ {
+ /* Valid if y overflow ignored (y = 1 mod p). (2/3) */
+ 0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c,
+ 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30,
+ },
+ {
+ /* Valid if y overflow ignored (y = 1 mod p). (3/3)*/
+ 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc,
+ 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30,
+ },
+ {
+ /* x on curve, y is from y^2 = x^3 + 8. */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03
+ }
+ };
+#define SECP256K1_EC_PARSE_TEST_NINVALID (7)
+ const unsigned char invalid[SECP256K1_EC_PARSE_TEST_NINVALID][64] = {
+ {
+ /* x is third root of -8, y is -1 * (x^3+7); also on the curve for y^2 = x^3 + 9. */
+ 0x0a, 0x2d, 0x2b, 0xa9, 0x35, 0x07, 0xf1, 0xdf, 0x23, 0x37, 0x70, 0xc2, 0xa7, 0x97, 0x96, 0x2c,
+ 0xc6, 0x1f, 0x6d, 0x15, 0xda, 0x14, 0xec, 0xd4, 0x7d, 0x8d, 0x27, 0xae, 0x1c, 0xd5, 0xf8, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ },
+ {
+ /* Valid if x overflow ignored (x = 1 mod p). */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30,
+ 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14,
+ 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee,
+ },
+ {
+ /* Valid if x overflow ignored (x = 1 mod p). */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30,
+ 0xbd, 0xe7, 0x0d, 0xf5, 0x19, 0x39, 0xb9, 0x4c, 0x9c, 0x24, 0x97, 0x9f, 0xa7, 0xdd, 0x04, 0xeb,
+ 0xd9, 0xb3, 0x57, 0x2d, 0xa7, 0x80, 0x22, 0x90, 0x43, 0x8a, 0xf2, 0xa6, 0x81, 0x89, 0x54, 0x41,
+ },
+ {
+ /* x is -1, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 5. */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e,
+ 0xf4, 0x84, 0x14, 0x5c, 0xb0, 0x14, 0x9b, 0x82, 0x5d, 0xff, 0x41, 0x2f, 0xa0, 0x52, 0xa8, 0x3f,
+ 0xcb, 0x72, 0xdb, 0x61, 0xd5, 0x6f, 0x37, 0x70, 0xce, 0x06, 0x6b, 0x73, 0x49, 0xa2, 0xaa, 0x28,
+ },
+ {
+ /* x is -1, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 5. */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e,
+ 0x0b, 0x7b, 0xeb, 0xa3, 0x4f, 0xeb, 0x64, 0x7d, 0xa2, 0x00, 0xbe, 0xd0, 0x5f, 0xad, 0x57, 0xc0,
+ 0x34, 0x8d, 0x24, 0x9e, 0x2a, 0x90, 0xc8, 0x8f, 0x31, 0xf9, 0x94, 0x8b, 0xb6, 0x5d, 0x52, 0x07,
+ },
+ {
+ /* x is zero, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 7. */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x8f, 0x53, 0x7e, 0xef, 0xdf, 0xc1, 0x60, 0x6a, 0x07, 0x27, 0xcd, 0x69, 0xb4, 0xa7, 0x33, 0x3d,
+ 0x38, 0xed, 0x44, 0xe3, 0x93, 0x2a, 0x71, 0x79, 0xee, 0xcb, 0x4b, 0x6f, 0xba, 0x93, 0x60, 0xdc,
+ },
+ {
+ /* x is zero, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 7. */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x70, 0xac, 0x81, 0x10, 0x20, 0x3e, 0x9f, 0x95, 0xf8, 0xd8, 0x32, 0x96, 0x4b, 0x58, 0xcc, 0xc2,
+ 0xc7, 0x12, 0xbb, 0x1c, 0x6c, 0xd5, 0x8e, 0x86, 0x11, 0x34, 0xb4, 0x8f, 0x45, 0x6c, 0x9b, 0x53
+ }
+ };
+ const unsigned char pubkeyc[66] = {
+ /* Serialization of G. */
+ 0x04, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B,
+ 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17,
+ 0x98, 0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC, 0x0E, 0x11, 0x08,
+ 0xA8, 0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54, 0x19, 0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10, 0xD4,
+ 0xB8, 0x00
+ };
+ unsigned char sout[65];
+ unsigned char shortkey[2];
+ secp256k1_ge ge;
+ secp256k1_pubkey pubkey;
+ size_t len;
+ int32_t i;
+ int32_t ecount;
+ int32_t ecount2;
+ ecount = 0;
+ /* Nothing should be reading this far into pubkeyc. */
+ VG_UNDEF(&pubkeyc[65], 1);
+ secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount);
+ /* Zero length claimed, fail, zeroize, no illegal arg error. */
+ memset(&pubkey, 0xfe, sizeof(pubkey));
+ ecount = 0;
+ VG_UNDEF(shortkey, 2);
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 0) == 0);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(ecount == 0);
+ CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0);
+ CHECK(ecount == 1);
+ /* Length one claimed, fail, zeroize, no illegal arg error. */
+ for (i = 0; i < 256 ; i++) {
+ memset(&pubkey, 0xfe, sizeof(pubkey));
+ ecount = 0;
+ shortkey[0] = i;
+ VG_UNDEF(&shortkey[1], 1);
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 1) == 0);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(ecount == 0);
+ CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0);
+ CHECK(ecount == 1);
+ }
+ /* Length two claimed, fail, zeroize, no illegal arg error. */
+ for (i = 0; i < 65536 ; i++) {
+ memset(&pubkey, 0xfe, sizeof(pubkey));
+ ecount = 0;
+ shortkey[0] = i & 255;
+ shortkey[1] = i >> 8;
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 2) == 0);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(ecount == 0);
+ CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0);
+ CHECK(ecount == 1);
+ }
+ memset(&pubkey, 0xfe, sizeof(pubkey));
+ ecount = 0;
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ /* 33 bytes claimed on otherwise valid input starting with 0x04, fail, zeroize output, no illegal arg error. */
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 33) == 0);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(ecount == 0);
+ CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0);
+ CHECK(ecount == 1);
+ /* NULL pubkey, illegal arg error. Pubkey isn't rewritten before this step, since it's NULL into the parser. */
+ CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, pubkeyc, 65) == 0);
+ CHECK(ecount == 2);
+ /* NULL input string. Illegal arg and zeroize output. */
+ memset(&pubkey, 0xfe, sizeof(pubkey));
+ ecount = 0;
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, NULL, 65) == 0);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(ecount == 1);
+ CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0);
+ CHECK(ecount == 2);
+ /* 64 bytes claimed on input starting with 0x04, fail, zeroize output, no illegal arg error. */
+ memset(&pubkey, 0xfe, sizeof(pubkey));
+ ecount = 0;
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 64) == 0);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(ecount == 0);
+ CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0);
+ CHECK(ecount == 1);
+ /* 66 bytes claimed, fail, zeroize output, no illegal arg error. */
+ memset(&pubkey, 0xfe, sizeof(pubkey));
+ ecount = 0;
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 66) == 0);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(ecount == 0);
+ CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0);
+ CHECK(ecount == 1);
+ /* Valid parse. */
+ memset(&pubkey, 0, sizeof(pubkey));
+ ecount = 0;
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 65) == 1);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(ecount == 0);
+ VG_UNDEF(&ge, sizeof(ge));
+ CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 1);
+ VG_CHECK(&ge.x, sizeof(ge.x));
+ VG_CHECK(&ge.y, sizeof(ge.y));
+ VG_CHECK(&ge.infinity, sizeof(ge.infinity));
+ ge_equals_ge(&secp256k1_ge_const_g, &ge);
+ CHECK(ecount == 0);
+ /* secp256k1_ec_pubkey_serialize illegal args. */
+ ecount = 0;
+ len = 65;
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, NULL, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0);
+ CHECK(ecount == 1);
+ CHECK(len == 0);
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, NULL, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0);
+ CHECK(ecount == 2);
+ len = 65;
+ VG_UNDEF(sout, 65);
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, NULL, SECP256K1_EC_UNCOMPRESSED) == 0);
+ VG_CHECK(sout, 65);
+ CHECK(ecount == 3);
+ CHECK(len == 0);
+ len = 65;
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, ~0) == 0);
+ CHECK(ecount == 4);
+ CHECK(len == 0);
+ len = 65;
+ VG_UNDEF(sout, 65);
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1);
+ VG_CHECK(sout, 65);
+ CHECK(ecount == 4);
+ CHECK(len == 65);
+ /* Multiple illegal args. Should still set arg error only once. */
+ ecount = 0;
+ ecount2 = 11;
+ CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, NULL, 65) == 0);
+ CHECK(ecount == 1);
+ /* Does the illegal arg callback actually change the behavior? */
+ secp256k1_context_set_illegal_callback(ctx, uncounting_illegal_callback_fn, &ecount2);
+ CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, NULL, 65) == 0);
+ CHECK(ecount == 1);
+ CHECK(ecount2 == 10);
+ secp256k1_context_set_illegal_callback(ctx, NULL, NULL);
+ /* Try a bunch of prefabbed points with all possible encodings. */
+ for (i = 0; i < SECP256K1_EC_PARSE_TEST_NVALID; i++) {
+ ec_pubkey_parse_pointtest(valid[i], 1, 1);
+ }
+ for (i = 0; i < SECP256K1_EC_PARSE_TEST_NXVALID; i++) {
+ ec_pubkey_parse_pointtest(onlyxvalid[i], 1, 0);
+ }
+ for (i = 0; i < SECP256K1_EC_PARSE_TEST_NINVALID; i++) {
+ ec_pubkey_parse_pointtest(invalid[i], 0, 0);
+ }
+}
+
+void run_eckey_edge_case_test(void) {
+ const unsigned char orderc[32] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+ 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
+ 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41
+ };
+ const unsigned char zeros[sizeof(secp256k1_pubkey)] = {0x00};
+ unsigned char ctmp[33];
+ unsigned char ctmp2[33];
+ secp256k1_pubkey pubkey;
+ secp256k1_pubkey pubkey2;
+ secp256k1_pubkey pubkey_one;
+ secp256k1_pubkey pubkey_negone;
+ const secp256k1_pubkey *pubkeys[3];
+ size_t len;
+ int32_t ecount;
+ /* Group order is too large, reject. */
+ CHECK(secp256k1_ec_seckey_verify(ctx, orderc) == 0);
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, orderc) == 0);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ /* Maximum value is too large, reject. */
+ memset(ctmp, 255, 32);
+ CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0);
+ memset(&pubkey, 1, sizeof(pubkey));
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ /* Zero is too small, reject. */
+ memset(ctmp, 0, 32);
+ CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0);
+ memset(&pubkey, 1, sizeof(pubkey));
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ /* One must be accepted. */
+ ctmp[31] = 0x01;
+ CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1);
+ memset(&pubkey, 0, sizeof(pubkey));
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
+ pubkey_one = pubkey;
+ /* Group order + 1 is too large, reject. */
+ memcpy(ctmp, orderc, 32);
+ ctmp[31] = 0x42;
+ CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0);
+ memset(&pubkey, 1, sizeof(pubkey));
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ /* -1 must be accepted. */
+ ctmp[31] = 0x40;
+ CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1);
+ memset(&pubkey, 0, sizeof(pubkey));
+ VG_UNDEF(&pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1);
+ VG_CHECK(&pubkey, sizeof(pubkey));
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
+ pubkey_negone = pubkey;
+ /* Tweak of zero leaves the value changed. */
+ memset(ctmp2, 0, 32);
+ CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, ctmp2) == 1);
+ CHECK(memcmp(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40);
+ memcpy(&pubkey2, &pubkey, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1);
+ CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
+ /* Multiply tweak of zero zeroizes the output. */
+ CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, ctmp2) == 0);
+ CHECK(memcmp(zeros, ctmp, 32) == 0);
+ CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, ctmp2) == 0);
+ CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
+ memcpy(&pubkey, &pubkey2, sizeof(pubkey));
+ /* Overflowing key tweak zeroizes. */
+ memcpy(ctmp, orderc, 32);
+ ctmp[31] = 0x40;
+ CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, orderc) == 0);
+ CHECK(memcmp(zeros, ctmp, 32) == 0);
+ memcpy(ctmp, orderc, 32);
+ ctmp[31] = 0x40;
+ CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, orderc) == 0);
+ CHECK(memcmp(zeros, ctmp, 32) == 0);
+ memcpy(ctmp, orderc, 32);
+ ctmp[31] = 0x40;
+ CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, orderc) == 0);
+ CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
+ memcpy(&pubkey, &pubkey2, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, orderc) == 0);
+ CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
+ memcpy(&pubkey, &pubkey2, sizeof(pubkey));
+ /* Private key tweaks results in a key of zero. */
+ ctmp2[31] = 1;
+ CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 0);
+ CHECK(memcmp(zeros, ctmp2, 32) == 0);
+ ctmp2[31] = 1;
+ CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0);
+ CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
+ memcpy(&pubkey, &pubkey2, sizeof(pubkey));
+ /* Tweak computation wraps and results in a key of 1. */
+ ctmp2[31] = 2;
+ CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 1);
+ CHECK(memcmp(ctmp2, zeros, 31) == 0 && ctmp2[31] == 1);
+ ctmp2[31] = 2;
+ CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1);
+ ctmp2[31] = 1;
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, ctmp2) == 1);
+ CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
+ /* Tweak mul * 2 = 1+1. */
+ CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1);
+ ctmp2[31] = 2;
+ CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 1);
+ CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
+ /* Test argument errors. */
+ ecount = 0;
+ secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount);
+ CHECK(ecount == 0);
+ /* Zeroize pubkey on parse error. */
+ memset(&pubkey, 0, 32);
+ CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0);
+ CHECK(ecount == 1);
+ CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
+ memcpy(&pubkey, &pubkey2, sizeof(pubkey));
+ memset(&pubkey2, 0, 32);
+ CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 0);
+ CHECK(ecount == 2);
+ CHECK(memcmp(&pubkey2, zeros, sizeof(pubkey2)) == 0);
+ /* Plain argument errors. */
+ ecount = 0;
+ CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1);
+ CHECK(ecount == 0);
+ CHECK(secp256k1_ec_seckey_verify(ctx, NULL) == 0);
+ CHECK(ecount == 1);
+ ecount = 0;
+ memset(ctmp2, 0, 32);
+ ctmp2[31] = 4;
+ CHECK(secp256k1_ec_pubkey_tweak_add(ctx, NULL, ctmp2) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, NULL) == 0);
+ CHECK(ecount == 2);
+ ecount = 0;
+ memset(ctmp2, 0, 32);
+ ctmp2[31] = 4;
+ CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, NULL, ctmp2) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, NULL) == 0);
+ CHECK(ecount == 2);
+ ecount = 0;
+ memset(ctmp2, 0, 32);
+ CHECK(secp256k1_ec_privkey_tweak_add(ctx, NULL, ctmp2) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, NULL) == 0);
+ CHECK(ecount == 2);
+ ecount = 0;
+ memset(ctmp2, 0, 32);
+ ctmp2[31] = 1;
+ CHECK(secp256k1_ec_privkey_tweak_mul(ctx, NULL, ctmp2) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, NULL) == 0);
+ CHECK(ecount == 2);
+ ecount = 0;
+ CHECK(secp256k1_ec_pubkey_create(ctx, NULL, ctmp) == 0);
+ CHECK(ecount == 1);
+ memset(&pubkey, 1, sizeof(pubkey));
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0);
+ CHECK(ecount == 2);
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ /* secp256k1_ec_pubkey_combine tests. */
+ ecount = 0;
+ pubkeys[0] = &pubkey_one;
+ VG_UNDEF(&pubkeys[0], sizeof(secp256k1_pubkey *));
+ VG_UNDEF(&pubkeys[1], sizeof(secp256k1_pubkey *));
+ VG_UNDEF(&pubkeys[2], sizeof(secp256k1_pubkey *));
+ memset(&pubkey, 255, sizeof(secp256k1_pubkey));
+ VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
+ CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 0) == 0);
+ VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ec_pubkey_combine(ctx, NULL, pubkeys, 1) == 0);
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(ecount == 2);
+ memset(&pubkey, 255, sizeof(secp256k1_pubkey));
+ VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
+ CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, NULL, 1) == 0);
+ VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(ecount == 3);
+ pubkeys[0] = &pubkey_negone;
+ memset(&pubkey, 255, sizeof(secp256k1_pubkey));
+ VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
+ CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 1) == 1);
+ VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
+ CHECK(ecount == 3);
+ len = 33;
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_negone, SECP256K1_EC_COMPRESSED) == 1);
+ CHECK(memcmp(ctmp, ctmp2, 33) == 0);
+ /* Result is infinity. */
+ pubkeys[0] = &pubkey_one;
+ pubkeys[1] = &pubkey_negone;
+ memset(&pubkey, 255, sizeof(secp256k1_pubkey));
+ VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
+ CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 0);
+ VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(ecount == 3);
+ /* Passes through infinity but comes out one. */
+ pubkeys[2] = &pubkey_one;
+ memset(&pubkey, 255, sizeof(secp256k1_pubkey));
+ VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
+ CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 3) == 1);
+ VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
+ CHECK(ecount == 3);
+ len = 33;
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_one, SECP256K1_EC_COMPRESSED) == 1);
+ CHECK(memcmp(ctmp, ctmp2, 33) == 0);
+ /* Adds to two. */
+ pubkeys[1] = &pubkey_one;
+ memset(&pubkey, 255, sizeof(secp256k1_pubkey));
+ VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
+ CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 1);
+ VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
+ CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
+ CHECK(ecount == 3);
+ secp256k1_context_set_illegal_callback(ctx, NULL, NULL);
+}
+
void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) {
secp256k1_scalar nonce;
do {
@@ -1771,7 +3354,7 @@ void test_ecdsa_sign_verify(void) {
random_scalar_order_test(&key);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubj, &key);
secp256k1_ge_set_gej(&pub, &pubj);
- getrec = secp256k1_rand32()&1;
+ getrec = secp256k1_rand_bits(1);
random_sign(&sigr, &sigs, &key, &msg, getrec?&recid:NULL);
if (getrec) {
CHECK(recid >= 0 && recid < 4);
@@ -1828,7 +3411,7 @@ static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char
}
return 1;
}
- /* Retry rate of 6979 is negligible esp. as we only call this in determinstic tests. */
+ /* Retry rate of 6979 is negligible esp. as we only call this in deterministic tests. */
/* If someone does fine a case where it retries for secp256k1, we'd like to know. */
if (counter > 5) {
return 0;
@@ -1846,7 +3429,8 @@ void test_ecdsa_end_to_end(void) {
unsigned char privkey[32];
unsigned char message[32];
unsigned char privkey2[32];
- secp256k1_ecdsa_signature signature[5];
+ secp256k1_ecdsa_signature signature[6];
+ secp256k1_scalar r, s;
unsigned char sig[74];
size_t siglen = 74;
unsigned char pubkeyc[65];
@@ -1869,17 +3453,17 @@ void test_ecdsa_end_to_end(void) {
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1);
/* Verify exporting and importing public key. */
- CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyc, &pubkeyclen, &pubkey, secp256k1_rand32() % 2) == 1);
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyc, &pubkeyclen, &pubkey, secp256k1_rand_bits(1) == 1 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED));
memset(&pubkey, 0, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1);
/* Verify private key import and export. */
- CHECK(secp256k1_ec_privkey_export(ctx, seckey, &seckeylen, privkey, (secp256k1_rand32() % 2) == 1) ? SECP256K1_EC_COMPRESSED : 0);
- CHECK(secp256k1_ec_privkey_import(ctx, privkey2, seckey, seckeylen) == 1);
+ CHECK(ec_privkey_export_der(ctx, seckey, &seckeylen, privkey, secp256k1_rand_bits(1) == 1));
+ CHECK(ec_privkey_import_der(ctx, privkey2, seckey, seckeylen) == 1);
CHECK(memcmp(privkey, privkey2, 32) == 0);
/* Optionally tweak the keys using addition. */
- if (secp256k1_rand32() % 3 == 0) {
+ if (secp256k1_rand_int(3) == 0) {
int ret1;
int ret2;
unsigned char rnd[32];
@@ -1896,7 +3480,7 @@ void test_ecdsa_end_to_end(void) {
}
/* Optionally tweak the keys using multiplication. */
- if (secp256k1_rand32() % 3 == 0) {
+ if (secp256k1_rand_int(3) == 0) {
int ret1;
int ret2;
unsigned char rnd[32];
@@ -1933,6 +3517,22 @@ void test_ecdsa_end_to_end(void) {
CHECK(secp256k1_ecdsa_verify(ctx, &signature[1], message, &pubkey) == 1);
CHECK(secp256k1_ecdsa_verify(ctx, &signature[2], message, &pubkey) == 1);
CHECK(secp256k1_ecdsa_verify(ctx, &signature[3], message, &pubkey) == 1);
+ /* Test lower-S form, malleate, verify and fail, test again, malleate again */
+ CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[0]));
+ secp256k1_ecdsa_signature_load(ctx, &r, &s, &signature[0]);
+ secp256k1_scalar_negate(&s, &s);
+ secp256k1_ecdsa_signature_save(&signature[5], &r, &s);
+ CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 0);
+ CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5]));
+ CHECK(secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5]));
+ CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5]));
+ CHECK(!secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5]));
+ CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1);
+ secp256k1_scalar_negate(&s, &s);
+ secp256k1_ecdsa_signature_save(&signature[5], &r, &s);
+ CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5]));
+ CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1);
+ CHECK(memcmp(&signature[5], &signature[0], 64) == 0);
/* Serialize/parse DER and verify again */
CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1);
@@ -1942,7 +3542,7 @@ void test_ecdsa_end_to_end(void) {
/* Serialize/destroy/parse DER and verify again. */
siglen = 74;
CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1);
- sig[secp256k1_rand32() % siglen] += 1 + (secp256k1_rand32() % 255);
+ sig[secp256k1_rand_int(siglen)] += 1 + secp256k1_rand_int(255);
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature[0], sig, siglen) == 0 ||
secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 0);
}
@@ -1952,23 +3552,18 @@ void test_random_pubkeys(void) {
secp256k1_ge elem2;
unsigned char in[65];
/* Generate some randomly sized pubkeys. */
- uint32_t r = secp256k1_rand32();
- size_t len = (r & 3) == 0 ? 65 : 33;
- r>>=2;
- if ((r & 3) == 0) {
- len = (r & 252) >> 3;
+ size_t len = secp256k1_rand_bits(2) == 0 ? 65 : 33;
+ if (secp256k1_rand_bits(2) == 0) {
+ len = secp256k1_rand_bits(6);
}
- r>>=8;
if (len == 65) {
- in[0] = (r & 2) ? 4 : ((r & 1)? 6 : 7);
+ in[0] = secp256k1_rand_bits(1) ? 4 : (secp256k1_rand_bits(1) ? 6 : 7);
} else {
- in[0] = (r & 1) ? 2 : 3;
+ in[0] = secp256k1_rand_bits(1) ? 2 : 3;
}
- r>>=2;
- if ((r & 7) == 0) {
- in[0] = (r & 2040) >> 3;
+ if (secp256k1_rand_bits(3) == 0) {
+ in[0] = secp256k1_rand_bits(8);
}
- r>>=11;
if (len > 1) {
secp256k1_rand256(&in[1]);
}
@@ -1982,7 +3577,7 @@ void test_random_pubkeys(void) {
size_t size = len;
firstb = in[0];
/* If the pubkey can be parsed, it should round-trip... */
- CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, (len == 33) ? SECP256K1_EC_COMPRESSED : 0));
+ CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, len == 33));
CHECK(size == len);
CHECK(memcmp(&in[1], &out[1], len-1) == 0);
/* ... except for the type of hybrid inputs. */
@@ -1995,7 +3590,7 @@ void test_random_pubkeys(void) {
CHECK(secp256k1_eckey_pubkey_parse(&elem2, in, size));
ge_equals_ge(&elem,&elem2);
/* Check that the X9.62 hybrid type is checked. */
- in[0] = (r & 1) ? 6 : 7;
+ in[0] = secp256k1_rand_bits(1) ? 6 : 7;
res = secp256k1_eckey_pubkey_parse(&elem2, in, size);
if (firstb == 2 || firstb == 3) {
if (in[0] == firstb + 4) {
@@ -2026,6 +3621,334 @@ void run_ecdsa_end_to_end(void) {
}
}
+int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_der, int certainly_not_der) {
+ static const unsigned char zeroes[32] = {0};
+#ifdef ENABLE_OPENSSL_TESTS
+ static const unsigned char max_scalar[32] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+ 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
+ 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40
+ };
+#endif
+
+ int ret = 0;
+
+ secp256k1_ecdsa_signature sig_der;
+ unsigned char roundtrip_der[2048];
+ unsigned char compact_der[64];
+ size_t len_der = 2048;
+ int parsed_der = 0, valid_der = 0, roundtrips_der = 0;
+
+ secp256k1_ecdsa_signature sig_der_lax;
+ unsigned char roundtrip_der_lax[2048];
+ unsigned char compact_der_lax[64];
+ size_t len_der_lax = 2048;
+ int parsed_der_lax = 0, valid_der_lax = 0, roundtrips_der_lax = 0;
+
+#ifdef ENABLE_OPENSSL_TESTS
+ ECDSA_SIG *sig_openssl;
+ const unsigned char *sigptr;
+ unsigned char roundtrip_openssl[2048];
+ int len_openssl = 2048;
+ int parsed_openssl, valid_openssl = 0, roundtrips_openssl = 0;
+#endif
+
+ parsed_der = secp256k1_ecdsa_signature_parse_der(ctx, &sig_der, sig, siglen);
+ if (parsed_der) {
+ ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der, &sig_der)) << 0;
+ valid_der = (memcmp(compact_der, zeroes, 32) != 0) && (memcmp(compact_der + 32, zeroes, 32) != 0);
+ }
+ if (valid_der) {
+ ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der, &len_der, &sig_der)) << 1;
+ roundtrips_der = (len_der == siglen) && memcmp(roundtrip_der, sig, siglen) == 0;
+ }
+
+ parsed_der_lax = ecdsa_signature_parse_der_lax(ctx, &sig_der_lax, sig, siglen);
+ if (parsed_der_lax) {
+ ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der_lax, &sig_der_lax)) << 10;
+ valid_der_lax = (memcmp(compact_der_lax, zeroes, 32) != 0) && (memcmp(compact_der_lax + 32, zeroes, 32) != 0);
+ }
+ if (valid_der_lax) {
+ ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der_lax, &len_der_lax, &sig_der_lax)) << 11;
+ roundtrips_der_lax = (len_der_lax == siglen) && memcmp(roundtrip_der_lax, sig, siglen) == 0;
+ }
+
+ if (certainly_der) {
+ ret |= (!parsed_der) << 2;
+ }
+ if (certainly_not_der) {
+ ret |= (parsed_der) << 17;
+ }
+ if (valid_der) {
+ ret |= (!roundtrips_der) << 3;
+ }
+
+ if (valid_der) {
+ ret |= (!roundtrips_der_lax) << 12;
+ ret |= (len_der != len_der_lax) << 13;
+ ret |= (memcmp(roundtrip_der_lax, roundtrip_der, len_der) != 0) << 14;
+ }
+ ret |= (roundtrips_der != roundtrips_der_lax) << 15;
+ if (parsed_der) {
+ ret |= (!parsed_der_lax) << 16;
+ }
+
+#ifdef ENABLE_OPENSSL_TESTS
+ sig_openssl = ECDSA_SIG_new();
+ sigptr = sig;
+ parsed_openssl = (d2i_ECDSA_SIG(&sig_openssl, &sigptr, siglen) != NULL);
+ if (parsed_openssl) {
+ valid_openssl = !BN_is_negative(sig_openssl->r) && !BN_is_negative(sig_openssl->s) && BN_num_bits(sig_openssl->r) > 0 && BN_num_bits(sig_openssl->r) <= 256 && BN_num_bits(sig_openssl->s) > 0 && BN_num_bits(sig_openssl->s) <= 256;
+ if (valid_openssl) {
+ unsigned char tmp[32] = {0};
+ BN_bn2bin(sig_openssl->r, tmp + 32 - BN_num_bytes(sig_openssl->r));
+ valid_openssl = memcmp(tmp, max_scalar, 32) < 0;
+ }
+ if (valid_openssl) {
+ unsigned char tmp[32] = {0};
+ BN_bn2bin(sig_openssl->s, tmp + 32 - BN_num_bytes(sig_openssl->s));
+ valid_openssl = memcmp(tmp, max_scalar, 32) < 0;
+ }
+ }
+ len_openssl = i2d_ECDSA_SIG(sig_openssl, NULL);
+ if (len_openssl <= 2048) {
+ unsigned char *ptr = roundtrip_openssl;
+ CHECK(i2d_ECDSA_SIG(sig_openssl, &ptr) == len_openssl);
+ roundtrips_openssl = valid_openssl && ((size_t)len_openssl == siglen) && (memcmp(roundtrip_openssl, sig, siglen) == 0);
+ } else {
+ len_openssl = 0;
+ }
+ ECDSA_SIG_free(sig_openssl);
+
+ ret |= (parsed_der && !parsed_openssl) << 4;
+ ret |= (valid_der && !valid_openssl) << 5;
+ ret |= (roundtrips_openssl && !parsed_der) << 6;
+ ret |= (roundtrips_der != roundtrips_openssl) << 7;
+ if (roundtrips_openssl) {
+ ret |= (len_der != (size_t)len_openssl) << 8;
+ ret |= (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0) << 9;
+ }
+#endif
+ return ret;
+}
+
+static void assign_big_endian(unsigned char *ptr, size_t ptrlen, uint32_t val) {
+ size_t i;
+ for (i = 0; i < ptrlen; i++) {
+ int shift = ptrlen - 1 - i;
+ if (shift >= 4) {
+ ptr[i] = 0;
+ } else {
+ ptr[i] = (val >> shift) & 0xFF;
+ }
+ }
+}
+
+static void damage_array(unsigned char *sig, size_t *len) {
+ int pos;
+ int action = secp256k1_rand_bits(3);
+ if (action < 1 && *len > 3) {
+ /* Delete a byte. */
+ pos = secp256k1_rand_int(*len);
+ memmove(sig + pos, sig + pos + 1, *len - pos - 1);
+ (*len)--;
+ return;
+ } else if (action < 2 && *len < 2048) {
+ /* Insert a byte. */
+ pos = secp256k1_rand_int(1 + *len);
+ memmove(sig + pos + 1, sig + pos, *len - pos);
+ sig[pos] = secp256k1_rand_bits(8);
+ (*len)++;
+ return;
+ } else if (action < 4) {
+ /* Modify a byte. */
+ sig[secp256k1_rand_int(*len)] += 1 + secp256k1_rand_int(255);
+ return;
+ } else { /* action < 8 */
+ /* Modify a bit. */
+ sig[secp256k1_rand_int(*len)] ^= 1 << secp256k1_rand_bits(3);
+ return;
+ }
+}
+
+static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly_der, int* certainly_not_der) {
+ int der;
+ int nlow[2], nlen[2], nlenlen[2], nhbit[2], nhbyte[2], nzlen[2];
+ size_t tlen, elen, glen;
+ int indet;
+ int n;
+
+ *len = 0;
+ der = secp256k1_rand_bits(2) == 0;
+ *certainly_der = der;
+ *certainly_not_der = 0;
+ indet = der ? 0 : secp256k1_rand_int(10) == 0;
+
+ for (n = 0; n < 2; n++) {
+ /* We generate two classes of numbers: nlow==1 "low" ones (up to 32 bytes), nlow==0 "high" ones (32 bytes with 129 top bits set, or larger than 32 bytes) */
+ nlow[n] = der ? 1 : (secp256k1_rand_bits(3) != 0);
+ /* The length of the number in bytes (the first byte of which will always be nonzero) */
+ nlen[n] = nlow[n] ? secp256k1_rand_int(33) : 32 + secp256k1_rand_int(200) * secp256k1_rand_int(8) / 8;
+ CHECK(nlen[n] <= 232);
+ /* The top bit of the number. */
+ nhbit[n] = (nlow[n] == 0 && nlen[n] == 32) ? 1 : (nlen[n] == 0 ? 0 : secp256k1_rand_bits(1));
+ /* The top byte of the number (after the potential hardcoded 16 0xFF characters for "high" 32 bytes numbers) */
+ nhbyte[n] = nlen[n] == 0 ? 0 : (nhbit[n] ? 128 + secp256k1_rand_bits(7) : 1 + secp256k1_rand_int(127));
+ /* The number of zero bytes in front of the number (which is 0 or 1 in case of DER, otherwise we extend up to 300 bytes) */
+ nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? secp256k1_rand_int(3) : secp256k1_rand_int(300 - nlen[n]) * secp256k1_rand_int(8) / 8);
+ if (nzlen[n] > ((nlen[n] == 0 || nhbit[n]) ? 1 : 0)) {
+ *certainly_not_der = 1;
+ }
+ CHECK(nlen[n] + nzlen[n] <= 300);
+ /* The length of the length descriptor for the number. 0 means short encoding, anything else is long encoding. */
+ nlenlen[n] = nlen[n] + nzlen[n] < 128 ? 0 : (nlen[n] + nzlen[n] < 256 ? 1 : 2);
+ if (!der) {
+ /* nlenlen[n] max 127 bytes */
+ int add = secp256k1_rand_int(127 - nlenlen[n]) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256;
+ nlenlen[n] += add;
+ if (add != 0) {
+ *certainly_not_der = 1;
+ }
+ }
+ CHECK(nlen[n] + nzlen[n] + nlenlen[n] <= 427);
+ }
+
+ /* The total length of the data to go, so far */
+ tlen = 2 + nlenlen[0] + nlen[0] + nzlen[0] + 2 + nlenlen[1] + nlen[1] + nzlen[1];
+ CHECK(tlen <= 856);
+
+ /* The length of the garbage inside the tuple. */
+ elen = (der || indet) ? 0 : secp256k1_rand_int(980 - tlen) * secp256k1_rand_int(8) / 8;
+ if (elen != 0) {
+ *certainly_not_der = 1;
+ }
+ tlen += elen;
+ CHECK(tlen <= 980);
+
+ /* The length of the garbage after the end of the tuple. */
+ glen = der ? 0 : secp256k1_rand_int(990 - tlen) * secp256k1_rand_int(8) / 8;
+ if (glen != 0) {
+ *certainly_not_der = 1;
+ }
+ CHECK(tlen + glen <= 990);
+
+ /* Write the tuple header. */
+ sig[(*len)++] = 0x30;
+ if (indet) {
+ /* Indeterminate length */
+ sig[(*len)++] = 0x80;
+ *certainly_not_der = 1;
+ } else {
+ int tlenlen = tlen < 128 ? 0 : (tlen < 256 ? 1 : 2);
+ if (!der) {
+ int add = secp256k1_rand_int(127 - tlenlen) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256;
+ tlenlen += add;
+ if (add != 0) {
+ *certainly_not_der = 1;
+ }
+ }
+ if (tlenlen == 0) {
+ /* Short length notation */
+ sig[(*len)++] = tlen;
+ } else {
+ /* Long length notation */
+ sig[(*len)++] = 128 + tlenlen;
+ assign_big_endian(sig + *len, tlenlen, tlen);
+ *len += tlenlen;
+ }
+ tlen += tlenlen;
+ }
+ tlen += 2;
+ CHECK(tlen + glen <= 1119);
+
+ for (n = 0; n < 2; n++) {
+ /* Write the integer header. */
+ sig[(*len)++] = 0x02;
+ if (nlenlen[n] == 0) {
+ /* Short length notation */
+ sig[(*len)++] = nlen[n] + nzlen[n];
+ } else {
+ /* Long length notation. */
+ sig[(*len)++] = 128 + nlenlen[n];
+ assign_big_endian(sig + *len, nlenlen[n], nlen[n] + nzlen[n]);
+ *len += nlenlen[n];
+ }
+ /* Write zero padding */
+ while (nzlen[n] > 0) {
+ sig[(*len)++] = 0x00;
+ nzlen[n]--;
+ }
+ if (nlen[n] == 32 && !nlow[n]) {
+ /* Special extra 16 0xFF bytes in "high" 32-byte numbers */
+ int i;
+ for (i = 0; i < 16; i++) {
+ sig[(*len)++] = 0xFF;
+ }
+ nlen[n] -= 16;
+ }
+ /* Write first byte of number */
+ if (nlen[n] > 0) {
+ sig[(*len)++] = nhbyte[n];
+ nlen[n]--;
+ }
+ /* Generate remaining random bytes of number */
+ secp256k1_rand_bytes_test(sig + *len, nlen[n]);
+ *len += nlen[n];
+ nlen[n] = 0;
+ }
+
+ /* Generate random garbage inside tuple. */
+ secp256k1_rand_bytes_test(sig + *len, elen);
+ *len += elen;
+
+ /* Generate end-of-contents bytes. */
+ if (indet) {
+ sig[(*len)++] = 0;
+ sig[(*len)++] = 0;
+ tlen += 2;
+ }
+ CHECK(tlen + glen <= 1121);
+
+ /* Generate random garbage outside tuple. */
+ secp256k1_rand_bytes_test(sig + *len, glen);
+ *len += glen;
+ tlen += glen;
+ CHECK(tlen <= 1121);
+ CHECK(tlen == *len);
+}
+
+void run_ecdsa_der_parse(void) {
+ int i,j;
+ for (i = 0; i < 200 * count; i++) {
+ unsigned char buffer[2048];
+ size_t buflen = 0;
+ int certainly_der = 0;
+ int certainly_not_der = 0;
+ random_ber_signature(buffer, &buflen, &certainly_der, &certainly_not_der);
+ CHECK(buflen <= 2048);
+ for (j = 0; j < 16; j++) {
+ int ret = 0;
+ if (j > 0) {
+ damage_array(buffer, &buflen);
+ /* We don't know anything anymore about the DERness of the result */
+ certainly_der = 0;
+ certainly_not_der = 0;
+ }
+ ret = test_ecdsa_der_parse(buffer, buflen, certainly_der, certainly_not_der);
+ if (ret != 0) {
+ size_t k;
+ fprintf(stderr, "Failure %x on ", ret);
+ for (k = 0; k < buflen; k++) {
+ fprintf(stderr, "%02x ", buffer[k]);
+ }
+ fprintf(stderr, "\n");
+ }
+ CHECK(ret == 0);
+ }
+ }
+}
+
/* Tests several edge cases. */
void test_ecdsa_edge_cases(void) {
int t;
@@ -2047,11 +3970,159 @@ void test_ecdsa_edge_cases(void) {
CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0);
}
- /*Signature where s would be zero.*/
+ /* Verify signature with r of zero fails. */
{
- unsigned char signature[72];
+ const unsigned char pubkey_mods_zero[33] = {
+ 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0,
+ 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41,
+ 0x41
+ };
+ secp256k1_ge key;
+ secp256k1_scalar msg;
+ secp256k1_scalar sr, ss;
+ secp256k1_scalar_set_int(&ss, 1);
+ secp256k1_scalar_set_int(&msg, 0);
+ secp256k1_scalar_set_int(&sr, 0);
+ CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey_mods_zero, 33));
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0);
+ }
+
+ /* Verify signature with s of zero fails. */
+ {
+ const unsigned char pubkey[33] = {
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01
+ };
+ secp256k1_ge key;
+ secp256k1_scalar msg;
+ secp256k1_scalar sr, ss;
+ secp256k1_scalar_set_int(&ss, 0);
+ secp256k1_scalar_set_int(&msg, 0);
+ secp256k1_scalar_set_int(&sr, 1);
+ CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33));
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0);
+ }
+
+ /* Verify signature with message 0 passes. */
+ {
+ const unsigned char pubkey[33] = {
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02
+ };
+ const unsigned char pubkey2[33] = {
+ 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0,
+ 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41,
+ 0x43
+ };
+ secp256k1_ge key;
+ secp256k1_ge key2;
+ secp256k1_scalar msg;
+ secp256k1_scalar sr, ss;
+ secp256k1_scalar_set_int(&ss, 2);
+ secp256k1_scalar_set_int(&msg, 0);
+ secp256k1_scalar_set_int(&sr, 2);
+ CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33));
+ CHECK(secp256k1_eckey_pubkey_parse(&key2, pubkey2, 33));
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1);
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1);
+ secp256k1_scalar_negate(&ss, &ss);
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1);
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1);
+ secp256k1_scalar_set_int(&ss, 1);
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0);
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 0);
+ }
+
+ /* Verify signature with message 1 passes. */
+ {
+ const unsigned char pubkey[33] = {
+ 0x02, 0x14, 0x4e, 0x5a, 0x58, 0xef, 0x5b, 0x22,
+ 0x6f, 0xd2, 0xe2, 0x07, 0x6a, 0x77, 0xcf, 0x05,
+ 0xb4, 0x1d, 0xe7, 0x4a, 0x30, 0x98, 0x27, 0x8c,
+ 0x93, 0xe6, 0xe6, 0x3c, 0x0b, 0xc4, 0x73, 0x76,
+ 0x25
+ };
+ const unsigned char pubkey2[33] = {
+ 0x02, 0x8a, 0xd5, 0x37, 0xed, 0x73, 0xd9, 0x40,
+ 0x1d, 0xa0, 0x33, 0xd2, 0xdc, 0xf0, 0xaf, 0xae,
+ 0x34, 0xcf, 0x5f, 0x96, 0x4c, 0x73, 0x28, 0x0f,
+ 0x92, 0xc0, 0xf6, 0x9d, 0xd9, 0xb2, 0x09, 0x10,
+ 0x62
+ };
+ const unsigned char csr[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4,
+ 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xeb
+ };
+ secp256k1_ge key;
+ secp256k1_ge key2;
+ secp256k1_scalar msg;
+ secp256k1_scalar sr, ss;
+ secp256k1_scalar_set_int(&ss, 1);
+ secp256k1_scalar_set_int(&msg, 1);
+ secp256k1_scalar_set_b32(&sr, csr, NULL);
+ CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33));
+ CHECK(secp256k1_eckey_pubkey_parse(&key2, pubkey2, 33));
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1);
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1);
+ secp256k1_scalar_negate(&ss, &ss);
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1);
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1);
+ secp256k1_scalar_set_int(&ss, 2);
+ secp256k1_scalar_inverse_var(&ss, &ss);
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0);
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 0);
+ }
+
+ /* Verify signature with message -1 passes. */
+ {
+ const unsigned char pubkey[33] = {
+ 0x03, 0xaf, 0x97, 0xff, 0x7d, 0x3a, 0xf6, 0xa0,
+ 0x02, 0x94, 0xbd, 0x9f, 0x4b, 0x2e, 0xd7, 0x52,
+ 0x28, 0xdb, 0x49, 0x2a, 0x65, 0xcb, 0x1e, 0x27,
+ 0x57, 0x9c, 0xba, 0x74, 0x20, 0xd5, 0x1d, 0x20,
+ 0xf1
+ };
+ const unsigned char csr[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4,
+ 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xee
+ };
+ secp256k1_ge key;
+ secp256k1_scalar msg;
+ secp256k1_scalar sr, ss;
+ secp256k1_scalar_set_int(&ss, 1);
+ secp256k1_scalar_set_int(&msg, 1);
+ secp256k1_scalar_negate(&msg, &msg);
+ secp256k1_scalar_set_b32(&sr, csr, NULL);
+ CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33));
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1);
+ secp256k1_scalar_negate(&ss, &ss);
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1);
+ secp256k1_scalar_set_int(&ss, 3);
+ secp256k1_scalar_inverse_var(&ss, &ss);
+ CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0);
+ }
+
+ /* Signature where s would be zero. */
+ {
+ secp256k1_pubkey pubkey;
size_t siglen;
- const unsigned char nonce[32] = {
+ int32_t ecount;
+ unsigned char signature[72];
+ static const unsigned char nonce[32] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -2075,15 +4146,72 @@ void test_ecdsa_edge_cases(void) {
0xb8, 0x12, 0xe0, 0x0b, 0x81, 0x7a, 0x77, 0x62,
0x65, 0xdf, 0xdd, 0x31, 0xb9, 0x3e, 0x29, 0xa9,
};
+ ecount = 0;
+ secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount);
CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce) == 0);
CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce2) == 0);
msg[31] = 0xaa;
CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce) == 1);
+ CHECK(ecount == 0);
+ CHECK(secp256k1_ecdsa_sign(ctx, NULL, msg, key, precomputed_nonce_function, nonce2) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ecdsa_sign(ctx, &sig, NULL, key, precomputed_nonce_function, nonce2) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, NULL, precomputed_nonce_function, nonce2) == 0);
+ CHECK(ecount == 3);
CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce2) == 1);
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, key) == 1);
+ CHECK(secp256k1_ecdsa_verify(ctx, NULL, msg, &pubkey) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_ecdsa_verify(ctx, &sig, NULL, &pubkey) == 0);
+ CHECK(ecount == 5);
+ CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, NULL) == 0);
+ CHECK(ecount == 6);
+ CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 1);
+ CHECK(ecount == 6);
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0);
+ CHECK(ecount == 7);
+ /* That pubkeyload fails via an ARGCHECK is a little odd but makes sense because pubkeys are an opaque data type. */
+ CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 0);
+ CHECK(ecount == 8);
siglen = 72;
+ CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, NULL, &siglen, &sig) == 0);
+ CHECK(ecount == 9);
+ CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, NULL, &sig) == 0);
+ CHECK(ecount == 10);
+ CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, NULL) == 0);
+ CHECK(ecount == 11);
CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, &sig) == 1);
+ CHECK(ecount == 11);
+ CHECK(secp256k1_ecdsa_signature_parse_der(ctx, NULL, signature, siglen) == 0);
+ CHECK(ecount == 12);
+ CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, NULL, siglen) == 0);
+ CHECK(ecount == 13);
+ CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, signature, siglen) == 1);
+ CHECK(ecount == 13);
siglen = 10;
+ /* Too little room for a signature does not fail via ARGCHECK. */
CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, &sig) == 0);
+ CHECK(ecount == 13);
+ ecount = 0;
+ CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, NULL) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, NULL, &sig) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, NULL) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, &sig) == 1);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, NULL, signature) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, NULL) == 0);
+ CHECK(ecount == 5);
+ CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature) == 1);
+ CHECK(ecount == 5);
+ memset(signature, 255, 64);
+ CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature) == 0);
+ CHECK(ecount == 5);
+ secp256k1_context_set_illegal_callback(ctx, NULL, NULL);
}
/* Nonce function corner cases. */
@@ -2116,7 +4244,7 @@ void test_ecdsa_edge_cases(void) {
CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, nonce_function_rfc6979, extra) == 1);
CHECK(!is_empty_signature(&sig2));
CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0);
- /* The default nonce function is determinstic. */
+ /* The default nonce function is deterministic. */
CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1);
CHECK(!is_empty_signature(&sig2));
CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0);
@@ -2147,6 +4275,34 @@ void test_ecdsa_edge_cases(void) {
key[0] = 0;
}
+ {
+ /* Check that optional nonce arguments do not have equivalent effect. */
+ const unsigned char zeros[32] = {0};
+ unsigned char nonce[32];
+ unsigned char nonce2[32];
+ unsigned char nonce3[32];
+ unsigned char nonce4[32];
+ VG_UNDEF(nonce,32);
+ VG_UNDEF(nonce2,32);
+ VG_UNDEF(nonce3,32);
+ VG_UNDEF(nonce4,32);
+ CHECK(nonce_function_rfc6979(nonce, zeros, zeros, NULL, NULL, 0) == 1);
+ VG_CHECK(nonce,32);
+ CHECK(nonce_function_rfc6979(nonce2, zeros, zeros, zeros, NULL, 0) == 1);
+ VG_CHECK(nonce2,32);
+ CHECK(nonce_function_rfc6979(nonce3, zeros, zeros, NULL, (void *)zeros, 0) == 1);
+ VG_CHECK(nonce3,32);
+ CHECK(nonce_function_rfc6979(nonce4, zeros, zeros, zeros, (void *)zeros, 0) == 1);
+ VG_CHECK(nonce4,32);
+ CHECK(memcmp(nonce, nonce2, 32) != 0);
+ CHECK(memcmp(nonce, nonce3, 32) != 0);
+ CHECK(memcmp(nonce, nonce4, 32) != 0);
+ CHECK(memcmp(nonce2, nonce3, 32) != 0);
+ CHECK(memcmp(nonce2, nonce4, 32) != 0);
+ CHECK(memcmp(nonce3, nonce4, 32) != 0);
+ }
+
+
/* Privkey export where pubkey is the point at infinity. */
{
unsigned char privkey[300];
@@ -2157,9 +4313,9 @@ void test_ecdsa_edge_cases(void) {
0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41,
};
size_t outlen = 300;
- CHECK(!secp256k1_ec_privkey_export(ctx, privkey, &outlen, seckey, 0));
+ CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 0));
outlen = 300;
- CHECK(!secp256k1_ec_privkey_export(ctx, privkey, &outlen, seckey, SECP256K1_EC_COMPRESSED));
+ CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 1));
}
}
@@ -2168,13 +4324,13 @@ void run_ecdsa_edge_cases(void) {
}
#ifdef ENABLE_OPENSSL_TESTS
-EC_KEY *get_openssl_key(const secp256k1_scalar *key) {
+EC_KEY *get_openssl_key(const unsigned char *key32) {
unsigned char privkey[300];
size_t privkeylen;
const unsigned char* pbegin = privkey;
- int compr = secp256k1_rand32() & 1;
+ int compr = secp256k1_rand_bits(1);
EC_KEY *ec_key = EC_KEY_new_by_curve_name(NID_secp256k1);
- CHECK(secp256k1_eckey_privkey_serialize(&ctx->ecmult_gen_ctx, privkey, &privkeylen, key, compr ? SECP256K1_EC_COMPRESSED : 0));
+ CHECK(ec_privkey_export_der(ctx, privkey, &privkeylen, key32, compr));
CHECK(d2i_ECPrivateKey(&ec_key, &pbegin, privkeylen));
CHECK(EC_KEY_check_key(ec_key));
return ec_key;
@@ -2192,12 +4348,14 @@ void test_ecdsa_openssl(void) {
size_t secp_sigsize = 80;
unsigned char message[32];
unsigned char signature[80];
+ unsigned char key32[32];
secp256k1_rand256_test(message);
secp256k1_scalar_set_b32(&msg, message, NULL);
random_scalar_order_test(&key);
+ secp256k1_scalar_get_b32(key32, &key);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &qj, &key);
secp256k1_ge_set_gej(&q, &qj);
- ec_key = get_openssl_key(&key);
+ ec_key = get_openssl_key(key32);
CHECK(ec_key != NULL);
CHECK(ECDSA_sign(0, message, sizeof(message), signature, &sigsize, ec_key));
CHECK(secp256k1_ecdsa_sig_parse(&sigr, &sigs, signature, sigsize));
@@ -2278,12 +4436,14 @@ int main(int argc, char **argv) {
/* initialize */
run_context_tests();
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
-
- if (secp256k1_rand32() & 1) {
+ if (secp256k1_rand_bits(1)) {
secp256k1_rand256(run32);
- CHECK(secp256k1_context_randomize(ctx, (secp256k1_rand32() & 1) ? run32 : NULL));
+ CHECK(secp256k1_context_randomize(ctx, secp256k1_rand_bits(1) ? run32 : NULL));
}
+ run_rand_bits();
+ run_rand_int();
+
run_sha256_tests();
run_hmac_sha256_tests();
run_rfc6979_hmac_sha256_tests();
@@ -2307,6 +4467,7 @@ int main(int argc, char **argv) {
/* group tests */
run_ge();
+ run_group_decompress();
/* ecmult tests */
run_wnaf();
@@ -2322,6 +4483,12 @@ int main(int argc, char **argv) {
run_endomorphism_tests();
#endif
+ /* EC point parser test */
+ run_ec_pubkey_parse_test();
+
+ /* EC key edge cases */
+ run_eckey_edge_case_test();
+
#ifdef ENABLE_MODULE_ECDH
/* ecdh tests */
run_ecdh_tests();
@@ -2329,6 +4496,7 @@ int main(int argc, char **argv) {
/* ecdsa tests */
run_random_pubkeys();
+ run_ecdsa_der_parse();
run_ecdsa_sign_verify();
run_ecdsa_end_to_end();
run_ecdsa_edge_cases();
diff --git a/crypto/secp256k1/libsecp256k1/src/tests_exhaustive.c b/crypto/secp256k1/libsecp256k1/src/tests_exhaustive.c
new file mode 100644
index 0000000000..b040bb0733
--- /dev/null
+++ b/crypto/secp256k1/libsecp256k1/src/tests_exhaustive.c
@@ -0,0 +1,470 @@
+/***********************************************************************
+ * Copyright (c) 2016 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#if defined HAVE_CONFIG_H
+#include "libsecp256k1-config.h"
+#endif
+
+#include
+#include
+
+#include
+
+#undef USE_ECMULT_STATIC_PRECOMPUTATION
+
+#ifndef EXHAUSTIVE_TEST_ORDER
+/* see group_impl.h for allowable values */
+#define EXHAUSTIVE_TEST_ORDER 13
+#define EXHAUSTIVE_TEST_LAMBDA 9 /* cube root of 1 mod 13 */
+#endif
+
+#include "include/secp256k1.h"
+#include "group.h"
+#include "secp256k1.c"
+#include "testrand_impl.h"
+
+#ifdef ENABLE_MODULE_RECOVERY
+#include "src/modules/recovery/main_impl.h"
+#include "include/secp256k1_recovery.h"
+#endif
+
+/** stolen from tests.c */
+void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) {
+ CHECK(a->infinity == b->infinity);
+ if (a->infinity) {
+ return;
+ }
+ CHECK(secp256k1_fe_equal_var(&a->x, &b->x));
+ CHECK(secp256k1_fe_equal_var(&a->y, &b->y));
+}
+
+void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) {
+ secp256k1_fe z2s;
+ secp256k1_fe u1, u2, s1, s2;
+ CHECK(a->infinity == b->infinity);
+ if (a->infinity) {
+ return;
+ }
+ /* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */
+ secp256k1_fe_sqr(&z2s, &b->z);
+ secp256k1_fe_mul(&u1, &a->x, &z2s);
+ u2 = b->x; secp256k1_fe_normalize_weak(&u2);
+ secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z);
+ s2 = b->y; secp256k1_fe_normalize_weak(&s2);
+ CHECK(secp256k1_fe_equal_var(&u1, &u2));
+ CHECK(secp256k1_fe_equal_var(&s1, &s2));
+}
+
+void random_fe(secp256k1_fe *x) {
+ unsigned char bin[32];
+ do {
+ secp256k1_rand256(bin);
+ if (secp256k1_fe_set_b32(x, bin)) {
+ return;
+ }
+ } while(1);
+}
+/** END stolen from tests.c */
+
+int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32,
+ const unsigned char *key32, const unsigned char *algo16,
+ void *data, unsigned int attempt) {
+ secp256k1_scalar s;
+ int *idata = data;
+ (void)msg32;
+ (void)key32;
+ (void)algo16;
+ /* Some nonces cannot be used because they'd cause s and/or r to be zero.
+ * The signing function has retry logic here that just re-calls the nonce
+ * function with an increased `attempt`. So if attempt > 0 this means we
+ * need to change the nonce to avoid an infinite loop. */
+ if (attempt > 0) {
+ *idata = (*idata + 1) % EXHAUSTIVE_TEST_ORDER;
+ }
+ secp256k1_scalar_set_int(&s, *idata);
+ secp256k1_scalar_get_b32(nonce32, &s);
+ return 1;
+}
+
+#ifdef USE_ENDOMORPHISM
+void test_exhaustive_endomorphism(const secp256k1_ge *group, int order) {
+ int i;
+ for (i = 0; i < order; i++) {
+ secp256k1_ge res;
+ secp256k1_ge_mul_lambda(&res, &group[i]);
+ ge_equals_ge(&group[i * EXHAUSTIVE_TEST_LAMBDA % EXHAUSTIVE_TEST_ORDER], &res);
+ }
+}
+#endif
+
+void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj, int order) {
+ int i, j;
+
+ /* Sanity-check (and check infinity functions) */
+ CHECK(secp256k1_ge_is_infinity(&group[0]));
+ CHECK(secp256k1_gej_is_infinity(&groupj[0]));
+ for (i = 1; i < order; i++) {
+ CHECK(!secp256k1_ge_is_infinity(&group[i]));
+ CHECK(!secp256k1_gej_is_infinity(&groupj[i]));
+ }
+
+ /* Check all addition formulae */
+ for (j = 0; j < order; j++) {
+ secp256k1_fe fe_inv;
+ secp256k1_fe_inv(&fe_inv, &groupj[j].z);
+ for (i = 0; i < order; i++) {
+ secp256k1_ge zless_gej;
+ secp256k1_gej tmp;
+ /* add_var */
+ secp256k1_gej_add_var(&tmp, &groupj[i], &groupj[j], NULL);
+ ge_equals_gej(&group[(i + j) % order], &tmp);
+ /* add_ge */
+ if (j > 0) {
+ secp256k1_gej_add_ge(&tmp, &groupj[i], &group[j]);
+ ge_equals_gej(&group[(i + j) % order], &tmp);
+ }
+ /* add_ge_var */
+ secp256k1_gej_add_ge_var(&tmp, &groupj[i], &group[j], NULL);
+ ge_equals_gej(&group[(i + j) % order], &tmp);
+ /* add_zinv_var */
+ zless_gej.infinity = groupj[j].infinity;
+ zless_gej.x = groupj[j].x;
+ zless_gej.y = groupj[j].y;
+ secp256k1_gej_add_zinv_var(&tmp, &groupj[i], &zless_gej, &fe_inv);
+ ge_equals_gej(&group[(i + j) % order], &tmp);
+ }
+ }
+
+ /* Check doubling */
+ for (i = 0; i < order; i++) {
+ secp256k1_gej tmp;
+ if (i > 0) {
+ secp256k1_gej_double_nonzero(&tmp, &groupj[i], NULL);
+ ge_equals_gej(&group[(2 * i) % order], &tmp);
+ }
+ secp256k1_gej_double_var(&tmp, &groupj[i], NULL);
+ ge_equals_gej(&group[(2 * i) % order], &tmp);
+ }
+
+ /* Check negation */
+ for (i = 1; i < order; i++) {
+ secp256k1_ge tmp;
+ secp256k1_gej tmpj;
+ secp256k1_ge_neg(&tmp, &group[i]);
+ ge_equals_ge(&group[order - i], &tmp);
+ secp256k1_gej_neg(&tmpj, &groupj[i]);
+ ge_equals_gej(&group[order - i], &tmpj);
+ }
+}
+
+void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *group, const secp256k1_gej *groupj, int order) {
+ int i, j, r_log;
+ for (r_log = 1; r_log < order; r_log++) {
+ for (j = 0; j < order; j++) {
+ for (i = 0; i < order; i++) {
+ secp256k1_gej tmp;
+ secp256k1_scalar na, ng;
+ secp256k1_scalar_set_int(&na, i);
+ secp256k1_scalar_set_int(&ng, j);
+
+ secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &groupj[r_log], &na, &ng);
+ ge_equals_gej(&group[(i * r_log + j) % order], &tmp);
+
+ if (i > 0) {
+ secp256k1_ecmult_const(&tmp, &group[i], &ng);
+ ge_equals_gej(&group[(i * j) % order], &tmp);
+ }
+ }
+ }
+ }
+}
+
+void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) {
+ secp256k1_fe x;
+ unsigned char x_bin[32];
+ k %= EXHAUSTIVE_TEST_ORDER;
+ x = group[k].x;
+ secp256k1_fe_normalize(&x);
+ secp256k1_fe_get_b32(x_bin, &x);
+ secp256k1_scalar_set_b32(r, x_bin, NULL);
+}
+
+void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
+ int s, r, msg, key;
+ for (s = 1; s < order; s++) {
+ for (r = 1; r < order; r++) {
+ for (msg = 1; msg < order; msg++) {
+ for (key = 1; key < order; key++) {
+ secp256k1_ge nonconst_ge;
+ secp256k1_ecdsa_signature sig;
+ secp256k1_pubkey pk;
+ secp256k1_scalar sk_s, msg_s, r_s, s_s;
+ secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s;
+ int k, should_verify;
+ unsigned char msg32[32];
+
+ secp256k1_scalar_set_int(&s_s, s);
+ secp256k1_scalar_set_int(&r_s, r);
+ secp256k1_scalar_set_int(&msg_s, msg);
+ secp256k1_scalar_set_int(&sk_s, key);
+
+ /* Verify by hand */
+ /* Run through every k value that gives us this r and check that *one* works.
+ * Note there could be none, there could be multiple, ECDSA is weird. */
+ should_verify = 0;
+ for (k = 0; k < order; k++) {
+ secp256k1_scalar check_x_s;
+ r_from_k(&check_x_s, group, k);
+ if (r_s == check_x_s) {
+ secp256k1_scalar_set_int(&s_times_k_s, k);
+ secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
+ secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s);
+ secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s);
+ should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s);
+ }
+ }
+ /* nb we have a "high s" rule */
+ should_verify &= !secp256k1_scalar_is_high(&s_s);
+
+ /* Verify by calling verify */
+ secp256k1_ecdsa_signature_save(&sig, &r_s, &s_s);
+ memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge));
+ secp256k1_pubkey_save(&pk, &nonconst_ge);
+ secp256k1_scalar_get_b32(msg32, &msg_s);
+ CHECK(should_verify ==
+ secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk));
+ }
+ }
+ }
+ }
+}
+
+void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
+ int i, j, k;
+
+ /* Loop */
+ for (i = 1; i < order; i++) { /* message */
+ for (j = 1; j < order; j++) { /* key */
+ for (k = 1; k < order; k++) { /* nonce */
+ const int starting_k = k;
+ secp256k1_ecdsa_signature sig;
+ secp256k1_scalar sk, msg, r, s, expected_r;
+ unsigned char sk32[32], msg32[32];
+ secp256k1_scalar_set_int(&msg, i);
+ secp256k1_scalar_set_int(&sk, j);
+ secp256k1_scalar_get_b32(sk32, &sk);
+ secp256k1_scalar_get_b32(msg32, &msg);
+
+ secp256k1_ecdsa_sign(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
+
+ secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
+ /* Note that we compute expected_r *after* signing -- this is important
+ * because our nonce-computing function function might change k during
+ * signing. */
+ r_from_k(&expected_r, group, k);
+ CHECK(r == expected_r);
+ CHECK((k * s) % order == (i + r * j) % order ||
+ (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
+
+ /* Overflow means we've tried every possible nonce */
+ if (k < starting_k) {
+ break;
+ }
+ }
+ }
+ }
+
+ /* We would like to verify zero-knowledge here by counting how often every
+ * possible (s, r) tuple appears, but because the group order is larger
+ * than the field order, when coercing the x-values to scalar values, some
+ * appear more often than others, so we are actually not zero-knowledge.
+ * (This effect also appears in the real code, but the difference is on the
+ * order of 1/2^128th the field order, so the deviation is not useful to a
+ * computationally bounded attacker.)
+ */
+}
+
+#ifdef ENABLE_MODULE_RECOVERY
+void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
+ int i, j, k;
+
+ /* Loop */
+ for (i = 1; i < order; i++) { /* message */
+ for (j = 1; j < order; j++) { /* key */
+ for (k = 1; k < order; k++) { /* nonce */
+ const int starting_k = k;
+ secp256k1_fe r_dot_y_normalized;
+ secp256k1_ecdsa_recoverable_signature rsig;
+ secp256k1_ecdsa_signature sig;
+ secp256k1_scalar sk, msg, r, s, expected_r;
+ unsigned char sk32[32], msg32[32];
+ int expected_recid;
+ int recid;
+ secp256k1_scalar_set_int(&msg, i);
+ secp256k1_scalar_set_int(&sk, j);
+ secp256k1_scalar_get_b32(sk32, &sk);
+ secp256k1_scalar_get_b32(msg32, &msg);
+
+ secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
+
+ /* Check directly */
+ secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig);
+ r_from_k(&expected_r, group, k);
+ CHECK(r == expected_r);
+ CHECK((k * s) % order == (i + r * j) % order ||
+ (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
+ /* In computing the recid, there is an overflow condition that is disabled in
+ * scalar_low_impl.h `secp256k1_scalar_set_b32` because almost every r.y value
+ * will exceed the group order, and our signing code always holds out for r
+ * values that don't overflow, so with a proper overflow check the tests would
+ * loop indefinitely. */
+ r_dot_y_normalized = group[k].y;
+ secp256k1_fe_normalize(&r_dot_y_normalized);
+ /* Also the recovery id is flipped depending if we hit the low-s branch */
+ if ((k * s) % order == (i + r * j) % order) {
+ expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 1 : 0;
+ } else {
+ expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 0 : 1;
+ }
+ CHECK(recid == expected_recid);
+
+ /* Convert to a standard sig then check */
+ secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
+ secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
+ /* Note that we compute expected_r *after* signing -- this is important
+ * because our nonce-computing function function might change k during
+ * signing. */
+ r_from_k(&expected_r, group, k);
+ CHECK(r == expected_r);
+ CHECK((k * s) % order == (i + r * j) % order ||
+ (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
+
+ /* Overflow means we've tried every possible nonce */
+ if (k < starting_k) {
+ break;
+ }
+ }
+ }
+ }
+}
+
+void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
+ /* This is essentially a copy of test_exhaustive_verify, with recovery added */
+ int s, r, msg, key;
+ for (s = 1; s < order; s++) {
+ for (r = 1; r < order; r++) {
+ for (msg = 1; msg < order; msg++) {
+ for (key = 1; key < order; key++) {
+ secp256k1_ge nonconst_ge;
+ secp256k1_ecdsa_recoverable_signature rsig;
+ secp256k1_ecdsa_signature sig;
+ secp256k1_pubkey pk;
+ secp256k1_scalar sk_s, msg_s, r_s, s_s;
+ secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s;
+ int recid = 0;
+ int k, should_verify;
+ unsigned char msg32[32];
+
+ secp256k1_scalar_set_int(&s_s, s);
+ secp256k1_scalar_set_int(&r_s, r);
+ secp256k1_scalar_set_int(&msg_s, msg);
+ secp256k1_scalar_set_int(&sk_s, key);
+ secp256k1_scalar_get_b32(msg32, &msg_s);
+
+ /* Verify by hand */
+ /* Run through every k value that gives us this r and check that *one* works.
+ * Note there could be none, there could be multiple, ECDSA is weird. */
+ should_verify = 0;
+ for (k = 0; k < order; k++) {
+ secp256k1_scalar check_x_s;
+ r_from_k(&check_x_s, group, k);
+ if (r_s == check_x_s) {
+ secp256k1_scalar_set_int(&s_times_k_s, k);
+ secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
+ secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s);
+ secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s);
+ should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s);
+ }
+ }
+ /* nb we have a "high s" rule */
+ should_verify &= !secp256k1_scalar_is_high(&s_s);
+
+ /* We would like to try recovering the pubkey and checking that it matches,
+ * but pubkey recovery is impossible in the exhaustive tests (the reason
+ * being that there are 12 nonzero r values, 12 nonzero points, and no
+ * overlap between the sets, so there are no valid signatures). */
+
+ /* Verify by converting to a standard signature and calling verify */
+ secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid);
+ secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
+ memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge));
+ secp256k1_pubkey_save(&pk, &nonconst_ge);
+ CHECK(should_verify ==
+ secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk));
+ }
+ }
+ }
+ }
+}
+#endif
+
+int main(void) {
+ int i;
+ secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER];
+ secp256k1_ge group[EXHAUSTIVE_TEST_ORDER];
+
+ /* Build context */
+ secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+
+ /* TODO set z = 1, then do num_tests runs with random z values */
+
+ /* Generate the entire group */
+ secp256k1_gej_set_infinity(&groupj[0]);
+ secp256k1_ge_set_gej(&group[0], &groupj[0]);
+ for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
+ /* Set a different random z-value for each Jacobian point */
+ secp256k1_fe z;
+ random_fe(&z);
+
+ secp256k1_gej_add_ge(&groupj[i], &groupj[i - 1], &secp256k1_ge_const_g);
+ secp256k1_ge_set_gej(&group[i], &groupj[i]);
+ secp256k1_gej_rescale(&groupj[i], &z);
+
+ /* Verify against ecmult_gen */
+ {
+ secp256k1_scalar scalar_i;
+ secp256k1_gej generatedj;
+ secp256k1_ge generated;
+
+ secp256k1_scalar_set_int(&scalar_i, i);
+ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &generatedj, &scalar_i);
+ secp256k1_ge_set_gej(&generated, &generatedj);
+
+ CHECK(group[i].infinity == 0);
+ CHECK(generated.infinity == 0);
+ CHECK(secp256k1_fe_equal_var(&generated.x, &group[i].x));
+ CHECK(secp256k1_fe_equal_var(&generated.y, &group[i].y));
+ }
+ }
+
+ /* Run the tests */
+#ifdef USE_ENDOMORPHISM
+ test_exhaustive_endomorphism(group, EXHAUSTIVE_TEST_ORDER);
+#endif
+ test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER);
+ test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER);
+ test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER);
+ test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER);
+
+#ifdef ENABLE_MODULE_RECOVERY
+ test_exhaustive_recovery_sign(ctx, group, EXHAUSTIVE_TEST_ORDER);
+ test_exhaustive_recovery_verify(ctx, group, EXHAUSTIVE_TEST_ORDER);
+#endif
+
+ secp256k1_context_destroy(ctx);
+ return 0;
+}
+
diff --git a/crypto/secp256k1/libsecp256k1/src/util.h b/crypto/secp256k1/libsecp256k1/src/util.h
index 4eef4ded47..4092a86c91 100644
--- a/crypto/secp256k1/libsecp256k1/src/util.h
+++ b/crypto/secp256k1/libsecp256k1/src/util.h
@@ -57,7 +57,10 @@ static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback *
#endif
/* Like assert(), but when VERIFY is defined, and side-effect safe. */
-#ifdef VERIFY
+#if defined(COVERAGE)
+#define VERIFY_CHECK(check)
+#define VERIFY_SETUP(stmt)
+#elif defined(VERIFY)
#define VERIFY_CHECK CHECK
#define VERIFY_SETUP(stmt) do { stmt; } while(0)
#else
diff --git a/crypto/secp256k1/notes.go b/crypto/secp256k1/notes.go
deleted file mode 100644
index 93e6d1902f..0000000000
--- a/crypto/secp256k1/notes.go
+++ /dev/null
@@ -1,208 +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 secp256k1
-
-/*
- sipa, int secp256k1_ecdsa_pubkey_create(unsigned char *pubkey, int *pubkeylen, const unsigned char *seckey, int compressed);
- is that how i generate private/public keys?
- HaltingState: you pass in a random 32-byte string as seckey
- HaltingState: if it is valid, the corresponding pubkey is put in pubkey
- and true is returned
- otherwise, false is returned
- around 1 in 2^128 32-byte strings are invalid, so the odds of even ever seeing one is extremely rare
-
- private keys are mathematically numbers
- each has a corresponding point on the curve as public key
- a private key is just a number
- a public key is a point with x/y coordinates
- almost every 256-bit number is a valid private key (one with a point on the curve corresponding to it)
- HaltingState: ok?
-
- more than half of random points are not on the curve
- and actually, it is less than the square root, not less than half, sorry :)
-!!!
- a private key is a NUMBER
- a public key is a POINT
- half the x,y values in the field are not on the curve, a private key is an integer.
-
- HaltingState: yes, n,q = private keys; N,Q = corresponding public keys (N=n*G, Q=q*G); then it follows that n*Q = n*q*G = q*n*G = q*N
- that's the reason ECDH works
- multiplication is associative and commutativ
-*/
-
-/*
- sipa, ok; i am doing compact signatures and I want to know; can someone change the signature to get another valid signature for same message without the private key
- because i know they can do that for the normal 72 byte signatures that openssl was putting out
- HaltingState: if you don't enforce non-malleability, yes
- HaltingState: if you force the highest bit of t
-
- it _creates_ signatures that already satisfy that condition
- but it will accept ones that don't
- maybe i should change that, and be strict
- yes; i want some way to know signature is valid but fails malleability
- well if the highest bit of S is 1, you can take its complement
- and end up with a valid signature
- that is canonical
-*/
-
-/*
-
- sipa, I am signing messages and highest bit of the compact signature is 1!!!
- if (b & 0x80) == 0x80 {
- log.Panic("b= %v b2= %v \n", b, b&0x80)
- }
- what bit?
-* Pengoo has quit (Ping timeout: 272 seconds)
- the highest bit of the first byte of signature
- it's the highest bit of S
- so the 32nd byte
- wtf
-
-*/
-
-/*
- For instance, nonces are used in HTTP digest access authentication to calculate an MD5 digest
- of the password. The nonces are different each time the 401 authentication challenge
- response code is presented, thus making replay attacks virtually impossible.
-
-can verify client/server match without sending password over network
-*/
-
-/*
- one thing I dont get about armory for instance,
-is how the hot-wallet can generate new addresses without
-knowing the master key
-*/
-
-/*
- i am yelling at the telehash people for using secp256r1
-instead of secp256k1; they thing r1 is "more secure" despite fact that
-there is no implementation that works and wrapping it is now taking
-up massive time, lol
- ...
-
- You know that the *r curves are selected via an undisclosed
-secret process, right?
- HaltingState: telehash is offtopic for this channel.
-*/
-/*
- For instance, nonces are used in HTTP digest access authentication to calculate an MD5 digest
- of the password. The nonces are different each time the 401 authentication challenge
- response code is presented, thus making replay attacks virtually impossible.
-
-can verify client/server match without sending password over network
-*/
-
-/*
-void secp256k1_start(void);
-void secp256k1_stop(void);
-
- * Verify an ECDSA signature.
- * Returns: 1: correct signature
- * 0: incorrect signature
- * -1: invalid public key
- * -2: invalid signature
- *
-int secp256k1_ecdsa_verify(const unsigned char *msg, int msglen,
- const unsigned char *sig, int siglen,
- const unsigned char *pubkey, int pubkeylen);
-
-http://www.nilsschneider.net/2013/01/28/recovering-bitcoin-private-keys.html
-
-Why did this work? ECDSA requires a random number for each signature. If this random
-number is ever used twice with the same private key it can be recovered.
-This transaction was generated by a hardware bitcoin wallet using a pseudo-random number
-generator that was returning the same “random” number every time.
-
-Nonce is 32 bytes?
-
- * Create an ECDSA signature.
- * Returns: 1: signature created
- * 0: nonce invalid, try another one
- * In: msg: the message being signed
- * msglen: the length of the message being signed
- * seckey: pointer to a 32-byte secret key (assumed to be valid)
- * nonce: pointer to a 32-byte nonce (generated with a cryptographic PRNG)
- * Out: sig: pointer to a 72-byte array where the signature will be placed.
- * siglen: pointer to an int, which will be updated to the signature length (<=72).
- *
-int secp256k1_ecdsa_sign(const unsigned char *msg, int msglen,
- unsigned char *sig, int *siglen,
- const unsigned char *seckey,
- const unsigned char *nonce);
-
-
- * Create a compact ECDSA signature (64 byte + recovery id).
- * Returns: 1: signature created
- * 0: nonce invalid, try another one
- * In: msg: the message being signed
- * msglen: the length of the message being signed
- * seckey: pointer to a 32-byte secret key (assumed to be valid)
- * nonce: pointer to a 32-byte nonce (generated with a cryptographic PRNG)
- * Out: sig: pointer to a 64-byte array where the signature will be placed.
- * recid: pointer to an int, which will be updated to contain the recovery id.
- *
-int secp256k1_ecdsa_sign_compact(const unsigned char *msg, int msglen,
- unsigned char *sig64,
- const unsigned char *seckey,
- const unsigned char *nonce,
- int *recid);
-
- * Recover an ECDSA public key from a compact signature.
- * Returns: 1: public key succesfully recovered (which guarantees a correct signature).
- * 0: otherwise.
- * In: msg: the message assumed to be signed
- * msglen: the length of the message
- * compressed: whether to recover a compressed or uncompressed pubkey
- * recid: the recovery id (as returned by ecdsa_sign_compact)
- * Out: pubkey: pointer to a 33 or 65 byte array to put the pubkey.
- * pubkeylen: pointer to an int that will contain the pubkey length.
- *
-
-recovery id is between 0 and 3
-
-int secp256k1_ecdsa_recover_compact(const unsigned char *msg, int msglen,
- const unsigned char *sig64,
- unsigned char *pubkey, int *pubkeylen,
- int compressed, int recid);
-
-
- * Verify an ECDSA secret key.
- * Returns: 1: secret key is valid
- * 0: secret key is invalid
- * In: seckey: pointer to a 32-byte secret key
- *
-int secp256k1_ecdsa_seckey_verify(const unsigned char *seckey);
-
-** Just validate a public key.
- * Returns: 1: valid public key
- * 0: invalid public key
- *
-int secp256k1_ecdsa_pubkey_verify(const unsigned char *pubkey, int pubkeylen);
-
-** Compute the public key for a secret key.
- * In: compressed: whether the computed public key should be compressed
- * seckey: pointer to a 32-byte private key.
- * Out: pubkey: pointer to a 33-byte (if compressed) or 65-byte (if uncompressed)
- * area to store the public key.
- * pubkeylen: pointer to int that will be updated to contains the pubkey's
- * length.
- * Returns: 1: secret was valid, public key stores
- * 0: secret was invalid, try again.
- *
-int secp256k1_ecdsa_pubkey_create(unsigned char *pubkey, int *pubkeylen, const unsigned char *seckey, int compressed);
-*/
diff --git a/crypto/secp256k1/pubkey_scalar_mul.h b/crypto/secp256k1/pubkey_scalar_mul.h
deleted file mode 100644
index 0511545ec0..0000000000
--- a/crypto/secp256k1/pubkey_scalar_mul.h
+++ /dev/null
@@ -1,56 +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 .
-
-/** Multiply point by scalar in constant time.
- * Returns: 1: multiplication was successful
- * 0: scalar was invalid (zero or overflow)
- * Args: ctx: pointer to a context object (cannot be NULL)
- * Out: point: the multiplied point (usually secret)
- * In: point: pointer to a 64-byte bytepublic point,
- encoded as two 256bit big-endian numbers.
- * scalar: a 32-byte scalar with which to multiply the point
- */
-int secp256k1_pubkey_scalar_mul(const secp256k1_context* ctx, unsigned char *point, const unsigned char *scalar) {
- int ret = 0;
- int overflow = 0;
- secp256k1_fe feX, feY;
- secp256k1_gej res;
- secp256k1_ge ge;
- secp256k1_scalar s;
- ARG_CHECK(point != NULL);
- ARG_CHECK(scalar != NULL);
- (void)ctx;
-
- secp256k1_fe_set_b32(&feX, point);
- secp256k1_fe_set_b32(&feY, point+32);
- secp256k1_ge_set_xy(&ge, &feX, &feY);
- secp256k1_scalar_set_b32(&s, scalar, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&s)) {
- ret = 0;
- } else {
- secp256k1_ecmult_const(&res, &ge, &s);
- secp256k1_ge_set_gej(&ge, &res);
- /* Note: can't use secp256k1_pubkey_save here because it is not constant time. */
- secp256k1_fe_normalize(&ge.x);
- secp256k1_fe_normalize(&ge.y);
- secp256k1_fe_get_b32(point, &ge.x);
- secp256k1_fe_get_b32(point+32, &ge.y);
- ret = 1;
- }
- secp256k1_scalar_clear(&s);
- return ret;
-}
-
diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go
index ebf92c5072..1d24960cb7 100644
--- a/crypto/secp256k1/secp256.go
+++ b/crypto/secp256k1/secp256.go
@@ -14,10 +14,9 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
+// Package secp256k1 wraps the bitcoin secp256k1 C library.
package secp256k1
-// TODO: set USE_SCALAR_4X64 depending on platform?
-
/*
#cgo CFLAGS: -I./libsecp256k1
#cgo CFLAGS: -I./libsecp256k1/src/
@@ -29,7 +28,7 @@ package secp256k1
#define NDEBUG
#include "./libsecp256k1/src/secp256k1.c"
#include "./libsecp256k1/src/modules/recovery/main_impl.h"
-#include "pubkey_scalar_mul.h"
+#include "ext.h"
typedef void (*callbackFunc) (const char* msg, void* data);
extern void secp256k1GoPanicIllegal(const char* msg, void* data);
@@ -45,16 +44,6 @@ import (
"github.com/ubiq/go-ubiq/crypto/randentropy"
)
-//#define USE_FIELD_5X64
-
-/*
- TODO:
- > store private keys in buffer and shuffle (deters persistance on swap disc)
- > byte permutation (changing)
- > xor with chaning random block (to deter scanning memory for 0x63) (stream cipher?)
-*/
-
-// holds ptr to secp256k1_context_struct (see secp256k1/include/secp256k1.h)
var (
context *C.secp256k1_context
N *big.Int
@@ -67,127 +56,57 @@ func init() {
HalfN, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", 16)
// around 20 ms on a modern CPU.
- context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
+ context = C.secp256k1_context_create_sign_verify()
C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil)
C.secp256k1_context_set_error_callback(context, C.callbackFunc(C.secp256k1GoPanicError), nil)
}
var (
- ErrInvalidMsgLen = errors.New("invalid message length for signature recovery")
+ ErrInvalidMsgLen = errors.New("invalid message length, need 32 bytes")
ErrInvalidSignatureLen = errors.New("invalid signature length")
ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
+ ErrInvalidKey = errors.New("invalid private key")
+ ErrSignFailed = errors.New("signing failed")
+ ErrRecoverFailed = errors.New("recovery failed")
)
-func GenerateKeyPair() ([]byte, []byte) {
- var seckey []byte = randentropy.GetEntropyCSPRNG(32)
- var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
- var pubkey64 []byte = make([]byte, 64) // secp256k1_pubkey
- var pubkey65 []byte = make([]byte, 65) // 65 byte uncompressed pubkey
- pubkey64_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey64[0]))
- pubkey65_ptr := (*C.uchar)(unsafe.Pointer(&pubkey65[0]))
-
- ret := C.secp256k1_ec_pubkey_create(
- context,
- pubkey64_ptr,
- seckey_ptr,
- )
-
- if ret != C.int(1) {
- return GenerateKeyPair() // invalid secret, try again
- }
-
- var output_len C.size_t
-
- C.secp256k1_ec_pubkey_serialize( // always returns 1
- context,
- pubkey65_ptr,
- &output_len,
- pubkey64_ptr,
- 0, // SECP256K1_EC_COMPRESSED
- )
-
- return pubkey65, seckey
-}
-
-func GeneratePubKey(seckey []byte) ([]byte, error) {
- if err := VerifySeckeyValidity(seckey); err != nil {
- return nil, err
- }
-
- var pubkey []byte = make([]byte, 64)
- var pubkey_ptr *C.secp256k1_pubkey = (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0]))
-
- var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
-
- ret := C.secp256k1_ec_pubkey_create(
- context,
- pubkey_ptr,
- seckey_ptr,
- )
-
- if ret != C.int(1) {
- return nil, errors.New("Unable to generate pubkey from seckey")
- }
-
- return pubkey, nil
-}
-
+// Sign creates a recoverable ECDSA signature.
+// The produced signature is in the 65-byte [R || S || V] format where V is 0 or 1.
+//
+// The caller is responsible for ensuring that msg cannot be chosen
+// directly by an attacker. It is usually preferable to use a cryptographic
+// hash function on any input before handing it to this function.
func Sign(msg []byte, seckey []byte) ([]byte, error) {
- msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
- seckey_ptr := (*C.uchar)(unsafe.Pointer(&seckey[0]))
-
- sig := make([]byte, 65)
- sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&sig[0]))
-
- nonce := randentropy.GetEntropyCSPRNG(32)
- ndata_ptr := unsafe.Pointer(&nonce[0])
-
- noncefp_ptr := &(*C.secp256k1_nonce_function_default)
-
- if C.secp256k1_ec_seckey_verify(context, seckey_ptr) != C.int(1) {
- return nil, errors.New("Invalid secret key")
+ if len(msg) != 32 {
+ return nil, ErrInvalidMsgLen
}
-
- ret := C.secp256k1_ecdsa_sign_recoverable(
- context,
- sig_ptr,
- msg_ptr,
- seckey_ptr,
- noncefp_ptr,
- ndata_ptr,
- )
-
- if ret == C.int(0) {
- return Sign(msg, seckey) //invalid secret, try again
- }
-
- sig_serialized := make([]byte, 65)
- sig_serialized_ptr := (*C.uchar)(unsafe.Pointer(&sig_serialized[0]))
- var recid C.int
-
- C.secp256k1_ecdsa_recoverable_signature_serialize_compact(
- context,
- sig_serialized_ptr, // 64 byte compact signature
- &recid,
- sig_ptr, // 65 byte "recoverable" signature
- )
-
- sig_serialized[64] = byte(int(recid)) // add back recid to get 65 bytes sig
-
- return sig_serialized, nil
-
-}
-
-func VerifySeckeyValidity(seckey []byte) error {
if len(seckey) != 32 {
- return errors.New("priv key is not 32 bytes")
+ return nil, ErrInvalidKey
}
- var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
- ret := C.secp256k1_ec_seckey_verify(context, seckey_ptr)
- if int(ret) != 1 {
- return errors.New("invalid seckey")
+ seckeydata := (*C.uchar)(unsafe.Pointer(&seckey[0]))
+ if C.secp256k1_ec_seckey_verify(context, seckeydata) != 1 {
+ return nil, ErrInvalidKey
}
- return nil
+
+ var (
+ msgdata = (*C.uchar)(unsafe.Pointer(&msg[0]))
+ nonce = randentropy.GetEntropyCSPRNG(32)
+ noncefunc = &(*C.secp256k1_nonce_function_default)
+ noncefuncData = unsafe.Pointer(&nonce[0])
+ sigstruct C.secp256k1_ecdsa_recoverable_signature
+ )
+ if C.secp256k1_ecdsa_sign_recoverable(context, &sigstruct, msgdata, seckeydata, noncefunc, noncefuncData) == 0 {
+ return nil, ErrSignFailed
+ }
+
+ var (
+ sig = make([]byte, 65)
+ sigdata = (*C.uchar)(unsafe.Pointer(&sig[0]))
+ recid C.int
+ )
+ C.secp256k1_ecdsa_recoverable_signature_serialize_compact(context, sigdata, &recid, &sigstruct)
+ sig[64] = byte(recid) // add back recid to get 65 bytes sig
+ return sig, nil
}
// RecoverPubkey returns the the public key of the signer.
@@ -202,49 +121,15 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
return nil, err
}
- msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
- sig_ptr := (*C.uchar)(unsafe.Pointer(&sig[0]))
- pubkey := make([]byte, 64)
- /*
- this slice is used for both the recoverable signature and the
- resulting serialized pubkey (both types in libsecp256k1 are 65
- bytes). this saves one allocation of 65 bytes, which is nice as
- pubkey recovery is one bottleneck during load in Ethereum
- */
- bytes65 := make([]byte, 65)
- pubkey_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0]))
- recoverable_sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&bytes65[0]))
- recid := C.int(sig[64])
-
- ret := C.secp256k1_ecdsa_recoverable_signature_parse_compact(
- context,
- recoverable_sig_ptr,
- sig_ptr,
- recid)
- if ret == C.int(0) {
- return nil, errors.New("Failed to parse signature")
- }
-
- ret = C.secp256k1_ecdsa_recover(
- context,
- pubkey_ptr,
- recoverable_sig_ptr,
- msg_ptr,
+ var (
+ pubkey = make([]byte, 65)
+ sigdata = (*C.uchar)(unsafe.Pointer(&sig[0]))
+ msgdata = (*C.uchar)(unsafe.Pointer(&msg[0]))
)
- if ret == C.int(0) {
- return nil, errors.New("Failed to recover public key")
+ if C.secp256k1_ecdsa_recover_pubkey(context, (*C.uchar)(unsafe.Pointer(&pubkey[0])), sigdata, msgdata) == 0 {
+ return nil, ErrRecoverFailed
}
-
- serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
- var output_len C.size_t
- C.secp256k1_ec_pubkey_serialize( // always returns 1
- context,
- serialized_pubkey_ptr,
- &output_len,
- pubkey_ptr,
- 0, // SECP256K1_EC_COMPRESSED
- )
- return bytes65, nil
+ return pubkey, nil
}
func checkSignature(sig []byte) error {
diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go
index cc87d867df..aad67f579a 100644
--- a/crypto/secp256k1/secp256_test.go
+++ b/crypto/secp256k1/secp256_test.go
@@ -18,6 +18,9 @@ package secp256k1
import (
"bytes"
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
"encoding/hex"
"testing"
@@ -26,15 +29,41 @@ import (
const TestCount = 1000
-func TestPrivkeyGenerate(t *testing.T) {
- _, seckey := GenerateKeyPair()
- if err := VerifySeckeyValidity(seckey); err != nil {
- t.Errorf("seckey not valid: %s", err)
+func generateKeyPair() (pubkey, privkey []byte) {
+ key, err := ecdsa.GenerateKey(S256(), rand.Reader)
+ if err != nil {
+ panic(err)
+ }
+ pubkey = elliptic.Marshal(S256(), key.X, key.Y)
+ privkey = make([]byte, 32)
+ readBits(privkey, key.D)
+ return pubkey, privkey
+}
+
+func randSig() []byte {
+ sig := randentropy.GetEntropyCSPRNG(65)
+ sig[32] &= 0x70
+ sig[64] %= 4
+ return sig
+}
+
+// tests for malleability
+// highest bit of signature ECDSA s value must be 0, in the 33th byte
+func compactSigCheck(t *testing.T, sig []byte) {
+ var b int = int(sig[32])
+ if b < 0 {
+ t.Errorf("highest bit is negative: %d", b)
+ }
+ if ((b >> 7) == 1) != ((b & 0x80) == 0x80) {
+ t.Errorf("highest bit: %d bit >> 7: %d", b, b>>7)
+ }
+ if (b & 0x80) == 0x80 {
+ t.Errorf("highest bit: %d bit & 0x80: %d", b, b&0x80)
}
}
func TestSignatureValidity(t *testing.T) {
- pubkey, seckey := GenerateKeyPair()
+ pubkey, seckey := generateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig, err := Sign(msg, seckey)
if err != nil {
@@ -57,7 +86,7 @@ func TestSignatureValidity(t *testing.T) {
}
func TestInvalidRecoveryID(t *testing.T) {
- _, seckey := GenerateKeyPair()
+ _, seckey := generateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
sig[64] = 99
@@ -68,7 +97,7 @@ func TestInvalidRecoveryID(t *testing.T) {
}
func TestSignAndRecover(t *testing.T) {
- pubkey1, seckey := GenerateKeyPair()
+ pubkey1, seckey := generateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig, err := Sign(msg, seckey)
if err != nil {
@@ -84,7 +113,7 @@ func TestSignAndRecover(t *testing.T) {
}
func TestRandomMessagesWithSameKey(t *testing.T) {
- pubkey, seckey := GenerateKeyPair()
+ pubkey, seckey := generateKeyPair()
keys := func() ([]byte, []byte) {
return pubkey, seckey
}
@@ -93,7 +122,7 @@ func TestRandomMessagesWithSameKey(t *testing.T) {
func TestRandomMessagesWithRandomKeys(t *testing.T) {
keys := func() ([]byte, []byte) {
- pubkey, seckey := GenerateKeyPair()
+ pubkey, seckey := generateKeyPair()
return pubkey, seckey
}
signAndRecoverWithRandomMessages(t, keys)
@@ -129,32 +158,20 @@ func signAndRecoverWithRandomMessages(t *testing.T, keys func() ([]byte, []byte)
}
func TestRecoveryOfRandomSignature(t *testing.T) {
- pubkey1, seckey := GenerateKeyPair()
+ pubkey1, _ := generateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
- sig, err := Sign(msg, seckey)
- if err != nil {
- t.Errorf("signature error: %s", err)
- }
for i := 0; i < TestCount; i++ {
- sig = randSig()
- pubkey2, _ := RecoverPubkey(msg, sig)
// recovery can sometimes work, but if so should always give wrong pubkey
+ pubkey2, _ := RecoverPubkey(msg, randSig())
if bytes.Equal(pubkey1, pubkey2) {
t.Fatalf("iteration: %d: pubkey mismatch: do NOT want %x: ", i, pubkey2)
}
}
}
-func randSig() []byte {
- sig := randentropy.GetEntropyCSPRNG(65)
- sig[32] &= 0x70
- sig[64] %= 4
- return sig
-}
-
func TestRandomMessagesAgainstValidSig(t *testing.T) {
- pubkey1, seckey := GenerateKeyPair()
+ pubkey1, seckey := generateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
@@ -168,14 +185,6 @@ func TestRandomMessagesAgainstValidSig(t *testing.T) {
}
}
-func TestZeroPrivkey(t *testing.T) {
- zeroedBytes := make([]byte, 32)
- err := VerifySeckeyValidity(zeroedBytes)
- if err == nil {
- t.Errorf("zeroed bytes should have returned error")
- }
-}
-
// Useful when the underlying libsecp256k1 API changes to quickly
// check only recover function without use of signature function
func TestRecoverSanity(t *testing.T) {
@@ -191,47 +200,23 @@ func TestRecoverSanity(t *testing.T) {
}
}
-// tests for malleability
-// highest bit of signature ECDSA s value must be 0, in the 33th byte
-func compactSigCheck(t *testing.T, sig []byte) {
- var b int = int(sig[32])
- if b < 0 {
- t.Errorf("highest bit is negative: %d", b)
- }
- if ((b >> 7) == 1) != ((b & 0x80) == 0x80) {
- t.Errorf("highest bit: %d bit >> 7: %d", b, b>>7)
- }
- if (b & 0x80) == 0x80 {
- t.Errorf("highest bit: %d bit & 0x80: %d", b, b&0x80)
- }
-}
-
-// godep go test -v -run=XXX -bench=BenchmarkSign
-// add -benchtime=10s to benchmark longer for more accurate average
-
-// to avoid compiler optimizing the benchmarked function call
-var err error
-
func BenchmarkSign(b *testing.B) {
+ _, seckey := generateKeyPair()
+ msg := randentropy.GetEntropyCSPRNG(32)
+ b.ResetTimer()
+
for i := 0; i < b.N; i++ {
- _, seckey := GenerateKeyPair()
- msg := randentropy.GetEntropyCSPRNG(32)
- b.StartTimer()
- _, e := Sign(msg, seckey)
- err = e
- b.StopTimer()
+ Sign(msg, seckey)
}
}
-//godep go test -v -run=XXX -bench=BenchmarkECRec
func BenchmarkRecover(b *testing.B) {
+ msg := randentropy.GetEntropyCSPRNG(32)
+ _, seckey := generateKeyPair()
+ sig, _ := Sign(msg, seckey)
+ b.ResetTimer()
+
for i := 0; i < b.N; i++ {
- _, seckey := GenerateKeyPair()
- msg := randentropy.GetEntropyCSPRNG(32)
- sig, _ := Sign(msg, seckey)
- b.StartTimer()
- _, e := RecoverPubkey(msg, sig)
- err = e
- b.StopTimer()
+ RecoverPubkey(msg, sig)
}
}
diff --git a/crypto/sha3/sha3_test.go b/crypto/sha3/sha3_test.go
index caf72f279f..c433761a8a 100644
--- a/crypto/sha3/sha3_test.go
+++ b/crypto/sha3/sha3_test.go
@@ -201,7 +201,7 @@ func TestSqueezing(t *testing.T) {
d1 := newShakeHash()
d1.Write([]byte(testString))
var multiple []byte
- for _ = range ref {
+ for range ref {
one := make([]byte, 1)
d1.Read(one)
multiple = append(multiple, one...)
diff --git a/errs/errors.go b/errs/errors.go
index d9ee8faeef..ab0c892cd4 100644
--- a/errs/errors.go
+++ b/errs/errors.go
@@ -19,7 +19,6 @@ package errs
import (
"fmt"
- "github.com/ubiq/go-ubiq/logger"
"github.com/ubiq/go-ubiq/logger/glog"
)
@@ -32,15 +31,10 @@ Fields:
Package:
name of the package/component
-
- Level:
- a function mapping error code to logger.LogLevel (severity)
- if not given, errors default to logger.InfoLevel
*/
type Errors struct {
Errors map[int]string
Package string
- Level func(code int) logger.LogLevel
}
/*
@@ -58,7 +52,6 @@ type Error struct {
Code int
Name string
Package string
- level logger.LogLevel
message string
format string
params []interface{}
@@ -69,15 +62,10 @@ func (self *Errors) New(code int, format string, params ...interface{}) *Error {
if !ok {
panic("invalid error code")
}
- level := logger.InfoLevel
- if self.Level != nil {
- level = self.Level(code)
- }
return &Error{
Code: code,
Name: name,
Package: self.Package,
- level: level,
format: format,
params: params,
}
@@ -98,13 +86,3 @@ func (self Error) Log(v glog.Verbose) {
v.Infoln(self)
}
}
-
-/*
-err.Fatal() is true if err's severity level is 0 or 1 (logger.ErrorLevel or logger.Silence)
-*/
-func (self *Error) Fatal() (fatal bool) {
- if self.level < logger.WarnLevel {
- fatal = true
- }
- return
-}
diff --git a/errs/errors_test.go b/errs/errors_test.go
index 2428590ed8..5a2ffbec32 100644
--- a/errs/errors_test.go
+++ b/errs/errors_test.go
@@ -19,8 +19,6 @@ package errs
import (
"fmt"
"testing"
-
- "github.com/ubiq/go-ubiq/logger"
)
func testErrors() *Errors {
@@ -30,14 +28,6 @@ func testErrors() *Errors {
0: "zero",
1: "one",
},
- Level: func(i int) (l logger.LogLevel) {
- if i == 0 {
- l = logger.ErrorLevel
- } else {
- l = logger.WarnLevel
- }
- return
- },
}
}
@@ -49,14 +39,3 @@ func TestErrorMessage(t *testing.T) {
t.Errorf("error message incorrect. expected %v, got %v", exp, message)
}
}
-
-func TestErrorSeverity(t *testing.T) {
- err0 := testErrors().New(0, "zero detail")
- if !err0.Fatal() {
- t.Errorf("error should be fatal")
- }
- err1 := testErrors().New(1, "one detail")
- if err1.Fatal() {
- t.Errorf("error should not be fatal")
- }
-}
diff --git a/eth/api.go b/eth/api.go
index b4e8fbb049..e2847405c5 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -18,6 +18,7 @@ package eth
import (
"bytes"
+ "compress/gzip"
"errors"
"fmt"
"io"
@@ -25,10 +26,12 @@ import (
"math/big"
"os"
"runtime"
+ "strings"
"time"
"github.com/ethereum/ethash"
"github.com/ubiq/go-ubiq/common"
+ "github.com/ubiq/go-ubiq/common/hexutil"
"github.com/ubiq/go-ubiq/core"
"github.com/ubiq/go-ubiq/core/state"
"github.com/ubiq/go-ubiq/core/types"
@@ -67,8 +70,8 @@ func (s *PublicEthereumAPI) Coinbase() (common.Address, error) {
}
// Hashrate returns the POW hashrate
-func (s *PublicEthereumAPI) Hashrate() *rpc.HexNumber {
- return rpc.NewHexNumber(s.e.Miner().HashRate())
+func (s *PublicEthereumAPI) Hashrate() hexutil.Uint64 {
+ return hexutil.Uint64(s.e.Miner().HashRate())
}
// PublicMinerAPI provides an API to control the miner.
@@ -80,7 +83,7 @@ type PublicMinerAPI struct {
// NewPublicMinerAPI create a new PublicMinerAPI instance.
func NewPublicMinerAPI(e *Ethereum) *PublicMinerAPI {
- agent := miner.NewRemoteAgent()
+ agent := miner.NewRemoteAgent(e.Pow())
e.Miner().Register(agent)
return &PublicMinerAPI{e, agent}
@@ -93,8 +96,8 @@ func (s *PublicMinerAPI) Mining() bool {
// SubmitWork can be used by external miner to submit their POW solution. It returns an indication if the work was
// accepted. Note, this is not an indication if the provided work was valid!
-func (s *PublicMinerAPI) SubmitWork(nonce rpc.HexNumber, solution, digest common.Hash) bool {
- return s.agent.SubmitWork(nonce.Uint64(), digest, solution)
+func (s *PublicMinerAPI) SubmitWork(nonce types.BlockNonce, solution, digest common.Hash) bool {
+ return s.agent.SubmitWork(nonce, digest, solution)
}
// GetWork returns a work package for external miner. The work package consists of 3 strings
@@ -117,8 +120,8 @@ func (s *PublicMinerAPI) GetWork() (work [3]string, err error) {
// SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined
// hash rate of all miners which submit work through this node. It accepts the miner hash rate and an identifier which
// must be unique between nodes.
-func (s *PublicMinerAPI) SubmitHashrate(hashrate rpc.HexNumber, id common.Hash) bool {
- s.agent.SubmitHashrate(id, hashrate.Uint64())
+func (s *PublicMinerAPI) SubmitHashrate(hashrate hexutil.Uint64, id common.Hash) bool {
+ s.agent.SubmitHashrate(id, uint64(hashrate))
return true
}
@@ -135,18 +138,15 @@ func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI {
// Start the miner with the given number of threads. If threads is nil the number of
// workers started is equal to the number of logical CPU's that are usable by this process.
-func (s *PrivateMinerAPI) Start(threads *rpc.HexNumber) (bool, error) {
+func (s *PrivateMinerAPI) Start(threads *int) (bool, error) {
s.e.StartAutoDAG()
-
+ var err error
if threads == nil {
- threads = rpc.NewHexNumber(runtime.NumCPU())
+ err = s.e.StartMining(runtime.NumCPU())
+ } else {
+ err = s.e.StartMining(*threads)
}
-
- err := s.e.StartMining(threads.Int())
- if err == nil {
- return true, nil
- }
- return false, err
+ return err == nil, err
}
// Stop the miner
@@ -164,8 +164,8 @@ func (s *PrivateMinerAPI) SetExtra(extra string) (bool, error) {
}
// SetGasPrice sets the minimum accepted gas price for the miner.
-func (s *PrivateMinerAPI) SetGasPrice(gasPrice rpc.HexNumber) bool {
- s.e.Miner().SetGasPrice(gasPrice.BigInt())
+func (s *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool {
+ s.e.Miner().SetGasPrice((*big.Int)(&gasPrice))
return true
}
@@ -217,8 +217,14 @@ func (api *PrivateAdminAPI) ExportChain(file string) (bool, error) {
}
defer out.Close()
+ var writer io.Writer = out
+ if strings.HasSuffix(file, ".gz") {
+ writer = gzip.NewWriter(writer)
+ defer writer.(*gzip.Writer).Close()
+ }
+
// Export the blockchain
- if err := api.eth.BlockChain().Export(out); err != nil {
+ if err := api.eth.BlockChain().Export(writer); err != nil {
return false, err
}
return true, nil
@@ -243,8 +249,15 @@ func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) {
}
defer in.Close()
+ var reader io.Reader = in
+ if strings.HasSuffix(file, ".gz") {
+ if reader, err = gzip.NewReader(reader); err != nil {
+ return false, err
+ }
+ }
+
// Run actual the import in pre-configured batches
- stream := rlp.NewStream(in, 0)
+ stream := rlp.NewStream(reader, 0)
blocks, index := make([]*types.Block, 0, 2500), 0
for batch := 0; ; batch++ {
@@ -422,7 +435,7 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, logConfig *vm.LogConf
return true, structLogger.StructLogs(), nil
}
-// callmsg is the message type used for call transations.
+// callmsg is the message type used for call transitions.
type callmsg struct {
addr common.Address
to *common.Address
@@ -515,9 +528,11 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
if err != nil {
return nil, fmt.Errorf("sender retrieval failed: %v", err)
}
+ context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain())
+
// Mutate the state if we haven't reached the tracing transaction yet
if uint64(idx) < txIndex {
- vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{})
+ vmenv := vm.NewEVM(context, stateDb, api.config, vm.Config{})
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
return nil, fmt.Errorf("mutation failed: %v", err)
@@ -525,8 +540,8 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
stateDb.DeleteSuicides()
continue
}
- // Otherwise trace the transaction and return
- vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Tracer: tracer})
+
+ vmenv := vm.NewEVM(context, stateDb, api.config, vm.Config{Debug: true, Tracer: tracer})
ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
return nil, fmt.Errorf("tracing failed: %v", err)
diff --git a/eth/api_backend.go b/eth/api_backend.go
index 22d40cd13d..89e9ce3019 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -31,7 +31,7 @@ import (
"github.com/ubiq/go-ubiq/event"
"github.com/ubiq/go-ubiq/internal/ethapi"
"github.com/ubiq/go-ubiq/params"
- rpc "github.com/ubiq/go-ubiq/rpc"
+ "github.com/ubiq/go-ubiq/rpc"
"golang.org/x/net/context"
)
@@ -106,12 +106,14 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(blockHash)
}
-func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) {
+func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.EVM, func() error, error) {
statedb := state.(EthApiState).state
from := statedb.GetOrNewStateObject(msg.From())
from.SetBalance(common.MaxBig)
vmError := func() error { return nil }
- return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}), vmError, nil
+
+ context := core.NewEVMContext(msg, header, b.eth.BlockChain())
+ return vm.NewEVM(context, statedb, b.eth.chainConfig, vm.Config{}), vmError, nil
}
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
@@ -129,15 +131,20 @@ func (b *EthApiBackend) RemoveTx(txHash common.Hash) {
b.eth.txPool.Remove(txHash)
}
-func (b *EthApiBackend) GetPoolTransactions() types.Transactions {
+func (b *EthApiBackend) GetPoolTransactions() (types.Transactions, error) {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
+ pending, err := b.eth.txPool.Pending()
+ if err != nil {
+ return nil, err
+ }
+
var txs types.Transactions
- for _, batch := range b.eth.txPool.Pending() {
+ for _, batch := range pending {
txs = append(txs, batch...)
}
- return txs
+ return txs, nil
}
func (b *EthApiBackend) GetPoolTransaction(hash common.Hash) *types.Transaction {
diff --git a/eth/backend.go b/eth/backend.go
index ec545c4f08..2b0d42cb00 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -45,6 +45,7 @@ import (
"github.com/ubiq/go-ubiq/node"
"github.com/ubiq/go-ubiq/p2p"
"github.com/ubiq/go-ubiq/params"
+ "github.com/ubiq/go-ubiq/pow"
"github.com/ubiq/go-ubiq/rpc"
)
@@ -79,6 +80,7 @@ type Config struct {
NatSpec bool
DocRoot string
AutoDAG bool
+ PowFake bool
PowTest bool
PowShared bool
ExtraData []byte
@@ -104,6 +106,7 @@ type Config struct {
type LesServer interface {
Start(srvr *p2p.Server)
+ Synced()
Stop()
Protocols() []p2p.Protocol
}
@@ -124,7 +127,7 @@ type Ethereum struct {
chainDb ethdb.Database // Block chain database
eventMux *event.TypeMux
- pow *ethash.Ethash
+ pow pow.PoW
accountManager *accounts.Manager
ApiBackend *EthApiBackend
@@ -138,13 +141,13 @@ type Ethereum struct {
solcPath string
NatSpec bool
- PowTest bool
netVersionId int
netRPCService *ethapi.PublicNetAPI
}
func (s *Ethereum) AddLesServer(ls LesServer) {
s.lesServer = ls
+ s.protocolManager.lesServer = ls
}
// New creates a new Ethereum object (including the
@@ -172,7 +175,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
stopDbUpgrade: stopDbUpgrade,
netVersionId: config.NetworkId,
NatSpec: config.NatSpec,
- PowTest: config.PowTest,
etherbase: config.Etherbase,
MinerThreads: config.MinerThreads,
AutoDAG: config.AutoDAG,
@@ -291,15 +293,17 @@ func SetupGenesisBlock(chainDb *ethdb.Database, config *Config) error {
}
// CreatePoW creates the required type of PoW instance for an Ethereum service
-func CreatePoW(config *Config) (*ethash.Ethash, error) {
+func CreatePoW(config *Config) (pow.PoW, error) {
switch {
+ case config.PowFake:
+ glog.V(logger.Info).Infof("ethash used in fake mode")
+ return pow.PoW(core.FakePow{}), nil
case config.PowTest:
glog.V(logger.Info).Infof("ethash used in test mode")
return ethash.NewForTesting()
case config.PowShared:
glog.V(logger.Info).Infof("ethash used in shared mode")
return ethash.NewShared(), nil
-
default:
return ethash.New(), nil
}
@@ -397,7 +401,7 @@ func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
-func (s *Ethereum) Pow() *ethash.Ethash { return s.pow }
+func (s *Ethereum) Pow() pow.PoW { return s.pow }
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
func (s *Ethereum) IsListening() bool { return true } // Always listening
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
diff --git a/eth/backend_test.go b/eth/backend_test.go
index 29d5efd68a..0eb82ef3fa 100644
--- a/eth/backend_test.go
+++ b/eth/backend_test.go
@@ -23,7 +23,6 @@ import (
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core"
"github.com/ubiq/go-ubiq/core/types"
- "github.com/ubiq/go-ubiq/core/vm"
"github.com/ubiq/go-ubiq/ethdb"
"github.com/ubiq/go-ubiq/params"
)
@@ -38,12 +37,12 @@ func TestMipmapUpgrade(t *testing.T) {
switch i {
case 1:
receipt := types.NewReceipt(nil, new(big.Int))
- receipt.Logs = vm.Logs{&vm.Log{Address: addr}}
+ receipt.Logs = []*types.Log{{Address: addr}}
gen.AddUncheckedReceipt(receipt)
receipts = types.Receipts{receipt}
case 2:
receipt := types.NewReceipt(nil, new(big.Int))
- receipt.Logs = vm.Logs{&vm.Log{Address: addr}}
+ receipt.Logs = []*types.Log{{Address: addr}}
gen.AddUncheckedReceipt(receipt)
receipts = types.Receipts{receipt}
}
diff --git a/eth/bind.go b/eth/bind.go
index 010862fa33..52b4b9f86a 100644
--- a/eth/bind.go
+++ b/eth/bind.go
@@ -21,6 +21,7 @@ import (
"github.com/ubiq/go-ubiq"
"github.com/ubiq/go-ubiq/common"
+ "github.com/ubiq/go-ubiq/common/hexutil"
"github.com/ubiq/go-ubiq/core/types"
"github.com/ubiq/go-ubiq/internal/ethapi"
"github.com/ubiq/go-ubiq/rlp"
@@ -83,16 +84,16 @@ func toCallArgs(msg ethereum.CallMsg) ethapi.CallArgs {
args := ethapi.CallArgs{
To: msg.To,
From: msg.From,
- Data: common.ToHex(msg.Data),
+ Data: msg.Data,
}
if msg.Gas != nil {
- args.Gas = *rpc.NewHexNumber(msg.Gas)
+ args.Gas = hexutil.Big(*msg.Gas)
}
if msg.GasPrice != nil {
- args.GasPrice = *rpc.NewHexNumber(msg.GasPrice)
+ args.GasPrice = hexutil.Big(*msg.GasPrice)
}
if msg.Value != nil {
- args.Value = *rpc.NewHexNumber(msg.Value)
+ args.Value = hexutil.Big(*msg.Value)
}
return args
}
@@ -106,9 +107,12 @@ func toBlockNumber(num *big.Int) rpc.BlockNumber {
// PendingAccountNonce implements bind.ContractTransactor retrieving the current
// pending nonce associated with an account.
-func (b *ContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
+func (b *ContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (nonce uint64, err error) {
out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber)
- return out.Uint64(), err
+ if out != nil {
+ nonce = uint64(*out)
+ }
+ return nonce, err
}
// SuggestGasPrice implements bind.ContractTransactor retrieving the currently
@@ -124,13 +128,13 @@ func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error)
// should provide a basis for setting a reasonable default.
func (b *ContractBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) {
out, err := b.bcapi.EstimateGas(ctx, toCallArgs(msg))
- return out.BigInt(), err
+ return out.ToInt(), err
}
// SendTransaction implements bind.ContractTransactor injects the transaction
// into the pending pool for execution.
func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
raw, _ := rlp.EncodeToBytes(tx)
- _, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw))
+ _, err := b.txapi.SendRawTransaction(ctx, raw)
return err
}
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index 3db459ab20..9334c70f93 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -1005,7 +1005,7 @@ func (d *Downloader) fetchNodeData() error {
// - fetchHook: tester callback to notify of new tasks being initiated (allows testing the scheduling logic)
// - fetch: network callback to actually send a particular download request to a physical remote peer
// - cancel: task callback to abort an in-flight download request and allow rescheduling it (in case of lost peer)
-// - capacity: network callback to retreive the estimated type-specific bandwidth capacity of a peer (traffic shaping)
+// - capacity: network callback to retrieve the estimated type-specific bandwidth capacity of a peer (traffic shaping)
// - idle: network callback to retrieve the currently (type specific) idle peers that can be assigned tasks
// - setIdle: network callback to set a peer back to idle and update its estimated capacity (traffic shaping)
// - kind: textual label of the type being downloaded to display in log mesages
diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go
index 95f037a39f..d404580563 100644
--- a/eth/downloader/downloader_test.go
+++ b/eth/downloader/downloader_test.go
@@ -119,7 +119,7 @@ func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, paren
// If the block number is multiple of 3, send a bonus transaction to the miner
if parent == dl.genesis && i%3 == 0 {
signer := types.MakeSigner(params.TestChainConfig, block.Number())
- tx, err := types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, testKey)
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey)
if err != nil {
panic(err)
}
@@ -650,7 +650,7 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng
}
// Verify the state trie too for fast syncs
if tester.downloader.mode == FastSync {
- index := 0
+ var index int
if pivot := int(tester.downloader.queue.fastSyncPivot); pivot < common {
index = pivot
} else {
diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go
index b062aef8b7..0ef47dbdf1 100644
--- a/eth/downloader/peer.go
+++ b/eth/downloader/peer.go
@@ -205,7 +205,7 @@ func (p *peer) FetchNodeData(request *fetchRequest) error {
// Convert the hash set to a retrievable slice
hashes := make([]common.Hash, 0, len(request.Hashes))
- for hash, _ := range request.Hashes {
+ for hash := range request.Hashes {
hashes = append(hashes, hash)
}
go p.getNodeData(hashes)
@@ -314,7 +314,7 @@ func (p *peer) MarkLacking(hash common.Hash) {
defer p.lock.Unlock()
for len(p.lacking) >= maxLackingHashes {
- for drop, _ := range p.lacking {
+ for drop := range p.lacking {
delete(p.lacking, drop)
break
}
diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go
index 930f6cee14..7b8731ac61 100644
--- a/eth/downloader/queue.go
+++ b/eth/downloader/queue.go
@@ -844,7 +844,7 @@ func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest,
}
}
// Remove the expired requests from the pending pool
- for id, _ := range expiries {
+ for id := range expiries {
delete(pendPool, id)
}
return expiries
@@ -1063,7 +1063,7 @@ func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(int, boo
// If no data was retrieved, mark their hashes as unavailable for the origin peer
if len(data) == 0 {
- for hash, _ := range request.Hashes {
+ for hash := range request.Hashes {
request.Peer.MarkLacking(hash)
}
}
@@ -1123,15 +1123,20 @@ func (q *queue) deliverNodeData(results []trie.SyncResult, callback func(int, bo
callback(i, progressed, errNoFetchesPending)
return
}
- if prog, _, err := q.stateScheduler.Process([]trie.SyncResult{result}); err != nil {
- // Processing a state result failed, bail out
+
+ batch := q.stateDatabase.NewBatch()
+ prog, _, err := q.stateScheduler.Process([]trie.SyncResult{result}, batch)
+ if err != nil {
q.stateSchedLock.Unlock()
callback(i, progressed, err)
- return
- } else if prog {
- progressed = true
}
+ if err = batch.Write(); err != nil {
+ q.stateSchedLock.Unlock()
+ callback(i, progressed, err)
+ }
+
// Item processing succeeded, release the lock (temporarily)
+ progressed = progressed || prog
q.stateSchedLock.Unlock()
}
callback(len(results), progressed, nil)
diff --git a/eth/fetcher/fetcher_test.go b/eth/fetcher/fetcher_test.go
index 9b81f57265..a101c170d0 100644
--- a/eth/fetcher/fetcher_test.go
+++ b/eth/fetcher/fetcher_test.go
@@ -51,7 +51,7 @@ func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common
// If the block number is multiple of 3, send a bonus transaction to the miner
if parent == genesis && i%3 == 0 {
signer := types.MakeSigner(params.TestChainConfig, block.Number())
- tx, err := types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, testKey)
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey)
if err != nil {
panic(err)
}
diff --git a/eth/filters/api.go b/eth/filters/api.go
index b17357b461..7d9eee6113 100644
--- a/eth/filters/api.go
+++ b/eth/filters/api.go
@@ -45,7 +45,7 @@ type filter struct {
deadline *time.Timer // filter is inactiv when deadline triggers
hashes []common.Hash
crit FilterCriteria
- logs []Log
+ logs []*types.Log
s *Subscription // associated subscription in event system
}
@@ -241,7 +241,7 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc
var (
rpcSub = notifier.CreateSubscription()
- matchedLogs = make(chan []Log)
+ matchedLogs = make(chan []*types.Log)
)
logsSub, err := api.events.SubscribeLogs(crit, matchedLogs)
@@ -290,16 +290,16 @@ type FilterCriteria struct {
//
// In case "fromBlock" > "toBlock" an error is returned.
//
-// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter
+// https://github.com/ubiq/wiki/wiki/JSON-RPC#eth_newfilter
func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) {
- logs := make(chan []Log)
+ logs := make(chan []*types.Log)
logsSub, err := api.events.SubscribeLogs(crit, logs)
if err != nil {
return rpc.ID(""), err
}
api.filtersMu.Lock()
- api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(deadline), logs: make([]Log, 0), s: logsSub}
+ api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(deadline), logs: make([]*types.Log, 0), s: logsSub}
api.filtersMu.Unlock()
go func() {
@@ -326,7 +326,7 @@ func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) {
// GetLogs returns logs matching the given argument that are stored within the state.
//
// https://github.com/ubiq/wiki/wiki/JSON-RPC#eth_getlogs
-func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]Log, error) {
+func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*types.Log, error) {
if crit.FromBlock == nil {
crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
}
@@ -365,7 +365,7 @@ func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool {
// If the filter could not be found an empty array of logs is returned.
//
// https://github.com/ubiq/wiki/wiki/JSON-RPC#eth_getfilterlogs
-func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]Log, error) {
+func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*types.Log, error) {
api.filtersMu.Lock()
f, found := api.filters[id]
api.filtersMu.Unlock()
@@ -388,7 +388,7 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]Log
filter.SetAddresses(f.crit.Addresses)
filter.SetTopics(f.crit.Topics)
- logs, err:= filter.Find(ctx)
+ logs, err := filter.Find(ctx)
if err != nil {
return nil, err
}
@@ -440,9 +440,9 @@ func returnHashes(hashes []common.Hash) []common.Hash {
// returnLogs is a helper that will return an empty log array in case the given logs array is nil,
// otherwise the given logs array is returned.
-func returnLogs(logs []Log) []Log {
+func returnLogs(logs []*types.Log) []*types.Log {
if logs == nil {
- return []Log{}
+ return []*types.Log{}
}
return logs
}
@@ -505,7 +505,7 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
switch topic := t.(type) {
case nil:
// ignore topic when matching logs
- args.Topics[i] = []common.Hash{common.Hash{}}
+ args.Topics[i] = []common.Hash{{}}
case string:
// match specific topic
diff --git a/eth/filters/filter.go b/eth/filters/filter.go
index b72fba51c5..6d49b68490 100644
--- a/eth/filters/filter.go
+++ b/eth/filters/filter.go
@@ -37,7 +37,7 @@ type Backend interface {
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
}
-// Filter can be used to retrieve and filter logs
+// Filter can be used to retrieve and filter logs.
type Filter struct {
backend Backend
useMipMap bool
@@ -52,6 +52,8 @@ type Filter struct {
// New creates a new filter which uses a bloom filter on blocks to figure out whether
// a particular block is interesting or not.
+// MipMaps allow past blocks to be searched much more efficiently, but are not available
+// to light clients.
func New(backend Backend, useMipMap bool) *Filter {
return &Filter{
backend: backend,
@@ -83,8 +85,11 @@ func (f *Filter) SetTopics(topics [][]common.Hash) {
f.topics = topics
}
-// Run filters logs with the current parameters set
-func (f *Filter) Find(ctx context.Context) ([]Log, error) {
+// FindOnce searches the blockchain for matching log entries, returning
+// all matching entries from the first block that contains matches,
+// updating the start point of the filter accordingly. If no results are
+// found, a nil slice is returned.
+func (f *Filter) FindOnce(ctx context.Context) ([]*types.Log, error) {
head, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
if head == nil {
return nil, nil
@@ -104,47 +109,69 @@ func (f *Filter) Find(ctx context.Context) ([]Log, error) {
// uses the mipmap bloom filters to check for fast inclusion and uses
// higher range probability in order to ensure at least a false positive
if !f.useMipMap || len(f.addresses) == 0 {
- return f.getLogs(ctx, beginBlockNo, endBlockNo)
+ logs, blockNumber, err := f.getLogs(ctx, beginBlockNo, endBlockNo)
+ f.begin = int64(blockNumber + 1)
+ return logs, err
}
- return f.mipFind(beginBlockNo, endBlockNo, 0), nil
+
+ logs, blockNumber := f.mipFind(beginBlockNo, endBlockNo, 0)
+ f.begin = int64(blockNumber + 1)
+ return logs, nil
}
-func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) {
+// Run filters logs with the current parameters set
+func (f *Filter) Find(ctx context.Context) (logs []*types.Log, err error) {
+ for {
+ newLogs, err := f.FindOnce(ctx)
+ if len(newLogs) == 0 || err != nil {
+ return logs, err
+ }
+ logs = append(logs, newLogs...)
+ }
+}
+
+func (f *Filter) mipFind(start, end uint64, depth int) (logs []*types.Log, blockNumber uint64) {
level := core.MIPMapLevels[depth]
// normalise numerator so we can work in level specific batches and
// work with the proper range checks
for num := start / level * level; num <= end; num += level {
// find addresses in bloom filters
bloom := core.GetMipmapBloom(f.db, num, level)
+ // Don't bother checking the first time through the loop - we're probably picking
+ // up where a previous run left off.
+ first := true
for _, addr := range f.addresses {
- if bloom.TestBytes(addr[:]) {
+ if first || bloom.TestBytes(addr[:]) {
+ first = false
// range check normalised values and make sure that
// we're resolving the correct range instead of the
// normalised values.
start := uint64(math.Max(float64(num), float64(start)))
end := uint64(math.Min(float64(num+level-1), float64(end)))
if depth+1 == len(core.MIPMapLevels) {
- l, _ := f.getLogs(context.Background(), start, end)
- logs = append(logs, l...)
+ l, blockNumber, _ := f.getLogs(context.Background(), start, end)
+ if len(l) > 0 {
+ return l, blockNumber
+ }
} else {
- logs = append(logs, f.mipFind(start, end, depth+1)...)
+ l, blockNumber := f.mipFind(start, end, depth+1)
+ if len(l) > 0 {
+ return l, blockNumber
+ }
}
- // break so we don't check the same range for each
- // possible address. Checks on multiple addresses
- // are handled further down the stack.
- break
}
}
}
- return logs
+ return nil, end
}
-func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, err error) {
+func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []*types.Log, blockNumber uint64, err error) {
for i := start; i <= end; i++ {
- header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(i))
+ blockNumber := rpc.BlockNumber(i)
+ header, err := f.backend.HeaderByNumber(ctx, blockNumber)
if header == nil || err != nil {
- return logs, err
+ return logs, end, err
}
// Use bloom filtering to see if this block is interesting given the
@@ -153,21 +180,20 @@ func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, er
// Get the logs of the block
receipts, err := f.backend.GetReceipts(ctx, header.Hash())
if err != nil {
- return nil, err
+ return nil, end, err
}
- var unfiltered []Log
+ var unfiltered []*types.Log
for _, receipt := range receipts {
- rl := make([]Log, len(receipt.Logs))
- for i, l := range receipt.Logs {
- rl[i] = Log{l, false}
- }
- unfiltered = append(unfiltered, rl...)
+ unfiltered = append(unfiltered, ([]*types.Log)(receipt.Logs)...)
+ }
+ logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
+ if len(logs) > 0 {
+ return logs, uint64(blockNumber), nil
}
- logs = append(logs, filterLogs(unfiltered, nil, nil, f.addresses, f.topics)...)
}
}
- return logs, nil
+ return logs, end, nil
}
func includes(addresses []common.Address, a common.Address) bool {
@@ -180,15 +206,15 @@ func includes(addresses []common.Address, a common.Address) bool {
return false
}
-func filterLogs(logs []Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []Log {
- var ret []Log
- // Filter the logs for interesting stuff
+// filterLogs creates a slice of logs matching the given criteria.
+func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log {
+ var ret []*types.Log
Logs:
for _, log := range logs {
- if fromBlock != nil && fromBlock.Int64() >= 0 && uint64(fromBlock.Int64()) > log.BlockNumber {
+ if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber {
continue
}
- if toBlock != nil && toBlock.Int64() >= 0 && uint64(toBlock.Int64()) < log.BlockNumber {
+ if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
continue
}
diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go
index 851495cfd0..a15c509253 100644
--- a/eth/filters/filter_system.go
+++ b/eth/filters/filter_system.go
@@ -19,7 +19,6 @@
package filters
import (
- "encoding/json"
"errors"
"fmt"
"sync"
@@ -28,7 +27,6 @@ import (
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core"
"github.com/ubiq/go-ubiq/core/types"
- "github.com/ubiq/go-ubiq/core/vm"
"github.com/ubiq/go-ubiq/event"
"github.com/ubiq/go-ubiq/rpc"
"golang.org/x/net/context"
@@ -39,7 +37,7 @@ import (
type Type byte
const (
- // UnknownSubscription indicates an unkown subscription type
+ // UnknownSubscription indicates an unknown subscription type
UnknownSubscription Type = iota
// LogsSubscription queries for new or removed (chain reorg) logs
LogsSubscription
@@ -60,42 +58,12 @@ var (
ErrInvalidSubscriptionID = errors.New("invalid id")
)
-// Log is a helper that can hold additional information about vm.Log
-// necessary for the RPC interface.
-type Log struct {
- *vm.Log
- Removed bool `json:"removed"`
-}
-
-// MarshalJSON returns *l as the JSON encoding of l.
-func (l *Log) MarshalJSON() ([]byte, error) {
- fields := map[string]interface{}{
- "address": l.Address,
- "data": fmt.Sprintf("0x%x", l.Data),
- "blockNumber": nil,
- "logIndex": fmt.Sprintf("%#x", l.Index),
- "blockHash": nil,
- "transactionHash": l.TxHash,
- "transactionIndex": fmt.Sprintf("%#x", l.TxIndex),
- "topics": l.Topics,
- "removed": l.Removed,
- }
-
- // mined logs
- if l.BlockHash != (common.Hash{}) {
- fields["blockNumber"] = fmt.Sprintf("%#x", l.BlockNumber)
- fields["blockHash"] = l.BlockHash
- }
-
- return json.Marshal(fields)
-}
-
type subscription struct {
id rpc.ID
typ Type
created time.Time
logsCrit FilterCriteria
- logs chan []Log
+ logs chan []*types.Log
hashes chan common.Hash
headers chan *types.Header
installed chan struct{} // closed when the filter is installed
@@ -182,7 +150,7 @@ func (es *EventSystem) subscribe(sub *subscription) *Subscription {
// SubscribeLogs creates a subscription that will write all logs matching the
// given criteria to the given logs channel. Default value for the from and to
// block is "latest". If the fromBlock > toBlock an error is returned.
-func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []Log) (*Subscription, error) {
+func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []*types.Log) (*Subscription, error) {
var from, to rpc.BlockNumber
if crit.FromBlock == nil {
from = rpc.LatestBlockNumber
@@ -220,7 +188,7 @@ func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []Log) (*Sub
// subscribeMinedPendingLogs creates a subscription that returned mined and
// pending logs that match the given criteria.
-func (es *EventSystem) subscribeMinedPendingLogs(crit FilterCriteria, logs chan []Log) *Subscription {
+func (es *EventSystem) subscribeMinedPendingLogs(crit FilterCriteria, logs chan []*types.Log) *Subscription {
sub := &subscription{
id: rpc.NewID(),
typ: MinedAndPendingLogsSubscription,
@@ -238,7 +206,7 @@ func (es *EventSystem) subscribeMinedPendingLogs(crit FilterCriteria, logs chan
// subscribeLogs creates a subscription that will write all logs matching the
// given criteria to the given logs channel.
-func (es *EventSystem) subscribeLogs(crit FilterCriteria, logs chan []Log) *Subscription {
+func (es *EventSystem) subscribeLogs(crit FilterCriteria, logs chan []*types.Log) *Subscription {
sub := &subscription{
id: rpc.NewID(),
typ: LogsSubscription,
@@ -256,7 +224,7 @@ func (es *EventSystem) subscribeLogs(crit FilterCriteria, logs chan []Log) *Subs
// subscribePendingLogs creates a subscription that writes transaction hashes for
// transactions that enter the transaction pool.
-func (es *EventSystem) subscribePendingLogs(crit FilterCriteria, logs chan []Log) *Subscription {
+func (es *EventSystem) subscribePendingLogs(crit FilterCriteria, logs chan []*types.Log) *Subscription {
sub := &subscription{
id: rpc.NewID(),
typ: PendingLogsSubscription,
@@ -279,7 +247,7 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti
id: rpc.NewID(),
typ: BlocksSubscription,
created: time.Now(),
- logs: make(chan []Log),
+ logs: make(chan []*types.Log),
hashes: make(chan common.Hash),
headers: headers,
installed: make(chan struct{}),
@@ -296,7 +264,7 @@ func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscr
id: rpc.NewID(),
typ: PendingTransactionsSubscription,
created: time.Now(),
- logs: make(chan []Log),
+ logs: make(chan []*types.Log),
hashes: hashes,
headers: make(chan *types.Header),
installed: make(chan struct{}),
@@ -315,11 +283,11 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
}
switch e := ev.Data.(type) {
- case vm.Logs:
+ case []*types.Log:
if len(e) > 0 {
for _, f := range filters[LogsSubscription] {
if ev.Time.After(f.created) {
- if matchedLogs := filterLogs(convertLogs(e, false), f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
+ if matchedLogs := filterLogs(e, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
f.logs <- matchedLogs
}
}
@@ -328,7 +296,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
case core.RemovedLogsEvent:
for _, f := range filters[LogsSubscription] {
if ev.Time.After(f.created) {
- if matchedLogs := filterLogs(convertLogs(e.Logs, true), f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
+ if matchedLogs := filterLogs(e.Logs, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
f.logs <- matchedLogs
}
}
@@ -336,7 +304,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
case core.PendingLogsEvent:
for _, f := range filters[PendingLogsSubscription] {
if ev.Time.After(f.created) {
- if matchedLogs := filterLogs(convertLogs(e.Logs, false), nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
+ if matchedLogs := filterLogs(e.Logs, nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
f.logs <- matchedLogs
}
}
@@ -401,25 +369,22 @@ func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func
}
// filter logs of a single header in light client mode
-func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []Log {
- //fmt.Println("lightFilterLogs", header.Number.Uint64(), remove)
+func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []*types.Log {
if bloomFilter(header.Bloom, addresses, topics) {
- //fmt.Println("bloom match")
// Get the logs of the block
ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
receipts, err := es.backend.GetReceipts(ctx, header.Hash())
if err != nil {
return nil
}
- var unfiltered []Log
+ var unfiltered []*types.Log
for _, receipt := range receipts {
- rl := make([]Log, len(receipt.Logs))
- for i, l := range receipt.Logs {
- rl[i] = Log{l, remove}
+ for _, log := range receipt.Logs {
+ logcopy := *log
+ logcopy.Removed = remove
+ unfiltered = append(unfiltered, &logcopy)
}
- unfiltered = append(unfiltered, rl...)
}
-
logs := filterLogs(unfiltered, nil, nil, addresses, topics)
return logs
}
@@ -430,7 +395,7 @@ func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.
func (es *EventSystem) eventLoop() {
var (
index = make(filterIndex)
- sub = es.mux.Subscribe(core.PendingLogsEvent{}, core.RemovedLogsEvent{}, vm.Logs{}, core.TxPreEvent{}, core.ChainEvent{})
+ sub = es.mux.Subscribe(core.PendingLogsEvent{}, core.RemovedLogsEvent{}, []*types.Log{}, core.TxPreEvent{}, core.ChainEvent{})
)
for i := UnknownSubscription; i < LastIndexSubscription; i++ {
@@ -465,13 +430,3 @@ func (es *EventSystem) eventLoop() {
}
}
}
-
-// convertLogs is a helper utility that converts vm.Logs to []filter.Log.
-func convertLogs(in vm.Logs, removed bool) []Log {
-
- logs := make([]Log, len(in))
- for i, l := range in {
- logs[i] = Log{l, removed}
- }
- return logs
-}
diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go
index bd16a5237e..fd1460434b 100644
--- a/eth/filters/filter_system_test.go
+++ b/eth/filters/filter_system_test.go
@@ -27,7 +27,6 @@ import (
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core"
"github.com/ubiq/go-ubiq/core/types"
- "github.com/ubiq/go-ubiq/core/vm"
"github.com/ubiq/go-ubiq/ethdb"
"github.com/ubiq/go-ubiq/event"
"github.com/ubiq/go-ubiq/params"
@@ -74,10 +73,10 @@ func TestBlockSubscription(t *testing.T) {
t.Parallel()
var (
- mux = new(event.TypeMux)
- db, _ = ethdb.NewMemDatabase()
+ mux = new(event.TypeMux)
+ db, _ = ethdb.NewMemDatabase()
backend = &testBackend{mux, db}
- api = NewPublicFilterAPI(backend, false)
+ api = NewPublicFilterAPI(backend, false)
genesis = core.WriteGenesisBlockForTesting(db)
chain, _ = core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {})
@@ -128,10 +127,10 @@ func TestPendingTxFilter(t *testing.T) {
t.Parallel()
var (
- mux = new(event.TypeMux)
- db, _ = ethdb.NewMemDatabase()
+ mux = new(event.TypeMux)
+ db, _ = ethdb.NewMemDatabase()
backend = &testBackend{mux, db}
- api = NewPublicFilterAPI(backend, false)
+ api = NewPublicFilterAPI(backend, false)
transactions = []*types.Transaction{
types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil),
@@ -178,10 +177,10 @@ func TestPendingTxFilter(t *testing.T) {
// If not it must return an error.
func TestLogFilterCreation(t *testing.T) {
var (
- mux = new(event.TypeMux)
- db, _ = ethdb.NewMemDatabase()
+ mux = new(event.TypeMux)
+ db, _ = ethdb.NewMemDatabase()
backend = &testBackend{mux, db}
- api = NewPublicFilterAPI(backend, false)
+ api = NewPublicFilterAPI(backend, false)
testCases = []struct {
crit FilterCriteria
@@ -223,10 +222,10 @@ func TestInvalidLogFilterCreation(t *testing.T) {
t.Parallel()
var (
- mux = new(event.TypeMux)
- db, _ = ethdb.NewMemDatabase()
+ mux = new(event.TypeMux)
+ db, _ = ethdb.NewMemDatabase()
backend = &testBackend{mux, db}
- api = NewPublicFilterAPI(backend, false)
+ api = NewPublicFilterAPI(backend, false)
)
// different situations where log filter creation should fail.
@@ -249,10 +248,10 @@ func TestLogFilter(t *testing.T) {
t.Parallel()
var (
- mux = new(event.TypeMux)
- db, _ = ethdb.NewMemDatabase()
+ mux = new(event.TypeMux)
+ db, _ = ethdb.NewMemDatabase()
backend = &testBackend{mux, db}
- api = NewPublicFilterAPI(backend, false)
+ api = NewPublicFilterAPI(backend, false)
firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
@@ -263,34 +262,34 @@ func TestLogFilter(t *testing.T) {
notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999")
// posted twice, once as vm.Logs and once as core.PendingLogsEvent
- allLogs = vm.Logs{
- vm.NewLog(firstAddr, []common.Hash{}, []byte(""), 0),
- vm.NewLog(firstAddr, []common.Hash{firstTopic}, []byte(""), 1),
- vm.NewLog(secondAddr, []common.Hash{firstTopic}, []byte(""), 1),
- vm.NewLog(thirdAddress, []common.Hash{secondTopic}, []byte(""), 2),
- vm.NewLog(thirdAddress, []common.Hash{secondTopic}, []byte(""), 3),
+ allLogs = []*types.Log{
+ {Address: firstAddr},
+ {Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1},
+ {Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1},
+ {Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 2},
+ {Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3},
}
- expectedCase7 = vm.Logs{allLogs[3], allLogs[4], allLogs[0], allLogs[1], allLogs[2], allLogs[3], allLogs[4]}
- expectedCase11 = vm.Logs{allLogs[1], allLogs[2], allLogs[1], allLogs[2]}
+ expectedCase7 = []*types.Log{allLogs[3], allLogs[4], allLogs[0], allLogs[1], allLogs[2], allLogs[3], allLogs[4]}
+ expectedCase11 = []*types.Log{allLogs[1], allLogs[2], allLogs[1], allLogs[2]}
testCases = []struct {
crit FilterCriteria
- expected vm.Logs
+ expected []*types.Log
id rpc.ID
}{
// match all
0: {FilterCriteria{}, allLogs, ""},
// match none due to no matching addresses
- 1: {FilterCriteria{Addresses: []common.Address{common.Address{}, notUsedAddress}, Topics: [][]common.Hash{allLogs[0].Topics}}, vm.Logs{}, ""},
+ 1: {FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{allLogs[0].Topics}}, []*types.Log{}, ""},
// match logs based on addresses, ignore topics
2: {FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""},
// match none due to no matching topics (match with address)
- 3: {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{[]common.Hash{notUsedTopic}}}, vm.Logs{}, ""},
+ 3: {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, ""},
// match logs based on addresses and topics
- 4: {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[3:5], ""},
+ 4: {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[3:5], ""},
// match logs based on multiple addresses and "or" topics
- 5: {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[2:5], ""},
+ 5: {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[2:5], ""},
// logs in the pending block
6: {FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, allLogs[:2], ""},
// mined logs with block num >= 2 or pending logs
@@ -300,9 +299,9 @@ func TestLogFilter(t *testing.T) {
// all "mined" logs
9: {FilterCriteria{ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs, ""},
// all "mined" logs with 1>= block num <=2 and topic secondTopic
- 10: {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2), Topics: [][]common.Hash{[]common.Hash{secondTopic}}}, allLogs[3:4], ""},
+ 10: {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2), Topics: [][]common.Hash{{secondTopic}}}, allLogs[3:4], ""},
// all "mined" and pending logs with topic firstTopic
- 11: {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), Topics: [][]common.Hash{[]common.Hash{firstTopic}}}, expectedCase11, ""},
+ 11: {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), Topics: [][]common.Hash{{firstTopic}}}, expectedCase11, ""},
}
)
@@ -321,14 +320,14 @@ func TestLogFilter(t *testing.T) {
}
for i, tt := range testCases {
- var fetched []Log
+ var fetched []*types.Log
for { // fetch all expected logs
results, err := api.GetFilterChanges(tt.id)
if err != nil {
t.Fatalf("Unable to fetch logs: %v", err)
}
- fetched = append(fetched, results.([]Log)...)
+ fetched = append(fetched, results.([]*types.Log)...)
if len(fetched) >= len(tt.expected) {
break
}
@@ -345,7 +344,7 @@ func TestLogFilter(t *testing.T) {
if fetched[l].Removed {
t.Errorf("expected log not to be removed for log %d in case %d", l, i)
}
- if !reflect.DeepEqual(fetched[l].Log, tt.expected[l]) {
+ if !reflect.DeepEqual(fetched[l], tt.expected[l]) {
t.Errorf("invalid log on index %d for case %d", l, i)
}
}
@@ -357,10 +356,10 @@ func TestPendingLogsSubscription(t *testing.T) {
t.Parallel()
var (
- mux = new(event.TypeMux)
- db, _ = ethdb.NewMemDatabase()
+ mux = new(event.TypeMux)
+ db, _ = ethdb.NewMemDatabase()
backend = &testBackend{mux, db}
- api = NewPublicFilterAPI(backend, false)
+ api = NewPublicFilterAPI(backend, false)
firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
@@ -373,21 +372,21 @@ func TestPendingLogsSubscription(t *testing.T) {
notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999")
allLogs = []core.PendingLogsEvent{
- core.PendingLogsEvent{Logs: vm.Logs{vm.NewLog(firstAddr, []common.Hash{}, []byte(""), 0)}},
- core.PendingLogsEvent{Logs: vm.Logs{vm.NewLog(firstAddr, []common.Hash{firstTopic}, []byte(""), 1)}},
- core.PendingLogsEvent{Logs: vm.Logs{vm.NewLog(secondAddr, []common.Hash{firstTopic}, []byte(""), 2)}},
- core.PendingLogsEvent{Logs: vm.Logs{vm.NewLog(thirdAddress, []common.Hash{secondTopic}, []byte(""), 3)}},
- core.PendingLogsEvent{Logs: vm.Logs{vm.NewLog(thirdAddress, []common.Hash{secondTopic}, []byte(""), 4)}},
- core.PendingLogsEvent{Logs: vm.Logs{
- vm.NewLog(thirdAddress, []common.Hash{firstTopic}, []byte(""), 5),
- vm.NewLog(thirdAddress, []common.Hash{thirdTopic}, []byte(""), 5),
- vm.NewLog(thirdAddress, []common.Hash{forthTopic}, []byte(""), 5),
- vm.NewLog(firstAddr, []common.Hash{firstTopic}, []byte(""), 5),
+ {Logs: []*types.Log{{Address: firstAddr, Topics: []common.Hash{}, BlockNumber: 0}}},
+ {Logs: []*types.Log{{Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}}},
+ {Logs: []*types.Log{{Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 2}}},
+ {Logs: []*types.Log{{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3}}},
+ {Logs: []*types.Log{{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 4}}},
+ {Logs: []*types.Log{
+ {Address: thirdAddress, Topics: []common.Hash{firstTopic}, BlockNumber: 5},
+ {Address: thirdAddress, Topics: []common.Hash{thirdTopic}, BlockNumber: 5},
+ {Address: thirdAddress, Topics: []common.Hash{forthTopic}, BlockNumber: 5},
+ {Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 5},
}},
}
- convertLogs = func(pl []core.PendingLogsEvent) vm.Logs {
- var logs vm.Logs
+ convertLogs = func(pl []core.PendingLogsEvent) []*types.Log {
+ var logs []*types.Log
for _, l := range pl {
logs = append(logs, l.Logs...)
}
@@ -396,26 +395,26 @@ func TestPendingLogsSubscription(t *testing.T) {
testCases = []struct {
crit FilterCriteria
- expected vm.Logs
- c chan []Log
+ expected []*types.Log
+ c chan []*types.Log
sub *Subscription
}{
// match all
{FilterCriteria{}, convertLogs(allLogs), nil, nil},
// match none due to no matching addresses
- {FilterCriteria{Addresses: []common.Address{common.Address{}, notUsedAddress}, Topics: [][]common.Hash{[]common.Hash{}}}, vm.Logs{}, nil, nil},
+ {FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{{}}}, []*types.Log{}, nil, nil},
// match logs based on addresses, ignore topics
{FilterCriteria{Addresses: []common.Address{firstAddr}}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil},
// match none due to no matching topics (match with address)
- {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{[]common.Hash{notUsedTopic}}}, vm.Logs{}, nil, nil},
+ {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, nil, nil},
// match logs based on addresses and topics
- {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, append(convertLogs(allLogs[3:5]), allLogs[5].Logs[0]), nil, nil},
+ {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[3:5]), allLogs[5].Logs[0]), nil, nil},
// match logs based on multiple addresses and "or" topics
- {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, append(convertLogs(allLogs[2:5]), allLogs[5].Logs[0]), nil, nil},
- // block numbers are ignored for filters created with New***Filter, these return all logs that match the given criterias when the state changes
+ {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[2:5]), allLogs[5].Logs[0]), nil, nil},
+ // block numbers are ignored for filters created with New***Filter, these return all logs that match the given criteria when the state changes
{FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(2), ToBlock: big.NewInt(3)}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil},
// multiple pending logs, should match only 2 topics from the logs in block 5
- {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, forthTopic}}}, vm.Logs{allLogs[5].Logs[0], allLogs[5].Logs[2]}, nil, nil},
+ {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, forthTopic}}}, []*types.Log{allLogs[5].Logs[0], allLogs[5].Logs[2]}, nil, nil},
}
)
@@ -423,7 +422,7 @@ func TestPendingLogsSubscription(t *testing.T) {
// on slow machines this could otherwise lead to missing events when the subscription is created after
// (some) events are posted.
for i := range testCases {
- testCases[i].c = make(chan []Log)
+ testCases[i].c = make(chan []*types.Log)
testCases[i].sub, _ = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c)
}
@@ -431,7 +430,7 @@ func TestPendingLogsSubscription(t *testing.T) {
i := n
tt := test
go func() {
- var fetched []Log
+ var fetched []*types.Log
fetchLoop:
for {
logs := <-tt.c
@@ -449,7 +448,7 @@ func TestPendingLogsSubscription(t *testing.T) {
if fetched[l].Removed {
t.Errorf("expected log not to be removed for log %d in case %d", l, i)
}
- if !reflect.DeepEqual(fetched[l].Log, tt.expected[l]) {
+ if !reflect.DeepEqual(fetched[l], tt.expected[l]) {
t.Errorf("invalid log on index %d for case %d", l, i)
}
}
diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go
index 5256711c2d..5904e41779 100644
--- a/eth/filters/filter_test.go
+++ b/eth/filters/filter_test.go
@@ -27,7 +27,6 @@ import (
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core"
"github.com/ubiq/go-ubiq/core/types"
- "github.com/ubiq/go-ubiq/core/vm"
"github.com/ubiq/go-ubiq/crypto"
"github.com/ubiq/go-ubiq/ethdb"
"github.com/ubiq/go-ubiq/event"
@@ -36,8 +35,8 @@ import (
func makeReceipt(addr common.Address) *types.Receipt {
receipt := types.NewReceipt(nil, new(big.Int))
- receipt.Logs = vm.Logs{
- &vm.Log{Address: addr},
+ receipt.Logs = []*types.Log{
+ {Address: addr},
}
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
return receipt
@@ -146,8 +145,8 @@ func TestFilters(t *testing.T) {
switch i {
case 1:
receipt := types.NewReceipt(nil, new(big.Int))
- receipt.Logs = vm.Logs{
- &vm.Log{
+ receipt.Logs = []*types.Log{
+ {
Address: addr,
Topics: []common.Hash{hash1},
},
@@ -156,8 +155,8 @@ func TestFilters(t *testing.T) {
receipts = types.Receipts{receipt}
case 2:
receipt := types.NewReceipt(nil, new(big.Int))
- receipt.Logs = vm.Logs{
- &vm.Log{
+ receipt.Logs = []*types.Log{
+ {
Address: addr,
Topics: []common.Hash{hash2},
},
@@ -166,8 +165,8 @@ func TestFilters(t *testing.T) {
receipts = types.Receipts{receipt}
case 998:
receipt := types.NewReceipt(nil, new(big.Int))
- receipt.Logs = vm.Logs{
- &vm.Log{
+ receipt.Logs = []*types.Log{
+ {
Address: addr,
Topics: []common.Hash{hash3},
},
@@ -176,8 +175,8 @@ func TestFilters(t *testing.T) {
receipts = types.Receipts{receipt}
case 999:
receipt := types.NewReceipt(nil, new(big.Int))
- receipt.Logs = vm.Logs{
- &vm.Log{
+ receipt.Logs = []*types.Log{
+ {
Address: addr,
Topics: []common.Hash{hash4},
},
@@ -211,7 +210,7 @@ func TestFilters(t *testing.T) {
filter := New(backend, true)
filter.SetAddresses([]common.Address{addr})
- filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}})
+ filter.SetTopics([][]common.Hash{{hash1, hash2, hash3, hash4}})
filter.SetBeginBlock(0)
filter.SetEndBlock(-1)
@@ -222,7 +221,7 @@ func TestFilters(t *testing.T) {
filter = New(backend, true)
filter.SetAddresses([]common.Address{addr})
- filter.SetTopics([][]common.Hash{[]common.Hash{hash3}})
+ filter.SetTopics([][]common.Hash{{hash3}})
filter.SetBeginBlock(900)
filter.SetEndBlock(999)
logs, _ = filter.Find(context.Background())
@@ -235,7 +234,7 @@ func TestFilters(t *testing.T) {
filter = New(backend, true)
filter.SetAddresses([]common.Address{addr})
- filter.SetTopics([][]common.Hash{[]common.Hash{hash3}})
+ filter.SetTopics([][]common.Hash{{hash3}})
filter.SetBeginBlock(990)
filter.SetEndBlock(-1)
logs, _ = filter.Find(context.Background())
@@ -247,7 +246,7 @@ func TestFilters(t *testing.T) {
}
filter = New(backend, true)
- filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}})
+ filter.SetTopics([][]common.Hash{{hash1, hash2}})
filter.SetBeginBlock(1)
filter.SetEndBlock(10)
@@ -258,7 +257,7 @@ func TestFilters(t *testing.T) {
failHash := common.BytesToHash([]byte("fail"))
filter = New(backend, true)
- filter.SetTopics([][]common.Hash{[]common.Hash{failHash}})
+ filter.SetTopics([][]common.Hash{{failHash}})
filter.SetBeginBlock(0)
filter.SetEndBlock(-1)
@@ -279,7 +278,7 @@ func TestFilters(t *testing.T) {
}
filter = New(backend, true)
- filter.SetTopics([][]common.Hash{[]common.Hash{failHash}, []common.Hash{hash1}})
+ filter.SetTopics([][]common.Hash{{failHash}, {hash1}})
filter.SetBeginBlock(0)
filter.SetEndBlock(-1)
diff --git a/eth/handler.go b/eth/handler.go
index 32d8b60698..90af93f685 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -83,6 +83,8 @@ type ProtocolManager struct {
quitSync chan struct{}
noMorePeers chan struct{}
+ lesServer LesServer
+
// wait group is used for graceful shutdowns during downloading
// and processing
wg sync.WaitGroup
@@ -167,7 +169,7 @@ func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int
return blockchain.CurrentBlock().NumberU64()
}
inserter := func(blocks types.Blocks) (int, error) {
- atomic.StoreUint32(&manager.synced, 1) // Mark initial sync done on any fetcher import
+ manager.setSynced() // Mark initial sync done on any fetcher import
return manager.insertChain(blocks)
}
manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, manager.removePeer)
@@ -544,38 +546,16 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
}
case msg.Code == NewBlockHashesMsg:
- // Retrieve and deserialize the remote new block hashes notification
- type announce struct {
- Hash common.Hash
- Number uint64
- }
- var announces = []announce{}
-
- if p.version < eth62 {
- // We're running the old protocol, make block number unknown (0)
- var hashes []common.Hash
- if err := msg.Decode(&hashes); err != nil {
- return errResp(ErrDecode, "%v: %v", msg, err)
- }
- for _, hash := range hashes {
- announces = append(announces, announce{hash, 0})
- }
- } else {
- // Otherwise extract both block hash and number
- var request newBlockHashesData
- if err := msg.Decode(&request); err != nil {
- return errResp(ErrDecode, "%v: %v", msg, err)
- }
- for _, block := range request {
- announces = append(announces, announce{block.Hash, block.Number})
- }
+ var announces newBlockHashesData
+ if err := msg.Decode(&announces); err != nil {
+ return errResp(ErrDecode, "%v: %v", msg, err)
}
// Mark the hashes as present at the remote node
for _, block := range announces {
p.MarkBlock(block.Hash)
}
// Schedule all the unknown hashes for retrieval
- unknown := make([]announce, 0, len(announces))
+ unknown := make(newBlockHashesData, 0, len(announces))
for _, block := range announces {
if !pm.blockchain.HasBlock(block.Hash) {
unknown = append(unknown, block)
@@ -709,7 +689,7 @@ func (self *ProtocolManager) txBroadcastLoop() {
// EthNodeInfo represents a short summary of the Ethereum sub-protocol metadata known
// about the host peer.
type EthNodeInfo struct {
- Network int `json:"network"` // Ethereum network ID (0=Olympic, 1=Frontier, 2=Morden)
+ Network int `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3)
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block
diff --git a/eth/handler_test.go b/eth/handler_test.go
index bc3dfedb8c..7195b4dfa2 100644
--- a/eth/handler_test.go
+++ b/eth/handler_test.go
@@ -75,7 +75,7 @@ func testGetBlockHeaders(t *testing.T, protocol int) {
// Create a "random" unknown hash for testing
var unknown common.Hash
- for i, _ := range unknown {
+ for i := range unknown {
unknown[i] = byte(i)
}
// Create a batch of tests for various scenarios
@@ -246,17 +246,17 @@ func testGetBlockBodies(t *testing.T, protocol int) {
{limit + 1, nil, nil, limit}, // No more than the possible block count should be returned
{0, []common.Hash{pm.blockchain.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable
{0, []common.Hash{pm.blockchain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable
- {0, []common.Hash{common.Hash{}}, []bool{false}, 0}, // A non existent block should not be returned
+ {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned
// Existing and non-existing blocks interleaved should not cause problems
{0, []common.Hash{
- common.Hash{},
+ {},
pm.blockchain.GetBlockByNumber(1).Hash(),
- common.Hash{},
+ {},
pm.blockchain.GetBlockByNumber(10).Hash(),
- common.Hash{},
+ {},
pm.blockchain.GetBlockByNumber(100).Hash(),
- common.Hash{},
+ {},
}, []bool{false, true, false, true, false, true, false}, 3},
}
// Run each of the tests and verify the results against the chain
@@ -311,13 +311,13 @@ func testGetNodeData(t *testing.T, protocol int) {
switch i {
case 0:
// In block 1, the test bank sends account #1 some ether.
- tx, _ := types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(signer, testBankKey)
+ tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
block.AddTx(tx)
case 1:
// In block 2, the test bank sends some more ether to account #1.
// acc1Addr passes it on to account #2.
- tx1, _ := types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, testBankKey)
- tx2, _ := types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, acc1Key)
+ tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
+ tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
block.AddTx(tx1)
block.AddTx(tx2)
case 2:
@@ -403,13 +403,13 @@ func testGetReceipt(t *testing.T, protocol int) {
switch i {
case 0:
// In block 1, the test bank sends account #1 some ether.
- tx, _ := types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(signer, testBankKey)
+ tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
block.AddTx(tx)
case 1:
// In block 2, the test bank sends some more ether to account #1.
// acc1Addr passes it on to account #2.
- tx1, _ := types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, testBankKey)
- tx2, _ := types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, acc1Key)
+ tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
+ tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
block.AddTx(tx1)
block.AddTx(tx2)
case 2:
@@ -445,4 +445,3 @@ func testGetReceipt(t *testing.T, protocol int) {
t.Errorf("receipts mismatch: %v", err)
}
}
-
diff --git a/eth/helper_test.go b/eth/helper_test.go
index 8165b698f7..d6cdf4489b 100644
--- a/eth/helper_test.go
+++ b/eth/helper_test.go
@@ -93,7 +93,7 @@ type testTxPool struct {
// AddBatch appends a batch of transactions to the pool, and notifies any
// listeners if the addition channel is non nil
-func (p *testTxPool) AddBatch(txs []*types.Transaction) {
+func (p *testTxPool) AddBatch(txs []*types.Transaction) error {
p.lock.Lock()
defer p.lock.Unlock()
@@ -101,10 +101,12 @@ func (p *testTxPool) AddBatch(txs []*types.Transaction) {
if p.added != nil {
p.added <- txs
}
+
+ return nil
}
// Pending returns all the transactions known to the pool
-func (p *testTxPool) Pending() map[common.Address]types.Transactions {
+func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) {
p.lock.RLock()
defer p.lock.RUnlock()
@@ -116,13 +118,13 @@ func (p *testTxPool) Pending() map[common.Address]types.Transactions {
for _, batch := range batches {
sort.Sort(types.TxByNonce(batch))
}
- return batches
+ return batches, nil
}
// newTestTransaction create a new dummy transaction.
func newTestTransaction(from *ecdsa.PrivateKey, nonce uint64, datasize int) *types.Transaction {
tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), big.NewInt(100000), big.NewInt(0), make([]byte, datasize))
- tx, _ = tx.SignECDSA(types.HomesteadSigner{}, from)
+ tx, _ = types.SignTx(tx, types.HomesteadSigner{}, from)
return tx
}
diff --git a/eth/protocol.go b/eth/protocol.go
index a657fa9546..4c267a52b2 100644
--- a/eth/protocol.go
+++ b/eth/protocol.go
@@ -98,11 +98,11 @@ var errorToString = map[int]string{
type txPool interface {
// AddBatch should add the given transactions to the pool.
- AddBatch([]*types.Transaction)
+ AddBatch([]*types.Transaction) error
// Pending should return pending transactions.
// The slice should be modifiable by the caller.
- Pending() map[common.Address]types.Transactions
+ Pending() (map[common.Address]types.Transactions, error)
}
// statusData is the network packet for the status message.
diff --git a/eth/protocol_test.go b/eth/protocol_test.go
index 07b05bddd2..f808d95100 100644
--- a/eth/protocol_test.go
+++ b/eth/protocol_test.go
@@ -82,7 +82,7 @@ func testStatusMsgErrors(t *testing.T, protocol int) {
t.Errorf("test %d: wrong error: got %q, want %q", i, err, test.wantError)
}
case <-time.After(2 * time.Second):
- t.Errorf("protocol did not shut down withing 2 seconds")
+ t.Errorf("protocol did not shut down within 2 seconds")
}
p.close()
}
@@ -178,7 +178,7 @@ func testSendTransactions(t *testing.T, protocol int) {
func TestGetBlockHeadersDataEncodeDecode(t *testing.T) {
// Create a "random" hash for testing
var hash common.Hash
- for i, _ := range hash {
+ for i := range hash {
hash[i] = byte(i)
}
// Assemble some table driven tests
diff --git a/eth/sync.go b/eth/sync.go
index 2dd963fe31..13342a4f9c 100644
--- a/eth/sync.go
+++ b/eth/sync.go
@@ -46,7 +46,8 @@ type txsync struct {
// syncTransactions starts sending all currently pending transactions to the given peer.
func (pm *ProtocolManager) syncTransactions(p *peer) {
var txs types.Transactions
- for _, batch := range pm.txpool.Pending() {
+ pending, _ := pm.txpool.Pending()
+ for _, batch := range pending {
txs = append(txs, batch...)
}
if len(txs) == 0 {
@@ -180,7 +181,7 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil {
return
}
- atomic.StoreUint32(&pm.synced, 1) // Mark initial sync done
+ pm.setSynced() // Mark initial sync done
// If fast sync was enabled, and we synced up, disable it
if atomic.LoadUint32(&pm.fastSync) == 1 {
@@ -191,3 +192,10 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
}
}
}
+
+// setSynced sets the synced flag and notifies the light server if present
+func (pm *ProtocolManager) setSynced() {
+ if atomic.SwapUint32(&pm.synced, 1) == 0 && pm.lesServer != nil {
+ pm.lesServer.Synced()
+ }
+}
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 4d8d6ca9f4..06db3f458f 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -26,7 +26,6 @@ import (
"github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/common/hexutil"
"github.com/ubiq/go-ubiq/core/types"
- "github.com/ubiq/go-ubiq/core/vm"
"github.com/ubiq/go-ubiq/rlp"
"github.com/ubiq/go-ubiq/rpc"
"golang.org/x/net/context"
@@ -81,6 +80,8 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
err := ec.c.CallContext(ctx, &raw, method, args...)
if err != nil {
return nil, err
+ } else if len(raw) == 0 {
+ return nil, ethereum.NotFound
}
// Decode header and transactions.
var head *types.Header
@@ -112,7 +113,7 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
for i := range reqs {
reqs[i] = rpc.BatchElem{
Method: "eth_getUncleByBlockHashAndIndex",
- Args: []interface{}{body.Hash, fmt.Sprintf("%#x", i)},
+ Args: []interface{}{body.Hash, hexutil.EncodeUint64(uint64(i))},
Result: &uncles[i],
}
}
@@ -123,6 +124,9 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
if reqs[i].Error != nil {
return nil, reqs[i].Error
}
+ if uncles[i] == nil {
+ return nil, fmt.Errorf("got null header for uncle %d of block %x", i, body.Hash[:])
+ }
}
}
return types.NewBlockWithHeader(head).WithBody(body.Transactions, uncles), nil
@@ -132,6 +136,9 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
var head *types.Header
err := ec.c.CallContext(ctx, &head, "eth_getBlockByHash", hash, false)
+ if err == nil && head == nil {
+ err = ethereum.NotFound
+ }
return head, err
}
@@ -140,19 +147,31 @@ func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.He
func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
var head *types.Header
err := ec.c.CallContext(ctx, &head, "eth_getBlockByNumber", toBlockNumArg(number), false)
+ if err == nil && head == nil {
+ err = ethereum.NotFound
+ }
return head, err
}
// TransactionByHash returns the transaction with the given hash.
-func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, error) {
- var tx *types.Transaction
- err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByHash", hash)
- if err == nil {
- if _, r, _ := tx.RawSignatureValues(); r == nil {
- return nil, fmt.Errorf("server returned transaction without signature")
- }
+func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
+ var raw json.RawMessage
+ err = ec.c.CallContext(ctx, &raw, "eth_getTransactionByHash", hash)
+ if err != nil {
+ return nil, false, err
+ } else if len(raw) == 0 {
+ return nil, false, ethereum.NotFound
}
- return tx, err
+ if err := json.Unmarshal(raw, &tx); err != nil {
+ return nil, false, err
+ } else if _, r, _ := tx.RawSignatureValues(); r == nil {
+ return nil, false, fmt.Errorf("server returned transaction without signature")
+ }
+ var block struct{ BlockHash *common.Hash }
+ if err := json.Unmarshal(raw, &block); err != nil {
+ return nil, false, err
+ }
+ return tx, block.BlockHash == nil, nil
}
// TransactionCount returns the total number of transactions in the given block.
@@ -165,13 +184,11 @@ func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) (
// TransactionInBlock returns a single transaction at index in the given block.
func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
var tx *types.Transaction
- err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByBlockHashAndIndex", blockHash, index)
+ err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByBlockHashAndIndex", blockHash, hexutil.Uint64(index))
if err == nil {
- var signer types.Signer = types.HomesteadSigner{}
- if tx.Protected() {
- signer = types.NewEIP155Signer(tx.ChainId())
- }
- if _, r, _ := types.SignatureValues(signer, tx); r == nil {
+ if tx == nil {
+ return nil, ethereum.NotFound
+ } else if _, r, _ := tx.RawSignatureValues(); r == nil {
return nil, fmt.Errorf("server returned transaction without signature")
}
}
@@ -183,8 +200,12 @@ func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash,
func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
var r *types.Receipt
err := ec.c.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash)
- if err == nil && r != nil && len(r.PostState) == 0 {
- return nil, fmt.Errorf("server returned receipt without post state")
+ if err == nil {
+ if r == nil {
+ return nil, ethereum.NotFound
+ } else if len(r.PostState) == 0 {
+ return nil, fmt.Errorf("server returned receipt without post state")
+ }
}
return r, err
}
@@ -193,7 +214,7 @@ func toBlockNumArg(number *big.Int) string {
if number == nil {
return "latest"
}
- return fmt.Sprintf("%#x", number)
+ return hexutil.EncodeBig(number)
}
type rpcProgress struct {
@@ -272,14 +293,14 @@ func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumb
// Filters
// FilterLogs executes a filter query.
-func (ec *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]vm.Log, error) {
- var result []vm.Log
+func (ec *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
+ var result []types.Log
err := ec.c.CallContext(ctx, &result, "eth_getLogs", toFilterArg(q))
return result, err
}
// SubscribeFilterLogs subscribes to the results of a streaming filter query.
-func (ec *Client) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- vm.Log) (ethereum.Subscription, error) {
+func (ec *Client) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
return ec.c.EthSubscribe(ctx, ch, "logs", toFilterArg(q))
}
diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go
index 349f7e217f..a3d21e6e3f 100644
--- a/ethclient/ethclient_test.go
+++ b/ethclient/ethclient_test.go
@@ -21,9 +21,9 @@ import "github.com/ubiq/go-ubiq"
// Verify that Client implements the ethereum interfaces.
var (
_ = ethereum.ChainReader(&Client{})
+ _ = ethereum.TransactionReader(&Client{})
_ = ethereum.ChainStateReader(&Client{})
_ = ethereum.ChainSyncReader(&Client{})
- _ = ethereum.ChainHeadEventer(&Client{})
_ = ethereum.ContractCaller(&Client{})
_ = ethereum.GasEstimator(&Client{})
_ = ethereum.GasPricer(&Client{})
diff --git a/ethdb/database.go b/ethdb/database.go
index 46378f168f..e31c769a69 100644
--- a/ethdb/database.go
+++ b/ethdb/database.go
@@ -305,3 +305,55 @@ func (b *ldbBatch) Put(key, value []byte) error {
func (b *ldbBatch) Write() error {
return b.db.Write(b.b, nil)
}
+
+type table struct {
+ db Database
+ prefix string
+}
+
+// NewTable returns a Database object that prefixes all keys with a given
+// string.
+func NewTable(db Database, prefix string) Database {
+ return &table{
+ db: db,
+ prefix: prefix,
+ }
+}
+
+func (dt *table) Put(key []byte, value []byte) error {
+ return dt.db.Put(append([]byte(dt.prefix), key...), value)
+}
+
+func (dt *table) Get(key []byte) ([]byte, error) {
+ return dt.db.Get(append([]byte(dt.prefix), key...))
+}
+
+func (dt *table) Delete(key []byte) error {
+ return dt.db.Delete(append([]byte(dt.prefix), key...))
+}
+
+func (dt *table) Close() {
+ // Do nothing; don't close the underlying DB.
+}
+
+type tableBatch struct {
+ batch Batch
+ prefix string
+}
+
+// NewTableBatch returns a Batch object which prefixes all keys with a given string.
+func NewTableBatch(db Database, prefix string) Batch {
+ return &tableBatch{db.NewBatch(), prefix}
+}
+
+func (dt *table) NewBatch() Batch {
+ return &tableBatch{dt.db.NewBatch(), dt.prefix}
+}
+
+func (tb *tableBatch) Put(key, value []byte) error {
+ return tb.batch.Put(append([]byte(tb.prefix), key...), value)
+}
+
+func (tb *tableBatch) Write() error {
+ return tb.batch.Write()
+}
diff --git a/ethdb/memory_database.go b/ethdb/memory_database.go
index 1b0e2cf70a..ada8c9fafa 100644
--- a/ethdb/memory_database.go
+++ b/ethdb/memory_database.go
@@ -67,7 +67,7 @@ func (db *MemDatabase) Keys() [][]byte {
defer db.lock.RUnlock()
keys := [][]byte{}
- for key, _ := range db.db {
+ for key := range db.db {
keys = append(keys, []byte(key))
}
return keys
diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go
index d175d34c75..19d23d28dc 100644
--- a/ethstats/ethstats.go
+++ b/ethstats/ethstats.go
@@ -25,6 +25,7 @@ import (
"regexp"
"runtime"
"strconv"
+ "strings"
"time"
"github.com/ubiq/go-ubiq/common"
@@ -41,6 +42,10 @@ import (
"golang.org/x/net/websocket"
)
+// historyUpdateRange is the number of blocks a node should report upon login or
+// history request.
+const historyUpdateRange = 50
+
// Service implements an Ethereum netstats reporting daemon that pushes local
// chain statistics up to a monitoring server.
type Service struct {
@@ -53,6 +58,9 @@ type Service struct {
node string // Name of the node to display on the monitoring page
pass string // Password to authorize access to the monitoring page
host string // Remote address of the monitoring service
+
+ pongCh chan struct{} // Pong notifications are fed into this channel
+ histCh chan []uint64 // History request block numbers are fed into this channel
}
// New returns a monitoring service ready for stats reporting.
@@ -65,11 +73,13 @@ func New(url string, ethServ *eth.Ethereum, lesServ *les.LightEthereum) (*Servic
}
// Assemble and return the stats service
return &Service{
- eth: ethServ,
- les: lesServ,
- node: parts[1],
- pass: parts[3],
- host: parts[4],
+ eth: ethServ,
+ les: lesServ,
+ node: parts[1],
+ pass: parts[3],
+ host: parts[4],
+ pongCh: make(chan struct{}),
+ histCh: make(chan []uint64, 1),
}, nil
}
@@ -115,7 +125,11 @@ func (s *Service) loop() {
// Loop reporting until termination
for {
// Establish a websocket connection to the server and authenticate the node
- conn, err := websocket.Dial(fmt.Sprintf("wss://%s/api", s.host), "", "http://localhost/")
+ url := fmt.Sprintf("%s/api", s.host)
+ if !strings.Contains(url, "://") {
+ url = "wss://" + url
+ }
+ conn, err := websocket.Dial(url, "", "http://localhost/")
if err != nil {
glog.V(logger.Warn).Infof("Stats server unreachable: %v", err)
time.Sleep(10 * time.Second)
@@ -130,22 +144,34 @@ func (s *Service) loop() {
time.Sleep(10 * time.Second)
continue
}
- if err = s.report(in, out); err != nil {
+ go s.readLoop(conn, in)
+
+ // Send the initial stats so our node looks decent from the get go
+ if err = s.report(out); err != nil {
glog.V(logger.Warn).Infof("Initial stats report failed: %v", err)
conn.Close()
continue
}
+ if err = s.reportHistory(out, nil); err != nil {
+ glog.V(logger.Warn).Infof("History report failed: %v", err)
+ conn.Close()
+ continue
+ }
// Keep sending status updates until the connection breaks
fullReport := time.NewTicker(15 * time.Second)
for err == nil {
select {
case <-fullReport.C:
- if err = s.report(in, out); err != nil {
+ if err = s.report(out); err != nil {
glog.V(logger.Warn).Infof("Full stats report failed: %v", err)
}
- case head := <-headSub.Chan():
- if head == nil { // node stopped
+ case list := <-s.histCh:
+ if err = s.reportHistory(out, list); err != nil {
+ glog.V(logger.Warn).Infof("Block history report failed: %v", err)
+ }
+ case head, ok := <-headSub.Chan():
+ if !ok { // node stopped
conn.Close()
return
}
@@ -155,8 +181,8 @@ func (s *Service) loop() {
if err = s.reportPending(out); err != nil {
glog.V(logger.Warn).Infof("Post-block transaction stats report failed: %v", err)
}
- case ev := <-txSub.Chan():
- if ev == nil { // node stopped
+ case _, ok := <-txSub.Chan():
+ if !ok { // node stopped
conn.Close()
return
}
@@ -178,6 +204,76 @@ func (s *Service) loop() {
}
}
+// readLoop loops as long as the connection is alive and retrieves data packets
+// from the network socket. If any of them match an active request, it forwards
+// it, if they themselves are requests it initiates a reply, and lastly it drops
+// unknown packets.
+func (s *Service) readLoop(conn *websocket.Conn, in *json.Decoder) {
+ // If the read loop exists, close the connection
+ defer conn.Close()
+
+ for {
+ // Retrieve the next generic network packet and bail out on error
+ var msg map[string][]interface{}
+ if err := in.Decode(&msg); err != nil {
+ glog.V(logger.Warn).Infof("Failed to decode stats server message: %v", err)
+ return
+ }
+ if len(msg["emit"]) == 0 {
+ glog.V(logger.Warn).Infof("Stats server sent non-broadcast: %v", msg)
+ return
+ }
+ command, ok := msg["emit"][0].(string)
+ if !ok {
+ glog.V(logger.Warn).Infof("Invalid stats server message type: %v", msg["emit"][0])
+ return
+ }
+ // If the message is a ping reply, deliver (someone must be listening!)
+ if len(msg["emit"]) == 2 && command == "node-pong" {
+ select {
+ case s.pongCh <- struct{}{}:
+ // Pong delivered, continue listening
+ continue
+ default:
+ // Ping routine dead, abort
+ glog.V(logger.Warn).Infof("Stats server pinger seems to have died")
+ return
+ }
+ }
+ // If the message is a history request, forward to the event processor
+ if len(msg["emit"]) == 2 && command == "history" {
+ // Make sure the request is valid and doesn't crash us
+ request, ok := msg["emit"][1].(map[string]interface{})
+ if !ok {
+ glog.V(logger.Warn).Infof("Invalid history request: %v", msg["emit"][1])
+ return
+ }
+ list, ok := request["list"].([]interface{})
+ if !ok {
+ glog.V(logger.Warn).Infof("Invalid history block list: %v", request["list"])
+ return
+ }
+ // Convert the block number list to an integer list
+ numbers := make([]uint64, len(list))
+ for i, num := range list {
+ n, ok := num.(float64)
+ if !ok {
+ glog.V(logger.Warn).Infof("Invalid history block number: %v", num)
+ return
+ }
+ numbers[i] = uint64(n)
+ }
+ select {
+ case s.histCh <- numbers:
+ continue
+ default:
+ }
+ }
+ // Report anything else and continue
+ glog.V(logger.Info).Infof("Unknown stats message: %v", msg)
+ }
+}
+
// nodeInfo is the collection of metainformation about a node that is displayed
// on the monitoring page.
type nodeInfo struct {
@@ -190,6 +286,7 @@ type nodeInfo struct {
Os string `json:"os"`
OsVer string `json:"os_v"`
Client string `json:"client"`
+ History bool `json:"canUpdateHistory"`
}
// authMsg is the authentication infos needed to login to a monitoring server.
@@ -224,11 +321,12 @@ func (s *Service) login(in *json.Decoder, out *json.Encoder) error {
Os: runtime.GOOS,
OsVer: runtime.GOARCH,
Client: "0.1.1",
+ History: true,
},
Secret: s.pass,
}
login := map[string][]interface{}{
- "emit": []interface{}{"hello", auth},
+ "emit": {"hello", auth},
}
if err := out.Encode(login); err != nil {
return err
@@ -244,8 +342,8 @@ func (s *Service) login(in *json.Decoder, out *json.Encoder) error {
// report collects all possible data to report and send it to the stats server.
// This should only be used on reconnects or rarely to avoid overloading the
// server. Use the individual methods for reporting subscribed events.
-func (s *Service) report(in *json.Decoder, out *json.Encoder) error {
- if err := s.reportLatency(in, out); err != nil {
+func (s *Service) report(out *json.Encoder) error {
+ if err := s.reportLatency(out); err != nil {
return err
}
if err := s.reportBlock(out, nil); err != nil {
@@ -262,12 +360,12 @@ func (s *Service) report(in *json.Decoder, out *json.Encoder) error {
// reportLatency sends a ping request to the server, measures the RTT time and
// finally sends a latency update.
-func (s *Service) reportLatency(in *json.Decoder, out *json.Encoder) error {
+func (s *Service) reportLatency(out *json.Encoder) error {
// Send the current time to the ethstats server
start := time.Now()
ping := map[string][]interface{}{
- "emit": []interface{}{"node-ping", map[string]string{
+ "emit": {"node-ping", map[string]string{
"id": s.node,
"clientTime": start.String(),
}},
@@ -276,27 +374,28 @@ func (s *Service) reportLatency(in *json.Decoder, out *json.Encoder) error {
return err
}
// Wait for the pong request to arrive back
- var pong map[string][]interface{}
- if err := in.Decode(&pong); err != nil || len(pong["emit"]) != 2 || pong["emit"][0].(string) != "node-pong" {
- return errors.New("unexpected ping reply")
+ select {
+ case <-s.pongCh:
+ // Pong delivered, report the latency
+ case <-time.After(3 * time.Second):
+ // Ping timeout, abort
+ return errors.New("ping timed out")
}
// Send back the measured latency
latency := map[string][]interface{}{
- "emit": []interface{}{"latency", map[string]string{
+ "emit": {"latency", map[string]string{
"id": s.node,
"latency": strconv.Itoa(int((time.Since(start) / time.Duration(2)).Nanoseconds() / 1000000)),
}},
}
- if err := out.Encode(latency); err != nil {
- return err
- }
- return nil
+ return out.Encode(latency)
}
// blockStats is the information to report about individual blocks.
type blockStats struct {
Number *big.Int `json:"number"`
Hash common.Hash `json:"hash"`
+ Timestamp *big.Int `json:"timestamp"`
Miner common.Address `json:"miner"`
GasUsed *big.Int `json:"gasUsed"`
GasLimit *big.Int `json:"gasLimit"`
@@ -330,9 +429,23 @@ func (s uncleStats) MarshalJSON() ([]byte, error) {
// reportBlock retrieves the current chain head and repors it to the stats server.
func (s *Service) reportBlock(out *json.Encoder, block *types.Block) error {
- // Gather the head block infos from the local blockchain
+ // Assemble the block stats report and send it to the server
+ stats := map[string]interface{}{
+ "id": s.node,
+ "block": s.assembleBlockStats(block),
+ }
+ report := map[string][]interface{}{
+ "emit": {"block", stats},
+ }
+ return out.Encode(report)
+}
+
+// assembleBlockStats retrieves any required metadata to report a single block
+// and assembles the block stats. If block is nil, the current head is processed.
+func (s *Service) assembleBlockStats(block *types.Block) *blockStats {
+ // Gather the block infos from the local blockchain
var (
- head *types.Header
+ header *types.Header
td *big.Int
txs []*types.Transaction
uncles []*types.Header
@@ -342,42 +455,77 @@ func (s *Service) reportBlock(out *json.Encoder, block *types.Block) error {
if block == nil {
block = s.eth.BlockChain().CurrentBlock()
}
- head = block.Header()
- td = s.eth.BlockChain().GetTd(head.Hash(), head.Number.Uint64())
+ header = block.Header()
+ td = s.eth.BlockChain().GetTd(header.Hash(), header.Number.Uint64())
txs = block.Transactions()
uncles = block.Uncles()
} else {
// Light nodes would need on-demand lookups for transactions/uncles, skip
if block != nil {
- head = block.Header()
+ header = block.Header()
+ } else {
+ header = s.les.BlockChain().CurrentHeader()
+ }
+ td = s.les.BlockChain().GetTd(header.Hash(), header.Number.Uint64())
+ }
+ // Assemble and return the block stats
+ return &blockStats{
+ Number: header.Number,
+ Hash: header.Hash(),
+ Timestamp: header.Time,
+ Miner: header.Coinbase,
+ GasUsed: new(big.Int).Set(header.GasUsed),
+ GasLimit: new(big.Int).Set(header.GasLimit),
+ Diff: header.Difficulty.String(),
+ TotalDiff: td.String(),
+ Txs: txs,
+ Uncles: uncles,
+ }
+}
+
+// reportHistory retrieves the most recent batch of blocks and reports it to the
+// stats server.
+func (s *Service) reportHistory(out *json.Encoder, list []uint64) error {
+ // Figure out the indexes that need reporting
+ indexes := make([]uint64, 0, historyUpdateRange)
+ if len(list) > 0 {
+ // Specific indexes requested, send them back in particular
+ indexes = append(indexes, list...)
+ } else {
+ // No indexes requested, send back the top ones
+ var head *types.Header
+ if s.eth != nil {
+ head = s.eth.BlockChain().CurrentHeader()
} else {
head = s.les.BlockChain().CurrentHeader()
}
- td = s.les.BlockChain().GetTd(head.Hash(), head.Number.Uint64())
+ start := head.Number.Int64() - historyUpdateRange
+ if start < 0 {
+ start = 0
+ }
+ for i := uint64(start); i <= head.Number.Uint64(); i++ {
+ indexes = append(indexes, i)
+ }
}
- // Assemble the block stats report and send it to the server
+ // Gather the batch of blocks to report
+ history := make([]*blockStats, len(indexes))
+ for i, number := range indexes {
+ if s.eth != nil {
+ history[i] = s.assembleBlockStats(s.eth.BlockChain().GetBlockByNumber(number))
+ } else {
+ history[i] = s.assembleBlockStats(types.NewBlockWithHeader(s.les.BlockChain().GetHeaderByNumber(number)))
+ }
+ }
+ // Assemble the history report and send it to the server
stats := map[string]interface{}{
- "id": s.node,
- "block": &blockStats{
- Number: head.Number,
- Hash: head.Hash(),
- Miner: head.Coinbase,
- GasUsed: new(big.Int).Set(head.GasUsed),
- GasLimit: new(big.Int).Set(head.GasLimit),
- Diff: head.Difficulty.String(),
- TotalDiff: td.String(),
- Txs: txs,
- Uncles: uncles,
- },
+ "id": s.node,
+ "history": history,
}
report := map[string][]interface{}{
- "emit": []interface{}{"block", stats},
+ "emit": {"history", stats},
}
- if err := out.Encode(report); err != nil {
- return err
- }
- return nil
+ return out.Encode(report)
}
// pendStats is the information to report about pending transactions.
@@ -403,12 +551,9 @@ func (s *Service) reportPending(out *json.Encoder) error {
},
}
report := map[string][]interface{}{
- "emit": []interface{}{"pending", stats},
+ "emit": {"pending", stats},
}
- if err := out.Encode(report); err != nil {
- return err
- }
- return nil
+ return out.Encode(report)
}
// blockStats is the information to report about the local node.
@@ -457,10 +602,7 @@ func (s *Service) reportStats(out *json.Encoder) error {
},
}
report := map[string][]interface{}{
- "emit": []interface{}{"stats", stats},
+ "emit": {"stats", stats},
}
- if err := out.Encode(report); err != nil {
- return err
- }
- return nil
+ return out.Encode(report)
}
diff --git a/event/event_test.go b/event/event_test.go
index 3940293013..2c56ecf29f 100644
--- a/event/event_test.go
+++ b/event/event_test.go
@@ -144,7 +144,7 @@ func TestMuxConcurrent(t *testing.T) {
func emptySubscriber(mux *TypeMux, types ...interface{}) {
s := mux.Subscribe(testEvent(0))
go func() {
- for _ = range s.Chan() {
+ for range s.Chan() {
}
}()
}
@@ -187,7 +187,7 @@ func BenchmarkChanSend(b *testing.B) {
c := make(chan interface{})
closed := make(chan struct{})
go func() {
- for _ = range c {
+ for range c {
}
}()
diff --git a/event/filter/generic_filter.go b/event/filter/generic_filter.go
index 27f35920d4..d679b8bfa8 100644
--- a/event/filter/generic_filter.go
+++ b/event/filter/generic_filter.go
@@ -34,7 +34,7 @@ func (self Generic) Compare(f Filter) bool {
strMatch = false
}
- for k, _ := range self.Data {
+ for k := range self.Data {
if _, ok := filter.Data[k]; !ok {
return false
}
diff --git a/generators/defaults.go b/generators/defaults.go
deleted file mode 100644
index ab9d4a52d7..0000000000
--- a/generators/defaults.go
+++ /dev/null
@@ -1,81 +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 .
-
-//go:generate go run defaults.go default.json defs.go
-
-package main //build !none
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
-)
-
-func fatalf(str string, v ...interface{}) {
- fmt.Fprintf(os.Stderr, str, v...)
- os.Exit(1)
-}
-
-type setting struct {
- Value int64 `json:"v"`
- Comment string `json:"d"`
-}
-
-func main() {
- if len(os.Args) < 3 {
- fatalf("usage %s