mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 22:26:42 +00:00
new files
This commit is contained in:
parent
141571ce98
commit
b221437ad4
137 changed files with 180037 additions and 0 deletions
27
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/LICENSE
generated
vendored
Normal file
27
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2009 Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
84
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/dce.go
generated
vendored
Normal file
84
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/dce.go
generated
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// A Domain represents a Version 2 domain
|
||||
type Domain byte
|
||||
|
||||
// Domain constants for DCE Security (Version 2) UUIDs.
|
||||
const (
|
||||
Person = Domain(0)
|
||||
Group = Domain(1)
|
||||
Org = Domain(2)
|
||||
)
|
||||
|
||||
// NewDCESecurity returns a DCE Security (Version 2) UUID.
|
||||
//
|
||||
// The domain should be one of Person, Group or Org.
|
||||
// On a POSIX system the id should be the users UID for the Person
|
||||
// domain and the users GID for the Group. The meaning of id for
|
||||
// the domain Org or on non-POSIX systems is site defined.
|
||||
//
|
||||
// For a given domain/id pair the same token may be returned for up to
|
||||
// 7 minutes and 10 seconds.
|
||||
func NewDCESecurity(domain Domain, id uint32) UUID {
|
||||
uuid := NewUUID()
|
||||
if uuid != nil {
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
|
||||
uuid[9] = byte(domain)
|
||||
binary.BigEndian.PutUint32(uuid[0:], id)
|
||||
}
|
||||
return uuid
|
||||
}
|
||||
|
||||
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
|
||||
// domain with the id returned by os.Getuid.
|
||||
//
|
||||
// NewDCEPerson(Person, uint32(os.Getuid()))
|
||||
func NewDCEPerson() UUID {
|
||||
return NewDCESecurity(Person, uint32(os.Getuid()))
|
||||
}
|
||||
|
||||
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
|
||||
// domain with the id returned by os.Getgid.
|
||||
//
|
||||
// NewDCEGroup(Group, uint32(os.Getgid()))
|
||||
func NewDCEGroup() UUID {
|
||||
return NewDCESecurity(Group, uint32(os.Getgid()))
|
||||
}
|
||||
|
||||
// Domain returns the domain for a Version 2 UUID or false.
|
||||
func (uuid UUID) Domain() (Domain, bool) {
|
||||
if v, _ := uuid.Version(); v != 2 {
|
||||
return 0, false
|
||||
}
|
||||
return Domain(uuid[9]), true
|
||||
}
|
||||
|
||||
// Id returns the id for a Version 2 UUID or false.
|
||||
func (uuid UUID) Id() (uint32, bool) {
|
||||
if v, _ := uuid.Version(); v != 2 {
|
||||
return 0, false
|
||||
}
|
||||
return binary.BigEndian.Uint32(uuid[0:4]), true
|
||||
}
|
||||
|
||||
func (d Domain) String() string {
|
||||
switch d {
|
||||
case Person:
|
||||
return "Person"
|
||||
case Group:
|
||||
return "Group"
|
||||
case Org:
|
||||
return "Org"
|
||||
}
|
||||
return fmt.Sprintf("Domain%d", int(d))
|
||||
}
|
||||
8
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/doc.go
generated
vendored
Normal file
8
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The uuid package generates and inspects UUIDs.
|
||||
//
|
||||
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services.
|
||||
package uuid
|
||||
53
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/hash.go
generated
vendored
Normal file
53
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/hash.go
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Well known Name Space IDs and UUIDs
|
||||
var (
|
||||
NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||
NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
|
||||
NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
|
||||
NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
|
||||
NIL = Parse("00000000-0000-0000-0000-000000000000")
|
||||
)
|
||||
|
||||
// NewHash returns a new UUID dervied from the hash of space concatenated with
|
||||
// data generated by h. The hash should be at least 16 byte in length. The
|
||||
// first 16 bytes of the hash are used to form the UUID. The version of the
|
||||
// UUID will be the lower 4 bits of version. NewHash is used to implement
|
||||
// NewMD5 and NewSHA1.
|
||||
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
|
||||
h.Reset()
|
||||
h.Write(space)
|
||||
h.Write([]byte(data))
|
||||
s := h.Sum(nil)
|
||||
uuid := make([]byte, 16)
|
||||
copy(uuid, s)
|
||||
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
|
||||
return uuid
|
||||
}
|
||||
|
||||
// NewMD5 returns a new MD5 (Version 3) UUID based on the
|
||||
// supplied name space and data.
|
||||
//
|
||||
// NewHash(md5.New(), space, data, 3)
|
||||
func NewMD5(space UUID, data []byte) UUID {
|
||||
return NewHash(md5.New(), space, data, 3)
|
||||
}
|
||||
|
||||
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
|
||||
// supplied name space and data.
|
||||
//
|
||||
// NewHash(sha1.New(), space, data, 5)
|
||||
func NewSHA1(space UUID, data []byte) UUID {
|
||||
return NewHash(sha1.New(), space, data, 5)
|
||||
}
|
||||
101
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/node.go
generated
vendored
Normal file
101
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/node.go
generated
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import "net"
|
||||
|
||||
var (
|
||||
interfaces []net.Interface // cached list of interfaces
|
||||
ifname string // name of interface being used
|
||||
nodeID []byte // hardware for version 1 UUIDs
|
||||
)
|
||||
|
||||
// NodeInterface returns the name of the interface from which the NodeID was
|
||||
// derived. The interface "user" is returned if the NodeID was set by
|
||||
// SetNodeID.
|
||||
func NodeInterface() string {
|
||||
return ifname
|
||||
}
|
||||
|
||||
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
|
||||
// If name is "" then the first usable interface found will be used or a random
|
||||
// Node ID will be generated. If a named interface cannot be found then false
|
||||
// is returned.
|
||||
//
|
||||
// SetNodeInterface never fails when name is "".
|
||||
func SetNodeInterface(name string) bool {
|
||||
if interfaces == nil {
|
||||
var err error
|
||||
interfaces, err = net.Interfaces()
|
||||
if err != nil && name != "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, ifs := range interfaces {
|
||||
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
|
||||
if setNodeID(ifs.HardwareAddr) {
|
||||
ifname = ifs.Name
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We found no interfaces with a valid hardware address. If name
|
||||
// does not specify a specific interface generate a random Node ID
|
||||
// (section 4.1.6)
|
||||
if name == "" {
|
||||
if nodeID == nil {
|
||||
nodeID = make([]byte, 6)
|
||||
}
|
||||
randomBits(nodeID)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
|
||||
// if not already set.
|
||||
func NodeID() []byte {
|
||||
if nodeID == nil {
|
||||
SetNodeInterface("")
|
||||
}
|
||||
nid := make([]byte, 6)
|
||||
copy(nid, nodeID)
|
||||
return nid
|
||||
}
|
||||
|
||||
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
|
||||
// of id are used. If id is less than 6 bytes then false is returned and the
|
||||
// Node ID is not set.
|
||||
func SetNodeID(id []byte) bool {
|
||||
if setNodeID(id) {
|
||||
ifname = "user"
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func setNodeID(id []byte) bool {
|
||||
if len(id) < 6 {
|
||||
return false
|
||||
}
|
||||
if nodeID == nil {
|
||||
nodeID = make([]byte, 6)
|
||||
}
|
||||
copy(nodeID, id)
|
||||
return true
|
||||
}
|
||||
|
||||
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
|
||||
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) NodeID() []byte {
|
||||
if len(uuid) != 16 {
|
||||
return nil
|
||||
}
|
||||
node := make([]byte, 6)
|
||||
copy(node, uuid[10:])
|
||||
return node
|
||||
}
|
||||
132
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/time.go
generated
vendored
Normal file
132
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/time.go
generated
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
|
||||
// 1582.
|
||||
type Time int64
|
||||
|
||||
const (
|
||||
lillian = 2299160 // Julian day of 15 Oct 1582
|
||||
unix = 2440587 // Julian day of 1 Jan 1970
|
||||
epoch = unix - lillian // Days between epochs
|
||||
g1582 = epoch * 86400 // seconds between epochs
|
||||
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
|
||||
)
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
lasttime uint64 // last time we returned
|
||||
clock_seq uint16 // clock sequence for this run
|
||||
|
||||
timeNow = time.Now // for testing
|
||||
)
|
||||
|
||||
// UnixTime converts t the number of seconds and nanoseconds using the Unix
|
||||
// epoch of 1 Jan 1970.
|
||||
func (t Time) UnixTime() (sec, nsec int64) {
|
||||
sec = int64(t - g1582ns100)
|
||||
nsec = (sec % 10000000) * 100
|
||||
sec /= 10000000
|
||||
return sec, nsec
|
||||
}
|
||||
|
||||
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
|
||||
// adjusts the clock sequence as needed. An error is returned if the current
|
||||
// time cannot be determined.
|
||||
func GetTime() (Time, error) {
|
||||
defer mu.Unlock()
|
||||
mu.Lock()
|
||||
return getTime()
|
||||
}
|
||||
|
||||
func getTime() (Time, error) {
|
||||
t := timeNow()
|
||||
|
||||
// If we don't have a clock sequence already, set one.
|
||||
if clock_seq == 0 {
|
||||
setClockSequence(-1)
|
||||
}
|
||||
now := uint64(t.UnixNano()/100) + g1582ns100
|
||||
|
||||
// If time has gone backwards with this clock sequence then we
|
||||
// increment the clock sequence
|
||||
if now <= lasttime {
|
||||
clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000
|
||||
}
|
||||
lasttime = now
|
||||
return Time(now), nil
|
||||
}
|
||||
|
||||
// ClockSequence returns the current clock sequence, generating one if not
|
||||
// already set. The clock sequence is only used for Version 1 UUIDs.
|
||||
//
|
||||
// The uuid package does not use global static storage for the clock sequence or
|
||||
// the last time a UUID was generated. Unless SetClockSequence a new random
|
||||
// clock sequence is generated the first time a clock sequence is requested by
|
||||
// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated
|
||||
// for
|
||||
func ClockSequence() int {
|
||||
defer mu.Unlock()
|
||||
mu.Lock()
|
||||
return clockSequence()
|
||||
}
|
||||
|
||||
func clockSequence() int {
|
||||
if clock_seq == 0 {
|
||||
setClockSequence(-1)
|
||||
}
|
||||
return int(clock_seq & 0x3fff)
|
||||
}
|
||||
|
||||
// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to
|
||||
// -1 causes a new sequence to be generated.
|
||||
func SetClockSequence(seq int) {
|
||||
defer mu.Unlock()
|
||||
mu.Lock()
|
||||
setClockSequence(seq)
|
||||
}
|
||||
|
||||
func setClockSequence(seq int) {
|
||||
if seq == -1 {
|
||||
var b [2]byte
|
||||
randomBits(b[:]) // clock sequence
|
||||
seq = int(b[0])<<8 | int(b[1])
|
||||
}
|
||||
old_seq := clock_seq
|
||||
clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant
|
||||
if old_seq != clock_seq {
|
||||
lasttime = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
|
||||
// uuid. It returns false if uuid is not valid. The time is only well defined
|
||||
// for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) Time() (Time, bool) {
|
||||
if len(uuid) != 16 {
|
||||
return 0, false
|
||||
}
|
||||
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
||||
return Time(time), true
|
||||
}
|
||||
|
||||
// ClockSequence returns the clock sequence encoded in uuid. It returns false
|
||||
// if uuid is not valid. The clock sequence is only well defined for version 1
|
||||
// and 2 UUIDs.
|
||||
func (uuid UUID) ClockSequence() (int, bool) {
|
||||
if len(uuid) != 16 {
|
||||
return 0, false
|
||||
}
|
||||
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true
|
||||
}
|
||||
43
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/util.go
generated
vendored
Normal file
43
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// randomBits completely fills slice b with random data.
|
||||
func randomBits(b []byte) {
|
||||
if _, err := io.ReadFull(rander, b); err != nil {
|
||||
panic(err.Error()) // rand should never fail
|
||||
}
|
||||
}
|
||||
|
||||
// xvalues returns the value of a byte as a hexadecimal digit or 255.
|
||||
var xvalues = []byte{
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
}
|
||||
|
||||
// xtob converts the the first two hex bytes of x into a byte.
|
||||
func xtob(x string) (byte, bool) {
|
||||
b1 := xvalues[x[0]]
|
||||
b2 := xvalues[x[1]]
|
||||
return (b1 << 4) | b2, b1 != 255 && b2 != 255
|
||||
}
|
||||
163
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid.go
generated
vendored
Normal file
163
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid.go
generated
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
|
||||
// 4122.
|
||||
type UUID []byte
|
||||
|
||||
// A Version represents a UUIDs version.
|
||||
type Version byte
|
||||
|
||||
// A Variant represents a UUIDs variant.
|
||||
type Variant byte
|
||||
|
||||
// Constants returned by Variant.
|
||||
const (
|
||||
Invalid = Variant(iota) // Invalid UUID
|
||||
RFC4122 // The variant specified in RFC4122
|
||||
Reserved // Reserved, NCS backward compatibility.
|
||||
Microsoft // Reserved, Microsoft Corporation backward compatibility.
|
||||
Future // Reserved for future definition.
|
||||
)
|
||||
|
||||
var rander = rand.Reader // random function
|
||||
|
||||
// New returns a new random (version 4) UUID as a string. It is a convenience
|
||||
// function for NewRandom().String().
|
||||
func New() string {
|
||||
return NewRandom().String()
|
||||
}
|
||||
|
||||
// Parse decodes s into a UUID or returns nil. Both the UUID form of
|
||||
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded.
|
||||
func Parse(s string) UUID {
|
||||
if len(s) == 36+9 {
|
||||
if strings.ToLower(s[:9]) != "urn:uuid:" {
|
||||
return nil
|
||||
}
|
||||
s = s[9:]
|
||||
} else if len(s) != 36 {
|
||||
return nil
|
||||
}
|
||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||
return nil
|
||||
}
|
||||
uuid := make([]byte, 16)
|
||||
for i, x := range []int{
|
||||
0, 2, 4, 6,
|
||||
9, 11,
|
||||
14, 16,
|
||||
19, 21,
|
||||
24, 26, 28, 30, 32, 34} {
|
||||
if v, ok := xtob(s[x:]); !ok {
|
||||
return nil
|
||||
} else {
|
||||
uuid[i] = v
|
||||
}
|
||||
}
|
||||
return uuid
|
||||
}
|
||||
|
||||
// Equal returns true if uuid1 and uuid2 are equal.
|
||||
func Equal(uuid1, uuid2 UUID) bool {
|
||||
return bytes.Equal(uuid1, uuid2)
|
||||
}
|
||||
|
||||
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
// , or "" if uuid is invalid.
|
||||
func (uuid UUID) String() string {
|
||||
if uuid == nil || len(uuid) != 16 {
|
||||
return ""
|
||||
}
|
||||
b := []byte(uuid)
|
||||
return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
|
||||
b[:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||
}
|
||||
|
||||
// URN returns the RFC 2141 URN form of uuid,
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
|
||||
func (uuid UUID) URN() string {
|
||||
if uuid == nil || len(uuid) != 16 {
|
||||
return ""
|
||||
}
|
||||
b := []byte(uuid)
|
||||
return fmt.Sprintf("urn:uuid:%08x-%04x-%04x-%04x-%012x",
|
||||
b[:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||
}
|
||||
|
||||
// Variant returns the variant encoded in uuid. It returns Invalid if
|
||||
// uuid is invalid.
|
||||
func (uuid UUID) Variant() Variant {
|
||||
if len(uuid) != 16 {
|
||||
return Invalid
|
||||
}
|
||||
switch {
|
||||
case (uuid[8] & 0xc0) == 0x80:
|
||||
return RFC4122
|
||||
case (uuid[8] & 0xe0) == 0xc0:
|
||||
return Microsoft
|
||||
case (uuid[8] & 0xe0) == 0xe0:
|
||||
return Future
|
||||
default:
|
||||
return Reserved
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Version returns the verison of uuid. It returns false if uuid is not
|
||||
// valid.
|
||||
func (uuid UUID) Version() (Version, bool) {
|
||||
if len(uuid) != 16 {
|
||||
return 0, false
|
||||
}
|
||||
return Version(uuid[6] >> 4), true
|
||||
}
|
||||
|
||||
func (v Version) String() string {
|
||||
if v > 15 {
|
||||
return fmt.Sprintf("BAD_VERSION_%d", v)
|
||||
}
|
||||
return fmt.Sprintf("VERSION_%d", v)
|
||||
}
|
||||
|
||||
func (v Variant) String() string {
|
||||
switch v {
|
||||
case RFC4122:
|
||||
return "RFC4122"
|
||||
case Reserved:
|
||||
return "Reserved"
|
||||
case Microsoft:
|
||||
return "Microsoft"
|
||||
case Future:
|
||||
return "Future"
|
||||
case Invalid:
|
||||
return "Invalid"
|
||||
}
|
||||
return fmt.Sprintf("BadVariant%d", int(v))
|
||||
}
|
||||
|
||||
// SetRand sets the random number generator to r, which implents io.Reader.
|
||||
// If r.Read returns an error when the package requests random data then
|
||||
// a panic will be issued.
|
||||
//
|
||||
// Calling SetRand with nil sets the random number generator to the default
|
||||
// generator.
|
||||
func SetRand(r io.Reader) {
|
||||
if r == nil {
|
||||
rander = rand.Reader
|
||||
return
|
||||
}
|
||||
rander = r
|
||||
}
|
||||
390
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid_test.go
generated
vendored
Normal file
390
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type test struct {
|
||||
in string
|
||||
version Version
|
||||
variant Variant
|
||||
isuuid bool
|
||||
}
|
||||
|
||||
var tests = []test{
|
||||
{"f47ac10b-58cc-0372-8567-0e02b2c3d479", 0, RFC4122, true},
|
||||
{"f47ac10b-58cc-1372-8567-0e02b2c3d479", 1, RFC4122, true},
|
||||
{"f47ac10b-58cc-2372-8567-0e02b2c3d479", 2, RFC4122, true},
|
||||
{"f47ac10b-58cc-3372-8567-0e02b2c3d479", 3, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-5372-8567-0e02b2c3d479", 5, RFC4122, true},
|
||||
{"f47ac10b-58cc-6372-8567-0e02b2c3d479", 6, RFC4122, true},
|
||||
{"f47ac10b-58cc-7372-8567-0e02b2c3d479", 7, RFC4122, true},
|
||||
{"f47ac10b-58cc-8372-8567-0e02b2c3d479", 8, RFC4122, true},
|
||||
{"f47ac10b-58cc-9372-8567-0e02b2c3d479", 9, RFC4122, true},
|
||||
{"f47ac10b-58cc-a372-8567-0e02b2c3d479", 10, RFC4122, true},
|
||||
{"f47ac10b-58cc-b372-8567-0e02b2c3d479", 11, RFC4122, true},
|
||||
{"f47ac10b-58cc-c372-8567-0e02b2c3d479", 12, RFC4122, true},
|
||||
{"f47ac10b-58cc-d372-8567-0e02b2c3d479", 13, RFC4122, true},
|
||||
{"f47ac10b-58cc-e372-8567-0e02b2c3d479", 14, RFC4122, true},
|
||||
{"f47ac10b-58cc-f372-8567-0e02b2c3d479", 15, RFC4122, true},
|
||||
|
||||
{"urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-1567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-2567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-3567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-4567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-5567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-6567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-7567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-9567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-a567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-b567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-c567-0e02b2c3d479", 4, Microsoft, true},
|
||||
{"f47ac10b-58cc-4372-d567-0e02b2c3d479", 4, Microsoft, true},
|
||||
{"f47ac10b-58cc-4372-e567-0e02b2c3d479", 4, Future, true},
|
||||
{"f47ac10b-58cc-4372-f567-0e02b2c3d479", 4, Future, true},
|
||||
|
||||
{"f47ac10b158cc-5372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc25372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc-53723a567-0e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc-5372-a56740e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc-5372-a567-0e02-2c3d479", 0, Invalid, false},
|
||||
{"g47ac10b-58cc-4372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||
}
|
||||
|
||||
var constants = []struct {
|
||||
c interface{}
|
||||
name string
|
||||
}{
|
||||
{Person, "Person"},
|
||||
{Group, "Group"},
|
||||
{Org, "Org"},
|
||||
{Invalid, "Invalid"},
|
||||
{RFC4122, "RFC4122"},
|
||||
{Reserved, "Reserved"},
|
||||
{Microsoft, "Microsoft"},
|
||||
{Future, "Future"},
|
||||
{Domain(17), "Domain17"},
|
||||
{Variant(42), "BadVariant42"},
|
||||
}
|
||||
|
||||
func testTest(t *testing.T, in string, tt test) {
|
||||
uuid := Parse(in)
|
||||
if ok := (uuid != nil); ok != tt.isuuid {
|
||||
t.Errorf("Parse(%s) got %v expected %v\b", in, ok, tt.isuuid)
|
||||
}
|
||||
if uuid == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if v := uuid.Variant(); v != tt.variant {
|
||||
t.Errorf("Variant(%s) got %d expected %d\b", in, v, tt.variant)
|
||||
}
|
||||
if v, _ := uuid.Version(); v != tt.version {
|
||||
t.Errorf("Version(%s) got %d expected %d\b", in, v, tt.version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUUID(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
testTest(t, tt.in, tt)
|
||||
testTest(t, strings.ToUpper(tt.in), tt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConstants(t *testing.T) {
|
||||
for x, tt := range constants {
|
||||
v, ok := tt.c.(fmt.Stringer)
|
||||
if !ok {
|
||||
t.Errorf("%x: %v: not a stringer", x, v)
|
||||
} else if s := v.String(); s != tt.name {
|
||||
v, _ := tt.c.(int)
|
||||
t.Errorf("%x: Constant %T:%d gives %q, expected %q\n", x, tt.c, v, s, tt.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomUUID(t *testing.T) {
|
||||
m := make(map[string]bool)
|
||||
for x := 1; x < 32; x++ {
|
||||
uuid := NewRandom()
|
||||
s := uuid.String()
|
||||
if m[s] {
|
||||
t.Errorf("NewRandom returned duplicated UUID %s\n", s)
|
||||
}
|
||||
m[s] = true
|
||||
if v, _ := uuid.Version(); v != 4 {
|
||||
t.Errorf("Random UUID of version %s\n", v)
|
||||
}
|
||||
if uuid.Variant() != RFC4122 {
|
||||
t.Errorf("Random UUID is variant %d\n", uuid.Variant())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
m := make(map[string]bool)
|
||||
for x := 1; x < 32; x++ {
|
||||
s := New()
|
||||
if m[s] {
|
||||
t.Errorf("New returned duplicated UUID %s\n", s)
|
||||
}
|
||||
m[s] = true
|
||||
uuid := Parse(s)
|
||||
if uuid == nil {
|
||||
t.Errorf("New returned %q which does not decode\n", s)
|
||||
continue
|
||||
}
|
||||
if v, _ := uuid.Version(); v != 4 {
|
||||
t.Errorf("Random UUID of version %s\n", v)
|
||||
}
|
||||
if uuid.Variant() != RFC4122 {
|
||||
t.Errorf("Random UUID is variant %d\n", uuid.Variant())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func clockSeq(t *testing.T, uuid UUID) int {
|
||||
seq, ok := uuid.ClockSequence()
|
||||
if !ok {
|
||||
t.Fatalf("%s: invalid clock sequence\n", uuid)
|
||||
}
|
||||
return seq
|
||||
}
|
||||
|
||||
func TestClockSeq(t *testing.T) {
|
||||
// Fake time.Now for this test to return a monotonically advancing time; restore it at end.
|
||||
defer func(orig func() time.Time) { timeNow = orig }(timeNow)
|
||||
monTime := time.Now()
|
||||
timeNow = func() time.Time {
|
||||
monTime = monTime.Add(1 * time.Second)
|
||||
return monTime
|
||||
}
|
||||
|
||||
SetClockSequence(-1)
|
||||
uuid1 := NewUUID()
|
||||
uuid2 := NewUUID()
|
||||
|
||||
if clockSeq(t, uuid1) != clockSeq(t, uuid2) {
|
||||
t.Errorf("clock sequence %d != %d\n", clockSeq(t, uuid1), clockSeq(t, uuid2))
|
||||
}
|
||||
|
||||
SetClockSequence(-1)
|
||||
uuid2 = NewUUID()
|
||||
|
||||
// Just on the very off chance we generated the same sequence
|
||||
// two times we try again.
|
||||
if clockSeq(t, uuid1) == clockSeq(t, uuid2) {
|
||||
SetClockSequence(-1)
|
||||
uuid2 = NewUUID()
|
||||
}
|
||||
if clockSeq(t, uuid1) == clockSeq(t, uuid2) {
|
||||
t.Errorf("Duplicate clock sequence %d\n", clockSeq(t, uuid1))
|
||||
}
|
||||
|
||||
SetClockSequence(0x1234)
|
||||
uuid1 = NewUUID()
|
||||
if seq := clockSeq(t, uuid1); seq != 0x1234 {
|
||||
t.Errorf("%s: expected seq 0x1234 got 0x%04x\n", uuid1, seq)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoding(t *testing.T) {
|
||||
text := "7d444840-9dc0-11d1-b245-5ffdce74fad2"
|
||||
urn := "urn:uuid:7d444840-9dc0-11d1-b245-5ffdce74fad2"
|
||||
data := UUID{
|
||||
0x7d, 0x44, 0x48, 0x40,
|
||||
0x9d, 0xc0,
|
||||
0x11, 0xd1,
|
||||
0xb2, 0x45,
|
||||
0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2,
|
||||
}
|
||||
if v := data.String(); v != text {
|
||||
t.Errorf("%x: encoded to %s, expected %s\n", data, v, text)
|
||||
}
|
||||
if v := data.URN(); v != urn {
|
||||
t.Errorf("%x: urn is %s, expected %s\n", data, v, urn)
|
||||
}
|
||||
|
||||
uuid := Parse(text)
|
||||
if !Equal(uuid, data) {
|
||||
t.Errorf("%s: decoded to %s, expected %s\n", text, uuid, data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersion1(t *testing.T) {
|
||||
uuid1 := NewUUID()
|
||||
uuid2 := NewUUID()
|
||||
|
||||
if Equal(uuid1, uuid2) {
|
||||
t.Errorf("%s:duplicate uuid\n", uuid1)
|
||||
}
|
||||
if v, _ := uuid1.Version(); v != 1 {
|
||||
t.Errorf("%s: version %s expected 1\n", uuid1, v)
|
||||
}
|
||||
if v, _ := uuid2.Version(); v != 1 {
|
||||
t.Errorf("%s: version %s expected 1\n", uuid2, v)
|
||||
}
|
||||
n1 := uuid1.NodeID()
|
||||
n2 := uuid2.NodeID()
|
||||
if !bytes.Equal(n1, n2) {
|
||||
t.Errorf("Different nodes %x != %x\n", n1, n2)
|
||||
}
|
||||
t1, ok := uuid1.Time()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid time\n", uuid1)
|
||||
}
|
||||
t2, ok := uuid2.Time()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid time\n", uuid2)
|
||||
}
|
||||
q1, ok := uuid1.ClockSequence()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid clock sequence\n", uuid1)
|
||||
}
|
||||
q2, ok := uuid2.ClockSequence()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid clock sequence", uuid2)
|
||||
}
|
||||
|
||||
switch {
|
||||
case t1 == t2 && q1 == q2:
|
||||
t.Errorf("time stopped\n")
|
||||
case t1 > t2 && q1 == q2:
|
||||
t.Errorf("time reversed\n")
|
||||
case t1 < t2 && q1 != q2:
|
||||
t.Errorf("clock sequence chaned unexpectedly\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeAndTime(t *testing.T) {
|
||||
// Time is February 5, 1998 12:30:23.136364800 AM GMT
|
||||
|
||||
uuid := Parse("7d444840-9dc0-11d1-b245-5ffdce74fad2")
|
||||
node := []byte{0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2}
|
||||
|
||||
ts, ok := uuid.Time()
|
||||
if ok {
|
||||
c := time.Unix(ts.UnixTime())
|
||||
want := time.Date(1998, 2, 5, 0, 30, 23, 136364800, time.UTC)
|
||||
if !c.Equal(want) {
|
||||
t.Errorf("Got time %v, want %v", c, want)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("%s: bad time\n", uuid)
|
||||
}
|
||||
if !bytes.Equal(node, uuid.NodeID()) {
|
||||
t.Errorf("Expected node %v got %v\n", node, uuid.NodeID())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMD5(t *testing.T) {
|
||||
uuid := NewMD5(NameSpace_DNS, []byte("python.org")).String()
|
||||
want := "6fa459ea-ee8a-3ca4-894e-db77e160355e"
|
||||
if uuid != want {
|
||||
t.Errorf("MD5: got %q expected %q\n", uuid, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSHA1(t *testing.T) {
|
||||
uuid := NewSHA1(NameSpace_DNS, []byte("python.org")).String()
|
||||
want := "886313e1-3b8a-5372-9b90-0c9aee199e5d"
|
||||
if uuid != want {
|
||||
t.Errorf("SHA1: got %q expected %q\n", uuid, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeID(t *testing.T) {
|
||||
nid := []byte{1, 2, 3, 4, 5, 6}
|
||||
SetNodeInterface("")
|
||||
s := NodeInterface()
|
||||
if s == "" || s == "user" {
|
||||
t.Errorf("NodeInterface %q after SetInteface\n", s)
|
||||
}
|
||||
node1 := NodeID()
|
||||
if node1 == nil {
|
||||
t.Errorf("NodeID nil after SetNodeInterface\n", s)
|
||||
}
|
||||
SetNodeID(nid)
|
||||
s = NodeInterface()
|
||||
if s != "user" {
|
||||
t.Errorf("Expected NodeInterface %q got %q\n", "user", s)
|
||||
}
|
||||
node2 := NodeID()
|
||||
if node2 == nil {
|
||||
t.Errorf("NodeID nil after SetNodeID\n", s)
|
||||
}
|
||||
if bytes.Equal(node1, node2) {
|
||||
t.Errorf("NodeID not changed after SetNodeID\n", s)
|
||||
} else if !bytes.Equal(nid, node2) {
|
||||
t.Errorf("NodeID is %x, expected %x\n", node2, nid)
|
||||
}
|
||||
}
|
||||
|
||||
func testDCE(t *testing.T, name string, uuid UUID, domain Domain, id uint32) {
|
||||
if uuid == nil {
|
||||
t.Errorf("%s failed\n", name)
|
||||
return
|
||||
}
|
||||
if v, _ := uuid.Version(); v != 2 {
|
||||
t.Errorf("%s: %s: expected version 2, got %s\n", name, uuid, v)
|
||||
return
|
||||
}
|
||||
if v, ok := uuid.Domain(); !ok || v != domain {
|
||||
if !ok {
|
||||
t.Errorf("%s: %d: Domain failed\n", name, uuid)
|
||||
} else {
|
||||
t.Errorf("%s: %s: expected domain %d, got %d\n", name, uuid, domain, v)
|
||||
}
|
||||
}
|
||||
if v, ok := uuid.Id(); !ok || v != id {
|
||||
if !ok {
|
||||
t.Errorf("%s: %d: Id failed\n", name, uuid)
|
||||
} else {
|
||||
t.Errorf("%s: %s: expected id %d, got %d\n", name, uuid, id, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDCE(t *testing.T) {
|
||||
testDCE(t, "NewDCESecurity", NewDCESecurity(42, 12345678), 42, 12345678)
|
||||
testDCE(t, "NewDCEPerson", NewDCEPerson(), Person, uint32(os.Getuid()))
|
||||
testDCE(t, "NewDCEGroup", NewDCEGroup(), Group, uint32(os.Getgid()))
|
||||
}
|
||||
|
||||
type badRand struct{}
|
||||
|
||||
func (r badRand) Read(buf []byte) (int, error) {
|
||||
for i, _ := range buf {
|
||||
buf[i] = byte(i)
|
||||
}
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func TestBadRand(t *testing.T) {
|
||||
SetRand(badRand{})
|
||||
uuid1 := New()
|
||||
uuid2 := New()
|
||||
if uuid1 != uuid2 {
|
||||
t.Errorf("execpted duplicates, got %q and %q\n", uuid1, uuid2)
|
||||
}
|
||||
SetRand(nil)
|
||||
uuid1 = New()
|
||||
uuid2 = New()
|
||||
if uuid1 == uuid2 {
|
||||
t.Errorf("unexecpted duplicates, got %q\n", uuid1)
|
||||
}
|
||||
}
|
||||
41
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version1.go
generated
vendored
Normal file
41
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version1.go
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// NewUUID returns a Version 1 UUID based on the current NodeID and clock
|
||||
// sequence, and the current time. If the NodeID has not been set by SetNodeID
|
||||
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
|
||||
// be set NewUUID returns nil. If clock sequence has not been set by
|
||||
// SetClockSequence then it will be set automatically. If GetTime fails to
|
||||
// return the current NewUUID returns nil.
|
||||
func NewUUID() UUID {
|
||||
if nodeID == nil {
|
||||
SetNodeInterface("")
|
||||
}
|
||||
|
||||
now, err := GetTime()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
uuid := make([]byte, 16)
|
||||
|
||||
time_low := uint32(now & 0xffffffff)
|
||||
time_mid := uint16((now >> 32) & 0xffff)
|
||||
time_hi := uint16((now >> 48) & 0x0fff)
|
||||
time_hi |= 0x1000 // Version 1
|
||||
|
||||
binary.BigEndian.PutUint32(uuid[0:], time_low)
|
||||
binary.BigEndian.PutUint16(uuid[4:], time_mid)
|
||||
binary.BigEndian.PutUint16(uuid[6:], time_hi)
|
||||
binary.BigEndian.PutUint16(uuid[8:], clock_seq)
|
||||
copy(uuid[10:], nodeID)
|
||||
|
||||
return uuid
|
||||
}
|
||||
25
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version4.go
generated
vendored
Normal file
25
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version4.go
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
// Random returns a Random (Version 4) UUID or panics.
|
||||
//
|
||||
// The strength of the UUIDs is based on the strength of the crypto/rand
|
||||
// package.
|
||||
//
|
||||
// A note about uniqueness derived from from the UUID Wikipedia entry:
|
||||
//
|
||||
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
|
||||
// hit by a meteorite is estimated to be one chance in 17 billion, that
|
||||
// means the probability is about 0.00000000006 (6 × 10−11),
|
||||
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
|
||||
// year and having one duplicate.
|
||||
func NewRandom() UUID {
|
||||
uuid := make([]byte, 16)
|
||||
randomBits([]byte(uuid))
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
|
||||
return uuid
|
||||
}
|
||||
622
Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go
generated
vendored
Normal file
622
Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,622 @@
|
|||
package cli_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func ExampleApp() {
|
||||
// set args for examples sake
|
||||
os.Args = []string{"greet", "--name", "Jeremy"}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "greet"
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
|
||||
}
|
||||
app.Action = func(c *cli.Context) {
|
||||
fmt.Printf("Hello %v\n", c.String("name"))
|
||||
}
|
||||
app.Author = "Harrison"
|
||||
app.Email = "harrison@lolwut.com"
|
||||
app.Authors = []cli.Author{cli.Author{Name: "Oliver Allen", Email: "oliver@toyshop.com"}}
|
||||
app.Run(os.Args)
|
||||
// Output:
|
||||
// Hello Jeremy
|
||||
}
|
||||
|
||||
func ExampleAppSubcommand() {
|
||||
// set args for examples sake
|
||||
os.Args = []string{"say", "hi", "english", "--name", "Jeremy"}
|
||||
app := cli.NewApp()
|
||||
app.Name = "say"
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "hello",
|
||||
Aliases: []string{"hi"},
|
||||
Usage: "use it to see a description",
|
||||
Description: "This is how we describe hello the function",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "english",
|
||||
Aliases: []string{"en"},
|
||||
Usage: "sends a greeting in english",
|
||||
Description: "greets someone in english",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Value: "Bob",
|
||||
Usage: "Name of the person to greet",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
fmt.Println("Hello,", c.String("name"))
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
// Output:
|
||||
// Hello, Jeremy
|
||||
}
|
||||
|
||||
func ExampleAppHelp() {
|
||||
// set args for examples sake
|
||||
os.Args = []string{"greet", "h", "describeit"}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "greet"
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "describeit",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "use it to see a description",
|
||||
Description: "This is how we describe describeit the function",
|
||||
Action: func(c *cli.Context) {
|
||||
fmt.Printf("i like to describe things")
|
||||
},
|
||||
},
|
||||
}
|
||||
app.Run(os.Args)
|
||||
// Output:
|
||||
// NAME:
|
||||
// describeit - use it to see a description
|
||||
//
|
||||
// USAGE:
|
||||
// command describeit [arguments...]
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// This is how we describe describeit the function
|
||||
}
|
||||
|
||||
func ExampleAppBashComplete() {
|
||||
// set args for examples sake
|
||||
os.Args = []string{"greet", "--generate-bash-completion"}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "greet"
|
||||
app.EnableBashCompletion = true
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "describeit",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "use it to see a description",
|
||||
Description: "This is how we describe describeit the function",
|
||||
Action: func(c *cli.Context) {
|
||||
fmt.Printf("i like to describe things")
|
||||
},
|
||||
}, {
|
||||
Name: "next",
|
||||
Usage: "next example",
|
||||
Description: "more stuff to see when generating bash completion",
|
||||
Action: func(c *cli.Context) {
|
||||
fmt.Printf("the next example")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
// Output:
|
||||
// describeit
|
||||
// d
|
||||
// next
|
||||
// help
|
||||
// h
|
||||
}
|
||||
|
||||
func TestApp_Run(t *testing.T) {
|
||||
s := ""
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Action = func(c *cli.Context) {
|
||||
s = s + c.Args().First()
|
||||
}
|
||||
|
||||
err := app.Run([]string{"command", "foo"})
|
||||
expect(t, err, nil)
|
||||
err = app.Run([]string{"command", "bar"})
|
||||
expect(t, err, nil)
|
||||
expect(t, s, "foobar")
|
||||
}
|
||||
|
||||
var commandAppTests = []struct {
|
||||
name string
|
||||
expected bool
|
||||
}{
|
||||
{"foobar", true},
|
||||
{"batbaz", true},
|
||||
{"b", true},
|
||||
{"f", true},
|
||||
{"bat", false},
|
||||
{"nothing", false},
|
||||
}
|
||||
|
||||
func TestApp_Command(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
fooCommand := cli.Command{Name: "foobar", Aliases: []string{"f"}}
|
||||
batCommand := cli.Command{Name: "batbaz", Aliases: []string{"b"}}
|
||||
app.Commands = []cli.Command{
|
||||
fooCommand,
|
||||
batCommand,
|
||||
}
|
||||
|
||||
for _, test := range commandAppTests {
|
||||
expect(t, app.Command(test.name) != nil, test.expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApp_CommandWithArgBeforeFlags(t *testing.T) {
|
||||
var parsedOption, firstArg string
|
||||
|
||||
app := cli.NewApp()
|
||||
command := cli.Command{
|
||||
Name: "cmd",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "option", Value: "", Usage: "some option"},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
parsedOption = c.String("option")
|
||||
firstArg = c.Args().First()
|
||||
},
|
||||
}
|
||||
app.Commands = []cli.Command{command}
|
||||
|
||||
app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"})
|
||||
|
||||
expect(t, parsedOption, "my-option")
|
||||
expect(t, firstArg, "my-arg")
|
||||
}
|
||||
|
||||
func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
|
||||
var context *cli.Context
|
||||
|
||||
a := cli.NewApp()
|
||||
a.Commands = []cli.Command{
|
||||
{
|
||||
Name: "foo",
|
||||
Action: func(c *cli.Context) {
|
||||
context = c
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "lang",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
},
|
||||
},
|
||||
Before: func(_ *cli.Context) error { return nil },
|
||||
},
|
||||
}
|
||||
a.Run([]string{"", "foo", "--lang", "spanish", "abcd"})
|
||||
|
||||
expect(t, context.Args().Get(0), "abcd")
|
||||
expect(t, context.String("lang"), "spanish")
|
||||
}
|
||||
|
||||
func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
|
||||
var parsedOption string
|
||||
var args []string
|
||||
|
||||
app := cli.NewApp()
|
||||
command := cli.Command{
|
||||
Name: "cmd",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "option", Value: "", Usage: "some option"},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
parsedOption = c.String("option")
|
||||
args = c.Args()
|
||||
},
|
||||
}
|
||||
app.Commands = []cli.Command{command}
|
||||
|
||||
app.Run([]string{"", "cmd", "my-arg", "--option", "my-option", "--", "--notARealFlag"})
|
||||
|
||||
expect(t, parsedOption, "my-option")
|
||||
expect(t, args[0], "my-arg")
|
||||
expect(t, args[1], "--")
|
||||
expect(t, args[2], "--notARealFlag")
|
||||
}
|
||||
|
||||
func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) {
|
||||
var args []string
|
||||
|
||||
app := cli.NewApp()
|
||||
command := cli.Command{
|
||||
Name: "cmd",
|
||||
Action: func(c *cli.Context) {
|
||||
args = c.Args()
|
||||
},
|
||||
}
|
||||
app.Commands = []cli.Command{command}
|
||||
|
||||
app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"})
|
||||
|
||||
expect(t, args[0], "my-arg")
|
||||
expect(t, args[1], "--")
|
||||
expect(t, args[2], "notAFlagAtAll")
|
||||
}
|
||||
|
||||
func TestApp_Float64Flag(t *testing.T) {
|
||||
var meters float64
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Flags = []cli.Flag{
|
||||
cli.Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"},
|
||||
}
|
||||
app.Action = func(c *cli.Context) {
|
||||
meters = c.Float64("height")
|
||||
}
|
||||
|
||||
app.Run([]string{"", "--height", "1.93"})
|
||||
expect(t, meters, 1.93)
|
||||
}
|
||||
|
||||
func TestApp_ParseSliceFlags(t *testing.T) {
|
||||
var parsedOption, firstArg string
|
||||
var parsedIntSlice []int
|
||||
var parsedStringSlice []string
|
||||
|
||||
app := cli.NewApp()
|
||||
command := cli.Command{
|
||||
Name: "cmd",
|
||||
Flags: []cli.Flag{
|
||||
cli.IntSliceFlag{Name: "p", Value: &cli.IntSlice{}, Usage: "set one or more ip addr"},
|
||||
cli.StringSliceFlag{Name: "ip", Value: &cli.StringSlice{}, Usage: "set one or more ports to open"},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
parsedIntSlice = c.IntSlice("p")
|
||||
parsedStringSlice = c.StringSlice("ip")
|
||||
parsedOption = c.String("option")
|
||||
firstArg = c.Args().First()
|
||||
},
|
||||
}
|
||||
app.Commands = []cli.Command{command}
|
||||
|
||||
app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"})
|
||||
|
||||
IntsEquals := func(a, b []int) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, v := range a {
|
||||
if v != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
StrsEquals := func(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, v := range a {
|
||||
if v != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
var expectedIntSlice = []int{22, 80}
|
||||
var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"}
|
||||
|
||||
if !IntsEquals(parsedIntSlice, expectedIntSlice) {
|
||||
t.Errorf("%v does not match %v", parsedIntSlice, expectedIntSlice)
|
||||
}
|
||||
|
||||
if !StrsEquals(parsedStringSlice, expectedStringSlice) {
|
||||
t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApp_DefaultStdout(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
|
||||
if app.Writer != os.Stdout {
|
||||
t.Error("Default output writer not set.")
|
||||
}
|
||||
}
|
||||
|
||||
type mockWriter struct {
|
||||
written []byte
|
||||
}
|
||||
|
||||
func (fw *mockWriter) Write(p []byte) (n int, err error) {
|
||||
if fw.written == nil {
|
||||
fw.written = p
|
||||
} else {
|
||||
fw.written = append(fw.written, p...)
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (fw *mockWriter) GetWritten() (b []byte) {
|
||||
return fw.written
|
||||
}
|
||||
|
||||
func TestApp_SetStdout(t *testing.T) {
|
||||
w := &mockWriter{}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "test"
|
||||
app.Writer = w
|
||||
|
||||
err := app.Run([]string{"help"})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Run error: %s", err)
|
||||
}
|
||||
|
||||
if len(w.written) == 0 {
|
||||
t.Error("App did not write output to desired writer.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApp_BeforeFunc(t *testing.T) {
|
||||
beforeRun, subcommandRun := false, false
|
||||
beforeError := fmt.Errorf("fail")
|
||||
var err error
|
||||
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Before = func(c *cli.Context) error {
|
||||
beforeRun = true
|
||||
s := c.String("opt")
|
||||
if s == "fail" {
|
||||
return beforeError
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
cli.Command{
|
||||
Name: "sub",
|
||||
Action: func(c *cli.Context) {
|
||||
subcommandRun = true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{Name: "opt"},
|
||||
}
|
||||
|
||||
// run with the Before() func succeeding
|
||||
err = app.Run([]string{"command", "--opt", "succeed", "sub"})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Run error: %s", err)
|
||||
}
|
||||
|
||||
if beforeRun == false {
|
||||
t.Errorf("Before() not executed when expected")
|
||||
}
|
||||
|
||||
if subcommandRun == false {
|
||||
t.Errorf("Subcommand not executed when expected")
|
||||
}
|
||||
|
||||
// reset
|
||||
beforeRun, subcommandRun = false, false
|
||||
|
||||
// run with the Before() func failing
|
||||
err = app.Run([]string{"command", "--opt", "fail", "sub"})
|
||||
|
||||
// should be the same error produced by the Before func
|
||||
if err != beforeError {
|
||||
t.Errorf("Run error expected, but not received")
|
||||
}
|
||||
|
||||
if beforeRun == false {
|
||||
t.Errorf("Before() not executed when expected")
|
||||
}
|
||||
|
||||
if subcommandRun == true {
|
||||
t.Errorf("Subcommand executed when NOT expected")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestApp_AfterFunc(t *testing.T) {
|
||||
afterRun, subcommandRun := false, false
|
||||
afterError := fmt.Errorf("fail")
|
||||
var err error
|
||||
|
||||
app := cli.NewApp()
|
||||
|
||||
app.After = func(c *cli.Context) error {
|
||||
afterRun = true
|
||||
s := c.String("opt")
|
||||
if s == "fail" {
|
||||
return afterError
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
cli.Command{
|
||||
Name: "sub",
|
||||
Action: func(c *cli.Context) {
|
||||
subcommandRun = true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{Name: "opt"},
|
||||
}
|
||||
|
||||
// run with the After() func succeeding
|
||||
err = app.Run([]string{"command", "--opt", "succeed", "sub"})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Run error: %s", err)
|
||||
}
|
||||
|
||||
if afterRun == false {
|
||||
t.Errorf("After() not executed when expected")
|
||||
}
|
||||
|
||||
if subcommandRun == false {
|
||||
t.Errorf("Subcommand not executed when expected")
|
||||
}
|
||||
|
||||
// reset
|
||||
afterRun, subcommandRun = false, false
|
||||
|
||||
// run with the Before() func failing
|
||||
err = app.Run([]string{"command", "--opt", "fail", "sub"})
|
||||
|
||||
// should be the same error produced by the Before func
|
||||
if err != afterError {
|
||||
t.Errorf("Run error expected, but not received")
|
||||
}
|
||||
|
||||
if afterRun == false {
|
||||
t.Errorf("After() not executed when expected")
|
||||
}
|
||||
|
||||
if subcommandRun == false {
|
||||
t.Errorf("Subcommand not executed when expected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppNoHelpFlag(t *testing.T) {
|
||||
oldFlag := cli.HelpFlag
|
||||
defer func() {
|
||||
cli.HelpFlag = oldFlag
|
||||
}()
|
||||
|
||||
cli.HelpFlag = cli.BoolFlag{}
|
||||
|
||||
app := cli.NewApp()
|
||||
err := app.Run([]string{"test", "-h"})
|
||||
|
||||
if err != flag.ErrHelp {
|
||||
t.Errorf("expected error about missing help flag, but got: %s (%T)", err, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppHelpPrinter(t *testing.T) {
|
||||
oldPrinter := cli.HelpPrinter
|
||||
defer func() {
|
||||
cli.HelpPrinter = oldPrinter
|
||||
}()
|
||||
|
||||
var wasCalled = false
|
||||
cli.HelpPrinter = func(template string, data interface{}) {
|
||||
wasCalled = true
|
||||
}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Run([]string{"-h"})
|
||||
|
||||
if wasCalled == false {
|
||||
t.Errorf("Help printer expected to be called, but was not")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppVersionPrinter(t *testing.T) {
|
||||
oldPrinter := cli.VersionPrinter
|
||||
defer func() {
|
||||
cli.VersionPrinter = oldPrinter
|
||||
}()
|
||||
|
||||
var wasCalled = false
|
||||
cli.VersionPrinter = func(c *cli.Context) {
|
||||
wasCalled = true
|
||||
}
|
||||
|
||||
app := cli.NewApp()
|
||||
ctx := cli.NewContext(app, nil, nil)
|
||||
cli.ShowVersion(ctx)
|
||||
|
||||
if wasCalled == false {
|
||||
t.Errorf("Version printer expected to be called, but was not")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppCommandNotFound(t *testing.T) {
|
||||
beforeRun, subcommandRun := false, false
|
||||
app := cli.NewApp()
|
||||
|
||||
app.CommandNotFound = func(c *cli.Context, command string) {
|
||||
beforeRun = true
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
cli.Command{
|
||||
Name: "bar",
|
||||
Action: func(c *cli.Context) {
|
||||
subcommandRun = true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run([]string{"command", "foo"})
|
||||
|
||||
expect(t, beforeRun, true)
|
||||
expect(t, subcommandRun, false)
|
||||
}
|
||||
|
||||
func TestGlobalFlagsInSubcommands(t *testing.T) {
|
||||
subcommandRun := false
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.BoolFlag{Name: "debug, d", Usage: "Enable debugging"},
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
cli.Command{
|
||||
Name: "foo",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "bar",
|
||||
Action: func(c *cli.Context) {
|
||||
if c.GlobalBool("debug") {
|
||||
subcommandRun = true
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run([]string{"command", "-d", "foo", "bar"})
|
||||
|
||||
expect(t, subcommandRun, true)
|
||||
}
|
||||
100
Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go
generated
vendored
Normal file
100
Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
package cli_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "todo"
|
||||
app.Usage = "task list on the command line"
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "add",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "add a task to the list",
|
||||
Action: func(c *cli.Context) {
|
||||
println("added task: ", c.Args().First())
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "complete",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "complete a task on the list",
|
||||
Action: func(c *cli.Context) {
|
||||
println("completed task: ", c.Args().First())
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
||||
func ExampleSubcommand() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "say"
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "hello",
|
||||
Aliases: []string{"hi"},
|
||||
Usage: "use it to see a description",
|
||||
Description: "This is how we describe hello the function",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "english",
|
||||
Aliases: []string{"en"},
|
||||
Usage: "sends a greeting in english",
|
||||
Description: "greets someone in english",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Value: "Bob",
|
||||
Usage: "Name of the person to greet",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
println("Hello, ", c.String("name"))
|
||||
},
|
||||
}, {
|
||||
Name: "spanish",
|
||||
Aliases: []string{"sp"},
|
||||
Usage: "sends a greeting in spanish",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "surname",
|
||||
Value: "Jones",
|
||||
Usage: "Surname of the person to greet",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
println("Hola, ", c.String("surname"))
|
||||
},
|
||||
}, {
|
||||
Name: "french",
|
||||
Aliases: []string{"fr"},
|
||||
Usage: "sends a greeting in french",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "nickname",
|
||||
Value: "Stevie",
|
||||
Usage: "Nickname of the person to greet",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
println("Bonjour, ", c.String("nickname"))
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: "bye",
|
||||
Usage: "says goodbye",
|
||||
Action: func(c *cli.Context) {
|
||||
println("bye")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
||||
49
Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go
generated
vendored
Normal file
49
Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package cli_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func TestCommandDoNotIgnoreFlags(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
test := []string{"blah", "blah", "-break"}
|
||||
set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, set)
|
||||
|
||||
command := cli.Command{
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(_ *cli.Context) {},
|
||||
}
|
||||
err := command.Run(c)
|
||||
|
||||
expect(t, err.Error(), "flag provided but not defined: -break")
|
||||
}
|
||||
|
||||
func TestCommandIgnoreFlags(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
test := []string{"blah", "blah"}
|
||||
set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, set)
|
||||
|
||||
command := cli.Command{
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(_ *cli.Context) {},
|
||||
SkipFlagParsing: true,
|
||||
}
|
||||
err := command.Run(c)
|
||||
|
||||
expect(t, err, nil)
|
||||
}
|
||||
111
Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go
generated
vendored
Normal file
111
Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package cli_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func TestNewContext(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Int("myflag", 12, "doc")
|
||||
globalSet := flag.NewFlagSet("test", 0)
|
||||
globalSet.Int("myflag", 42, "doc")
|
||||
command := cli.Command{Name: "mycommand"}
|
||||
c := cli.NewContext(nil, set, globalSet)
|
||||
c.Command = command
|
||||
expect(t, c.Int("myflag"), 12)
|
||||
expect(t, c.GlobalInt("myflag"), 42)
|
||||
expect(t, c.Command.Name, "mycommand")
|
||||
}
|
||||
|
||||
func TestContext_Int(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Int("myflag", 12, "doc")
|
||||
c := cli.NewContext(nil, set, set)
|
||||
expect(t, c.Int("myflag"), 12)
|
||||
}
|
||||
|
||||
func TestContext_Duration(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Duration("myflag", time.Duration(12*time.Second), "doc")
|
||||
c := cli.NewContext(nil, set, set)
|
||||
expect(t, c.Duration("myflag"), time.Duration(12*time.Second))
|
||||
}
|
||||
|
||||
func TestContext_String(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String("myflag", "hello world", "doc")
|
||||
c := cli.NewContext(nil, set, set)
|
||||
expect(t, c.String("myflag"), "hello world")
|
||||
}
|
||||
|
||||
func TestContext_Bool(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
c := cli.NewContext(nil, set, set)
|
||||
expect(t, c.Bool("myflag"), false)
|
||||
}
|
||||
|
||||
func TestContext_BoolT(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", true, "doc")
|
||||
c := cli.NewContext(nil, set, set)
|
||||
expect(t, c.BoolT("myflag"), true)
|
||||
}
|
||||
|
||||
func TestContext_Args(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
c := cli.NewContext(nil, set, set)
|
||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
expect(t, len(c.Args()), 2)
|
||||
expect(t, c.Bool("myflag"), true)
|
||||
}
|
||||
|
||||
func TestContext_IsSet(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
set.String("otherflag", "hello world", "doc")
|
||||
globalSet := flag.NewFlagSet("test", 0)
|
||||
globalSet.Bool("myflagGlobal", true, "doc")
|
||||
c := cli.NewContext(nil, set, globalSet)
|
||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
|
||||
expect(t, c.IsSet("myflag"), true)
|
||||
expect(t, c.IsSet("otherflag"), false)
|
||||
expect(t, c.IsSet("bogusflag"), false)
|
||||
expect(t, c.IsSet("myflagGlobal"), false)
|
||||
}
|
||||
|
||||
func TestContext_GlobalIsSet(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
set.String("otherflag", "hello world", "doc")
|
||||
globalSet := flag.NewFlagSet("test", 0)
|
||||
globalSet.Bool("myflagGlobal", true, "doc")
|
||||
globalSet.Bool("myflagGlobalUnset", true, "doc")
|
||||
c := cli.NewContext(nil, set, globalSet)
|
||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
|
||||
expect(t, c.GlobalIsSet("myflag"), false)
|
||||
expect(t, c.GlobalIsSet("otherflag"), false)
|
||||
expect(t, c.GlobalIsSet("bogusflag"), false)
|
||||
expect(t, c.GlobalIsSet("myflagGlobal"), true)
|
||||
expect(t, c.GlobalIsSet("myflagGlobalUnset"), false)
|
||||
expect(t, c.GlobalIsSet("bogusGlobal"), false)
|
||||
}
|
||||
|
||||
func TestContext_NumFlags(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
set.String("otherflag", "hello world", "doc")
|
||||
globalSet := flag.NewFlagSet("test", 0)
|
||||
globalSet.Bool("myflagGlobal", true, "doc")
|
||||
c := cli.NewContext(nil, set, globalSet)
|
||||
set.Parse([]string{"--myflag", "--otherflag=foo"})
|
||||
globalSet.Parse([]string{"--myflagGlobal"})
|
||||
expect(t, c.NumFlags(), 2)
|
||||
}
|
||||
742
Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go
generated
vendored
Normal file
742
Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,742 @@
|
|||
package cli_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var boolFlagTests = []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"help", "--help\t"},
|
||||
{"h", "-h\t"},
|
||||
}
|
||||
|
||||
func TestBoolFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range boolFlagTests {
|
||||
flag := cli.BoolFlag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stringFlagTests = []struct {
|
||||
name string
|
||||
value string
|
||||
expected string
|
||||
}{
|
||||
{"help", "", "--help \t"},
|
||||
{"h", "", "-h \t"},
|
||||
{"h", "", "-h \t"},
|
||||
{"test", "Something", "--test \"Something\"\t"},
|
||||
}
|
||||
|
||||
func TestStringFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range stringFlagTests {
|
||||
flag := cli.StringFlag{Name: test.name, Value: test.value}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_FOO", "derp")
|
||||
for _, test := range stringFlagTests {
|
||||
flag := cli.StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_FOO]") {
|
||||
t.Errorf("%s does not end with [$APP_FOO]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stringSliceFlagTests = []struct {
|
||||
name string
|
||||
value *cli.StringSlice
|
||||
expected string
|
||||
}{
|
||||
{"help", func() *cli.StringSlice {
|
||||
s := &cli.StringSlice{}
|
||||
s.Set("")
|
||||
return s
|
||||
}(), "--help [--help option --help option]\t"},
|
||||
{"h", func() *cli.StringSlice {
|
||||
s := &cli.StringSlice{}
|
||||
s.Set("")
|
||||
return s
|
||||
}(), "-h [-h option -h option]\t"},
|
||||
{"h", func() *cli.StringSlice {
|
||||
s := &cli.StringSlice{}
|
||||
s.Set("")
|
||||
return s
|
||||
}(), "-h [-h option -h option]\t"},
|
||||
{"test", func() *cli.StringSlice {
|
||||
s := &cli.StringSlice{}
|
||||
s.Set("Something")
|
||||
return s
|
||||
}(), "--test [--test option --test option]\t"},
|
||||
}
|
||||
|
||||
func TestStringSliceFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range stringSliceFlagTests {
|
||||
flag := cli.StringSliceFlag{Name: test.name, Value: test.value}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%q does not match %q", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_QWWX", "11,4")
|
||||
for _, test := range stringSliceFlagTests {
|
||||
flag := cli.StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_QWWX]") {
|
||||
t.Errorf("%q does not end with [$APP_QWWX]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var intFlagTests = []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"help", "--help \"0\"\t"},
|
||||
{"h", "-h \"0\"\t"},
|
||||
}
|
||||
|
||||
func TestIntFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range intFlagTests {
|
||||
flag := cli.IntFlag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_BAR", "2")
|
||||
for _, test := range intFlagTests {
|
||||
flag := cli.IntFlag{Name: test.name, EnvVar: "APP_BAR"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_BAR]") {
|
||||
t.Errorf("%s does not end with [$APP_BAR]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var durationFlagTests = []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"help", "--help \"0\"\t"},
|
||||
{"h", "-h \"0\"\t"},
|
||||
}
|
||||
|
||||
func TestDurationFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range durationFlagTests {
|
||||
flag := cli.DurationFlag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_BAR", "2h3m6s")
|
||||
for _, test := range durationFlagTests {
|
||||
flag := cli.DurationFlag{Name: test.name, EnvVar: "APP_BAR"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_BAR]") {
|
||||
t.Errorf("%s does not end with [$APP_BAR]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var intSliceFlagTests = []struct {
|
||||
name string
|
||||
value *cli.IntSlice
|
||||
expected string
|
||||
}{
|
||||
{"help", &cli.IntSlice{}, "--help [--help option --help option]\t"},
|
||||
{"h", &cli.IntSlice{}, "-h [-h option -h option]\t"},
|
||||
{"h", &cli.IntSlice{}, "-h [-h option -h option]\t"},
|
||||
{"test", func() *cli.IntSlice {
|
||||
i := &cli.IntSlice{}
|
||||
i.Set("9")
|
||||
return i
|
||||
}(), "--test [--test option --test option]\t"},
|
||||
}
|
||||
|
||||
func TestIntSliceFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range intSliceFlagTests {
|
||||
flag := cli.IntSliceFlag{Name: test.name, Value: test.value}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%q does not match %q", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_SMURF", "42,3")
|
||||
for _, test := range intSliceFlagTests {
|
||||
flag := cli.IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_SMURF]") {
|
||||
t.Errorf("%q does not end with [$APP_SMURF]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var float64FlagTests = []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"help", "--help \"0\"\t"},
|
||||
{"h", "-h \"0\"\t"},
|
||||
}
|
||||
|
||||
func TestFloat64FlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range float64FlagTests {
|
||||
flag := cli.Float64Flag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_BAZ", "99.4")
|
||||
for _, test := range float64FlagTests {
|
||||
flag := cli.Float64Flag{Name: test.name, EnvVar: "APP_BAZ"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_BAZ]") {
|
||||
t.Errorf("%s does not end with [$APP_BAZ]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var genericFlagTests = []struct {
|
||||
name string
|
||||
value cli.Generic
|
||||
expected string
|
||||
}{
|
||||
{"test", &Parser{"abc", "def"}, "--test \"abc,def\"\ttest flag"},
|
||||
{"t", &Parser{"abc", "def"}, "-t \"abc,def\"\ttest flag"},
|
||||
}
|
||||
|
||||
func TestGenericFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range genericFlagTests {
|
||||
flag := cli.GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%q does not match %q", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_ZAP", "3")
|
||||
for _, test := range genericFlagTests {
|
||||
flag := cli.GenericFlag{Name: test.name, EnvVar: "APP_ZAP"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_ZAP]") {
|
||||
t.Errorf("%s does not end with [$APP_ZAP]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMultiString(t *testing.T) {
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.String("serve") != "10" {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.String("s") != "10" {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run", "-s", "10"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_COUNT", "20")
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "count, c", EnvVar: "APP_COUNT"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.String("count") != "20" {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.String("c") != "20" {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_COUNT", "20")
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.String("count") != "20" {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.String("c") != "20" {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringSlice(t *testing.T) {
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.StringSliceFlag{Name: "serve, s", Value: &cli.StringSlice{}},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run", "-s", "10", "-s", "20"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringSliceFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.StringSliceFlag{Name: "intervals, i", Value: &cli.StringSlice{}, EnvVar: "APP_INTERVALS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringSliceFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.StringSliceFlag{Name: "intervals, i", Value: &cli.StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiInt(t *testing.T) {
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Int("serve") != 10 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Int("s") != 10 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "-s", "10"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "10")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Int("timeout") != 10 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Int("t") != 10 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "10")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Int("timeout") != 10 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Int("t") != 10 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntSlice(t *testing.T) {
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.IntSliceFlag{Name: "serve, s", Value: &cli.IntSlice{}},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run", "-s", "10", "-s", "20"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntSliceFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.IntSliceFlag{Name: "intervals, i", Value: &cli.IntSlice{}, EnvVar: "APP_INTERVALS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.IntSliceFlag{Name: "intervals, i", Value: &cli.IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiFloat64(t *testing.T) {
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.Float64Flag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Float64("serve") != 10.2 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Float64("s") != 10.2 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "-s", "10.2"})
|
||||
}
|
||||
|
||||
func TestParseMultiFloat64FromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Float64("timeout") != 15.5 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Float64("t") != 15.5 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Float64("timeout") != 15.5 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Float64("t") != 15.5 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiBool(t *testing.T) {
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Bool("serve") != true {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Bool("s") != true {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "--serve"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_DEBUG", "1")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Bool("debug") != true {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if ctx.Bool("d") != true {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_DEBUG", "1")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Bool("debug") != true {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if ctx.Bool("d") != true {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolT(t *testing.T) {
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolTFlag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.BoolT("serve") != true {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.BoolT("s") != true {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "--serve"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolTFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_DEBUG", "0")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.BoolT("debug") != false {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if ctx.BoolT("d") != false {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolTFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_DEBUG", "0")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.BoolT("debug") != false {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if ctx.BoolT("d") != false {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
type Parser [2]string
|
||||
|
||||
func (p *Parser) Set(value string) error {
|
||||
parts := strings.Split(value, ",")
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid format")
|
||||
}
|
||||
|
||||
(*p)[0] = parts[0]
|
||||
(*p)[1] = parts[1]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) String() string {
|
||||
return fmt.Sprintf("%s,%s", p[0], p[1])
|
||||
}
|
||||
|
||||
func TestParseGeneric(t *testing.T) {
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.GenericFlag{Name: "serve, s", Value: &Parser{}},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "-s", "10,20"})
|
||||
}
|
||||
|
||||
func TestParseGenericFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_SERVE", "20,30")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseGenericFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_FOO", "99,2000")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) {
|
||||
t.Errorf("value not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
19
Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package cli_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
/* Test Helpers */
|
||||
func expect(t *testing.T, a interface{}, b interface{}) {
|
||||
if a != b {
|
||||
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||
}
|
||||
}
|
||||
|
||||
func refute(t *testing.T, a interface{}, b interface{}) {
|
||||
if a == b {
|
||||
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||
}
|
||||
}
|
||||
62
Godeps/_workspace/src/github.com/huin/goupnp/example/example_test.go
generated
vendored
Normal file
62
Godeps/_workspace/src/github.com/huin/goupnp/example/example_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package example_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/huin/goupnp"
|
||||
"github.com/huin/goupnp/dcps/internetgateway1"
|
||||
)
|
||||
|
||||
// Use discovered WANPPPConnection1 services to find external IP addresses.
|
||||
func Example_WANPPPConnection1_GetExternalIPAddress() {
|
||||
clients, errors, err := internetgateway1.NewWANPPPConnection1Clients()
|
||||
extIPClients := make([]GetExternalIPAddresser, len(clients))
|
||||
for i, client := range clients {
|
||||
extIPClients[i] = client
|
||||
}
|
||||
DisplayExternalIPResults(extIPClients, errors, err)
|
||||
// Output:
|
||||
}
|
||||
|
||||
// Use discovered WANIPConnection services to find external IP addresses.
|
||||
func Example_WANIPConnection_GetExternalIPAddress() {
|
||||
clients, errors, err := internetgateway1.NewWANIPConnection1Clients()
|
||||
extIPClients := make([]GetExternalIPAddresser, len(clients))
|
||||
for i, client := range clients {
|
||||
extIPClients[i] = client
|
||||
}
|
||||
DisplayExternalIPResults(extIPClients, errors, err)
|
||||
// Output:
|
||||
}
|
||||
|
||||
type GetExternalIPAddresser interface {
|
||||
GetExternalIPAddress() (NewExternalIPAddress string, err error)
|
||||
GetServiceClient() *goupnp.ServiceClient
|
||||
}
|
||||
|
||||
func DisplayExternalIPResults(clients []GetExternalIPAddresser, errors []error, err error) {
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error discovering service with UPnP: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
fmt.Fprintf(os.Stderr, "Error discovering %d services:\n", len(errors))
|
||||
for _, err := range errors {
|
||||
fmt.Println(" ", err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Successfully discovered %d services:\n", len(clients))
|
||||
for _, client := range clients {
|
||||
device := &client.GetServiceClient().RootDevice.Device
|
||||
|
||||
fmt.Fprintln(os.Stderr, " Device:", device.FriendlyName)
|
||||
if addr, err := client.GetExternalIPAddress(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, " Failed to get external IP address: %v\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, " External IP address: %v\n", addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
85
Godeps/_workspace/src/github.com/huin/goupnp/soap/soap_test.go
generated
vendored
Normal file
85
Godeps/_workspace/src/github.com/huin/goupnp/soap/soap_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package soap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type capturingRoundTripper struct {
|
||||
err error
|
||||
resp *http.Response
|
||||
capturedReq *http.Request
|
||||
}
|
||||
|
||||
func (rt *capturingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
rt.capturedReq = req
|
||||
return rt.resp, rt.err
|
||||
}
|
||||
|
||||
func TestActionInputs(t *testing.T) {
|
||||
url, err := url.Parse("http://example.com/soap")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rt := &capturingRoundTripper{
|
||||
err: nil,
|
||||
resp: &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString(`
|
||||
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<s:Body>
|
||||
<u:myactionResponse xmlns:u="mynamespace">
|
||||
<A>valueA</A>
|
||||
<B>valueB</B>
|
||||
</u:myactionResponse>
|
||||
</s:Body>
|
||||
</s:Envelope>
|
||||
`)),
|
||||
},
|
||||
}
|
||||
client := SOAPClient{
|
||||
EndpointURL: *url,
|
||||
HTTPClient: http.Client{
|
||||
Transport: rt,
|
||||
},
|
||||
}
|
||||
|
||||
type In struct {
|
||||
Foo string
|
||||
Bar string `soap:"bar"`
|
||||
}
|
||||
type Out struct {
|
||||
A string
|
||||
B string
|
||||
}
|
||||
in := In{"foo", "bar"}
|
||||
gotOut := Out{}
|
||||
err = client.PerformAction("mynamespace", "myaction", &in, &gotOut)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
wantBody := (soapPrefix +
|
||||
`<u:myaction xmlns:u="mynamespace">` +
|
||||
`<Foo>foo</Foo>` +
|
||||
`<bar>bar</bar>` +
|
||||
`</u:myaction>` +
|
||||
soapSuffix)
|
||||
body, err := ioutil.ReadAll(rt.capturedReq.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gotBody := string(body)
|
||||
if wantBody != gotBody {
|
||||
t.Errorf("Bad request body\nwant: %q\n got: %q", wantBody, gotBody)
|
||||
}
|
||||
|
||||
wantOut := Out{"valueA", "valueB"}
|
||||
if !reflect.DeepEqual(wantOut, gotOut) {
|
||||
t.Errorf("Bad output\nwant: %+v\n got: %+v", wantOut, gotOut)
|
||||
}
|
||||
}
|
||||
481
Godeps/_workspace/src/github.com/huin/goupnp/soap/types_test.go
generated
vendored
Normal file
481
Godeps/_workspace/src/github.com/huin/goupnp/soap/types_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,481 @@
|
|||
package soap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type convTest interface {
|
||||
Marshal() (string, error)
|
||||
Unmarshal(string) (interface{}, error)
|
||||
Equal(result interface{}) bool
|
||||
}
|
||||
|
||||
// duper is an interface that convTest values may optionally also implement to
|
||||
// generate another convTest for a value in an otherwise identical testCase.
|
||||
type duper interface {
|
||||
Dupe(tag string) []convTest
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
value convTest
|
||||
str string
|
||||
wantMarshalErr bool
|
||||
wantUnmarshalErr bool
|
||||
noMarshal bool
|
||||
noUnMarshal bool
|
||||
tag string
|
||||
}
|
||||
|
||||
type Ui1Test uint8
|
||||
|
||||
func (v Ui1Test) Marshal() (string, error) {
|
||||
return MarshalUi1(uint8(v))
|
||||
}
|
||||
func (v Ui1Test) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalUi1(s)
|
||||
}
|
||||
func (v Ui1Test) Equal(result interface{}) bool {
|
||||
return uint8(v) == result.(uint8)
|
||||
}
|
||||
func (v Ui1Test) Dupe(tag string) []convTest {
|
||||
if tag == "dupe" {
|
||||
return []convTest{
|
||||
Ui2Test(v),
|
||||
Ui4Test(v),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Ui2Test uint16
|
||||
|
||||
func (v Ui2Test) Marshal() (string, error) {
|
||||
return MarshalUi2(uint16(v))
|
||||
}
|
||||
func (v Ui2Test) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalUi2(s)
|
||||
}
|
||||
func (v Ui2Test) Equal(result interface{}) bool {
|
||||
return uint16(v) == result.(uint16)
|
||||
}
|
||||
|
||||
type Ui4Test uint32
|
||||
|
||||
func (v Ui4Test) Marshal() (string, error) {
|
||||
return MarshalUi4(uint32(v))
|
||||
}
|
||||
func (v Ui4Test) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalUi4(s)
|
||||
}
|
||||
func (v Ui4Test) Equal(result interface{}) bool {
|
||||
return uint32(v) == result.(uint32)
|
||||
}
|
||||
|
||||
type I1Test int8
|
||||
|
||||
func (v I1Test) Marshal() (string, error) {
|
||||
return MarshalI1(int8(v))
|
||||
}
|
||||
func (v I1Test) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalI1(s)
|
||||
}
|
||||
func (v I1Test) Equal(result interface{}) bool {
|
||||
return int8(v) == result.(int8)
|
||||
}
|
||||
func (v I1Test) Dupe(tag string) []convTest {
|
||||
if tag == "dupe" {
|
||||
return []convTest{
|
||||
I2Test(v),
|
||||
I4Test(v),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type I2Test int16
|
||||
|
||||
func (v I2Test) Marshal() (string, error) {
|
||||
return MarshalI2(int16(v))
|
||||
}
|
||||
func (v I2Test) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalI2(s)
|
||||
}
|
||||
func (v I2Test) Equal(result interface{}) bool {
|
||||
return int16(v) == result.(int16)
|
||||
}
|
||||
|
||||
type I4Test int32
|
||||
|
||||
func (v I4Test) Marshal() (string, error) {
|
||||
return MarshalI4(int32(v))
|
||||
}
|
||||
func (v I4Test) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalI4(s)
|
||||
}
|
||||
func (v I4Test) Equal(result interface{}) bool {
|
||||
return int32(v) == result.(int32)
|
||||
}
|
||||
|
||||
type IntTest int64
|
||||
|
||||
func (v IntTest) Marshal() (string, error) {
|
||||
return MarshalInt(int64(v))
|
||||
}
|
||||
func (v IntTest) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalInt(s)
|
||||
}
|
||||
func (v IntTest) Equal(result interface{}) bool {
|
||||
return int64(v) == result.(int64)
|
||||
}
|
||||
|
||||
type Fixed14_4Test float64
|
||||
|
||||
func (v Fixed14_4Test) Marshal() (string, error) {
|
||||
return MarshalFixed14_4(float64(v))
|
||||
}
|
||||
func (v Fixed14_4Test) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalFixed14_4(s)
|
||||
}
|
||||
func (v Fixed14_4Test) Equal(result interface{}) bool {
|
||||
return math.Abs(float64(v)-result.(float64)) < 0.001
|
||||
}
|
||||
|
||||
type CharTest rune
|
||||
|
||||
func (v CharTest) Marshal() (string, error) {
|
||||
return MarshalChar(rune(v))
|
||||
}
|
||||
func (v CharTest) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalChar(s)
|
||||
}
|
||||
func (v CharTest) Equal(result interface{}) bool {
|
||||
return rune(v) == result.(rune)
|
||||
}
|
||||
|
||||
type DateTest struct{ time.Time }
|
||||
|
||||
func (v DateTest) Marshal() (string, error) {
|
||||
return MarshalDate(time.Time(v.Time))
|
||||
}
|
||||
func (v DateTest) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalDate(s)
|
||||
}
|
||||
func (v DateTest) Equal(result interface{}) bool {
|
||||
return v.Time.Equal(result.(time.Time))
|
||||
}
|
||||
func (v DateTest) Dupe(tag string) []convTest {
|
||||
if tag != "no:dateTime" {
|
||||
return []convTest{DateTimeTest{v.Time}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TimeOfDayTest struct {
|
||||
TimeOfDay
|
||||
}
|
||||
|
||||
func (v TimeOfDayTest) Marshal() (string, error) {
|
||||
return MarshalTimeOfDay(v.TimeOfDay)
|
||||
}
|
||||
func (v TimeOfDayTest) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalTimeOfDay(s)
|
||||
}
|
||||
func (v TimeOfDayTest) Equal(result interface{}) bool {
|
||||
return v.TimeOfDay == result.(TimeOfDay)
|
||||
}
|
||||
func (v TimeOfDayTest) Dupe(tag string) []convTest {
|
||||
if tag != "no:time.tz" {
|
||||
return []convTest{TimeOfDayTzTest{v.TimeOfDay}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TimeOfDayTzTest struct {
|
||||
TimeOfDay
|
||||
}
|
||||
|
||||
func (v TimeOfDayTzTest) Marshal() (string, error) {
|
||||
return MarshalTimeOfDayTz(v.TimeOfDay)
|
||||
}
|
||||
func (v TimeOfDayTzTest) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalTimeOfDayTz(s)
|
||||
}
|
||||
func (v TimeOfDayTzTest) Equal(result interface{}) bool {
|
||||
return v.TimeOfDay == result.(TimeOfDay)
|
||||
}
|
||||
|
||||
type DateTimeTest struct{ time.Time }
|
||||
|
||||
func (v DateTimeTest) Marshal() (string, error) {
|
||||
return MarshalDateTime(time.Time(v.Time))
|
||||
}
|
||||
func (v DateTimeTest) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalDateTime(s)
|
||||
}
|
||||
func (v DateTimeTest) Equal(result interface{}) bool {
|
||||
return v.Time.Equal(result.(time.Time))
|
||||
}
|
||||
func (v DateTimeTest) Dupe(tag string) []convTest {
|
||||
if tag != "no:dateTime.tz" {
|
||||
return []convTest{DateTimeTzTest{v.Time}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DateTimeTzTest struct{ time.Time }
|
||||
|
||||
func (v DateTimeTzTest) Marshal() (string, error) {
|
||||
return MarshalDateTimeTz(time.Time(v.Time))
|
||||
}
|
||||
func (v DateTimeTzTest) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalDateTimeTz(s)
|
||||
}
|
||||
func (v DateTimeTzTest) Equal(result interface{}) bool {
|
||||
return v.Time.Equal(result.(time.Time))
|
||||
}
|
||||
|
||||
type BooleanTest bool
|
||||
|
||||
func (v BooleanTest) Marshal() (string, error) {
|
||||
return MarshalBoolean(bool(v))
|
||||
}
|
||||
func (v BooleanTest) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalBoolean(s)
|
||||
}
|
||||
func (v BooleanTest) Equal(result interface{}) bool {
|
||||
return bool(v) == result.(bool)
|
||||
}
|
||||
|
||||
type BinBase64Test []byte
|
||||
|
||||
func (v BinBase64Test) Marshal() (string, error) {
|
||||
return MarshalBinBase64([]byte(v))
|
||||
}
|
||||
func (v BinBase64Test) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalBinBase64(s)
|
||||
}
|
||||
func (v BinBase64Test) Equal(result interface{}) bool {
|
||||
return bytes.Equal([]byte(v), result.([]byte))
|
||||
}
|
||||
|
||||
type BinHexTest []byte
|
||||
|
||||
func (v BinHexTest) Marshal() (string, error) {
|
||||
return MarshalBinHex([]byte(v))
|
||||
}
|
||||
func (v BinHexTest) Unmarshal(s string) (interface{}, error) {
|
||||
return UnmarshalBinHex(s)
|
||||
}
|
||||
func (v BinHexTest) Equal(result interface{}) bool {
|
||||
return bytes.Equal([]byte(v), result.([]byte))
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
const time010203 time.Duration = (1*3600 + 2*60 + 3) * time.Second
|
||||
const time0102 time.Duration = (1*3600 + 2*60) * time.Second
|
||||
const time01 time.Duration = (1 * 3600) * time.Second
|
||||
const time235959 time.Duration = (23*3600 + 59*60 + 59) * time.Second
|
||||
|
||||
// Fake out the local time for the implementation.
|
||||
localLoc = time.FixedZone("Fake/Local", 6*3600)
|
||||
defer func() {
|
||||
localLoc = time.Local
|
||||
}()
|
||||
|
||||
tests := []testCase{
|
||||
// ui1
|
||||
{str: "", value: Ui1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"},
|
||||
{str: " ", value: Ui1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"},
|
||||
{str: "abc", value: Ui1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"},
|
||||
{str: "-1", value: Ui1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"},
|
||||
{str: "0", value: Ui1Test(0), tag: "dupe"},
|
||||
{str: "1", value: Ui1Test(1), tag: "dupe"},
|
||||
{str: "255", value: Ui1Test(255), tag: "dupe"},
|
||||
{str: "256", value: Ui1Test(0), wantUnmarshalErr: true, noMarshal: true},
|
||||
|
||||
// ui2
|
||||
{str: "65535", value: Ui2Test(65535)},
|
||||
{str: "65536", value: Ui2Test(0), wantUnmarshalErr: true, noMarshal: true},
|
||||
|
||||
// ui4
|
||||
{str: "4294967295", value: Ui4Test(4294967295)},
|
||||
{str: "4294967296", value: Ui4Test(0), wantUnmarshalErr: true, noMarshal: true},
|
||||
|
||||
// i1
|
||||
{str: "", value: I1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"},
|
||||
{str: " ", value: I1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"},
|
||||
{str: "abc", value: I1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"},
|
||||
{str: "0", value: I1Test(0), tag: "dupe"},
|
||||
{str: "-1", value: I1Test(-1), tag: "dupe"},
|
||||
{str: "127", value: I1Test(127), tag: "dupe"},
|
||||
{str: "-128", value: I1Test(-128), tag: "dupe"},
|
||||
{str: "128", value: I1Test(0), wantUnmarshalErr: true, noMarshal: true},
|
||||
{str: "-129", value: I1Test(0), wantUnmarshalErr: true, noMarshal: true},
|
||||
|
||||
// i2
|
||||
{str: "32767", value: I2Test(32767)},
|
||||
{str: "-32768", value: I2Test(-32768)},
|
||||
{str: "32768", value: I2Test(0), wantUnmarshalErr: true, noMarshal: true},
|
||||
{str: "-32769", value: I2Test(0), wantUnmarshalErr: true, noMarshal: true},
|
||||
|
||||
// i4
|
||||
{str: "2147483647", value: I4Test(2147483647)},
|
||||
{str: "-2147483648", value: I4Test(-2147483648)},
|
||||
{str: "2147483648", value: I4Test(0), wantUnmarshalErr: true, noMarshal: true},
|
||||
{str: "-2147483649", value: I4Test(0), wantUnmarshalErr: true, noMarshal: true},
|
||||
|
||||
// int
|
||||
{str: "9223372036854775807", value: IntTest(9223372036854775807)},
|
||||
{str: "-9223372036854775808", value: IntTest(-9223372036854775808)},
|
||||
{str: "9223372036854775808", value: IntTest(0), wantUnmarshalErr: true, noMarshal: true},
|
||||
{str: "-9223372036854775809", value: IntTest(0), wantUnmarshalErr: true, noMarshal: true},
|
||||
|
||||
// fixed.14.4
|
||||
{str: "0.0000", value: Fixed14_4Test(0)},
|
||||
{str: "1.0000", value: Fixed14_4Test(1)},
|
||||
{str: "1.2346", value: Fixed14_4Test(1.23456)},
|
||||
{str: "-1.0000", value: Fixed14_4Test(-1)},
|
||||
{str: "-1.2346", value: Fixed14_4Test(-1.23456)},
|
||||
{str: "10000000000000.0000", value: Fixed14_4Test(1e13)},
|
||||
{str: "100000000000000.0000", value: Fixed14_4Test(1e14), wantMarshalErr: true, wantUnmarshalErr: true},
|
||||
{str: "-10000000000000.0000", value: Fixed14_4Test(-1e13)},
|
||||
{str: "-100000000000000.0000", value: Fixed14_4Test(-1e14), wantMarshalErr: true, wantUnmarshalErr: true},
|
||||
|
||||
// char
|
||||
{str: "a", value: CharTest('a')},
|
||||
{str: "z", value: CharTest('z')},
|
||||
{str: "\u1234", value: CharTest(0x1234)},
|
||||
{str: "aa", value: CharTest(0), wantMarshalErr: true, wantUnmarshalErr: true},
|
||||
{str: "", value: CharTest(0), wantMarshalErr: true, wantUnmarshalErr: true},
|
||||
|
||||
// date
|
||||
{str: "2013-10-08", value: DateTest{time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc)}, tag: "no:dateTime"},
|
||||
{str: "20131008", value: DateTest{time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc)}, noMarshal: true, tag: "no:dateTime"},
|
||||
{str: "2013-10-08T10:30:50", value: DateTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime"},
|
||||
{str: "2013-10-08T10:30:50Z", value: DateTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime"},
|
||||
{str: "", value: DateTest{}, wantMarshalErr: true, wantUnmarshalErr: true, noMarshal: true},
|
||||
{str: "-1", value: DateTest{}, wantUnmarshalErr: true, noMarshal: true},
|
||||
|
||||
// time
|
||||
{str: "00:00:00", value: TimeOfDayTest{TimeOfDay{FromMidnight: 0}}},
|
||||
{str: "000000", value: TimeOfDayTest{TimeOfDay{FromMidnight: 0}}, noMarshal: true},
|
||||
{str: "24:00:00", value: TimeOfDayTest{TimeOfDay{FromMidnight: 24 * time.Hour}}, noMarshal: true}, // ISO8601 special case
|
||||
{str: "24:01:00", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true},
|
||||
{str: "24:00:01", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true},
|
||||
{str: "25:00:00", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true},
|
||||
{str: "00:60:00", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true},
|
||||
{str: "00:00:60", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true},
|
||||
{str: "01:02:03", value: TimeOfDayTest{TimeOfDay{FromMidnight: time010203}}},
|
||||
{str: "010203", value: TimeOfDayTest{TimeOfDay{FromMidnight: time010203}}, noMarshal: true},
|
||||
{str: "23:59:59", value: TimeOfDayTest{TimeOfDay{FromMidnight: time235959}}},
|
||||
{str: "235959", value: TimeOfDayTest{TimeOfDay{FromMidnight: time235959}}, noMarshal: true},
|
||||
{str: "01:02", value: TimeOfDayTest{TimeOfDay{FromMidnight: time0102}}, noMarshal: true},
|
||||
{str: "0102", value: TimeOfDayTest{TimeOfDay{FromMidnight: time0102}}, noMarshal: true},
|
||||
{str: "01", value: TimeOfDayTest{TimeOfDay{FromMidnight: time01}}, noMarshal: true},
|
||||
{str: "foo 01:02:03", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"},
|
||||
{str: "foo\n01:02:03", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"},
|
||||
{str: "01:02:03 foo", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"},
|
||||
{str: "01:02:03\nfoo", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"},
|
||||
{str: "01:02:03Z", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"},
|
||||
{str: "01:02:03+01", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"},
|
||||
{str: "01:02:03+01:23", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"},
|
||||
{str: "01:02:03+0123", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"},
|
||||
{str: "01:02:03-01", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"},
|
||||
{str: "01:02:03-01:23", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"},
|
||||
{str: "01:02:03-0123", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"},
|
||||
|
||||
// time.tz
|
||||
{str: "24:00:01", value: TimeOfDayTzTest{}, wantUnmarshalErr: true, noMarshal: true},
|
||||
{str: "01Z", value: TimeOfDayTzTest{TimeOfDay{time01, true, 0}}, noMarshal: true},
|
||||
{str: "01:02:03Z", value: TimeOfDayTzTest{TimeOfDay{time010203, true, 0}}},
|
||||
{str: "01+01", value: TimeOfDayTzTest{TimeOfDay{time01, true, 3600}}, noMarshal: true},
|
||||
{str: "01:02:03+01", value: TimeOfDayTzTest{TimeOfDay{time010203, true, 3600}}, noMarshal: true},
|
||||
{str: "01:02:03+01:23", value: TimeOfDayTzTest{TimeOfDay{time010203, true, 3600 + 23*60}}},
|
||||
{str: "01:02:03+0123", value: TimeOfDayTzTest{TimeOfDay{time010203, true, 3600 + 23*60}}, noMarshal: true},
|
||||
{str: "01:02:03-01", value: TimeOfDayTzTest{TimeOfDay{time010203, true, -3600}}, noMarshal: true},
|
||||
{str: "01:02:03-01:23", value: TimeOfDayTzTest{TimeOfDay{time010203, true, -(3600 + 23*60)}}},
|
||||
{str: "01:02:03-0123", value: TimeOfDayTzTest{TimeOfDay{time010203, true, -(3600 + 23*60)}}, noMarshal: true},
|
||||
|
||||
// dateTime
|
||||
{str: "2013-10-08T00:00:00", value: DateTimeTest{time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc)}, tag: "no:dateTime.tz"},
|
||||
{str: "20131008", value: DateTimeTest{time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc)}, noMarshal: true},
|
||||
{str: "2013-10-08T10:30:50", value: DateTimeTest{time.Date(2013, 10, 8, 10, 30, 50, 0, localLoc)}, tag: "no:dateTime.tz"},
|
||||
{str: "2013-10-08T10:30:50T", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true},
|
||||
{str: "2013-10-08T10:30:50+01", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime.tz"},
|
||||
{str: "2013-10-08T10:30:50+01:23", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime.tz"},
|
||||
{str: "2013-10-08T10:30:50+0123", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime.tz"},
|
||||
{str: "2013-10-08T10:30:50-01", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime.tz"},
|
||||
{str: "2013-10-08T10:30:50-01:23", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime.tz"},
|
||||
{str: "2013-10-08T10:30:50-0123", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime.tz"},
|
||||
|
||||
// dateTime.tz
|
||||
{str: "2013-10-08T10:30:50", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, localLoc)}, noMarshal: true},
|
||||
{str: "2013-10-08T10:30:50+01", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("+01:00", 3600))}, noMarshal: true},
|
||||
{str: "2013-10-08T10:30:50+01:23", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("+01:23", 3600+23*60))}},
|
||||
{str: "2013-10-08T10:30:50+0123", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("+01:23", 3600+23*60))}, noMarshal: true},
|
||||
{str: "2013-10-08T10:30:50-01", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("-01:00", -3600))}, noMarshal: true},
|
||||
{str: "2013-10-08T10:30:50-01:23", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("-01:23", -(3600+23*60)))}},
|
||||
{str: "2013-10-08T10:30:50-0123", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("-01:23", -(3600+23*60)))}, noMarshal: true},
|
||||
|
||||
// boolean
|
||||
{str: "0", value: BooleanTest(false)},
|
||||
{str: "1", value: BooleanTest(true)},
|
||||
{str: "false", value: BooleanTest(false), noMarshal: true},
|
||||
{str: "true", value: BooleanTest(true), noMarshal: true},
|
||||
{str: "no", value: BooleanTest(false), noMarshal: true},
|
||||
{str: "yes", value: BooleanTest(true), noMarshal: true},
|
||||
{str: "", value: BooleanTest(false), noMarshal: true, wantUnmarshalErr: true},
|
||||
{str: "other", value: BooleanTest(false), noMarshal: true, wantUnmarshalErr: true},
|
||||
{str: "2", value: BooleanTest(false), noMarshal: true, wantUnmarshalErr: true},
|
||||
{str: "-1", value: BooleanTest(false), noMarshal: true, wantUnmarshalErr: true},
|
||||
|
||||
// bin.base64
|
||||
{str: "", value: BinBase64Test{}},
|
||||
{str: "YQ==", value: BinBase64Test("a")},
|
||||
{str: "TG9uZ2VyIFN0cmluZy4=", value: BinBase64Test("Longer String.")},
|
||||
{str: "TG9uZ2VyIEFsaWduZWQu", value: BinBase64Test("Longer Aligned.")},
|
||||
|
||||
// bin.hex
|
||||
{str: "", value: BinHexTest{}},
|
||||
{str: "61", value: BinHexTest("a")},
|
||||
{str: "4c6f6e67657220537472696e672e", value: BinHexTest("Longer String.")},
|
||||
{str: "4C6F6E67657220537472696E672E", value: BinHexTest("Longer String."), noMarshal: true},
|
||||
}
|
||||
|
||||
// Generate extra test cases from convTests that implement duper.
|
||||
var extras []testCase
|
||||
for i := range tests {
|
||||
if duper, ok := tests[i].value.(duper); ok {
|
||||
dupes := duper.Dupe(tests[i].tag)
|
||||
for _, duped := range dupes {
|
||||
dupedCase := testCase(tests[i])
|
||||
dupedCase.value = duped
|
||||
extras = append(extras, dupedCase)
|
||||
}
|
||||
}
|
||||
}
|
||||
tests = append(tests, extras...)
|
||||
|
||||
for _, test := range tests {
|
||||
if test.noMarshal {
|
||||
} else if resultStr, err := test.value.Marshal(); err != nil && !test.wantMarshalErr {
|
||||
t.Errorf("For %T marshal %v, want %q, got error: %v", test.value, test.value, test.str, err)
|
||||
} else if err == nil && test.wantMarshalErr {
|
||||
t.Errorf("For %T marshal %v, want error, got %q", test.value, test.value, resultStr)
|
||||
} else if err == nil && resultStr != test.str {
|
||||
t.Errorf("For %T marshal %v, want %q, got %q", test.value, test.value, test.str, resultStr)
|
||||
}
|
||||
|
||||
if test.noUnMarshal {
|
||||
} else if resultValue, err := test.value.Unmarshal(test.str); err != nil && !test.wantUnmarshalErr {
|
||||
t.Errorf("For %T unmarshal %q, want %v, got error: %v", test.value, test.str, test.value, err)
|
||||
} else if err == nil && test.wantUnmarshalErr {
|
||||
t.Errorf("For %T unmarshal %q, want error, got %v", test.value, test.str, resultValue)
|
||||
} else if err == nil && !test.value.Equal(resultValue) {
|
||||
t.Errorf("For %T unmarshal %q, want %v, got %v", test.value, test.str, test.value, resultValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Godeps/_workspace/src/github.com/mattn/go-sqlite3/.gitignore
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/mattn/go-sqlite3/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
*.db
|
||||
*.exe
|
||||
*.dll
|
||||
9
Godeps/_workspace/src/github.com/mattn/go-sqlite3/.travis.yml
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/mattn/go-sqlite3/.travis.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
language: go
|
||||
go:
|
||||
- tip
|
||||
before_install:
|
||||
- go get github.com/axw/gocov/gocov
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
script:
|
||||
- $HOME/gopath/bin/goveralls -repotoken 3qJVUE0iQwqnCbmNcDsjYu1nh4J4KIFXx
|
||||
21
Godeps/_workspace/src/github.com/mattn/go-sqlite3/LICENSE
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/mattn/go-sqlite3/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Yasuhiro Matsumoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
70
Godeps/_workspace/src/github.com/mattn/go-sqlite3/README.md
generated
vendored
Normal file
70
Godeps/_workspace/src/github.com/mattn/go-sqlite3/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
go-sqlite3
|
||||
==========
|
||||
|
||||
[](https://travis-ci.org/mattn/go-sqlite3)
|
||||
[](https://coveralls.io/r/mattn/go-sqlite3?branch=master)
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
sqlite3 driver conforming to the built-in database/sql interface
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
This package can be installed with the go get command:
|
||||
|
||||
go get github.com/mattn/go-sqlite3
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
API documentation can be found here: http://godoc.org/github.com/mattn/go-sqlite3
|
||||
|
||||
Examples can be found under the `./_example` directory
|
||||
|
||||
FAQ
|
||||
---
|
||||
|
||||
* Want to build go-sqlite3 with libsqlite3 on my linux.
|
||||
|
||||
Use `go build --tags "libsqlite3 linux"`
|
||||
|
||||
* Want to build go-sqlite3 with icu extension.
|
||||
|
||||
Use `go build --tags "icu"`
|
||||
|
||||
* Can't build go-sqlite3 on windows 64bit.
|
||||
|
||||
> Probably, you are using go 1.0, go1.0 has a problem when it comes to compiling/linking on windows 64bit.
|
||||
> See: https://github.com/mattn/go-sqlite3/issues/27
|
||||
|
||||
* Getting insert error while query is opened.
|
||||
|
||||
> You can pass some arguments into the connection string, for example, a URI.
|
||||
> See: https://github.com/mattn/go-sqlite3/issues/39
|
||||
|
||||
* Do you want cross compiling? mingw on Linux or Mac?
|
||||
|
||||
> See: https://github.com/mattn/go-sqlite3/issues/106
|
||||
> See also: http://www.limitlessfx.com/cross-compile-golang-app-for-windows-from-linux.html
|
||||
|
||||
* Want to get time.Time with current locale
|
||||
|
||||
Use `loc=auto` in SQLite3 filename schema like `file:foo.db?loc=auto`.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
MIT: http://mattn.mit-license.org/2012
|
||||
|
||||
sqlite3-binding.c, sqlite3-binding.h, sqlite3ext.h
|
||||
|
||||
The -binding suffix was added to avoid build failures under gccgo.
|
||||
|
||||
In this repository, those files are amalgamation code that copied from SQLite3. The license of those codes are depend on the license of SQLite3.
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
Yasuhiro Matsumoto (a.k.a mattn)
|
||||
70
Godeps/_workspace/src/github.com/mattn/go-sqlite3/backup.go
generated
vendored
Normal file
70
Godeps/_workspace/src/github.com/mattn/go-sqlite3/backup.go
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sqlite3
|
||||
|
||||
/*
|
||||
#include <sqlite3-binding.h>
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type SQLiteBackup struct {
|
||||
b *C.sqlite3_backup
|
||||
}
|
||||
|
||||
func (c *SQLiteConn) Backup(dest string, conn *SQLiteConn, src string) (*SQLiteBackup, error) {
|
||||
destptr := C.CString(dest)
|
||||
defer C.free(unsafe.Pointer(destptr))
|
||||
srcptr := C.CString(src)
|
||||
defer C.free(unsafe.Pointer(srcptr))
|
||||
|
||||
if b := C.sqlite3_backup_init(c.db, destptr, conn.db, srcptr); b != nil {
|
||||
bb := &SQLiteBackup{b: b}
|
||||
runtime.SetFinalizer(bb, (*SQLiteBackup).Finish)
|
||||
return bb, nil
|
||||
}
|
||||
return nil, c.lastError()
|
||||
}
|
||||
|
||||
// Backs up for one step. Calls the underlying `sqlite3_backup_step` function.
|
||||
// This function returns a boolean indicating if the backup is done and
|
||||
// an error signalling any other error. Done is returned if the underlying C
|
||||
// function returns SQLITE_DONE (Code 101)
|
||||
func (b *SQLiteBackup) Step(p int) (bool, error) {
|
||||
ret := C.sqlite3_backup_step(b.b, C.int(p))
|
||||
if ret == C.SQLITE_DONE {
|
||||
return true, nil
|
||||
} else if ret != 0 && ret != C.SQLITE_LOCKED && ret != C.SQLITE_BUSY {
|
||||
return false, Error{Code: ErrNo(ret)}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (b *SQLiteBackup) Remaining() int {
|
||||
return int(C.sqlite3_backup_remaining(b.b))
|
||||
}
|
||||
|
||||
func (b *SQLiteBackup) PageCount() int {
|
||||
return int(C.sqlite3_backup_pagecount(b.b))
|
||||
}
|
||||
|
||||
func (b *SQLiteBackup) Finish() error {
|
||||
return b.Close()
|
||||
}
|
||||
|
||||
func (b *SQLiteBackup) Close() error {
|
||||
ret := C.sqlite3_backup_finish(b.b)
|
||||
if ret != 0 {
|
||||
return Error{Code: ErrNo(ret)}
|
||||
}
|
||||
b.b = nil
|
||||
runtime.SetFinalizer(b, nil)
|
||||
return nil
|
||||
}
|
||||
147782
Godeps/_workspace/src/github.com/mattn/go-sqlite3/code/sqlite3-binding.c
generated
vendored
Normal file
147782
Godeps/_workspace/src/github.com/mattn/go-sqlite3/code/sqlite3-binding.c
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
7478
Godeps/_workspace/src/github.com/mattn/go-sqlite3/code/sqlite3-binding.h
generated
vendored
Normal file
7478
Godeps/_workspace/src/github.com/mattn/go-sqlite3/code/sqlite3-binding.h
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
487
Godeps/_workspace/src/github.com/mattn/go-sqlite3/code/sqlite3ext.h
generated
vendored
Normal file
487
Godeps/_workspace/src/github.com/mattn/go-sqlite3/code/sqlite3ext.h
generated
vendored
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
/*
|
||||
** 2006 June 7
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This header file defines the SQLite interface for use by
|
||||
** shared libraries that want to be imported as extensions into
|
||||
** an SQLite instance. Shared libraries that intend to be loaded
|
||||
** as extensions by SQLite should #include this file instead of
|
||||
** sqlite3.h.
|
||||
*/
|
||||
#ifndef _SQLITE3EXT_H_
|
||||
#define _SQLITE3EXT_H_
|
||||
#include "sqlite3-binding.h"
|
||||
|
||||
typedef struct sqlite3_api_routines sqlite3_api_routines;
|
||||
|
||||
/*
|
||||
** The following structure holds pointers to all of the SQLite API
|
||||
** routines.
|
||||
**
|
||||
** WARNING: In order to maintain backwards compatibility, add new
|
||||
** interfaces to the end of this structure only. If you insert new
|
||||
** interfaces in the middle of this structure, then older different
|
||||
** versions of SQLite will not be able to load each others' shared
|
||||
** libraries!
|
||||
*/
|
||||
struct sqlite3_api_routines {
|
||||
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
||||
int (*aggregate_count)(sqlite3_context*);
|
||||
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
||||
int (*bind_double)(sqlite3_stmt*,int,double);
|
||||
int (*bind_int)(sqlite3_stmt*,int,int);
|
||||
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
||||
int (*bind_null)(sqlite3_stmt*,int);
|
||||
int (*bind_parameter_count)(sqlite3_stmt*);
|
||||
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
||||
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
||||
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
||||
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
||||
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
||||
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
||||
int (*busy_timeout)(sqlite3*,int ms);
|
||||
int (*changes)(sqlite3*);
|
||||
int (*close)(sqlite3*);
|
||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const char*));
|
||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const void*));
|
||||
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_count)(sqlite3_stmt*pStmt);
|
||||
const char * (*column_database_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_decltype)(sqlite3_stmt*,int i);
|
||||
const void * (*column_decltype16)(sqlite3_stmt*,int);
|
||||
double (*column_double)(sqlite3_stmt*,int iCol);
|
||||
int (*column_int)(sqlite3_stmt*,int iCol);
|
||||
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
|
||||
const char * (*column_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_origin_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_origin_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_table_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_table_name16)(sqlite3_stmt*,int);
|
||||
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
|
||||
const void * (*column_text16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_type)(sqlite3_stmt*,int iCol);
|
||||
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
|
||||
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
||||
int (*complete)(const char*sql);
|
||||
int (*complete16)(const void*sql);
|
||||
int (*create_collation)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_collation16)(sqlite3*,const void*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
||||
int (*data_count)(sqlite3_stmt*pStmt);
|
||||
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
||||
int (*declare_vtab)(sqlite3*,const char*);
|
||||
int (*enable_shared_cache)(int);
|
||||
int (*errcode)(sqlite3*db);
|
||||
const char * (*errmsg)(sqlite3*);
|
||||
const void * (*errmsg16)(sqlite3*);
|
||||
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
|
||||
int (*expired)(sqlite3_stmt*);
|
||||
int (*finalize)(sqlite3_stmt*pStmt);
|
||||
void (*free)(void*);
|
||||
void (*free_table)(char**result);
|
||||
int (*get_autocommit)(sqlite3*);
|
||||
void * (*get_auxdata)(sqlite3_context*,int);
|
||||
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
||||
int (*global_recover)(void);
|
||||
void (*interruptx)(sqlite3*);
|
||||
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
||||
const char * (*libversion)(void);
|
||||
int (*libversion_number)(void);
|
||||
void *(*malloc)(int);
|
||||
char * (*mprintf)(const char*,...);
|
||||
int (*open)(const char*,sqlite3**);
|
||||
int (*open16)(const void*,sqlite3**);
|
||||
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
|
||||
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
||||
void *(*realloc)(void*,int);
|
||||
int (*reset)(sqlite3_stmt*pStmt);
|
||||
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_double)(sqlite3_context*,double);
|
||||
void (*result_error)(sqlite3_context*,const char*,int);
|
||||
void (*result_error16)(sqlite3_context*,const void*,int);
|
||||
void (*result_int)(sqlite3_context*,int);
|
||||
void (*result_int64)(sqlite3_context*,sqlite_int64);
|
||||
void (*result_null)(sqlite3_context*);
|
||||
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
|
||||
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
||||
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
||||
const char*,const char*),void*);
|
||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||
char * (*snprintf)(int,char*,const char*,...);
|
||||
int (*step)(sqlite3_stmt*);
|
||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
||||
char const**,char const**,int*,int*,int*);
|
||||
void (*thread_cleanup)(void);
|
||||
int (*total_changes)(sqlite3*);
|
||||
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
||||
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
||||
sqlite_int64),void*);
|
||||
void * (*user_data)(sqlite3_context*);
|
||||
const void * (*value_blob)(sqlite3_value*);
|
||||
int (*value_bytes)(sqlite3_value*);
|
||||
int (*value_bytes16)(sqlite3_value*);
|
||||
double (*value_double)(sqlite3_value*);
|
||||
int (*value_int)(sqlite3_value*);
|
||||
sqlite_int64 (*value_int64)(sqlite3_value*);
|
||||
int (*value_numeric_type)(sqlite3_value*);
|
||||
const unsigned char * (*value_text)(sqlite3_value*);
|
||||
const void * (*value_text16)(sqlite3_value*);
|
||||
const void * (*value_text16be)(sqlite3_value*);
|
||||
const void * (*value_text16le)(sqlite3_value*);
|
||||
int (*value_type)(sqlite3_value*);
|
||||
char *(*vmprintf)(const char*,va_list);
|
||||
/* Added ??? */
|
||||
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
|
||||
/* Added by 3.3.13 */
|
||||
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
int (*clear_bindings)(sqlite3_stmt*);
|
||||
/* Added by 3.4.1 */
|
||||
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
|
||||
void (*xDestroy)(void *));
|
||||
/* Added by 3.5.0 */
|
||||
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
|
||||
int (*blob_bytes)(sqlite3_blob*);
|
||||
int (*blob_close)(sqlite3_blob*);
|
||||
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
|
||||
int,sqlite3_blob**);
|
||||
int (*blob_read)(sqlite3_blob*,void*,int,int);
|
||||
int (*blob_write)(sqlite3_blob*,const void*,int,int);
|
||||
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*),
|
||||
void(*)(void*));
|
||||
int (*file_control)(sqlite3*,const char*,int,void*);
|
||||
sqlite3_int64 (*memory_highwater)(int);
|
||||
sqlite3_int64 (*memory_used)(void);
|
||||
sqlite3_mutex *(*mutex_alloc)(int);
|
||||
void (*mutex_enter)(sqlite3_mutex*);
|
||||
void (*mutex_free)(sqlite3_mutex*);
|
||||
void (*mutex_leave)(sqlite3_mutex*);
|
||||
int (*mutex_try)(sqlite3_mutex*);
|
||||
int (*open_v2)(const char*,sqlite3**,int,const char*);
|
||||
int (*release_memory)(int);
|
||||
void (*result_error_nomem)(sqlite3_context*);
|
||||
void (*result_error_toobig)(sqlite3_context*);
|
||||
int (*sleep)(int);
|
||||
void (*soft_heap_limit)(int);
|
||||
sqlite3_vfs *(*vfs_find)(const char*);
|
||||
int (*vfs_register)(sqlite3_vfs*,int);
|
||||
int (*vfs_unregister)(sqlite3_vfs*);
|
||||
int (*xthreadsafe)(void);
|
||||
void (*result_zeroblob)(sqlite3_context*,int);
|
||||
void (*result_error_code)(sqlite3_context*,int);
|
||||
int (*test_control)(int, ...);
|
||||
void (*randomness)(int,void*);
|
||||
sqlite3 *(*context_db_handle)(sqlite3_context*);
|
||||
int (*extended_result_codes)(sqlite3*,int);
|
||||
int (*limit)(sqlite3*,int,int);
|
||||
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
|
||||
const char *(*sql)(sqlite3_stmt*);
|
||||
int (*status)(int,int*,int*,int);
|
||||
int (*backup_finish)(sqlite3_backup*);
|
||||
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
|
||||
int (*backup_pagecount)(sqlite3_backup*);
|
||||
int (*backup_remaining)(sqlite3_backup*);
|
||||
int (*backup_step)(sqlite3_backup*,int);
|
||||
const char *(*compileoption_get)(int);
|
||||
int (*compileoption_used)(const char*);
|
||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void(*xDestroy)(void*));
|
||||
int (*db_config)(sqlite3*,int,...);
|
||||
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
||||
int (*db_status)(sqlite3*,int,int*,int*,int);
|
||||
int (*extended_errcode)(sqlite3*);
|
||||
void (*log)(int,const char*,...);
|
||||
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
|
||||
const char *(*sourceid)(void);
|
||||
int (*stmt_status)(sqlite3_stmt*,int,int);
|
||||
int (*strnicmp)(const char*,const char*,int);
|
||||
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
|
||||
int (*wal_autocheckpoint)(sqlite3*,int);
|
||||
int (*wal_checkpoint)(sqlite3*,const char*);
|
||||
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
||||
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
|
||||
int (*vtab_config)(sqlite3*,int op,...);
|
||||
int (*vtab_on_conflict)(sqlite3*);
|
||||
/* Version 3.7.16 and later */
|
||||
int (*close_v2)(sqlite3*);
|
||||
const char *(*db_filename)(sqlite3*,const char*);
|
||||
int (*db_readonly)(sqlite3*,const char*);
|
||||
int (*db_release_memory)(sqlite3*);
|
||||
const char *(*errstr)(int);
|
||||
int (*stmt_busy)(sqlite3_stmt*);
|
||||
int (*stmt_readonly)(sqlite3_stmt*);
|
||||
int (*stricmp)(const char*,const char*);
|
||||
int (*uri_boolean)(const char*,const char*,int);
|
||||
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
||||
const char *(*uri_parameter)(const char*,const char*);
|
||||
char *(*vsnprintf)(int,char*,const char*,va_list);
|
||||
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
||||
};
|
||||
|
||||
/*
|
||||
** The following macros redefine the API routines so that they are
|
||||
** redirected throught the global sqlite3_api structure.
|
||||
**
|
||||
** This header file is also used by the loadext.c source file
|
||||
** (part of the main SQLite library - not an extension) so that
|
||||
** it can get access to the sqlite3_api_routines structure
|
||||
** definition. But the main library does not want to redefine
|
||||
** the API. So the redefinition macros are only valid if the
|
||||
** SQLITE_CORE macros is undefined.
|
||||
*/
|
||||
#ifndef SQLITE_CORE
|
||||
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
||||
#endif
|
||||
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
||||
#define sqlite3_bind_double sqlite3_api->bind_double
|
||||
#define sqlite3_bind_int sqlite3_api->bind_int
|
||||
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
||||
#define sqlite3_bind_null sqlite3_api->bind_null
|
||||
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
||||
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
||||
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
||||
#define sqlite3_bind_text sqlite3_api->bind_text
|
||||
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
||||
#define sqlite3_bind_value sqlite3_api->bind_value
|
||||
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
||||
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
||||
#define sqlite3_changes sqlite3_api->changes
|
||||
#define sqlite3_close sqlite3_api->close
|
||||
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
||||
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
||||
#define sqlite3_column_blob sqlite3_api->column_blob
|
||||
#define sqlite3_column_bytes sqlite3_api->column_bytes
|
||||
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
|
||||
#define sqlite3_column_count sqlite3_api->column_count
|
||||
#define sqlite3_column_database_name sqlite3_api->column_database_name
|
||||
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
|
||||
#define sqlite3_column_decltype sqlite3_api->column_decltype
|
||||
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
|
||||
#define sqlite3_column_double sqlite3_api->column_double
|
||||
#define sqlite3_column_int sqlite3_api->column_int
|
||||
#define sqlite3_column_int64 sqlite3_api->column_int64
|
||||
#define sqlite3_column_name sqlite3_api->column_name
|
||||
#define sqlite3_column_name16 sqlite3_api->column_name16
|
||||
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
|
||||
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
|
||||
#define sqlite3_column_table_name sqlite3_api->column_table_name
|
||||
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
|
||||
#define sqlite3_column_text sqlite3_api->column_text
|
||||
#define sqlite3_column_text16 sqlite3_api->column_text16
|
||||
#define sqlite3_column_type sqlite3_api->column_type
|
||||
#define sqlite3_column_value sqlite3_api->column_value
|
||||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
||||
#define sqlite3_complete sqlite3_api->complete
|
||||
#define sqlite3_complete16 sqlite3_api->complete16
|
||||
#define sqlite3_create_collation sqlite3_api->create_collation
|
||||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
||||
#define sqlite3_create_function sqlite3_api->create_function
|
||||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
||||
#define sqlite3_create_module sqlite3_api->create_module
|
||||
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
||||
#define sqlite3_data_count sqlite3_api->data_count
|
||||
#define sqlite3_db_handle sqlite3_api->db_handle
|
||||
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
|
||||
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
|
||||
#define sqlite3_errcode sqlite3_api->errcode
|
||||
#define sqlite3_errmsg sqlite3_api->errmsg
|
||||
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
||||
#define sqlite3_exec sqlite3_api->exec
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_expired sqlite3_api->expired
|
||||
#endif
|
||||
#define sqlite3_finalize sqlite3_api->finalize
|
||||
#define sqlite3_free sqlite3_api->free
|
||||
#define sqlite3_free_table sqlite3_api->free_table
|
||||
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
||||
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
||||
#define sqlite3_get_table sqlite3_api->get_table
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
||||
#endif
|
||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
||||
#define sqlite3_libversion sqlite3_api->libversion
|
||||
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
||||
#define sqlite3_malloc sqlite3_api->malloc
|
||||
#define sqlite3_mprintf sqlite3_api->mprintf
|
||||
#define sqlite3_open sqlite3_api->open
|
||||
#define sqlite3_open16 sqlite3_api->open16
|
||||
#define sqlite3_prepare sqlite3_api->prepare
|
||||
#define sqlite3_prepare16 sqlite3_api->prepare16
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_profile sqlite3_api->profile
|
||||
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
||||
#define sqlite3_realloc sqlite3_api->realloc
|
||||
#define sqlite3_reset sqlite3_api->reset
|
||||
#define sqlite3_result_blob sqlite3_api->result_blob
|
||||
#define sqlite3_result_double sqlite3_api->result_double
|
||||
#define sqlite3_result_error sqlite3_api->result_error
|
||||
#define sqlite3_result_error16 sqlite3_api->result_error16
|
||||
#define sqlite3_result_int sqlite3_api->result_int
|
||||
#define sqlite3_result_int64 sqlite3_api->result_int64
|
||||
#define sqlite3_result_null sqlite3_api->result_null
|
||||
#define sqlite3_result_text sqlite3_api->result_text
|
||||
#define sqlite3_result_text16 sqlite3_api->result_text16
|
||||
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
||||
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
||||
#define sqlite3_result_value sqlite3_api->result_value
|
||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
||||
#define sqlite3_snprintf sqlite3_api->snprintf
|
||||
#define sqlite3_step sqlite3_api->step
|
||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||
#define sqlite3_total_changes sqlite3_api->total_changes
|
||||
#define sqlite3_trace sqlite3_api->trace
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
||||
#endif
|
||||
#define sqlite3_update_hook sqlite3_api->update_hook
|
||||
#define sqlite3_user_data sqlite3_api->user_data
|
||||
#define sqlite3_value_blob sqlite3_api->value_blob
|
||||
#define sqlite3_value_bytes sqlite3_api->value_bytes
|
||||
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
|
||||
#define sqlite3_value_double sqlite3_api->value_double
|
||||
#define sqlite3_value_int sqlite3_api->value_int
|
||||
#define sqlite3_value_int64 sqlite3_api->value_int64
|
||||
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
|
||||
#define sqlite3_value_text sqlite3_api->value_text
|
||||
#define sqlite3_value_text16 sqlite3_api->value_text16
|
||||
#define sqlite3_value_text16be sqlite3_api->value_text16be
|
||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||
#define sqlite3_value_type sqlite3_api->value_type
|
||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
|
||||
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
|
||||
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
|
||||
#define sqlite3_blob_close sqlite3_api->blob_close
|
||||
#define sqlite3_blob_open sqlite3_api->blob_open
|
||||
#define sqlite3_blob_read sqlite3_api->blob_read
|
||||
#define sqlite3_blob_write sqlite3_api->blob_write
|
||||
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
|
||||
#define sqlite3_file_control sqlite3_api->file_control
|
||||
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
|
||||
#define sqlite3_memory_used sqlite3_api->memory_used
|
||||
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
|
||||
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
|
||||
#define sqlite3_mutex_free sqlite3_api->mutex_free
|
||||
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
|
||||
#define sqlite3_mutex_try sqlite3_api->mutex_try
|
||||
#define sqlite3_open_v2 sqlite3_api->open_v2
|
||||
#define sqlite3_release_memory sqlite3_api->release_memory
|
||||
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
||||
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
||||
#define sqlite3_sleep sqlite3_api->sleep
|
||||
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
||||
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
||||
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
||||
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
||||
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
||||
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
||||
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
||||
#define sqlite3_test_control sqlite3_api->test_control
|
||||
#define sqlite3_randomness sqlite3_api->randomness
|
||||
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
|
||||
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
|
||||
#define sqlite3_limit sqlite3_api->limit
|
||||
#define sqlite3_next_stmt sqlite3_api->next_stmt
|
||||
#define sqlite3_sql sqlite3_api->sql
|
||||
#define sqlite3_status sqlite3_api->status
|
||||
#define sqlite3_backup_finish sqlite3_api->backup_finish
|
||||
#define sqlite3_backup_init sqlite3_api->backup_init
|
||||
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
|
||||
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
|
||||
#define sqlite3_backup_step sqlite3_api->backup_step
|
||||
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
|
||||
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
|
||||
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
|
||||
#define sqlite3_db_config sqlite3_api->db_config
|
||||
#define sqlite3_db_mutex sqlite3_api->db_mutex
|
||||
#define sqlite3_db_status sqlite3_api->db_status
|
||||
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
|
||||
#define sqlite3_log sqlite3_api->log
|
||||
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
|
||||
#define sqlite3_sourceid sqlite3_api->sourceid
|
||||
#define sqlite3_stmt_status sqlite3_api->stmt_status
|
||||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
||||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
||||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||||
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
||||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
||||
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
|
||||
/* Version 3.7.16 and later */
|
||||
#define sqlite3_close_v2 sqlite3_api->close_v2
|
||||
#define sqlite3_db_filename sqlite3_api->db_filename
|
||||
#define sqlite3_db_readonly sqlite3_api->db_readonly
|
||||
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
|
||||
#define sqlite3_errstr sqlite3_api->errstr
|
||||
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
|
||||
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
|
||||
#define sqlite3_stricmp sqlite3_api->stricmp
|
||||
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
||||
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
||||
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
||||
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
|
||||
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
||||
#endif /* SQLITE_CORE */
|
||||
|
||||
#ifndef SQLITE_CORE
|
||||
/* This case when the file really is being compiled as a loadable
|
||||
** extension */
|
||||
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
|
||||
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
|
||||
# define SQLITE_EXTENSION_INIT3 \
|
||||
extern const sqlite3_api_routines *sqlite3_api;
|
||||
#else
|
||||
/* This case when the file is being statically linked into the
|
||||
** application */
|
||||
# define SQLITE_EXTENSION_INIT1 /*no-op*/
|
||||
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
|
||||
# define SQLITE_EXTENSION_INIT3 /*no-op*/
|
||||
#endif
|
||||
|
||||
#endif /* _SQLITE3EXT_H_ */
|
||||
95
Godeps/_workspace/src/github.com/mattn/go-sqlite3/doc.go
generated
vendored
Normal file
95
Godeps/_workspace/src/github.com/mattn/go-sqlite3/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Package sqlite3 provides interface to SQLite3 databases.
|
||||
|
||||
This works as driver for database/sql.
|
||||
|
||||
Installation
|
||||
|
||||
go get github.com/mattn/go-sqlite3
|
||||
|
||||
Supported Types
|
||||
|
||||
Currently, go-sqlite3 support following data types.
|
||||
|
||||
+------------------------------+
|
||||
|go | sqlite3 |
|
||||
|----------|-------------------|
|
||||
|nil | null |
|
||||
|int | integer |
|
||||
|int64 | integer |
|
||||
|float64 | float |
|
||||
|bool | integer |
|
||||
|[]byte | blob |
|
||||
|string | text |
|
||||
|time.Time | timestamp/datetime|
|
||||
+------------------------------+
|
||||
|
||||
SQLite3 Extension
|
||||
|
||||
You can write your own extension module for sqlite3. For example, below is a
|
||||
extension for Regexp matcher operation.
|
||||
|
||||
#include <pcre.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sqlite3ext.h>
|
||||
|
||||
SQLITE_EXTENSION_INIT1
|
||||
static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
if (argc >= 2) {
|
||||
const char *target = (const char *)sqlite3_value_text(argv[1]);
|
||||
const char *pattern = (const char *)sqlite3_value_text(argv[0]);
|
||||
const char* errstr = NULL;
|
||||
int erroff = 0;
|
||||
int vec[500];
|
||||
int n, rc;
|
||||
pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL);
|
||||
rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500);
|
||||
if (rc <= 0) {
|
||||
sqlite3_result_error(context, errstr, 0);
|
||||
return;
|
||||
}
|
||||
sqlite3_result_int(context, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_extension_init(sqlite3 *db, char **errmsg,
|
||||
const sqlite3_api_routines *api) {
|
||||
SQLITE_EXTENSION_INIT2(api);
|
||||
return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8,
|
||||
(void*)db, regexp_func, NULL, NULL);
|
||||
}
|
||||
|
||||
It need to build as so/dll shared library. And you need to register
|
||||
extension module like below.
|
||||
|
||||
sql.Register("sqlite3_with_extensions",
|
||||
&sqlite3.SQLiteDriver{
|
||||
Extensions: []string{
|
||||
"sqlite3_mod_regexp",
|
||||
},
|
||||
})
|
||||
|
||||
Then, you can use this extension.
|
||||
|
||||
rows, err := db.Query("select text from mytable where name regexp '^golang'")
|
||||
|
||||
Connection Hook
|
||||
|
||||
You can hook and inject your codes when connection established. database/sql
|
||||
doesn't provide the way to get native go-sqlite3 interfaces. So if you want,
|
||||
you need to hook ConnectHook and get the SQLiteConn.
|
||||
|
||||
sql.Register("sqlite3_with_hook_example",
|
||||
&sqlite3.SQLiteDriver{
|
||||
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
|
||||
sqlite3conn = append(sqlite3conn, conn)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
*/
|
||||
package sqlite3
|
||||
128
Godeps/_workspace/src/github.com/mattn/go-sqlite3/error.go
generated
vendored
Normal file
128
Godeps/_workspace/src/github.com/mattn/go-sqlite3/error.go
generated
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sqlite3
|
||||
|
||||
import "C"
|
||||
|
||||
type ErrNo int
|
||||
|
||||
const ErrNoMask C.int = 0xff
|
||||
|
||||
type ErrNoExtended int
|
||||
|
||||
type Error struct {
|
||||
Code ErrNo /* The error code returned by SQLite */
|
||||
ExtendedCode ErrNoExtended /* The extended error code returned by SQLite */
|
||||
err string /* The error string returned by sqlite3_errmsg(),
|
||||
this usually contains more specific details. */
|
||||
}
|
||||
|
||||
// result codes from http://www.sqlite.org/c3ref/c_abort.html
|
||||
var (
|
||||
ErrError = ErrNo(1) /* SQL error or missing database */
|
||||
ErrInternal = ErrNo(2) /* Internal logic error in SQLite */
|
||||
ErrPerm = ErrNo(3) /* Access permission denied */
|
||||
ErrAbort = ErrNo(4) /* Callback routine requested an abort */
|
||||
ErrBusy = ErrNo(5) /* The database file is locked */
|
||||
ErrLocked = ErrNo(6) /* A table in the database is locked */
|
||||
ErrNomem = ErrNo(7) /* A malloc() failed */
|
||||
ErrReadonly = ErrNo(8) /* Attempt to write a readonly database */
|
||||
ErrInterrupt = ErrNo(9) /* Operation terminated by sqlite3_interrupt() */
|
||||
ErrIoErr = ErrNo(10) /* Some kind of disk I/O error occurred */
|
||||
ErrCorrupt = ErrNo(11) /* The database disk image is malformed */
|
||||
ErrNotFound = ErrNo(12) /* Unknown opcode in sqlite3_file_control() */
|
||||
ErrFull = ErrNo(13) /* Insertion failed because database is full */
|
||||
ErrCantOpen = ErrNo(14) /* Unable to open the database file */
|
||||
ErrProtocol = ErrNo(15) /* Database lock protocol error */
|
||||
ErrEmpty = ErrNo(16) /* Database is empty */
|
||||
ErrSchema = ErrNo(17) /* The database schema changed */
|
||||
ErrTooBig = ErrNo(18) /* String or BLOB exceeds size limit */
|
||||
ErrConstraint = ErrNo(19) /* Abort due to constraint violation */
|
||||
ErrMismatch = ErrNo(20) /* Data type mismatch */
|
||||
ErrMisuse = ErrNo(21) /* Library used incorrectly */
|
||||
ErrNoLFS = ErrNo(22) /* Uses OS features not supported on host */
|
||||
ErrAuth = ErrNo(23) /* Authorization denied */
|
||||
ErrFormat = ErrNo(24) /* Auxiliary database format error */
|
||||
ErrRange = ErrNo(25) /* 2nd parameter to sqlite3_bind out of range */
|
||||
ErrNotADB = ErrNo(26) /* File opened that is not a database file */
|
||||
ErrNotice = ErrNo(27) /* Notifications from sqlite3_log() */
|
||||
ErrWarning = ErrNo(28) /* Warnings from sqlite3_log() */
|
||||
)
|
||||
|
||||
func (err ErrNo) Error() string {
|
||||
return Error{Code: err}.Error()
|
||||
}
|
||||
|
||||
func (err ErrNo) Extend(by int) ErrNoExtended {
|
||||
return ErrNoExtended(int(err) | (by << 8))
|
||||
}
|
||||
|
||||
func (err ErrNoExtended) Error() string {
|
||||
return Error{Code: ErrNo(C.int(err) & ErrNoMask), ExtendedCode: err}.Error()
|
||||
}
|
||||
|
||||
func (err Error) Error() string {
|
||||
if err.err != "" {
|
||||
return err.err
|
||||
}
|
||||
return errorString(err)
|
||||
}
|
||||
|
||||
// result codes from http://www.sqlite.org/c3ref/c_abort_rollback.html
|
||||
var (
|
||||
ErrIoErrRead = ErrIoErr.Extend(1)
|
||||
ErrIoErrShortRead = ErrIoErr.Extend(2)
|
||||
ErrIoErrWrite = ErrIoErr.Extend(3)
|
||||
ErrIoErrFsync = ErrIoErr.Extend(4)
|
||||
ErrIoErrDirFsync = ErrIoErr.Extend(5)
|
||||
ErrIoErrTruncate = ErrIoErr.Extend(6)
|
||||
ErrIoErrFstat = ErrIoErr.Extend(7)
|
||||
ErrIoErrUnlock = ErrIoErr.Extend(8)
|
||||
ErrIoErrRDlock = ErrIoErr.Extend(9)
|
||||
ErrIoErrDelete = ErrIoErr.Extend(10)
|
||||
ErrIoErrBlocked = ErrIoErr.Extend(11)
|
||||
ErrIoErrNoMem = ErrIoErr.Extend(12)
|
||||
ErrIoErrAccess = ErrIoErr.Extend(13)
|
||||
ErrIoErrCheckReservedLock = ErrIoErr.Extend(14)
|
||||
ErrIoErrLock = ErrIoErr.Extend(15)
|
||||
ErrIoErrClose = ErrIoErr.Extend(16)
|
||||
ErrIoErrDirClose = ErrIoErr.Extend(17)
|
||||
ErrIoErrSHMOpen = ErrIoErr.Extend(18)
|
||||
ErrIoErrSHMSize = ErrIoErr.Extend(19)
|
||||
ErrIoErrSHMLock = ErrIoErr.Extend(20)
|
||||
ErrIoErrSHMMap = ErrIoErr.Extend(21)
|
||||
ErrIoErrSeek = ErrIoErr.Extend(22)
|
||||
ErrIoErrDeleteNoent = ErrIoErr.Extend(23)
|
||||
ErrIoErrMMap = ErrIoErr.Extend(24)
|
||||
ErrIoErrGetTempPath = ErrIoErr.Extend(25)
|
||||
ErrIoErrConvPath = ErrIoErr.Extend(26)
|
||||
ErrLockedSharedCache = ErrLocked.Extend(1)
|
||||
ErrBusyRecovery = ErrBusy.Extend(1)
|
||||
ErrBusySnapshot = ErrBusy.Extend(2)
|
||||
ErrCantOpenNoTempDir = ErrCantOpen.Extend(1)
|
||||
ErrCantOpenIsDir = ErrCantOpen.Extend(2)
|
||||
ErrCantOpenFullPath = ErrCantOpen.Extend(3)
|
||||
ErrCantOpenConvPath = ErrCantOpen.Extend(4)
|
||||
ErrCorruptVTab = ErrCorrupt.Extend(1)
|
||||
ErrReadonlyRecovery = ErrReadonly.Extend(1)
|
||||
ErrReadonlyCantLock = ErrReadonly.Extend(2)
|
||||
ErrReadonlyRollback = ErrReadonly.Extend(3)
|
||||
ErrReadonlyDbMoved = ErrReadonly.Extend(4)
|
||||
ErrAbortRollback = ErrAbort.Extend(2)
|
||||
ErrConstraintCheck = ErrConstraint.Extend(1)
|
||||
ErrConstraintCommitHook = ErrConstraint.Extend(2)
|
||||
ErrConstraintForeignKey = ErrConstraint.Extend(3)
|
||||
ErrConstraintFunction = ErrConstraint.Extend(4)
|
||||
ErrConstraintNotNull = ErrConstraint.Extend(5)
|
||||
ErrConstraintPrimaryKey = ErrConstraint.Extend(6)
|
||||
ErrConstraintTrigger = ErrConstraint.Extend(7)
|
||||
ErrConstraintUnique = ErrConstraint.Extend(8)
|
||||
ErrConstraintVTab = ErrConstraint.Extend(9)
|
||||
ErrConstraintRowId = ErrConstraint.Extend(10)
|
||||
ErrNoticeRecoverWAL = ErrNotice.Extend(1)
|
||||
ErrNoticeRecoverRollback = ErrNotice.Extend(2)
|
||||
ErrWarningAutoIndex = ErrWarning.Extend(1)
|
||||
)
|
||||
242
Godeps/_workspace/src/github.com/mattn/go-sqlite3/error_test.go
generated
vendored
Normal file
242
Godeps/_workspace/src/github.com/mattn/go-sqlite3/error_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSimpleError(t *testing.T) {
|
||||
e := ErrError.Error()
|
||||
if e != "SQL logic error or missing database" {
|
||||
t.Error("wrong error code:" + e)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorruptDbErrors(t *testing.T) {
|
||||
dirName, err := ioutil.TempDir("", "sqlite3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dirName)
|
||||
|
||||
dbFileName := path.Join(dirName, "test.db")
|
||||
f, err := os.Create(dbFileName)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
f.Write([]byte{1, 2, 3, 4, 5})
|
||||
f.Close()
|
||||
|
||||
db, err := sql.Open("sqlite3", dbFileName)
|
||||
if err == nil {
|
||||
_, err = db.Exec("drop table foo")
|
||||
}
|
||||
|
||||
sqliteErr := err.(Error)
|
||||
if sqliteErr.Code != ErrNotADB {
|
||||
t.Error("wrong error code for corrupted DB")
|
||||
}
|
||||
if err.Error() == "" {
|
||||
t.Error("wrong error string for corrupted DB")
|
||||
}
|
||||
db.Close()
|
||||
}
|
||||
|
||||
func TestSqlLogicErrors(t *testing.T) {
|
||||
dirName, err := ioutil.TempDir("", "sqlite3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dirName)
|
||||
|
||||
dbFileName := path.Join(dirName, "test.db")
|
||||
db, err := sql.Open("sqlite3", dbFileName)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
const expectedErr = "table Foo already exists"
|
||||
_, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)")
|
||||
if err.Error() != expectedErr {
|
||||
t.Errorf("Unexpected error: %s, expected %s", err.Error(), expectedErr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestExtendedErrorCodes_ForeignKey(t *testing.T) {
|
||||
dirName, err := ioutil.TempDir("", "sqlite3-err")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dirName)
|
||||
|
||||
dbFileName := path.Join(dirName, "test.db")
|
||||
db, err := sql.Open("sqlite3", dbFileName)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec("PRAGMA foreign_keys=ON;")
|
||||
if err != nil {
|
||||
t.Errorf("PRAGMA foreign_keys=ON: %v", err)
|
||||
}
|
||||
|
||||
_, err = db.Exec(`CREATE TABLE Foo (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
value INTEGER NOT NULL,
|
||||
ref INTEGER NULL REFERENCES Foo (id),
|
||||
UNIQUE(value)
|
||||
);`)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = db.Exec("INSERT INTO Foo (ref, value) VALUES (100, 100);")
|
||||
if err == nil {
|
||||
t.Error("No error!")
|
||||
} else {
|
||||
sqliteErr := err.(Error)
|
||||
if sqliteErr.Code != ErrConstraint {
|
||||
t.Errorf("Wrong basic error code: %d != %d",
|
||||
sqliteErr.Code, ErrConstraint)
|
||||
}
|
||||
if sqliteErr.ExtendedCode != ErrConstraintForeignKey {
|
||||
t.Errorf("Wrong extended error code: %d != %d",
|
||||
sqliteErr.ExtendedCode, ErrConstraintForeignKey)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestExtendedErrorCodes_NotNull(t *testing.T) {
|
||||
dirName, err := ioutil.TempDir("", "sqlite3-err")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dirName)
|
||||
|
||||
dbFileName := path.Join(dirName, "test.db")
|
||||
db, err := sql.Open("sqlite3", dbFileName)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec("PRAGMA foreign_keys=ON;")
|
||||
if err != nil {
|
||||
t.Errorf("PRAGMA foreign_keys=ON: %v", err)
|
||||
}
|
||||
|
||||
_, err = db.Exec(`CREATE TABLE Foo (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
value INTEGER NOT NULL,
|
||||
ref INTEGER NULL REFERENCES Foo (id),
|
||||
UNIQUE(value)
|
||||
);`)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
res, err := db.Exec("INSERT INTO Foo (value) VALUES (100);")
|
||||
if err != nil {
|
||||
t.Fatalf("Creating first row: %v", err)
|
||||
}
|
||||
|
||||
id, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
t.Fatalf("Retrieving last insert id: %v", err)
|
||||
}
|
||||
|
||||
_, err = db.Exec("INSERT INTO Foo (ref) VALUES (?);", id)
|
||||
if err == nil {
|
||||
t.Error("No error!")
|
||||
} else {
|
||||
sqliteErr := err.(Error)
|
||||
if sqliteErr.Code != ErrConstraint {
|
||||
t.Errorf("Wrong basic error code: %d != %d",
|
||||
sqliteErr.Code, ErrConstraint)
|
||||
}
|
||||
if sqliteErr.ExtendedCode != ErrConstraintNotNull {
|
||||
t.Errorf("Wrong extended error code: %d != %d",
|
||||
sqliteErr.ExtendedCode, ErrConstraintNotNull)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestExtendedErrorCodes_Unique(t *testing.T) {
|
||||
dirName, err := ioutil.TempDir("", "sqlite3-err")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dirName)
|
||||
|
||||
dbFileName := path.Join(dirName, "test.db")
|
||||
db, err := sql.Open("sqlite3", dbFileName)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec("PRAGMA foreign_keys=ON;")
|
||||
if err != nil {
|
||||
t.Errorf("PRAGMA foreign_keys=ON: %v", err)
|
||||
}
|
||||
|
||||
_, err = db.Exec(`CREATE TABLE Foo (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
value INTEGER NOT NULL,
|
||||
ref INTEGER NULL REFERENCES Foo (id),
|
||||
UNIQUE(value)
|
||||
);`)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
res, err := db.Exec("INSERT INTO Foo (value) VALUES (100);")
|
||||
if err != nil {
|
||||
t.Fatalf("Creating first row: %v", err)
|
||||
}
|
||||
|
||||
id, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
t.Fatalf("Retrieving last insert id: %v", err)
|
||||
}
|
||||
|
||||
_, err = db.Exec("INSERT INTO Foo (ref, value) VALUES (?, 100);", id)
|
||||
if err == nil {
|
||||
t.Error("No error!")
|
||||
} else {
|
||||
sqliteErr := err.(Error)
|
||||
if sqliteErr.Code != ErrConstraint {
|
||||
t.Errorf("Wrong basic error code: %d != %d",
|
||||
sqliteErr.Code, ErrConstraint)
|
||||
}
|
||||
if sqliteErr.ExtendedCode != ErrConstraintUnique {
|
||||
t.Errorf("Wrong extended error code: %d != %d",
|
||||
sqliteErr.ExtendedCode, ErrConstraintUnique)
|
||||
}
|
||||
extended := sqliteErr.Code.Extend(3).Error()
|
||||
expected := "constraint failed"
|
||||
if extended != expected {
|
||||
t.Errorf("Wrong basic error code: %q != %q",
|
||||
extended, expected)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
4
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3-binding.c
generated
vendored
Normal file
4
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3-binding.c
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#ifndef USE_LIBSQLITE3
|
||||
# include "code/sqlite3-binding.c"
|
||||
#endif
|
||||
|
||||
5
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3-binding.h
generated
vendored
Normal file
5
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3-binding.h
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#ifndef USE_LIBSQLITE3
|
||||
#include "code/sqlite3-binding.h"
|
||||
#else
|
||||
#include <sqlite3.h>
|
||||
#endif
|
||||
660
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3.go
generated
vendored
Normal file
660
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3.go
generated
vendored
Normal file
|
|
@ -0,0 +1,660 @@
|
|||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sqlite3
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -std=gnu99
|
||||
#cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE
|
||||
#cgo CFLAGS: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS
|
||||
#include <sqlite3-binding.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
# include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OPEN_READWRITE
|
||||
# define SQLITE_OPEN_READWRITE 0
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OPEN_FULLMUTEX
|
||||
# define SQLITE_OPEN_FULLMUTEX 0
|
||||
#endif
|
||||
|
||||
static int
|
||||
_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs) {
|
||||
#ifdef SQLITE_OPEN_URI
|
||||
return sqlite3_open_v2(filename, ppDb, flags | SQLITE_OPEN_URI, zVfs);
|
||||
#else
|
||||
return sqlite3_open_v2(filename, ppDb, flags, zVfs);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
_sqlite3_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
|
||||
return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
static int
|
||||
_sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
|
||||
return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static int
|
||||
_sqlite3_exec(sqlite3* db, const char* pcmd, long long* rowid, long long* changes)
|
||||
{
|
||||
int rv = sqlite3_exec(db, pcmd, 0, 0, 0);
|
||||
*rowid = (long long) sqlite3_last_insert_rowid(db);
|
||||
*changes = (long long) sqlite3_changes(db);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int
|
||||
_sqlite3_step(sqlite3_stmt* stmt, long long* rowid, long long* changes)
|
||||
{
|
||||
int rv = sqlite3_step(stmt);
|
||||
sqlite3* db = sqlite3_db_handle(stmt);
|
||||
*rowid = (long long) sqlite3_last_insert_rowid(db);
|
||||
*changes = (long long) sqlite3_changes(db);
|
||||
return rv;
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Timestamp formats understood by both this module and SQLite.
|
||||
// The first format in the slice will be used when saving time values
|
||||
// into the database. When parsing a string from a timestamp or
|
||||
// datetime column, the formats are tried in order.
|
||||
var SQLiteTimestampFormats = []string{
|
||||
"2006-01-02 15:04:05.999999999",
|
||||
"2006-01-02T15:04:05.999999999",
|
||||
"2006-01-02 15:04:05",
|
||||
"2006-01-02T15:04:05",
|
||||
"2006-01-02 15:04",
|
||||
"2006-01-02T15:04",
|
||||
"2006-01-02",
|
||||
"2006-01-02 15:04:05-07:00",
|
||||
}
|
||||
|
||||
func init() {
|
||||
sql.Register("sqlite3", &SQLiteDriver{})
|
||||
}
|
||||
|
||||
// Return SQLite library Version information.
|
||||
func Version() (libVersion string, libVersionNumber int, sourceId string) {
|
||||
libVersion = C.GoString(C.sqlite3_libversion())
|
||||
libVersionNumber = int(C.sqlite3_libversion_number())
|
||||
sourceId = C.GoString(C.sqlite3_sourceid())
|
||||
return libVersion, libVersionNumber, sourceId
|
||||
}
|
||||
|
||||
// Driver struct.
|
||||
type SQLiteDriver struct {
|
||||
Extensions []string
|
||||
ConnectHook func(*SQLiteConn) error
|
||||
}
|
||||
|
||||
// Conn struct.
|
||||
type SQLiteConn struct {
|
||||
db *C.sqlite3
|
||||
loc *time.Location
|
||||
txlock string
|
||||
}
|
||||
|
||||
// Tx struct.
|
||||
type SQLiteTx struct {
|
||||
c *SQLiteConn
|
||||
}
|
||||
|
||||
// Stmt struct.
|
||||
type SQLiteStmt struct {
|
||||
c *SQLiteConn
|
||||
s *C.sqlite3_stmt
|
||||
nv int
|
||||
nn []string
|
||||
t string
|
||||
closed bool
|
||||
cls bool
|
||||
}
|
||||
|
||||
// Result struct.
|
||||
type SQLiteResult struct {
|
||||
id int64
|
||||
changes int64
|
||||
}
|
||||
|
||||
// Rows struct.
|
||||
type SQLiteRows struct {
|
||||
s *SQLiteStmt
|
||||
nc int
|
||||
cols []string
|
||||
decltype []string
|
||||
cls bool
|
||||
}
|
||||
|
||||
// Commit transaction.
|
||||
func (tx *SQLiteTx) Commit() error {
|
||||
_, err := tx.c.exec("COMMIT")
|
||||
return err
|
||||
}
|
||||
|
||||
// Rollback transaction.
|
||||
func (tx *SQLiteTx) Rollback() error {
|
||||
_, err := tx.c.exec("ROLLBACK")
|
||||
return err
|
||||
}
|
||||
|
||||
// AutoCommit return which currently auto commit or not.
|
||||
func (c *SQLiteConn) AutoCommit() bool {
|
||||
return int(C.sqlite3_get_autocommit(c.db)) != 0
|
||||
}
|
||||
|
||||
func (c *SQLiteConn) lastError() Error {
|
||||
return Error{
|
||||
Code: ErrNo(C.sqlite3_errcode(c.db)),
|
||||
ExtendedCode: ErrNoExtended(C.sqlite3_extended_errcode(c.db)),
|
||||
err: C.GoString(C.sqlite3_errmsg(c.db)),
|
||||
}
|
||||
}
|
||||
|
||||
// Implements Execer
|
||||
func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
||||
if len(args) == 0 {
|
||||
return c.exec(query)
|
||||
}
|
||||
|
||||
for {
|
||||
s, err := c.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res driver.Result
|
||||
if s.(*SQLiteStmt).s != nil {
|
||||
na := s.NumInput()
|
||||
if len(args) < na {
|
||||
return nil, fmt.Errorf("Not enough args to execute query. Expected %d, got %d.", na, len(args))
|
||||
}
|
||||
res, err = s.Exec(args[:na])
|
||||
if err != nil && err != driver.ErrSkip {
|
||||
s.Close()
|
||||
return nil, err
|
||||
}
|
||||
args = args[na:]
|
||||
}
|
||||
tail := s.(*SQLiteStmt).t
|
||||
s.Close()
|
||||
if tail == "" {
|
||||
return res, nil
|
||||
}
|
||||
query = tail
|
||||
}
|
||||
}
|
||||
|
||||
// Implements Queryer
|
||||
func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
||||
for {
|
||||
s, err := c.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.(*SQLiteStmt).cls = true
|
||||
na := s.NumInput()
|
||||
if len(args) < na {
|
||||
return nil, fmt.Errorf("Not enough args to execute query. Expected %d, got %d.", na, len(args))
|
||||
}
|
||||
rows, err := s.Query(args[:na])
|
||||
if err != nil && err != driver.ErrSkip {
|
||||
s.Close()
|
||||
return nil, err
|
||||
}
|
||||
args = args[na:]
|
||||
tail := s.(*SQLiteStmt).t
|
||||
if tail == "" {
|
||||
return rows, nil
|
||||
}
|
||||
rows.Close()
|
||||
s.Close()
|
||||
query = tail
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SQLiteConn) exec(cmd string) (driver.Result, error) {
|
||||
pcmd := C.CString(cmd)
|
||||
defer C.free(unsafe.Pointer(pcmd))
|
||||
|
||||
var rowid, changes C.longlong
|
||||
rv := C._sqlite3_exec(c.db, pcmd, &rowid, &changes)
|
||||
if rv != C.SQLITE_OK {
|
||||
return nil, c.lastError()
|
||||
}
|
||||
return &SQLiteResult{int64(rowid), int64(changes)}, nil
|
||||
}
|
||||
|
||||
// Begin transaction.
|
||||
func (c *SQLiteConn) Begin() (driver.Tx, error) {
|
||||
if _, err := c.exec(c.txlock); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SQLiteTx{c}, nil
|
||||
}
|
||||
|
||||
func errorString(err Error) string {
|
||||
return C.GoString(C.sqlite3_errstr(C.int(err.Code)))
|
||||
}
|
||||
|
||||
// Open database and return a new connection.
|
||||
// You can specify DSN string with URI filename.
|
||||
// test.db
|
||||
// file:test.db?cache=shared&mode=memory
|
||||
// :memory:
|
||||
// file::memory:
|
||||
// go-sqlite handle especially query parameters.
|
||||
// _loc=XXX
|
||||
// Specify location of time format. It's possible to specify "auto".
|
||||
// _busy_timeout=XXX
|
||||
// Specify value for sqlite3_busy_timeout.
|
||||
// _txlock=XXX
|
||||
// Specify locking behavior for transactions. XXX can be "immediate",
|
||||
// "deferred", "exclusive".
|
||||
func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
|
||||
if C.sqlite3_threadsafe() == 0 {
|
||||
return nil, errors.New("sqlite library was not compiled for thread-safe operation")
|
||||
}
|
||||
|
||||
var loc *time.Location
|
||||
txlock := "BEGIN"
|
||||
busy_timeout := 5000
|
||||
pos := strings.IndexRune(dsn, '?')
|
||||
if pos >= 1 {
|
||||
params, err := url.ParseQuery(dsn[pos+1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// _loc
|
||||
if val := params.Get("_loc"); val != "" {
|
||||
if val == "auto" {
|
||||
loc = time.Local
|
||||
} else {
|
||||
loc, err = time.LoadLocation(val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid _loc: %v: %v", val, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _busy_timeout
|
||||
if val := params.Get("_busy_timeout"); val != "" {
|
||||
iv, err := strconv.ParseInt(val, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid _busy_timeout: %v: %v", val, err)
|
||||
}
|
||||
busy_timeout = int(iv)
|
||||
}
|
||||
|
||||
// _txlock
|
||||
if val := params.Get("_txlock"); val != "" {
|
||||
switch val {
|
||||
case "immediate":
|
||||
txlock = "BEGIN IMMEDIATE"
|
||||
case "exclusive":
|
||||
txlock = "BEGIN EXCLUSIVE"
|
||||
case "deferred":
|
||||
txlock = "BEGIN"
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid _txlock: %v", val)
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(dsn, "file:") {
|
||||
dsn = dsn[:pos]
|
||||
}
|
||||
}
|
||||
|
||||
var db *C.sqlite3
|
||||
name := C.CString(dsn)
|
||||
defer C.free(unsafe.Pointer(name))
|
||||
rv := C._sqlite3_open_v2(name, &db,
|
||||
C.SQLITE_OPEN_FULLMUTEX|
|
||||
C.SQLITE_OPEN_READWRITE|
|
||||
C.SQLITE_OPEN_CREATE,
|
||||
nil)
|
||||
if rv != 0 {
|
||||
return nil, Error{Code: ErrNo(rv)}
|
||||
}
|
||||
if db == nil {
|
||||
return nil, errors.New("sqlite succeeded without returning a database")
|
||||
}
|
||||
|
||||
rv = C.sqlite3_busy_timeout(db, C.int(busy_timeout))
|
||||
if rv != C.SQLITE_OK {
|
||||
return nil, Error{Code: ErrNo(rv)}
|
||||
}
|
||||
|
||||
conn := &SQLiteConn{db: db, loc: loc, txlock: txlock}
|
||||
|
||||
if len(d.Extensions) > 0 {
|
||||
if err := conn.loadExtensions(d.Extensions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if d.ConnectHook != nil {
|
||||
if err := d.ConnectHook(conn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
runtime.SetFinalizer(conn, (*SQLiteConn).Close)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Close the connection.
|
||||
func (c *SQLiteConn) Close() error {
|
||||
rv := C.sqlite3_close_v2(c.db)
|
||||
if rv != C.SQLITE_OK {
|
||||
return c.lastError()
|
||||
}
|
||||
c.db = nil
|
||||
runtime.SetFinalizer(c, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Prepare query string. Return a new statement.
|
||||
func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) {
|
||||
pquery := C.CString(query)
|
||||
defer C.free(unsafe.Pointer(pquery))
|
||||
var s *C.sqlite3_stmt
|
||||
var tail *C.char
|
||||
rv := C.sqlite3_prepare_v2(c.db, pquery, -1, &s, &tail)
|
||||
if rv != C.SQLITE_OK {
|
||||
return nil, c.lastError()
|
||||
}
|
||||
var t string
|
||||
if tail != nil && *tail != '\000' {
|
||||
t = strings.TrimSpace(C.GoString(tail))
|
||||
}
|
||||
nv := int(C.sqlite3_bind_parameter_count(s))
|
||||
var nn []string
|
||||
for i := 0; i < nv; i++ {
|
||||
pn := C.GoString(C.sqlite3_bind_parameter_name(s, C.int(i+1)))
|
||||
if len(pn) > 1 && pn[0] == '$' && 48 <= pn[1] && pn[1] <= 57 {
|
||||
nn = append(nn, C.GoString(C.sqlite3_bind_parameter_name(s, C.int(i+1))))
|
||||
}
|
||||
}
|
||||
ss := &SQLiteStmt{c: c, s: s, nv: nv, nn: nn, t: t}
|
||||
runtime.SetFinalizer(ss, (*SQLiteStmt).Close)
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
// Close the statement.
|
||||
func (s *SQLiteStmt) Close() error {
|
||||
if s.closed {
|
||||
return nil
|
||||
}
|
||||
s.closed = true
|
||||
if s.c == nil || s.c.db == nil {
|
||||
return errors.New("sqlite statement with already closed database connection")
|
||||
}
|
||||
rv := C.sqlite3_finalize(s.s)
|
||||
if rv != C.SQLITE_OK {
|
||||
return s.c.lastError()
|
||||
}
|
||||
runtime.SetFinalizer(s, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return a number of parameters.
|
||||
func (s *SQLiteStmt) NumInput() int {
|
||||
return s.nv
|
||||
}
|
||||
|
||||
type bindArg struct {
|
||||
n int
|
||||
v driver.Value
|
||||
}
|
||||
|
||||
func (s *SQLiteStmt) bind(args []driver.Value) error {
|
||||
rv := C.sqlite3_reset(s.s)
|
||||
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
|
||||
return s.c.lastError()
|
||||
}
|
||||
|
||||
var vargs []bindArg
|
||||
narg := len(args)
|
||||
vargs = make([]bindArg, narg)
|
||||
if len(s.nn) > 0 {
|
||||
for i, v := range s.nn {
|
||||
if pi, err := strconv.Atoi(v[1:]); err == nil {
|
||||
vargs[i] = bindArg{pi, args[i]}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i, v := range args {
|
||||
vargs[i] = bindArg{i + 1, v}
|
||||
}
|
||||
}
|
||||
|
||||
for _, varg := range vargs {
|
||||
n := C.int(varg.n)
|
||||
v := varg.v
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
rv = C.sqlite3_bind_null(s.s, n)
|
||||
case string:
|
||||
if len(v) == 0 {
|
||||
b := []byte{0}
|
||||
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(0))
|
||||
} else {
|
||||
b := []byte(v)
|
||||
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b)))
|
||||
}
|
||||
case int64:
|
||||
rv = C.sqlite3_bind_int64(s.s, n, C.sqlite3_int64(v))
|
||||
case bool:
|
||||
if bool(v) {
|
||||
rv = C.sqlite3_bind_int(s.s, n, 1)
|
||||
} else {
|
||||
rv = C.sqlite3_bind_int(s.s, n, 0)
|
||||
}
|
||||
case float64:
|
||||
rv = C.sqlite3_bind_double(s.s, n, C.double(v))
|
||||
case []byte:
|
||||
var p *byte
|
||||
if len(v) > 0 {
|
||||
p = &v[0]
|
||||
}
|
||||
rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(p), C.int(len(v)))
|
||||
case time.Time:
|
||||
b := []byte(v.UTC().Format(SQLiteTimestampFormats[0]))
|
||||
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b)))
|
||||
}
|
||||
if rv != C.SQLITE_OK {
|
||||
return s.c.lastError()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Query the statement with arguments. Return records.
|
||||
func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) {
|
||||
if err := s.bind(args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SQLiteRows{s, int(C.sqlite3_column_count(s.s)), nil, nil, s.cls}, nil
|
||||
}
|
||||
|
||||
// Return last inserted ID.
|
||||
func (r *SQLiteResult) LastInsertId() (int64, error) {
|
||||
return r.id, nil
|
||||
}
|
||||
|
||||
// Return how many rows affected.
|
||||
func (r *SQLiteResult) RowsAffected() (int64, error) {
|
||||
return r.changes, nil
|
||||
}
|
||||
|
||||
// Execute the statement with arguments. Return result object.
|
||||
func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
if err := s.bind(args); err != nil {
|
||||
C.sqlite3_reset(s.s)
|
||||
C.sqlite3_clear_bindings(s.s)
|
||||
return nil, err
|
||||
}
|
||||
var rowid, changes C.longlong
|
||||
rv := C._sqlite3_step(s.s, &rowid, &changes)
|
||||
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
|
||||
err := s.c.lastError()
|
||||
C.sqlite3_reset(s.s)
|
||||
C.sqlite3_clear_bindings(s.s)
|
||||
return nil, err
|
||||
}
|
||||
return &SQLiteResult{int64(rowid), int64(changes)}, nil
|
||||
}
|
||||
|
||||
// Close the rows.
|
||||
func (rc *SQLiteRows) Close() error {
|
||||
if rc.s.closed {
|
||||
return nil
|
||||
}
|
||||
if rc.cls {
|
||||
return rc.s.Close()
|
||||
}
|
||||
rv := C.sqlite3_reset(rc.s.s)
|
||||
if rv != C.SQLITE_OK {
|
||||
return rc.s.c.lastError()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return column names.
|
||||
func (rc *SQLiteRows) Columns() []string {
|
||||
if rc.nc != len(rc.cols) {
|
||||
rc.cols = make([]string, rc.nc)
|
||||
for i := 0; i < rc.nc; i++ {
|
||||
rc.cols[i] = C.GoString(C.sqlite3_column_name(rc.s.s, C.int(i)))
|
||||
}
|
||||
}
|
||||
return rc.cols
|
||||
}
|
||||
|
||||
// Move cursor to next.
|
||||
func (rc *SQLiteRows) Next(dest []driver.Value) error {
|
||||
rv := C.sqlite3_step(rc.s.s)
|
||||
if rv == C.SQLITE_DONE {
|
||||
return io.EOF
|
||||
}
|
||||
if rv != C.SQLITE_ROW {
|
||||
rv = C.sqlite3_reset(rc.s.s)
|
||||
if rv != C.SQLITE_OK {
|
||||
return rc.s.c.lastError()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if rc.decltype == nil {
|
||||
rc.decltype = make([]string, rc.nc)
|
||||
for i := 0; i < rc.nc; i++ {
|
||||
rc.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))))
|
||||
}
|
||||
}
|
||||
|
||||
for i := range dest {
|
||||
switch C.sqlite3_column_type(rc.s.s, C.int(i)) {
|
||||
case C.SQLITE_INTEGER:
|
||||
val := int64(C.sqlite3_column_int64(rc.s.s, C.int(i)))
|
||||
switch rc.decltype[i] {
|
||||
case "timestamp", "datetime", "date":
|
||||
unixTimestamp := strconv.FormatInt(val, 10)
|
||||
var t time.Time
|
||||
if len(unixTimestamp) == 13 {
|
||||
duration, err := time.ParseDuration(unixTimestamp + "ms")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing %s value %d, %s", rc.decltype[i], val, err)
|
||||
}
|
||||
epoch := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
t = epoch.Add(duration)
|
||||
} else {
|
||||
t = time.Unix(val, 0)
|
||||
}
|
||||
if rc.s.c.loc != nil {
|
||||
t = t.In(rc.s.c.loc)
|
||||
}
|
||||
dest[i] = t
|
||||
case "boolean":
|
||||
dest[i] = val > 0
|
||||
default:
|
||||
dest[i] = val
|
||||
}
|
||||
case C.SQLITE_FLOAT:
|
||||
dest[i] = float64(C.sqlite3_column_double(rc.s.s, C.int(i)))
|
||||
case C.SQLITE_BLOB:
|
||||
p := C.sqlite3_column_blob(rc.s.s, C.int(i))
|
||||
if p == nil {
|
||||
dest[i] = nil
|
||||
continue
|
||||
}
|
||||
n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i)))
|
||||
switch dest[i].(type) {
|
||||
case sql.RawBytes:
|
||||
dest[i] = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n]
|
||||
default:
|
||||
slice := make([]byte, n)
|
||||
copy(slice[:], (*[1 << 30]byte)(unsafe.Pointer(p))[0:n])
|
||||
dest[i] = slice
|
||||
}
|
||||
case C.SQLITE_NULL:
|
||||
dest[i] = nil
|
||||
case C.SQLITE_TEXT:
|
||||
var err error
|
||||
var timeVal time.Time
|
||||
|
||||
n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i)))
|
||||
s := C.GoStringN((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i)))), C.int(n))
|
||||
|
||||
switch rc.decltype[i] {
|
||||
case "timestamp", "datetime", "date":
|
||||
var t time.Time
|
||||
s = strings.TrimSuffix(s, "Z")
|
||||
for _, format := range SQLiteTimestampFormats {
|
||||
if timeVal, err = time.ParseInLocation(format, s, time.UTC); err == nil {
|
||||
t = timeVal
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
// The column is a time value, so return the zero time on parse failure.
|
||||
t = time.Time{}
|
||||
}
|
||||
if rc.s.c.loc != nil {
|
||||
t = t.In(rc.s.c.loc)
|
||||
}
|
||||
dest[i] = t
|
||||
default:
|
||||
dest[i] = []byte(s)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
83
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_fts3_test.go
generated
vendored
Normal file
83
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_fts3_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright (C) 2015 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFTS3(t *testing.T) {
|
||||
tempFilename := TempFilename()
|
||||
db, err := sql.Open("sqlite3", tempFilename)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to open database:", err)
|
||||
}
|
||||
defer os.Remove(tempFilename)
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec("DROP TABLE foo")
|
||||
_, err = db.Exec("CREATE VIRTUAL TABLE foo USING fts3(id INTEGER PRIMARY KEY, value TEXT)")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create table:", err)
|
||||
}
|
||||
|
||||
_, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 1, `今日の 晩御飯は 天麩羅よ`)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to insert value:", err)
|
||||
}
|
||||
|
||||
_, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 2, `今日は いい 天気だ`)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to insert value:", err)
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT id, value FROM foo WHERE value MATCH '今日* 天*'")
|
||||
if err != nil {
|
||||
t.Fatal("Unable to query foo table:", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var id int
|
||||
var value string
|
||||
|
||||
if err := rows.Scan(&id, &value); err != nil {
|
||||
t.Error("Unable to scan results:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if id == 1 && value != `今日の 晩御飯は 天麩羅よ` {
|
||||
t.Error("Value for id 1 should be `今日の 晩御飯は 天麩羅よ`, but:", value)
|
||||
} else if id == 2 && value != `今日は いい 天気だ` {
|
||||
t.Error("Value for id 2 should be `今日は いい 天気だ`, but:", value)
|
||||
}
|
||||
}
|
||||
|
||||
rows, err = db.Query("SELECT value FROM foo WHERE value MATCH '今日* 天麩羅*'")
|
||||
if err != nil {
|
||||
t.Fatal("Unable to query foo table:", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var value string
|
||||
if !rows.Next() {
|
||||
t.Fatal("Result should be only one")
|
||||
}
|
||||
|
||||
if err := rows.Scan(&value); err != nil {
|
||||
t.Fatal("Unable to scan results:", err)
|
||||
}
|
||||
|
||||
if value != `今日の 晩御飯は 天麩羅よ` {
|
||||
t.Fatal("Value should be `今日の 晩御飯は 天麩羅よ`, but:", value)
|
||||
}
|
||||
|
||||
if rows.Next() {
|
||||
t.Fatal("Result should be only one")
|
||||
}
|
||||
}
|
||||
13
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_icu.go
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_icu.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// +build icu
|
||||
|
||||
package sqlite3
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -licuuc -licui18n
|
||||
#cgo CFLAGS: -DSQLITE_ENABLE_ICU
|
||||
*/
|
||||
import "C"
|
||||
13
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_libsqlite3.go
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_libsqlite3.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// +build libsqlite3
|
||||
|
||||
package sqlite3
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -DUSE_LIBSQLITE3
|
||||
#cgo LDFLAGS: -lsqlite3
|
||||
*/
|
||||
import "C"
|
||||
39
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_load_extension.go
generated
vendored
Normal file
39
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_load_extension.go
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// +build !sqlite_omit_load_extension
|
||||
|
||||
package sqlite3
|
||||
|
||||
/*
|
||||
#include <sqlite3-binding.h>
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (c *SQLiteConn) loadExtensions(extensions []string) error {
|
||||
rv := C.sqlite3_enable_load_extension(c.db, 1)
|
||||
if rv != C.SQLITE_OK {
|
||||
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
|
||||
}
|
||||
|
||||
for _, extension := range extensions {
|
||||
cext := C.CString(extension)
|
||||
defer C.free(unsafe.Pointer(cext))
|
||||
rv = C.sqlite3_load_extension(c.db, cext, nil, nil)
|
||||
if rv != C.SQLITE_OK {
|
||||
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
|
||||
}
|
||||
}
|
||||
|
||||
rv = C.sqlite3_enable_load_extension(c.db, 0)
|
||||
if rv != C.SQLITE_OK {
|
||||
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
19
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_omit_load_extension.go
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_omit_load_extension.go
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// +build sqlite_omit_load_extension
|
||||
|
||||
package sqlite3
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -DSQLITE_OMIT_LOAD_EXTENSION
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
func (c *SQLiteConn) loadExtensions(extensions []string) error {
|
||||
return errors.New("Extensions have been disabled for static builds")
|
||||
}
|
||||
13
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_other.go
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_other.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// +build !windows
|
||||
|
||||
package sqlite3
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -I.
|
||||
#cgo linux LDFLAGS: -ldl
|
||||
*/
|
||||
import "C"
|
||||
1060
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_test.go
generated
vendored
Normal file
1060
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
412
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_test/sqltest.go
generated
vendored
Normal file
412
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_test/sqltest.go
generated
vendored
Normal file
|
|
@ -0,0 +1,412 @@
|
|||
package sqlite3_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Dialect int
|
||||
|
||||
const (
|
||||
SQLITE Dialect = iota
|
||||
POSTGRESQL
|
||||
MYSQL
|
||||
)
|
||||
|
||||
type DB struct {
|
||||
*testing.T
|
||||
*sql.DB
|
||||
dialect Dialect
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
var db *DB
|
||||
|
||||
// the following tables will be created and dropped during the test
|
||||
var testTables = []string{"foo", "bar", "t", "bench"}
|
||||
|
||||
var tests = []testing.InternalTest{
|
||||
{"TestBlobs", TestBlobs},
|
||||
{"TestManyQueryRow", TestManyQueryRow},
|
||||
{"TestTxQuery", TestTxQuery},
|
||||
{"TestPreparedStmt", TestPreparedStmt},
|
||||
}
|
||||
|
||||
var benchmarks = []testing.InternalBenchmark{
|
||||
{"BenchmarkExec", BenchmarkExec},
|
||||
{"BenchmarkQuery", BenchmarkQuery},
|
||||
{"BenchmarkParams", BenchmarkParams},
|
||||
{"BenchmarkStmt", BenchmarkStmt},
|
||||
{"BenchmarkRows", BenchmarkRows},
|
||||
{"BenchmarkStmtRows", BenchmarkStmtRows},
|
||||
}
|
||||
|
||||
// RunTests runs the SQL test suite
|
||||
func RunTests(t *testing.T, d *sql.DB, dialect Dialect) {
|
||||
db = &DB{t, d, dialect, sync.Once{}}
|
||||
testing.RunTests(func(string, string) (bool, error) { return true, nil }, tests)
|
||||
|
||||
if !testing.Short() {
|
||||
for _, b := range benchmarks {
|
||||
fmt.Printf("%-20s", b.Name)
|
||||
r := testing.Benchmark(b.F)
|
||||
fmt.Printf("%10d %10.0f req/s\n", r.N, float64(r.N)/r.T.Seconds())
|
||||
}
|
||||
}
|
||||
db.tearDown()
|
||||
}
|
||||
|
||||
func (db *DB) mustExec(sql string, args ...interface{}) sql.Result {
|
||||
res, err := db.Exec(sql, args...)
|
||||
if err != nil {
|
||||
db.Fatalf("Error running %q: %v", sql, err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (db *DB) tearDown() {
|
||||
for _, tbl := range testTables {
|
||||
switch db.dialect {
|
||||
case SQLITE:
|
||||
db.mustExec("drop table if exists " + tbl)
|
||||
case MYSQL, POSTGRESQL:
|
||||
db.mustExec("drop table if exists " + tbl)
|
||||
default:
|
||||
db.Fatal("unkown dialect")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// q replaces ? parameters if needed
|
||||
func (db *DB) q(sql string) string {
|
||||
switch db.dialect {
|
||||
case POSTGRESQL: // repace with $1, $2, ..
|
||||
qrx := regexp.MustCompile(`\?`)
|
||||
n := 0
|
||||
return qrx.ReplaceAllStringFunc(sql, func(string) string {
|
||||
n++
|
||||
return "$" + strconv.Itoa(n)
|
||||
})
|
||||
}
|
||||
return sql
|
||||
}
|
||||
|
||||
func (db *DB) blobType(size int) string {
|
||||
switch db.dialect {
|
||||
case SQLITE:
|
||||
return fmt.Sprintf("blob[%d]", size)
|
||||
case POSTGRESQL:
|
||||
return "bytea"
|
||||
case MYSQL:
|
||||
return fmt.Sprintf("VARBINARY(%d)", size)
|
||||
}
|
||||
panic("unkown dialect")
|
||||
}
|
||||
|
||||
func (db *DB) serialPK() string {
|
||||
switch db.dialect {
|
||||
case SQLITE:
|
||||
return "integer primary key autoincrement"
|
||||
case POSTGRESQL:
|
||||
return "serial primary key"
|
||||
case MYSQL:
|
||||
return "integer primary key auto_increment"
|
||||
}
|
||||
panic("unkown dialect")
|
||||
}
|
||||
|
||||
func (db *DB) now() string {
|
||||
switch db.dialect {
|
||||
case SQLITE:
|
||||
return "datetime('now')"
|
||||
case POSTGRESQL:
|
||||
return "now()"
|
||||
case MYSQL:
|
||||
return "now()"
|
||||
}
|
||||
panic("unkown dialect")
|
||||
}
|
||||
|
||||
func makeBench() {
|
||||
if _, err := db.Exec("create table bench (n varchar(32), i integer, d double, s varchar(32), t datetime)"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
st, err := db.Prepare("insert into bench values (?, ?, ?, ?, ?)")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer st.Close()
|
||||
for i := 0; i < 100; i++ {
|
||||
if _, err = st.Exec(nil, i, float64(i), fmt.Sprintf("%d", i), time.Now()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResult(t *testing.T) {
|
||||
db.tearDown()
|
||||
db.mustExec("create temporary table test (id " + db.serialPK() + ", name varchar(10))")
|
||||
|
||||
for i := 1; i < 3; i++ {
|
||||
r := db.mustExec(db.q("insert into test (name) values (?)"), fmt.Sprintf("row %d", i))
|
||||
n, err := r.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != 1 {
|
||||
t.Errorf("got %v, want %v", n, 1)
|
||||
}
|
||||
n, err = r.LastInsertId()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != int64(i) {
|
||||
t.Errorf("got %v, want %v", n, i)
|
||||
}
|
||||
}
|
||||
if _, err := db.Exec("error!"); err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlobs(t *testing.T) {
|
||||
db.tearDown()
|
||||
var blob = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
db.mustExec("create table foo (id integer primary key, bar " + db.blobType(16) + ")")
|
||||
db.mustExec(db.q("insert into foo (id, bar) values(?,?)"), 0, blob)
|
||||
|
||||
want := fmt.Sprintf("%x", blob)
|
||||
|
||||
b := make([]byte, 16)
|
||||
err := db.QueryRow(db.q("select bar from foo where id = ?"), 0).Scan(&b)
|
||||
got := fmt.Sprintf("%x", b)
|
||||
if err != nil {
|
||||
t.Errorf("[]byte scan: %v", err)
|
||||
} else if got != want {
|
||||
t.Errorf("for []byte, got %q; want %q", got, want)
|
||||
}
|
||||
|
||||
err = db.QueryRow(db.q("select bar from foo where id = ?"), 0).Scan(&got)
|
||||
want = string(blob)
|
||||
if err != nil {
|
||||
t.Errorf("string scan: %v", err)
|
||||
} else if got != want {
|
||||
t.Errorf("for string, got %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManyQueryRow(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Log("skipping in short mode")
|
||||
return
|
||||
}
|
||||
db.tearDown()
|
||||
db.mustExec("create table foo (id integer primary key, name varchar(50))")
|
||||
db.mustExec(db.q("insert into foo (id, name) values(?,?)"), 1, "bob")
|
||||
var name string
|
||||
for i := 0; i < 10000; i++ {
|
||||
err := db.QueryRow(db.q("select name from foo where id = ?"), 1).Scan(&name)
|
||||
if err != nil || name != "bob" {
|
||||
t.Fatalf("on query %d: err=%v, name=%q", i, err, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTxQuery(t *testing.T) {
|
||||
db.tearDown()
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
_, err = tx.Exec("create table foo (id integer primary key, name varchar(50))")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = tx.Exec(db.q("insert into foo (id, name) values(?,?)"), 1, "bob")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r, err := tx.Query(db.q("select name from foo where id = ?"), 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
if !r.Next() {
|
||||
if r.Err() != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Fatal("expected one rows")
|
||||
}
|
||||
|
||||
var name string
|
||||
err = r.Scan(&name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreparedStmt(t *testing.T) {
|
||||
db.tearDown()
|
||||
db.mustExec("CREATE TABLE t (count INT)")
|
||||
sel, err := db.Prepare("SELECT count FROM t ORDER BY count DESC")
|
||||
if err != nil {
|
||||
t.Fatalf("prepare 1: %v", err)
|
||||
}
|
||||
ins, err := db.Prepare(db.q("INSERT INTO t (count) VALUES (?)"))
|
||||
if err != nil {
|
||||
t.Fatalf("prepare 2: %v", err)
|
||||
}
|
||||
|
||||
for n := 1; n <= 3; n++ {
|
||||
if _, err := ins.Exec(n); err != nil {
|
||||
t.Fatalf("insert(%d) = %v", n, err)
|
||||
}
|
||||
}
|
||||
|
||||
const nRuns = 10
|
||||
ch := make(chan bool)
|
||||
for i := 0; i < nRuns; i++ {
|
||||
go func() {
|
||||
defer func() {
|
||||
ch <- true
|
||||
}()
|
||||
for j := 0; j < 10; j++ {
|
||||
count := 0
|
||||
if err := sel.QueryRow().Scan(&count); err != nil && err != sql.ErrNoRows {
|
||||
t.Errorf("Query: %v", err)
|
||||
return
|
||||
}
|
||||
if _, err := ins.Exec(rand.Intn(100)); err != nil {
|
||||
t.Errorf("Insert: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
for i := 0; i < nRuns; i++ {
|
||||
<-ch
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmarks need to use panic() since b.Error errors are lost when
|
||||
// running via testing.Benchmark() I would like to run these via go
|
||||
// test -bench but calling Benchmark() from a benchmark test
|
||||
// currently hangs go.
|
||||
|
||||
func BenchmarkExec(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := db.Exec("select 1"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkQuery(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var n sql.NullString
|
||||
var i int
|
||||
var f float64
|
||||
var s string
|
||||
// var t time.Time
|
||||
if err := db.QueryRow("select null, 1, 1.1, 'foo'").Scan(&n, &i, &f, &s); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParams(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var n sql.NullString
|
||||
var i int
|
||||
var f float64
|
||||
var s string
|
||||
// var t time.Time
|
||||
if err := db.QueryRow("select ?, ?, ?, ?", nil, 1, 1.1, "foo").Scan(&n, &i, &f, &s); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStmt(b *testing.B) {
|
||||
st, err := db.Prepare("select ?, ?, ?, ?")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer st.Close()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
var n sql.NullString
|
||||
var i int
|
||||
var f float64
|
||||
var s string
|
||||
// var t time.Time
|
||||
if err := st.QueryRow(nil, 1, 1.1, "foo").Scan(&n, &i, &f, &s); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRows(b *testing.B) {
|
||||
db.once.Do(makeBench)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
var n sql.NullString
|
||||
var i int
|
||||
var f float64
|
||||
var s string
|
||||
var t time.Time
|
||||
r, err := db.Query("select * from bench")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for r.Next() {
|
||||
if err = r.Scan(&n, &i, &f, &s, &t); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if err = r.Err(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStmtRows(b *testing.B) {
|
||||
db.once.Do(makeBench)
|
||||
|
||||
st, err := db.Prepare("select * from bench")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer st.Close()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
var n sql.NullString
|
||||
var i int
|
||||
var f float64
|
||||
var s string
|
||||
var t time.Time
|
||||
r, err := st.Query()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for r.Next() {
|
||||
if err = r.Scan(&n, &i, &f, &s, &t); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if err = r.Err(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
14
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_windows.go
generated
vendored
Normal file
14
Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// +build windows
|
||||
|
||||
package sqlite3
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -I. -fno-stack-check -fno-stack-protector -mno-stack-arg-probe
|
||||
#cgo windows,386 CFLAGS: -D_localtime32=localtime
|
||||
#cgo LDFLAGS: -lmingwex -lmingw32
|
||||
*/
|
||||
import "C"
|
||||
13
build/ldflags.sh
Executable file
13
build/ldflags.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ ! -f "build/env.sh" ]; then
|
||||
echo "$0 must be run from the root of the repository."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# set gitCommit when running from a Git checkout.
|
||||
if [ -f ".git/HEAD" ]; then
|
||||
echo "-ldflags '-X main.gitCommit $(git rev-parse HEAD)'"
|
||||
fi
|
||||
136
cmd/chatty/blocktestcmd.go
Normal file
136
cmd/chatty/blocktestcmd.go
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/chattynet/chatty/cmd/utils"
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/eth"
|
||||
"github.com/chattynet/chatty/ethdb"
|
||||
"github.com/chattynet/chatty/tests"
|
||||
)
|
||||
|
||||
var blocktestCommand = cli.Command{
|
||||
Action: runBlockTest,
|
||||
Name: "blocktest",
|
||||
Usage: `loads a block test file`,
|
||||
Description: `
|
||||
The first argument should be a block test file.
|
||||
The second argument is the name of a block test from the file.
|
||||
|
||||
The block test will be loaded into an in-memory database.
|
||||
If loading succeeds, the RPC server is started. Clients will
|
||||
be able to interact with the chain defined by the test.
|
||||
`,
|
||||
}
|
||||
|
||||
func runBlockTest(ctx *cli.Context) {
|
||||
var (
|
||||
file, testname string
|
||||
rpc bool
|
||||
)
|
||||
args := ctx.Args()
|
||||
switch {
|
||||
case len(args) == 1:
|
||||
file = args[0]
|
||||
case len(args) == 2:
|
||||
file, testname = args[0], args[1]
|
||||
case len(args) == 3:
|
||||
file, testname = args[0], args[1]
|
||||
rpc = true
|
||||
default:
|
||||
utils.Fatalf(`Usage: shift blocktest <path-to-test-file> [ <test-name> [ "rpc" ] ]`)
|
||||
}
|
||||
bt, err := tests.LoadBlockTests(file)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
// run all tests if no test name is specified
|
||||
if testname == "" {
|
||||
ecode := 0
|
||||
for name, test := range bt {
|
||||
fmt.Printf("----------------- Running Block Test %q\n", name)
|
||||
ethereum, err := runOneBlockTest(ctx, test)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println("FAIL")
|
||||
ecode = 1
|
||||
}
|
||||
if ethereum != nil {
|
||||
ethereum.Stop()
|
||||
ethereum.WaitForShutdown()
|
||||
}
|
||||
}
|
||||
os.Exit(ecode)
|
||||
return
|
||||
}
|
||||
// otherwise, run the given test
|
||||
test, ok := bt[testname]
|
||||
if !ok {
|
||||
utils.Fatalf("Test file does not contain test named %q", testname)
|
||||
}
|
||||
ethereum, err := runOneBlockTest(ctx, test)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
if rpc {
|
||||
fmt.Println("Block Test post state validated, starting RPC interface.")
|
||||
startEth(ctx, ethereum)
|
||||
utils.StartRPC(ethereum, ctx)
|
||||
ethereum.WaitForShutdown()
|
||||
}
|
||||
}
|
||||
|
||||
func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, error) {
|
||||
cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
|
||||
cfg.NewDB = func(path string) (common.Database, error) { return ethdb.NewMemDatabase() }
|
||||
cfg.MaxPeers = 0 // disable network
|
||||
cfg.Shh = false // disable whisper
|
||||
cfg.NAT = nil // disable port mapping
|
||||
|
||||
ethereum, err := eth.New(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// if err := ethereum.Start(); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// import the genesis block
|
||||
ethereum.ResetWithGenesisBlock(test.Genesis)
|
||||
|
||||
// import pre accounts
|
||||
statedb, err := test.InsertPreState(ethereum)
|
||||
if err != nil {
|
||||
return ethereum, fmt.Errorf("InsertPreState: %v", err)
|
||||
}
|
||||
|
||||
if err := test.TryBlocksInsert(ethereum.ChainManager()); err != nil {
|
||||
return ethereum, fmt.Errorf("Block Test load error: %v", err)
|
||||
}
|
||||
|
||||
if err := test.ValidatePostState(statedb); err != nil {
|
||||
return ethereum, fmt.Errorf("post state validation failed: %v", err)
|
||||
}
|
||||
return ethereum, nil
|
||||
}
|
||||
198
cmd/chatty/chaincmd.go
Normal file
198
cmd/chatty/chaincmd.go
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/chattynet/chatty/cmd/utils"
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/core"
|
||||
"github.com/chattynet/chatty/core/state"
|
||||
"github.com/chattynet/chatty/core/types"
|
||||
"github.com/chattynet/chatty/logger/glog"
|
||||
)
|
||||
|
||||
var (
|
||||
importCommand = cli.Command{
|
||||
Action: importChain,
|
||||
Name: "import",
|
||||
Usage: `import a blockchain file`,
|
||||
}
|
||||
exportCommand = cli.Command{
|
||||
Action: exportChain,
|
||||
Name: "export",
|
||||
Usage: `export blockchain into file`,
|
||||
Description: `
|
||||
Requires a first argument of the file to write to.
|
||||
Optional second and third arguments control the first and
|
||||
last block to write. In this mode, the file will be appended
|
||||
if already existing.
|
||||
`,
|
||||
}
|
||||
upgradedbCommand = cli.Command{
|
||||
Action: upgradeDB,
|
||||
Name: "upgradedb",
|
||||
Usage: "upgrade chainblock database",
|
||||
}
|
||||
removedbCommand = cli.Command{
|
||||
Action: removeDB,
|
||||
Name: "removedb",
|
||||
Usage: "Remove blockchain and state databases",
|
||||
}
|
||||
dumpCommand = cli.Command{
|
||||
Action: dump,
|
||||
Name: "dump",
|
||||
Usage: `dump a specific block from storage`,
|
||||
Description: `
|
||||
The arguments are interpreted as block numbers or hashes.
|
||||
Use "shift dump 0" to dump the genesis block.
|
||||
`,
|
||||
}
|
||||
)
|
||||
|
||||
func importChain(ctx *cli.Context) {
|
||||
if len(ctx.Args()) != 1 {
|
||||
utils.Fatalf("This command requires an argument.")
|
||||
}
|
||||
chain, chainDb := utils.MakeChain(ctx)
|
||||
start := time.Now()
|
||||
err := utils.ImportChain(chain, ctx.Args().First())
|
||||
chainDb.Close()
|
||||
if err != nil {
|
||||
utils.Fatalf("Import error: %v", err)
|
||||
}
|
||||
fmt.Printf("Import done in %v", time.Since(start))
|
||||
}
|
||||
|
||||
func exportChain(ctx *cli.Context) {
|
||||
if len(ctx.Args()) < 1 {
|
||||
utils.Fatalf("This command requires an argument.")
|
||||
}
|
||||
chain, _ := utils.MakeChain(ctx)
|
||||
start := time.Now()
|
||||
|
||||
var err error
|
||||
fp := ctx.Args().First()
|
||||
if len(ctx.Args()) < 3 {
|
||||
err = utils.ExportChain(chain, fp)
|
||||
} else {
|
||||
// This can be improved to allow for numbers larger than 9223372036854775807
|
||||
first, ferr := strconv.ParseInt(ctx.Args().Get(1), 10, 64)
|
||||
last, lerr := strconv.ParseInt(ctx.Args().Get(2), 10, 64)
|
||||
if ferr != nil || lerr != nil {
|
||||
utils.Fatalf("Export error in parsing parameters: block number not an integer\n")
|
||||
}
|
||||
if first < 0 || last < 0 {
|
||||
utils.Fatalf("Export error: block number must be greater than 0\n")
|
||||
}
|
||||
err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.Fatalf("Export error: %v\n", err)
|
||||
}
|
||||
fmt.Printf("Export done in %v", time.Since(start))
|
||||
}
|
||||
|
||||
func removeDB(ctx *cli.Context) {
|
||||
confirm, err := utils.PromptConfirm("Remove local database?")
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
if confirm {
|
||||
fmt.Println("Removing chaindata...")
|
||||
start := time.Now()
|
||||
|
||||
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "chaindata"))
|
||||
|
||||
fmt.Printf("Removed in %v\n", time.Since(start))
|
||||
} else {
|
||||
fmt.Println("Operation aborted")
|
||||
}
|
||||
}
|
||||
|
||||
func upgradeDB(ctx *cli.Context) {
|
||||
glog.Infoln("Upgrading blockchain database")
|
||||
|
||||
chain, chainDb := utils.MakeChain(ctx)
|
||||
v, _ := chainDb.Get([]byte("BlockchainVersion"))
|
||||
bcVersion := int(common.NewValue(v).Uint())
|
||||
if bcVersion == 0 {
|
||||
bcVersion = core.BlockChainVersion
|
||||
}
|
||||
|
||||
// Export the current chain.
|
||||
filename := fmt.Sprintf("blockchain_%d_%s.chain", bcVersion, time.Now().Format("20060102_150405"))
|
||||
exportFile := filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), filename)
|
||||
if err := utils.ExportChain(chain, exportFile); err != nil {
|
||||
utils.Fatalf("Unable to export chain for reimport %s", err)
|
||||
}
|
||||
chainDb.Close()
|
||||
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "chaindata"))
|
||||
|
||||
// Import the chain file.
|
||||
chain, chainDb = utils.MakeChain(ctx)
|
||||
chainDb.Put([]byte("BlockchainVersion"), common.NewValue(core.BlockChainVersion).Bytes())
|
||||
err := utils.ImportChain(chain, exportFile)
|
||||
chainDb.Close()
|
||||
if err != nil {
|
||||
utils.Fatalf("Import error %v (a backup is made in %s, use the import command to import it)", err, exportFile)
|
||||
} else {
|
||||
os.Remove(exportFile)
|
||||
glog.Infoln("Import finished")
|
||||
}
|
||||
}
|
||||
|
||||
func dump(ctx *cli.Context) {
|
||||
chain, chainDb := utils.MakeChain(ctx)
|
||||
for _, arg := range ctx.Args() {
|
||||
var block *types.Block
|
||||
if hashish(arg) {
|
||||
block = chain.GetBlock(common.HexToHash(arg))
|
||||
} else {
|
||||
num, _ := strconv.Atoi(arg)
|
||||
block = chain.GetBlockByNumber(uint64(num))
|
||||
}
|
||||
if block == nil {
|
||||
fmt.Println("{}")
|
||||
utils.Fatalf("block not found")
|
||||
} else {
|
||||
state := state.New(block.Root(), chainDb)
|
||||
fmt.Printf("%s\n", state.Dump())
|
||||
}
|
||||
}
|
||||
chainDb.Close()
|
||||
}
|
||||
|
||||
// hashish returns true for strings that look like hashes.
|
||||
func hashish(x string) bool {
|
||||
_, err := strconv.Atoi(x)
|
||||
return err != nil
|
||||
}
|
||||
|
||||
func closeAll(dbs ...common.Database) {
|
||||
for _, db := range dbs {
|
||||
db.Close()
|
||||
}
|
||||
}
|
||||
1
cmd/chatty/info_test.json
Normal file
1
cmd/chatty/info_test.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"code":"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056","info":{"abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"compilerVersion":"0.9.23","developerDoc":{"methods":{}},"language":"Solidity","languageVersion":"0","source":"contract test {\n /// @notice Will multiply `a` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply `a` by 7."}}}}}
|
||||
501
cmd/chatty/js.go
Normal file
501
cmd/chatty/js.go
Normal file
|
|
@ -0,0 +1,501 @@
|
|||
// Copyright 2014 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"sort"
|
||||
|
||||
"github.com/chattynet/chatty/cmd/utils"
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/common/docserver"
|
||||
"github.com/chattynet/chatty/common/natspec"
|
||||
"github.com/chattynet/chatty/common/registrar"
|
||||
"github.com/chattynet/chatty/eth"
|
||||
re "github.com/chattynet/chatty/jsre"
|
||||
"github.com/chattynet/chatty/rpc"
|
||||
"github.com/chattynet/chatty/rpc/api"
|
||||
"github.com/chattynet/chatty/rpc/codec"
|
||||
"github.com/chattynet/chatty/rpc/comms"
|
||||
"github.com/chattynet/chatty/rpc/shared"
|
||||
"github.com/chattynet/chatty/xeth"
|
||||
"github.com/peterh/liner"
|
||||
"github.com/robertkrimen/otto"
|
||||
)
|
||||
|
||||
var passwordRegexp = regexp.MustCompile("personal.[nu]")
|
||||
|
||||
const passwordRepl = ""
|
||||
|
||||
type prompter interface {
|
||||
AppendHistory(string)
|
||||
Prompt(p string) (string, error)
|
||||
PasswordPrompt(p string) (string, error)
|
||||
}
|
||||
|
||||
type dumbterm struct{ r *bufio.Reader }
|
||||
|
||||
func (r dumbterm) Prompt(p string) (string, error) {
|
||||
fmt.Print(p)
|
||||
line, err := r.r.ReadString('\n')
|
||||
return strings.TrimSuffix(line, "\n"), err
|
||||
}
|
||||
|
||||
func (r dumbterm) PasswordPrompt(p string) (string, error) {
|
||||
fmt.Println("!! Unsupported terminal, password will echo.")
|
||||
fmt.Print(p)
|
||||
input, err := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
fmt.Println()
|
||||
return input, err
|
||||
}
|
||||
|
||||
func (r dumbterm) AppendHistory(string) {}
|
||||
|
||||
type jsre struct {
|
||||
ds *docserver.DocServer
|
||||
re *re.JSRE
|
||||
ethereum *eth.Ethereum
|
||||
xeth *xeth.XEth
|
||||
wait chan *big.Int
|
||||
ps1 string
|
||||
atexit func()
|
||||
corsDomain string
|
||||
client comms.EthereumClient
|
||||
prompter
|
||||
}
|
||||
|
||||
var (
|
||||
loadedModulesMethods map[string][]string
|
||||
)
|
||||
|
||||
func keywordCompleter(line string) []string {
|
||||
results := make([]string, 0)
|
||||
|
||||
if strings.Contains(line, ".") {
|
||||
elements := strings.Split(line, ".")
|
||||
if len(elements) == 2 {
|
||||
module := elements[0]
|
||||
partialMethod := elements[1]
|
||||
if methods, found := loadedModulesMethods[module]; found {
|
||||
for _, method := range methods {
|
||||
if strings.HasPrefix(method, partialMethod) { // e.g. debug.se
|
||||
results = append(results, module+"."+method)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for module, methods := range loadedModulesMethods {
|
||||
if line == module { // user typed in full module name, show all methods
|
||||
for _, method := range methods {
|
||||
results = append(results, module+"."+method)
|
||||
}
|
||||
} else if strings.HasPrefix(module, line) { // partial method name, e.g. admi
|
||||
results = append(results, module)
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func apiWordCompleter(line string, pos int) (head string, completions []string, tail string) {
|
||||
if len(line) == 0 || pos == 0{
|
||||
return "", nil, ""
|
||||
}
|
||||
|
||||
i := 0
|
||||
for i = pos - 1; i > 0; i-- {
|
||||
if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') {
|
||||
continue
|
||||
}
|
||||
if i >= 3 && line[i] == '3' && line[i-3] == 'w' && line[i-2] == 'e' && line[i-1] == 'b' {
|
||||
continue
|
||||
}
|
||||
i += 1
|
||||
break
|
||||
}
|
||||
|
||||
begin := line[:i]
|
||||
keyword := line[i:pos]
|
||||
end := line[pos:]
|
||||
|
||||
completionWords := keywordCompleter(keyword)
|
||||
return begin, completionWords, end
|
||||
}
|
||||
|
||||
func newLightweightJSRE(libPath string, client comms.EthereumClient, interactive bool) *jsre {
|
||||
js := &jsre{ps1: "> "}
|
||||
js.wait = make(chan *big.Int)
|
||||
js.client = client
|
||||
js.ds = docserver.New("/")
|
||||
|
||||
// update state in separare forever blocks
|
||||
js.re = re.New(libPath)
|
||||
if err := js.apiBindings(js); err != nil {
|
||||
utils.Fatalf("Unable to initialize console - %v", err)
|
||||
}
|
||||
|
||||
if !liner.TerminalSupported() || !interactive {
|
||||
js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
|
||||
} else {
|
||||
lr := liner.NewLiner()
|
||||
js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) })
|
||||
lr.SetCtrlCAborts(true)
|
||||
js.loadAutoCompletion()
|
||||
lr.SetWordCompleter(apiWordCompleter)
|
||||
lr.SetTabCompletionStyle(liner.TabPrints)
|
||||
js.prompter = lr
|
||||
js.atexit = func() {
|
||||
js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
|
||||
lr.Close()
|
||||
close(js.wait)
|
||||
}
|
||||
}
|
||||
return js
|
||||
}
|
||||
|
||||
func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre {
|
||||
js := &jsre{ethereum: ethereum, ps1: "> "}
|
||||
// set default cors domain used by startRpc from CLI flag
|
||||
js.corsDomain = corsDomain
|
||||
if f == nil {
|
||||
f = js
|
||||
}
|
||||
js.ds = docserver.New("/")
|
||||
js.xeth = xeth.New(ethereum, f)
|
||||
js.wait = js.xeth.UpdateState()
|
||||
js.client = client
|
||||
if clt, ok := js.client.(*comms.InProcClient); ok {
|
||||
if offeredApis, err := api.ParseApiString(shared.AllApis, codec.JSON, js.xeth, ethereum); err == nil {
|
||||
clt.Initialize(api.Merge(offeredApis...))
|
||||
}
|
||||
}
|
||||
|
||||
// update state in separare forever blocks
|
||||
js.re = re.New(libPath)
|
||||
if err := js.apiBindings(f); err != nil {
|
||||
utils.Fatalf("Unable to connect - %v", err)
|
||||
}
|
||||
|
||||
if !liner.TerminalSupported() || !interactive {
|
||||
js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
|
||||
} else {
|
||||
lr := liner.NewLiner()
|
||||
js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) })
|
||||
lr.SetCtrlCAborts(true)
|
||||
js.loadAutoCompletion()
|
||||
lr.SetWordCompleter(apiWordCompleter)
|
||||
lr.SetTabCompletionStyle(liner.TabPrints)
|
||||
js.prompter = lr
|
||||
js.atexit = func() {
|
||||
js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
|
||||
lr.Close()
|
||||
close(js.wait)
|
||||
}
|
||||
}
|
||||
return js
|
||||
}
|
||||
|
||||
func (self *jsre) loadAutoCompletion() {
|
||||
if modules, err := self.supportedApis(); err == nil {
|
||||
loadedModulesMethods = make(map[string][]string)
|
||||
for module, _ := range modules {
|
||||
loadedModulesMethods[module] = api.AutoCompletion[module]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *jsre) batch(statement string) {
|
||||
err := self.re.EvalAndPrettyPrint(statement)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("error: %v", err)
|
||||
}
|
||||
|
||||
if self.atexit != nil {
|
||||
self.atexit()
|
||||
}
|
||||
|
||||
self.re.Stop(false)
|
||||
}
|
||||
|
||||
// show summary of current geth instance
|
||||
func (self *jsre) welcome() {
|
||||
self.re.Run(`
|
||||
(function () {
|
||||
console.log('instance: ' + web3.version.client);
|
||||
console.log(' datadir: ' + admin.datadir);
|
||||
console.log("coinbase: " + eth.coinbase);
|
||||
var ts = 1000 * eth.getBlock(eth.blockNumber).timestamp;
|
||||
console.log("at block: " + eth.blockNumber + " (" + new Date(ts) + ")");
|
||||
})();
|
||||
`)
|
||||
if modules, err := self.supportedApis(); err == nil {
|
||||
loadedModules := make([]string, 0)
|
||||
for api, version := range modules {
|
||||
loadedModules = append(loadedModules, fmt.Sprintf("%s:%s", api, version))
|
||||
}
|
||||
sort.Strings(loadedModules)
|
||||
fmt.Println("modules:", strings.Join(loadedModules, " "))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *jsre) supportedApis() (map[string]string, error) {
|
||||
return self.client.SupportedModules()
|
||||
}
|
||||
|
||||
func (js *jsre) apiBindings(f xeth.Frontend) error {
|
||||
apis, err := js.supportedApis()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apiNames := make([]string, 0, len(apis))
|
||||
for a, _ := range apis {
|
||||
apiNames = append(apiNames, a)
|
||||
}
|
||||
|
||||
apiImpl, err := api.ParseApiString(strings.Join(apiNames, ","), codec.JSON, js.xeth, js.ethereum)
|
||||
if err != nil {
|
||||
utils.Fatalf("Unable to determine supported api's: %v", err)
|
||||
}
|
||||
|
||||
jeth := rpc.NewJeth(api.Merge(apiImpl...), js.re, js.client, f)
|
||||
js.re.Set("jeth", struct{}{})
|
||||
t, _ := js.re.Get("jeth")
|
||||
jethObj := t.Object()
|
||||
|
||||
jethObj.Set("send", jeth.Send)
|
||||
jethObj.Set("sendAsync", jeth.Send)
|
||||
|
||||
err = js.re.Compile("bignumber.js", re.BigNumber_JS)
|
||||
if err != nil {
|
||||
utils.Fatalf("Error loading bignumber.js: %v", err)
|
||||
}
|
||||
|
||||
err = js.re.Compile("ethereum.js", re.Web3_JS)
|
||||
if err != nil {
|
||||
utils.Fatalf("Error loading web3.js: %v", err)
|
||||
}
|
||||
|
||||
_, err = js.re.Run("var web3 = require('web3');")
|
||||
if err != nil {
|
||||
utils.Fatalf("Error requiring web3: %v", err)
|
||||
}
|
||||
|
||||
_, err = js.re.Run("web3.setProvider(jeth)")
|
||||
if err != nil {
|
||||
utils.Fatalf("Error setting web3 provider: %v", err)
|
||||
}
|
||||
|
||||
// load only supported API's in javascript runtime
|
||||
shortcuts := "var eth = web3.eth; "
|
||||
for _, apiName := range apiNames {
|
||||
if apiName == shared.Web3ApiName {
|
||||
continue // manually mapped
|
||||
}
|
||||
|
||||
if err = js.re.Compile(fmt.Sprintf("%s.js", apiName), api.Javascript(apiName)); err == nil {
|
||||
shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName)
|
||||
} else {
|
||||
utils.Fatalf("Error loading %s.js: %v", apiName, err)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = js.re.Run(shortcuts)
|
||||
|
||||
if err != nil {
|
||||
utils.Fatalf("Error setting namespaces: %v", err)
|
||||
}
|
||||
|
||||
js.re.Run(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *jsre) ConfirmTransaction(tx string) bool {
|
||||
if self.ethereum.NatSpec {
|
||||
notice := natspec.GetNotice(self.xeth, tx, self.ds)
|
||||
fmt.Println(notice)
|
||||
answer, _ := self.Prompt("Confirm Transaction [y/n]")
|
||||
return strings.HasPrefix(strings.Trim(answer, " "), "y")
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (self *jsre) UnlockAccount(addr []byte) bool {
|
||||
fmt.Printf("Please unlock account %x.\n", addr)
|
||||
pass, err := self.PasswordPrompt("Passphrase: ")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// TODO: allow retry
|
||||
if err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(addr), pass); err != nil {
|
||||
return false
|
||||
} else {
|
||||
fmt.Println("Account is now unlocked for this session.")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (self *jsre) IsAccountLocked(addr []byte) bool {
|
||||
fmt.Printf("Please check account lock status %x.\n", addr)
|
||||
|
||||
locked := self.ethereum.AccountManager().IsLocked(common.BytesToAddress(addr))
|
||||
if ( locked ) {
|
||||
fmt.Println("Account is still locked.")
|
||||
} else {
|
||||
fmt.Println("Account is still unlocked.")
|
||||
}
|
||||
|
||||
return locked
|
||||
}
|
||||
|
||||
func (self *jsre) exec(filename string) error {
|
||||
if err := self.re.Exec(filename); err != nil {
|
||||
self.re.Stop(false)
|
||||
return fmt.Errorf("Javascript Error: %v", err)
|
||||
}
|
||||
self.re.Stop(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *jsre) interactive() {
|
||||
// Read input lines.
|
||||
prompt := make(chan string)
|
||||
inputln := make(chan string)
|
||||
go func() {
|
||||
defer close(inputln)
|
||||
for {
|
||||
line, err := self.Prompt(<-prompt)
|
||||
if err != nil {
|
||||
if err == liner.ErrPromptAborted { // ctrl-C
|
||||
self.resetPrompt()
|
||||
inputln <- ""
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
inputln <- line
|
||||
}
|
||||
}()
|
||||
// Wait for Ctrl-C, too.
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, os.Interrupt)
|
||||
|
||||
defer func() {
|
||||
if self.atexit != nil {
|
||||
self.atexit()
|
||||
}
|
||||
self.re.Stop(false)
|
||||
}()
|
||||
for {
|
||||
prompt <- self.ps1
|
||||
select {
|
||||
case <-sig:
|
||||
fmt.Println("caught interrupt, exiting")
|
||||
return
|
||||
case input, ok := <-inputln:
|
||||
if !ok || indentCount <= 0 && input == "exit" {
|
||||
return
|
||||
}
|
||||
if input == "" {
|
||||
continue
|
||||
}
|
||||
str += input + "\n"
|
||||
self.setIndent()
|
||||
if indentCount <= 0 {
|
||||
hist := hidepassword(str[:len(str)-1])
|
||||
if len(hist) > 0 {
|
||||
self.AppendHistory(hist)
|
||||
}
|
||||
self.parseInput(str)
|
||||
str = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func hidepassword(input string) string {
|
||||
if passwordRegexp.MatchString(input) {
|
||||
return passwordRepl
|
||||
} else {
|
||||
return input
|
||||
}
|
||||
}
|
||||
|
||||
func (self *jsre) withHistory(op func(*os.File)) {
|
||||
datadir := common.DefaultDataDir()
|
||||
if self.ethereum != nil {
|
||||
datadir = self.ethereum.DataDir
|
||||
}
|
||||
|
||||
hist, err := os.OpenFile(filepath.Join(datadir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
|
||||
if err != nil {
|
||||
fmt.Printf("unable to open history file: %v\n", err)
|
||||
return
|
||||
}
|
||||
op(hist)
|
||||
hist.Close()
|
||||
}
|
||||
|
||||
func (self *jsre) parseInput(code string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("[native] error", r)
|
||||
}
|
||||
}()
|
||||
if err := self.re.EvalAndPrettyPrint(code); err != nil {
|
||||
if ottoErr, ok := err.(*otto.Error); ok {
|
||||
fmt.Println(ottoErr.String())
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var indentCount = 0
|
||||
var str = ""
|
||||
|
||||
func (self *jsre) resetPrompt() {
|
||||
indentCount = 0
|
||||
str = ""
|
||||
self.ps1 = "> "
|
||||
}
|
||||
|
||||
func (self *jsre) setIndent() {
|
||||
open := strings.Count(str, "{")
|
||||
open += strings.Count(str, "(")
|
||||
closed := strings.Count(str, "}")
|
||||
closed += strings.Count(str, ")")
|
||||
indentCount = open - closed
|
||||
if indentCount <= 0 {
|
||||
self.ps1 = "> "
|
||||
} else {
|
||||
self.ps1 = strings.Join(make([]string, indentCount*2), "..")
|
||||
self.ps1 += " "
|
||||
}
|
||||
}
|
||||
521
cmd/chatty/js_test.go
Normal file
521
cmd/chatty/js_test.go
Normal file
|
|
@ -0,0 +1,521 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/chattynet/chatty/accounts"
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/common/compiler"
|
||||
"github.com/chattynet/chatty/common/docserver"
|
||||
"github.com/chattynet/chatty/common/natspec"
|
||||
"github.com/chattynet/chatty/common/registrar"
|
||||
"github.com/chattynet/chatty/core"
|
||||
"github.com/chattynet/chatty/crypto"
|
||||
"github.com/chattynet/chatty/eth"
|
||||
"github.com/chattynet/chatty/ethdb"
|
||||
"github.com/chattynet/chatty/rpc/codec"
|
||||
"github.com/chattynet/chatty/rpc/comms"
|
||||
)
|
||||
|
||||
const (
|
||||
testSolcPath = ""
|
||||
solcVersion = "0.9.23"
|
||||
|
||||
testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674"
|
||||
testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||
testBalance = "10000000000000000000"
|
||||
// of empty string
|
||||
testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
)
|
||||
|
||||
var (
|
||||
versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`))
|
||||
testNodeKey = crypto.ToECDSA(common.Hex2Bytes("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f"))
|
||||
testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}`
|
||||
)
|
||||
|
||||
type testjethre struct {
|
||||
*jsre
|
||||
lastConfirm string
|
||||
ds *docserver.DocServer
|
||||
}
|
||||
|
||||
func (self *testjethre) UnlockAccount(acc []byte) bool {
|
||||
err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "")
|
||||
if err != nil {
|
||||
panic("unable to unlock")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *testjethre) IsAccountLocked(acc []byte) bool {
|
||||
return self.ethereum.AccountManager().IsLocked(common.BytesToAddress(acc))
|
||||
}
|
||||
|
||||
func (self *testjethre) ConfirmTransaction(tx string) bool {
|
||||
if self.ethereum.NatSpec {
|
||||
self.lastConfirm = natspec.GetNotice(self.xeth, tx, self.ds)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
|
||||
return testREPL(t, nil)
|
||||
}
|
||||
|
||||
func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *eth.Ethereum) {
|
||||
tmp, err := ioutil.TempDir("", "geth-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
core.WriteGenesisBlockForTesting(db, common.HexToAddress(testAddress), common.String2Big(testBalance))
|
||||
ks := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore"))
|
||||
am := accounts.NewManager(ks)
|
||||
conf := ð.Config{
|
||||
NodeKey: testNodeKey,
|
||||
DataDir: tmp,
|
||||
AccountManager: am,
|
||||
MaxPeers: 0,
|
||||
Name: "test",
|
||||
SolcPath: testSolcPath,
|
||||
PowTest: true,
|
||||
NewDB: func(path string) (common.Database, error) { return db, nil },
|
||||
}
|
||||
if config != nil {
|
||||
config(conf)
|
||||
}
|
||||
ethereum, err := eth.New(conf)
|
||||
if err != nil {
|
||||
t.Fatal("%v", err)
|
||||
}
|
||||
|
||||
keyb, err := crypto.HexToECDSA(testKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key := crypto.NewKeyFromECDSA(keyb)
|
||||
err = ks.StoreKey(key, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = am.Unlock(key.Address, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
|
||||
client := comms.NewInProcClient(codec.JSON)
|
||||
ds := docserver.New("/")
|
||||
tf := &testjethre{ds: ds}
|
||||
repl := newJSRE(ethereum, assetPath, "", client, false, tf)
|
||||
tf.jsre = repl
|
||||
return tmp, tf, ethereum
|
||||
}
|
||||
|
||||
func TestNodeInfo(t *testing.T) {
|
||||
t.Skip("broken after p2p update")
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Fatalf("error starting ethereum: %v", err)
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
want := `{"DiscPort":0,"IP":"0.0.0.0","ListenAddr":"","Name":"test","NodeID":"4cb2fc32924e94277bf94b5e4c983beedb2eabd5a0bc941db32202735c6625d020ca14a5963d1738af43b6ac0a711d61b1a06de931a499fe2aa0b1a132a902b5","NodeUrl":"enode://4cb2fc32924e94277bf94b5e4c983beedb2eabd5a0bc941db32202735c6625d020ca14a5963d1738af43b6ac0a711d61b1a06de931a499fe2aa0b1a132a902b5@0.0.0.0:0","TCPPort":0,"Td":"131072"}`
|
||||
checkEvalJSON(t, repl, `admin.nodeInfo`, want)
|
||||
}
|
||||
|
||||
func TestAccounts(t *testing.T) {
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Fatalf("error starting ethereum: %v", err)
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`"]`)
|
||||
checkEvalJSON(t, repl, `eth.coinbase`, `"`+testAddress+`"`)
|
||||
val, err := repl.re.Run(`personal.newAccount("password")`)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
addr := val.String()
|
||||
if !regexp.MustCompile(`0x[0-9a-f]{40}`).MatchString(addr) {
|
||||
t.Errorf("address not hex: %q", addr)
|
||||
}
|
||||
|
||||
checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`","`+addr+`"]`)
|
||||
|
||||
}
|
||||
|
||||
func TestBlockChain(t *testing.T) {
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Fatalf("error starting ethereum: %v", err)
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
// get current block dump before export/import.
|
||||
val, err := repl.re.Run("JSON.stringify(debug.dumpBlock(eth.blockNumber))")
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
beforeExport := val.String()
|
||||
|
||||
// do the export
|
||||
extmp, err := ioutil.TempDir("", "geth-test-export")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(extmp)
|
||||
tmpfile := filepath.Join(extmp, "export.chain")
|
||||
tmpfileq := strconv.Quote(tmpfile)
|
||||
|
||||
ethereum.ChainManager().Reset()
|
||||
|
||||
checkEvalJSON(t, repl, `admin.exportChain(`+tmpfileq+`)`, `true`)
|
||||
if _, err := os.Stat(tmpfile); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// check import, verify that dumpBlock gives the same result.
|
||||
checkEvalJSON(t, repl, `admin.importChain(`+tmpfileq+`)`, `true`)
|
||||
checkEvalJSON(t, repl, `debug.dumpBlock(eth.blockNumber)`, beforeExport)
|
||||
}
|
||||
|
||||
func TestMining(t *testing.T) {
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Fatalf("error starting ethereum: %v", err)
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
checkEvalJSON(t, repl, `eth.mining`, `false`)
|
||||
}
|
||||
|
||||
func TestRPC(t *testing.T) {
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Errorf("error starting ethereum: %v", err)
|
||||
return
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
checkEvalJSON(t, repl, `admin.startRPC("127.0.0.1", 53901, "*", "web3,chy,net")`, `true`)
|
||||
}
|
||||
|
||||
func TestCheckTestAccountBalance(t *testing.T) {
|
||||
t.Skip() // i don't think it tests the correct behaviour here. it's actually testing
|
||||
// internals which shouldn't be tested. This now fails because of a change in the core
|
||||
// and i have no means to fix this, sorry - @obscuren
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Errorf("error starting ethereum: %v", err)
|
||||
return
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
repl.re.Run(`primary = "` + testAddress + `"`)
|
||||
checkEvalJSON(t, repl, `eth.getBalance(primary)`, `"`+testBalance+`"`)
|
||||
}
|
||||
|
||||
func TestSignature(t *testing.T) {
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Errorf("error starting ethereum: %v", err)
|
||||
return
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
val, err := repl.re.Run(`chy.sign("` + testAddress + `", "` + testHash + `")`)
|
||||
|
||||
// This is a very preliminary test, lacking actual signature verification
|
||||
if err != nil {
|
||||
t.Errorf("Error running js: %v", err)
|
||||
return
|
||||
}
|
||||
output := val.String()
|
||||
t.Logf("Output: %v", output)
|
||||
|
||||
regex := regexp.MustCompile(`^0x[0-9a-f]{130}$`)
|
||||
if !regex.MatchString(output) {
|
||||
t.Errorf("Signature is not 65 bytes represented in hexadecimal.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestContract(t *testing.T) {
|
||||
t.Skip("contract testing is implemented with mining in ethash test mode. This takes about 7seconds to run. Unskip and run on demand")
|
||||
coinbase := common.HexToAddress(testAddress)
|
||||
tmp, repl, ethereum := testREPL(t, func(conf *eth.Config) {
|
||||
conf.Etherbase = coinbase
|
||||
conf.PowTest = true
|
||||
})
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Errorf("error starting ethereum: %v", err)
|
||||
return
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
reg := registrar.New(repl.xeth)
|
||||
_, err := reg.SetGlobalRegistrar("", coinbase)
|
||||
if err != nil {
|
||||
t.Errorf("error setting HashReg: %v", err)
|
||||
}
|
||||
_, err = reg.SetHashReg("", coinbase)
|
||||
if err != nil {
|
||||
t.Errorf("error setting HashReg: %v", err)
|
||||
}
|
||||
_, err = reg.SetUrlHint("", coinbase)
|
||||
if err != nil {
|
||||
t.Errorf("error setting HashReg: %v", err)
|
||||
}
|
||||
/* TODO:
|
||||
* lookup receipt and contract addresses by tx hash
|
||||
* name registration for HashReg and UrlHint addresses
|
||||
* mine those transactions
|
||||
* then set once more SetHashReg SetUrlHint
|
||||
*/
|
||||
|
||||
source := `contract test {\n` +
|
||||
" /// @notice Will multiply `a` by 7." + `\n` +
|
||||
` function multiply(uint a) returns(uint d) {\n` +
|
||||
` return a * 7;\n` +
|
||||
` }\n` +
|
||||
`}\n`
|
||||
|
||||
if checkEvalJSON(t, repl, `admin.stopNatSpec()`, `true`) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
contractInfo, err := ioutil.ReadFile("info_test.json")
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
if checkEvalJSON(t, repl, `primary = eth.accounts[0]`, `"`+testAddress+`"`) != nil {
|
||||
return
|
||||
}
|
||||
if checkEvalJSON(t, repl, `source = "`+source+`"`, `"`+source+`"`) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// if solc is found with right version, test it, otherwise read from file
|
||||
sol, err := compiler.New("")
|
||||
if err != nil {
|
||||
t.Logf("solc not found: mocking contract compilation step")
|
||||
} else if sol.Version() != solcVersion {
|
||||
t.Logf("WARNING: solc different version found (%v, test written for %v, may need to update)", sol.Version(), solcVersion)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
info, err := ioutil.ReadFile("info_test.json")
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
_, err = repl.re.Run(`contract = JSON.parse(` + strconv.Quote(string(info)) + `)`)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
} else {
|
||||
if checkEvalJSON(t, repl, `contract = eth.compile.solidity(source).test`, string(contractInfo)) != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if checkEvalJSON(t, repl, `contract.code`, `"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"`) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if checkEvalJSON(
|
||||
t, repl,
|
||||
`contractaddress = eth.sendTransaction({from: primary, data: contract.code})`,
|
||||
`"0x46d69d55c3c4b86a924a92c9fc4720bb7bce1d74"`,
|
||||
) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !processTxs(repl, t, 8) {
|
||||
return
|
||||
}
|
||||
|
||||
callSetup := `abiDef = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]');
|
||||
Multiply7 = eth.contract(abiDef);
|
||||
multiply7 = Multiply7.at(contractaddress);
|
||||
`
|
||||
_, err = repl.re.Run(callSetup)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error setting up contract, got %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
expNotice := ""
|
||||
if repl.lastConfirm != expNotice {
|
||||
t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm)
|
||||
return
|
||||
}
|
||||
|
||||
if checkEvalJSON(t, repl, `admin.startNatSpec()`, `true`) != nil {
|
||||
return
|
||||
}
|
||||
if checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary })`, `"0x4ef9088431a8033e4580d00e4eb2487275e031ff4163c7529df0ef45af17857b"`) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !processTxs(repl, t, 1) {
|
||||
return
|
||||
}
|
||||
|
||||
expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x46d69d55c3c4b86a924a92c9fc4720bb7bce1d74","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}`
|
||||
if repl.lastConfirm != expNotice {
|
||||
t.Errorf("incorrect confirmation message: expected\n%v, got\n%v", expNotice, repl.lastConfirm)
|
||||
return
|
||||
}
|
||||
|
||||
var contentHash = `"0x86d2b7cf1e72e9a7a3f8d96601f0151742a2f780f1526414304fbe413dc7f9bd"`
|
||||
if sol != nil && solcVersion != sol.Version() {
|
||||
modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"`+sol.Version()+`"`))
|
||||
fmt.Printf("modified contractinfo:\n%s\n", modContractInfo)
|
||||
contentHash = `"` + common.ToHex(crypto.Sha3([]byte(modContractInfo))) + `"`
|
||||
}
|
||||
if checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`) != nil {
|
||||
return
|
||||
}
|
||||
if checkEvalJSON(t, repl, `contentHash = admin.saveInfo(contract.info, filename)`, contentHash) != nil {
|
||||
return
|
||||
}
|
||||
if checkEvalJSON(t, repl, `admin.register(primary, contractaddress, contentHash)`, `true`) != nil {
|
||||
return
|
||||
}
|
||||
if checkEvalJSON(t, repl, `admin.registerUrl(primary, contentHash, "file://"+filename)`, `true`) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if checkEvalJSON(t, repl, `admin.startNatSpec()`, `true`) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !processTxs(repl, t, 3) {
|
||||
return
|
||||
}
|
||||
|
||||
if checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary })`, `"0x66d7635c12ad0b231e66da2f987ca3dfdca58ffe49c6442aa55960858103fd0c"`) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !processTxs(repl, t, 1) {
|
||||
return
|
||||
}
|
||||
|
||||
expNotice = "Will multiply 6 by 7."
|
||||
if repl.lastConfirm != expNotice {
|
||||
t.Errorf("incorrect confirmation message: expected\n%v, got\n%v", expNotice, repl.lastConfirm)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func pendingTransactions(repl *testjethre, t *testing.T) (txc int64, err error) {
|
||||
txs := repl.ethereum.TxPool().GetTransactions()
|
||||
return int64(len(txs)), nil
|
||||
}
|
||||
|
||||
func processTxs(repl *testjethre, t *testing.T, expTxc int) bool {
|
||||
var txc int64
|
||||
var err error
|
||||
for i := 0; i < 50; i++ {
|
||||
txc, err = pendingTransactions(repl, t)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error checking pending transactions: %v", err)
|
||||
return false
|
||||
}
|
||||
if expTxc < int(txc) {
|
||||
t.Errorf("too many pending transactions: expected %v, got %v", expTxc, txc)
|
||||
return false
|
||||
} else if expTxc == int(txc) {
|
||||
break
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
if int(txc) != expTxc {
|
||||
t.Errorf("incorrect number of pending transactions, expected %v, got %v", expTxc, txc)
|
||||
return false
|
||||
}
|
||||
|
||||
err = repl.ethereum.StartMining(runtime.NumCPU())
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error mining: %v", err)
|
||||
return false
|
||||
}
|
||||
defer repl.ethereum.StopMining()
|
||||
|
||||
timer := time.NewTimer(100 * time.Second)
|
||||
height := new(big.Int).Add(repl.xeth.CurrentBlock().Number(), big.NewInt(1))
|
||||
repl.wait <- height
|
||||
select {
|
||||
case <-timer.C:
|
||||
// if times out make sure the xeth loop does not block
|
||||
go func() {
|
||||
select {
|
||||
case repl.wait <- nil:
|
||||
case <-repl.wait:
|
||||
}
|
||||
}()
|
||||
case <-repl.wait:
|
||||
}
|
||||
txc, err = pendingTransactions(repl, t)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error checking pending transactions: %v", err)
|
||||
return false
|
||||
}
|
||||
if txc != 0 {
|
||||
t.Errorf("%d trasactions were not mined", txc)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func checkEvalJSON(t *testing.T, re *testjethre, expr, want string) error {
|
||||
val, err := re.re.Run("JSON.stringify(" + expr + ")")
|
||||
if err == nil && val.String() != want {
|
||||
err = fmt.Errorf("Output mismatch for `%s`:\ngot: %s\nwant: %s", expr, val.String(), want)
|
||||
}
|
||||
if err != nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
file = filepath.Base(file)
|
||||
fmt.Printf("\t%s:%d: %v\n", file, line, err)
|
||||
t.Fail()
|
||||
}
|
||||
return err
|
||||
}
|
||||
783
cmd/chatty/main.go
Normal file
783
cmd/chatty/main.go
Normal file
|
|
@ -0,0 +1,783 @@
|
|||
// Copyright 2014 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// chatty is the official command-line client for Ethereum.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/chattynet/chatty/accounts"
|
||||
"github.com/chattynet/chatty/cmd/utils"
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/core"
|
||||
"github.com/chattynet/chatty/core/types"
|
||||
"github.com/chattynet/chatty/eth"
|
||||
"github.com/chattynet/chatty/ethdb"
|
||||
"github.com/chattynet/chatty/logger"
|
||||
"github.com/chattynet/chatty/logger/glog"
|
||||
"github.com/chattynet/chatty/metrics"
|
||||
"github.com/chattynet/chatty/params"
|
||||
"github.com/chattynet/chatty/rlp"
|
||||
"github.com/chattynet/chatty/rpc/codec"
|
||||
"github.com/chattynet/chatty/rpc/comms"
|
||||
)
|
||||
|
||||
const (
|
||||
ClientIdentifier = "CHATTY"
|
||||
Version = "0.1.0"
|
||||
VersionMajor = 0
|
||||
VersionMinor = 1
|
||||
VersionPatch = 0
|
||||
)
|
||||
|
||||
var (
|
||||
gitCommit string // set via linker flagg
|
||||
nodeNameVersion string
|
||||
app *cli.App
|
||||
)
|
||||
|
||||
func init() {
|
||||
if gitCommit == "" {
|
||||
nodeNameVersion = Version
|
||||
} else {
|
||||
nodeNameVersion = Version + "-" + gitCommit[:8]
|
||||
}
|
||||
|
||||
app = utils.NewApp(Version, "the Chatty command line interface")
|
||||
app.Action = run
|
||||
app.HideVersion = true // we have a command to print the version
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Action: blockRecovery,
|
||||
Name: "recover",
|
||||
Usage: "attempts to recover a corrupted database by setting a new block by number or hash. See help recover.",
|
||||
Description: `
|
||||
The recover commands will attempt to read out the last
|
||||
block based on that.
|
||||
|
||||
recover #number recovers by number
|
||||
recover <hex> recovers by hash
|
||||
`,
|
||||
},
|
||||
blocktestCommand,
|
||||
importCommand,
|
||||
exportCommand,
|
||||
upgradedbCommand,
|
||||
removedbCommand,
|
||||
dumpCommand,
|
||||
monitorCommand,
|
||||
{
|
||||
Action: makedag,
|
||||
Name: "makedag",
|
||||
Usage: "generate ethash dag (for testing)",
|
||||
Description: `
|
||||
The makedag command generates an ethash DAG in /tmp/dag.
|
||||
|
||||
This command exists to support the system testing project.
|
||||
Regular users do not need to execute it.
|
||||
`,
|
||||
},
|
||||
{
|
||||
Action: version,
|
||||
Name: "version",
|
||||
Usage: "print chatty version numbers",
|
||||
Description: `
|
||||
The output of this command is supposed to be machine-readable.
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
Name: "wallet",
|
||||
Usage: "chatty presale wallet",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Action: importWallet,
|
||||
Name: "import",
|
||||
Usage: "Not used in Chatty.",
|
||||
},
|
||||
},
|
||||
Description: `
|
||||
|
||||
get wallet import /path/to/my/presale.wallet
|
||||
|
||||
will prompt for your password and imports your ether presale account.
|
||||
It can be used non-interactively with the --password option taking a
|
||||
passwordfile as argument containing the wallet password in plaintext.
|
||||
|
||||
`},
|
||||
{
|
||||
Action: accountList,
|
||||
Name: "account",
|
||||
Usage: "manage accounts",
|
||||
Description: `
|
||||
|
||||
Manage accounts lets you create new accounts, list all existing accounts,
|
||||
import a private key into a new account.
|
||||
|
||||
' help' shows a list of subcommands or help for one subcommand.
|
||||
|
||||
It supports interactive mode, when you are prompted for password as well as
|
||||
non-interactive mode where passwords are supplied via a given password file.
|
||||
Non-interactive mode is only meant for scripted use on test networks or known
|
||||
safe environments.
|
||||
|
||||
Make sure you remember the password you gave when creating a new account (with
|
||||
either new or import). Without it you are not able to unlock your account.
|
||||
|
||||
Note that exporting your key in unencrypted format is NOT supported.
|
||||
|
||||
Keys are stored under <DATADIR>/keys.
|
||||
It is safe to transfer the entire directory or the individual keys therein
|
||||
between ethereum nodes by simply copying.
|
||||
Make sure you backup your keys regularly.
|
||||
|
||||
In order to use your account to send transactions, you need to unlock them using the
|
||||
'--unlock' option. The argument is a comma
|
||||
|
||||
And finally. DO NOT FORGET YOUR PASSWORD.
|
||||
`,
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Action: accountList,
|
||||
Name: "list",
|
||||
Usage: "print account addresses",
|
||||
},
|
||||
{
|
||||
Action: accountCreate,
|
||||
Name: "new",
|
||||
Usage: "create a new account",
|
||||
Description: `
|
||||
|
||||
chatty account new
|
||||
|
||||
Creates a new account. Prints the address.
|
||||
|
||||
The account is saved in encrypted format, you are prompted for a passphrase.
|
||||
|
||||
You must remember this passphrase to unlock your account in the future.
|
||||
|
||||
For non-interactive use the passphrase can be specified with the --password flag:
|
||||
|
||||
chatty --password <passwordfile> account new
|
||||
|
||||
Note, this is meant to be used for testing only, it is a bad idea to save your
|
||||
password to file or expose in any other way.
|
||||
`,
|
||||
},
|
||||
{
|
||||
Action: accountUpdate,
|
||||
Name: "update",
|
||||
Usage: "update an existing account",
|
||||
Description: `
|
||||
|
||||
chatty account update <address>
|
||||
|
||||
Update an existing account.
|
||||
|
||||
The account is saved in the newest version in encrypted format, you are prompted
|
||||
for a passphrase to unlock the account and another to save the updated file.
|
||||
|
||||
This same command can therefore be used to migrate an account of a deprecated
|
||||
format to the newest format or change the password for an account.
|
||||
|
||||
For non-interactive use the passphrase can be specified with the --password flag:
|
||||
|
||||
chatty --password <passwordfile> account new
|
||||
|
||||
Since only one password can be given, only format update can be performed,
|
||||
changing your password is only possible interactively.
|
||||
|
||||
Note that account update has the a side effect that the order of your accounts
|
||||
changes.
|
||||
`,
|
||||
},
|
||||
// doesn't work since it's in memory for the running instance: TODO: do an IPC call to it
|
||||
/*{
|
||||
Action: isAccountLocked,
|
||||
Name: "locked",
|
||||
Usage: "check account lock status",
|
||||
Description: `
|
||||
|
||||
chatty account locked <address>
|
||||
|
||||
Check locked status of an existing account.
|
||||
|
||||
chatty account locked <addr>
|
||||
`,
|
||||
},*/
|
||||
{
|
||||
Action: accountImport,
|
||||
Name: "import",
|
||||
Usage: "import a private key into a new account",
|
||||
Description: `
|
||||
|
||||
chatty account import <keyfile>
|
||||
|
||||
Imports an unencrypted private key from <keyfile> and creates a new account.
|
||||
Prints the address.
|
||||
|
||||
The keyfile is assumed to contain an unencrypted private key in hexadecimal format.
|
||||
|
||||
The account is saved in encrypted format, you are prompted for a passphrase.
|
||||
|
||||
You must remember this passphrase to unlock your account in the future.
|
||||
|
||||
For non-interactive use the passphrase can be specified with the -password flag:
|
||||
|
||||
chatty --password <passwordfile> account import <keyfile>
|
||||
|
||||
Note:
|
||||
As you can directly copy your encrypted accounts to another ethereum instance,
|
||||
this import mechanism is not needed when you transfer an account between
|
||||
nodes.
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Action: console,
|
||||
Name: "console",
|
||||
Usage: `Chatty Console: interactive JavaScript environment`,
|
||||
Description: `
|
||||
The chatty console is an interactive shell for the JavaScript runtime environment
|
||||
which exposes a node admin interface as well as the Ðapp JavaScript API.
|
||||
See https://github.com/chattynet/chatty/wiki/Javascipt-Console
|
||||
`},
|
||||
{
|
||||
Action: attach,
|
||||
Name: "attach",
|
||||
Usage: `Chatty Console: interactive JavaScript environment (connect to node)`,
|
||||
Description: `
|
||||
The chatty console is an interactive shell for the JavaScript runtime environment
|
||||
which exposes a node admin interface as well as the Ðapp JavaScript API.
|
||||
See https://github.com/chattynet/chatty/wiki/Javascipt-Console.
|
||||
This command allows to open a console on a running chatty node.
|
||||
`,
|
||||
},
|
||||
{
|
||||
Action: execJSFiles,
|
||||
Name: "js",
|
||||
Usage: `executes the given JavaScript files in the chatty JavaScript VM`,
|
||||
Description: `
|
||||
The JavaScript VM exposes a node admin interface as well as the Ðapp
|
||||
JavaScript API. See https://github.com/chattynet/chatty/wiki/Javascipt-Console
|
||||
`,
|
||||
},
|
||||
}
|
||||
app.Flags = []cli.Flag{
|
||||
utils.IdentityFlag,
|
||||
utils.UnlockedAccountFlag,
|
||||
utils.PasswordFileFlag,
|
||||
utils.GenesisFileFlag,
|
||||
utils.BootnodesFlag,
|
||||
utils.DataDirFlag,
|
||||
utils.BlockchainVersionFlag,
|
||||
utils.OlympicFlag,
|
||||
utils.CacheFlag,
|
||||
utils.JSpathFlag,
|
||||
utils.ListenPortFlag,
|
||||
utils.MaxPeersFlag,
|
||||
utils.MaxPendingPeersFlag,
|
||||
utils.EtherbaseFlag,
|
||||
utils.GasPriceFlag,
|
||||
utils.MinerThreadsFlag,
|
||||
utils.MiningEnabledFlag,
|
||||
utils.AutoDAGFlag,
|
||||
utils.NATFlag,
|
||||
utils.NatspecEnabledFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
utils.NodeKeyFileFlag,
|
||||
utils.NodeKeyHexFlag,
|
||||
utils.RPCEnabledFlag,
|
||||
utils.RPCListenAddrFlag,
|
||||
utils.RPCPortFlag,
|
||||
utils.RpcApiFlag,
|
||||
utils.IPCDisabledFlag,
|
||||
utils.IPCApiFlag,
|
||||
utils.IPCPathFlag,
|
||||
utils.ExecFlag,
|
||||
utils.WhisperEnabledFlag,
|
||||
utils.VMDebugFlag,
|
||||
utils.VMForceJitFlag,
|
||||
utils.VMJitCacheFlag,
|
||||
utils.VMEnableJitFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.RPCCORSDomainFlag,
|
||||
utils.VerbosityFlag,
|
||||
utils.BacktraceAtFlag,
|
||||
utils.LogToStdErrFlag,
|
||||
utils.LogVModuleFlag,
|
||||
utils.LogFileFlag,
|
||||
utils.LogJSONFlag,
|
||||
utils.PProfEanbledFlag,
|
||||
utils.PProfPortFlag,
|
||||
utils.MetricsEnabledFlag,
|
||||
utils.SolcPathFlag,
|
||||
utils.GpoMinGasPriceFlag,
|
||||
utils.GpoMaxGasPriceFlag,
|
||||
utils.GpoFullBlockRatioFlag,
|
||||
utils.GpobaseStepDownFlag,
|
||||
utils.GpobaseStepUpFlag,
|
||||
utils.GpobaseCorrectionFactorFlag,
|
||||
}
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
utils.SetupLogger(ctx)
|
||||
utils.SetupVM(ctx)
|
||||
if ctx.GlobalBool(utils.PProfEanbledFlag.Name) {
|
||||
utils.StartPProf(ctx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Start system runtime metrics collection
|
||||
go metrics.CollectProcessMetrics(3 * time.Second)
|
||||
}
|
||||
|
||||
func main() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
defer logger.Flush()
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func makeDefaultExtra() []byte {
|
||||
var clientInfo = struct {
|
||||
Version uint
|
||||
Name string
|
||||
GoVersion string
|
||||
Os string
|
||||
}{uint(VersionMajor<<16 | VersionMinor<<8 | VersionPatch), ClientIdentifier, runtime.Version(), runtime.GOOS}
|
||||
extra, err := rlp.EncodeToBytes(clientInfo)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information:", err)
|
||||
}
|
||||
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize)
|
||||
glog.V(logger.Debug).Infof("extra: %x\n", extra)
|
||||
return nil
|
||||
}
|
||||
|
||||
return extra
|
||||
}
|
||||
|
||||
func run(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
if ctx.GlobalBool(utils.OlympicFlag.Name) {
|
||||
utils.InitOlympic()
|
||||
}
|
||||
|
||||
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
|
||||
cfg.ExtraData = makeDefaultExtra()
|
||||
|
||||
ethereum, err := eth.New(cfg)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
startEth(ctx, ethereum)
|
||||
// this blocks the thread
|
||||
ethereum.WaitForShutdown()
|
||||
}
|
||||
|
||||
func attach(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
var client comms.EthereumClient
|
||||
var err error
|
||||
if ctx.Args().Present() {
|
||||
client, err = comms.ClientFromEndpoint(ctx.Args().First(), codec.JSON)
|
||||
} else {
|
||||
cfg := comms.IpcConfig{
|
||||
Endpoint: ctx.GlobalString(utils.IPCPathFlag.Name),
|
||||
}
|
||||
client, err = comms.NewIpcClient(cfg, codec.JSON)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.Fatalf("Unable to attach to chatty node - %v", err)
|
||||
}
|
||||
|
||||
repl := newLightweightJSRE(
|
||||
ctx.GlobalString(utils.JSpathFlag.Name),
|
||||
client,
|
||||
true,
|
||||
)
|
||||
|
||||
if ctx.GlobalString(utils.ExecFlag.Name) != "" {
|
||||
repl.batch(ctx.GlobalString(utils.ExecFlag.Name))
|
||||
} else {
|
||||
repl.welcome()
|
||||
repl.interactive()
|
||||
}
|
||||
}
|
||||
|
||||
func console(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
|
||||
ethereum, err := eth.New(cfg)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
client := comms.NewInProcClient(codec.JSON)
|
||||
|
||||
startEth(ctx, ethereum)
|
||||
repl := newJSRE(
|
||||
ethereum,
|
||||
ctx.GlobalString(utils.JSpathFlag.Name),
|
||||
ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
|
||||
client,
|
||||
true,
|
||||
nil,
|
||||
)
|
||||
|
||||
if ctx.GlobalString(utils.ExecFlag.Name) != "" {
|
||||
repl.batch(ctx.GlobalString(utils.ExecFlag.Name))
|
||||
} else {
|
||||
repl.welcome()
|
||||
repl.interactive()
|
||||
}
|
||||
|
||||
ethereum.Stop()
|
||||
ethereum.WaitForShutdown()
|
||||
}
|
||||
|
||||
func execJSFiles(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
|
||||
ethereum, err := eth.New(cfg)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
client := comms.NewInProcClient(codec.JSON)
|
||||
startEth(ctx, ethereum)
|
||||
repl := newJSRE(
|
||||
ethereum,
|
||||
ctx.GlobalString(utils.JSpathFlag.Name),
|
||||
ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
|
||||
client,
|
||||
false,
|
||||
nil,
|
||||
)
|
||||
for _, file := range ctx.Args() {
|
||||
repl.exec(file)
|
||||
}
|
||||
|
||||
ethereum.Stop()
|
||||
ethereum.WaitForShutdown()
|
||||
}
|
||||
|
||||
func unlockAccount(ctx *cli.Context, am *accounts.Manager, addr string, i int) (addrHex, auth string) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
var err error
|
||||
addrHex, err = utils.ParamToAddress(addr, am)
|
||||
if err == nil {
|
||||
// Attempt to unlock the account 3 times
|
||||
attempts := 3
|
||||
for tries := 0; tries < attempts; tries++ {
|
||||
msg := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", addr, tries+1, attempts)
|
||||
auth = getPassPhrase(ctx, msg, false, i)
|
||||
err = am.Unlock(common.HexToAddress(addrHex), auth)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.Fatalf("Unlock account failed '%v'", err)
|
||||
}
|
||||
fmt.Printf("Account '%s' unlocked.\n", addr)
|
||||
return
|
||||
}
|
||||
|
||||
// doesn't work since it's in memory for the running instance: TODO: do an IPC call to it
|
||||
/*func accountLocked(ctx *cli.Context, am *accounts.Manager, addr string) (addrHex string, locked bool) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
var err error
|
||||
addrHex, err = utils.ParamToAddress(addr, am)
|
||||
if err == nil {
|
||||
locked = am.IsLocked(common.HexToAddress(addrHex))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.Fatalf("Account lock check failed '%v'", err)
|
||||
}
|
||||
|
||||
return
|
||||
}*/
|
||||
|
||||
func blockRecovery(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
arg := ctx.Args().First()
|
||||
if len(ctx.Args()) < 1 && len(arg) > 0 {
|
||||
glog.Fatal("recover requires block number or hash")
|
||||
}
|
||||
|
||||
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
|
||||
utils.CheckLegalese(cfg.DataDir)
|
||||
|
||||
blockDb, err := ethdb.NewLDBDatabase(filepath.Join(cfg.DataDir, "blockchain"), cfg.DatabaseCache)
|
||||
if err != nil {
|
||||
glog.Fatalln("could not open db:", err)
|
||||
}
|
||||
|
||||
var block *types.Block
|
||||
if arg[0] == '#' {
|
||||
block = core.GetBlockByNumber(blockDb, common.String2Big(arg[1:]).Uint64())
|
||||
} else {
|
||||
block = core.GetBlockByHash(blockDb, common.HexToHash(arg))
|
||||
}
|
||||
|
||||
if block == nil {
|
||||
glog.Fatalln("block not found. Recovery failed")
|
||||
}
|
||||
|
||||
err = core.WriteHead(blockDb, block)
|
||||
if err != nil {
|
||||
glog.Fatalln("block write err", err)
|
||||
}
|
||||
glog.Infof("Recovery succesful. New HEAD %x\n", block.Hash())
|
||||
}
|
||||
|
||||
func startEth(ctx *cli.Context, eth *eth.Ethereum) {
|
||||
// Start Ethereum itself
|
||||
utils.StartEthereum(eth)
|
||||
|
||||
am := eth.AccountManager()
|
||||
account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
|
||||
accounts := strings.Split(account, " ")
|
||||
for i, account := range accounts {
|
||||
if len(account) > 0 {
|
||||
if account == "primary" {
|
||||
utils.Fatalf("the 'primary' keyword is deprecated. You can use integer indexes, but the indexes are not permanent, they can change if you add external keys, export your keys or copy your keystore to another node.")
|
||||
}
|
||||
unlockAccount(ctx, am, account, i)
|
||||
}
|
||||
}
|
||||
// Start auxiliary services if enabled.
|
||||
if !ctx.GlobalBool(utils.IPCDisabledFlag.Name) {
|
||||
if err := utils.StartIPC(eth, ctx); err != nil {
|
||||
utils.Fatalf("Error string IPC: %v", err)
|
||||
}
|
||||
}
|
||||
if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
|
||||
if err := utils.StartRPC(eth, ctx); err != nil {
|
||||
utils.Fatalf("Error starting RPC: %v", err)
|
||||
}
|
||||
}
|
||||
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
|
||||
if err := eth.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name)); err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func accountList(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
am := utils.MakeAccountManager(ctx)
|
||||
accts, err := am.Accounts()
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not list accounts: %v", err)
|
||||
}
|
||||
for i, acct := range accts {
|
||||
fmt.Printf("Account #%d: %x\n", i, acct)
|
||||
}
|
||||
}
|
||||
|
||||
func getPassPhrase(ctx *cli.Context, desc string, confirmation bool, i int) (passphrase string) {
|
||||
passfile := ctx.GlobalString(utils.PasswordFileFlag.Name)
|
||||
if len(passfile) == 0 {
|
||||
fmt.Println(desc)
|
||||
auth, err := utils.PromptPassword("Passphrase: ", true)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
if confirmation {
|
||||
confirm, err := utils.PromptPassword("Repeat Passphrase: ", false)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
if auth != confirm {
|
||||
utils.Fatalf("Passphrases did not match.")
|
||||
}
|
||||
}
|
||||
passphrase = auth
|
||||
|
||||
} else {
|
||||
passbytes, err := ioutil.ReadFile(passfile)
|
||||
if err != nil {
|
||||
utils.Fatalf("Unable to read password file '%s': %v", passfile, err)
|
||||
}
|
||||
// this is backwards compatible if the same password unlocks several accounts
|
||||
// it also has the consequence that trailing newlines will not count as part
|
||||
// of the password, so --password <(echo -n 'pass') will now work without -n
|
||||
passphrases := strings.Split(string(passbytes), "\n")
|
||||
if i >= len(passphrases) {
|
||||
passphrase = passphrases[len(passphrases)-1]
|
||||
} else {
|
||||
passphrase = passphrases[i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func accountCreate(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
am := utils.MakeAccountManager(ctx)
|
||||
passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0)
|
||||
acct, err := am.NewAccount(passphrase)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not create the account: %v", err)
|
||||
}
|
||||
fmt.Printf("Address: %x\n", acct)
|
||||
}
|
||||
|
||||
func accountUpdate(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
am := utils.MakeAccountManager(ctx)
|
||||
arg := ctx.Args().First()
|
||||
if len(arg) == 0 {
|
||||
utils.Fatalf("account address or index must be given as argument")
|
||||
}
|
||||
|
||||
addr, authFrom := unlockAccount(ctx, am, arg, 0)
|
||||
authTo := getPassPhrase(ctx, "Please give a new password. Do not forget this password.", true, 0)
|
||||
err := am.Update(common.HexToAddress(addr), authFrom, authTo)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not update the account: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// doesn't work since it's in memory for the running instance: TODO: do an IPC call to it
|
||||
/*
|
||||
func isAccountLocked(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
am := utils.MakeAccountManager(ctx)
|
||||
arg := ctx.Args().First()
|
||||
if len(arg) == 0 {
|
||||
utils.Fatalf("account address or index must be given as argument")
|
||||
}
|
||||
|
||||
acct, locked := accountLocked(ctx, am, arg)
|
||||
fmt.Printf("Account '%s' lock status: %t\n", acct, locked)
|
||||
}*/
|
||||
|
||||
func importWallet(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
keyfile := ctx.Args().First()
|
||||
if len(keyfile) == 0 {
|
||||
utils.Fatalf("keyfile must be given as argument")
|
||||
}
|
||||
keyJson, err := ioutil.ReadFile(keyfile)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not read wallet file: %v", err)
|
||||
}
|
||||
|
||||
am := utils.MakeAccountManager(ctx)
|
||||
passphrase := getPassPhrase(ctx, "", false, 0)
|
||||
|
||||
acct, err := am.ImportPreSaleKey(keyJson, passphrase)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not create the account: %v", err)
|
||||
}
|
||||
fmt.Printf("Address: %x\n", acct)
|
||||
}
|
||||
|
||||
func accountImport(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
keyfile := ctx.Args().First()
|
||||
if len(keyfile) == 0 {
|
||||
utils.Fatalf("keyfile must be given as argument")
|
||||
}
|
||||
am := utils.MakeAccountManager(ctx)
|
||||
passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0)
|
||||
acct, err := am.Import(keyfile, passphrase)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not create the account: %v", err)
|
||||
}
|
||||
fmt.Printf("Address: %x\n", acct)
|
||||
}
|
||||
|
||||
func makedag(ctx *cli.Context) {
|
||||
utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
|
||||
|
||||
args := ctx.Args()
|
||||
wrongArgs := func() {
|
||||
utils.Fatalf(`Usage: chatty makedag <block number> <outputdir>`)
|
||||
}
|
||||
switch {
|
||||
case len(args) == 2:
|
||||
blockNum, err := strconv.ParseUint(args[0], 0, 64)
|
||||
dir := args[1]
|
||||
if err != nil {
|
||||
wrongArgs()
|
||||
} else {
|
||||
dir = filepath.Clean(dir)
|
||||
// seems to require a trailing slash
|
||||
if !strings.HasSuffix(dir, "/") {
|
||||
dir = dir + "/"
|
||||
}
|
||||
_, err = ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
utils.Fatalf("Can't find dir")
|
||||
}
|
||||
fmt.Println("making DAG, this could take awhile...")
|
||||
ethash.MakeDAG(blockNum, dir)
|
||||
}
|
||||
default:
|
||||
wrongArgs()
|
||||
}
|
||||
}
|
||||
|
||||
func version(c *cli.Context) {
|
||||
fmt.Println(ClientIdentifier)
|
||||
fmt.Println("Version:", Version)
|
||||
if gitCommit != "" {
|
||||
fmt.Println("Git Commit:", gitCommit)
|
||||
}
|
||||
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
|
||||
fmt.Println("Network Id:", c.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())
|
||||
}
|
||||
352
cmd/chatty/monitorcmd.go
Normal file
352
cmd/chatty/monitorcmd.go
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/chattynet/chatty/cmd/utils"
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/rpc"
|
||||
"github.com/chattynet/chatty/rpc/codec"
|
||||
"github.com/chattynet/chatty/rpc/comms"
|
||||
"github.com/gizak/termui"
|
||||
)
|
||||
|
||||
var (
|
||||
monitorCommandAttachFlag = cli.StringFlag{
|
||||
Name: "attach",
|
||||
Value: "ipc:" + common.DefaultIpcPath(),
|
||||
Usage: "API endpoint to attach to",
|
||||
}
|
||||
monitorCommandRowsFlag = cli.IntFlag{
|
||||
Name: "rows",
|
||||
Value: 5,
|
||||
Usage: "Maximum rows in the chart grid",
|
||||
}
|
||||
monitorCommandRefreshFlag = cli.IntFlag{
|
||||
Name: "refresh",
|
||||
Value: 3,
|
||||
Usage: "Refresh interval in seconds",
|
||||
}
|
||||
monitorCommand = cli.Command{
|
||||
Action: monitor,
|
||||
Name: "monitor",
|
||||
Usage: `chatty Monitor: node metrics monitoring and visualization`,
|
||||
Description: `
|
||||
The chatty monitor is a tool to collect and visualize various internal metrics
|
||||
gathered by the node, supporting different chart types as well as the capacity
|
||||
to display multiple metrics simultaneously.
|
||||
`,
|
||||
Flags: []cli.Flag{
|
||||
monitorCommandAttachFlag,
|
||||
monitorCommandRowsFlag,
|
||||
monitorCommandRefreshFlag,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// monitor starts a terminal UI based monitoring tool for the requested metrics.
|
||||
func monitor(ctx *cli.Context) {
|
||||
var (
|
||||
client comms.EthereumClient
|
||||
err error
|
||||
)
|
||||
// Attach to an chatty node over IPC or RPC
|
||||
endpoint := ctx.String(monitorCommandAttachFlag.Name)
|
||||
if client, err = comms.ClientFromEndpoint(endpoint, codec.JSON); err != nil {
|
||||
utils.Fatalf("Unable to attach to chatty node: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
xeth := rpc.NewXeth(client)
|
||||
|
||||
// Retrieve all the available metrics and resolve the user pattens
|
||||
metrics, err := retrieveMetrics(xeth)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to retrieve system metrics: %v", err)
|
||||
}
|
||||
monitored := resolveMetrics(metrics, ctx.Args())
|
||||
if len(monitored) == 0 {
|
||||
list := expandMetrics(metrics, "")
|
||||
sort.Strings(list)
|
||||
|
||||
if len(list) > 0 {
|
||||
utils.Fatalf("No metrics specified.\n\nAvailable:\n - %s", strings.Join(list, "\n - "))
|
||||
} else {
|
||||
utils.Fatalf("No metrics collected by chatty (--%s).\n", utils.MetricsEnabledFlag.Name)
|
||||
}
|
||||
}
|
||||
sort.Strings(monitored)
|
||||
if cols := len(monitored) / ctx.Int(monitorCommandRowsFlag.Name); cols > 6 {
|
||||
utils.Fatalf("Requested metrics (%d) spans more that 6 columns:\n - %s", len(monitored), strings.Join(monitored, "\n - "))
|
||||
}
|
||||
// Create and configure the chart UI defaults
|
||||
if err := termui.Init(); err != nil {
|
||||
utils.Fatalf("Unable to initialize terminal UI: %v", err)
|
||||
}
|
||||
defer termui.Close()
|
||||
|
||||
termui.UseTheme("helloworld")
|
||||
|
||||
rows := len(monitored)
|
||||
if max := ctx.Int(monitorCommandRowsFlag.Name); rows > max {
|
||||
rows = max
|
||||
}
|
||||
cols := (len(monitored) + rows - 1) / rows
|
||||
for i := 0; i < rows; i++ {
|
||||
termui.Body.AddRows(termui.NewRow())
|
||||
}
|
||||
// Create each individual data chart
|
||||
footer := termui.NewPar("")
|
||||
footer.HasBorder = true
|
||||
footer.Height = 3
|
||||
|
||||
charts := make([]*termui.LineChart, len(monitored))
|
||||
units := make([]int, len(monitored))
|
||||
data := make([][]float64, len(monitored))
|
||||
for i := 0; i < len(monitored); i++ {
|
||||
charts[i] = createChart((termui.TermHeight() - footer.Height) / rows)
|
||||
row := termui.Body.Rows[i%rows]
|
||||
row.Cols = append(row.Cols, termui.NewCol(12/cols, 0, charts[i]))
|
||||
}
|
||||
termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer)))
|
||||
|
||||
refreshCharts(xeth, monitored, data, units, charts, ctx, footer)
|
||||
termui.Body.Align()
|
||||
termui.Render(termui.Body)
|
||||
|
||||
// Watch for various system events, and periodically refresh the charts
|
||||
refresh := time.Tick(time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second)
|
||||
for {
|
||||
select {
|
||||
case event := <-termui.EventCh():
|
||||
if event.Type == termui.EventKey && event.Key == termui.KeyCtrlC {
|
||||
return
|
||||
}
|
||||
if event.Type == termui.EventResize {
|
||||
termui.Body.Width = termui.TermWidth()
|
||||
for _, chart := range charts {
|
||||
chart.Height = (termui.TermHeight() - footer.Height) / rows
|
||||
}
|
||||
termui.Body.Align()
|
||||
termui.Render(termui.Body)
|
||||
}
|
||||
case <-refresh:
|
||||
if refreshCharts(xeth, monitored, data, units, charts, ctx, footer) {
|
||||
termui.Body.Align()
|
||||
}
|
||||
termui.Render(termui.Body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// retrieveMetrics contacts the attached chatty node and retrieves the entire set
|
||||
// of collected system metrics.
|
||||
func retrieveMetrics(xeth *rpc.Xeth) (map[string]interface{}, error) {
|
||||
return xeth.Call("debug_metrics", []interface{}{true})
|
||||
}
|
||||
|
||||
// resolveMetrics takes a list of input metric patterns, and resolves each to one
|
||||
// or more canonical metric names.
|
||||
func resolveMetrics(metrics map[string]interface{}, patterns []string) []string {
|
||||
res := []string{}
|
||||
for _, pattern := range patterns {
|
||||
res = append(res, resolveMetric(metrics, pattern, "")...)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// resolveMetrics takes a single of input metric pattern, and resolves it to one
|
||||
// or more canonical metric names.
|
||||
func resolveMetric(metrics map[string]interface{}, pattern string, path string) []string {
|
||||
results := []string{}
|
||||
|
||||
// If a nested metric was requested, recurse optionally branching (via comma)
|
||||
parts := strings.SplitN(pattern, "/", 2)
|
||||
if len(parts) > 1 {
|
||||
for _, variation := range strings.Split(parts[0], ",") {
|
||||
if submetrics, ok := metrics[variation].(map[string]interface{}); !ok {
|
||||
utils.Fatalf("Failed to retrieve system metrics: %s", path+variation)
|
||||
return nil
|
||||
} else {
|
||||
results = append(results, resolveMetric(submetrics, parts[1], path+variation+"/")...)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
// Depending what the last link is, return or expand
|
||||
for _, variation := range strings.Split(pattern, ",") {
|
||||
switch metric := metrics[variation].(type) {
|
||||
case float64:
|
||||
// Final metric value found, return as singleton
|
||||
results = append(results, path+variation)
|
||||
|
||||
case map[string]interface{}:
|
||||
results = append(results, expandMetrics(metric, path+variation+"/")...)
|
||||
|
||||
default:
|
||||
utils.Fatalf("Metric pattern resolved to unexpected type: %v", reflect.TypeOf(metric))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// expandMetrics expands the entire tree of metrics into a flat list of paths.
|
||||
func expandMetrics(metrics map[string]interface{}, path string) []string {
|
||||
// Iterate over all fields and expand individually
|
||||
list := []string{}
|
||||
for name, metric := range metrics {
|
||||
switch metric := metric.(type) {
|
||||
case float64:
|
||||
// Final metric value found, append to list
|
||||
list = append(list, path+name)
|
||||
|
||||
case map[string]interface{}:
|
||||
// Tree of metrics found, expand recursively
|
||||
list = append(list, expandMetrics(metric, path+name+"/")...)
|
||||
|
||||
default:
|
||||
utils.Fatalf("Metric pattern %s resolved to unexpected type: %v", path+name, reflect.TypeOf(metric))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// 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
|
||||
for _, part := range parts[:len(parts)-1] {
|
||||
metrics, found = metrics[part].(map[string]interface{})
|
||||
if !found {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
if v, ok := metrics[parts[len(parts)-1]].(float64); ok {
|
||||
return v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// refreshCharts retrieves a next batch of metrics, and inserts all the new
|
||||
// values into the active datasets and charts
|
||||
func refreshCharts(xeth *rpc.Xeth, metrics []string, data [][]float64, units []int, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) (realign bool) {
|
||||
values, err := retrieveMetrics(xeth)
|
||||
for i, metric := range metrics {
|
||||
if len(data) < 512 {
|
||||
data[i] = append([]float64{fetchMetric(values, metric)}, data[i]...)
|
||||
} else {
|
||||
data[i] = append([]float64{fetchMetric(values, metric)}, data[i][:len(data[i])-1]...)
|
||||
}
|
||||
if updateChart(metric, data[i], &units[i], charts[i], err) {
|
||||
realign = true
|
||||
}
|
||||
}
|
||||
updateFooter(ctx, err, footer)
|
||||
return
|
||||
}
|
||||
|
||||
// updateChart inserts a dataset into a line chart, scaling appropriately as to
|
||||
// not display weird labels, also updating the chart label accordingly.
|
||||
func updateChart(metric string, data []float64, base *int, chart *termui.LineChart, err error) (realign bool) {
|
||||
dataUnits := []string{"", "K", "M", "G", "T", "E"}
|
||||
timeUnits := []string{"ns", "µs", "ms", "s", "ks", "ms"}
|
||||
colors := []termui.Attribute{termui.ColorBlue, termui.ColorCyan, termui.ColorGreen, termui.ColorYellow, termui.ColorRed, termui.ColorRed}
|
||||
|
||||
// Extract only part of the data that's actually visible
|
||||
if chart.Width*2 < len(data) {
|
||||
data = data[:chart.Width*2]
|
||||
}
|
||||
// Find the maximum value and scale under 1K
|
||||
high := 0.0
|
||||
if len(data) > 0 {
|
||||
high = data[0]
|
||||
for _, value := range data[1:] {
|
||||
high = math.Max(high, value)
|
||||
}
|
||||
}
|
||||
unit, scale := 0, 1.0
|
||||
for high >= 1000 {
|
||||
high, unit, scale = high/1000, unit+1, scale*1000
|
||||
}
|
||||
// If the unit changes, re-create the chart (hack to set max height...)
|
||||
if unit != *base {
|
||||
realign, *base, *chart = true, unit, *createChart(chart.Height)
|
||||
}
|
||||
// Update the chart's data points with the scaled values
|
||||
if cap(chart.Data) < len(data) {
|
||||
chart.Data = make([]float64, len(data))
|
||||
}
|
||||
chart.Data = chart.Data[:len(data)]
|
||||
for i, value := range data {
|
||||
chart.Data[i] = value / scale
|
||||
}
|
||||
// Update the chart's label with the scale units
|
||||
units := dataUnits
|
||||
if strings.Contains(metric, "/Percentiles/") || strings.Contains(metric, "/pauses/") || strings.Contains(metric, "/time/") {
|
||||
units = timeUnits
|
||||
}
|
||||
chart.Border.Label = metric
|
||||
if len(units[unit]) > 0 {
|
||||
chart.Border.Label += " [" + units[unit] + "]"
|
||||
}
|
||||
chart.LineColor = colors[unit] | termui.AttrBold
|
||||
if err != nil {
|
||||
chart.LineColor = termui.ColorRed | termui.AttrBold
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// createChart creates an empty line chart with the default configs.
|
||||
func createChart(height int) *termui.LineChart {
|
||||
chart := termui.NewLineChart()
|
||||
if runtime.GOOS == "windows" {
|
||||
chart.Mode = "dot"
|
||||
}
|
||||
chart.DataLabels = []string{""}
|
||||
chart.Height = height
|
||||
chart.AxesColor = termui.ColorWhite
|
||||
chart.PaddingBottom = -2
|
||||
|
||||
chart.Border.LabelFgColor = chart.Border.FgColor | termui.AttrBold
|
||||
chart.Border.FgColor = chart.Border.BgColor
|
||||
|
||||
return chart
|
||||
}
|
||||
|
||||
// updateFooter updates the footer contents based on any encountered errors.
|
||||
func updateFooter(ctx *cli.Context, err error, footer *termui.Par) {
|
||||
// Generate the basic footer
|
||||
refresh := time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second
|
||||
footer.Text = fmt.Sprintf("Press Ctrl+C to quit. Refresh interval: %v.", refresh)
|
||||
footer.TextFgColor = termui.Theme().ParTextFg | termui.AttrBold
|
||||
|
||||
// Append any encountered errors
|
||||
if err != nil {
|
||||
footer.Text = fmt.Sprintf("Error: %v.", err)
|
||||
footer.TextFgColor = termui.ColorRed | termui.AttrBold
|
||||
}
|
||||
}
|
||||
41
cmd/utils/legalese.go
Normal file
41
cmd/utils/legalese.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package utils
|
||||
|
||||
const (
|
||||
legalese = `
|
||||
=======================================
|
||||
Disclaimer of Liabilites and Warranties
|
||||
=======================================
|
||||
|
||||
THE USER EXPRESSLY KNOWS AND AGREES THAT THE USER IS USING THE ETHEREUM PLATFORM AT THE USER’S SOLE
|
||||
RISK. THE USER REPRESENTS THAT THE USER HAS AN ADEQUATE UNDERSTANDING OF THE RISKS, USAGE AND
|
||||
INTRICACIES OF CRYPTOGRAPHIC TOKENS AND BLOCKCHAIN-BASED OPEN SOURCE SOFTWARE, ETH PLATFORM AND ETH.
|
||||
THE USER ACKNOWLEDGES AND AGREES THAT, TO THE FULLEST EXTENT PERMITTED BY ANY APPLICABLE LAW, THE
|
||||
DISCLAIMERS OF LIABILITY CONTAINED HEREIN APPLY TO ANY AND ALL DAMAGES OR INJURY WHATSOEVER CAUSED
|
||||
BY OR RELATED TO RISKS OF, USE OF, OR INABILITY TO USE, ETH OR THE ETHEREUM PLATFORM UNDER ANY CAUSE
|
||||
OR ACTION WHATSOEVER OF ANY KIND IN ANY JURISDICTION, INCLUDING, WITHOUT LIMITATION, ACTIONS FOR
|
||||
BREACH OF WARRANTY, BREACH OF CONTRACT OR TORT (INCLUDING NEGLIGENCE) AND THAT NEITHER STIFTUNG
|
||||
ETHEREUM NOR ETHEREUM TEAM SHALL BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR
|
||||
CONSEQUENTIAL DAMAGES, INCLUDING FOR LOSS OF PROFITS, GOODWILL OR DATA. SOME JURISDICTIONS DO NOT
|
||||
ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR THE LIMITATION OR EXCLUSION OF LIABILITY FOR CERTAIN
|
||||
TYPES OF DAMAGES. THEREFORE, SOME OF THE ABOVE LIMITATIONS IN THIS SECTION MAY NOT APPLY TO A USER.
|
||||
IN PARTICULAR, NOTHING IN THESE TERMS SHALL AFFECT THE STATUTORY RIGHTS OF ANY USER OR EXCLUDE
|
||||
INJURY ARISING FROM ANY WILLFUL MISCONDUCT OR FRAUD OF STIFTUNG ETHEREUM.
|
||||
|
||||
Do you accept this agreement?`
|
||||
)
|
||||
26
common/db.go
Normal file
26
common/db.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package common
|
||||
|
||||
// Database interface
|
||||
type Database interface {
|
||||
Put(key []byte, value []byte) error
|
||||
Get(key []byte) ([]byte, error)
|
||||
Delete(key []byte) error
|
||||
Close()
|
||||
Flush() error
|
||||
}
|
||||
114
common/docserver/docserver.go
Normal file
114
common/docserver/docserver.go
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package docserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/crypto"
|
||||
)
|
||||
|
||||
type DocServer struct {
|
||||
*http.Transport
|
||||
DocRoot string
|
||||
schemes []string
|
||||
}
|
||||
|
||||
func New(docRoot string) (self *DocServer) {
|
||||
self = &DocServer{
|
||||
Transport: &http.Transport{},
|
||||
DocRoot: docRoot,
|
||||
schemes: []string{"file"},
|
||||
}
|
||||
self.RegisterProtocol("file", http.NewFileTransport(http.Dir(self.DocRoot)))
|
||||
return
|
||||
}
|
||||
|
||||
// Clients should be reused instead of created as needed. Clients are safe for concurrent use by multiple goroutines.
|
||||
|
||||
// A Client is higher-level than a RoundTripper (such as Transport) and additionally handles HTTP details such as cookies and redirects.
|
||||
|
||||
func (self *DocServer) Client() *http.Client {
|
||||
return &http.Client{
|
||||
Transport: self,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *DocServer) RegisterScheme(scheme string, rt http.RoundTripper) {
|
||||
self.schemes = append(self.schemes, scheme)
|
||||
self.RegisterProtocol(scheme, rt)
|
||||
}
|
||||
|
||||
func (self *DocServer) HasScheme(scheme string) bool {
|
||||
for _, s := range self.schemes {
|
||||
if s == scheme {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *DocServer) GetAuthContent(uri string, hash common.Hash) (content []byte, err error) {
|
||||
// retrieve content
|
||||
content, err = self.Get(uri, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// check hash to authenticate content
|
||||
chash := crypto.Sha3Hash(content)
|
||||
if chash != hash {
|
||||
content = nil
|
||||
err = fmt.Errorf("content hash mismatch %x != %x (exp)", hash[:], chash[:])
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// Get(uri, path) downloads the document at uri, if path is non-empty it
|
||||
// is interpreted as a filepath to which the contents are saved
|
||||
func (self *DocServer) Get(uri, path string) (content []byte, err error) {
|
||||
// retrieve content
|
||||
resp, err := self.Client().Get(uri)
|
||||
|
||||
defer func() {
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
content, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if path != "" {
|
||||
var abspath string
|
||||
abspath, err = filepath.Abs(path)
|
||||
ioutil.WriteFile(abspath, content, 0700)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
77
common/docserver/docserver_test.go
Normal file
77
common/docserver/docserver_test.go
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package docserver
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/crypto"
|
||||
)
|
||||
|
||||
func TestGetAuthContent(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "docserver-test")
|
||||
if err != nil {
|
||||
t.Fatal("cannot create temporary directory:", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
ds := New(dir)
|
||||
|
||||
text := "test"
|
||||
hash := crypto.Sha3Hash([]byte(text))
|
||||
if err := ioutil.WriteFile(path.Join(dir, "test.content"), []byte(text), os.ModePerm); err != nil {
|
||||
t.Fatal("could not write test file", err)
|
||||
}
|
||||
content, err := ds.GetAuthContent("file:///test.content", hash)
|
||||
if err != nil {
|
||||
t.Errorf("no error expected, got %v", err)
|
||||
}
|
||||
if string(content) != text {
|
||||
t.Errorf("incorrect content. expected %v, got %v", text, string(content))
|
||||
}
|
||||
|
||||
hash = common.Hash{}
|
||||
content, err = ds.GetAuthContent("file:///test.content", hash)
|
||||
expected := "content hash mismatch 0000000000000000000000000000000000000000000000000000000000000000 != 9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658 (exp)"
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got nothing")
|
||||
} else {
|
||||
if err.Error() != expected {
|
||||
t.Errorf("expected error '%s' got '%v'", expected, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type rt struct{}
|
||||
|
||||
func (rt) RoundTrip(req *http.Request) (resp *http.Response, err error) { return }
|
||||
|
||||
func TestRegisterScheme(t *testing.T) {
|
||||
ds := New("/tmp/")
|
||||
if ds.HasScheme("scheme") {
|
||||
t.Errorf("expected scheme not to be registered")
|
||||
}
|
||||
ds.RegisterScheme("scheme", rt{})
|
||||
if !ds.HasScheme("scheme") {
|
||||
t.Errorf("expected scheme to be registered")
|
||||
}
|
||||
}
|
||||
120
core/block_cache.go
Normal file
120
core/block_cache.go
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/core/types"
|
||||
)
|
||||
|
||||
// BlockCache implements a caching mechanism specifically for blocks and uses FILO to pop
|
||||
type BlockCache struct {
|
||||
size int
|
||||
|
||||
hashes []common.Hash
|
||||
blocks map[common.Hash]*types.Block
|
||||
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// Creates and returns a `BlockCache` with `size`. If `size` is smaller than 1 it will panic
|
||||
func NewBlockCache(size int) *BlockCache {
|
||||
if size < 1 {
|
||||
panic("block cache size not allowed to be smaller than 1")
|
||||
}
|
||||
|
||||
bc := &BlockCache{size: size}
|
||||
bc.Clear()
|
||||
return bc
|
||||
}
|
||||
|
||||
func (bc *BlockCache) Clear() {
|
||||
bc.blocks = make(map[common.Hash]*types.Block)
|
||||
bc.hashes = nil
|
||||
|
||||
}
|
||||
|
||||
func (bc *BlockCache) Push(block *types.Block) {
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
if len(bc.hashes) == bc.size {
|
||||
delete(bc.blocks, bc.hashes[0])
|
||||
|
||||
// XXX There are a few other options on solving this
|
||||
// 1) use a poller / GC like mechanism to clean up untracked objects
|
||||
// 2) copy as below
|
||||
// re-use the slice and remove the reference to bc.hashes[0]
|
||||
// this will allow the element to be garbage collected.
|
||||
copy(bc.hashes, bc.hashes[1:])
|
||||
} else {
|
||||
bc.hashes = append(bc.hashes, common.Hash{})
|
||||
}
|
||||
|
||||
hash := block.Hash()
|
||||
bc.blocks[hash] = block
|
||||
bc.hashes[len(bc.hashes)-1] = hash
|
||||
}
|
||||
|
||||
func (bc *BlockCache) Delete(hash common.Hash) {
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
if _, ok := bc.blocks[hash]; ok {
|
||||
delete(bc.blocks, hash)
|
||||
for i, h := range bc.hashes {
|
||||
if hash == h {
|
||||
bc.hashes = bc.hashes[:i+copy(bc.hashes[i:], bc.hashes[i+1:])]
|
||||
// or ? => bc.hashes = append(bc.hashes[:i], bc.hashes[i+1]...)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (bc *BlockCache) Get(hash common.Hash) *types.Block {
|
||||
bc.mu.RLock()
|
||||
defer bc.mu.RUnlock()
|
||||
|
||||
if block, haz := bc.blocks[hash]; haz {
|
||||
return block
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bc *BlockCache) Has(hash common.Hash) bool {
|
||||
bc.mu.RLock()
|
||||
defer bc.mu.RUnlock()
|
||||
|
||||
_, ok := bc.blocks[hash]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (bc *BlockCache) Each(cb func(int, *types.Block)) {
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
i := 0
|
||||
for _, block := range bc.blocks {
|
||||
cb(i, block)
|
||||
i++
|
||||
}
|
||||
}
|
||||
76
core/block_cache_test.go
Normal file
76
core/block_cache_test.go
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/core/types"
|
||||
)
|
||||
|
||||
func newChain(size int) (chain []*types.Block) {
|
||||
var parentHash common.Hash
|
||||
for i := 0; i < size; i++ {
|
||||
head := &types.Header{ParentHash: parentHash, Number: big.NewInt(int64(i))}
|
||||
block := types.NewBlock(head, nil, nil, nil)
|
||||
chain = append(chain, block)
|
||||
parentHash = block.Hash()
|
||||
}
|
||||
return chain
|
||||
}
|
||||
|
||||
func insertChainCache(cache *BlockCache, chain []*types.Block) {
|
||||
for _, block := range chain {
|
||||
cache.Push(block)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewBlockCache(t *testing.T) {
|
||||
chain := newChain(3)
|
||||
cache := NewBlockCache(2)
|
||||
insertChainCache(cache, chain)
|
||||
|
||||
if cache.hashes[0] != chain[1].Hash() {
|
||||
t.Error("oldest block incorrect")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInclusion(t *testing.T) {
|
||||
chain := newChain(3)
|
||||
cache := NewBlockCache(3)
|
||||
insertChainCache(cache, chain)
|
||||
|
||||
for _, block := range chain {
|
||||
if b := cache.Get(block.Hash()); b == nil {
|
||||
t.Errorf("getting %x failed", block.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeletion(t *testing.T) {
|
||||
chain := newChain(3)
|
||||
cache := NewBlockCache(3)
|
||||
insertChainCache(cache, chain)
|
||||
|
||||
cache.Delete(chain[1].Hash())
|
||||
|
||||
if cache.Has(chain[1].Hash()) {
|
||||
t.Errorf("expected %x not to be included")
|
||||
}
|
||||
}
|
||||
421
core/block_processor.go
Normal file
421
core/block_processor.go
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/core/state"
|
||||
"github.com/chattynet/chatty/core/types"
|
||||
"github.com/chattynet/chatty/crypto"
|
||||
"github.com/chattynet/chatty/event"
|
||||
"github.com/chattynet/chatty/logger"
|
||||
"github.com/chattynet/chatty/logger/glog"
|
||||
"github.com/chattynet/chatty/params"
|
||||
"github.com/chattynet/chatty/pow"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
const (
|
||||
// must be bumped when consensus algorithm is changed, this forces the upgradedb
|
||||
// command to be run (forces the blocks to be imported again using the new algorithm)
|
||||
BlockChainVersion = 3
|
||||
)
|
||||
|
||||
type BlockProcessor struct {
|
||||
chainDb common.Database
|
||||
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
||||
mutex sync.Mutex
|
||||
// Canonical block chain
|
||||
bc *ChainManager
|
||||
// non-persistent key/value memory storage
|
||||
mem map[string]*big.Int
|
||||
// Proof of work used for validating
|
||||
Pow pow.PoW
|
||||
|
||||
events event.Subscription
|
||||
|
||||
eventMux *event.TypeMux
|
||||
}
|
||||
|
||||
// TODO: type GasPool big.Int
|
||||
//
|
||||
// GasPool is implemented by state.StateObject. This is a historical
|
||||
// coincidence. Gas tracking should move out of StateObject.
|
||||
|
||||
// GasPool tracks the amount of gas available during
|
||||
// execution of the transactions in a block.
|
||||
type GasPool interface {
|
||||
AddGas(gas, price *big.Int)
|
||||
SubGas(gas, price *big.Int) error
|
||||
}
|
||||
|
||||
func NewBlockProcessor(db common.Database, pow pow.PoW, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
|
||||
sm := &BlockProcessor{
|
||||
chainDb: db,
|
||||
mem: make(map[string]*big.Int),
|
||||
Pow: pow,
|
||||
bc: chainManager,
|
||||
eventMux: eventMux,
|
||||
}
|
||||
return sm
|
||||
}
|
||||
|
||||
func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) {
|
||||
gp := statedb.GetOrNewStateObject(block.Coinbase())
|
||||
gp.SetGasLimit(block.GasLimit())
|
||||
|
||||
// Process the transactions on to parent state
|
||||
receipts, err = sm.ApplyTransactions(gp, statedb, block, block.Transactions(), transientProcess)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return receipts, nil
|
||||
}
|
||||
|
||||
func (self *BlockProcessor) ApplyTransaction(gp GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
|
||||
_, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, header), tx, gp)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Update the state with pending changes
|
||||
statedb.SyncIntermediate()
|
||||
|
||||
usedGas.Add(usedGas, gas)
|
||||
receipt := types.NewReceipt(statedb.Root().Bytes(), usedGas)
|
||||
receipt.TxHash = tx.Hash()
|
||||
receipt.GasUsed = new(big.Int).Set(gas)
|
||||
if MessageCreatesContract(tx) {
|
||||
from, _ := tx.From()
|
||||
receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
|
||||
}
|
||||
|
||||
logs := statedb.GetLogs(tx.Hash())
|
||||
receipt.SetLogs(logs)
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
|
||||
glog.V(logger.Debug).Infoln(receipt)
|
||||
|
||||
// Notify all subscribers
|
||||
if !transientProcess {
|
||||
go self.eventMux.Post(TxPostEvent{tx})
|
||||
go self.eventMux.Post(logs)
|
||||
}
|
||||
|
||||
return receipt, gas, err
|
||||
}
|
||||
func (self *BlockProcessor) ChainManager() *ChainManager {
|
||||
return self.bc
|
||||
}
|
||||
|
||||
func (self *BlockProcessor) ApplyTransactions(gp GasPool, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, error) {
|
||||
var (
|
||||
receipts types.Receipts
|
||||
totalUsedGas = big.NewInt(0)
|
||||
err error
|
||||
cumulativeSum = new(big.Int)
|
||||
header = block.Header()
|
||||
)
|
||||
|
||||
for i, tx := range txs {
|
||||
statedb.StartRecord(tx.Hash(), block.Hash(), i)
|
||||
|
||||
receipt, txGas, err := self.ApplyTransaction(gp, statedb, header, tx, totalUsedGas, transientProcess)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
glog.V(logger.Core).Infoln("TX err:", err)
|
||||
}
|
||||
receipts = append(receipts, receipt)
|
||||
|
||||
cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice()))
|
||||
}
|
||||
|
||||
if block.GasUsed().Cmp(totalUsedGas) != 0 {
|
||||
return nil, ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), totalUsedGas))
|
||||
}
|
||||
|
||||
if transientProcess {
|
||||
go self.eventMux.Post(PendingBlockEvent{block, statedb.Logs()})
|
||||
}
|
||||
|
||||
return receipts, err
|
||||
}
|
||||
|
||||
func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.Logs, err error) {
|
||||
// Processing a blocks may never happen simultaneously
|
||||
sm.mutex.Lock()
|
||||
defer sm.mutex.Unlock()
|
||||
|
||||
if !sm.bc.HasBlock(block.ParentHash()) {
|
||||
return nil, ParentError(block.ParentHash())
|
||||
}
|
||||
parent := sm.bc.GetBlock(block.ParentHash())
|
||||
|
||||
// FIXME Change to full header validation. See #1225
|
||||
errch := make(chan bool)
|
||||
go func() { errch <- sm.Pow.Verify(block) }()
|
||||
|
||||
logs, _, err = sm.processWithParent(block, parent)
|
||||
if !<-errch {
|
||||
return nil, ValidationError("Block's nonce is invalid (= %x)", block.Nonce)
|
||||
}
|
||||
|
||||
return logs, err
|
||||
}
|
||||
|
||||
// Process block will attempt to process the given block's transactions and applies them
|
||||
// on top of the block's parent state (given it exists) and will return wether it was
|
||||
// successful or not.
|
||||
func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, receipts types.Receipts, err error) {
|
||||
// Processing a blocks may never happen simultaneously
|
||||
sm.mutex.Lock()
|
||||
defer sm.mutex.Unlock()
|
||||
|
||||
if sm.bc.HasBlock(block.Hash()) {
|
||||
return nil, nil, &KnownBlockError{block.Number(), block.Hash()}
|
||||
}
|
||||
|
||||
if !sm.bc.HasBlock(block.ParentHash()) {
|
||||
return nil, nil, ParentError(block.ParentHash())
|
||||
}
|
||||
parent := sm.bc.GetBlock(block.ParentHash())
|
||||
return sm.processWithParent(block, parent)
|
||||
}
|
||||
|
||||
func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs state.Logs, receipts types.Receipts, err error) {
|
||||
// Create a new state based on the parent's root (e.g., create copy)
|
||||
state := state.New(parent.Root(), sm.chainDb)
|
||||
header := block.Header()
|
||||
uncles := block.Uncles()
|
||||
txs := block.Transactions()
|
||||
|
||||
// Block validation
|
||||
if err = ValidateHeader(sm.Pow, header, parent, false, false); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// There can be at most two uncles
|
||||
if len(uncles) > 2 {
|
||||
return nil, nil, ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(uncles))
|
||||
}
|
||||
|
||||
receipts, err = sm.TransitionState(state, parent, block, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the received block's bloom with the one derived from the generated receipts.
|
||||
// For valid blocks this should always validate to true.
|
||||
rbloom := types.CreateBloom(receipts)
|
||||
if rbloom != header.Bloom {
|
||||
err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
|
||||
return
|
||||
}
|
||||
|
||||
// The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
|
||||
// can be used by light clients to make sure they've received the correct Txs
|
||||
txSha := types.DeriveSha(txs)
|
||||
if txSha != header.TxHash {
|
||||
err = fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha)
|
||||
return
|
||||
}
|
||||
|
||||
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
|
||||
receiptSha := types.DeriveSha(receipts)
|
||||
if receiptSha != header.ReceiptHash {
|
||||
err = fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify UncleHash before running other uncle validations
|
||||
unclesSha := types.CalcUncleHash(uncles)
|
||||
if unclesSha != header.UncleHash {
|
||||
err = fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify uncles
|
||||
if err = sm.VerifyUncles(state, block, parent); err != nil {
|
||||
return
|
||||
}
|
||||
// Accumulate static rewards; block reward, uncle's and uncle inclusion.
|
||||
AccumulateRewards(state, header, uncles)
|
||||
|
||||
// Commit state objects/accounts to a temporary trie (does not save)
|
||||
// used to calculate the state root.
|
||||
state.SyncObjects()
|
||||
if header.Root != state.Root() {
|
||||
err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
|
||||
return
|
||||
}
|
||||
|
||||
// Sync the current block's state to the database
|
||||
state.Sync()
|
||||
|
||||
return state.Logs(), receipts, nil
|
||||
}
|
||||
|
||||
var (
|
||||
big8 = big.NewInt(8)
|
||||
big32 = big.NewInt(32)
|
||||
)
|
||||
|
||||
// AccumulateRewards credits the coinbase of the given block with the
|
||||
// mining reward. The total reward consists of the static block reward
|
||||
// and rewards for included uncles. The coinbase of each uncle block is
|
||||
// also rewarded.
|
||||
func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) {
|
||||
reward := new(big.Int).Set(BlockReward)
|
||||
r := new(big.Int)
|
||||
for _, uncle := range uncles {
|
||||
r.Add(uncle.Number, big8)
|
||||
r.Sub(r, header.Number)
|
||||
r.Mul(r, BlockReward)
|
||||
r.Div(r, big8)
|
||||
statedb.AddBalance(uncle.Coinbase, r)
|
||||
r.Div(BlockReward, big32)
|
||||
|
||||
// Set static blockreward to 1 SHF
|
||||
r.Div(BlockReward, params.RewardDivisor)
|
||||
|
||||
reward.Add(reward, r)
|
||||
}
|
||||
statedb.AddBalance(header.Coinbase, reward)
|
||||
}
|
||||
|
||||
func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error {
|
||||
uncles := set.New()
|
||||
ancestors := make(map[common.Hash]*types.Block)
|
||||
for _, ancestor := range sm.bc.GetBlocksFromHash(block.ParentHash(), 7) {
|
||||
ancestors[ancestor.Hash()] = ancestor
|
||||
// Include ancestors uncles in the uncle set. Uncles must be unique.
|
||||
for _, uncle := range ancestor.Uncles() {
|
||||
uncles.Add(uncle.Hash())
|
||||
}
|
||||
}
|
||||
ancestors[block.Hash()] = block
|
||||
uncles.Add(block.Hash())
|
||||
|
||||
for i, uncle := range block.Uncles() {
|
||||
hash := uncle.Hash()
|
||||
if uncles.Has(hash) {
|
||||
// Error not unique
|
||||
return UncleError("uncle[%d](%x) not unique", i, hash[:4])
|
||||
}
|
||||
uncles.Add(hash)
|
||||
|
||||
if ancestors[hash] != nil {
|
||||
branch := fmt.Sprintf(" O - %x\n |\n", block.Hash())
|
||||
for h := range ancestors {
|
||||
branch += fmt.Sprintf(" O - %x\n |\n", h)
|
||||
}
|
||||
glog.Infoln(branch)
|
||||
return UncleError("uncle[%d](%x) is ancestor", i, hash[:4])
|
||||
}
|
||||
|
||||
if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() {
|
||||
return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4])
|
||||
}
|
||||
|
||||
if err := ValidateHeader(sm.Pow, uncle, ancestors[uncle.ParentHash], true, true); err != nil {
|
||||
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBlockReceipts returns the receipts beloniging to the block hash
|
||||
func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts {
|
||||
if block := sm.ChainManager().GetBlock(bhash); block != nil {
|
||||
return GetBlockReceipts(sm.chainDb, block.Hash())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLogs returns the logs of the given block. This method is using a two step approach
|
||||
// where it tries to get it from the (updated) method which gets them from the receipts or
|
||||
// the depricated way by re-processing the block.
|
||||
func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err error) {
|
||||
receipts := GetBlockReceipts(sm.chainDb, block.Hash())
|
||||
// coalesce logs
|
||||
for _, receipt := range receipts {
|
||||
logs = append(logs, receipt.Logs()...)
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// See YP section 4.3.4. "Block Header Validity"
|
||||
// Validates a block. Returns an error if the block is invalid.
|
||||
func ValidateHeader(pow pow.PoW, block *types.Header, parent *types.Block, checkPow, uncle bool) error {
|
||||
if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
|
||||
return fmt.Errorf("Block extra data too long (%d)", len(block.Extra))
|
||||
}
|
||||
|
||||
if uncle {
|
||||
if block.Time.Cmp(common.MaxBig) == 1 {
|
||||
return BlockTSTooBigErr
|
||||
}
|
||||
} else {
|
||||
if block.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 {
|
||||
return BlockFutureErr
|
||||
}
|
||||
}
|
||||
if block.Time.Cmp(parent.Time()) != 1 {
|
||||
return BlockEqualTSErr
|
||||
}
|
||||
|
||||
expd := CalcDifficulty(block.Time.Uint64(), parent.Time().Uint64(), parent.Number(), parent.Difficulty())
|
||||
if expd.Cmp(block.Difficulty) != 0 {
|
||||
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
|
||||
}
|
||||
|
||||
var a, b *big.Int
|
||||
a = parent.GasLimit()
|
||||
a = a.Sub(a, block.GasLimit)
|
||||
a.Abs(a)
|
||||
b = parent.GasLimit()
|
||||
b = b.Div(b, params.GasLimitBoundDivisor)
|
||||
if !(a.Cmp(b) < 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) {
|
||||
return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b)
|
||||
}
|
||||
|
||||
num := parent.Number()
|
||||
num.Sub(block.Number, num)
|
||||
if num.Cmp(big.NewInt(1)) != 0 {
|
||||
return BlockNumberErr
|
||||
}
|
||||
|
||||
if checkPow {
|
||||
// Verify the nonce of the block. Return an error if it's not valid
|
||||
if !pow.Verify(types.NewBlockWithHeader(block)) {
|
||||
return ValidationError("Block's nonce is invalid (= %x)", block.Nonce)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
88
core/block_processor_test.go
Normal file
88
core/block_processor_test.go
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/core/state"
|
||||
"github.com/chattynet/chatty/core/types"
|
||||
"github.com/chattynet/chatty/ethdb"
|
||||
"github.com/chattynet/chatty/event"
|
||||
"github.com/chattynet/chatty/pow/ezp"
|
||||
)
|
||||
|
||||
func proc() (*BlockProcessor, *ChainManager) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
var mux event.TypeMux
|
||||
|
||||
WriteTestNetGenesisBlock(db, 0)
|
||||
chainMan, err := NewChainManager(db, thePow(), &mux)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return NewBlockProcessor(db, ezp.New(), chainMan, &mux), chainMan
|
||||
}
|
||||
|
||||
func TestNumber(t *testing.T) {
|
||||
pow := ezp.New()
|
||||
_, chain := proc()
|
||||
|
||||
statedb := state.New(chain.Genesis().Root(), chain.chainDb)
|
||||
header := makeHeader(chain.Genesis(), statedb)
|
||||
header.Number = big.NewInt(3)
|
||||
err := ValidateHeader(pow, header, chain.Genesis(), false, false)
|
||||
if err != BlockNumberErr {
|
||||
t.Errorf("expected block number error, got %q", err)
|
||||
}
|
||||
|
||||
header = makeHeader(chain.Genesis(), statedb)
|
||||
err = ValidateHeader(pow, header, chain.Genesis(), false, false)
|
||||
if err == BlockNumberErr {
|
||||
t.Errorf("didn't expect block number error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPutReceipt(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
var addr common.Address
|
||||
addr[0] = 1
|
||||
var hash common.Hash
|
||||
hash[0] = 2
|
||||
|
||||
receipt := new(types.Receipt)
|
||||
receipt.SetLogs(state.Logs{&state.Log{
|
||||
Address: addr,
|
||||
Topics: []common.Hash{hash},
|
||||
Data: []byte("hi"),
|
||||
Number: 42,
|
||||
TxHash: hash,
|
||||
TxIndex: 0,
|
||||
BlockHash: hash,
|
||||
Index: 0,
|
||||
}})
|
||||
|
||||
PutReceipts(db, types.Receipts{receipt})
|
||||
receipt = GetReceipt(db, common.Hash{})
|
||||
if receipt == nil {
|
||||
t.Error("expected to get 1 receipt, got none.")
|
||||
}
|
||||
}
|
||||
903
core/chain_manager.go
Normal file
903
core/chain_manager.go
Normal file
|
|
@ -0,0 +1,903 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package core implements the Ethereum consensus protocol.
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/core/state"
|
||||
"github.com/chattynet/chatty/core/types"
|
||||
"github.com/chattynet/chatty/event"
|
||||
"github.com/chattynet/chatty/logger"
|
||||
"github.com/chattynet/chatty/logger/glog"
|
||||
"github.com/chattynet/chatty/metrics"
|
||||
"github.com/chattynet/chatty/pow"
|
||||
"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")
|
||||
)
|
||||
|
||||
const (
|
||||
blockCacheLimit = 256
|
||||
maxFutureBlocks = 256
|
||||
maxTimeFutureBlocks = 30
|
||||
checkpointLimit = 200
|
||||
)
|
||||
|
||||
type ChainManager struct {
|
||||
//eth EthManager
|
||||
chainDb common.Database
|
||||
sqlDB types.SQLDatabase
|
||||
processor types.BlockProcessor
|
||||
eventMux *event.TypeMux
|
||||
genesisBlock *types.Block
|
||||
// Last known total difficulty
|
||||
mu sync.RWMutex
|
||||
chainmu sync.RWMutex
|
||||
tsmu sync.RWMutex
|
||||
|
||||
checkpoint int // checkpoint counts towards the new checkpoint
|
||||
td *big.Int
|
||||
currentBlock *types.Block
|
||||
lastBlockHash common.Hash
|
||||
currentGasLimit *big.Int
|
||||
|
||||
cache *lru.Cache // cache is the LRU caching
|
||||
futureBlocks *lru.Cache // future blocks are blocks added for later processing
|
||||
|
||||
quit chan struct{}
|
||||
running int32 // running must be called automically
|
||||
// procInterrupt must be atomically called
|
||||
procInterrupt int32 // interrupt signaler for block processing
|
||||
wg sync.WaitGroup
|
||||
|
||||
pow pow.PoW
|
||||
}
|
||||
|
||||
func NewChainManager(chainDb common.Database, sqlDB types.SQLDatabase, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) {
|
||||
cache, _ := lru.New(blockCacheLimit)
|
||||
bc := &ChainManager{
|
||||
chainDb: chainDb,
|
||||
sqlDB: sqlDB,
|
||||
eventMux: mux,
|
||||
quit: make(chan struct{}),
|
||||
cache: cache,
|
||||
pow: pow,
|
||||
}
|
||||
|
||||
bc.genesisBlock = bc.GetBlockByNumber(0)
|
||||
if bc.genesisBlock == nil {
|
||||
reader, err := NewDefaultGenesisReader()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bc.genesisBlock, err = WriteGenesisBlock(chainDb, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
glog.V(logger.Info).Infoln("WARNING: Wrote default shift genesis block")
|
||||
}
|
||||
|
||||
if err := bc.setLastState(); err != nil {
|
||||
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 {
|
||||
if block := bc.GetBlock(hash); block != nil {
|
||||
glog.V(logger.Error).Infof("Found bad hash. Reorganising chain to state %x\n", block.ParentHash().Bytes()[:4])
|
||||
block = bc.GetBlock(block.ParentHash())
|
||||
if block == nil {
|
||||
glog.Fatal("Unable to complete. Parent block not found. Corrupted DB?")
|
||||
}
|
||||
bc.SetHead(block)
|
||||
|
||||
glog.V(logger.Error).Infoln("Chain reorg was successfull. Resuming normal operation")
|
||||
}
|
||||
}
|
||||
|
||||
// Take ownership of this particular state
|
||||
|
||||
bc.futureBlocks, _ = lru.New(maxFutureBlocks)
|
||||
bc.makeCache()
|
||||
|
||||
go bc.update()
|
||||
|
||||
return bc, nil
|
||||
}
|
||||
|
||||
func (bc *ChainManager) SetHead(head *types.Block) {
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
for block := bc.currentBlock; block != nil && block.Hash() != head.Hash(); block = bc.GetBlock(block.ParentHash()) {
|
||||
bc.removeBlock(block)
|
||||
}
|
||||
|
||||
bc.cache, _ = lru.New(blockCacheLimit)
|
||||
bc.currentBlock = head
|
||||
bc.makeCache()
|
||||
|
||||
bc.setTotalDifficulty(head.Td)
|
||||
bc.insert(head)
|
||||
bc.setLastState()
|
||||
}
|
||||
|
||||
func (self *ChainManager) Td() *big.Int {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return new(big.Int).Set(self.td)
|
||||
}
|
||||
|
||||
func (self *ChainManager) GasLimit() *big.Int {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.currentBlock.GasLimit()
|
||||
}
|
||||
|
||||
func (self *ChainManager) LastBlockHash() common.Hash {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.lastBlockHash
|
||||
}
|
||||
|
||||
func (self *ChainManager) CurrentBlock() *types.Block {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.currentBlock
|
||||
}
|
||||
|
||||
func (self *ChainManager) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return new(big.Int).Set(self.td), self.currentBlock.Hash(), self.genesisBlock.Hash()
|
||||
}
|
||||
|
||||
func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
|
||||
self.processor = proc
|
||||
}
|
||||
|
||||
func (self *ChainManager) State() *state.StateDB {
|
||||
return state.New(self.CurrentBlock().Root(), self.chainDb)
|
||||
}
|
||||
|
||||
func (bc *ChainManager) recover() bool {
|
||||
data, _ := bc.chainDb.Get([]byte("checkpoint"))
|
||||
if len(data) != 0 {
|
||||
block := bc.GetBlock(common.BytesToHash(data))
|
||||
if block != nil {
|
||||
err := bc.chainDb.Put([]byte("LastBlock"), block.Hash().Bytes())
|
||||
if err != nil {
|
||||
glog.Fatalln("db write err:", err)
|
||||
}
|
||||
|
||||
bc.currentBlock = block
|
||||
bc.lastBlockHash = block.Hash()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (bc *ChainManager) setLastState() error {
|
||||
data, _ := bc.chainDb.Get([]byte("LastBlock"))
|
||||
if len(data) != 0 {
|
||||
block := bc.GetBlock(common.BytesToHash(data))
|
||||
if block != nil {
|
||||
bc.currentBlock = block
|
||||
bc.lastBlockHash = block.Hash()
|
||||
} else {
|
||||
glog.Infof("LastBlock (%x) not found. Recovering...\n", data)
|
||||
if bc.recover() {
|
||||
glog.Infof("Recover successful")
|
||||
} else {
|
||||
glog.Fatalf("Recover failed. Please report")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bc.Reset()
|
||||
}
|
||||
bc.td = bc.currentBlock.Td
|
||||
bc.currentGasLimit = CalcGasLimit(bc.currentBlock)
|
||||
|
||||
if glog.V(logger.Info) {
|
||||
glog.Infof("Last block (#%v) %x TD=%v\n", bc.currentBlock.Number(), bc.currentBlock.Hash(), bc.td)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bc *ChainManager) makeCache() {
|
||||
bc.cache, _ = lru.New(blockCacheLimit)
|
||||
// load in last `blockCacheLimit` - 1 blocks. Last block is the current.
|
||||
bc.cache.Add(bc.genesisBlock.Hash(), bc.genesisBlock)
|
||||
for _, block := range bc.GetBlocksFromHash(bc.currentBlock.Hash(), blockCacheLimit) {
|
||||
bc.cache.Add(block.Hash(), block)
|
||||
}
|
||||
}
|
||||
|
||||
func (bc *ChainManager) Reset() {
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.ParentHash()) {
|
||||
bc.removeBlock(block)
|
||||
}
|
||||
|
||||
bc.cache, _ = lru.New(blockCacheLimit)
|
||||
|
||||
// Prepare the genesis block
|
||||
err := WriteBlock(bc.chainDb, bc.genesisBlock)
|
||||
if err != nil {
|
||||
glog.Fatalln("db err:", err)
|
||||
}
|
||||
|
||||
bc.insert(bc.genesisBlock)
|
||||
bc.currentBlock = bc.genesisBlock
|
||||
bc.makeCache()
|
||||
|
||||
bc.setTotalDifficulty(common.Big("0"))
|
||||
}
|
||||
|
||||
func (bc *ChainManager) removeBlock(block *types.Block) {
|
||||
if bc.sqlDB != nil {
|
||||
bc.sqlDB.DeleteBlock(block)
|
||||
}
|
||||
|
||||
bc.chainDb.Delete(append(blockHashPre, block.Hash().Bytes()...))
|
||||
}
|
||||
|
||||
func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) {
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.ParentHash()) {
|
||||
bc.removeBlock(block)
|
||||
}
|
||||
|
||||
// Prepare the genesis block
|
||||
gb.Td = gb.Difficulty()
|
||||
bc.genesisBlock = gb
|
||||
|
||||
err := WriteBlock(bc.chainDb, bc.genesisBlock)
|
||||
if err != nil {
|
||||
glog.Fatalln("db err:", err)
|
||||
}
|
||||
|
||||
bc.insert(bc.genesisBlock)
|
||||
bc.currentBlock = bc.genesisBlock
|
||||
bc.makeCache()
|
||||
bc.td = gb.Difficulty()
|
||||
}
|
||||
|
||||
// Export writes the active chain to the given writer.
|
||||
func (self *ChainManager) Export(w io.Writer) error {
|
||||
if err := self.ExportN(w, uint64(0), self.currentBlock.NumberU64()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExportN writes a subset of the active chain to the given writer.
|
||||
func (self *ChainManager) ExportN(w io.Writer, first uint64, last uint64) error {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
if first > last {
|
||||
return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
|
||||
}
|
||||
|
||||
glog.V(logger.Info).Infof("exporting %d blocks...\n", last-first+1)
|
||||
|
||||
for nr := first; nr <= last; nr++ {
|
||||
block := self.GetBlockByNumber(nr)
|
||||
if block == nil {
|
||||
return fmt.Errorf("export failed on #%d: not found", nr)
|
||||
}
|
||||
|
||||
if err := block.EncodeRLP(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// insert injects a block into the current chain block chain. Note, this function
|
||||
// assumes that the `mu` mutex is held!
|
||||
func (bc *ChainManager) insert(block *types.Block) {
|
||||
err := WriteHead(bc.chainDb, block)
|
||||
if err != nil {
|
||||
glog.Fatal("db write fail:", err)
|
||||
}
|
||||
|
||||
bc.checkpoint++
|
||||
if bc.checkpoint > checkpointLimit {
|
||||
err = bc.chainDb.Put([]byte("checkpoint"), block.Hash().Bytes())
|
||||
if err != nil {
|
||||
glog.Fatal("db write fail:", err)
|
||||
}
|
||||
|
||||
bc.checkpoint = 0
|
||||
}
|
||||
|
||||
bc.currentBlock = block
|
||||
bc.lastBlockHash = block.Hash()
|
||||
|
||||
if bc.sqlDB != nil {
|
||||
bc.sqlDB.InsertBlock(block)
|
||||
}
|
||||
}
|
||||
|
||||
// Accessors
|
||||
func (bc *ChainManager) Genesis() *types.Block {
|
||||
return bc.genesisBlock
|
||||
}
|
||||
|
||||
// Block fetching methods
|
||||
func (bc *ChainManager) HasBlock(hash common.Hash) bool {
|
||||
if bc.cache.Contains(hash) {
|
||||
return true
|
||||
}
|
||||
|
||||
data, _ := bc.chainDb.Get(append(blockHashPre, hash[:]...))
|
||||
return len(data) != 0
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) (chain []common.Hash) {
|
||||
block := self.GetBlock(hash)
|
||||
if block == nil {
|
||||
return
|
||||
}
|
||||
// XXX Could be optimised by using a different database which only holds hashes (i.e., linked list)
|
||||
for i := uint64(0); i < max; i++ {
|
||||
block = self.GetBlock(block.ParentHash())
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
|
||||
chain = append(chain, block.Hash())
|
||||
if block.Number().Cmp(common.Big0) <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetBlock(hash common.Hash) *types.Block {
|
||||
if block, ok := self.cache.Get(hash); ok {
|
||||
return block.(*types.Block)
|
||||
}
|
||||
|
||||
block := GetBlockByHash(self.chainDb, hash)
|
||||
if block == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add the block to the cache
|
||||
self.cache.Add(hash, (*types.Block)(block))
|
||||
|
||||
return (*types.Block)(block)
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.getBlockByNumber(num)
|
||||
|
||||
}
|
||||
|
||||
// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
|
||||
func (self *ChainManager) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {
|
||||
for i := 0; i < n; i++ {
|
||||
block := self.GetBlock(hash)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
blocks = append(blocks, block)
|
||||
hash = block.ParentHash()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// non blocking version
|
||||
func (self *ChainManager) getBlockByNumber(num uint64) *types.Block {
|
||||
return GetBlockByNumber(self.chainDb, num)
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncles []*types.Header) {
|
||||
for i := 0; block != nil && i < length; i++ {
|
||||
uncles = append(uncles, block.Uncles()...)
|
||||
block = self.GetBlock(block.ParentHash())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// setTotalDifficulty updates the TD of the chain manager. Note, this function
|
||||
// assumes that the `mu` mutex is held!
|
||||
func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
|
||||
bc.td = new(big.Int).Set(td)
|
||||
}
|
||||
|
||||
func (bc *ChainManager) Stop() {
|
||||
if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) {
|
||||
return
|
||||
}
|
||||
close(bc.quit)
|
||||
atomic.StoreInt32(&bc.procInterrupt, 1)
|
||||
|
||||
bc.wg.Wait()
|
||||
|
||||
glog.V(logger.Info).Infoln("Chain manager stopped")
|
||||
}
|
||||
|
||||
type queueEvent struct {
|
||||
queue []interface{}
|
||||
canonicalCount int
|
||||
sideCount int
|
||||
splitCount int
|
||||
}
|
||||
|
||||
func (self *ChainManager) procFutureBlocks() {
|
||||
blocks := make([]*types.Block, self.futureBlocks.Len())
|
||||
for i, hash := range self.futureBlocks.Keys() {
|
||||
block, _ := self.futureBlocks.Get(hash)
|
||||
blocks[i] = block.(*types.Block)
|
||||
}
|
||||
if len(blocks) > 0 {
|
||||
types.BlockBy(types.Number).Sort(blocks)
|
||||
self.InsertChain(blocks)
|
||||
}
|
||||
}
|
||||
|
||||
type writeStatus byte
|
||||
|
||||
const (
|
||||
NonStatTy writeStatus = iota
|
||||
CanonStatTy
|
||||
SplitStatTy
|
||||
SideStatTy
|
||||
)
|
||||
|
||||
// WriteBlock writes the block to the chain (or pending queue)
|
||||
func (self *ChainManager) WriteBlock(block *types.Block, queued bool) (status writeStatus, err error) {
|
||||
self.wg.Add(1)
|
||||
defer self.wg.Done()
|
||||
|
||||
cblock := self.currentBlock
|
||||
// Compare the TD of the last known block in the canonical chain to make sure it's greater.
|
||||
// At this point it's possible that a different chain (fork) becomes the new canonical chain.
|
||||
if block.Td.Cmp(self.Td()) > 0 {
|
||||
// chain fork
|
||||
if block.ParentHash() != cblock.Hash() {
|
||||
// during split we merge two different chains and create the new canonical chain
|
||||
err := self.merge(cblock, block)
|
||||
if err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
|
||||
status = SplitStatTy
|
||||
}
|
||||
|
||||
self.mu.Lock()
|
||||
self.setTotalDifficulty(block.Td)
|
||||
self.insert(block)
|
||||
self.mu.Unlock()
|
||||
|
||||
status = CanonStatTy
|
||||
} else {
|
||||
status = SideStatTy
|
||||
}
|
||||
|
||||
err = WriteBlock(self.chainDb, block)
|
||||
if err != nil {
|
||||
glog.Fatalln("db err:", err)
|
||||
}
|
||||
// Delete from future blocks
|
||||
self.futureBlocks.Remove(block.Hash())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 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 *ChainManager) InsertChain(chain types.Blocks) (int, error) {
|
||||
self.wg.Add(1)
|
||||
defer self.wg.Done()
|
||||
|
||||
self.chainmu.Lock()
|
||||
defer self.chainmu.Unlock()
|
||||
|
||||
// A queued approach to delivering events. This is generally
|
||||
// faster than direct delivery and requires much less mutex
|
||||
// acquiring.
|
||||
var (
|
||||
queue = make([]interface{}, len(chain))
|
||||
queueEvent = queueEvent{queue: queue}
|
||||
stats struct{ queued, processed, ignored int }
|
||||
tstart = time.Now()
|
||||
|
||||
nonceDone = make(chan nonceResult, len(chain))
|
||||
nonceQuit = make(chan struct{})
|
||||
nonceChecked = make([]bool, len(chain))
|
||||
)
|
||||
|
||||
// Start the parallel nonce verifier.
|
||||
go verifyNonces(self.pow, chain, nonceQuit, nonceDone)
|
||||
defer close(nonceQuit)
|
||||
|
||||
txcount := 0
|
||||
for i, block := range chain {
|
||||
if atomic.LoadInt32(&self.procInterrupt) == 1 {
|
||||
glog.V(logger.Debug).Infoln("Premature abort during chain processing")
|
||||
break
|
||||
}
|
||||
|
||||
bstart := time.Now()
|
||||
// Wait for block i's nonce to be verified before processing
|
||||
// its state transition.
|
||||
for !nonceChecked[i] {
|
||||
r := <-nonceDone
|
||||
nonceChecked[r.i] = true
|
||||
if !r.valid {
|
||||
block := chain[r.i]
|
||||
return r.i, &BlockNonceErr{Hash: block.Hash(), Number: block.Number(), Nonce: block.Nonce()}
|
||||
}
|
||||
}
|
||||
|
||||
if BadHashes[block.Hash()] {
|
||||
err := fmt.Errorf("Found known bad hash in chain %x", block.Hash())
|
||||
blockErr(block, err)
|
||||
return i, err
|
||||
}
|
||||
|
||||
// Setting block.Td regardless of error (known for example) prevents errors down the line
|
||||
// in the protocol handler
|
||||
block.Td = new(big.Int).Set(CalcTD(block, self.GetBlock(block.ParentHash())))
|
||||
|
||||
// Call in to the block processor and check for errors. It's likely that if one block fails
|
||||
// all others will fail too (unless a known block is returned).
|
||||
logs, receipts, err := self.processor.Process(block)
|
||||
if err != nil {
|
||||
if IsKnownBlockErr(err) {
|
||||
stats.ignored++
|
||||
continue
|
||||
}
|
||||
|
||||
if err == BlockFutureErr {
|
||||
// Allow up to MaxFuture second in the future blocks. If this limit
|
||||
// is exceeded the chain is discarded and processed at a later time
|
||||
// if given.
|
||||
max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks)
|
||||
if block.Time().Cmp(max) == 1 {
|
||||
return i, fmt.Errorf("%v: BlockFutureErr, %v > %v", BlockFutureErr, block.Time(), max)
|
||||
}
|
||||
|
||||
self.futureBlocks.Add(block.Hash(), block)
|
||||
stats.queued++
|
||||
continue
|
||||
}
|
||||
|
||||
if IsParentErr(err) && self.futureBlocks.Contains(block.ParentHash()) {
|
||||
self.futureBlocks.Add(block.Hash(), block)
|
||||
stats.queued++
|
||||
continue
|
||||
}
|
||||
|
||||
blockErr(block, err)
|
||||
|
||||
go ReportBlock(block, err)
|
||||
|
||||
return i, err
|
||||
}
|
||||
|
||||
txcount += len(block.Transactions())
|
||||
|
||||
// write the block to the chain and get the status
|
||||
status, err := self.WriteBlock(block, true)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
switch status {
|
||||
case CanonStatTy:
|
||||
if glog.V(logger.Debug) {
|
||||
glog.Infof("[%v] inserted block #%d (%d TXs %v G %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), block.GasUsed(), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
|
||||
}
|
||||
queue[i] = ChainEvent{block, block.Hash(), logs}
|
||||
queueEvent.canonicalCount++
|
||||
|
||||
// This puts transactions in a extra db for rpc
|
||||
PutTransactions(self.chainDb, block, block.Transactions())
|
||||
// store the receipts
|
||||
PutReceipts(self.chainDb, receipts)
|
||||
case SideStatTy:
|
||||
if glog.V(logger.Detail) {
|
||||
glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...). Took %v\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
|
||||
}
|
||||
queue[i] = ChainSideEvent{block, logs}
|
||||
queueEvent.sideCount++
|
||||
case SplitStatTy:
|
||||
queue[i] = ChainSplitEvent{block, logs}
|
||||
queueEvent.splitCount++
|
||||
}
|
||||
if err := PutBlockReceipts(self.chainDb, block, receipts); err != nil {
|
||||
glog.V(logger.Warn).Infoln("error writing block receipts:", err)
|
||||
}
|
||||
|
||||
stats.processed++
|
||||
}
|
||||
|
||||
if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) {
|
||||
tend := time.Since(tstart)
|
||||
start, end := chain[0], chain[len(chain)-1]
|
||||
glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", stats.processed, stats.queued, stats.ignored, txcount, tend, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
|
||||
}
|
||||
|
||||
go self.eventMux.Post(queueEvent)
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
|
||||
// to be part of the new canonical chain and accumulates potential missing transactions and post an
|
||||
// event about them
|
||||
func (self *ChainManager) reorg(oldBlock, newBlock *types.Block) error {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
|
||||
var (
|
||||
newChain types.Blocks
|
||||
commonBlock *types.Block
|
||||
oldStart = oldBlock
|
||||
newStart = newBlock
|
||||
deletedTxs types.Transactions
|
||||
addedTxs types.Transactions
|
||||
)
|
||||
|
||||
// first reduce whoever is higher bound
|
||||
if oldBlock.NumberU64() > newBlock.NumberU64() {
|
||||
// reduce old chain
|
||||
for oldBlock = oldBlock; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = self.GetBlock(oldBlock.ParentHash()) {
|
||||
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
|
||||
}
|
||||
} else {
|
||||
// reduce new chain and append new chain blocks for inserting later on
|
||||
for newBlock = newBlock; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = self.GetBlock(newBlock.ParentHash()) {
|
||||
newChain = append(newChain, newBlock)
|
||||
}
|
||||
}
|
||||
if oldBlock == nil {
|
||||
return fmt.Errorf("Invalid old chain")
|
||||
}
|
||||
if newBlock == nil {
|
||||
return fmt.Errorf("Invalid new chain")
|
||||
}
|
||||
|
||||
numSplit := newBlock.Number()
|
||||
for {
|
||||
if oldBlock.Hash() == newBlock.Hash() {
|
||||
commonBlock = oldBlock
|
||||
break
|
||||
}
|
||||
newChain = append(newChain, newBlock)
|
||||
|
||||
oldBlock, newBlock = self.GetBlock(oldBlock.ParentHash()), self.GetBlock(newBlock.ParentHash())
|
||||
if oldBlock == nil {
|
||||
return fmt.Errorf("Invalid old chain")
|
||||
}
|
||||
if newBlock == nil {
|
||||
return fmt.Errorf("Invalid new chain")
|
||||
}
|
||||
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
|
||||
}
|
||||
|
||||
if glog.V(logger.Debug) {
|
||||
commonHash := commonBlock.Hash()
|
||||
glog.Infof("Chain split detected @ %x. Reorganising chain from #%v %x to %x", commonHash[:4], numSplit, oldStart.Hash().Bytes()[:4], newStart.Hash().Bytes()[:4])
|
||||
}
|
||||
|
||||
// insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly
|
||||
for _, block := range newChain {
|
||||
// insert the block in the canonical way, re-writing history
|
||||
self.insert(block)
|
||||
// write canonical receipts and transactions
|
||||
PutTransactions(self.chainDb, block, block.Transactions())
|
||||
PutReceipts(self.chainDb, GetBlockReceipts(self.chainDb, block.Hash()))
|
||||
|
||||
addedTxs = append(addedTxs, block.Transactions()...)
|
||||
}
|
||||
|
||||
var diff types.Transactions
|
||||
diff.Difference(deletedTxs, addedTxs)
|
||||
self.eventMux.Post(RemovedTransactionEvent{diff})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// diff takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
|
||||
// to be part of the new canonical chain.
|
||||
func (self *ChainManager) diff(oldBlock, newBlock *types.Block) (types.Blocks, error) {
|
||||
var (
|
||||
newChain types.Blocks
|
||||
commonBlock *types.Block
|
||||
oldStart = oldBlock
|
||||
newStart = newBlock
|
||||
)
|
||||
|
||||
// first reduce whoever is higher bound
|
||||
if oldBlock.NumberU64() > newBlock.NumberU64() {
|
||||
// reduce old chain
|
||||
for oldBlock = oldBlock; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = self.GetBlock(oldBlock.ParentHash()) {
|
||||
}
|
||||
} else {
|
||||
// reduce new chain and append new chain blocks for inserting later on
|
||||
for newBlock = newBlock; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = self.GetBlock(newBlock.ParentHash()) {
|
||||
newChain = append(newChain, newBlock)
|
||||
}
|
||||
}
|
||||
if oldBlock == nil {
|
||||
return nil, fmt.Errorf("Invalid old chain")
|
||||
}
|
||||
if newBlock == nil {
|
||||
return nil, fmt.Errorf("Invalid new chain")
|
||||
}
|
||||
|
||||
numSplit := newBlock.Number()
|
||||
for {
|
||||
if oldBlock.Hash() == newBlock.Hash() {
|
||||
commonBlock = oldBlock
|
||||
break
|
||||
}
|
||||
newChain = append(newChain, newBlock)
|
||||
|
||||
oldBlock, newBlock = self.GetBlock(oldBlock.ParentHash()), self.GetBlock(newBlock.ParentHash())
|
||||
if oldBlock == nil {
|
||||
return nil, fmt.Errorf("Invalid old chain")
|
||||
}
|
||||
if newBlock == nil {
|
||||
return nil, fmt.Errorf("Invalid new chain")
|
||||
}
|
||||
}
|
||||
|
||||
if glog.V(logger.Debug) {
|
||||
commonHash := commonBlock.Hash()
|
||||
glog.Infof("Chain split detected @ %x. Reorganising chain from #%v %x to %x", commonHash[:4], numSplit, oldStart.Hash().Bytes()[:4], newStart.Hash().Bytes()[:4])
|
||||
}
|
||||
|
||||
return newChain, nil
|
||||
}
|
||||
|
||||
// merge merges two different chain to the new canonical chain
|
||||
func (self *ChainManager) merge(oldBlock, newBlock *types.Block) error {
|
||||
newChain, err := self.diff(oldBlock, newBlock)
|
||||
if err != nil {
|
||||
return fmt.Errorf("chain reorg failed: %v", err)
|
||||
}
|
||||
|
||||
// insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly
|
||||
self.mu.Lock()
|
||||
for _, block := range newChain {
|
||||
// insert the block in the canonical way, re-writing history
|
||||
self.insert(block)
|
||||
// write canonical receipts and transactions
|
||||
PutTransactions(self.chainDb, block, block.Transactions())
|
||||
PutReceipts(self.chainDb, GetBlockReceipts(self.chainDb, block.Hash()))
|
||||
|
||||
}
|
||||
self.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ChainManager) update() {
|
||||
events := self.eventMux.Subscribe(queueEvent{})
|
||||
futureTimer := time.Tick(5 * time.Second)
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case ev := <-events.Chan():
|
||||
switch ev := ev.(type) {
|
||||
case queueEvent:
|
||||
for _, event := range ev.queue {
|
||||
switch event := event.(type) {
|
||||
case ChainEvent:
|
||||
// We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long
|
||||
// and in most cases isn't even necessary.
|
||||
if self.lastBlockHash == event.Hash {
|
||||
self.currentGasLimit = CalcGasLimit(event.Block)
|
||||
self.eventMux.Post(ChainHeadEvent{event.Block})
|
||||
}
|
||||
}
|
||||
|
||||
self.eventMux.Post(event)
|
||||
}
|
||||
}
|
||||
case <-futureTimer:
|
||||
self.procFutureBlocks()
|
||||
case <-self.quit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func blockErr(block *types.Block, err error) {
|
||||
h := block.Header()
|
||||
glog.V(logger.Error).Infof("Bad block #%v (%x)\n", h.Number, h.Hash().Bytes())
|
||||
glog.V(logger.Error).Infoln(err)
|
||||
glog.V(logger.Debug).Infoln(verifyNonces)
|
||||
}
|
||||
|
||||
type nonceResult struct {
|
||||
i int
|
||||
valid bool
|
||||
}
|
||||
|
||||
// block verifies nonces of the given blocks in parallel and returns
|
||||
// an error if one of the blocks nonce verifications failed.
|
||||
func verifyNonces(pow pow.PoW, blocks []*types.Block, quit <-chan struct{}, done chan<- nonceResult) {
|
||||
// Spawn a few workers. They listen for blocks on the in channel
|
||||
// and send results on done. The workers will exit in the
|
||||
// background when in is closed.
|
||||
var (
|
||||
in = make(chan int)
|
||||
nworkers = runtime.GOMAXPROCS(0)
|
||||
)
|
||||
defer close(in)
|
||||
if len(blocks) < nworkers {
|
||||
nworkers = len(blocks)
|
||||
}
|
||||
for i := 0; i < nworkers; i++ {
|
||||
go func() {
|
||||
for i := range in {
|
||||
done <- nonceResult{i: i, valid: pow.Verify(blocks[i])}
|
||||
}
|
||||
}()
|
||||
}
|
||||
// Feed block indices to the workers.
|
||||
for i := range blocks {
|
||||
select {
|
||||
case in <- i:
|
||||
continue
|
||||
case <-quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
517
core/chain_manager_test.go
Normal file
517
core/chain_manager_test.go
Normal file
|
|
@ -0,0 +1,517 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/core/state"
|
||||
"github.com/chattynet/chatty/core/types"
|
||||
"github.com/chattynet/chatty/ethdb"
|
||||
"github.com/chattynet/chatty/event"
|
||||
"github.com/chattynet/chatty/pow"
|
||||
"github.com/chattynet/chatty/rlp"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
}
|
||||
|
||||
func thePow() pow.PoW {
|
||||
pow, _ := ethash.NewForTesting()
|
||||
return pow
|
||||
}
|
||||
|
||||
func theChainManager(db common.Database, t *testing.T) *ChainManager {
|
||||
var eventMux event.TypeMux
|
||||
WriteTestNetGenesisBlock(db, 0)
|
||||
chainMan, err := NewChainManager(db, thePow(), &eventMux)
|
||||
if err != nil {
|
||||
t.Error("failed creating chainmanager:", err)
|
||||
t.FailNow()
|
||||
return nil
|
||||
}
|
||||
blockMan := NewBlockProcessor(db, nil, chainMan, &eventMux)
|
||||
chainMan.SetProcessor(blockMan)
|
||||
|
||||
return chainMan
|
||||
}
|
||||
|
||||
// Test fork of length N starting from block i
|
||||
func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big.Int)) {
|
||||
// switch databases to process the new chain
|
||||
db, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create db:", err)
|
||||
}
|
||||
// copy old chain up to i into new db with deterministic canonical
|
||||
bman2, err := newCanonical(i, db)
|
||||
if err != nil {
|
||||
t.Fatal("could not make new canonical in testFork", err)
|
||||
}
|
||||
// asert the bmans have the same block at i
|
||||
bi1 := bman.bc.GetBlockByNumber(uint64(i)).Hash()
|
||||
bi2 := bman2.bc.GetBlockByNumber(uint64(i)).Hash()
|
||||
if bi1 != bi2 {
|
||||
t.Fatal("chains do not have the same hash at height", i)
|
||||
}
|
||||
bman2.bc.SetProcessor(bman2)
|
||||
|
||||
// extend the fork
|
||||
parent := bman2.bc.CurrentBlock()
|
||||
chainB := makeChain(parent, N, db, forkSeed)
|
||||
_, err = bman2.bc.InsertChain(chainB)
|
||||
if err != nil {
|
||||
t.Fatal("Insert chain error for fork:", err)
|
||||
}
|
||||
|
||||
tdpre := bman.bc.Td()
|
||||
// Test the fork's blocks on the original chain
|
||||
td, err := testChain(chainB, bman)
|
||||
if err != nil {
|
||||
t.Fatal("expected chainB not to give errors:", err)
|
||||
}
|
||||
// Compare difficulties
|
||||
f(tdpre, td)
|
||||
|
||||
// Loop over parents making sure reconstruction is done properly
|
||||
}
|
||||
|
||||
func printChain(bc *ChainManager) {
|
||||
for i := bc.CurrentBlock().Number().Uint64(); i > 0; i-- {
|
||||
b := bc.GetBlockByNumber(uint64(i))
|
||||
fmt.Printf("\t%x %v\n", b.Hash(), b.Difficulty())
|
||||
}
|
||||
}
|
||||
|
||||
// process blocks against a chain
|
||||
func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) {
|
||||
td := new(big.Int)
|
||||
for _, block := range chainB {
|
||||
_, _, err := bman.bc.processor.Process(block)
|
||||
if err != nil {
|
||||
if IsKnownBlockErr(err) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
parent := bman.bc.GetBlock(block.ParentHash())
|
||||
block.Td = CalcTD(block, parent)
|
||||
td = block.Td
|
||||
|
||||
bman.bc.mu.Lock()
|
||||
{
|
||||
WriteBlock(bman.bc.chainDb, block)
|
||||
}
|
||||
bman.bc.mu.Unlock()
|
||||
}
|
||||
return td, nil
|
||||
}
|
||||
|
||||
func loadChain(fn string, t *testing.T) (types.Blocks, error) {
|
||||
fh, err := os.OpenFile(filepath.Join("..", "_data", fn), os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
var chain types.Blocks
|
||||
if err := rlp.Decode(fh, &chain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *testing.T) {
|
||||
_, err := chainMan.InsertChain(chain)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
done <- true
|
||||
}
|
||||
|
||||
func TestExtendCanonical(t *testing.T) {
|
||||
CanonicalLength := 5
|
||||
db, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create db:", err)
|
||||
}
|
||||
// make first chain starting from genesis
|
||||
bman, err := newCanonical(CanonicalLength, db)
|
||||
if err != nil {
|
||||
t.Fatal("Could not make new canonical chain:", err)
|
||||
}
|
||||
f := func(td1, td2 *big.Int) {
|
||||
if td2.Cmp(td1) <= 0 {
|
||||
t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1)
|
||||
}
|
||||
}
|
||||
// Start fork from current height (CanonicalLength)
|
||||
testFork(t, bman, CanonicalLength, 1, f)
|
||||
testFork(t, bman, CanonicalLength, 2, f)
|
||||
testFork(t, bman, CanonicalLength, 5, f)
|
||||
testFork(t, bman, CanonicalLength, 10, f)
|
||||
}
|
||||
|
||||
func TestShorterFork(t *testing.T) {
|
||||
db, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create db:", err)
|
||||
}
|
||||
// make first chain starting from genesis
|
||||
bman, err := newCanonical(10, db)
|
||||
if err != nil {
|
||||
t.Fatal("Could not make new canonical chain:", err)
|
||||
}
|
||||
f := func(td1, td2 *big.Int) {
|
||||
if td2.Cmp(td1) >= 0 {
|
||||
t.Error("expected chainB to have lower difficulty. Got", td2, "expected less than", td1)
|
||||
}
|
||||
}
|
||||
// Sum of numbers must be less than 10
|
||||
// for this to be a shorter fork
|
||||
testFork(t, bman, 0, 3, f)
|
||||
testFork(t, bman, 0, 7, f)
|
||||
testFork(t, bman, 1, 1, f)
|
||||
testFork(t, bman, 1, 7, f)
|
||||
testFork(t, bman, 5, 3, f)
|
||||
testFork(t, bman, 5, 4, f)
|
||||
}
|
||||
|
||||
func TestLongerFork(t *testing.T) {
|
||||
db, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create db:", err)
|
||||
}
|
||||
// make first chain starting from genesis
|
||||
bman, err := newCanonical(10, db)
|
||||
if err != nil {
|
||||
t.Fatal("Could not make new canonical chain:", err)
|
||||
}
|
||||
f := func(td1, td2 *big.Int) {
|
||||
if td2.Cmp(td1) <= 0 {
|
||||
t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1)
|
||||
}
|
||||
}
|
||||
// Sum of numbers must be greater than 10
|
||||
// for this to be a longer fork
|
||||
testFork(t, bman, 0, 11, f)
|
||||
testFork(t, bman, 0, 15, f)
|
||||
testFork(t, bman, 1, 10, f)
|
||||
testFork(t, bman, 1, 12, f)
|
||||
testFork(t, bman, 5, 6, f)
|
||||
testFork(t, bman, 5, 8, f)
|
||||
}
|
||||
|
||||
func TestEqualFork(t *testing.T) {
|
||||
db, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create db:", err)
|
||||
}
|
||||
bman, err := newCanonical(10, db)
|
||||
if err != nil {
|
||||
t.Fatal("Could not make new canonical chain:", err)
|
||||
}
|
||||
f := func(td1, td2 *big.Int) {
|
||||
if td2.Cmp(td1) != 0 {
|
||||
t.Error("expected chainB to have equal difficulty. Got", td2, "expected ", td1)
|
||||
}
|
||||
}
|
||||
// Sum of numbers must be equal to 10
|
||||
// for this to be an equal fork
|
||||
testFork(t, bman, 0, 10, f)
|
||||
testFork(t, bman, 1, 9, f)
|
||||
testFork(t, bman, 2, 8, f)
|
||||
testFork(t, bman, 5, 5, f)
|
||||
testFork(t, bman, 6, 4, f)
|
||||
testFork(t, bman, 9, 1, f)
|
||||
}
|
||||
|
||||
func TestBrokenChain(t *testing.T) {
|
||||
db, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create db:", err)
|
||||
}
|
||||
bman, err := newCanonical(10, db)
|
||||
if err != nil {
|
||||
t.Fatal("Could not make new canonical chain:", err)
|
||||
}
|
||||
db2, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create db:", err)
|
||||
}
|
||||
bman2, err := newCanonical(10, db2)
|
||||
if err != nil {
|
||||
t.Fatal("Could not make new canonical chain:", err)
|
||||
}
|
||||
bman2.bc.SetProcessor(bman2)
|
||||
parent := bman2.bc.CurrentBlock()
|
||||
chainB := makeChain(parent, 5, db2, forkSeed)
|
||||
chainB = chainB[1:]
|
||||
_, err = testChain(chainB, bman)
|
||||
if err == nil {
|
||||
t.Error("expected broken chain to return error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainInsertions(t *testing.T) {
|
||||
t.Skip("Skipped: outdated test files")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
chain1, err := loadChain("valid1", t)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
chain2, err := loadChain("valid2", t)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
chainMan := theChainManager(db, t)
|
||||
|
||||
const max = 2
|
||||
done := make(chan bool, max)
|
||||
|
||||
go insertChain(done, chainMan, chain1, t)
|
||||
go insertChain(done, chainMan, chain2, t)
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
if chain2[len(chain2)-1].Hash() != chainMan.CurrentBlock().Hash() {
|
||||
t.Error("chain2 is canonical and shouldn't be")
|
||||
}
|
||||
|
||||
if chain1[len(chain1)-1].Hash() != chainMan.CurrentBlock().Hash() {
|
||||
t.Error("chain1 isn't canonical and should be")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainMultipleInsertions(t *testing.T) {
|
||||
t.Skip("Skipped: outdated test files")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
const max = 4
|
||||
chains := make([]types.Blocks, max)
|
||||
var longest int
|
||||
for i := 0; i < max; i++ {
|
||||
var err error
|
||||
name := "valid" + strconv.Itoa(i+1)
|
||||
chains[i], err = loadChain(name, t)
|
||||
if len(chains[i]) >= len(chains[longest]) {
|
||||
longest = i
|
||||
}
|
||||
fmt.Println("loaded", name, "with a length of", len(chains[i]))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
chainMan := theChainManager(db, t)
|
||||
|
||||
done := make(chan bool, max)
|
||||
for i, chain := range chains {
|
||||
// XXX the go routine would otherwise reference the same (chain[3]) variable and fail
|
||||
i := i
|
||||
chain := chain
|
||||
go func() {
|
||||
insertChain(done, chainMan, chain, t)
|
||||
fmt.Println(i, "done")
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
if chains[longest][len(chains[longest])-1].Hash() != chainMan.CurrentBlock().Hash() {
|
||||
t.Error("Invalid canonical chain")
|
||||
}
|
||||
}
|
||||
|
||||
type bproc struct{}
|
||||
|
||||
func (bproc) Process(*types.Block) (state.Logs, types.Receipts, error) { return nil, nil, nil }
|
||||
|
||||
func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block {
|
||||
var chain []*types.Block
|
||||
for i, difficulty := range d {
|
||||
header := &types.Header{
|
||||
Coinbase: common.Address{seed},
|
||||
Number: big.NewInt(int64(i + 1)),
|
||||
Difficulty: big.NewInt(int64(difficulty)),
|
||||
}
|
||||
if i == 0 {
|
||||
header.ParentHash = genesis.Hash()
|
||||
} else {
|
||||
header.ParentHash = chain[i-1].Hash()
|
||||
}
|
||||
block := types.NewBlockWithHeader(header)
|
||||
chain = append(chain, block)
|
||||
}
|
||||
return chain
|
||||
}
|
||||
|
||||
func chm(genesis *types.Block, db common.Database) *ChainManager {
|
||||
var eventMux event.TypeMux
|
||||
bc := &ChainManager{chainDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}}
|
||||
bc.cache, _ = lru.New(100)
|
||||
bc.futureBlocks, _ = lru.New(100)
|
||||
bc.processor = bproc{}
|
||||
bc.ResetWithGenesisBlock(genesis)
|
||||
|
||||
return bc
|
||||
}
|
||||
|
||||
func TestReorgLongest(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
genesis, err := WriteTestNetGenesisBlock(db, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
bc := chm(genesis, db)
|
||||
|
||||
chain1 := makeChainWithDiff(genesis, []int{1, 2, 4}, 10)
|
||||
chain2 := makeChainWithDiff(genesis, []int{1, 2, 3, 4}, 11)
|
||||
|
||||
bc.InsertChain(chain1)
|
||||
bc.InsertChain(chain2)
|
||||
|
||||
prev := bc.CurrentBlock()
|
||||
for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) {
|
||||
if prev.ParentHash() != block.Hash() {
|
||||
t.Errorf("parent hash mismatch %x - %x", prev.ParentHash(), block.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReorgShortest(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis, err := WriteTestNetGenesisBlock(db, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
bc := chm(genesis, db)
|
||||
|
||||
chain1 := makeChainWithDiff(genesis, []int{1, 2, 3, 4}, 10)
|
||||
chain2 := makeChainWithDiff(genesis, []int{1, 10}, 11)
|
||||
|
||||
bc.InsertChain(chain1)
|
||||
bc.InsertChain(chain2)
|
||||
|
||||
prev := bc.CurrentBlock()
|
||||
for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) {
|
||||
if prev.ParentHash() != block.Hash() {
|
||||
t.Errorf("parent hash mismatch %x - %x", prev.ParentHash(), block.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsertNonceError(t *testing.T) {
|
||||
for i := 1; i < 25 && !t.Failed(); i++ {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis, err := WriteTestNetGenesisBlock(db, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
bc := chm(genesis, db)
|
||||
bc.processor = NewBlockProcessor(db, bc.pow, bc, bc.eventMux)
|
||||
blocks := makeChain(bc.currentBlock, i, db, 0)
|
||||
|
||||
fail := rand.Int() % len(blocks)
|
||||
failblock := blocks[fail]
|
||||
bc.pow = failpow{failblock.NumberU64()}
|
||||
n, err := bc.InsertChain(blocks)
|
||||
|
||||
// Check that the returned error indicates the nonce failure.
|
||||
if n != fail {
|
||||
t.Errorf("(i=%d) wrong failed block index: got %d, want %d", i, n, fail)
|
||||
}
|
||||
if !IsBlockNonceErr(err) {
|
||||
t.Fatalf("(i=%d) got %q, want a nonce error", i, err)
|
||||
}
|
||||
nerr := err.(*BlockNonceErr)
|
||||
if nerr.Number.Cmp(failblock.Number()) != 0 {
|
||||
t.Errorf("(i=%d) wrong block number in error, got %v, want %v", i, nerr.Number, failblock.Number())
|
||||
}
|
||||
if nerr.Hash != failblock.Hash() {
|
||||
t.Errorf("(i=%d) wrong block hash in error, got %v, want %v", i, nerr.Hash, failblock.Hash())
|
||||
}
|
||||
|
||||
// Check that all no blocks after the failing block have been inserted.
|
||||
for _, block := range blocks[fail:] {
|
||||
if bc.HasBlock(block.Hash()) {
|
||||
t.Errorf("(i=%d) invalid block %d present in chain", i, block.NumberU64())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestGenesisMismatch(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
var mux event.TypeMux
|
||||
genesis := GenesisBlock(0, db)
|
||||
_, err := NewChainManager(genesis, db, db, db, thePow(), &mux)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
genesis = GenesisBlock(1, db)
|
||||
_, err = NewChainManager(genesis, db, db, db, thePow(), &mux)
|
||||
if err == nil {
|
||||
t.Error("expected genesis mismatch error")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// failpow returns false from Verify for a certain block number.
|
||||
type failpow struct{ num uint64 }
|
||||
|
||||
func (pow failpow) Search(pow.Block, <-chan struct{}) (nonce uint64, mixHash []byte) {
|
||||
return 0, nil
|
||||
}
|
||||
func (pow failpow) Verify(b pow.Block) bool {
|
||||
return b.NumberU64() != pow.num
|
||||
}
|
||||
func (pow failpow) GetHashrate() int64 {
|
||||
return 0
|
||||
}
|
||||
func (pow failpow) Turbo(bool) {
|
||||
}
|
||||
179
core/chain_util.go
Normal file
179
core/chain_util.go
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/core/types"
|
||||
"github.com/chattynet/chatty/logger"
|
||||
"github.com/chattynet/chatty/logger/glog"
|
||||
"github.com/chattynet/chatty/params"
|
||||
"github.com/chattynet/chatty/rlp"
|
||||
)
|
||||
|
||||
var (
|
||||
blockHashPre = []byte("block-hash-")
|
||||
blockNumPre = []byte("block-num-")
|
||||
ExpDiffPeriod = big.NewInt(100000)
|
||||
)
|
||||
|
||||
// CalcDifficulty is the difficulty adjustment algorithm. It returns
|
||||
// the difficulty that a new block b should have when created at time
|
||||
// given the parent block's time and difficulty.
|
||||
func CalcDifficulty(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
|
||||
diff := new(big.Int)
|
||||
adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor)
|
||||
bigTime := new(big.Int)
|
||||
bigParentTime := new(big.Int)
|
||||
|
||||
bigTime.SetUint64(time)
|
||||
bigParentTime.SetUint64(parentTime)
|
||||
|
||||
if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 {
|
||||
diff.Add(parentDiff, adjust)
|
||||
} else {
|
||||
diff.Sub(parentDiff, adjust)
|
||||
}
|
||||
if diff.Cmp(params.MinimumDifficulty) < 0 {
|
||||
diff = params.MinimumDifficulty
|
||||
}
|
||||
|
||||
periodCount := new(big.Int).Add(parentNumber, common.Big1)
|
||||
periodCount.Div(periodCount, ExpDiffPeriod)
|
||||
if periodCount.Cmp(common.Big1) > 0 {
|
||||
// diff = diff + 2^(periodCount - 2)
|
||||
expDiff := periodCount.Sub(periodCount, common.Big2)
|
||||
expDiff.Exp(common.Big2, expDiff, nil)
|
||||
diff.Add(diff, expDiff)
|
||||
diff = common.BigMax(diff, params.MinimumDifficulty)
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
// CalcTD computes the total difficulty of block.
|
||||
func CalcTD(block, parent *types.Block) *big.Int {
|
||||
if parent == nil {
|
||||
return block.Difficulty()
|
||||
}
|
||||
d := block.Difficulty()
|
||||
d.Add(d, parent.Td)
|
||||
return d
|
||||
}
|
||||
|
||||
// CalcGasLimit computes the gas limit of the next block after parent.
|
||||
// The result may be modified by the caller.
|
||||
// This is miner strategy, not consensus protocol.
|
||||
func CalcGasLimit(parent *types.Block) *big.Int {
|
||||
// contrib = (parentGasUsed * 3 / 2) / 1024
|
||||
contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3))
|
||||
contrib = contrib.Div(contrib, big.NewInt(2))
|
||||
contrib = contrib.Div(contrib, params.GasLimitBoundDivisor)
|
||||
|
||||
// decay = parentGasLimit / 1024 -1
|
||||
decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor)
|
||||
decay.Sub(decay, big.NewInt(1))
|
||||
|
||||
/*
|
||||
strategy: gasLimit of block-to-mine is set based on parent's
|
||||
gasUsed value. if parentGasUsed > parentGasLimit * (2/3) then we
|
||||
increase it, otherwise lower it (or leave it unchanged if it's right
|
||||
at that usage) the amount increased/decreased depends on how far away
|
||||
from parentGasLimit * (2/3) parentGasUsed is.
|
||||
*/
|
||||
gl := new(big.Int).Sub(parent.GasLimit(), decay)
|
||||
gl = gl.Add(gl, contrib)
|
||||
gl.Set(common.BigMax(gl, params.MinGasLimit))
|
||||
|
||||
// however, if we're now below the target (GenesisGasLimit) we increase the
|
||||
// limit as much as we can (parentGasLimit / 1024 -1)
|
||||
if gl.Cmp(params.GenesisGasLimit) < 0 {
|
||||
gl.Add(parent.GasLimit(), decay)
|
||||
gl.Set(common.BigMin(gl, params.GenesisGasLimit))
|
||||
}
|
||||
return gl
|
||||
}
|
||||
|
||||
// GetBlockByHash returns the block corresponding to the hash or nil if not found
|
||||
func GetBlockByHash(db common.Database, hash common.Hash) *types.Block {
|
||||
data, _ := db.Get(append(blockHashPre, hash[:]...))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
var block types.StorageBlock
|
||||
if err := rlp.Decode(bytes.NewReader(data), &block); err != nil {
|
||||
glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err)
|
||||
return nil
|
||||
}
|
||||
return (*types.Block)(&block)
|
||||
}
|
||||
|
||||
// GetBlockByHash returns the canonical block by number or nil if not found
|
||||
func GetBlockByNumber(db common.Database, number uint64) *types.Block {
|
||||
key, _ := db.Get(append(blockNumPre, big.NewInt(int64(number)).Bytes()...))
|
||||
if len(key) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return GetBlockByHash(db, common.BytesToHash(key))
|
||||
}
|
||||
|
||||
// WriteCanonNumber writes the canonical hash for the given block
|
||||
func WriteCanonNumber(db common.Database, block *types.Block) error {
|
||||
key := append(blockNumPre, block.Number().Bytes()...)
|
||||
err := db.Put(key, block.Hash().Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteHead force writes the current head
|
||||
func WriteHead(db common.Database, block *types.Block) error {
|
||||
err := WriteCanonNumber(db, block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = db.Put([]byte("LastBlock"), block.Hash().Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteBlock writes a block to the database
|
||||
func WriteBlock(db common.Database, block *types.Block) error {
|
||||
tstart := time.Now()
|
||||
|
||||
enc, _ := rlp.EncodeToBytes((*types.StorageBlock)(block))
|
||||
key := append(blockHashPre, block.Hash().Bytes()...)
|
||||
err := db.Put(key, enc)
|
||||
if err != nil {
|
||||
glog.Fatal("db write fail:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if glog.V(logger.Debug) {
|
||||
glog.Infof("wrote block #%v %s. Took %v\n", block.Number(), common.PP(block.Hash().Bytes()), time.Since(tstart))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
77
core/chain_util_test.go
Normal file
77
core/chain_util_test.go
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
)
|
||||
|
||||
type diffTest struct {
|
||||
ParentTimestamp uint64
|
||||
ParentDifficulty *big.Int
|
||||
CurrentTimestamp uint64
|
||||
CurrentBlocknumber *big.Int
|
||||
CurrentDifficulty *big.Int
|
||||
}
|
||||
|
||||
func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
|
||||
var ext struct {
|
||||
ParentTimestamp string
|
||||
ParentDifficulty string
|
||||
CurrentTimestamp string
|
||||
CurrentBlocknumber string
|
||||
CurrentDifficulty string
|
||||
}
|
||||
if err := json.Unmarshal(b, &ext); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.ParentTimestamp = common.String2Big(ext.ParentTimestamp).Uint64()
|
||||
d.ParentDifficulty = common.String2Big(ext.ParentDifficulty)
|
||||
d.CurrentTimestamp = common.String2Big(ext.CurrentTimestamp).Uint64()
|
||||
d.CurrentBlocknumber = common.String2Big(ext.CurrentBlocknumber)
|
||||
d.CurrentDifficulty = common.String2Big(ext.CurrentDifficulty)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestDifficulty(t *testing.T) {
|
||||
file, err := os.Open("../tests/files/BasicTests/difficulty.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
tests := make(map[string]diffTest)
|
||||
err = json.NewDecoder(file).Decode(&tests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
|
||||
diff := CalcDifficulty(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
|
||||
if diff.Cmp(test.CurrentDifficulty) != 0 {
|
||||
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
212
core/filter.go
Normal file
212
core/filter.go
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/core/state"
|
||||
"github.com/chattynet/chatty/core/types"
|
||||
"github.com/chattynet/chatty/logger"
|
||||
"github.com/chattynet/chatty/logger/glog"
|
||||
)
|
||||
|
||||
type AccountChange struct {
|
||||
Address, StateAddress []byte
|
||||
}
|
||||
|
||||
// Filtering interface
|
||||
type Filter struct {
|
||||
eth Backend
|
||||
earliest int64
|
||||
latest int64
|
||||
skip int
|
||||
address []common.Address
|
||||
max int
|
||||
topics [][]common.Hash
|
||||
|
||||
BlockCallback func(*types.Block, state.Logs)
|
||||
TransactionCallback func(*types.Transaction)
|
||||
LogsCallback func(state.Logs)
|
||||
}
|
||||
|
||||
// Create a new filter which uses a bloom filter on blocks to figure out whether a particular block
|
||||
// is interesting or not.
|
||||
func NewFilter(eth Backend) *Filter {
|
||||
return &Filter{eth: eth}
|
||||
}
|
||||
|
||||
// Set the earliest and latest block for filtering.
|
||||
// -1 = latest block (i.e., the current block)
|
||||
// hash = particular hash from-to
|
||||
func (self *Filter) SetEarliestBlock(earliest int64) {
|
||||
self.earliest = earliest
|
||||
}
|
||||
|
||||
func (self *Filter) SetLatestBlock(latest int64) {
|
||||
self.latest = latest
|
||||
}
|
||||
|
||||
func (self *Filter) SetAddress(addr []common.Address) {
|
||||
self.address = addr
|
||||
}
|
||||
|
||||
func (self *Filter) SetTopics(topics [][]common.Hash) {
|
||||
self.topics = topics
|
||||
}
|
||||
|
||||
func (self *Filter) SetMax(max int) {
|
||||
self.max = max
|
||||
}
|
||||
|
||||
func (self *Filter) SetSkip(skip int) {
|
||||
self.skip = skip
|
||||
}
|
||||
|
||||
// Run filters logs with the current parameters set
|
||||
func (self *Filter) Find() state.Logs {
|
||||
earliestBlock := self.eth.ChainManager().CurrentBlock()
|
||||
var earliestBlockNo uint64 = uint64(self.earliest)
|
||||
if self.earliest == -1 {
|
||||
earliestBlockNo = earliestBlock.NumberU64()
|
||||
}
|
||||
var latestBlockNo uint64 = uint64(self.latest)
|
||||
if self.latest == -1 {
|
||||
latestBlockNo = earliestBlock.NumberU64()
|
||||
}
|
||||
|
||||
var (
|
||||
logs state.Logs
|
||||
block = self.eth.ChainManager().GetBlockByNumber(latestBlockNo)
|
||||
)
|
||||
|
||||
done:
|
||||
for i := 0; block != nil; i++ {
|
||||
// Quit on latest
|
||||
switch {
|
||||
case block.NumberU64() == 0:
|
||||
break done
|
||||
case block.NumberU64() < earliestBlockNo:
|
||||
break done
|
||||
case self.max <= len(logs):
|
||||
break done
|
||||
}
|
||||
|
||||
// Use bloom filtering to see if this block is interesting given the
|
||||
// current parameters
|
||||
if self.bloomFilter(block) {
|
||||
// Get the logs of the block
|
||||
unfiltered, err := self.eth.BlockProcessor().GetLogs(block)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("err: filter get logs ", err)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
logs = append(logs, self.FilterLogs(unfiltered)...)
|
||||
}
|
||||
|
||||
block = self.eth.ChainManager().GetBlock(block.ParentHash())
|
||||
}
|
||||
|
||||
skip := int(math.Min(float64(len(logs)), float64(self.skip)))
|
||||
|
||||
return logs[skip:]
|
||||
}
|
||||
|
||||
func includes(addresses []common.Address, a common.Address) bool {
|
||||
for _, addr := range addresses {
|
||||
if addr == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *Filter) FilterLogs(logs state.Logs) state.Logs {
|
||||
var ret state.Logs
|
||||
|
||||
// Filter the logs for interesting stuff
|
||||
Logs:
|
||||
for _, log := range logs {
|
||||
if len(self.address) > 0 && !includes(self.address, log.Address) {
|
||||
continue
|
||||
}
|
||||
|
||||
logTopics := make([]common.Hash, len(self.topics))
|
||||
copy(logTopics, log.Topics)
|
||||
|
||||
// If the to filtered topics is greater than the amount of topics in
|
||||
// logs, skip.
|
||||
if len(self.topics) > len(log.Topics) {
|
||||
continue Logs
|
||||
}
|
||||
|
||||
for i, topics := range self.topics {
|
||||
var match bool
|
||||
for _, topic := range topics {
|
||||
// common.Hash{} is a match all (wildcard)
|
||||
if (topic == common.Hash{}) || log.Topics[i] == topic {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !match {
|
||||
continue Logs
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ret = append(ret, log)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (self *Filter) bloomFilter(block *types.Block) bool {
|
||||
if len(self.address) > 0 {
|
||||
var included bool
|
||||
for _, addr := range self.address {
|
||||
if types.BloomLookup(block.Bloom(), addr) {
|
||||
included = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !included {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, sub := range self.topics {
|
||||
var included bool
|
||||
for _, topic := range sub {
|
||||
if (topic == common.Hash{}) || types.BloomLookup(block.Bloom(), topic) {
|
||||
included = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !included {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
34
core/manager.go
Normal file
34
core/manager.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"github.com/chattynet/chatty/accounts"
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/event"
|
||||
)
|
||||
|
||||
// TODO move this to types?
|
||||
type Backend interface {
|
||||
AccountManager() *accounts.Manager
|
||||
BlockProcessor() *BlockProcessor
|
||||
ChainManager() *ChainManager
|
||||
TxPool() *TxPool
|
||||
ChainDb() common.Database
|
||||
DappDb() common.Database
|
||||
EventMux() *event.TypeMux
|
||||
}
|
||||
39
core/state/errors.go
Normal file
39
core/state/errors.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type GasLimitErr struct {
|
||||
Message string
|
||||
Is, Max *big.Int
|
||||
}
|
||||
|
||||
func IsGasLimitErr(err error) bool {
|
||||
_, ok := err.(*GasLimitErr)
|
||||
|
||||
return ok
|
||||
}
|
||||
func (err *GasLimitErr) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
func GasLimitError(is, max *big.Int) *GasLimitErr {
|
||||
return &GasLimitErr{Message: fmt.Sprintf("GasLimit error. Max %s, transaction would take it to %s", max, is), Is: is, Max: max}
|
||||
}
|
||||
66
core/state/log.go
Normal file
66
core/state/log.go
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/rlp"
|
||||
)
|
||||
|
||||
type Log struct {
|
||||
Address common.Address
|
||||
Topics []common.Hash
|
||||
Data []byte
|
||||
Number uint64
|
||||
|
||||
TxHash common.Hash
|
||||
TxIndex uint
|
||||
BlockHash common.Hash
|
||||
Index uint
|
||||
}
|
||||
|
||||
func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log {
|
||||
return &Log{Address: address, Topics: topics, Data: data, Number: number}
|
||||
}
|
||||
|
||||
func (self *Log) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, []interface{}{self.Address, self.Topics, self.Data})
|
||||
}
|
||||
|
||||
func (self *Log) String() string {
|
||||
return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, self.Address, self.Topics, self.Data, self.TxHash, self.TxIndex, self.BlockHash, self.Index)
|
||||
}
|
||||
|
||||
type Logs []*Log
|
||||
|
||||
type LogForStorage Log
|
||||
|
||||
func (self *LogForStorage) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, []interface{}{
|
||||
self.Address,
|
||||
self.Topics,
|
||||
self.Data,
|
||||
self.Number,
|
||||
self.TxHash,
|
||||
self.TxIndex,
|
||||
self.BlockHash,
|
||||
self.Index,
|
||||
})
|
||||
}
|
||||
161
core/transaction_util.go
Normal file
161
core/transaction_util.go
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/core/types"
|
||||
"github.com/chattynet/chatty/ethdb"
|
||||
"github.com/chattynet/chatty/logger"
|
||||
"github.com/chattynet/chatty/logger/glog"
|
||||
"github.com/chattynet/chatty/rlp"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
)
|
||||
|
||||
var (
|
||||
receiptsPre = []byte("receipts-")
|
||||
blockReceiptsPre = []byte("receipts-block-")
|
||||
)
|
||||
|
||||
// PutTransactions stores the transactions in the given database
|
||||
func PutTransactions(db common.Database, block *types.Block, txs types.Transactions) {
|
||||
batch := new(leveldb.Batch)
|
||||
_, batchWrite := db.(*ethdb.LDBDatabase)
|
||||
|
||||
for i, tx := range block.Transactions() {
|
||||
rlpEnc, err := rlp.EncodeToBytes(tx)
|
||||
if err != nil {
|
||||
glog.V(logger.Debug).Infoln("Failed encoding tx", err)
|
||||
return
|
||||
}
|
||||
|
||||
if batchWrite {
|
||||
batch.Put(tx.Hash().Bytes(), rlpEnc)
|
||||
} else {
|
||||
db.Put(tx.Hash().Bytes(), rlpEnc)
|
||||
}
|
||||
|
||||
var txExtra struct {
|
||||
BlockHash common.Hash
|
||||
BlockIndex uint64
|
||||
Index uint64
|
||||
}
|
||||
txExtra.BlockHash = block.Hash()
|
||||
txExtra.BlockIndex = block.NumberU64()
|
||||
txExtra.Index = uint64(i)
|
||||
rlpMeta, err := rlp.EncodeToBytes(txExtra)
|
||||
if err != nil {
|
||||
glog.V(logger.Debug).Infoln("Failed encoding tx meta data", err)
|
||||
return
|
||||
}
|
||||
|
||||
if batchWrite {
|
||||
batch.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta)
|
||||
} else {
|
||||
db.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta)
|
||||
}
|
||||
}
|
||||
|
||||
if db, ok := db.(*ethdb.LDBDatabase); ok {
|
||||
if err := db.LDB().Write(batch, nil); err != nil {
|
||||
glog.V(logger.Error).Infoln("db write err:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PutReceipts stores the receipts in the current database
|
||||
func PutReceipts(db common.Database, receipts types.Receipts) error {
|
||||
batch := new(leveldb.Batch)
|
||||
_, batchWrite := db.(*ethdb.LDBDatabase)
|
||||
|
||||
for _, receipt := range receipts {
|
||||
storageReceipt := (*types.ReceiptForStorage)(receipt)
|
||||
bytes, err := rlp.EncodeToBytes(storageReceipt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if batchWrite {
|
||||
batch.Put(append(receiptsPre, receipt.TxHash[:]...), bytes)
|
||||
} else {
|
||||
err = db.Put(append(receiptsPre, receipt.TxHash[:]...), bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if db, ok := db.(*ethdb.LDBDatabase); ok {
|
||||
if err := db.LDB().Write(batch, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetReceipt returns a receipt by hash
|
||||
func GetReceipt(db common.Database, txHash common.Hash) *types.Receipt {
|
||||
data, _ := db.Get(append(receiptsPre, txHash[:]...))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var receipt types.Receipt
|
||||
err := rlp.DecodeBytes(data, &receipt)
|
||||
if err != nil {
|
||||
glog.V(logger.Core).Infoln("GetReceipt err:", err)
|
||||
}
|
||||
return &receipt
|
||||
}
|
||||
|
||||
// GetBlockReceipts returns the receipts generated by the transactions
|
||||
// included in block's given hash.
|
||||
func GetBlockReceipts(db common.Database, hash common.Hash) types.Receipts {
|
||||
data, _ := db.Get(append(blockReceiptsPre, hash[:]...))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var receipts types.Receipts
|
||||
err := rlp.DecodeBytes(data, &receipts)
|
||||
if err != nil {
|
||||
glog.V(logger.Core).Infoln("GetReceiptse err", err)
|
||||
}
|
||||
return receipts
|
||||
}
|
||||
|
||||
// PutBlockReceipts stores the block's transactions associated receipts
|
||||
// and stores them by block hash in a single slice. This is required for
|
||||
// forks and chain reorgs
|
||||
func PutBlockReceipts(db common.Database, block *types.Block, receipts types.Receipts) error {
|
||||
rs := make([]*types.ReceiptForStorage, len(receipts))
|
||||
for i, receipt := range receipts {
|
||||
rs[i] = (*types.ReceiptForStorage)(receipt)
|
||||
}
|
||||
bytes, err := rlp.EncodeToBytes(rs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hash := block.Hash()
|
||||
err = db.Put(append(blockReceiptsPre, hash[:]...), bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
56
core/types/common.go
Normal file
56
core/types/common.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
"github.com/chattynet/chatty/core/state"
|
||||
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type BlockProcessor interface {
|
||||
Process(*Block) (state.Logs, Receipts, error)
|
||||
}
|
||||
|
||||
const bloomLength = 256
|
||||
|
||||
type Bloom [bloomLength]byte
|
||||
|
||||
func BytesToBloom(b []byte) Bloom {
|
||||
var bloom Bloom
|
||||
bloom.SetBytes(b)
|
||||
return bloom
|
||||
}
|
||||
|
||||
func (b *Bloom) SetBytes(d []byte) {
|
||||
if len(b) < len(d) {
|
||||
panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d)))
|
||||
}
|
||||
|
||||
copy(b[bloomLength-len(d):], d)
|
||||
}
|
||||
|
||||
func (b Bloom) Big() *big.Int {
|
||||
return common.Bytes2Big(b[:])
|
||||
}
|
||||
|
||||
func (b Bloom) Bytes() []byte {
|
||||
return b[:]
|
||||
}
|
||||
120
core/vm/context.go
Normal file
120
core/vm/context.go
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/chattynet/chatty/common"
|
||||
)
|
||||
|
||||
type ContextRef interface {
|
||||
ReturnGas(*big.Int, *big.Int)
|
||||
Address() common.Address
|
||||
SetCode([]byte)
|
||||
}
|
||||
|
||||
type Context struct {
|
||||
caller ContextRef
|
||||
self ContextRef
|
||||
|
||||
jumpdests destinations // result of JUMPDEST analysis.
|
||||
|
||||
Code []byte
|
||||
Input []byte
|
||||
CodeAddr *common.Address
|
||||
|
||||
value, Gas, UsedGas, Price *big.Int
|
||||
|
||||
Args []byte
|
||||
}
|
||||
|
||||
// Create a new context for the given data items.
|
||||
func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int) *Context {
|
||||
c := &Context{caller: caller, self: object, Args: nil}
|
||||
|
||||
if parent, ok := caller.(*Context); ok {
|
||||
// Reuse JUMPDEST analysis from parent context if available.
|
||||
c.jumpdests = parent.jumpdests
|
||||
} else {
|
||||
c.jumpdests = make(destinations)
|
||||
}
|
||||
|
||||
// Gas should be a pointer so it can safely be reduced through the run
|
||||
// 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
|
||||
}
|
||||
|
||||
func (c *Context) GetOp(n uint64) OpCode {
|
||||
return OpCode(c.GetByte(n))
|
||||
}
|
||||
|
||||
func (c *Context) GetByte(n uint64) byte {
|
||||
if n < uint64(len(c.Code)) {
|
||||
return c.Code[n]
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *Context) Return(ret []byte) []byte {
|
||||
// Return the remaining gas to the caller
|
||||
c.caller.ReturnGas(c.Gas, c.Price)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
/*
|
||||
* Gas functions
|
||||
*/
|
||||
func (c *Context) UseGas(gas *big.Int) (ok bool) {
|
||||
ok = UseGas(c.Gas, gas)
|
||||
if ok {
|
||||
c.UsedGas.Add(c.UsedGas, gas)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Implement the caller interface
|
||||
func (c *Context) ReturnGas(gas, price *big.Int) {
|
||||
// Return the gas to the context
|
||||
c.Gas.Add(c.Gas, gas)
|
||||
c.UsedGas.Sub(c.UsedGas, gas)
|
||||
}
|
||||
|
||||
/*
|
||||
* Set / Get
|
||||
*/
|
||||
func (c *Context) Address() common.Address {
|
||||
return c.self.Address()
|
||||
}
|
||||
|
||||
func (self *Context) SetCode(code []byte) {
|
||||
self.Code = code
|
||||
}
|
||||
|
||||
func (self *Context) SetCallCode(addr *common.Address, code []byte) {
|
||||
self.Code = code
|
||||
self.CodeAddr = addr
|
||||
}
|
||||
25
core/vm/settings.go
Normal file
25
core/vm/settings.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
var (
|
||||
EnableJit bool // Enables the JIT VM
|
||||
ForceJit bool // Force the JIT, skip byte VM
|
||||
MaxProgSize int // Max cache size for JIT Programs
|
||||
)
|
||||
|
||||
const defaultJitMaxCache int = 64
|
||||
17
cross/crosscompile.sh
Executable file
17
cross/crosscompile.sh
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
## This is only a note to "self" so i do not forget the steps.
|
||||
## Ugly code, live with it.
|
||||
|
||||
go_path="./go";
|
||||
|
||||
if [[ -d $go_path ]]; then
|
||||
cd $go_path && git clone https://github.com/karalabe/xgo && cd xgo/ && go build
|
||||
docker pull karalabe/xgo-latest
|
||||
./xgo --targets="linux/amd64,windows/amd64,darwin/amd64" --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 -pkg=cmd/shift github.com/chattynet/chatty
|
||||
else
|
||||
echo "Create the directory $go_path";
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
exit 0;
|
||||
32
crypto/secp256k1/secp256k1/.travis.yml
Normal file
32
crypto/secp256k1/secp256k1/.travis.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
language: c
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
install:
|
||||
- sudo apt-get install -qq libssl-dev
|
||||
- if [ "$BIGNUM" = "gmp" -o "$BIGNUM" = "auto" ]; then sudo apt-get install --no-install-recommends --no-upgrade -qq libgmp-dev; fi
|
||||
- if [ -n "$EXTRAPACKAGES" ]; then sudo apt-get update && sudo apt-get install --no-install-recommends --no-upgrade $EXTRAPACKAGES; fi
|
||||
env:
|
||||
global:
|
||||
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no ASM=no BUILD=check EXTRAFLAGS= HOST= EXTRAPACKAGES=
|
||||
matrix:
|
||||
- SCALAR=32bit
|
||||
- SCALAR=64bit
|
||||
- FIELD=64bit
|
||||
- FIELD=64bit ENDOMORPHISM=yes
|
||||
- FIELD=64bit ASM=x86_64
|
||||
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
|
||||
- FIELD=32bit
|
||||
- FIELD=32bit ENDOMORPHISM=yes
|
||||
- BIGNUM=no
|
||||
- BIGNUM=no ENDOMORPHISM=yes
|
||||
- BUILD=distcheck
|
||||
- EXTRAFLAGS=CFLAGS=-DDETERMINISTIC
|
||||
- HOST=i686-linux-gnu EXTRAPACKAGES="gcc-multilib"
|
||||
- HOST=i686-linux-gnu EXTRAPACKAGES="gcc-multilib" ENDOMORPHISM=yes
|
||||
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 $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
|
||||
os: linux
|
||||
19
crypto/secp256k1/secp256k1/COPYING
Normal file
19
crypto/secp256k1/secp256k1/COPYING
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2013 Pieter Wuille
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
77
crypto/secp256k1/secp256k1/Makefile.am
Normal file
77
crypto/secp256k1/secp256k1/Makefile.am
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
ACLOCAL_AMFLAGS = -I build-aux/m4
|
||||
|
||||
lib_LTLIBRARIES = libsecp256k1.la
|
||||
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_impl.h
|
||||
noinst_HEADERS += src/scalar_4x64_impl.h
|
||||
noinst_HEADERS += src/scalar_8x32_impl.h
|
||||
noinst_HEADERS += src/group.h
|
||||
noinst_HEADERS += src/group_impl.h
|
||||
noinst_HEADERS += src/num_gmp.h
|
||||
noinst_HEADERS += src/num_gmp_impl.h
|
||||
noinst_HEADERS += src/ecdsa.h
|
||||
noinst_HEADERS += src/ecdsa_impl.h
|
||||
noinst_HEADERS += src/eckey.h
|
||||
noinst_HEADERS += src/eckey_impl.h
|
||||
noinst_HEADERS += src/ecmult.h
|
||||
noinst_HEADERS += src/ecmult_impl.h
|
||||
noinst_HEADERS += src/ecmult_gen.h
|
||||
noinst_HEADERS += src/ecmult_gen_impl.h
|
||||
noinst_HEADERS += src/num.h
|
||||
noinst_HEADERS += src/num_impl.h
|
||||
noinst_HEADERS += src/field_10x26.h
|
||||
noinst_HEADERS += src/field_10x26_impl.h
|
||||
noinst_HEADERS += src/field_5x52.h
|
||||
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/util.h
|
||||
noinst_HEADERS += src/testrand.h
|
||||
noinst_HEADERS += src/testrand_impl.h
|
||||
noinst_HEADERS += src/hash.h
|
||||
noinst_HEADERS += src/hash_impl.h
|
||||
noinst_HEADERS += src/field.h
|
||||
noinst_HEADERS += src/field_impl.h
|
||||
noinst_HEADERS += src/bench.h
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libsecp256k1.pc
|
||||
|
||||
libsecp256k1_la_SOURCES = src/secp256k1.c
|
||||
libsecp256k1_la_CPPFLAGS = -I$(top_srcdir)/include $(SECP_INCLUDES)
|
||||
libsecp256k1_la_LIBADD = $(SECP_LIBS)
|
||||
|
||||
|
||||
noinst_PROGRAMS =
|
||||
if USE_BENCHMARK
|
||||
noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_internal
|
||||
bench_verify_SOURCES = src/bench_verify.c
|
||||
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
|
||||
bench_verify_LDFLAGS = -static
|
||||
bench_recover_SOURCES = src/bench_recover.c
|
||||
bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS)
|
||||
bench_recover_LDFLAGS = -static
|
||||
bench_sign_SOURCES = src/bench_sign.c
|
||||
bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS)
|
||||
bench_sign_LDFLAGS = -static
|
||||
bench_internal_SOURCES = src/bench_internal.c
|
||||
bench_internal_LDADD = $(SECP_LIBS)
|
||||
bench_internal_LDFLAGS = -static
|
||||
bench_internal_CPPFLAGS = $(SECP_INCLUDES)
|
||||
endif
|
||||
|
||||
if USE_TESTS
|
||||
noinst_PROGRAMS += tests
|
||||
tests_SOURCES = src/tests.c
|
||||
tests_CPPFLAGS = -DVERIFY $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
|
||||
tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS)
|
||||
tests_LDFLAGS = -static
|
||||
TESTS = tests
|
||||
endif
|
||||
|
||||
EXTRA_DIST = autogen.sh
|
||||
61
crypto/secp256k1/secp256k1/README.md
Normal file
61
crypto/secp256k1/secp256k1/README.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
libsecp256k1
|
||||
============
|
||||
|
||||
[](https://travis-ci.org/bitcoin/secp256k1)
|
||||
|
||||
Optimized C library for EC operations on curve secp256k1.
|
||||
|
||||
This library is a work in progress and is being used to research best practices. Use at your own risk.
|
||||
|
||||
Features:
|
||||
* secp256k1 ECDSA signing/verification and key generation.
|
||||
* Adding/multiplying private/public keys.
|
||||
* Serialization/parsing of private keys, public keys, signatures.
|
||||
* Constant time, constant memory access signing and pubkey generation.
|
||||
* Derandomized DSA (via RFC6979 or with a caller provided function.)
|
||||
* Very efficient implementation.
|
||||
|
||||
Implementation details
|
||||
----------------------
|
||||
|
||||
* General
|
||||
* No runtime heap allocation.
|
||||
* Extensive testing infrastructure.
|
||||
* Structured to facilitate review and analysis.
|
||||
* Intended to be portable to any system with a C89 compiler and uint64_t support.
|
||||
* Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.")
|
||||
* Field operations
|
||||
* Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1).
|
||||
* Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys).
|
||||
* Using 10 26-bit limbs.
|
||||
* Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman).
|
||||
* Scalar operations
|
||||
* Optimized implementation without data-dependent branches of arithmetic modulo the curve's order.
|
||||
* Using 4 64-bit limbs (relying on __int128 support in the compiler).
|
||||
* Using 8 32-bit limbs.
|
||||
* Group operations
|
||||
* Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7).
|
||||
* Use addition between points in Jacobian and affine coordinates where possible.
|
||||
* Use a unified addition/doubling formula where necessary to avoid data-dependent branches.
|
||||
* Point/x comparison without a field inversion by comparison in the Jacobian coordinate space.
|
||||
* Point multiplication for verification (a*P + b*G).
|
||||
* Use wNAF notation for point multiplicands.
|
||||
* Use a much larger window for multiples of G, using precomputed multiples.
|
||||
* Use Shamir's trick to do the multiplication with the public key and the generator simultaneously.
|
||||
* Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones.
|
||||
* Point multiplication for signing
|
||||
* Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions.
|
||||
* Access the table with branch-free conditional moves so memory access is uniform.
|
||||
* No data-dependent branches
|
||||
* The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally.
|
||||
|
||||
Build steps
|
||||
-----------
|
||||
|
||||
libsecp256k1 is built using autotools:
|
||||
|
||||
$ ./autogen.sh
|
||||
$ ./configure
|
||||
$ make
|
||||
$ ./tests
|
||||
$ sudo make install # optional
|
||||
3
crypto/secp256k1/secp256k1/TODO
Normal file
3
crypto/secp256k1/secp256k1/TODO
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
* Unit tests for fieldelem/groupelem, including ones intended to
|
||||
trigger fieldelem's boundary cases.
|
||||
* Complete constant-time operations for signing/keygen
|
||||
3
crypto/secp256k1/secp256k1/autogen.sh
Executable file
3
crypto/secp256k1/secp256k1/autogen.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
autoreconf -if --warnings=all
|
||||
61
crypto/secp256k1/secp256k1/build-aux/m4/bitcoin_secp.m4
Normal file
61
crypto/secp256k1/secp256k1/build-aux/m4/bitcoin_secp.m4
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
dnl libsecp25k1 helper checks
|
||||
AC_DEFUN([SECP_INT128_CHECK],[
|
||||
has_int128=$ac_cv_type___int128
|
||||
])
|
||||
|
||||
dnl
|
||||
AC_DEFUN([SECP_64BIT_ASM_CHECK],[
|
||||
AC_MSG_CHECKING(for x86_64 assembly availability)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <stdint.h>]],[[
|
||||
uint64_t a = 11, tmp;
|
||||
__asm__ __volatile__("movq $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],[
|
||||
if test x"$use_pkgconfig" = x"yes"; then
|
||||
: #NOP
|
||||
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
|
||||
])
|
||||
else
|
||||
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 <openssl/ec.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/obj_mac.h>]],[[
|
||||
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);
|
||||
]])],[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
|
||||
])
|
||||
330
crypto/secp256k1/secp256k1/configure.ac
Normal file
330
crypto/secp256k1/secp256k1/configure.ac
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
AC_PREREQ([2.60])
|
||||
AC_INIT([libsecp256k1],[0.1])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_MACRO_DIR([build-aux/m4])
|
||||
AC_CANONICAL_HOST
|
||||
AH_TOP([#ifndef LIBSECP256K1_CONFIG_H])
|
||||
AH_TOP([#define LIBSECP256K1_CONFIG_H])
|
||||
AH_BOTTOM([#endif /*LIBSECP256K1_CONFIG_H*/])
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
LT_INIT
|
||||
|
||||
dnl make the compilation flags quiet unless V=1 is used
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
AC_PATH_TOOL(AR, ar)
|
||||
AC_PATH_TOOL(RANLIB, ranlib)
|
||||
AC_PATH_TOOL(STRIP, strip)
|
||||
|
||||
if test "x$CFLAGS" = "x"; then
|
||||
CFLAGS="-O3 -g"
|
||||
fi
|
||||
|
||||
AC_PROG_CC_C89
|
||||
if test x"$ac_cv_prog_cc_c89" = x"no"; then
|
||||
AC_MSG_ERROR([c89 compiler support required])
|
||||
fi
|
||||
|
||||
case $host in
|
||||
*mingw*)
|
||||
use_pkgconfig=no
|
||||
;;
|
||||
*)
|
||||
use_pkgconfig=yes
|
||||
;;
|
||||
esac
|
||||
|
||||
case $host_os in
|
||||
*darwin*)
|
||||
if test x$cross_compiling != xyes; then
|
||||
AC_PATH_PROG([BREW],brew,)
|
||||
if test x$BREW != x; then
|
||||
dnl These Homebrew packages may be keg-only, meaning that they won't be found
|
||||
dnl in expected paths because they may conflict with system files. Ask
|
||||
dnl Homebrew where each one is located, then adjust paths accordingly.
|
||||
|
||||
openssl_prefix=`$BREW --prefix openssl 2>/dev/null`
|
||||
gmp_prefix=`$BREW --prefix gmp 2>/dev/null`
|
||||
if test x$openssl_prefix != x; then
|
||||
PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export PKG_CONFIG_PATH
|
||||
fi
|
||||
if test x$gmp_prefix != x; then
|
||||
GMP_CPPFLAGS="-I$gmp_prefix/include"
|
||||
GMP_LIBS="-L$gmp_prefix/lib"
|
||||
fi
|
||||
else
|
||||
AC_PATH_PROG([PORT],port,)
|
||||
dnl if homebrew isn't installed and macports is, add the macports default paths
|
||||
dnl as a last resort.
|
||||
if test x$PORT != x; then
|
||||
CPPFLAGS="$CPPFLAGS -isystem /opt/local/include"
|
||||
LDFLAGS="$LDFLAGS -L/opt/local/lib"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
CFLAGS="$CFLAGS -W"
|
||||
|
||||
warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wno-unused-function -Wno-long-long -Wno-overlength-strings"
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $warn_CFLAGS"
|
||||
AC_MSG_CHECKING([if ${CC} supports ${warn_CFLAGS}])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
|
||||
[ AC_MSG_RESULT([yes]) ],
|
||||
[ AC_MSG_RESULT([no])
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
])
|
||||
|
||||
|
||||
AC_ARG_ENABLE(benchmark,
|
||||
AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is no)]),
|
||||
[use_benchmark=$enableval],
|
||||
[use_benchmark=no])
|
||||
|
||||
AC_ARG_ENABLE(tests,
|
||||
AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]),
|
||||
[use_tests=$enableval],
|
||||
[use_tests=yes])
|
||||
|
||||
AC_ARG_ENABLE(endomorphism,
|
||||
AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]),
|
||||
[use_endomorphism=$enableval],
|
||||
[use_endomorphism=no])
|
||||
|
||||
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
|
||||
[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto])
|
||||
|
||||
AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
|
||||
[Specify Bignum Implementation. Default is auto])],[req_bignum=$withval], [req_bignum=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_CHECK_TYPES([__int128])
|
||||
|
||||
AC_MSG_CHECKING([for __builtin_expect])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])],
|
||||
[ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_EXPECT,1,[Define this symbol if __builtin_expect is available]) ],
|
||||
[ AC_MSG_RESULT([no])
|
||||
])
|
||||
|
||||
if test x"$req_asm" = x"auto"; then
|
||||
SECP_64BIT_ASM_CHECK
|
||||
if test x"$has_64bit_asm" = x"yes"; then
|
||||
set_asm=x86_64
|
||||
fi
|
||||
if test x"$set_asm" = x; then
|
||||
set_asm=no
|
||||
fi
|
||||
else
|
||||
set_asm=$req_asm
|
||||
case $set_asm in
|
||||
x86_64)
|
||||
SECP_64BIT_ASM_CHECK
|
||||
if test x"$has_64bit_asm" != x"yes"; then
|
||||
AC_MSG_ERROR([x86_64 assembly optimization requested but not available])
|
||||
fi
|
||||
;;
|
||||
no)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid assembly optimization selection])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x"$req_field" = x"auto"; then
|
||||
if test x"set_asm" = x"x86_64"; then
|
||||
set_field=64bit
|
||||
fi
|
||||
if test x"$set_field" = x; then
|
||||
SECP_INT128_CHECK
|
||||
if test x"$has_int128" = x"yes"; then
|
||||
set_field=64bit
|
||||
fi
|
||||
fi
|
||||
if test x"$set_field" = x; then
|
||||
set_field=32bit
|
||||
fi
|
||||
else
|
||||
set_field=$req_field
|
||||
case $set_field in
|
||||
64bit)
|
||||
if test x"$set_asm" != x"x86_64"; then
|
||||
SECP_INT128_CHECK
|
||||
if test x"$has_int128" != x"yes"; then
|
||||
AC_MSG_ERROR([64bit field explicitly requested but neither __int128 support or x86_64 assembly available])
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
32bit)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid field implementation selection])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x"$req_scalar" = x"auto"; then
|
||||
SECP_INT128_CHECK
|
||||
if test x"$has_int128" = x"yes"; then
|
||||
set_scalar=64bit
|
||||
fi
|
||||
if test x"$set_scalar" = x; then
|
||||
set_scalar=32bit
|
||||
fi
|
||||
else
|
||||
set_scalar=$req_scalar
|
||||
case $set_scalar in
|
||||
64bit)
|
||||
SECP_INT128_CHECK
|
||||
if test x"$has_int128" != x"yes"; then
|
||||
AC_MSG_ERROR([64bit scalar explicitly requested but __int128 support not available])
|
||||
fi
|
||||
;;
|
||||
32bit)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid scalar implementation selected])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x"$req_bignum" = x"auto"; then
|
||||
SECP_GMP_CHECK
|
||||
if test x"$has_gmp" = x"yes"; then
|
||||
set_bignum=gmp
|
||||
fi
|
||||
|
||||
if test x"$set_bignum" = x; then
|
||||
set_bignum=no
|
||||
fi
|
||||
else
|
||||
set_bignum=$req_bignum
|
||||
case $set_bignum in
|
||||
gmp)
|
||||
SECP_GMP_CHECK
|
||||
if test x"$has_gmp" != x"yes"; then
|
||||
AC_MSG_ERROR([gmp bignum explicitly requested but libgmp not available])
|
||||
fi
|
||||
;;
|
||||
no)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid bignum implementation selection])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# select assembly optimization
|
||||
case $set_asm in
|
||||
x86_64)
|
||||
AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations])
|
||||
;;
|
||||
no)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid assembly optimizations])
|
||||
;;
|
||||
esac
|
||||
|
||||
# select field implementation
|
||||
case $set_field in
|
||||
64bit)
|
||||
AC_DEFINE(USE_FIELD_5X52, 1, [Define this symbol to use the FIELD_5X52 implementation])
|
||||
;;
|
||||
32bit)
|
||||
AC_DEFINE(USE_FIELD_10X26, 1, [Define this symbol to use the FIELD_10X26 implementation])
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid field implementation])
|
||||
;;
|
||||
esac
|
||||
|
||||
# select bignum implementation
|
||||
case $set_bignum in
|
||||
gmp)
|
||||
AC_DEFINE(HAVE_LIBGMP, 1, [Define this symbol if libgmp is installed])
|
||||
AC_DEFINE(USE_NUM_GMP, 1, [Define this symbol to use the gmp implementation for num])
|
||||
AC_DEFINE(USE_FIELD_INV_NUM, 1, [Define this symbol to use the num-based field inverse implementation])
|
||||
AC_DEFINE(USE_SCALAR_INV_NUM, 1, [Define this symbol to use the num-based scalar inverse implementation])
|
||||
;;
|
||||
no)
|
||||
AC_DEFINE(USE_NUM_NONE, 1, [Define this symbol to use no num implementation])
|
||||
AC_DEFINE(USE_FIELD_INV_BUILTIN, 1, [Define this symbol to use the native field inverse implementation])
|
||||
AC_DEFINE(USE_SCALAR_INV_BUILTIN, 1, [Define this symbol to use the native scalar inverse implementation])
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid bignum implementation])
|
||||
;;
|
||||
esac
|
||||
|
||||
#select scalar implementation
|
||||
case $set_scalar in
|
||||
64bit)
|
||||
AC_DEFINE(USE_SCALAR_4X64, 1, [Define this symbol to use the 4x64 scalar implementation])
|
||||
;;
|
||||
32bit)
|
||||
AC_DEFINE(USE_SCALAR_8X32, 1, [Define this symbol to use the 8x32 scalar implementation])
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid scalar implementation])
|
||||
;;
|
||||
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"
|
||||
|
||||
case $host in
|
||||
*mingw*)
|
||||
SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32"
|
||||
;;
|
||||
esac
|
||||
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$set_bignum" = x"gmp"; then
|
||||
SECP_LIBS="$SECP_LIBS $GMP_LIBS"
|
||||
SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS"
|
||||
fi
|
||||
|
||||
if test x"$use_endomorphism" = x"yes"; then
|
||||
AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization])
|
||||
fi
|
||||
|
||||
AC_C_BIGENDIAN()
|
||||
|
||||
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_CONFIG_HEADERS([src/libsecp256k1-config.h])
|
||||
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
|
||||
AC_SUBST(SECP_INCLUDES)
|
||||
AC_SUBST(SECP_LIBS)
|
||||
AC_SUBST(SECP_TEST_LIBS)
|
||||
AC_SUBST(SECP_TEST_INCLUDES)
|
||||
AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"])
|
||||
AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
|
||||
|
||||
dnl make sure nothing new is exported so that we don't break the cache
|
||||
PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"
|
||||
unset PKG_CONFIG_PATH
|
||||
PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP"
|
||||
|
||||
AC_OUTPUT
|
||||
295
crypto/secp256k1/secp256k1/include/secp256k1.h
Normal file
295
crypto/secp256k1/secp256k1/include/secp256k1.h
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
#ifndef _SECP256K1_
|
||||
# define _SECP256K1_
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
# if !defined(SECP256K1_GNUC_PREREQ)
|
||||
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
|
||||
# define SECP256K1_GNUC_PREREQ(_maj,_min) \
|
||||
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
|
||||
# else
|
||||
# define SECP256K1_GNUC_PREREQ(_maj,_min) 0
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) )
|
||||
# if SECP256K1_GNUC_PREREQ(2,7)
|
||||
# define SECP256K1_INLINE __inline__
|
||||
# elif (defined(_MSC_VER))
|
||||
# define SECP256K1_INLINE __inline
|
||||
# else
|
||||
# define SECP256K1_INLINE
|
||||
# endif
|
||||
# else
|
||||
# define SECP256K1_INLINE inline
|
||||
# endif
|
||||
|
||||
/**Warning attributes
|
||||
* NONNULL is not used if SECP256K1_BUILD is set to avoid the compiler optimizing out
|
||||
* some paranoid null checks. */
|
||||
# if defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4)
|
||||
# define SECP256K1_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__))
|
||||
# else
|
||||
# define SECP256K1_WARN_UNUSED_RESULT
|
||||
# endif
|
||||
# if !defined(SECP256K1_BUILD) && defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4)
|
||||
# define SECP256K1_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x)))
|
||||
# else
|
||||
# define SECP256K1_ARG_NONNULL(_x)
|
||||
# endif
|
||||
|
||||
|
||||
/** Flags to pass to secp256k1_start. */
|
||||
# define SECP256K1_START_VERIFY (1 << 0)
|
||||
# define SECP256K1_START_SIGN (1 << 1)
|
||||
|
||||
/** Initialize the library. This may take some time (10-100 ms).
|
||||
* You need to call this before calling any other function.
|
||||
* It cannot run in parallel with any other functions, but once
|
||||
* secp256k1_start() returns, all other functions are thread-safe.
|
||||
*/
|
||||
void secp256k1_start(unsigned int flags);
|
||||
|
||||
/** Free all memory associated with this library. After this, no
|
||||
* functions can be called anymore, except secp256k1_start()
|
||||
*/
|
||||
void secp256k1_stop(void);
|
||||
|
||||
/** Verify an ECDSA signature.
|
||||
* Returns: 1: correct signature
|
||||
* 0: incorrect signature
|
||||
* -1: invalid public key
|
||||
* -2: invalid signature
|
||||
* In: msg32: the 32-byte message hash being verified (cannot be NULL)
|
||||
* sig: the signature being verified (cannot be NULL)
|
||||
* siglen: the length of the signature
|
||||
* pubkey: the public key to verify with (cannot be NULL)
|
||||
* pubkeylen: the length of pubkey
|
||||
* Requires starting using SECP256K1_START_VERIFY.
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
|
||||
const unsigned char *msg32,
|
||||
const unsigned char *sig,
|
||||
int siglen,
|
||||
const unsigned char *pubkey,
|
||||
int pubkeylen
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** A pointer to a function to deterministically generate a nonce.
|
||||
* Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail.
|
||||
* In: msg32: the 32-byte message hash being verified (will not be NULL)
|
||||
* key32: pointer to a 32-byte secret key (will not be NULL)
|
||||
* attempt: how many iterations we have tried to find a nonce.
|
||||
* This will almost always be 0, but different attempt values
|
||||
* are required to result in a different nonce.
|
||||
* data: Arbitrary data pointer that is passed through.
|
||||
* Out: nonce32: pointer to a 32-byte array to be filled by the function.
|
||||
* Except for test cases, this function should compute some cryptographic hash of
|
||||
* the message, the key and the attempt.
|
||||
*/
|
||||
typedef int (*secp256k1_nonce_function_t)(
|
||||
unsigned char *nonce32,
|
||||
const unsigned char *msg32,
|
||||
const unsigned char *key32,
|
||||
unsigned int attempt,
|
||||
const void *data
|
||||
);
|
||||
|
||||
/** 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_t secp256k1_nonce_function_rfc6979;
|
||||
|
||||
/** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */
|
||||
extern const secp256k1_nonce_function_t secp256k1_nonce_function_default;
|
||||
|
||||
|
||||
/** Create an ECDSA signature.
|
||||
* Returns: 1: signature created
|
||||
* 0: the nonce generation function failed, the private key was invalid, or there is not
|
||||
* enough space in the signature (as indicated by siglen).
|
||||
* 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)
|
||||
* Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
|
||||
* In/Out: siglen: pointer to an int with the length of sig, which will be updated
|
||||
* to contain the actual signature length (<=72). If 0 is returned, this will be
|
||||
* set to zero.
|
||||
* Requires starting using SECP256K1_START_SIGN.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
int secp256k1_ecdsa_sign(
|
||||
const unsigned char *msg32,
|
||||
unsigned char *sig,
|
||||
int *siglen,
|
||||
const unsigned char *seckey,
|
||||
secp256k1_nonce_function_t noncefp,
|
||||
const void *ndata
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Create a compact ECDSA signature (64 byte + recovery id).
|
||||
* Returns: 1: signature created
|
||||
* 0: the nonce generation function failed, or the secret key was invalid.
|
||||
* 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)
|
||||
* Out: sig: pointer to a 64-byte array where the signature will be placed (cannot be NULL)
|
||||
* In case 0 is returned, the returned signature length will be zero.
|
||||
* recid: pointer to an int, which will be updated to contain the recovery id (can be NULL)
|
||||
* Requires starting using SECP256K1_START_SIGN.
|
||||
*/
|
||||
int secp256k1_ecdsa_sign_compact(
|
||||
const unsigned char *msg32,
|
||||
unsigned char *sig64,
|
||||
const unsigned char *seckey,
|
||||
secp256k1_nonce_function_t noncefp,
|
||||
const void *ndata,
|
||||
int *recid
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Recover an ECDSA public key from a compact signature.
|
||||
* Returns: 1: public key successfully recovered (which guarantees a correct signature).
|
||||
* 0: otherwise.
|
||||
* In: msg32: the 32-byte message hash assumed to be signed (cannot be NULL)
|
||||
* sig64: signature as 64 byte array (cannot be NULL)
|
||||
* compressed: whether to recover a compressed or uncompressed pubkey
|
||||
* recid: the recovery id (0-3, as returned by ecdsa_sign_compact)
|
||||
* Out: pubkey: pointer to a 33 or 65 byte array to put the pubkey (cannot be NULL)
|
||||
* pubkeylen: pointer to an int that will contain the pubkey length (cannot be NULL)
|
||||
* Requires starting using SECP256K1_START_VERIFY.
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover_compact(
|
||||
const unsigned char *msg32,
|
||||
const unsigned char *sig64,
|
||||
unsigned char *pubkey,
|
||||
int *pubkeylen,
|
||||
int compressed,
|
||||
int recid
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** 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 (cannot be NULL)
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify(const unsigned char *seckey) SECP256K1_ARG_NONNULL(1);
|
||||
|
||||
/** Just validate a public key.
|
||||
* Returns: 1: valid public key
|
||||
* 0: invalid public key
|
||||
* In: pubkey: pointer to a 33-byte or 65-byte public key (cannot be NULL).
|
||||
* pubkeylen: length of pubkey
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_verify(const unsigned char *pubkey, int pubkeylen) SECP256K1_ARG_NONNULL(1);
|
||||
|
||||
/** 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 (cannot be NULL)
|
||||
* Out: pubkey: pointer to a 33-byte (if compressed) or 65-byte (if uncompressed)
|
||||
* area to store the public key (cannot be NULL)
|
||||
* pubkeylen: pointer to int that will be updated to contains the pubkey's
|
||||
* length (cannot be NULL)
|
||||
* Returns: 1: secret was valid, public key stores
|
||||
* 0: secret was invalid, try again.
|
||||
* Requires starting using SECP256K1_START_SIGN.
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
|
||||
unsigned char *pubkey,
|
||||
int *pubkeylen,
|
||||
const unsigned char *seckey,
|
||||
int compressed
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Decompress a public key.
|
||||
* In/Out: pubkey: pointer to a 65-byte array to put the decompressed public key.
|
||||
It must contain a 33-byte or 65-byte public key already (cannot be NULL)
|
||||
* pubkeylen: pointer to the size of the public key pointed to by pubkey (cannot be NULL)
|
||||
It will be updated to reflect the new size.
|
||||
* Returns: 0 if the passed public key was invalid, 1 otherwise. If 1 is returned, the
|
||||
pubkey is replaced with its decompressed version.
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_decompress(
|
||||
unsigned char *pubkey,
|
||||
int *pubkeylen
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
|
||||
|
||||
/** Export a private key in DER format. */
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_export(
|
||||
const unsigned char *seckey,
|
||||
unsigned char *privkey,
|
||||
int *privkeylen,
|
||||
int compressed
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Import a private key in DER format. */
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_import(
|
||||
unsigned char *seckey,
|
||||
const unsigned char *privkey,
|
||||
int privkeylen
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
|
||||
|
||||
/** Tweak a private key by adding tweak to it. */
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
|
||||
unsigned char *seckey,
|
||||
const unsigned char *tweak
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
|
||||
|
||||
/** Tweak a public key by adding tweak times the generator to it.
|
||||
* Requires starting with SECP256K1_START_VERIFY.
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
|
||||
unsigned char *pubkey,
|
||||
int pubkeylen,
|
||||
const unsigned char *tweak
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Tweak a private key by multiplying it with tweak. */
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
|
||||
unsigned char *seckey,
|
||||
const unsigned char *tweak
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
|
||||
|
||||
/** Tweak a public key by multiplying it with tweak.
|
||||
* Requires starting with SECP256K1_START_VERIFY.
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
|
||||
unsigned char *pubkey,
|
||||
int pubkeylen,
|
||||
const unsigned char *tweak
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
|
||||
#endif
|
||||
13
crypto/secp256k1/secp256k1/libsecp256k1.pc.in
Normal file
13
crypto/secp256k1/secp256k1/libsecp256k1.pc.in
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: libsecp256k1
|
||||
Description: Optimized C library for EC operations on curve secp256k1
|
||||
URL: https://github.com/bitcoin/secp256k1
|
||||
Version: @PACKAGE_VERSION@
|
||||
Cflags: -I${includedir}
|
||||
Libs.private: @SECP_LIBS@
|
||||
Libs: -L${libdir} -lsecp256k1
|
||||
|
||||
0
crypto/secp256k1/secp256k1/obj/.gitignore
vendored
Normal file
0
crypto/secp256k1/secp256k1/obj/.gitignore
vendored
Normal file
56
crypto/secp256k1/secp256k1/src/bench.h
Normal file
56
crypto/secp256k1/secp256k1/src/bench.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_BENCH_H_
|
||||
#define _SECP256K1_BENCH_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "sys/time.h"
|
||||
|
||||
static double gettimedouble(void) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_usec * 0.000001 + tv.tv_sec;
|
||||
}
|
||||
|
||||
void print_number(double x) {
|
||||
double y = x;
|
||||
int c = 0;
|
||||
if (y < 0.0) y = -y;
|
||||
while (y < 100.0) {
|
||||
y *= 10.0;
|
||||
c++;
|
||||
}
|
||||
printf("%.*f", c, x);
|
||||
}
|
||||
|
||||
void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), void (*teardown)(void*), void* data, int count, int iter) {
|
||||
int i;
|
||||
double min = HUGE_VAL;
|
||||
double sum = 0.0;
|
||||
double max = 0.0;
|
||||
for (i = 0; i < count; i++) {
|
||||
double begin, total;
|
||||
if (setup) setup(data);
|
||||
begin = gettimedouble();
|
||||
benchmark(data);
|
||||
total = gettimedouble() - begin;
|
||||
if (teardown) teardown(data);
|
||||
if (total < min) min = total;
|
||||
if (total > max) max = total;
|
||||
sum += total;
|
||||
}
|
||||
printf("%s: min ", name);
|
||||
print_number(min * 1000000.0 / iter);
|
||||
printf("us / avg ");
|
||||
print_number((sum / count) * 1000000.0 / iter);
|
||||
printf("us / avg ");
|
||||
print_number(max * 1000000.0 / iter);
|
||||
printf("us\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
318
crypto/secp256k1/secp256k1/src/bench_internal.c
Normal file
318
crypto/secp256k1/secp256k1/src/bench_internal.c
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
/**********************************************************************
|
||||
* 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 <stdio.h>
|
||||
|
||||
#include "include/secp256k1.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "hash_impl.h"
|
||||
#include "num_impl.h"
|
||||
#include "field_impl.h"
|
||||
#include "group_impl.h"
|
||||
#include "scalar_impl.h"
|
||||
#include "ecmult_impl.h"
|
||||
#include "bench.h"
|
||||
|
||||
typedef struct {
|
||||
secp256k1_scalar_t scalar_x, scalar_y;
|
||||
secp256k1_fe_t fe_x, fe_y;
|
||||
secp256k1_ge_t ge_x, ge_y;
|
||||
secp256k1_gej_t gej_x, gej_y;
|
||||
unsigned char data[32];
|
||||
int wnaf[256];
|
||||
} bench_inv_t;
|
||||
|
||||
void bench_setup(void* arg) {
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
static const unsigned char init_x[32] = {
|
||||
0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13,
|
||||
0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35,
|
||||
0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59,
|
||||
0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83
|
||||
};
|
||||
|
||||
static const unsigned char init_y[32] = {
|
||||
0x82, 0x83, 0x85, 0x87, 0x8b, 0x8d, 0x81, 0x83,
|
||||
0x97, 0xad, 0xaf, 0xb5, 0xb9, 0xbb, 0xbf, 0xc5,
|
||||
0xdb, 0xdd, 0xe3, 0xe7, 0xe9, 0xef, 0xf3, 0xf9,
|
||||
0x11, 0x15, 0x17, 0x1b, 0x1d, 0xb1, 0xbf, 0xd3
|
||||
};
|
||||
|
||||
secp256k1_scalar_set_b32(&data->scalar_x, init_x, NULL);
|
||||
secp256k1_scalar_set_b32(&data->scalar_y, init_y, NULL);
|
||||
secp256k1_fe_set_b32(&data->fe_x, init_x);
|
||||
secp256k1_fe_set_b32(&data->fe_y, init_y);
|
||||
CHECK(secp256k1_ge_set_xo_var(&data->ge_x, &data->fe_x, 0));
|
||||
CHECK(secp256k1_ge_set_xo_var(&data->ge_y, &data->fe_y, 1));
|
||||
secp256k1_gej_set_ge(&data->gej_x, &data->ge_x);
|
||||
secp256k1_gej_set_ge(&data->gej_y, &data->ge_y);
|
||||
memcpy(data->data, init_x, 32);
|
||||
}
|
||||
|
||||
void bench_scalar_add(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 2000000; i++) {
|
||||
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_scalar_negate(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 2000000; i++) {
|
||||
secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_scalar_sqr(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_scalar_mul(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
void bench_scalar_split(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_scalar_t l, r;
|
||||
secp256k1_scalar_split_lambda_var(&l, &r, &data->scalar_x);
|
||||
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void bench_scalar_inverse(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 2000; i++) {
|
||||
secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x);
|
||||
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_scalar_inverse_var(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 2000; i++) {
|
||||
secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x);
|
||||
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_normalize(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 2000000; i++) {
|
||||
secp256k1_fe_normalize(&data->fe_x);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_normalize_weak(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 2000000; i++) {
|
||||
secp256k1_fe_normalize_weak(&data->fe_x);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_mul(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_sqr(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_fe_sqr(&data->fe_x, &data->fe_x);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_inverse(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_fe_inv(&data->fe_x, &data->fe_x);
|
||||
secp256k1_fe_add(&data->fe_x, &data->fe_y);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_inverse_var(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_fe_inv_var(&data->fe_x, &data->fe_x);
|
||||
secp256k1_fe_add(&data->fe_x, &data->fe_y);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_sqrt_var(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_add(&data->fe_x, &data->fe_y);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_group_double_var(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_gej_double_var(&data->gej_x, &data->gej_x);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_group_add_var(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_group_add_affine(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_group_add_affine_var(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_ecmult_wnaf(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_ecmult_wnaf(data->wnaf, &data->scalar_x, WINDOW_A);
|
||||
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bench_sha256(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
secp256k1_sha256_t sha;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, data->data, 32);
|
||||
secp256k1_sha256_finalize(&sha, data->data);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_hmac_sha256(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
secp256k1_hmac_sha256_t hmac;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_hmac_sha256_initialize(&hmac, data->data, 32);
|
||||
secp256k1_hmac_sha256_write(&hmac, data->data, 32);
|
||||
secp256k1_hmac_sha256_finalize(&hmac, data->data);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_rfc6979_hmac_sha256(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
secp256k1_rfc6979_hmac_sha256_t rng;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 32, data->data, 32, NULL, 0);
|
||||
secp256k1_rfc6979_hmac_sha256_generate(&rng, data->data, 32);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int have_flag(int argc, char** argv, char *flag) {
|
||||
char** argm = argv + argc;
|
||||
argv++;
|
||||
if (argv == argm) {
|
||||
return 1;
|
||||
}
|
||||
while (argv != NULL && argv != argm) {
|
||||
if (strcmp(*argv, flag) == 0) return 1;
|
||||
argv++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
bench_inv_t data;
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, 2000000);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, 2000000);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, 200000);
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, 20000);
|
||||
#endif
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, 2000);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, 2000);
|
||||
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, 2000000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, 2000000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, 200000);
|
||||
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, "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, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, 20000);
|
||||
|
||||
if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "sha256")) run_benchmark("hash_sha256", bench_sha256, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "hmac")) run_benchmark("hash_hmac_sha256", bench_hmac_sha256, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "rng6979")) run_benchmark("hash_rfc6979_hmac_sha256", bench_rfc6979_hmac_sha256, bench_setup, NULL, &data, 10, 20000);
|
||||
return 0;
|
||||
}
|
||||
49
crypto/secp256k1/secp256k1/src/bench_recover.c
Normal file
49
crypto/secp256k1/secp256k1/src/bench_recover.c
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#include "include/secp256k1.h"
|
||||
#include "util.h"
|
||||
#include "bench.h"
|
||||
|
||||
typedef struct {
|
||||
unsigned char msg[32];
|
||||
unsigned char sig[64];
|
||||
} bench_recover_t;
|
||||
|
||||
void bench_recover(void* arg) {
|
||||
int i;
|
||||
bench_recover_t *data = (bench_recover_t*)arg;
|
||||
unsigned char pubkey[33];
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
int j;
|
||||
int pubkeylen = 33;
|
||||
CHECK(secp256k1_ecdsa_recover_compact(data->msg, data->sig, pubkey, &pubkeylen, 1, i % 2));
|
||||
for (j = 0; j < 32; j++) {
|
||||
data->sig[j + 32] = data->msg[j]; /* Move former message to S. */
|
||||
data->msg[j] = data->sig[j]; /* Move former R to message. */
|
||||
data->sig[j] = pubkey[j + 1]; /* Move recovered pubkey X coordinate to R (which must be a valid X coordinate). */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bench_recover_setup(void* arg) {
|
||||
int i;
|
||||
bench_recover_t *data = (bench_recover_t*)arg;
|
||||
|
||||
for (i = 0; i < 32; i++) data->msg[i] = 1 + i;
|
||||
for (i = 0; i < 64; i++) data->sig[i] = 65 + i;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
bench_recover_t data;
|
||||
secp256k1_start(SECP256K1_START_VERIFY);
|
||||
|
||||
run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, 20000);
|
||||
|
||||
secp256k1_stop();
|
||||
return 0;
|
||||
}
|
||||
48
crypto/secp256k1/secp256k1/src/bench_sign.c
Normal file
48
crypto/secp256k1/secp256k1/src/bench_sign.c
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#include "include/secp256k1.h"
|
||||
#include "util.h"
|
||||
#include "bench.h"
|
||||
|
||||
typedef struct {
|
||||
unsigned char msg[32];
|
||||
unsigned char key[32];
|
||||
} bench_sign_t;
|
||||
|
||||
static void bench_sign_setup(void* arg) {
|
||||
int i;
|
||||
bench_sign_t *data = (bench_sign_t*)arg;
|
||||
|
||||
for (i = 0; i < 32; i++) data->msg[i] = i + 1;
|
||||
for (i = 0; i < 32; i++) data->key[i] = i + 65;
|
||||
}
|
||||
|
||||
static void bench_sign(void* arg) {
|
||||
int i;
|
||||
bench_sign_t *data = (bench_sign_t*)arg;
|
||||
|
||||
unsigned char sig[64];
|
||||
for (i = 0; i < 20000; i++) {
|
||||
int j;
|
||||
int recid = 0;
|
||||
CHECK(secp256k1_ecdsa_sign_compact(data->msg, sig, data->key, NULL, NULL, &recid));
|
||||
for (j = 0; j < 32; j++) {
|
||||
data->msg[j] = sig[j]; /* Move former R to message. */
|
||||
data->key[j] = sig[j + 32]; /* Move former S to key. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
bench_sign_t data;
|
||||
secp256k1_start(SECP256K1_START_SIGN);
|
||||
|
||||
run_benchmark("ecdsa_sign", bench_sign, bench_sign_setup, NULL, &data, 10, 20000);
|
||||
|
||||
secp256k1_stop();
|
||||
return 0;
|
||||
}
|
||||
55
crypto/secp256k1/secp256k1/src/bench_verify.c
Normal file
55
crypto/secp256k1/secp256k1/src/bench_verify.c
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "include/secp256k1.h"
|
||||
#include "util.h"
|
||||
#include "bench.h"
|
||||
|
||||
typedef struct {
|
||||
unsigned char msg[32];
|
||||
unsigned char key[32];
|
||||
unsigned char sig[72];
|
||||
int siglen;
|
||||
unsigned char pubkey[33];
|
||||
int pubkeylen;
|
||||
} benchmark_verify_t;
|
||||
|
||||
static void benchmark_verify(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);
|
||||
CHECK(secp256k1_ecdsa_verify(data->msg, data->sig, data->siglen, data->pubkey, data->pubkeylen) == (i == 0));
|
||||
data->sig[data->siglen - 1] ^= (i & 0xFF);
|
||||
data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF);
|
||||
data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int i;
|
||||
benchmark_verify_t data;
|
||||
|
||||
secp256k1_start(SECP256K1_START_VERIFY | SECP256K1_START_SIGN);
|
||||
|
||||
for (i = 0; i < 32; i++) data.msg[i] = 1 + i;
|
||||
for (i = 0; i < 32; i++) data.key[i] = 33 + i;
|
||||
data.siglen = 72;
|
||||
secp256k1_ecdsa_sign(data.msg, data.sig, &data.siglen, data.key, NULL, NULL);
|
||||
data.pubkeylen = 33;
|
||||
CHECK(secp256k1_ec_pubkey_create(data.pubkey, &data.pubkeylen, data.key, 1));
|
||||
|
||||
run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, 20000);
|
||||
|
||||
secp256k1_stop();
|
||||
return 0;
|
||||
}
|
||||
23
crypto/secp256k1/secp256k1/src/ecdsa.h
Normal file
23
crypto/secp256k1/secp256k1/src/ecdsa.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_ECDSA_
|
||||
#define _SECP256K1_ECDSA_
|
||||
|
||||
#include "scalar.h"
|
||||
#include "group.h"
|
||||
|
||||
typedef struct {
|
||||
secp256k1_scalar_t r, s;
|
||||
} secp256k1_ecdsa_sig_t;
|
||||
|
||||
static int secp256k1_ecdsa_sig_parse(secp256k1_ecdsa_sig_t *r, const unsigned char *sig, int size);
|
||||
static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, int *size, const secp256k1_ecdsa_sig_t *a);
|
||||
static int secp256k1_ecdsa_sig_verify(const secp256k1_ecdsa_sig_t *sig, const secp256k1_ge_t *pubkey, const secp256k1_scalar_t *message);
|
||||
static int secp256k1_ecdsa_sig_sign(secp256k1_ecdsa_sig_t *sig, const secp256k1_scalar_t *seckey, const secp256k1_scalar_t *message, const secp256k1_scalar_t *nonce, int *recid);
|
||||
static int secp256k1_ecdsa_sig_recover(const secp256k1_ecdsa_sig_t *sig, secp256k1_ge_t *pubkey, const secp256k1_scalar_t *message, int recid);
|
||||
|
||||
#endif
|
||||
263
crypto/secp256k1/secp256k1/src/ecdsa_impl.h
Normal file
263
crypto/secp256k1/secp256k1/src/ecdsa_impl.h
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
|
||||
#ifndef _SECP256K1_ECDSA_IMPL_H_
|
||||
#define _SECP256K1_ECDSA_IMPL_H_
|
||||
|
||||
#include "scalar.h"
|
||||
#include "field.h"
|
||||
#include "group.h"
|
||||
#include "ecmult.h"
|
||||
#include "ecmult_gen.h"
|
||||
#include "ecdsa.h"
|
||||
|
||||
/** Group order for secp256k1 defined as 'n' in "Standards for Efficient Cryptography" (SEC2) 2.7.1
|
||||
* sage: for t in xrange(1023, -1, -1):
|
||||
* .. p = 2**256 - 2**32 - t
|
||||
* .. if p.is_prime():
|
||||
* .. print '%x'%p
|
||||
* .. break
|
||||
* 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'
|
||||
* sage: a = 0
|
||||
* sage: b = 7
|
||||
* sage: F = FiniteField (p)
|
||||
* sage: '%x' % (EllipticCurve ([F (a), F (b)]).order())
|
||||
* 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'
|
||||
*/
|
||||
static const secp256k1_fe_t secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST(
|
||||
0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL,
|
||||
0xBAAEDCE6UL, 0xAF48A03BUL, 0xBFD25E8CUL, 0xD0364141UL
|
||||
);
|
||||
|
||||
/** Difference between field and order, values 'p' and 'n' values defined in
|
||||
* "Standards for Efficient Cryptography" (SEC2) 2.7.1.
|
||||
* sage: p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
|
||||
* sage: a = 0
|
||||
* sage: b = 7
|
||||
* sage: F = FiniteField (p)
|
||||
* sage: '%x' % (p - EllipticCurve ([F (a), F (b)]).order())
|
||||
* '14551231950b75fc4402da1722fc9baee'
|
||||
*/
|
||||
static const secp256k1_fe_t secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CONST(
|
||||
0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL
|
||||
);
|
||||
|
||||
static int secp256k1_ecdsa_sig_parse(secp256k1_ecdsa_sig_t *r, const unsigned char *sig, int size) {
|
||||
unsigned char ra[32] = {0}, sa[32] = {0};
|
||||
const unsigned char *rp;
|
||||
const unsigned char *sp;
|
||||
int lenr;
|
||||
int lens;
|
||||
int overflow;
|
||||
if (sig[0] != 0x30) {
|
||||
return 0;
|
||||
}
|
||||
lenr = sig[3];
|
||||
if (5+lenr >= size) {
|
||||
return 0;
|
||||
}
|
||||
lens = sig[lenr+5];
|
||||
if (sig[1] != lenr+lens+4) {
|
||||
return 0;
|
||||
}
|
||||
if (lenr+lens+6 > size) {
|
||||
return 0;
|
||||
}
|
||||
if (sig[2] != 0x02) {
|
||||
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(&r->r, ra, &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_scalar_set_b32(&r->s, sa, &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, int *size, const secp256k1_ecdsa_sig_t *a) {
|
||||
unsigned char r[33] = {0}, s[33] = {0};
|
||||
unsigned char *rp = r, *sp = s;
|
||||
int lenR = 33, lenS = 33;
|
||||
secp256k1_scalar_get_b32(&r[1], &a->r);
|
||||
secp256k1_scalar_get_b32(&s[1], &a->s);
|
||||
while (lenR > 1 && rp[0] == 0 && rp[1] < 0x80) { lenR--; rp++; }
|
||||
while (lenS > 1 && sp[0] == 0 && sp[1] < 0x80) { lenS--; sp++; }
|
||||
if (*size < 6+lenS+lenR) {
|
||||
return 0;
|
||||
}
|
||||
*size = 6 + lenS + lenR;
|
||||
sig[0] = 0x30;
|
||||
sig[1] = 4 + lenS + lenR;
|
||||
sig[2] = 0x02;
|
||||
sig[3] = lenR;
|
||||
memcpy(sig+4, rp, lenR);
|
||||
sig[4+lenR] = 0x02;
|
||||
sig[5+lenR] = lenS;
|
||||
memcpy(sig+lenR+6, sp, lenS);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int secp256k1_ecdsa_sig_verify(const secp256k1_ecdsa_sig_t *sig, const secp256k1_ge_t *pubkey, const secp256k1_scalar_t *message) {
|
||||
unsigned char c[32];
|
||||
secp256k1_scalar_t sn, u1, u2;
|
||||
secp256k1_fe_t xr;
|
||||
secp256k1_gej_t pubkeyj;
|
||||
secp256k1_gej_t pr;
|
||||
|
||||
if (secp256k1_scalar_is_zero(&sig->r) || secp256k1_scalar_is_zero(&sig->s)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_scalar_inverse_var(&sn, &sig->s);
|
||||
secp256k1_scalar_mul(&u1, &sn, message);
|
||||
secp256k1_scalar_mul(&u2, &sn, &sig->r);
|
||||
secp256k1_gej_set_ge(&pubkeyj, pubkey);
|
||||
secp256k1_ecmult(&pr, &pubkeyj, &u2, &u1);
|
||||
if (secp256k1_gej_is_infinity(&pr)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_scalar_get_b32(c, &sig->r);
|
||||
secp256k1_fe_set_b32(&xr, c);
|
||||
|
||||
/** We now have the recomputed R point in pr, and its claimed x coordinate (modulo n)
|
||||
* in xr. Naively, we would extract the x coordinate from pr (requiring a inversion modulo p),
|
||||
* compute the remainder modulo n, and compare it to xr. However:
|
||||
*
|
||||
* xr == X(pr) mod n
|
||||
* <=> exists h. (xr + h * n < p && xr + h * n == X(pr))
|
||||
* [Since 2 * n > p, h can only be 0 or 1]
|
||||
* <=> (xr == X(pr)) || (xr + n < p && xr + n == X(pr))
|
||||
* [In Jacobian coordinates, X(pr) is pr.x / pr.z^2 mod p]
|
||||
* <=> (xr == pr.x / pr.z^2 mod p) || (xr + n < p && xr + n == pr.x / pr.z^2 mod p)
|
||||
* [Multiplying both sides of the equations by pr.z^2 mod p]
|
||||
* <=> (xr * pr.z^2 mod p == pr.x) || (xr + n < p && (xr + n) * pr.z^2 mod p == pr.x)
|
||||
*
|
||||
* Thus, we can avoid the inversion, but we have to check both cases separately.
|
||||
* 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. */
|
||||
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. */
|
||||
return 0;
|
||||
}
|
||||
secp256k1_fe_add(&xr, &secp256k1_ecdsa_const_order_as_fe);
|
||||
if (secp256k1_gej_eq_x_var(&xr, &pr)) {
|
||||
/* (xr + n) * pr.z^2 mod p == pr.x, so the signature is valid. */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int secp256k1_ecdsa_sig_recover(const secp256k1_ecdsa_sig_t *sig, secp256k1_ge_t *pubkey, const secp256k1_scalar_t *message, int recid) {
|
||||
unsigned char brx[32];
|
||||
secp256k1_fe_t fx;
|
||||
secp256k1_ge_t x;
|
||||
secp256k1_gej_t xj;
|
||||
secp256k1_scalar_t rn, u1, u2;
|
||||
secp256k1_gej_t qj;
|
||||
|
||||
if (secp256k1_scalar_is_zero(&sig->r) || secp256k1_scalar_is_zero(&sig->s)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_scalar_get_b32(brx, &sig->r);
|
||||
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, &sig->r);
|
||||
secp256k1_scalar_mul(&u1, &rn, message);
|
||||
secp256k1_scalar_negate(&u1, &u1);
|
||||
secp256k1_scalar_mul(&u2, &rn, &sig->s);
|
||||
secp256k1_ecmult(&qj, &xj, &u2, &u1);
|
||||
secp256k1_ge_set_gej_var(pubkey, &qj);
|
||||
return !secp256k1_gej_is_infinity(&qj);
|
||||
}
|
||||
|
||||
static int secp256k1_ecdsa_sig_sign(secp256k1_ecdsa_sig_t *sig, const secp256k1_scalar_t *seckey, const secp256k1_scalar_t *message, const secp256k1_scalar_t *nonce, int *recid) {
|
||||
unsigned char b[32];
|
||||
secp256k1_gej_t rp;
|
||||
secp256k1_ge_t r;
|
||||
secp256k1_scalar_t n;
|
||||
int overflow = 0;
|
||||
|
||||
secp256k1_ecmult_gen(&rp, nonce);
|
||||
secp256k1_ge_set_gej(&r, &rp);
|
||||
secp256k1_fe_normalize(&r.x);
|
||||
secp256k1_fe_normalize(&r.y);
|
||||
secp256k1_fe_get_b32(b, &r.x);
|
||||
secp256k1_scalar_set_b32(&sig->r, b, &overflow);
|
||||
if (secp256k1_scalar_is_zero(&sig->r)) {
|
||||
/* 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;
|
||||
}
|
||||
if (recid) {
|
||||
*recid = (overflow ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0);
|
||||
}
|
||||
secp256k1_scalar_mul(&n, &sig->r, seckey);
|
||||
secp256k1_scalar_add(&n, &n, message);
|
||||
secp256k1_scalar_inverse(&sig->s, nonce);
|
||||
secp256k1_scalar_mul(&sig->s, &sig->s, &n);
|
||||
secp256k1_scalar_clear(&n);
|
||||
secp256k1_gej_clear(&rp);
|
||||
secp256k1_ge_clear(&r);
|
||||
if (secp256k1_scalar_is_zero(&sig->s)) {
|
||||
return 0;
|
||||
}
|
||||
if (secp256k1_scalar_is_high(&sig->s)) {
|
||||
secp256k1_scalar_negate(&sig->s, &sig->s);
|
||||
if (recid) {
|
||||
*recid ^= 1;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
24
crypto/secp256k1/secp256k1/src/eckey.h
Normal file
24
crypto/secp256k1/secp256k1/src/eckey.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_ECKEY_
|
||||
#define _SECP256K1_ECKEY_
|
||||
|
||||
#include "group.h"
|
||||
#include "scalar.h"
|
||||
|
||||
static int secp256k1_eckey_pubkey_parse(secp256k1_ge_t *elem, const unsigned char *pub, int size);
|
||||
static int secp256k1_eckey_pubkey_serialize(secp256k1_ge_t *elem, unsigned char *pub, int *size, int compressed);
|
||||
|
||||
static int secp256k1_eckey_privkey_parse(secp256k1_scalar_t *key, const unsigned char *privkey, int privkeylen);
|
||||
static int secp256k1_eckey_privkey_serialize(unsigned char *privkey, int *privkeylen, const secp256k1_scalar_t *key, int compressed);
|
||||
|
||||
static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar_t *key, const secp256k1_scalar_t *tweak);
|
||||
static int secp256k1_eckey_pubkey_tweak_add(secp256k1_ge_t *key, const secp256k1_scalar_t *tweak);
|
||||
static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar_t *key, const secp256k1_scalar_t *tweak);
|
||||
static int secp256k1_eckey_pubkey_tweak_mul(secp256k1_ge_t *key, const secp256k1_scalar_t *tweak);
|
||||
|
||||
#endif
|
||||
202
crypto/secp256k1/secp256k1/src/eckey_impl.h
Normal file
202
crypto/secp256k1/secp256k1/src/eckey_impl.h
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_ECKEY_IMPL_H_
|
||||
#define _SECP256K1_ECKEY_IMPL_H_
|
||||
|
||||
#include "eckey.h"
|
||||
|
||||
#include "scalar.h"
|
||||
#include "field.h"
|
||||
#include "group.h"
|
||||
#include "ecmult_gen.h"
|
||||
|
||||
static int secp256k1_eckey_pubkey_parse(secp256k1_ge_t *elem, const unsigned char *pub, int size) {
|
||||
if (size == 33 && (pub[0] == 0x02 || pub[0] == 0x03)) {
|
||||
secp256k1_fe_t x;
|
||||
return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == 0x03);
|
||||
} else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) {
|
||||
secp256k1_fe_t x, y;
|
||||
if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_ge_set_xy(elem, &x, &y);
|
||||
if ((pub[0] == 0x06 || pub[0] == 0x07) && secp256k1_fe_is_odd(&y) != (pub[0] == 0x07)) {
|
||||
return 0;
|
||||
}
|
||||
return secp256k1_ge_is_valid_var(elem);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int secp256k1_eckey_pubkey_serialize(secp256k1_ge_t *elem, unsigned char *pub, int *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 (compressed) {
|
||||
*size = 33;
|
||||
pub[0] = 0x02 | (secp256k1_fe_is_odd(&elem->y) ? 0x01 : 0x00);
|
||||
} else {
|
||||
*size = 65;
|
||||
pub[0] = 0x04;
|
||||
secp256k1_fe_get_b32(&pub[33], &elem->y);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int secp256k1_eckey_privkey_parse(secp256k1_scalar_t *key, const unsigned char *privkey, int 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(unsigned char *privkey, int *privkeylen, const secp256k1_scalar_t *key, int compressed) {
|
||||
secp256k1_gej_t rp;
|
||||
secp256k1_ge_t r;
|
||||
int pubkeylen = 0;
|
||||
secp256k1_ecmult_gen(&rp, key);
|
||||
secp256k1_ge_set_gej(&r, &rp);
|
||||
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);
|
||||
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_t *key, const secp256k1_scalar_t *tweak) {
|
||||
secp256k1_scalar_add(key, key, tweak);
|
||||
if (secp256k1_scalar_is_zero(key)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int secp256k1_eckey_pubkey_tweak_add(secp256k1_ge_t *key, const secp256k1_scalar_t *tweak) {
|
||||
secp256k1_gej_t pt;
|
||||
secp256k1_scalar_t one;
|
||||
secp256k1_gej_set_ge(&pt, key);
|
||||
secp256k1_scalar_set_int(&one, 1);
|
||||
secp256k1_ecmult(&pt, &pt, &one, tweak);
|
||||
|
||||
if (secp256k1_gej_is_infinity(&pt)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_ge_set_gej(key, &pt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar_t *key, const secp256k1_scalar_t *tweak) {
|
||||
if (secp256k1_scalar_is_zero(tweak)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_scalar_mul(key, key, tweak);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int secp256k1_eckey_pubkey_tweak_mul(secp256k1_ge_t *key, const secp256k1_scalar_t *tweak) {
|
||||
secp256k1_scalar_t zero;
|
||||
secp256k1_gej_t pt;
|
||||
if (secp256k1_scalar_is_zero(tweak)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_scalar_set_int(&zero, 0);
|
||||
secp256k1_gej_set_ge(&pt, key);
|
||||
secp256k1_ecmult(&pt, &pt, tweak, &zero);
|
||||
secp256k1_ge_set_gej(key, &pt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
19
crypto/secp256k1/secp256k1/src/ecmult.h
Normal file
19
crypto/secp256k1/secp256k1/src/ecmult.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_ECMULT_
|
||||
#define _SECP256K1_ECMULT_
|
||||
|
||||
#include "num.h"
|
||||
#include "group.h"
|
||||
|
||||
static void secp256k1_ecmult_start(void);
|
||||
static void secp256k1_ecmult_stop(void);
|
||||
|
||||
/** Double multiply: R = na*A + ng*G */
|
||||
static void secp256k1_ecmult(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng);
|
||||
|
||||
#endif
|
||||
19
crypto/secp256k1/secp256k1/src/ecmult_gen.h
Normal file
19
crypto/secp256k1/secp256k1/src/ecmult_gen.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_ECMULT_GEN_
|
||||
#define _SECP256K1_ECMULT_GEN_
|
||||
|
||||
#include "scalar.h"
|
||||
#include "group.h"
|
||||
|
||||
static void secp256k1_ecmult_gen_start(void);
|
||||
static void secp256k1_ecmult_gen_stop(void);
|
||||
|
||||
/** Multiply with the generator: R = a*G */
|
||||
static void secp256k1_ecmult_gen(secp256k1_gej_t *r, const secp256k1_scalar_t *a);
|
||||
|
||||
#endif
|
||||
128
crypto/secp256k1/secp256k1/src/ecmult_gen_impl.h
Normal file
128
crypto/secp256k1/secp256k1/src/ecmult_gen_impl.h
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_ECMULT_GEN_IMPL_H_
|
||||
#define _SECP256K1_ECMULT_GEN_IMPL_H_
|
||||
|
||||
#include "scalar.h"
|
||||
#include "group.h"
|
||||
#include "ecmult_gen.h"
|
||||
|
||||
typedef struct {
|
||||
/* For accelerating the computation of a*G:
|
||||
* To harden against timing attacks, use the following mechanism:
|
||||
* * Break up the multiplicand into groups of 4 bits, called n_0, n_1, n_2, ..., n_63.
|
||||
* * Compute sum(n_i * 16^i * G + U_i, i=0..63), where:
|
||||
* * U_i = U * 2^i (for i=0..62)
|
||||
* * U_i = U * (1-2^63) (for i=63)
|
||||
* where U is a point with no known corresponding scalar. Note that sum(U_i, i=0..63) = 0.
|
||||
* For each i, and each of the 16 possible values of n_i, (n_i * 16^i * G + U_i) is
|
||||
* precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0..63).
|
||||
* None of the resulting prec group elements have a known scalar, and neither do any of
|
||||
* the intermediate sums while computing a*G.
|
||||
*/
|
||||
secp256k1_ge_storage_t prec[64][16]; /* prec[j][i] = 16^j * i * G + U_i */
|
||||
} secp256k1_ecmult_gen_consts_t;
|
||||
|
||||
static const secp256k1_ecmult_gen_consts_t *secp256k1_ecmult_gen_consts = NULL;
|
||||
|
||||
static void secp256k1_ecmult_gen_start(void) {
|
||||
secp256k1_ge_t prec[1024];
|
||||
secp256k1_gej_t gj;
|
||||
secp256k1_gej_t nums_gej;
|
||||
secp256k1_ecmult_gen_consts_t *ret;
|
||||
int i, j;
|
||||
if (secp256k1_ecmult_gen_consts != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate the precomputation table. */
|
||||
ret = (secp256k1_ecmult_gen_consts_t*)checked_malloc(sizeof(secp256k1_ecmult_gen_consts_t));
|
||||
|
||||
/* get the generator */
|
||||
secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g);
|
||||
|
||||
/* Construct a group element with no known corresponding scalar (nothing up my sleeve). */
|
||||
{
|
||||
static const unsigned char nums_b32[33] = "The scalar for this x is unknown";
|
||||
secp256k1_fe_t nums_x;
|
||||
secp256k1_ge_t nums_ge;
|
||||
VERIFY_CHECK(secp256k1_fe_set_b32(&nums_x, nums_b32));
|
||||
VERIFY_CHECK(secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0));
|
||||
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);
|
||||
}
|
||||
|
||||
/* compute prec. */
|
||||
{
|
||||
secp256k1_gej_t precj[1024]; /* Jacobian versions of prec. */
|
||||
secp256k1_gej_t gbase;
|
||||
secp256k1_gej_t numsbase;
|
||||
gbase = gj; /* 16^j * G */
|
||||
numsbase = nums_gej; /* 2^j * nums. */
|
||||
for (j = 0; j < 64; j++) {
|
||||
/* Set precj[j*16 .. j*16+15] to (numsbase, numsbase + gbase, ..., numsbase + 15*gbase). */
|
||||
precj[j*16] = numsbase;
|
||||
for (i = 1; i < 16; i++) {
|
||||
secp256k1_gej_add_var(&precj[j*16 + i], &precj[j*16 + i - 1], &gbase);
|
||||
}
|
||||
/* Multiply gbase by 16. */
|
||||
for (i = 0; i < 4; i++) {
|
||||
secp256k1_gej_double_var(&gbase, &gbase);
|
||||
}
|
||||
/* Multiply numbase by 2. */
|
||||
secp256k1_gej_double_var(&numsbase, &numsbase);
|
||||
if (j == 62) {
|
||||
/* In the last iteration, numsbase is (1 - 2^j) * nums instead. */
|
||||
secp256k1_gej_neg(&numsbase, &numsbase);
|
||||
secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej);
|
||||
}
|
||||
}
|
||||
secp256k1_ge_set_all_gej_var(1024, prec, precj);
|
||||
}
|
||||
for (j = 0; j < 64; j++) {
|
||||
for (i = 0; i < 16; i++) {
|
||||
secp256k1_ge_to_storage(&ret->prec[j][i], &prec[j*16 + i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the global pointer to the precomputation table. */
|
||||
secp256k1_ecmult_gen_consts = ret;
|
||||
}
|
||||
|
||||
static void secp256k1_ecmult_gen_stop(void) {
|
||||
secp256k1_ecmult_gen_consts_t *c;
|
||||
if (secp256k1_ecmult_gen_consts == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
c = (secp256k1_ecmult_gen_consts_t*)secp256k1_ecmult_gen_consts;
|
||||
secp256k1_ecmult_gen_consts = NULL;
|
||||
free(c);
|
||||
}
|
||||
|
||||
static void secp256k1_ecmult_gen(secp256k1_gej_t *r, const secp256k1_scalar_t *gn) {
|
||||
const secp256k1_ecmult_gen_consts_t *c = secp256k1_ecmult_gen_consts;
|
||||
secp256k1_ge_t add;
|
||||
secp256k1_ge_storage_t adds;
|
||||
int bits;
|
||||
int i, j;
|
||||
secp256k1_gej_set_infinity(r);
|
||||
add.infinity = 0;
|
||||
for (j = 0; j < 64; j++) {
|
||||
bits = secp256k1_scalar_get_bits(gn, j * 4, 4);
|
||||
for (i = 0; i < 16; i++) {
|
||||
secp256k1_ge_storage_cmov(&adds, &c->prec[j][i], i == bits);
|
||||
}
|
||||
secp256k1_ge_from_storage(&add, &adds);
|
||||
secp256k1_gej_add_ge(r, r, &add);
|
||||
}
|
||||
bits = 0;
|
||||
secp256k1_ge_clear(&add);
|
||||
}
|
||||
|
||||
#endif
|
||||
302
crypto/secp256k1/secp256k1/src/ecmult_impl.h
Normal file
302
crypto/secp256k1/secp256k1/src/ecmult_impl.h
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_ECMULT_IMPL_H_
|
||||
#define _SECP256K1_ECMULT_IMPL_H_
|
||||
|
||||
#include "group.h"
|
||||
#include "scalar.h"
|
||||
#include "ecmult.h"
|
||||
|
||||
/* 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
|
||||
/** Two tables for window size 15: 1.375 MiB. */
|
||||
#define WINDOW_G 15
|
||||
#else
|
||||
/** One table for window size 16: 1.375 MiB. */
|
||||
#define WINDOW_G 16
|
||||
#endif
|
||||
|
||||
/** Fill a table 'pre' with precomputed odd multiples of a. W determines the size of the table.
|
||||
* pre will contains the values [1*a,3*a,5*a,...,(2^(w-1)-1)*a], so it needs place for
|
||||
* 2^(w-2) entries.
|
||||
*
|
||||
* There are two versions of this function:
|
||||
* - secp256k1_ecmult_precomp_wnaf_gej, which operates on group elements in jacobian notation,
|
||||
* fast to precompute, but slower to use in later additions.
|
||||
* - secp256k1_ecmult_precomp_wnaf_ge, which operates on group elements in affine notations,
|
||||
* (much) slower to precompute, but a bit faster to use in later additions.
|
||||
* To compute a*P + b*G, we use the jacobian version for P, and the affine version for G, as
|
||||
* G is constant, so it only needs to be done once in advance.
|
||||
*/
|
||||
static void secp256k1_ecmult_table_precomp_gej_var(secp256k1_gej_t *pre, const secp256k1_gej_t *a, int w) {
|
||||
secp256k1_gej_t d;
|
||||
int i;
|
||||
pre[0] = *a;
|
||||
secp256k1_gej_double_var(&d, &pre[0]);
|
||||
for (i = 1; i < (1 << (w-2)); i++) {
|
||||
secp256k1_gej_add_var(&pre[i], &d, &pre[i-1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void secp256k1_ecmult_table_precomp_ge_storage_var(secp256k1_ge_storage_t *pre, const secp256k1_gej_t *a, int w) {
|
||||
secp256k1_gej_t d;
|
||||
int i;
|
||||
const int table_size = 1 << (w-2);
|
||||
secp256k1_gej_t *prej = (secp256k1_gej_t *)checked_malloc(sizeof(secp256k1_gej_t) * table_size);
|
||||
secp256k1_ge_t *prea = (secp256k1_ge_t *)checked_malloc(sizeof(secp256k1_ge_t) * table_size);
|
||||
prej[0] = *a;
|
||||
secp256k1_gej_double_var(&d, a);
|
||||
for (i = 1; i < table_size; i++) {
|
||||
secp256k1_gej_add_var(&prej[i], &d, &prej[i-1]);
|
||||
}
|
||||
secp256k1_ge_set_all_gej_var(table_size, prea, prej);
|
||||
for (i = 0; i < table_size; i++) {
|
||||
secp256k1_ge_to_storage(&pre[i], &prea[i]);
|
||||
}
|
||||
free(prej);
|
||||
free(prea);
|
||||
}
|
||||
|
||||
/** The number of entries a table with precomputed multiples needs to have. */
|
||||
#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2))
|
||||
|
||||
/** The following two macro retrieves a particular odd multiple from a table
|
||||
* of precomputed multiples. */
|
||||
#define ECMULT_TABLE_GET_GEJ(r,pre,n,w) do { \
|
||||
VERIFY_CHECK(((n) & 1) == 1); \
|
||||
VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \
|
||||
VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \
|
||||
if ((n) > 0) { \
|
||||
*(r) = (pre)[((n)-1)/2]; \
|
||||
} else { \
|
||||
secp256k1_gej_neg((r), &(pre)[(-(n)-1)/2]); \
|
||||
} \
|
||||
} while(0)
|
||||
#define ECMULT_TABLE_GET_GE_STORAGE(r,pre,n,w) do { \
|
||||
VERIFY_CHECK(((n) & 1) == 1); \
|
||||
VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \
|
||||
VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \
|
||||
if ((n) > 0) { \
|
||||
secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \
|
||||
} else { \
|
||||
secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \
|
||||
secp256k1_ge_neg((r), (r)); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
typedef struct {
|
||||
/* For accelerating the computation of a*P + b*G: */
|
||||
secp256k1_ge_storage_t pre_g[ECMULT_TABLE_SIZE(WINDOW_G)]; /* odd multiples of the generator */
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ge_storage_t pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)]; /* odd multiples of 2^128*generator */
|
||||
#endif
|
||||
} secp256k1_ecmult_consts_t;
|
||||
|
||||
static const secp256k1_ecmult_consts_t *secp256k1_ecmult_consts = NULL;
|
||||
|
||||
static void secp256k1_ecmult_start(void) {
|
||||
secp256k1_gej_t gj;
|
||||
secp256k1_ecmult_consts_t *ret;
|
||||
if (secp256k1_ecmult_consts != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate the precomputation table. */
|
||||
ret = (secp256k1_ecmult_consts_t*)checked_malloc(sizeof(secp256k1_ecmult_consts_t));
|
||||
|
||||
/* get the generator */
|
||||
secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g);
|
||||
|
||||
|
||||
/* precompute the tables with odd multiples */
|
||||
secp256k1_ecmult_table_precomp_ge_storage_var(ret->pre_g, &gj, WINDOW_G);
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
{
|
||||
secp256k1_gej_t g_128j;
|
||||
int i;
|
||||
/* calculate 2^128*generator */
|
||||
g_128j = gj;
|
||||
for (i = 0; i < 128; i++) {
|
||||
secp256k1_gej_double_var(&g_128j, &g_128j);
|
||||
}
|
||||
secp256k1_ecmult_table_precomp_ge_storage_var(ret->pre_g_128, &g_128j, WINDOW_G);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set the global pointer to the precomputation table. */
|
||||
secp256k1_ecmult_consts = ret;
|
||||
}
|
||||
|
||||
static void secp256k1_ecmult_stop(void) {
|
||||
secp256k1_ecmult_consts_t *c;
|
||||
if (secp256k1_ecmult_consts == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
c = (secp256k1_ecmult_consts_t*)secp256k1_ecmult_consts;
|
||||
secp256k1_ecmult_consts = NULL;
|
||||
free(c);
|
||||
}
|
||||
|
||||
/** Convert a number to WNAF notation. The number becomes represented by sum(2^i * wnaf[i], i=0..bits),
|
||||
* with the following guarantees:
|
||||
* - each wnaf[i] is either 0, or an odd integer between -(1<<(w-1) - 1) and (1<<(w-1) - 1)
|
||||
* - two non-zero entries in wnaf are separated by at least w-1 zeroes.
|
||||
* - the number of set values in wnaf is returned. This number is at most 256, and at most one more
|
||||
* - than the number of bits in the (absolute value) of the input.
|
||||
*/
|
||||
static int secp256k1_ecmult_wnaf(int *wnaf, const secp256k1_scalar_t *a, int w) {
|
||||
secp256k1_scalar_t s = *a;
|
||||
int set_bits = 0;
|
||||
int bit = 0;
|
||||
int sign = 1;
|
||||
|
||||
if (secp256k1_scalar_get_bits(&s, 255, 1)) {
|
||||
secp256k1_scalar_negate(&s, &s);
|
||||
sign = -1;
|
||||
}
|
||||
|
||||
while (bit < 256) {
|
||||
int now;
|
||||
int word;
|
||||
if (secp256k1_scalar_get_bits(&s, bit, 1) == 0) {
|
||||
bit++;
|
||||
continue;
|
||||
}
|
||||
while (set_bits < bit) {
|
||||
wnaf[set_bits++] = 0;
|
||||
}
|
||||
now = w;
|
||||
if (bit + now > 256) {
|
||||
now = 256 - bit;
|
||||
}
|
||||
word = secp256k1_scalar_get_bits_var(&s, bit, now);
|
||||
if (word & (1 << (w-1))) {
|
||||
secp256k1_scalar_add_bit(&s, bit + w);
|
||||
wnaf[set_bits++] = sign * (word - (1 << w));
|
||||
} else {
|
||||
wnaf[set_bits++] = sign * word;
|
||||
}
|
||||
bit += now;
|
||||
}
|
||||
return set_bits;
|
||||
}
|
||||
|
||||
static void secp256k1_ecmult(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng) {
|
||||
secp256k1_gej_t tmpj;
|
||||
secp256k1_gej_t pre_a[ECMULT_TABLE_SIZE(WINDOW_A)];
|
||||
secp256k1_ge_t tmpa;
|
||||
const secp256k1_ecmult_consts_t *c = secp256k1_ecmult_consts;
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_gej_t pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
|
||||
secp256k1_scalar_t na_1, na_lam;
|
||||
/* Splitted G factors. */
|
||||
secp256k1_scalar_t ng_1, ng_128;
|
||||
int wnaf_na_1[130];
|
||||
int wnaf_na_lam[130];
|
||||
int bits_na_1;
|
||||
int bits_na_lam;
|
||||
int wnaf_ng_1[129];
|
||||
int bits_ng_1;
|
||||
int wnaf_ng_128[129];
|
||||
int bits_ng_128;
|
||||
#else
|
||||
int wnaf_na[256];
|
||||
int bits_na;
|
||||
int wnaf_ng[257];
|
||||
int bits_ng;
|
||||
#endif
|
||||
int i;
|
||||
int bits;
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
/* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */
|
||||
secp256k1_scalar_split_lambda_var(&na_1, &na_lam, na);
|
||||
|
||||
/* build wnaf representation for na_1 and na_lam. */
|
||||
bits_na_1 = secp256k1_ecmult_wnaf(wnaf_na_1, &na_1, WINDOW_A);
|
||||
bits_na_lam = secp256k1_ecmult_wnaf(wnaf_na_lam, &na_lam, WINDOW_A);
|
||||
VERIFY_CHECK(bits_na_1 <= 130);
|
||||
VERIFY_CHECK(bits_na_lam <= 130);
|
||||
bits = bits_na_1;
|
||||
if (bits_na_lam > bits) {
|
||||
bits = bits_na_lam;
|
||||
}
|
||||
#else
|
||||
/* build wnaf representation for na. */
|
||||
bits_na = secp256k1_ecmult_wnaf(wnaf_na, na, WINDOW_A);
|
||||
bits = bits_na;
|
||||
#endif
|
||||
|
||||
/* calculate odd multiples of a */
|
||||
secp256k1_ecmult_table_precomp_gej_var(pre_a, a, WINDOW_A);
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
|
||||
secp256k1_gej_mul_lambda(&pre_a_lam[i], &pre_a[i]);
|
||||
}
|
||||
|
||||
/* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */
|
||||
secp256k1_scalar_split_128(&ng_1, &ng_128, ng);
|
||||
|
||||
/* Build wnaf representation for ng_1 and ng_128 */
|
||||
bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, &ng_1, WINDOW_G);
|
||||
bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, &ng_128, WINDOW_G);
|
||||
if (bits_ng_1 > bits) {
|
||||
bits = bits_ng_1;
|
||||
}
|
||||
if (bits_ng_128 > bits) {
|
||||
bits = bits_ng_128;
|
||||
}
|
||||
#else
|
||||
bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, ng, WINDOW_G);
|
||||
if (bits_ng > bits) {
|
||||
bits = bits_ng;
|
||||
}
|
||||
#endif
|
||||
|
||||
secp256k1_gej_set_infinity(r);
|
||||
|
||||
for (i = bits-1; i >= 0; i--) {
|
||||
int n;
|
||||
secp256k1_gej_double_var(r, r);
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
if (i < bits_na_1 && (n = wnaf_na_1[i])) {
|
||||
ECMULT_TABLE_GET_GEJ(&tmpj, pre_a, n, WINDOW_A);
|
||||
secp256k1_gej_add_var(r, r, &tmpj);
|
||||
}
|
||||
if (i < bits_na_lam && (n = wnaf_na_lam[i])) {
|
||||
ECMULT_TABLE_GET_GEJ(&tmpj, pre_a_lam, n, WINDOW_A);
|
||||
secp256k1_gej_add_var(r, r, &tmpj);
|
||||
}
|
||||
if (i < bits_ng_1 && (n = wnaf_ng_1[i])) {
|
||||
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, c->pre_g, n, WINDOW_G);
|
||||
secp256k1_gej_add_ge_var(r, r, &tmpa);
|
||||
}
|
||||
if (i < bits_ng_128 && (n = wnaf_ng_128[i])) {
|
||||
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, c->pre_g_128, n, WINDOW_G);
|
||||
secp256k1_gej_add_ge_var(r, r, &tmpa);
|
||||
}
|
||||
#else
|
||||
if (i < bits_na && (n = wnaf_na[i])) {
|
||||
ECMULT_TABLE_GET_GEJ(&tmpj, pre_a, n, WINDOW_A);
|
||||
secp256k1_gej_add_var(r, r, &tmpj);
|
||||
}
|
||||
if (i < bits_ng && (n = wnaf_ng[i])) {
|
||||
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, c->pre_g, n, WINDOW_G);
|
||||
secp256k1_gej_add_ge_var(r, r, &tmpa);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
116
crypto/secp256k1/secp256k1/src/field.h
Normal file
116
crypto/secp256k1/secp256k1/src/field.h
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_FIELD_
|
||||
#define _SECP256K1_FIELD_
|
||||
|
||||
/** Field element module.
|
||||
*
|
||||
* Field elements can be represented in several ways, but code accessing
|
||||
* it (and implementations) need to take certain properaties 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
|
||||
* always have a magnitude of 1, but a magnitude of 1 doesn't imply
|
||||
* normality.
|
||||
*/
|
||||
|
||||
#if defined HAVE_CONFIG_H
|
||||
#include "libsecp256k1-config.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_FIELD_10X26)
|
||||
#include "field_10x26.h"
|
||||
#elif defined(USE_FIELD_5X52)
|
||||
#include "field_5x52.h"
|
||||
#else
|
||||
#error "Please select field implementation"
|
||||
#endif
|
||||
|
||||
/** Normalize a field element. */
|
||||
static void secp256k1_fe_normalize(secp256k1_fe_t *r);
|
||||
|
||||
/** Weakly normalize a field element: reduce it magnitude to 1, but don't fully normalize. */
|
||||
static void secp256k1_fe_normalize_weak(secp256k1_fe_t *r);
|
||||
|
||||
/** Normalize a field element, without constant-time guarantee. */
|
||||
static void secp256k1_fe_normalize_var(secp256k1_fe_t *r);
|
||||
|
||||
/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field
|
||||
* implementation may optionally normalize the input, but this should not be relied upon. */
|
||||
static int secp256k1_fe_normalizes_to_zero(secp256k1_fe_t *r);
|
||||
|
||||
/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field
|
||||
* implementation may optionally normalize the input, but this should not be relied upon. */
|
||||
static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe_t *r);
|
||||
|
||||
/** Set a field element equal to a small integer. Resulting field element is normalized. */
|
||||
static void secp256k1_fe_set_int(secp256k1_fe_t *r, int a);
|
||||
|
||||
/** Verify whether a field element is zero. Requires the input to be normalized. */
|
||||
static int secp256k1_fe_is_zero(const secp256k1_fe_t *a);
|
||||
|
||||
/** Check the "oddness" of a field element. Requires the input to be normalized. */
|
||||
static int secp256k1_fe_is_odd(const secp256k1_fe_t *a);
|
||||
|
||||
/** Compare two field elements. Requires magnitude-1 inputs. */
|
||||
static int secp256k1_fe_equal_var(const secp256k1_fe_t *a, const secp256k1_fe_t *b);
|
||||
|
||||
/** Compare two field elements. Requires both inputs to be normalized */
|
||||
static int secp256k1_fe_cmp_var(const secp256k1_fe_t *a, const secp256k1_fe_t *b);
|
||||
|
||||
/** Set a field element equal to 32-byte big endian value. If succesful, the resulting field element is normalized. */
|
||||
static int secp256k1_fe_set_b32(secp256k1_fe_t *r, const unsigned char *a);
|
||||
|
||||
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
|
||||
static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe_t *a);
|
||||
|
||||
/** Set a field element equal to the additive inverse of another. Takes a maximum magnitude of the input
|
||||
* as an argument. The magnitude of the output is one higher. */
|
||||
static void secp256k1_fe_negate(secp256k1_fe_t *r, const secp256k1_fe_t *a, int m);
|
||||
|
||||
/** Multiplies the passed field element with a small integer constant. Multiplies the magnitude by that
|
||||
* small integer. */
|
||||
static void secp256k1_fe_mul_int(secp256k1_fe_t *r, int a);
|
||||
|
||||
/** Adds a field element to another. The result has the sum of the inputs' magnitudes as magnitude. */
|
||||
static void secp256k1_fe_add(secp256k1_fe_t *r, const secp256k1_fe_t *a);
|
||||
|
||||
/** Sets a field element to be the product of two others. Requires the inputs' magnitudes to be at most 8.
|
||||
* The output magnitude is 1 (but not guaranteed to be normalized). */
|
||||
static void secp256k1_fe_mul(secp256k1_fe_t *r, const secp256k1_fe_t *a, const secp256k1_fe_t * SECP256K1_RESTRICT b);
|
||||
|
||||
/** Sets a field element to be the square of another. Requires the input's magnitude to be at most 8.
|
||||
* The output magnitude is 1 (but not guaranteed to be normalized). */
|
||||
static void secp256k1_fe_sqr(secp256k1_fe_t *r, const secp256k1_fe_t *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_t *r, const secp256k1_fe_t *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). */
|
||||
static void secp256k1_fe_inv(secp256k1_fe_t *r, const secp256k1_fe_t *a);
|
||||
|
||||
/** Potentially faster version of secp256k1_fe_inv, without constant-time guarantee. */
|
||||
static void secp256k1_fe_inv_var(secp256k1_fe_t *r, const secp256k1_fe_t *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_t *r, const secp256k1_fe_t *a);
|
||||
|
||||
/** Convert a field element to the storage type. */
|
||||
static void secp256k1_fe_to_storage(secp256k1_fe_storage_t *r, const secp256k1_fe_t*);
|
||||
|
||||
/** Convert a field element back from the storage type. */
|
||||
static void secp256k1_fe_from_storage(secp256k1_fe_t *r, const secp256k1_fe_storage_t*);
|
||||
|
||||
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */
|
||||
static void secp256k1_fe_storage_cmov(secp256k1_fe_storage_t *r, const secp256k1_fe_storage_t *a, int flag);
|
||||
|
||||
#endif
|
||||
47
crypto/secp256k1/secp256k1/src/field_10x26.h
Normal file
47
crypto/secp256k1/secp256k1/src/field_10x26.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_FIELD_REPR_
|
||||
#define _SECP256K1_FIELD_REPR_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
/* X = sum(i=0..9, elem[i]*2^26) mod n */
|
||||
uint32_t n[10];
|
||||
#ifdef VERIFY
|
||||
int magnitude;
|
||||
int normalized;
|
||||
#endif
|
||||
} secp256k1_fe_t;
|
||||
|
||||
/* Unpacks a constant into a overlapping multi-limbed FE element. */
|
||||
#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \
|
||||
(d0) & 0x3FFFFFFUL, \
|
||||
((d0) >> 26) | ((d1) & 0xFFFFFUL) << 6, \
|
||||
((d1) >> 20) | ((d2) & 0x3FFFUL) << 12, \
|
||||
((d2) >> 14) | ((d3) & 0xFFUL) << 18, \
|
||||
((d3) >> 8) | ((d4) & 0x3) << 24, \
|
||||
((d4) >> 2) & 0x3FFFFFFUL, \
|
||||
((d4) >> 28) | ((d5) & 0x3FFFFFUL) << 4, \
|
||||
((d5) >> 22) | ((d6) & 0xFFFF) << 10, \
|
||||
((d6) >> 16) | ((d7) & 0x3FF) << 16, \
|
||||
((d7) >> 10) \
|
||||
}
|
||||
|
||||
#ifdef VERIFY
|
||||
#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1}
|
||||
#else
|
||||
#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))}
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t n[8];
|
||||
} secp256k1_fe_storage_t;
|
||||
|
||||
#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}
|
||||
|
||||
#endif
|
||||
1116
crypto/secp256k1/secp256k1/src/field_10x26_impl.h
Normal file
1116
crypto/secp256k1/secp256k1/src/field_10x26_impl.h
Normal file
File diff suppressed because it is too large
Load diff
47
crypto/secp256k1/secp256k1/src/field_5x52.h
Normal file
47
crypto/secp256k1/secp256k1/src/field_5x52.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_FIELD_REPR_
|
||||
#define _SECP256K1_FIELD_REPR_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
/* X = sum(i=0..4, elem[i]*2^52) mod n */
|
||||
uint64_t n[5];
|
||||
#ifdef VERIFY
|
||||
int magnitude;
|
||||
int normalized;
|
||||
#endif
|
||||
} secp256k1_fe_t;
|
||||
|
||||
/* Unpacks a constant into a overlapping multi-limbed FE element. */
|
||||
#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \
|
||||
(d0) | ((uint64_t)(d1) & 0xFFFFFUL) << 32, \
|
||||
((d1) >> 20) | ((uint64_t)(d2)) << 12 | ((uint64_t)(d3) & 0xFFUL) << 44, \
|
||||
((d3) >> 8) | ((uint64_t)(d4) & 0xFFFFFFFUL) << 24, \
|
||||
((d4) >> 28) | ((uint64_t)(d5)) << 4 | ((uint64_t)(d6) & 0xFFFFUL) << 36, \
|
||||
((d6) >> 16) | ((uint64_t)(d7)) << 16 \
|
||||
}
|
||||
|
||||
#ifdef VERIFY
|
||||
#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1}
|
||||
#else
|
||||
#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))}
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint64_t n[4];
|
||||
} secp256k1_fe_storage_t;
|
||||
|
||||
#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ \
|
||||
(d0) | ((uint64_t)(d1)) << 32, \
|
||||
(d2) | ((uint64_t)(d3)) << 32, \
|
||||
(d4) | ((uint64_t)(d5)) << 32, \
|
||||
(d6) | ((uint64_t)(d7)) << 32 \
|
||||
}}
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue