Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
M
mybee
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
vicotor
mybee
Commits
d2c668e9
Unverified
Commit
d2c668e9
authored
Jun 16, 2021
by
Ralph Pichler
Committed by
GitHub
Jun 16, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add various transaction endpoint and track pending transactions (#1469)
parent
20a93521
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
879 additions
and
60 deletions
+879
-60
SwarmCommon.yaml
openapi/SwarmCommon.yaml
+31
-0
SwarmDebug.yaml
openapi/SwarmDebug.yaml
+67
-0
debugapi.go
pkg/debugapi/debugapi.go
+4
-1
debugapi_test.go
pkg/debugapi/debugapi_test.go
+6
-2
export_test.go
pkg/debugapi/export_test.go
+14
-7
router.go
pkg/debugapi/router.go
+8
-0
transaction.go
pkg/debugapi/transaction.go
+135
-0
transaction_test.go
pkg/debugapi/transaction_test.go
+282
-0
node.go
pkg/node/node.go
+4
-1
cashout.go
pkg/settlement/swap/chequebook/cashout.go
+6
-5
chequebook.go
pkg/settlement/swap/chequebook/chequebook.go
+6
-5
factory.go
pkg/settlement/swap/chequebook/factory.go
+6
-5
erc20.go
pkg/settlement/swap/erc20/erc20.go
+6
-5
export_test.go
pkg/transaction/export_test.go
+0
-2
transaction.go
pkg/transaction/mock/transaction.go
+46
-0
transaction.go
pkg/transaction/transaction.go
+161
-27
transaction_test.go
pkg/transaction/transaction_test.go
+97
-0
No files found.
openapi/SwarmCommon.yaml
View file @
d2c668e9
...
...
@@ -438,6 +438,37 @@ components:
properties
:
transactionHash
:
$ref
:
"
#/components/schemas/TransactionHash"
TransactionInfoResponse
:
type
:
object
properties
:
transactionHash
:
$ref
:
"
#/components/schemas/TransactionHash"
to
:
$ref
:
"
#/components/schemas/EthereumAddress"
nonce
:
type
:
integer
gasPrice
:
type
:
"
#/components/schemas/BigInt"
gasLimit
:
type
:
integer
data
:
type
:
string
created
:
type
:
"
#/components/schemas/DateTime"
description
:
type
:
string
value
:
type
:
"
#/components/schemas/BigInt"
PendingTransactionReponse
:
type
:
object
properties
:
pendingTransactions
:
type
:
array
nullable
:
true
items
:
$ref
:
"
#/components/schemas/TransactionHash"
Uid
:
type
:
integer
...
...
openapi/SwarmDebug.yaml
View file @
d2c668e9
...
...
@@ -669,3 +669,70 @@ paths:
$ref
:
"
SwarmCommon.yaml#/components/responses/500"
default
:
description
:
Default response
"
/transaction"
:
get
:
summary
:
Get list of pending transactions
tags
:
-
Transaction
responses
:
"
200"
:
description
:
List of pending transactions
content
:
application/json
:
schema
:
$ref
:
"
SwarmCommon.yaml#/components/schemas/PendingTransactionReponse"
"
500"
:
$ref
:
"
SwarmCommon.yaml#/components/responses/500"
default
:
description
:
Default response
"
/transaction/{txHash}"
:
get
:
summary
:
Get information about a sent transaction
parameters
:
-
in
:
path
name
:
txHash
schema
:
$ref
:
"
SwarmCommon.yaml#/components/schemas/TransactionHash"
required
:
true
description
:
Hash of the transaction
tags
:
-
Transaction
responses
:
"
200"
:
description
:
Get info about transaction
content
:
application/json
:
schema
:
$ref
:
"
SwarmCommon.yaml#/components/schemas/TransactionInfoResponse"
"
404"
:
$ref
:
"
SwarmCommon.yaml#/components/responses/404"
"
500"
:
$ref
:
"
SwarmCommon.yaml#/components/responses/500"
default
:
description
:
Default response
post
:
summary
:
Rebroadcast existing transaction
parameters
:
-
in
:
path
name
:
txHash
schema
:
$ref
:
"
SwarmCommon.yaml#/components/schemas/TransactionHash"
required
:
true
description
:
Hash of the transaction
tags
:
-
Transaction
responses
:
"
200"
:
description
:
Hash of the transaction
content
:
application/json
:
schema
:
$ref
:
"
SwarmCommon.yaml#/components/schemas/TransactionResponse"
"
404"
:
$ref
:
"
SwarmCommon.yaml#/components/responses/404"
"
500"
:
$ref
:
"
SwarmCommon.yaml#/components/responses/500"
default
:
description
:
Default response
\ No newline at end of file
pkg/debugapi/debugapi.go
View file @
d2c668e9
...
...
@@ -27,6 +27,7 @@ import (
"github.com/ethersphere/bee/pkg/topology"
"github.com/ethersphere/bee/pkg/topology/lightnode"
"github.com/ethersphere/bee/pkg/tracing"
"github.com/ethersphere/bee/pkg/transaction"
"github.com/prometheus/client_golang/prometheus"
)
...
...
@@ -49,6 +50,7 @@ type Service struct {
chequebook
chequebook
.
Service
swap
swap
.
Interface
batchStore
postage
.
Storer
transaction
transaction
.
Service
corsAllowedOrigins
[]
string
metricsRegistry
*
prometheus
.
Registry
lightNodes
*
lightnode
.
Container
...
...
@@ -80,7 +82,7 @@ func New(overlay swarm.Address, publicKey, pssPublicKey ecdsa.PublicKey, ethereu
// Configure injects required dependencies and configuration parameters and
// constructs HTTP routes that depend on them. It is intended and safe to call
// this method only once.
func
(
s
*
Service
)
Configure
(
p2p
p2p
.
DebugService
,
pingpong
pingpong
.
Interface
,
topologyDriver
topology
.
Driver
,
lightNodes
*
lightnode
.
Container
,
storer
storage
.
Storer
,
tags
*
tags
.
Tags
,
accounting
accounting
.
Interface
,
pseudosettle
settlement
.
Interface
,
chequebookEnabled
bool
,
swap
swap
.
Interface
,
chequebook
chequebook
.
Service
,
batchStore
postage
.
Storer
)
{
func
(
s
*
Service
)
Configure
(
p2p
p2p
.
DebugService
,
pingpong
pingpong
.
Interface
,
topologyDriver
topology
.
Driver
,
lightNodes
*
lightnode
.
Container
,
storer
storage
.
Storer
,
tags
*
tags
.
Tags
,
accounting
accounting
.
Interface
,
pseudosettle
settlement
.
Interface
,
chequebookEnabled
bool
,
swap
swap
.
Interface
,
chequebook
chequebook
.
Service
,
batchStore
postage
.
Storer
,
transaction
transaction
.
Service
)
{
s
.
p2p
=
p2p
s
.
pingpong
=
pingpong
s
.
topologyDriver
=
topologyDriver
...
...
@@ -93,6 +95,7 @@ func (s *Service) Configure(p2p p2p.DebugService, pingpong pingpong.Interface, t
s
.
lightNodes
=
lightNodes
s
.
batchStore
=
batchStore
s
.
pseudosettle
=
pseudosettle
s
.
transaction
=
transaction
s
.
setRouter
(
s
.
newRouter
())
}
...
...
pkg/debugapi/debugapi_test.go
View file @
d2c668e9
...
...
@@ -32,6 +32,7 @@ import (
"github.com/ethersphere/bee/pkg/tags"
"github.com/ethersphere/bee/pkg/topology/lightnode"
topologymock
"github.com/ethersphere/bee/pkg/topology/mock"
transactionmock
"github.com/ethersphere/bee/pkg/transaction/mock"
"github.com/multiformats/go-multiaddr"
"resenje.org/web"
)
...
...
@@ -53,6 +54,7 @@ type testServerOptions struct {
ChequebookOpts
[]
chequebookmock
.
Option
SwapOpts
[]
swapmock
.
Option
BatchStore
postage
.
Storer
TransactionOpts
[]
transactionmock
.
Option
}
type
testServer
struct
{
...
...
@@ -66,9 +68,10 @@ func newTestServer(t *testing.T, o testServerOptions) *testServer {
settlement
:=
swapmock
.
New
(
o
.
SettlementOpts
...
)
chequebook
:=
chequebookmock
.
NewChequebook
(
o
.
ChequebookOpts
...
)
swapserv
:=
swapmock
.
New
(
o
.
SwapOpts
...
)
transaction
:=
transactionmock
.
New
(
o
.
TransactionOpts
...
)
ln
:=
lightnode
.
NewContainer
(
o
.
Overlay
)
s
:=
debugapi
.
New
(
o
.
Overlay
,
o
.
PublicKey
,
o
.
PSSPublicKey
,
o
.
EthereumAddress
,
logging
.
New
(
ioutil
.
Discard
,
0
),
nil
,
o
.
CORSAllowedOrigins
)
s
.
Configure
(
o
.
P2P
,
o
.
Pingpong
,
topologyDriver
,
ln
,
o
.
Storer
,
o
.
Tags
,
acc
,
settlement
,
true
,
swapserv
,
chequebook
,
o
.
BatchStore
)
s
.
Configure
(
o
.
P2P
,
o
.
Pingpong
,
topologyDriver
,
ln
,
o
.
Storer
,
o
.
Tags
,
acc
,
settlement
,
true
,
swapserv
,
chequebook
,
o
.
BatchStore
,
transaction
)
ts
:=
httptest
.
NewServer
(
s
)
t
.
Cleanup
(
ts
.
Close
)
...
...
@@ -134,6 +137,7 @@ func TestServer_Configure(t *testing.T) {
chequebook
:=
chequebookmock
.
NewChequebook
(
o
.
ChequebookOpts
...
)
swapserv
:=
swapmock
.
New
(
o
.
SwapOpts
...
)
ln
:=
lightnode
.
NewContainer
(
o
.
Overlay
)
transaction
:=
transactionmock
.
New
(
o
.
TransactionOpts
...
)
s
:=
debugapi
.
New
(
o
.
Overlay
,
o
.
PublicKey
,
o
.
PSSPublicKey
,
o
.
EthereumAddress
,
logging
.
New
(
ioutil
.
Discard
,
0
),
nil
,
nil
)
ts
:=
httptest
.
NewServer
(
s
)
t
.
Cleanup
(
ts
.
Close
)
...
...
@@ -166,7 +170,7 @@ func TestServer_Configure(t *testing.T) {
}),
)
s
.
Configure
(
o
.
P2P
,
o
.
Pingpong
,
topologyDriver
,
ln
,
o
.
Storer
,
o
.
Tags
,
acc
,
settlement
,
true
,
swapserv
,
chequebook
,
nil
)
s
.
Configure
(
o
.
P2P
,
o
.
Pingpong
,
topologyDriver
,
ln
,
o
.
Storer
,
o
.
Tags
,
acc
,
settlement
,
true
,
swapserv
,
chequebook
,
nil
,
transaction
)
testBasicRouter
(
t
,
client
)
jsonhttptest
.
Request
(
t
,
client
,
http
.
MethodGet
,
"/readiness"
,
http
.
StatusOK
,
...
...
pkg/debugapi/export_test.go
View file @
d2c668e9
...
...
@@ -25,17 +25,24 @@ type (
SwapCashoutResponse
=
swapCashoutResponse
SwapCashoutStatusResponse
=
swapCashoutStatusResponse
SwapCashoutStatusResult
=
swapCashoutStatusResult
TransactionInfo
=
transactionInfo
TransactionPendingList
=
transactionPendingList
TransactionHashResponse
=
transactionHashResponse
TagResponse
=
tagResponse
ReserveStateResponse
=
reserveStateResponse
ChainStateResponse
=
chainStateResponse
)
var
(
ErrCantBalance
=
errCantBalance
ErrCantBalances
=
errCantBalances
ErrNoBalance
=
errNoBalance
ErrCantSettlementsPeer
=
errCantSettlementsPeer
ErrCantSettlements
=
errCantSettlements
ErrChequebookBalance
=
errChequebookBalance
ErrInvalidAddress
=
errInvalidAddress
ErrCantBalance
=
errCantBalance
ErrCantBalances
=
errCantBalances
ErrNoBalance
=
errNoBalance
ErrCantSettlementsPeer
=
errCantSettlementsPeer
ErrCantSettlements
=
errCantSettlements
ErrChequebookBalance
=
errChequebookBalance
ErrInvalidAddress
=
errInvalidAddress
ErrUnknownTransaction
=
errUnknownTransaction
ErrCantGetTransaction
=
errCantGetTransaction
ErrCantResendTransaction
=
errCantResendTransaction
ErrAlreadyImported
=
errAlreadyImported
)
pkg/debugapi/router.go
View file @
d2c668e9
...
...
@@ -169,6 +169,14 @@ func (s *Service) newRouter() *mux.Router {
"GET"
:
http
.
HandlerFunc
(
s
.
swapCashoutStatusHandler
),
"POST"
:
http
.
HandlerFunc
(
s
.
swapCashoutHandler
),
})
router
.
Handle
(
"/transactions"
,
jsonhttp
.
MethodHandler
{
"GET"
:
http
.
HandlerFunc
(
s
.
transactionListHandler
),
})
router
.
Handle
(
"/transactions/{hash}"
,
jsonhttp
.
MethodHandler
{
"GET"
:
http
.
HandlerFunc
(
s
.
transactionDetailHandler
),
"POST"
:
http
.
HandlerFunc
(
s
.
transactionResendHandler
),
})
}
router
.
Handle
(
"/tags/{id}"
,
jsonhttp
.
MethodHandler
{
...
...
pkg/debugapi/transaction.go
0 → 100644
View file @
d2c668e9
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
debugapi
import
(
"errors"
"net/http"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethersphere/bee/pkg/bigint"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/transaction"
"github.com/gorilla/mux"
)
const
(
errCantGetTransaction
=
"cannot get transaction"
errUnknownTransaction
=
"unknown transaction"
errAlreadyImported
=
"already imported"
errCantResendTransaction
=
"can't resend transaction"
)
type
transactionInfo
struct
{
TransactionHash
common
.
Hash
`json:"transactionHash"`
To
*
common
.
Address
`json:"to"`
Nonce
uint64
`json:"nonce"`
GasPrice
*
bigint
.
BigInt
`json:"gasPrice"`
GasLimit
uint64
`json:"gasLimit"`
Data
string
`json:"data"`
Created
time
.
Time
`json:"created"`
Description
string
`json:"description"`
Value
*
bigint
.
BigInt
`json:"value"`
}
type
transactionPendingList
struct
{
PendingTransactions
[]
transactionInfo
`json:"pendingTransactions"`
}
func
(
s
*
Service
)
transactionListHandler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
txHashes
,
err
:=
s
.
transaction
.
PendingTransactions
()
if
err
!=
nil
{
s
.
logger
.
Debugf
(
"debug api: transactions: get pending transactions: %v"
,
err
)
s
.
logger
.
Errorf
(
"debug api: transactions: can't get pending transactions"
)
jsonhttp
.
InternalServerError
(
w
,
errCantGetTransaction
)
return
}
var
transactionInfos
[]
transactionInfo
=
make
([]
transactionInfo
,
0
)
for
_
,
txHash
:=
range
txHashes
{
storedTransaction
,
err
:=
s
.
transaction
.
StoredTransaction
(
txHash
)
if
err
!=
nil
{
s
.
logger
.
Debugf
(
"debug api: transactions: get stored transaction %x: %v"
,
txHash
,
err
)
s
.
logger
.
Errorf
(
"debug api: transactions: can't get stored transaction %x"
,
txHash
)
jsonhttp
.
InternalServerError
(
w
,
errCantGetTransaction
)
return
}
transactionInfos
=
append
(
transactionInfos
,
transactionInfo
{
TransactionHash
:
txHash
,
To
:
storedTransaction
.
To
,
Nonce
:
storedTransaction
.
Nonce
,
GasPrice
:
bigint
.
Wrap
(
storedTransaction
.
GasPrice
),
GasLimit
:
storedTransaction
.
GasLimit
,
Data
:
hexutil
.
Encode
(
storedTransaction
.
Data
),
Created
:
time
.
Unix
(
storedTransaction
.
Created
,
0
),
Description
:
storedTransaction
.
Description
,
Value
:
bigint
.
Wrap
(
storedTransaction
.
Value
),
})
}
jsonhttp
.
OK
(
w
,
transactionPendingList
{
PendingTransactions
:
transactionInfos
,
})
}
func
(
s
*
Service
)
transactionDetailHandler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
hash
:=
mux
.
Vars
(
r
)[
"hash"
]
txHash
:=
common
.
HexToHash
(
hash
)
storedTransaction
,
err
:=
s
.
transaction
.
StoredTransaction
(
txHash
)
if
err
!=
nil
{
s
.
logger
.
Debugf
(
"debug api: transactions: get transaction %x: %v"
,
txHash
,
err
)
s
.
logger
.
Errorf
(
"debug api: transactions: can't get transaction %x"
,
txHash
)
if
errors
.
Is
(
err
,
transaction
.
ErrUnknownTransaction
)
{
jsonhttp
.
NotFound
(
w
,
errUnknownTransaction
)
}
else
{
jsonhttp
.
InternalServerError
(
w
,
errCantGetTransaction
)
}
return
}
jsonhttp
.
OK
(
w
,
transactionInfo
{
TransactionHash
:
txHash
,
To
:
storedTransaction
.
To
,
Nonce
:
storedTransaction
.
Nonce
,
GasPrice
:
bigint
.
Wrap
(
storedTransaction
.
GasPrice
),
GasLimit
:
storedTransaction
.
GasLimit
,
Data
:
hexutil
.
Encode
(
storedTransaction
.
Data
),
Created
:
time
.
Unix
(
storedTransaction
.
Created
,
0
),
Description
:
storedTransaction
.
Description
,
Value
:
bigint
.
Wrap
(
storedTransaction
.
Value
),
})
}
type
transactionHashResponse
struct
{
TransactionHash
common
.
Hash
`json:"transactionHash"`
}
func
(
s
*
Service
)
transactionResendHandler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
hash
:=
mux
.
Vars
(
r
)[
"hash"
]
txHash
:=
common
.
HexToHash
(
hash
)
err
:=
s
.
transaction
.
ResendTransaction
(
txHash
)
if
err
!=
nil
{
s
.
logger
.
Debugf
(
"debug api: transactions: resend %x: %v"
,
txHash
,
err
)
s
.
logger
.
Errorf
(
"debug api: transactions: can't resend transaction %x"
,
txHash
)
if
errors
.
Is
(
err
,
transaction
.
ErrUnknownTransaction
)
{
jsonhttp
.
NotFound
(
w
,
errUnknownTransaction
)
}
else
if
errors
.
Is
(
err
,
transaction
.
ErrAlreadyImported
)
{
jsonhttp
.
BadRequest
(
w
,
errAlreadyImported
)
}
else
{
jsonhttp
.
InternalServerError
(
w
,
errCantResendTransaction
)
}
return
}
jsonhttp
.
OK
(
w
,
transactionHashResponse
{
TransactionHash
:
txHash
,
})
}
pkg/debugapi/transaction_test.go
0 → 100644
View file @
d2c668e9
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
debugapi_test
import
(
"errors"
"math/big"
"net/http"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethersphere/bee/pkg/bigint"
"github.com/ethersphere/bee/pkg/debugapi"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/transaction"
"github.com/ethersphere/bee/pkg/transaction/mock"
)
func
TestTransactionStoredTransaction
(
t
*
testing
.
T
)
{
txHashStr
:=
"0xabcd"
txHash
:=
common
.
HexToHash
(
txHashStr
)
dataStr
:=
"abdd"
data
:=
common
.
Hex2Bytes
(
dataStr
)
created
:=
int64
(
1616451040
)
recipient
:=
common
.
HexToAddress
(
"fffe"
)
gasPrice
:=
big
.
NewInt
(
23
)
gasLimit
:=
uint64
(
200
)
value
:=
big
.
NewInt
(
50
)
nonce
:=
uint64
(
12
)
description
:=
"test"
t
.
Run
(
"found"
,
func
(
t
*
testing
.
T
)
{
testServer
:=
newTestServer
(
t
,
testServerOptions
{
TransactionOpts
:
[]
mock
.
Option
{
mock
.
WithStoredTransactionFunc
(
func
(
txHash
common
.
Hash
)
(
*
transaction
.
StoredTransaction
,
error
)
{
return
&
transaction
.
StoredTransaction
{
To
:
&
recipient
,
Created
:
created
,
Data
:
data
,
GasPrice
:
gasPrice
,
GasLimit
:
gasLimit
,
Value
:
value
,
Nonce
:
nonce
,
Description
:
description
,
},
nil
}),
},
})
jsonhttptest
.
Request
(
t
,
testServer
.
Client
,
http
.
MethodGet
,
"/transactions/"
+
txHashStr
,
http
.
StatusOK
,
jsonhttptest
.
WithExpectedJSONResponse
(
debugapi
.
TransactionInfo
{
TransactionHash
:
txHash
,
Created
:
time
.
Unix
(
created
,
0
),
Data
:
"0x"
+
dataStr
,
To
:
&
recipient
,
GasPrice
:
bigint
.
Wrap
(
gasPrice
),
GasLimit
:
gasLimit
,
Value
:
bigint
.
Wrap
(
value
),
Nonce
:
nonce
,
Description
:
description
,
}),
)
})
t
.
Run
(
"not found"
,
func
(
t
*
testing
.
T
)
{
testServer
:=
newTestServer
(
t
,
testServerOptions
{
TransactionOpts
:
[]
mock
.
Option
{
mock
.
WithStoredTransactionFunc
(
func
(
txHash
common
.
Hash
)
(
*
transaction
.
StoredTransaction
,
error
)
{
return
nil
,
transaction
.
ErrUnknownTransaction
}),
},
})
jsonhttptest
.
Request
(
t
,
testServer
.
Client
,
http
.
MethodGet
,
"/transactions/"
+
txHashStr
,
http
.
StatusNotFound
,
jsonhttptest
.
WithExpectedJSONResponse
(
jsonhttp
.
StatusResponse
{
Message
:
debugapi
.
ErrUnknownTransaction
,
Code
:
http
.
StatusNotFound
,
}))
})
t
.
Run
(
"other errors"
,
func
(
t
*
testing
.
T
)
{
testServer
:=
newTestServer
(
t
,
testServerOptions
{
TransactionOpts
:
[]
mock
.
Option
{
mock
.
WithStoredTransactionFunc
(
func
(
txHash
common
.
Hash
)
(
*
transaction
.
StoredTransaction
,
error
)
{
return
nil
,
errors
.
New
(
"err"
)
}),
},
})
jsonhttptest
.
Request
(
t
,
testServer
.
Client
,
http
.
MethodGet
,
"/transactions/"
+
txHashStr
,
http
.
StatusInternalServerError
,
jsonhttptest
.
WithExpectedJSONResponse
(
jsonhttp
.
StatusResponse
{
Message
:
debugapi
.
ErrCantGetTransaction
,
Code
:
http
.
StatusInternalServerError
,
}))
})
}
func
TestTransactionList
(
t
*
testing
.
T
)
{
recipient
:=
common
.
HexToAddress
(
"dfff"
)
txHash1
:=
common
.
HexToHash
(
"abcd"
)
txHash2
:=
common
.
HexToHash
(
"efff"
)
storedTransactions
:=
map
[
common
.
Hash
]
*
transaction
.
StoredTransaction
{
txHash1
:
{
To
:
&
recipient
,
Created
:
1
,
Data
:
[]
byte
{
1
,
2
,
3
,
4
},
GasPrice
:
big
.
NewInt
(
12
),
GasLimit
:
5345
,
Value
:
big
.
NewInt
(
4
),
Nonce
:
3
,
Description
:
"test"
,
},
txHash2
:
{
To
:
&
recipient
,
Created
:
2
,
Data
:
[]
byte
{
3
,
2
,
3
,
4
},
GasPrice
:
big
.
NewInt
(
42
),
GasLimit
:
53451
,
Value
:
big
.
NewInt
(
41
),
Nonce
:
32
,
Description
:
"test2"
,
},
}
testServer
:=
newTestServer
(
t
,
testServerOptions
{
TransactionOpts
:
[]
mock
.
Option
{
mock
.
WithPendingTransactionsFunc
(
func
()
([]
common
.
Hash
,
error
)
{
return
[]
common
.
Hash
{
txHash1
,
txHash2
},
nil
}),
mock
.
WithStoredTransactionFunc
(
func
(
txHash
common
.
Hash
)
(
*
transaction
.
StoredTransaction
,
error
)
{
return
storedTransactions
[
txHash
],
nil
}),
},
})
jsonhttptest
.
Request
(
t
,
testServer
.
Client
,
http
.
MethodGet
,
"/transactions"
,
http
.
StatusOK
,
jsonhttptest
.
WithExpectedJSONResponse
(
debugapi
.
TransactionPendingList
{
PendingTransactions
:
[]
debugapi
.
TransactionInfo
{
{
TransactionHash
:
txHash1
,
Created
:
time
.
Unix
(
storedTransactions
[
txHash1
]
.
Created
,
0
),
Data
:
hexutil
.
Encode
(
storedTransactions
[
txHash1
]
.
Data
),
To
:
storedTransactions
[
txHash1
]
.
To
,
GasPrice
:
bigint
.
Wrap
(
storedTransactions
[
txHash1
]
.
GasPrice
),
GasLimit
:
storedTransactions
[
txHash1
]
.
GasLimit
,
Value
:
bigint
.
Wrap
(
storedTransactions
[
txHash1
]
.
Value
),
Nonce
:
storedTransactions
[
txHash1
]
.
Nonce
,
Description
:
storedTransactions
[
txHash1
]
.
Description
,
},
{
TransactionHash
:
txHash2
,
Created
:
time
.
Unix
(
storedTransactions
[
txHash2
]
.
Created
,
0
),
Data
:
hexutil
.
Encode
(
storedTransactions
[
txHash2
]
.
Data
),
To
:
storedTransactions
[
txHash2
]
.
To
,
GasPrice
:
bigint
.
Wrap
(
storedTransactions
[
txHash2
]
.
GasPrice
),
GasLimit
:
storedTransactions
[
txHash2
]
.
GasLimit
,
Value
:
bigint
.
Wrap
(
storedTransactions
[
txHash2
]
.
Value
),
Nonce
:
storedTransactions
[
txHash2
]
.
Nonce
,
Description
:
storedTransactions
[
txHash2
]
.
Description
,
},
},
}),
)
}
func
TestTransactionListError
(
t
*
testing
.
T
)
{
txHash1
:=
common
.
HexToHash
(
"abcd"
)
t
.
Run
(
"pending transactions error"
,
func
(
t
*
testing
.
T
)
{
testServer
:=
newTestServer
(
t
,
testServerOptions
{
TransactionOpts
:
[]
mock
.
Option
{
mock
.
WithPendingTransactionsFunc
(
func
()
([]
common
.
Hash
,
error
)
{
return
nil
,
errors
.
New
(
"err"
)
}),
mock
.
WithStoredTransactionFunc
(
func
(
txHash
common
.
Hash
)
(
*
transaction
.
StoredTransaction
,
error
)
{
return
nil
,
nil
}),
},
})
jsonhttptest
.
Request
(
t
,
testServer
.
Client
,
http
.
MethodGet
,
"/transactions"
,
http
.
StatusInternalServerError
,
jsonhttptest
.
WithExpectedJSONResponse
(
jsonhttp
.
StatusResponse
{
Code
:
http
.
StatusInternalServerError
,
Message
:
debugapi
.
ErrCantGetTransaction
,
}),
)
})
t
.
Run
(
"pending transactions error"
,
func
(
t
*
testing
.
T
)
{
testServer
:=
newTestServer
(
t
,
testServerOptions
{
TransactionOpts
:
[]
mock
.
Option
{
mock
.
WithPendingTransactionsFunc
(
func
()
([]
common
.
Hash
,
error
)
{
return
[]
common
.
Hash
{
txHash1
},
nil
}),
mock
.
WithStoredTransactionFunc
(
func
(
txHash
common
.
Hash
)
(
*
transaction
.
StoredTransaction
,
error
)
{
return
nil
,
errors
.
New
(
"error"
)
}),
},
})
jsonhttptest
.
Request
(
t
,
testServer
.
Client
,
http
.
MethodGet
,
"/transactions"
,
http
.
StatusInternalServerError
,
jsonhttptest
.
WithExpectedJSONResponse
(
jsonhttp
.
StatusResponse
{
Code
:
http
.
StatusInternalServerError
,
Message
:
debugapi
.
ErrCantGetTransaction
,
}),
)
})
}
func
TestTransactionResend
(
t
*
testing
.
T
)
{
txHash
:=
common
.
HexToHash
(
"abcd"
)
t
.
Run
(
"ok"
,
func
(
t
*
testing
.
T
)
{
testServer
:=
newTestServer
(
t
,
testServerOptions
{
TransactionOpts
:
[]
mock
.
Option
{
mock
.
WithResendTransactionFunc
(
func
(
txHash
common
.
Hash
)
error
{
return
nil
}),
},
})
jsonhttptest
.
Request
(
t
,
testServer
.
Client
,
http
.
MethodPost
,
"/transactions/"
+
txHash
.
String
(),
http
.
StatusOK
,
jsonhttptest
.
WithExpectedJSONResponse
(
debugapi
.
TransactionHashResponse
{
TransactionHash
:
txHash
,
}),
)
})
t
.
Run
(
"unknown transaction"
,
func
(
t
*
testing
.
T
)
{
testServer
:=
newTestServer
(
t
,
testServerOptions
{
TransactionOpts
:
[]
mock
.
Option
{
mock
.
WithResendTransactionFunc
(
func
(
txHash
common
.
Hash
)
error
{
return
transaction
.
ErrUnknownTransaction
}),
},
})
jsonhttptest
.
Request
(
t
,
testServer
.
Client
,
http
.
MethodPost
,
"/transactions/"
+
txHash
.
String
(),
http
.
StatusNotFound
,
jsonhttptest
.
WithExpectedJSONResponse
(
jsonhttp
.
StatusResponse
{
Code
:
http
.
StatusNotFound
,
Message
:
debugapi
.
ErrUnknownTransaction
,
}),
)
})
t
.
Run
(
"already imported"
,
func
(
t
*
testing
.
T
)
{
testServer
:=
newTestServer
(
t
,
testServerOptions
{
TransactionOpts
:
[]
mock
.
Option
{
mock
.
WithResendTransactionFunc
(
func
(
txHash
common
.
Hash
)
error
{
return
transaction
.
ErrAlreadyImported
}),
},
})
jsonhttptest
.
Request
(
t
,
testServer
.
Client
,
http
.
MethodPost
,
"/transactions/"
+
txHash
.
String
(),
http
.
StatusBadRequest
,
jsonhttptest
.
WithExpectedJSONResponse
(
jsonhttp
.
StatusResponse
{
Code
:
http
.
StatusBadRequest
,
Message
:
debugapi
.
ErrAlreadyImported
,
}),
)
})
t
.
Run
(
"other error"
,
func
(
t
*
testing
.
T
)
{
testServer
:=
newTestServer
(
t
,
testServerOptions
{
TransactionOpts
:
[]
mock
.
Option
{
mock
.
WithResendTransactionFunc
(
func
(
txHash
common
.
Hash
)
error
{
return
errors
.
New
(
"err"
)
}),
},
})
jsonhttptest
.
Request
(
t
,
testServer
.
Client
,
http
.
MethodPost
,
"/transactions/"
+
txHash
.
String
(),
http
.
StatusInternalServerError
,
jsonhttptest
.
WithExpectedJSONResponse
(
jsonhttp
.
StatusResponse
{
Code
:
http
.
StatusInternalServerError
,
Message
:
debugapi
.
ErrCantResendTransaction
,
}),
)
})
}
pkg/node/node.go
View file @
d2c668e9
...
...
@@ -102,6 +102,7 @@ type Bee struct {
pssCloser
io
.
Closer
ethClientCloser
func
()
transactionMonitorCloser
io
.
Closer
transactionCloser
io
.
Closer
recoveryHandleCleanup
func
()
listenerCloser
io
.
Closer
postageServiceCloser
io
.
Closer
...
...
@@ -259,6 +260,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
return
nil
,
fmt
.
Errorf
(
"init chain: %w"
,
err
)
}
b
.
ethClientCloser
=
swapBackend
.
Close
b
.
transactionCloser
=
tracerCloser
b
.
transactionMonitorCloser
=
transactionMonitor
}
...
...
@@ -717,7 +719,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
}
// inject dependencies and configure full debug api http path routes
debugAPIService
.
Configure
(
p2ps
,
pingPong
,
kad
,
lightNodes
,
storer
,
tagService
,
acc
,
pseudosettleService
,
o
.
SwapEnable
,
swapService
,
chequebookService
,
batchStore
)
debugAPIService
.
Configure
(
p2ps
,
pingPong
,
kad
,
lightNodes
,
storer
,
tagService
,
acc
,
pseudosettleService
,
o
.
SwapEnable
,
swapService
,
chequebookService
,
batchStore
,
transactionService
)
}
if
err
:=
kad
.
Start
(
p2pCtx
);
err
!=
nil
{
...
...
@@ -819,6 +821,7 @@ func (b *Bee) Shutdown(ctx context.Context) error {
go
func
()
{
defer
wg
.
Done
()
tryClose
(
b
.
transactionMonitorCloser
,
"transaction monitor"
)
tryClose
(
b
.
transactionCloser
,
"transaction"
)
}()
go
func
()
{
defer
wg
.
Done
()
...
...
pkg/settlement/swap/chequebook/cashout.go
View file @
d2c668e9
...
...
@@ -146,11 +146,12 @@ func (s *cashoutService) CashCheque(ctx context.Context, chequebook, recipient c
lim
=
300000
}
request
:=
&
transaction
.
TxRequest
{
To
:
&
chequebook
,
Data
:
callData
,
GasPrice
:
sctx
.
GetGasPrice
(
ctx
),
GasLimit
:
lim
,
Value
:
big
.
NewInt
(
0
),
To
:
&
chequebook
,
Data
:
callData
,
GasPrice
:
sctx
.
GetGasPrice
(
ctx
),
GasLimit
:
lim
,
Value
:
big
.
NewInt
(
0
),
Description
:
"cheque cashout"
,
}
txHash
,
err
:=
s
.
transactionService
.
Send
(
ctx
,
request
)
...
...
pkg/settlement/swap/chequebook/chequebook.go
View file @
d2c668e9
...
...
@@ -323,11 +323,12 @@ func (s *service) Withdraw(ctx context.Context, amount *big.Int) (hash common.Ha
}
request
:=
&
transaction
.
TxRequest
{
To
:
&
s
.
address
,
Data
:
callData
,
GasPrice
:
sctx
.
GetGasPrice
(
ctx
),
GasLimit
:
95000
,
Value
:
big
.
NewInt
(
0
),
To
:
&
s
.
address
,
Data
:
callData
,
GasPrice
:
sctx
.
GetGasPrice
(
ctx
),
GasLimit
:
95000
,
Value
:
big
.
NewInt
(
0
),
Description
:
fmt
.
Sprintf
(
"chequebook withdrawal of %d BZZ"
,
amount
),
}
txHash
,
err
:=
s
.
transactionService
.
Send
(
ctx
,
request
)
...
...
pkg/settlement/swap/chequebook/factory.go
View file @
d2c668e9
...
...
@@ -79,11 +79,12 @@ func (c *factory) Deploy(ctx context.Context, issuer common.Address, defaultHard
}
request
:=
&
transaction
.
TxRequest
{
To
:
&
c
.
address
,
Data
:
callData
,
GasPrice
:
sctx
.
GetGasPrice
(
ctx
),
GasLimit
:
175000
,
Value
:
big
.
NewInt
(
0
),
To
:
&
c
.
address
,
Data
:
callData
,
GasPrice
:
sctx
.
GetGasPrice
(
ctx
),
GasLimit
:
175000
,
Value
:
big
.
NewInt
(
0
),
Description
:
"chequebook deployment"
,
}
txHash
,
err
:=
c
.
transactionService
.
Send
(
ctx
,
request
)
...
...
pkg/settlement/swap/erc20/erc20.go
View file @
d2c668e9
...
...
@@ -77,11 +77,12 @@ func (c *erc20Service) Transfer(ctx context.Context, address common.Address, val
}
request
:=
&
transaction
.
TxRequest
{
To
:
&
c
.
address
,
Data
:
callData
,
GasPrice
:
sctx
.
GetGasPrice
(
ctx
),
GasLimit
:
90000
,
Value
:
big
.
NewInt
(
0
),
To
:
&
c
.
address
,
Data
:
callData
,
GasPrice
:
sctx
.
GetGasPrice
(
ctx
),
GasLimit
:
90000
,
Value
:
big
.
NewInt
(
0
),
Description
:
"token transfer"
,
}
txHash
,
err
:=
c
.
transactionService
.
Send
(
ctx
,
request
)
...
...
pkg/transaction/export_test.go
View file @
d2c668e9
...
...
@@ -4,8 +4,6 @@
package
transaction
type
StoredTransaction
=
storedTransaction
var
(
StoredTransactionKey
=
storedTransactionKey
)
pkg/transaction/mock/transaction.go
View file @
d2c668e9
...
...
@@ -22,6 +22,9 @@ type transactionServiceMock struct {
waitForReceipt
func
(
ctx
context
.
Context
,
txHash
common
.
Hash
)
(
receipt
*
types
.
Receipt
,
err
error
)
watchSentTransaction
func
(
txHash
common
.
Hash
)
(
chan
types
.
Receipt
,
chan
error
,
error
)
call
func
(
ctx
context
.
Context
,
request
*
transaction
.
TxRequest
)
(
result
[]
byte
,
err
error
)
pendingTransactions
func
()
([]
common
.
Hash
,
error
)
resendTransaction
func
(
txHash
common
.
Hash
)
error
storedTransaction
func
(
txHash
common
.
Hash
)
(
*
transaction
.
StoredTransaction
,
error
)
}
func
(
m
*
transactionServiceMock
)
Send
(
ctx
context
.
Context
,
request
*
transaction
.
TxRequest
)
(
txHash
common
.
Hash
,
err
error
)
{
...
...
@@ -52,6 +55,31 @@ func (m *transactionServiceMock) Call(ctx context.Context, request *transaction.
return
nil
,
errors
.
New
(
"not implemented"
)
}
func
(
m
*
transactionServiceMock
)
PendingTransactions
()
([]
common
.
Hash
,
error
)
{
if
m
.
pendingTransactions
!=
nil
{
return
m
.
pendingTransactions
()
}
return
nil
,
errors
.
New
(
"not implemented"
)
}
func
(
m
*
transactionServiceMock
)
ResendTransaction
(
txHash
common
.
Hash
)
error
{
if
m
.
resendTransaction
!=
nil
{
return
m
.
resendTransaction
(
txHash
)
}
return
errors
.
New
(
"not implemented"
)
}
func
(
m
*
transactionServiceMock
)
StoredTransaction
(
txHash
common
.
Hash
)
(
*
transaction
.
StoredTransaction
,
error
)
{
if
m
.
storedTransaction
!=
nil
{
return
m
.
storedTransaction
(
txHash
)
}
return
nil
,
errors
.
New
(
"not implemented"
)
}
func
(
m
*
transactionServiceMock
)
Close
()
error
{
return
nil
}
// Option is the option passed to the mock Chequebook service
type
Option
interface
{
apply
(
*
transactionServiceMock
)
...
...
@@ -79,6 +107,24 @@ func WithCallFunc(f func(ctx context.Context, request *transaction.TxRequest) (r
})
}
func
WithStoredTransactionFunc
(
f
func
(
txHash
common
.
Hash
)
(
*
transaction
.
StoredTransaction
,
error
))
Option
{
return
optionFunc
(
func
(
s
*
transactionServiceMock
)
{
s
.
storedTransaction
=
f
})
}
func
WithPendingTransactionsFunc
(
f
func
()
([]
common
.
Hash
,
error
))
Option
{
return
optionFunc
(
func
(
s
*
transactionServiceMock
)
{
s
.
pendingTransactions
=
f
})
}
func
WithResendTransactionFunc
(
f
func
(
txHash
common
.
Hash
)
error
)
Option
{
return
optionFunc
(
func
(
s
*
transactionServiceMock
)
{
s
.
resendTransaction
=
f
})
}
func
New
(
opts
...
Option
)
transaction
.
Service
{
mock
:=
new
(
transactionServiceMock
)
for
_
,
o
:=
range
opts
{
...
...
pkg/transaction/transaction.go
View file @
d2c668e9
...
...
@@ -7,8 +7,11 @@ package transaction
import
(
"errors"
"fmt"
"io"
"math/big"
"strings"
"sync"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
...
...
@@ -20,37 +23,44 @@ import (
)
const
(
noncePrefix
=
"transaction_nonce_"
storedTransactionPrefix
=
"transaction_stored_"
noncePrefix
=
"transaction_nonce_"
storedTransactionPrefix
=
"transaction_stored_"
pendingTransactionPrefix
=
"transaction_pending_"
)
var
(
// ErrTransactionReverted denotes that the sent transaction has been
// reverted.
ErrTransactionReverted
=
errors
.
New
(
"transaction reverted"
)
ErrUnknownTransaction
=
errors
.
New
(
"unknown transaction"
)
ErrAlreadyImported
=
errors
.
New
(
"already imported"
)
)
// TxRequest describes a request for a transaction that can be executed.
type
TxRequest
struct
{
To
*
common
.
Address
// recipient of the transaction
Data
[]
byte
// transaction data
GasPrice
*
big
.
Int
// gas price or nil if suggested gas price should be used
GasLimit
uint64
// gas limit or 0 if it should be estimated
Value
*
big
.
Int
// amount of wei to send
To
*
common
.
Address
// recipient of the transaction
Data
[]
byte
// transaction data
GasPrice
*
big
.
Int
// gas price or nil if suggested gas price should be used
GasLimit
uint64
// gas limit or 0 if it should be estimated
Value
*
big
.
Int
// amount of wei to send
Description
string
// optional description
}
type
storedTransaction
struct
{
To
*
common
.
Address
// recipient of the transaction
Data
[]
byte
// transaction data
GasPrice
*
big
.
Int
// used gas price
GasLimit
uint64
// used gas limit
Value
*
big
.
Int
// amount of wei to send
Nonce
uint64
// used nonce
type
StoredTransaction
struct
{
To
*
common
.
Address
// recipient of the transaction
Data
[]
byte
// transaction data
GasPrice
*
big
.
Int
// used gas price
GasLimit
uint64
// used gas limit
Value
*
big
.
Int
// amount of wei to send
Nonce
uint64
// used nonce
Created
int64
// creation timestamp
Description
string
// description
}
// Service is the service to send transactions. It takes care of gas price, gas
// limit and nonce management.
type
Service
interface
{
io
.
Closer
// Send creates a transaction based on the request and sends it.
Send
(
ctx
context
.
Context
,
request
*
TxRequest
)
(
txHash
common
.
Hash
,
err
error
)
// Call simulate a transaction based on the request.
...
...
@@ -62,10 +72,20 @@ type Service interface {
// This wraps the monitors watch function by loading the correct nonce from the store.
// This is only valid for transaction sent by this service.
WatchSentTransaction
(
txHash
common
.
Hash
)
(
<-
chan
types
.
Receipt
,
<-
chan
error
,
error
)
// StoredTransaction retrieves the stored information for the transaction
StoredTransaction
(
txHash
common
.
Hash
)
(
*
StoredTransaction
,
error
)
// PendingTransactions retrieves the list of all pending transaction hashes
PendingTransactions
()
([]
common
.
Hash
,
error
)
// Resend resends a previously sent transaction
// This operation can be useful if for some reason the transaction vanished from the eth networks pending pool
ResendTransaction
(
txHash
common
.
Hash
)
error
}
type
transactionService
struct
{
lock
sync
.
Mutex
wg
sync
.
WaitGroup
lock
sync
.
Mutex
ctx
context
.
Context
cancel
context
.
CancelFunc
logger
logging
.
Logger
backend
Backend
...
...
@@ -83,7 +103,11 @@ func NewService(logger logging.Logger, backend Backend, signer crypto.Signer, st
return
nil
,
err
}
return
&
transactionService
{
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
t
:=
&
transactionService
{
ctx
:
ctx
,
cancel
:
cancel
,
logger
:
logger
,
backend
:
backend
,
signer
:
signer
,
...
...
@@ -91,7 +115,17 @@ func NewService(logger logging.Logger, backend Backend, signer crypto.Signer, st
store
:
store
,
chainID
:
chainID
,
monitor
:
monitor
,
},
nil
}
pendingTxs
,
err
:=
t
.
PendingTransactions
()
if
err
!=
nil
{
return
nil
,
err
}
for
_
,
txHash
:=
range
pendingTxs
{
t
.
waitForPendingTx
(
txHash
)
}
return
t
,
nil
}
// Send creates and signs a transaction based on the request and sends it.
...
...
@@ -128,21 +162,51 @@ func (t *transactionService) Send(ctx context.Context, request *TxRequest) (txHa
txHash
=
signedTx
.
Hash
()
err
=
t
.
store
.
Put
(
storedTransactionKey
(
txHash
),
storedTransaction
{
To
:
signedTx
.
To
(),
Data
:
signedTx
.
Data
(),
GasPrice
:
signedTx
.
GasPrice
(),
GasLimit
:
signedTx
.
Gas
(),
Value
:
signedTx
.
Value
(),
Nonce
:
signedTx
.
Nonce
(),
err
=
t
.
store
.
Put
(
storedTransactionKey
(
txHash
),
StoredTransaction
{
To
:
signedTx
.
To
(),
Data
:
signedTx
.
Data
(),
GasPrice
:
signedTx
.
GasPrice
(),
GasLimit
:
signedTx
.
Gas
(),
Value
:
signedTx
.
Value
(),
Nonce
:
signedTx
.
Nonce
(),
Created
:
time
.
Now
()
.
Unix
(),
Description
:
request
.
Description
,
})
if
err
!=
nil
{
return
common
.
Hash
{},
err
}
err
=
t
.
store
.
Put
(
pendingTransactionKey
(
txHash
),
struct
{}{})
if
err
!=
nil
{
return
common
.
Hash
{},
err
}
t
.
waitForPendingTx
(
txHash
)
return
signedTx
.
Hash
(),
nil
}
func
(
t
*
transactionService
)
waitForPendingTx
(
txHash
common
.
Hash
)
{
t
.
wg
.
Add
(
1
)
go
func
()
{
defer
t
.
wg
.
Done
()
_
,
err
:=
t
.
WaitForReceipt
(
t
.
ctx
,
txHash
)
if
err
!=
nil
{
if
!
errors
.
Is
(
err
,
ErrTransactionCancelled
)
{
t
.
logger
.
Errorf
(
"error while waiting for pending transaction %x: %v"
,
txHash
,
err
)
}
t
.
logger
.
Warningf
(
"pending transaction %x cancelled"
,
txHash
)
}
else
{
t
.
logger
.
Tracef
(
"pending transaction %x confirmed"
,
txHash
)
}
err
=
t
.
store
.
Delete
(
pendingTransactionKey
(
txHash
))
if
err
!=
nil
{
t
.
logger
.
Errorf
(
"error while unregistering transaction as pending %x: %v"
,
txHash
,
err
)
}
}()
}
func
(
t
*
transactionService
)
Call
(
ctx
context
.
Context
,
request
*
TxRequest
)
([]
byte
,
error
)
{
msg
:=
ethereum
.
CallMsg
{
From
:
t
.
sender
,
...
...
@@ -160,10 +224,13 @@ func (t *transactionService) Call(ctx context.Context, request *TxRequest) ([]by
return
data
,
nil
}
func
(
t
*
transactionService
)
getStoredTransaction
(
txHash
common
.
Hash
)
(
*
s
toredTransaction
,
error
)
{
var
tx
s
toredTransaction
func
(
t
*
transactionService
)
StoredTransaction
(
txHash
common
.
Hash
)
(
*
S
toredTransaction
,
error
)
{
var
tx
S
toredTransaction
err
:=
t
.
store
.
Get
(
storedTransactionKey
(
txHash
),
&
tx
)
if
err
!=
nil
{
if
errors
.
Is
(
err
,
storage
.
ErrNotFound
)
{
return
nil
,
ErrUnknownTransaction
}
return
nil
,
err
}
return
&
tx
,
nil
...
...
@@ -226,6 +293,10 @@ func storedTransactionKey(txHash common.Hash) string {
return
fmt
.
Sprintf
(
"%s%x"
,
storedTransactionPrefix
,
txHash
)
}
func
pendingTransactionKey
(
txHash
common
.
Hash
)
string
{
return
fmt
.
Sprintf
(
"%s%x"
,
pendingTransactionPrefix
,
txHash
)
}
func
(
t
*
transactionService
)
nextNonce
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
onchainNonce
,
err
:=
t
.
backend
.
PendingNonceAt
(
ctx
,
t
.
sender
)
if
err
!=
nil
{
...
...
@@ -278,10 +349,73 @@ func (t *transactionService) WatchSentTransaction(txHash common.Hash) (<-chan ty
// loading the tx here guarantees it was in fact sent from this transaction service
// also it allows us to avoid having to load the transaction during the watch loop
storedTransaction
,
err
:=
t
.
get
StoredTransaction
(
txHash
)
storedTransaction
,
err
:=
t
.
StoredTransaction
(
txHash
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
return
t
.
monitor
.
WatchTransaction
(
txHash
,
storedTransaction
.
Nonce
)
}
func
(
t
*
transactionService
)
PendingTransactions
()
([]
common
.
Hash
,
error
)
{
var
txHashes
[]
common
.
Hash
=
make
([]
common
.
Hash
,
0
)
err
:=
t
.
store
.
Iterate
(
pendingTransactionPrefix
,
func
(
key
,
value
[]
byte
)
(
stop
bool
,
err
error
)
{
txHash
:=
common
.
HexToHash
(
strings
.
TrimPrefix
(
string
(
key
),
pendingTransactionPrefix
))
txHashes
=
append
(
txHashes
,
txHash
)
return
false
,
nil
})
if
err
!=
nil
{
return
nil
,
err
}
return
txHashes
,
nil
}
func
(
t
*
transactionService
)
ResendTransaction
(
txHash
common
.
Hash
)
error
{
storedTransaction
,
err
:=
t
.
StoredTransaction
(
txHash
)
if
err
!=
nil
{
return
err
}
var
tx
*
types
.
Transaction
if
storedTransaction
.
To
!=
nil
{
tx
=
types
.
NewTransaction
(
storedTransaction
.
Nonce
,
*
storedTransaction
.
To
,
storedTransaction
.
Value
,
storedTransaction
.
GasLimit
,
storedTransaction
.
GasPrice
,
storedTransaction
.
Data
,
)
}
else
{
tx
=
types
.
NewContractCreation
(
storedTransaction
.
Nonce
,
storedTransaction
.
Value
,
storedTransaction
.
GasLimit
,
storedTransaction
.
GasPrice
,
storedTransaction
.
Data
,
)
}
signedTx
,
err
:=
t
.
signer
.
SignTx
(
tx
,
t
.
chainID
)
if
err
!=
nil
{
return
err
}
if
signedTx
.
Hash
()
!=
txHash
{
return
errors
.
New
(
"transaction hash changed"
)
}
err
=
t
.
backend
.
SendTransaction
(
t
.
ctx
,
signedTx
)
if
err
!=
nil
{
if
strings
.
Contains
(
err
.
Error
(),
"already imported"
)
{
return
ErrAlreadyImported
}
}
return
nil
}
func
(
t
*
transactionService
)
Close
()
error
{
t
.
cancel
()
t
.
wg
.
Wait
()
return
nil
}
pkg/transaction/transaction_test.go
View file @
d2c668e9
...
...
@@ -124,6 +124,7 @@ func TestTransactionSend(t *testing.T) {
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
defer
transactionService
.
Close
()
txHash
,
err
:=
transactionService
.
Send
(
context
.
Background
(),
request
)
if
err
!=
nil
{
...
...
@@ -142,6 +143,47 @@ func TestTransactionSend(t *testing.T) {
if
storedNonce
!=
nonce
+
1
{
t
.
Fatalf
(
"nonce not stored correctly: want %d, got %d"
,
nonce
+
1
,
storedNonce
)
}
storedTransaction
,
err
:=
transactionService
.
StoredTransaction
(
txHash
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
storedTransaction
.
To
==
nil
||
*
storedTransaction
.
To
!=
recipient
{
t
.
Fatalf
(
"got wrong recipient in stored transaction. wanted %x, got %x"
,
recipient
,
storedTransaction
.
To
)
}
if
!
bytes
.
Equal
(
storedTransaction
.
Data
,
request
.
Data
)
{
t
.
Fatalf
(
"got wrong data in stored transaction. wanted %x, got %x"
,
request
.
Data
,
storedTransaction
.
Data
)
}
if
storedTransaction
.
Description
!=
request
.
Description
{
t
.
Fatalf
(
"got wrong description in stored transaction. wanted %x, got %x"
,
request
.
Description
,
storedTransaction
.
Description
)
}
if
storedTransaction
.
GasLimit
!=
estimatedGasLimit
{
t
.
Fatalf
(
"got wrong gas limit in stored transaction. wanted %d, got %d"
,
estimatedGasLimit
,
storedTransaction
.
GasLimit
)
}
if
suggestedGasPrice
.
Cmp
(
storedTransaction
.
GasPrice
)
!=
0
{
t
.
Fatalf
(
"got wrong gas price in stored transaction. wanted %d, got %d"
,
suggestedGasPrice
,
storedTransaction
.
GasPrice
)
}
if
storedTransaction
.
Nonce
!=
nonce
{
t
.
Fatalf
(
"got wrong nonce in stored transaction. wanted %d, got %d"
,
nonce
,
storedTransaction
.
Nonce
)
}
pending
,
err
:=
transactionService
.
PendingTransactions
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
len
(
pending
)
!=
1
{
t
.
Fatalf
(
"expected one pending transaction, got %d"
,
len
(
pending
))
}
if
pending
[
0
]
!=
txHash
{
t
.
Fatalf
(
"got wrong pending transaction. wanted %x, got %x"
,
txHash
,
pending
[
0
])
}
})
t
.
Run
(
"send_no_nonce"
,
func
(
t
*
testing
.
T
)
{
...
...
@@ -185,6 +227,7 @@ func TestTransactionSend(t *testing.T) {
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
defer
transactionService
.
Close
()
txHash
,
err
:=
transactionService
.
Send
(
context
.
Background
(),
request
)
if
err
!=
nil
{
...
...
@@ -311,6 +354,7 @@ func TestTransactionSend(t *testing.T) {
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
defer
transactionService
.
Close
()
txHash
,
err
:=
transactionService
.
Send
(
context
.
Background
(),
request
)
if
err
!=
nil
{
...
...
@@ -369,6 +413,7 @@ func TestTransactionWaitForReceipt(t *testing.T) {
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
defer
transactionService
.
Close
()
receipt
,
err
:=
transactionService
.
WaitForReceipt
(
context
.
Background
(),
txHash
)
if
err
!=
nil
{
...
...
@@ -379,3 +424,55 @@ func TestTransactionWaitForReceipt(t *testing.T) {
t
.
Fatal
(
"got wrong receipt"
)
}
}
func
TestTransactionResend
(
t
*
testing
.
T
)
{
logger
:=
logging
.
New
(
ioutil
.
Discard
,
0
)
recipient
:=
common
.
HexToAddress
(
"0xbbbddd"
)
chainID
:=
big
.
NewInt
(
5
)
nonce
:=
uint64
(
10
)
data
:=
[]
byte
{
1
,
2
,
3
,
4
}
gasPrice
:=
big
.
NewInt
(
0
)
gasLimit
:=
uint64
(
100000
)
value
:=
big
.
NewInt
(
0
)
store
:=
storemock
.
NewStateStore
()
defer
store
.
Close
()
signedTx
:=
types
.
NewTransaction
(
nonce
,
recipient
,
value
,
gasLimit
,
gasPrice
,
data
)
err
:=
store
.
Put
(
transaction
.
StoredTransactionKey
(
signedTx
.
Hash
()),
transaction
.
StoredTransaction
{
Nonce
:
nonce
,
To
:
&
recipient
,
Data
:
data
,
GasPrice
:
gasPrice
,
GasLimit
:
gasLimit
,
Value
:
value
,
})
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
transactionService
,
err
:=
transaction
.
NewService
(
logger
,
backendmock
.
New
(
backendmock
.
WithSendTransactionFunc
(
func
(
ctx
context
.
Context
,
tx
*
types
.
Transaction
)
error
{
if
tx
!=
signedTx
{
t
.
Fatal
(
"not sending signed transaction"
)
}
return
nil
}),
),
signerMockForTransaction
(
signedTx
,
recipient
,
chainID
,
t
),
store
,
chainID
,
monitormock
.
New
(),
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
defer
transactionService
.
Close
()
err
=
transactionService
.
ResendTransaction
(
signedTx
.
Hash
())
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment