|
|
@ -1,12 +1,10 @@ |
|
|
|
|
|
|
|
// eXtended ETHereum
|
|
|
|
package xeth |
|
|
|
package xeth |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
|
|
* eXtended ETHereum |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"bytes" |
|
|
|
"bytes" |
|
|
|
"encoding/json" |
|
|
|
"encoding/json" |
|
|
|
|
|
|
|
"fmt" |
|
|
|
"math/big" |
|
|
|
"math/big" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/accounts" |
|
|
|
"github.com/ethereum/go-ethereum/accounts" |
|
|
@ -19,7 +17,6 @@ import ( |
|
|
|
"github.com/ethereum/go-ethereum/miner" |
|
|
|
"github.com/ethereum/go-ethereum/miner" |
|
|
|
"github.com/ethereum/go-ethereum/p2p" |
|
|
|
"github.com/ethereum/go-ethereum/p2p" |
|
|
|
"github.com/ethereum/go-ethereum/state" |
|
|
|
"github.com/ethereum/go-ethereum/state" |
|
|
|
"github.com/ethereum/go-ethereum/ui" |
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/whisper" |
|
|
|
"github.com/ethereum/go-ethereum/whisper" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
@ -41,6 +38,26 @@ type Backend interface { |
|
|
|
Miner() *miner.Miner |
|
|
|
Miner() *miner.Miner |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Frontend should be implemented by users of XEth. Its methods are
|
|
|
|
|
|
|
|
// called whenever XEth makes a decision that requires user input.
|
|
|
|
|
|
|
|
type Frontend interface { |
|
|
|
|
|
|
|
// UnlockAccount is called when a transaction needs to be signed
|
|
|
|
|
|
|
|
// but the key corresponding to the transaction's sender is
|
|
|
|
|
|
|
|
// locked.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// It should unlock the account with the given address and return
|
|
|
|
|
|
|
|
// true if unlocking succeeded.
|
|
|
|
|
|
|
|
UnlockAccount(address []byte) bool |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This is called for all transactions inititated through
|
|
|
|
|
|
|
|
// Transact. It should prompt the user to confirm the transaction
|
|
|
|
|
|
|
|
// and return true if the transaction was acknowledged.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// ConfirmTransaction is not used for Call transactions
|
|
|
|
|
|
|
|
// because they cannot change any state.
|
|
|
|
|
|
|
|
ConfirmTransaction(tx *types.Transaction) bool |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type XEth struct { |
|
|
|
type XEth struct { |
|
|
|
eth Backend |
|
|
|
eth Backend |
|
|
|
blockProcessor *core.BlockProcessor |
|
|
|
blockProcessor *core.BlockProcessor |
|
|
@ -50,15 +67,20 @@ type XEth struct { |
|
|
|
whisper *Whisper |
|
|
|
whisper *Whisper |
|
|
|
miner *miner.Miner |
|
|
|
miner *miner.Miner |
|
|
|
|
|
|
|
|
|
|
|
frontend ui.Interface |
|
|
|
frontend Frontend |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type TmpFrontend struct{} |
|
|
|
// dummyFrontend is a non-interactive frontend that allows all
|
|
|
|
|
|
|
|
// transactions but cannot not unlock any keys.
|
|
|
|
|
|
|
|
type dummyFrontend struct{} |
|
|
|
|
|
|
|
|
|
|
|
func (TmpFrontend) UnlockAccount([]byte) bool { panic("UNLOCK ACCOUNT") } |
|
|
|
func (dummyFrontend) UnlockAccount([]byte) bool { return false } |
|
|
|
func (TmpFrontend) ConfirmTransaction(*types.Transaction) bool { panic("CONFIRM TRANSACTION") } |
|
|
|
func (dummyFrontend) ConfirmTransaction(*types.Transaction) bool { return true } |
|
|
|
|
|
|
|
|
|
|
|
func New(eth Backend, frontend ui.Interface) *XEth { |
|
|
|
// New creates an XEth that uses the given frontend.
|
|
|
|
|
|
|
|
// If a nil Frontend is provided, a default frontend which
|
|
|
|
|
|
|
|
// confirms all transactions will be used.
|
|
|
|
|
|
|
|
func New(eth Backend, frontend Frontend) *XEth { |
|
|
|
xeth := &XEth{ |
|
|
|
xeth := &XEth{ |
|
|
|
eth: eth, |
|
|
|
eth: eth, |
|
|
|
blockProcessor: eth.BlockProcessor(), |
|
|
|
blockProcessor: eth.BlockProcessor(), |
|
|
@ -66,14 +88,12 @@ func New(eth Backend, frontend ui.Interface) *XEth { |
|
|
|
accountManager: eth.AccountManager(), |
|
|
|
accountManager: eth.AccountManager(), |
|
|
|
whisper: NewWhisper(eth.Whisper()), |
|
|
|
whisper: NewWhisper(eth.Whisper()), |
|
|
|
miner: eth.Miner(), |
|
|
|
miner: eth.Miner(), |
|
|
|
|
|
|
|
frontend: frontend, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if frontend == nil { |
|
|
|
if frontend == nil { |
|
|
|
xeth.frontend = TmpFrontend{} |
|
|
|
xeth.frontend = dummyFrontend{} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
xeth.state = NewState(xeth, xeth.chainManager.TransState()) |
|
|
|
xeth.state = NewState(xeth, xeth.chainManager.TransState()) |
|
|
|
|
|
|
|
|
|
|
|
return xeth |
|
|
|
return xeth |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -283,7 +303,6 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { |
|
|
|
func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { |
|
|
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
var ( |
|
|
|
from []byte |
|
|
|
from []byte |
|
|
|
to []byte |
|
|
|
to []byte |
|
|
@ -310,16 +329,12 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt |
|
|
|
|
|
|
|
|
|
|
|
state := self.chainManager.TransState() |
|
|
|
state := self.chainManager.TransState() |
|
|
|
nonce := state.GetNonce(from) |
|
|
|
nonce := state.GetNonce(from) |
|
|
|
|
|
|
|
|
|
|
|
tx.SetNonce(nonce) |
|
|
|
tx.SetNonce(nonce) |
|
|
|
sig, err := self.accountManager.Sign(accounts.Account{Address: from}, tx.Hash()) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
if err := self.sign(tx, from, false); err != nil { |
|
|
|
return "", err |
|
|
|
return "", err |
|
|
|
} |
|
|
|
} |
|
|
|
tx.SetSignatureValues(sig) |
|
|
|
if err := self.eth.TxPool().Add(tx); err != nil { |
|
|
|
|
|
|
|
|
|
|
|
err = self.eth.TxPool().Add(tx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return "", err |
|
|
|
return "", err |
|
|
|
} |
|
|
|
} |
|
|
|
state.SetNonce(from, nonce+1) |
|
|
|
state.SetNonce(from, nonce+1) |
|
|
@ -332,10 +347,27 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt |
|
|
|
if types.IsContractAddr(to) { |
|
|
|
if types.IsContractAddr(to) { |
|
|
|
return toHex(core.AddressFromMessage(tx)), nil |
|
|
|
return toHex(core.AddressFromMessage(tx)), nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return toHex(tx.Hash()), nil |
|
|
|
return toHex(tx.Hash()), nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (self *XEth) sign(tx *types.Transaction, from []byte, didUnlock bool) error { |
|
|
|
|
|
|
|
sig, err := self.accountManager.Sign(accounts.Account{Address: from}, tx.Hash()) |
|
|
|
|
|
|
|
if err == accounts.ErrLocked { |
|
|
|
|
|
|
|
if didUnlock { |
|
|
|
|
|
|
|
return fmt.Errorf("sender account still locked after successful unlock") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if !self.frontend.UnlockAccount(from) { |
|
|
|
|
|
|
|
return fmt.Errorf("could not unlock sender account") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// retry signing, the account should now be unlocked.
|
|
|
|
|
|
|
|
self.sign(tx, from, true) |
|
|
|
|
|
|
|
} else if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
tx.SetSignatureValues(sig) |
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// callmsg is the message type used for call transations.
|
|
|
|
// callmsg is the message type used for call transations.
|
|
|
|
type callmsg struct { |
|
|
|
type callmsg struct { |
|
|
|
from *state.StateObject |
|
|
|
from *state.StateObject |
|
|
|