2016-09-11 11:44:14 +00:00
|
|
|
package geth
|
|
|
|
|
|
|
|
import (
|
2016-10-07 14:48:36 +00:00
|
|
|
"context"
|
2016-09-11 11:44:14 +00:00
|
|
|
"encoding/json"
|
2016-10-13 18:02:48 +00:00
|
|
|
"math/big"
|
|
|
|
"strconv"
|
2016-09-11 11:44:14 +00:00
|
|
|
|
2017-02-27 12:52:10 +00:00
|
|
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
2016-09-11 11:44:14 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2017-02-20 09:44:38 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
2016-09-11 11:44:14 +00:00
|
|
|
"github.com/ethereum/go-ethereum/les/status"
|
2016-10-07 14:48:36 +00:00
|
|
|
"github.com/robertkrimen/otto"
|
2016-09-11 11:44:14 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
EventTransactionQueued = "transaction.queued"
|
2016-10-26 02:51:33 +00:00
|
|
|
EventTransactionFailed = "transaction.failed"
|
2016-10-07 14:48:36 +00:00
|
|
|
SendTransactionRequest = "eth_sendTransaction"
|
|
|
|
MessageIdKey = "message_id"
|
2016-10-26 02:51:33 +00:00
|
|
|
|
|
|
|
// tx error codes
|
2016-10-30 22:35:10 +00:00
|
|
|
SendTransactionNoErrorCode = "0"
|
|
|
|
SendTransactionDefaultErrorCode = "1"
|
|
|
|
SendTransactionPasswordErrorCode = "2"
|
|
|
|
SendTransactionTimeoutErrorCode = "3"
|
|
|
|
SendTransactionDiscardedErrorCode = "4"
|
2016-09-11 11:44:14 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func onSendTransactionRequest(queuedTx status.QueuedTx) {
|
2016-12-18 20:36:17 +00:00
|
|
|
SendSignal(SignalEnvelope{
|
2016-09-11 11:44:14 +00:00
|
|
|
Type: EventTransactionQueued,
|
|
|
|
Event: SendTransactionEvent{
|
2016-10-07 14:48:36 +00:00
|
|
|
Id: string(queuedTx.Id),
|
|
|
|
Args: queuedTx.Args,
|
2016-10-13 18:02:48 +00:00
|
|
|
MessageId: messageIdFromContext(queuedTx.Context),
|
2016-09-11 11:44:14 +00:00
|
|
|
},
|
2016-12-18 20:36:17 +00:00
|
|
|
})
|
2016-09-11 11:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-10-30 22:35:10 +00:00
|
|
|
func onSendTransactionReturn(queuedTx *status.QueuedTx, err error) {
|
2016-10-26 02:51:33 +00:00
|
|
|
if err == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-10-30 22:35:10 +00:00
|
|
|
// discard notifications with empty tx
|
|
|
|
if queuedTx == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-10-26 02:51:33 +00:00
|
|
|
// error occurred, signal up to application
|
2016-12-18 20:36:17 +00:00
|
|
|
SendSignal(SignalEnvelope{
|
2016-10-26 02:51:33 +00:00
|
|
|
Type: EventTransactionFailed,
|
|
|
|
Event: ReturnSendTransactionEvent{
|
|
|
|
Id: string(queuedTx.Id),
|
|
|
|
Args: queuedTx.Args,
|
|
|
|
MessageId: messageIdFromContext(queuedTx.Context),
|
|
|
|
ErrorMessage: err.Error(),
|
|
|
|
ErrorCode: sendTransactionErrorCode(err),
|
|
|
|
},
|
2016-12-18 20:36:17 +00:00
|
|
|
})
|
2016-10-26 02:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func sendTransactionErrorCode(err error) string {
|
|
|
|
if err == nil {
|
|
|
|
return SendTransactionNoErrorCode
|
|
|
|
}
|
|
|
|
|
2016-10-30 22:35:10 +00:00
|
|
|
switch err {
|
2017-02-27 12:52:10 +00:00
|
|
|
case keystore.ErrDecrypt:
|
2016-10-26 02:51:33 +00:00
|
|
|
return SendTransactionPasswordErrorCode
|
2016-10-30 22:35:10 +00:00
|
|
|
case status.ErrQueuedTxTimedOut:
|
2016-10-26 02:51:33 +00:00
|
|
|
return SendTransactionTimeoutErrorCode
|
2016-10-30 22:35:10 +00:00
|
|
|
case status.ErrQueuedTxDiscarded:
|
|
|
|
return SendTransactionDiscardedErrorCode
|
|
|
|
default:
|
|
|
|
return SendTransactionDefaultErrorCode
|
2016-10-26 02:51:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-11 11:44:14 +00:00
|
|
|
func CompleteTransaction(id, password string) (common.Hash, error) {
|
2016-12-07 21:07:08 +00:00
|
|
|
lightEthereum, err := NodeManagerInstance().LightEthereumService()
|
2016-09-11 11:44:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return common.Hash{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
backend := lightEthereum.StatusBackend
|
|
|
|
|
2016-12-15 09:49:05 +00:00
|
|
|
ctx := context.Background()
|
|
|
|
ctx = context.WithValue(ctx, status.SelectedAccountKey, NodeManagerInstance().SelectedAccount.Hex())
|
|
|
|
|
|
|
|
return backend.CompleteQueuedTransaction(ctx, status.QueuedTxId(id), password)
|
2016-09-11 11:44:14 +00:00
|
|
|
}
|
2016-10-07 14:48:36 +00:00
|
|
|
|
2016-11-05 17:12:24 +00:00
|
|
|
func CompleteTransactions(ids, password string) map[string]RawCompleteTransactionResult {
|
|
|
|
results := make(map[string]RawCompleteTransactionResult)
|
|
|
|
|
|
|
|
parsedIds, err := parseJSONArray(ids)
|
|
|
|
if err != nil {
|
|
|
|
results["none"] = RawCompleteTransactionResult{
|
|
|
|
Error: err,
|
|
|
|
}
|
|
|
|
return results
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, txId := range parsedIds {
|
|
|
|
txHash, txErr := CompleteTransaction(txId, password)
|
|
|
|
results[txId] = RawCompleteTransactionResult{
|
|
|
|
Hash: txHash,
|
|
|
|
Error: txErr,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return results
|
|
|
|
}
|
|
|
|
|
2016-10-30 22:35:10 +00:00
|
|
|
func DiscardTransaction(id string) error {
|
2016-12-07 21:07:08 +00:00
|
|
|
lightEthereum, err := NodeManagerInstance().LightEthereumService()
|
2016-10-30 22:35:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
backend := lightEthereum.StatusBackend
|
|
|
|
|
|
|
|
return backend.DiscardQueuedTransaction(status.QueuedTxId(id))
|
|
|
|
}
|
|
|
|
|
2016-11-05 17:12:24 +00:00
|
|
|
func DiscardTransactions(ids string) map[string]RawDiscardTransactionResult {
|
|
|
|
var parsedIds []string
|
|
|
|
results := make(map[string]RawDiscardTransactionResult)
|
|
|
|
|
|
|
|
parsedIds, err := parseJSONArray(ids)
|
|
|
|
if err != nil {
|
|
|
|
results["none"] = RawDiscardTransactionResult{
|
|
|
|
Error: err,
|
|
|
|
}
|
|
|
|
return results
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, txId := range parsedIds {
|
|
|
|
err := DiscardTransaction(txId)
|
|
|
|
if err != nil {
|
|
|
|
results[txId] = RawDiscardTransactionResult{
|
|
|
|
Error: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return results
|
|
|
|
}
|
|
|
|
|
2016-10-12 17:51:25 +00:00
|
|
|
func messageIdFromContext(ctx context.Context) string {
|
2016-10-07 14:48:36 +00:00
|
|
|
if ctx == nil {
|
|
|
|
return ""
|
|
|
|
}
|
2016-10-12 17:51:25 +00:00
|
|
|
if messageId, ok := ctx.Value(MessageIdKey).(string); ok {
|
2016-10-07 14:48:36 +00:00
|
|
|
return messageId
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2016-10-13 18:02:48 +00:00
|
|
|
type JailedRequestQueue struct{}
|
2016-10-07 14:48:36 +00:00
|
|
|
|
|
|
|
func NewJailedRequestsQueue() *JailedRequestQueue {
|
2016-10-13 18:02:48 +00:00
|
|
|
return &JailedRequestQueue{}
|
2016-10-07 14:48:36 +00:00
|
|
|
}
|
|
|
|
|
2016-10-13 18:02:48 +00:00
|
|
|
func (q *JailedRequestQueue) PreProcessRequest(vm *otto.Otto, req RPCCall) (string, error) {
|
2016-10-07 14:48:36 +00:00
|
|
|
messageId := currentMessageId(vm.Context())
|
|
|
|
|
2016-10-13 18:02:48 +00:00
|
|
|
return messageId, nil
|
2016-10-07 14:48:36 +00:00
|
|
|
}
|
|
|
|
|
2016-10-13 18:02:48 +00:00
|
|
|
func (q *JailedRequestQueue) PostProcessRequest(vm *otto.Otto, req RPCCall, messageId string) {
|
2016-10-07 14:48:36 +00:00
|
|
|
if len(messageId) > 0 {
|
2016-10-12 17:51:25 +00:00
|
|
|
vm.Call("addContext", nil, messageId, MessageIdKey, messageId)
|
2016-10-07 14:48:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// set extra markers for queued transaction requests
|
|
|
|
if req.Method == SendTransactionRequest {
|
2016-10-12 17:51:25 +00:00
|
|
|
vm.Call("addContext", nil, messageId, SendTransactionRequest, true)
|
2016-10-07 14:48:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-13 18:02:48 +00:00
|
|
|
func (q *JailedRequestQueue) ProcessSendTransactionRequest(vm *otto.Otto, req RPCCall) (common.Hash, error) {
|
|
|
|
// obtain status backend from LES service
|
2016-12-07 21:07:08 +00:00
|
|
|
lightEthereum, err := NodeManagerInstance().LightEthereumService()
|
2016-10-13 18:02:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return common.Hash{}, err
|
2016-10-07 14:48:36 +00:00
|
|
|
}
|
2016-10-13 18:02:48 +00:00
|
|
|
backend := lightEthereum.StatusBackend
|
2016-10-07 14:48:36 +00:00
|
|
|
|
2016-10-13 18:02:48 +00:00
|
|
|
messageId, err := q.PreProcessRequest(vm, req)
|
|
|
|
if err != nil {
|
|
|
|
return common.Hash{}, err
|
2016-10-07 14:48:36 +00:00
|
|
|
}
|
2016-10-13 18:02:48 +00:00
|
|
|
// onSendTransactionRequest() will use context to obtain and release ticket
|
|
|
|
ctx := context.Background()
|
|
|
|
ctx = context.WithValue(ctx, MessageIdKey, messageId)
|
2016-10-07 14:48:36 +00:00
|
|
|
|
2016-10-13 18:02:48 +00:00
|
|
|
// this call blocks, up until Complete Transaction is called
|
|
|
|
txHash, err := backend.SendTransaction(ctx, sendTxArgsFromRPCCall(req))
|
|
|
|
if err != nil {
|
|
|
|
return common.Hash{}, err
|
2016-10-07 14:48:36 +00:00
|
|
|
}
|
|
|
|
|
2016-10-13 18:02:48 +00:00
|
|
|
// invoke post processing
|
|
|
|
q.PostProcessRequest(vm, req, messageId)
|
2016-10-07 14:48:36 +00:00
|
|
|
|
2016-10-13 18:02:48 +00:00
|
|
|
return txHash, nil
|
2016-10-07 14:48:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// currentMessageId looks for `status.message_id` variable in current JS context
|
|
|
|
func currentMessageId(ctx otto.Context) string {
|
|
|
|
if statusObj, ok := ctx.Symbols["status"]; ok {
|
|
|
|
messageId, err := statusObj.Object().Get("message_id")
|
|
|
|
if err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
if messageId, err := messageId.ToString(); err == nil {
|
|
|
|
return messageId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2016-10-13 18:02:48 +00:00
|
|
|
func sendTxArgsFromRPCCall(req RPCCall) status.SendTxArgs {
|
2016-10-07 14:48:36 +00:00
|
|
|
if req.Method != SendTransactionRequest { // no need to persist extra state for other requests
|
2016-10-13 18:02:48 +00:00
|
|
|
return status.SendTxArgs{}
|
2016-10-07 14:48:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
params, ok := req.Params[0].(map[string]interface{})
|
|
|
|
if !ok {
|
2016-10-13 18:02:48 +00:00
|
|
|
return status.SendTxArgs{}
|
2016-10-07 14:48:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
from, ok := params["from"].(string)
|
|
|
|
if !ok {
|
|
|
|
from = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
to, ok := params["to"].(string)
|
|
|
|
if !ok {
|
|
|
|
to = ""
|
|
|
|
}
|
|
|
|
|
2016-10-13 18:02:48 +00:00
|
|
|
param, ok := params["value"].(string)
|
2016-10-07 14:48:36 +00:00
|
|
|
if !ok {
|
2016-10-13 18:02:48 +00:00
|
|
|
param = "0x0"
|
|
|
|
}
|
|
|
|
value, err := strconv.ParseInt(param, 0, 64)
|
|
|
|
if err != nil {
|
|
|
|
return status.SendTxArgs{}
|
2016-10-07 14:48:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
data, ok := params["data"].(string)
|
|
|
|
if !ok {
|
|
|
|
data = ""
|
|
|
|
}
|
|
|
|
|
2016-10-13 18:02:48 +00:00
|
|
|
toAddress := common.HexToAddress(to)
|
|
|
|
return status.SendTxArgs{
|
|
|
|
From: common.HexToAddress(from),
|
|
|
|
To: &toAddress,
|
2017-02-20 09:44:38 +00:00
|
|
|
Value: (*hexutil.Big)(big.NewInt(value)),
|
|
|
|
Data: hexutil.Bytes(data),
|
2016-10-07 14:48:36 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-05 17:12:24 +00:00
|
|
|
|
|
|
|
func parseJSONArray(items string) ([]string, error) {
|
|
|
|
var parsedItems []string
|
|
|
|
err := json.Unmarshal([]byte(items), &parsedItems)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return parsedItems, nil
|
|
|
|
}
|