CompleteTransaction() exposed + onSendTransactionRequest() notifier added
This commit is contained in:
parent
45ffd5599d
commit
92737d0395
|
@ -1,15 +1,25 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <jni.h>
|
||||
extern bool GethServiceSignalEvent( const char *jsonEvent );
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"encoding/json"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
errextra "github.com/pkg/errors"
|
||||
)
|
||||
|
@ -95,7 +105,6 @@ func createAndStartNode(inputDir string) error {
|
|||
|
||||
}
|
||||
|
||||
|
||||
func doAddPeer(url string) (bool, error) {
|
||||
server := currentNode.Server()
|
||||
if server == nil {
|
||||
|
@ -109,3 +118,29 @@ func doAddPeer(url string) (bool, error) {
|
|||
server.AddPeer(node)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func onSendTransactionRequest(queuedTx les.QueuedTx) {
|
||||
event := GethEvent{
|
||||
Type: "sendTransactionQueued",
|
||||
Event: SendTransactionEvent{
|
||||
Hash: queuedTx.Hash.Hex(),
|
||||
Args: queuedTx.Args,
|
||||
},
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(&event)
|
||||
C.GethServiceSignalEvent(C.CString(string(body)))
|
||||
}
|
||||
|
||||
func completeTransaction(hash string) error {
|
||||
if currentNode != nil {
|
||||
|
||||
if lightEthereum != nil {
|
||||
backend := lightEthereum.StatusBackend
|
||||
return backend.CompleteQueuedTransaction(les.QueuedTxHash(hash))
|
||||
}
|
||||
return errors.New("Could not retrieve light ethereum service")
|
||||
}
|
||||
|
||||
return errors.New("No running node detected for account unlock")
|
||||
}
|
||||
|
|
|
@ -6,9 +6,15 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// TestAccountBindings makes sure we can create an account and subsequently
|
||||
|
@ -37,7 +43,7 @@ func TestAccountBindings(t *testing.T) {
|
|||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// test to see if the account was injected in whisqer
|
||||
// test to see if the account was injected in whisper
|
||||
var whisperInstance *whisper.Whisper
|
||||
if err := currentNode.Service(&whisperInstance); err != nil {
|
||||
t.Errorf("whisper service not running: %v", err)
|
||||
|
@ -56,11 +62,67 @@ func TestAccountBindings(t *testing.T) {
|
|||
Payload: "test message",
|
||||
}
|
||||
whisperAPI := whisper.NewPublicWhisperAPI(whisperInstance)
|
||||
postSucess, err := whisperAPI.Post(postArgs)
|
||||
if !postSucess || err != nil {
|
||||
postSuccess, err := whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("Test failed: Could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
// create another account
|
||||
address1, _, err := createAccount("badpassword")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
t.Error("Test failed: could not create account")
|
||||
}
|
||||
|
||||
// unlock the created account
|
||||
err = unlockAccount(address1, "badpassword", 3)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.Error("Test failed: could not unlock account")
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// test transaction queueing
|
||||
var lightEthereum *les.LightEthereum
|
||||
if err := currentNode.Service(&lightEthereum); err != nil {
|
||||
t.Errorf("Test failed: LES service is not running: %v", err)
|
||||
}
|
||||
backend := lightEthereum.StatusBackend
|
||||
|
||||
// replace transaction notification hanlder
|
||||
sentinel := 0
|
||||
backend.SetTransactionQueueHandler(func(queuedTx les.QueuedTx) {
|
||||
glog.V(logger.Info).Infof("[STATUS-GO] Tx queue value: %v\n", queuedTx.Hash.Hex())
|
||||
if err := completeTransaction(queuedTx.Hash.Hex()); err != nil {
|
||||
t.Errorf("Test failed: cannot complete queued transation[%s]: %v", queuedTx.Hash.Hex(), err)
|
||||
}
|
||||
sentinel = 1
|
||||
})
|
||||
|
||||
from, err := utils.MakeAddress(accountManager, address1)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: Could not retrieve account from address: %v", err)
|
||||
}
|
||||
|
||||
to, err := utils.MakeAddress(accountManager, address)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: Could not retrieve account from address: %v", err)
|
||||
}
|
||||
|
||||
err = backend.SendTransaction(nil, les.SendTxArgs{
|
||||
From: from.Address,
|
||||
To: &to.Address,
|
||||
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: cannot send transaction: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
if sentinel != 1 {
|
||||
t.Error("Test failed: transaction was never queued or completed")
|
||||
}
|
||||
|
||||
// clean up
|
||||
err = os.RemoveAll(".ethereumtest")
|
||||
if err != nil {
|
||||
|
|
|
@ -4,8 +4,8 @@ import "C"
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
"os"
|
||||
)
|
||||
|
||||
var emptyError = ""
|
||||
|
@ -63,6 +63,24 @@ func UnlockAccount(address, password *C.char, seconds int) *C.char {
|
|||
return C.CString(string(outBytes))
|
||||
}
|
||||
|
||||
//export CompleteTransaction
|
||||
func CompleteTransaction(hash *C.char) *C.char {
|
||||
err := completeTransaction(C.GoString(hash))
|
||||
|
||||
errString := emptyError
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := JSONError{
|
||||
Error: errString,
|
||||
}
|
||||
outBytes, _ := json.Marshal(&out)
|
||||
|
||||
return C.CString(string(outBytes))
|
||||
}
|
||||
|
||||
//export StartNode
|
||||
func StartNode(datadir *C.char) *C.char {
|
||||
|
||||
|
@ -121,11 +139,11 @@ func addPeer(url *C.char) *C.char {
|
|||
//export addWhisperFilter
|
||||
func addWhisperFilter(filterJson *C.char) *C.char {
|
||||
|
||||
var id int
|
||||
var filter whisper.NewFilterArgs
|
||||
var id int
|
||||
var filter whisper.NewFilterArgs
|
||||
|
||||
err := json.Unmarshal([]byte(C.GoString(filterJson)), &filter)
|
||||
if err == nil {
|
||||
err := json.Unmarshal([]byte(C.GoString(filterJson)), &filter)
|
||||
if err == nil {
|
||||
id = doAddWhisperFilter(filter)
|
||||
}
|
||||
|
||||
|
@ -136,7 +154,7 @@ func addWhisperFilter(filterJson *C.char) *C.char {
|
|||
}
|
||||
|
||||
out := AddWhisperFilterResult{
|
||||
Id: id,
|
||||
Id: id,
|
||||
Error: errString,
|
||||
}
|
||||
outBytes, _ := json.Marshal(&out)
|
||||
|
@ -154,5 +172,5 @@ func removeWhisperFilter(idFilter int) {
|
|||
//export clearWhisperFilters
|
||||
func clearWhisperFilters() {
|
||||
|
||||
doClearWhisperFilters()
|
||||
doClearWhisperFilters()
|
||||
}
|
26
src/main.go
26
src/main.go
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
|
@ -29,14 +30,15 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
vString string // Combined textual representation of the version
|
||||
rConfig release.Config // Structured version information and release oracle config
|
||||
currentNode *node.Node // currently running geth node
|
||||
c *cli.Context // the CLI context used to start the geth node
|
||||
accountSync *[]node.Service // the object used to sync accounts between geth services
|
||||
accountManager *accounts.Manager // the account manager attached to the currentNode
|
||||
whisperService *whisper.Whisper // whisper service
|
||||
datadir string // data directory for geth
|
||||
vString string // Combined textual representation of the version
|
||||
rConfig release.Config // Structured version information and release oracle config
|
||||
currentNode *node.Node // currently running geth node
|
||||
c *cli.Context // the CLI context used to start the geth node
|
||||
accountSync *[]node.Service // the object used to sync accounts between geth services
|
||||
lightEthereum *les.LightEthereum // LES service
|
||||
accountManager *accounts.Manager // the account manager attached to the currentNode
|
||||
whisperService *whisper.Whisper // whisper service
|
||||
datadir string // data directory for geth
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -60,7 +62,8 @@ func MakeNode(inputDir string) *node.Node {
|
|||
set.Bool("rpc", true, "enable rpc")
|
||||
set.String("rpcaddr", "localhost", "host for RPC")
|
||||
set.String("rpcport", "8545", "rpc port")
|
||||
set.String("rpcapi", "db,eth,net,web3,shh,admin", "rpc api(s)")
|
||||
set.String("verbosity", "3", "verbosity level")
|
||||
set.String("rpcapi", "db,eth,net,web3,shh,personal,admin", "rpc api(s)")
|
||||
set.String("datadir", datadir, "data directory for geth")
|
||||
set.String("logdir", datadir, "log dir for glog")
|
||||
c = cli.NewContext(nil, set, nil)
|
||||
|
@ -92,6 +95,11 @@ func RunNode(nodeIn *node.Node) {
|
|||
if err := nodeIn.Service(&whisperService); err != nil {
|
||||
glog.V(logger.Warn).Infoln("cannot get whisper service:", err)
|
||||
}
|
||||
if err := nodeIn.Service(&lightEthereum); err != nil {
|
||||
glog.V(logger.Warn).Infoln("cannot get light ethereum service:", err)
|
||||
}
|
||||
lightEthereum.StatusBackend.SetTransactionQueueHandler(onSendTransactionRequest)
|
||||
|
||||
nodeIn.Wait()
|
||||
}
|
||||
|
||||
|
|
33
src/types.go
33
src/types.go
|
@ -1,5 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
)
|
||||
|
||||
type AccountInfo struct {
|
||||
Address string `json:"address"`
|
||||
PubKey string `json:"pubkey"`
|
||||
|
@ -11,25 +15,30 @@ type JSONError struct {
|
|||
}
|
||||
|
||||
type AddPeerResult struct {
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error"`
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type AddWhisperFilterResult struct {
|
||||
Id int `json:"id"`
|
||||
Error string `json:"error"`
|
||||
Id int `json:"id"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type WhisperMessageEvent struct {
|
||||
Payload string `json:"payload"`
|
||||
To string `json:"to"`
|
||||
From string `json:"from"`
|
||||
Sent int64 `json:"sent"`
|
||||
TTL int64 `json:"ttl"`
|
||||
Hash string `json:"hash"`
|
||||
Payload string `json:"payload"`
|
||||
To string `json:"to"`
|
||||
From string `json:"from"`
|
||||
Sent int64 `json:"sent"`
|
||||
TTL int64 `json:"ttl"`
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
|
||||
type SendTransactionEvent struct {
|
||||
Hash string `json:"hash"`
|
||||
Args les.SendTxArgs `json:"args"`
|
||||
}
|
||||
|
||||
type GethEvent struct {
|
||||
Type string `json:"type"`
|
||||
Event interface{} `json:"event"`
|
||||
Type string `json:"type"`
|
||||
Event interface{} `json:"event"`
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
|
||||
index fc58447..e9d9ef1 100644
|
||||
--- a/cmd/utils/flags.go
|
||||
+++ b/cmd/utils/flags.go
|
||||
@@ -39,6 +39,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
+ "github.com/ethereum/go-ethereum/internal/debug"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/light"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
@@ -160,7 +161,7 @@ var (
|
||||
Usage: "Enable light client mode",
|
||||
}
|
||||
NoDefSrvFlag = cli.BoolFlag{
|
||||
- Name: "nodefsrv",
|
||||
+ Name: "nodefsrv",
|
||||
Usage: "Don't add default LES server (only for test version)",
|
||||
}
|
||||
LightServFlag = cli.IntFlag{
|
||||
@@ -419,6 +420,13 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
+// DebugSetup sets up the debugging parameters such that logs can be retrieved when a
|
||||
+// node is started via importing go-ethereum packages, as opposed to starting via CLI
|
||||
+func DebugSetup(ctx *cli.Context) error {
|
||||
+ err := debug.Setup(ctx)
|
||||
+ return err
|
||||
+}
|
||||
+
|
||||
// MustMakeDataDir retrieves the currently requested data directory, terminating
|
||||
// if none (or the empty string) is specified. If the node is starting a testnet,
|
||||
// the a subdirectory of the specified datadir will be used.
|
||||
@@ -669,7 +677,7 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
||||
DataDir: MustMakeDataDir(ctx),
|
||||
PrivateKey: MakeNodeKey(ctx),
|
||||
Name: MakeNodeName(name, version, ctx),
|
||||
- NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name), // light client hack
|
||||
+ NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name), // light client hack
|
||||
BootstrapNodes: MakeBootstrapNodes(ctx),
|
||||
ListenAddr: MakeListenAddress(ctx),
|
||||
NAT: MakeNAT(ctx),
|
|
@ -35,7 +35,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
|
@ -47,7 +46,7 @@ import (
|
|||
)
|
||||
|
||||
const defaultGas = uint64(90000)
|
||||
const defaultTxQueueCap = uint8(5)
|
||||
const defaultTxQueueCap = int(5)
|
||||
|
||||
// PublicEthereumAPI provides an API to access Ethereum related information.
|
||||
// It offers only methods that operate on public data that is freely available to anyone.
|
||||
|
@ -870,12 +869,22 @@ type PublicTransactionPoolAPI struct {
|
|||
txQueue chan QueuedTx
|
||||
}
|
||||
|
||||
var txSingletonQueue chan QueuedTx
|
||||
|
||||
// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool.
|
||||
func NewPublicTransactionPoolAPI(b Backend) *PublicTransactionPoolAPI {
|
||||
var once sync.Once
|
||||
once.Do(func() {
|
||||
if txSingletonQueue == nil {
|
||||
glog.V(logger.Debug).Infof("Transaction queue (for status-go) inited")
|
||||
txSingletonQueue = make(chan QueuedTx, defaultTxQueueCap)
|
||||
}
|
||||
})
|
||||
|
||||
api := &PublicTransactionPoolAPI{
|
||||
b: b,
|
||||
pendingTxSubs: make(map[string]rpc.Subscription),
|
||||
txQueue: make(chan QueuedTx, defaultTxQueueCap),
|
||||
txQueue: txSingletonQueue,
|
||||
}
|
||||
|
||||
go api.subscriptionLoop()
|
||||
|
@ -1132,20 +1141,18 @@ func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, si
|
|||
return signedTx.Hash(), nil
|
||||
}
|
||||
|
||||
// Queued Transaction is a container that holds context and arguments enough to complete transaction.
|
||||
// SendTransaction() queues transactions, to be fulfilled by SendQueuedTransaction()
|
||||
// Queued Transaction is a container that holds context and arguments enough to complete the queued transaction.
|
||||
type QueuedTx struct {
|
||||
Hash common.Hash
|
||||
Context context.Context
|
||||
Args SendTxArgs
|
||||
}
|
||||
|
||||
func (s *PublicTransactionPoolAPI) GetTransactionQueue() (<-chan QueuedTx, error) {
|
||||
func (s *PublicTransactionPoolAPI) GetTransactionQueue() (chan QueuedTx, error) {
|
||||
return s.txQueue, nil
|
||||
}
|
||||
|
||||
// SendTransaction creates a transaction for the given argument, sign it and submit it to the
|
||||
// transaction pool.
|
||||
// SendTransaction queues transactions, to be fulfilled by CompleteQueuedTransaction()
|
||||
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
|
||||
queuedTx := QueuedTx{
|
||||
Hash: common.Hash{},
|
||||
|
@ -1154,19 +1161,19 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
|
|||
}
|
||||
|
||||
// populate transaction hash
|
||||
hw := sha3.NewKeccak256()
|
||||
rlp.Encode(hw, queuedTx)
|
||||
hw.Sum(queuedTx.Hash[:0])
|
||||
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
queuedTx.Hash = common.BytesToHash(crypto.FromECDSA(key))
|
||||
|
||||
s.txQueue <- queuedTx
|
||||
|
||||
return queuedTx.Hash, nil
|
||||
}
|
||||
|
||||
func (s *PublicTransactionPoolAPI) CompleteQueuedTransaction(queuedTx QueuedTx) (common.Hash, error) {
|
||||
ctx, args := queuedTx.Context, queuedTx.Args
|
||||
|
||||
// CompleteQueuedTransaction creates a transaction by unpacking queued transaction, signs it and submits to the
|
||||
// transaction pool.
|
||||
func (s *PublicTransactionPoolAPI) CompleteQueuedTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
|
||||
var err error
|
||||
args, err = prepareSendTxArgs(ctx, args, s.b)
|
||||
if err != nil {
|
||||
|
|
|
@ -69,6 +69,8 @@ type LightEthereum struct {
|
|||
PowTest bool
|
||||
netVersionId int
|
||||
netRPCService *ethapi.PublicNetAPI
|
||||
|
||||
StatusBackend *StatusBackend
|
||||
}
|
||||
|
||||
func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
||||
|
@ -125,6 +127,10 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
|||
|
||||
eth.ApiBackend = &LesApiBackend{eth, nil}
|
||||
eth.ApiBackend.gpo = gasprice.NewLightPriceOracle(eth.ApiBackend)
|
||||
|
||||
// inject status-im backend
|
||||
eth.StatusBackend = NewStatusBackend(eth.ApiBackend)
|
||||
|
||||
return eth, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
package les
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
const defaultTxQueueCap = int(5)
|
||||
const defaultEvictingTxQueueCap = int(20)
|
||||
const defaultEvictingTxQueueEvictionStep = int(5) // how many item to evict in a single run
|
||||
|
||||
// StatusBackend implements les.StatusBackend with direct calls to Ethereum
|
||||
// internals to support calls from status-go bindings (to internal packages e.g. ethapi)
|
||||
type StatusBackend struct {
|
||||
eapi *ethapi.PublicEthereumAPI // Wrapper around the Ethereum object to access metadata
|
||||
bcapi *ethapi.PublicBlockChainAPI // Wrapper around the blockchain to access chain data
|
||||
txapi *ethapi.PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data
|
||||
|
||||
txQueue chan QueuedTx
|
||||
txQueueHandler QueuedTxHandler
|
||||
muTxQueueHanlder sync.Mutex
|
||||
|
||||
txEvictingQueue evictingTxQueue
|
||||
}
|
||||
|
||||
type QueuedTxHash string
|
||||
|
||||
type evictingTxQueue struct {
|
||||
transactions map[QueuedTxHash]*QueuedTx
|
||||
evictionQueue chan QueuedTxHash
|
||||
cap int
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
type QueuedTxHandler func(QueuedTx)
|
||||
|
||||
type QueuedTx struct {
|
||||
Hash common.Hash
|
||||
Context context.Context
|
||||
Args SendTxArgs
|
||||
}
|
||||
|
||||
// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool.
|
||||
type SendTxArgs struct {
|
||||
From common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *rpc.HexNumber `json:"gas"`
|
||||
GasPrice *rpc.HexNumber `json:"gasPrice"`
|
||||
Value *rpc.HexNumber `json:"value"`
|
||||
Data string `json:"data"`
|
||||
Nonce *rpc.HexNumber `json:"nonce"`
|
||||
}
|
||||
|
||||
// NewStatusBackend creates a new backend using an existing Ethereum object.
|
||||
func NewStatusBackend(apiBackend ethapi.Backend) *StatusBackend {
|
||||
glog.V(logger.Debug).Infof("Status service started")
|
||||
backend := &StatusBackend{
|
||||
eapi: ethapi.NewPublicEthereumAPI(apiBackend, nil, nil),
|
||||
bcapi: ethapi.NewPublicBlockChainAPI(apiBackend),
|
||||
txapi: ethapi.NewPublicTransactionPoolAPI(apiBackend),
|
||||
txQueue: make(chan QueuedTx, defaultTxQueueCap),
|
||||
txEvictingQueue: evictingTxQueue{
|
||||
transactions: make(map[QueuedTxHash]*QueuedTx),
|
||||
evictionQueue: make(chan QueuedTxHash, defaultEvictingTxQueueCap), // will be used to evict in FIFO
|
||||
cap: defaultEvictingTxQueueCap,
|
||||
},
|
||||
}
|
||||
|
||||
go backend.transactionQueueForwardingLoop()
|
||||
|
||||
return backend
|
||||
}
|
||||
|
||||
func (b *StatusBackend) SetTransactionQueueHandler(fn QueuedTxHandler) {
|
||||
b.muTxQueueHanlder.Lock()
|
||||
defer b.muTxQueueHanlder.Unlock()
|
||||
|
||||
b.txQueueHandler = fn
|
||||
}
|
||||
|
||||
// SendTransaction wraps call to PublicTransactionPoolAPI.SendTransaction
|
||||
func (b *StatusBackend) SendTransaction(ctx context.Context, args SendTxArgs) error {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
_, err := b.txapi.SendTransaction(ctx, ethapi.SendTxArgs(args))
|
||||
return err
|
||||
}
|
||||
|
||||
// CompleteQueuedTransaction wraps call to PublicTransactionPoolAPI.CompleteQueuedTransaction
|
||||
func (b *StatusBackend) CompleteQueuedTransaction(hash QueuedTxHash) error {
|
||||
queuedTx, err := b.txEvictingQueue.getQueuedTransaction(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = b.txapi.CompleteQueuedTransaction(queuedTx.Context, ethapi.SendTxArgs(queuedTx.Args))
|
||||
return err
|
||||
}
|
||||
|
||||
// GetTransactionQueue wraps call to PublicTransactionPoolAPI.GetTransactionQueue
|
||||
func (b *StatusBackend) GetTransactionQueue() (chan QueuedTx, error) {
|
||||
return b.txQueue, nil
|
||||
}
|
||||
|
||||
func (b *StatusBackend) transactionQueueForwardingLoop() {
|
||||
txQueue, err := b.txapi.GetTransactionQueue()
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Cannot read from transaction queue")
|
||||
return
|
||||
}
|
||||
|
||||
// forward internal ethapi transactions
|
||||
for queuedTx := range txQueue {
|
||||
if b.txQueueHandler == nil { //discard, until handler is provided
|
||||
continue
|
||||
}
|
||||
tx := QueuedTx{
|
||||
Hash: queuedTx.Hash,
|
||||
Context: queuedTx.Context,
|
||||
Args: SendTxArgs(queuedTx.Args),
|
||||
}
|
||||
b.txEvictingQueue.enqueueQueuedTransaction(tx)
|
||||
b.txQueueHandler(tx)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *evictingTxQueue) enqueueQueuedTransaction(tx QueuedTx) error {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
if q.cap <= len(q.transactions) { // eviction is required
|
||||
for i := 0; i < defaultEvictingTxQueueEvictionStep; i++ {
|
||||
hash := <-q.evictionQueue
|
||||
delete(q.transactions, hash)
|
||||
}
|
||||
}
|
||||
|
||||
q.transactions[QueuedTxHash(tx.Hash.Hex())] = &tx
|
||||
q.evictionQueue <- QueuedTxHash(tx.Hash.Hex())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *evictingTxQueue) getQueuedTransaction(hash QueuedTxHash) (*QueuedTx, error) {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
if tx, ok := q.transactions[hash]; ok {
|
||||
delete(q.transactions, hash)
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
Loading…
Reference in New Issue