Skip to content

Commit

Permalink
Merge pull request #14 from expanse-project/rebase-1.4.4
Browse files Browse the repository at this point in the history
Rebase 1.4.5
  • Loading branch information
chrisfranko committed May 26, 2016
2 parents bb79e8c + 40b4de5 commit 6162f05
Show file tree
Hide file tree
Showing 17 changed files with 563 additions and 178 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.4.4
1.4.5
4 changes: 2 additions & 2 deletions accounts/abi/abi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,10 @@ func TestUnpackSetInterfaceSlice(t *testing.T) {
t.Fatal(err)
}
if *var1 != 1 {
t.Errorf("expected var1 to be 1, got", *var1)
t.Error("expected var1 to be 1, got", *var1)
}
if *var2 != 2 {
t.Errorf("expected var2 to be 2, got", *var2)
t.Error("expected var2 to be 2, got", *var2)
}

out = []interface{}{var1}
Expand Down
49 changes: 43 additions & 6 deletions accounts/abi/bind/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,16 @@ import (
// ErrNoCode is returned by call and transact operations for which the requested
// recipient contract to operate on does not exist in the state db or does not
// have any code associated with it (i.e. suicided).
//
// Please note, this error string is part of the RPC API and is expected by the
// native contract bindings to signal this particular error. Do not change this
// as it will break all dependent code!
var ErrNoCode = errors.New("no contract code at given address")

// ContractCaller defines the methods needed to allow operating with contract on a read
// only basis.
type ContractCaller interface {
// HasCode checks if the contract at the given address has any code associated
// with it or not. This is needed to differentiate between contract internal
// errors and the local chain being out of sync.
HasCode(contract common.Address, pending bool) (bool, error)

// ContractCall executes an Expanse contract call with the specified data as
// the input. The pending flag requests execution against the pending block, not
// the stable head of the chain.
Expand All @@ -55,6 +56,11 @@ type ContractTransactor interface {
// execution of a transaction.
SuggestGasPrice() (*big.Int, error)

// HasCode checks if the contract at the given address has any code associated
// with it or not. This is needed to differentiate between contract internal
// errors and the local chain being out of sync.
HasCode(contract common.Address, pending bool) (bool, error)

// EstimateGasLimit tries to estimate the gas needed to execute a specific
// transaction based on the current pending state of the backend blockchain.
// There is no guarantee that this is the true gas limit requirement as other
Expand All @@ -68,7 +74,38 @@ type ContractTransactor interface {

// ContractBackend defines the methods needed to allow operating with contract
// on a read-write basis.
//
// This interface is essentially the union of ContractCaller and ContractTransactor
// but due to a bug in the Go compiler (https://github.com/golang/go/issues/6977),
// we cannot simply list it as the two interfaces. The other solution is to add a
// third interface containing the common methods, but that convolutes the user API
// as it introduces yet another parameter to require for initialization.
type ContractBackend interface {
ContractCaller
ContractTransactor
// HasCode checks if the contract at the given address has any code associated
// with it or not. This is needed to differentiate between contract internal
// errors and the local chain being out of sync.
HasCode(contract common.Address, pending bool) (bool, error)

// ContractCall executes an Ethereum contract call with the specified data as
// the input. The pending flag requests execution against the pending block, not
// the stable head of the chain.
ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error)

// PendingAccountNonce retrieves the current pending nonce associated with an
// account.
PendingAccountNonce(account common.Address) (uint64, error)

// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
// execution of a transaction.
SuggestGasPrice() (*big.Int, error)

// EstimateGasLimit tries to estimate the gas needed to execute a specific
// transaction based on the current pending state of the backend blockchain.
// There is no guarantee that this is the true gas limit requirement as other
// transactions may be added or removed by miners, but it should provide a basis
// for setting a reasonable default.
EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error)

// SendTransaction injects the transaction into the pending pool for execution.
SendTransaction(tx *types.Transaction) error
}
1 change: 1 addition & 0 deletions accounts/abi/bind/backends/nil.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func (*nilBackend) ContractCall(common.Address, []byte, bool) ([]byte, error) {
func (*nilBackend) EstimateGasLimit(common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) {
panic("not implemented")
}
func (*nilBackend) HasCode(common.Address, bool) (bool, error) { panic("not implemented") }
func (*nilBackend) SuggestGasPrice() (*big.Int, error) { panic("not implemented") }
func (*nilBackend) PendingAccountNonce(common.Address) (uint64, error) { panic("not implemented") }
func (*nilBackend) SendTransaction(*types.Transaction) error { panic("not implemented") }
Expand Down
20 changes: 20 additions & 0 deletions accounts/abi/bind/backends/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,26 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa
return res.Result, nil
}

// HasCode implements ContractVerifier.HasCode by retrieving any code associated
// with the contract from the remote node, and checking its size.
func (b *rpcBackend) HasCode(contract common.Address, pending bool) (bool, error) {
// Execute the RPC code retrieval
block := "latest"
if pending {
block = "pending"
}
res, err := b.request("eth_getCode", []interface{}{contract.Hex(), block})
if err != nil {
return false, err
}
var hex string
if err := json.Unmarshal(res, &hex); err != nil {
return false, err
}
// Convert the response back to a Go byte slice and return
return len(common.FromHex(hex)) > 0, nil
}

// ContractCall implements ContractCaller.ContractCall, delegating the execution of
// a contract call to the remote node, returning the reply to for local processing.
func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) {
Expand Down
10 changes: 10 additions & 0 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ func (b *SimulatedBackend) Rollback() {
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
}

// HasCode implements ContractVerifier.HasCode, checking whether there is any
// code associated with a certain account in the blockchain.
func (b *SimulatedBackend) HasCode(contract common.Address, pending bool) (bool, error) {
if pending {
return len(b.pendingState.GetCode(contract)) > 0, nil
}
statedb, _ := b.blockchain.State()
return len(statedb.GetCode(contract)) > 0, nil
}

// ContractCall implements ContractCaller.ContractCall, executing the specified
// contract with the given input data.
func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) {
Expand Down
27 changes: 27 additions & 0 deletions accounts/abi/bind/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"math/big"
"sync/atomic"

"github.com/expanse-project/go-expanse/accounts/abi"
"github.com/expanse-project/go-expanse/common"
Expand Down Expand Up @@ -56,6 +57,9 @@ type BoundContract struct {
abi abi.ABI // Reflect based ABI to access the correct Expanse methods
caller ContractCaller // Read interface to interact with the blockchain
transactor ContractTransactor // Write interface to interact with the blockchain

latestHasCode uint32 // Cached verification that the latest state contains code for this contract
pendingHasCode uint32 // Cached verification that the pending state contains code for this contract
}

// NewBoundContract creates a low level contract interface through which calls
Expand Down Expand Up @@ -96,6 +100,19 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
if opts == nil {
opts = new(CallOpts)
}
// Make sure we have a contract to operate on, and bail out otherwise
if (opts.Pending && atomic.LoadUint32(&c.pendingHasCode) == 0) || (!opts.Pending && atomic.LoadUint32(&c.latestHasCode) == 0) {
if code, err := c.caller.HasCode(c.address, opts.Pending); err != nil {
return err
} else if !code {
return ErrNoCode
}
if opts.Pending {
atomic.StoreUint32(&c.pendingHasCode, 1)
} else {
atomic.StoreUint32(&c.latestHasCode, 1)
}
}
// Pack the input, call and unpack the results
input, err := c.abi.Pack(method, params...)
if err != nil {
Expand Down Expand Up @@ -153,6 +170,16 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
}
gasLimit := opts.GasLimit
if gasLimit == nil {
// Gas estimation cannot succeed without code for method invocations
if contract != nil && atomic.LoadUint32(&c.pendingHasCode) == 0 {
if code, err := c.transactor.HasCode(c.address, true); err != nil {
return nil, err
} else if !code {
return nil, ErrNoCode
}
atomic.StoreUint32(&c.pendingHasCode, 1)
}
// If the contract surely has code (or code is not needed), estimate the transaction
gasLimit, err = c.transactor.EstimateGasLimit(opts.From, contract, value, input)
if err != nil {
return nil, fmt.Errorf("failed to exstimate gas needed: %v", err)
Expand Down
16 changes: 14 additions & 2 deletions accounts/account_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,21 @@ func (am *Manager) Sign(addr common.Address, hash []byte) (signature []byte, err
return crypto.Sign(hash, unlockedKey.PrivateKey)
}

// SignWithPassphrase signs hash if the private key matching the given address can be
// decrypted with the given passphrase.
func (am *Manager) SignWithPassphrase(addr common.Address, passphrase string, hash []byte) (signature []byte, err error) {
_, key, err := am.getDecryptedKey(Account{Address: addr}, passphrase)
if err != nil {
return nil, err
}

defer zeroKey(key.PrivateKey)
return crypto.Sign(hash, key.PrivateKey)
}

// Unlock unlocks the given account indefinitely.
func (am *Manager) Unlock(a Account, keyAuth string) error {
return am.TimedUnlock(a, keyAuth, 0)
func (am *Manager) Unlock(a Account, passphrase string) error {
return am.TimedUnlock(a, passphrase, 0)
}

// Lock removes the private key with the given address from memory.
Expand Down
28 changes: 28 additions & 0 deletions accounts/accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,34 @@ func TestSign(t *testing.T) {
}
}

func TestSignWithPassphrase(t *testing.T) {
dir, am := tmpManager(t, true)
defer os.RemoveAll(dir)

pass := "passwd"
acc, err := am.NewAccount(pass)
if err != nil {
t.Fatal(err)
}

if _, unlocked := am.unlocked[acc.Address]; unlocked {
t.Fatal("expected account to be locked")
}

_, err = am.SignWithPassphrase(acc.Address, pass, testSigData)
if err != nil {
t.Fatal(err)
}

if _, unlocked := am.unlocked[acc.Address]; unlocked {
t.Fatal("expected account to be locked")
}

if _, err = am.SignWithPassphrase(acc.Address, "invalid passwd", testSigData); err == nil {
t.Fatal("expected SignHash to fail with invalid password")
}
}

func TestTimedUnlock(t *testing.T) {
dir, am := tmpManager(t, true)
defer os.RemoveAll(dir)
Expand Down
11 changes: 4 additions & 7 deletions cmd/gexp/js.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ import (
)

var (
passwordRegexp = regexp.MustCompile("personal.[nu]")
leadingSpace = regexp.MustCompile("^ ")
passwordRegexp = regexp.MustCompile("personal.[nus]")
onlyws = regexp.MustCompile("^\\s*$")
exit = regexp.MustCompile("^\\s*exit\\s*;*\\s*$")
)
Expand Down Expand Up @@ -362,7 +361,7 @@ func (self *jsre) interactive() {
str += input + "\n"
self.setIndent()
if indentCount <= 0 {
if mustLogInHistory(str) {
if !excludeFromHistory(str) {
utils.Stdin.AppendHistory(str[:len(str)-1])
}
self.parseInput(str)
Expand All @@ -372,10 +371,8 @@ func (self *jsre) interactive() {
}
}

func mustLogInHistory(input string) bool {
return len(input) == 0 ||
passwordRegexp.MatchString(input) ||
!leadingSpace.MatchString(input)
func excludeFromHistory(input string) bool {
return len(input) == 0 || input[0] == ' ' || passwordRegexp.MatchString(input)
}


Expand Down
2 changes: 1 addition & 1 deletion cmd/gexp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const (
clientIdentifier = "Gexp" // Client identifier to advertise over the network
versionMajor = 1 // Major version component of the current release
versionMinor = 4 // Minor version component of the current release
versionPatch = 4 // Patch version component of the current release
versionPatch = 5 // Patch version component of the current release
versionMeta = "stable" // Version metadata to append to the version string
versionOracle = "0x926d69cc3bbf81d52cba6886d788df007a15a3cd" // Expanse address of the Gexp release oracle
)
Expand Down

0 comments on commit 6162f05

Please sign in to comment.