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
9a2634d4
Unverified
Commit
9a2634d4
authored
Feb 18, 2021
by
Ralph Pichler
Committed by
GitHub
Feb 18, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
transaction: add simple nonce tracking (#1266)
parent
3c9f6cfe
Changes
5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
363 additions
and
158 deletions
+363
-158
signer.go
pkg/crypto/mock/signer.go
+13
-3
node.go
pkg/node/node.go
+1
-1
backend.go
pkg/settlement/swap/transaction/backend.go
+7
-0
transaction.go
pkg/settlement/swap/transaction/transaction.go
+73
-18
transaction_test.go
pkg/settlement/swap/transaction/transaction_test.go
+269
-136
No files found.
pkg/crypto/mock/signer.go
View file @
9a2634d4
...
@@ -14,11 +14,15 @@ import (
...
@@ -14,11 +14,15 @@ import (
)
)
type
signerMock
struct
{
type
signerMock
struct
{
signTx
func
(
transaction
*
types
.
Transaction
)
(
*
types
.
Transaction
,
error
)
signTx
func
(
transaction
*
types
.
Transaction
)
(
*
types
.
Transaction
,
error
)
signTypedData
func
(
*
eip712
.
TypedData
)
([]
byte
,
error
)
signTypedData
func
(
*
eip712
.
TypedData
)
([]
byte
,
error
)
ethereumAddress
func
()
(
common
.
Address
,
error
)
}
}
func
(
*
signerMock
)
EthereumAddress
()
(
common
.
Address
,
error
)
{
func
(
m
*
signerMock
)
EthereumAddress
()
(
common
.
Address
,
error
)
{
if
m
.
ethereumAddress
!=
nil
{
return
m
.
ethereumAddress
()
}
return
common
.
Address
{},
nil
return
common
.
Address
{},
nil
}
}
...
@@ -66,3 +70,9 @@ func WithSignTypedDataFunc(f func(*eip712.TypedData) ([]byte, error)) Option {
...
@@ -66,3 +70,9 @@ func WithSignTypedDataFunc(f func(*eip712.TypedData) ([]byte, error)) Option {
s
.
signTypedData
=
f
s
.
signTypedData
=
f
})
})
}
}
func
WithEthereumAddressFunc
(
f
func
()
(
common
.
Address
,
error
))
Option
{
return
optionFunc
(
func
(
s
*
signerMock
)
{
s
.
ethereumAddress
=
f
})
}
pkg/node/node.go
View file @
9a2634d4
...
@@ -153,7 +153,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
...
@@ -153,7 +153,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
transactionService
,
err
:=
transaction
.
NewService
(
logger
,
swapBackend
,
signer
)
transactionService
,
err
:=
transaction
.
NewService
(
logger
,
swapBackend
,
signer
,
stateStore
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
...
...
pkg/settlement/swap/transaction/backend.go
View file @
9a2634d4
...
@@ -24,6 +24,10 @@ type Backend interface {
...
@@ -24,6 +24,10 @@ type Backend interface {
BalanceAt
(
ctx
context
.
Context
,
address
common
.
Address
,
block
*
big
.
Int
)
(
*
big
.
Int
,
error
)
BalanceAt
(
ctx
context
.
Context
,
address
common
.
Address
,
block
*
big
.
Int
)
(
*
big
.
Int
,
error
)
}
}
// IsSynced will check if we are synced with the given blockchain backend. This
// is true if the current wall clock is after the block time of last block
// with the given maxDelay as the maximum duration we can be behind the block
// time.
func
IsSynced
(
ctx
context
.
Context
,
backend
Backend
,
maxDelay
time
.
Duration
)
(
bool
,
error
)
{
func
IsSynced
(
ctx
context
.
Context
,
backend
Backend
,
maxDelay
time
.
Duration
)
(
bool
,
error
)
{
number
,
err
:=
backend
.
BlockNumber
(
ctx
)
number
,
err
:=
backend
.
BlockNumber
(
ctx
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -40,6 +44,9 @@ func IsSynced(ctx context.Context, backend Backend, maxDelay time.Duration) (boo
...
@@ -40,6 +44,9 @@ func IsSynced(ctx context.Context, backend Backend, maxDelay time.Duration) (boo
return
blockTime
.
After
(
time
.
Now
()
.
UTC
()
.
Add
(
-
maxDelay
)),
nil
return
blockTime
.
After
(
time
.
Now
()
.
UTC
()
.
Add
(
-
maxDelay
)),
nil
}
}
// WaitSynced will wait until we are synced with the given blockchain backend,
// with the given maxDelay duration as the maximum time we can be behind the
// last block.
func
WaitSynced
(
ctx
context
.
Context
,
backend
Backend
,
maxDelay
time
.
Duration
)
error
{
func
WaitSynced
(
ctx
context
.
Context
,
backend
Backend
,
maxDelay
time
.
Duration
)
error
{
for
{
for
{
synced
,
err
:=
IsSynced
(
ctx
,
backend
,
maxDelay
)
synced
,
err
:=
IsSynced
(
ctx
,
backend
,
maxDelay
)
...
...
pkg/settlement/swap/transaction/transaction.go
View file @
9a2634d4
...
@@ -6,7 +6,9 @@ package transaction
...
@@ -6,7 +6,9 @@ package transaction
import
(
import
(
"errors"
"errors"
"fmt"
"math/big"
"math/big"
"sync"
"time"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum"
...
@@ -14,10 +16,17 @@ import (
...
@@ -14,10 +16,17 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/storage"
"golang.org/x/net/context"
"golang.org/x/net/context"
)
)
const
(
noncePrefix
=
"transaction_nonce_"
)
var
(
var
(
// ErrTransactionReverted denotes that the sent transaction has been
// reverted.
ErrTransactionReverted
=
errors
.
New
(
"transaction reverted"
)
ErrTransactionReverted
=
errors
.
New
(
"transaction reverted"
)
)
)
...
@@ -30,7 +39,8 @@ type TxRequest struct {
...
@@ -30,7 +39,8 @@ type TxRequest struct {
Value
*
big
.
Int
// amount of wei to send
Value
*
big
.
Int
// amount of wei to send
}
}
// Service is the service to send transactions. It takes care of gas price, gas limit and nonce management.
// Service is the service to send transactions. It takes care of gas price, gas
// limit and nonce management.
type
Service
interface
{
type
Service
interface
{
// Send creates a transaction based on the request and sends it.
// Send creates a transaction based on the request and sends it.
Send
(
ctx
context
.
Context
,
request
*
TxRequest
)
(
txHash
common
.
Hash
,
err
error
)
Send
(
ctx
context
.
Context
,
request
*
TxRequest
)
(
txHash
common
.
Hash
,
err
error
)
...
@@ -39,14 +49,17 @@ type Service interface {
...
@@ -39,14 +49,17 @@ type Service interface {
}
}
type
transactionService
struct
{
type
transactionService
struct
{
lock
sync
.
Mutex
logger
logging
.
Logger
logger
logging
.
Logger
backend
Backend
backend
Backend
signer
crypto
.
Signer
signer
crypto
.
Signer
sender
common
.
Address
sender
common
.
Address
store
storage
.
StateStorer
}
}
// NewService creates a new transaction service.
// NewService creates a new transaction service.
func
NewService
(
logger
logging
.
Logger
,
backend
Backend
,
signer
crypto
.
Signer
)
(
Service
,
error
)
{
func
NewService
(
logger
logging
.
Logger
,
backend
Backend
,
signer
crypto
.
Signer
,
store
storage
.
StateStorer
)
(
Service
,
error
)
{
senderAddress
,
err
:=
signer
.
EthereumAddress
()
senderAddress
,
err
:=
signer
.
EthereumAddress
()
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
...
@@ -56,12 +69,21 @@ func NewService(logger logging.Logger, backend Backend, signer crypto.Signer) (S
...
@@ -56,12 +69,21 @@ func NewService(logger logging.Logger, backend Backend, signer crypto.Signer) (S
backend
:
backend
,
backend
:
backend
,
signer
:
signer
,
signer
:
signer
,
sender
:
senderAddress
,
sender
:
senderAddress
,
store
:
store
,
},
nil
},
nil
}
}
// Send creates and signs a transaction based on the request and sends it.
// Send creates and signs a transaction based on the request and sends it.
func
(
t
*
transactionService
)
Send
(
ctx
context
.
Context
,
request
*
TxRequest
)
(
txHash
common
.
Hash
,
err
error
)
{
func
(
t
*
transactionService
)
Send
(
ctx
context
.
Context
,
request
*
TxRequest
)
(
txHash
common
.
Hash
,
err
error
)
{
tx
,
err
:=
prepareTransaction
(
ctx
,
request
,
t
.
sender
,
t
.
backend
)
t
.
lock
.
Lock
()
defer
t
.
lock
.
Unlock
()
nonce
,
err
:=
t
.
nextNonce
(
ctx
)
if
err
!=
nil
{
return
common
.
Hash
{},
err
}
tx
,
err
:=
prepareTransaction
(
ctx
,
request
,
t
.
sender
,
t
.
backend
,
nonce
)
if
err
!=
nil
{
if
err
!=
nil
{
return
common
.
Hash
{},
err
return
common
.
Hash
{},
err
}
}
...
@@ -76,10 +98,16 @@ func (t *transactionService) Send(ctx context.Context, request *TxRequest) (txHa
...
@@ -76,10 +98,16 @@ func (t *transactionService) Send(ctx context.Context, request *TxRequest) (txHa
return
common
.
Hash
{},
err
return
common
.
Hash
{},
err
}
}
err
=
t
.
putNonce
(
nonce
+
1
)
if
err
!=
nil
{
return
common
.
Hash
{},
err
}
return
signedTx
.
Hash
(),
nil
return
signedTx
.
Hash
(),
nil
}
}
// WaitForReceipt waits until either the transaction with the given hash has been mined or the context is cancelled.
// WaitForReceipt waits until either the transaction with the given hash has
// been mined or the context is cancelled.
func
(
t
*
transactionService
)
WaitForReceipt
(
ctx
context
.
Context
,
txHash
common
.
Hash
)
(
receipt
*
types
.
Receipt
,
err
error
)
{
func
(
t
*
transactionService
)
WaitForReceipt
(
ctx
context
.
Context
,
txHash
common
.
Hash
)
(
receipt
*
types
.
Receipt
,
err
error
)
{
for
{
for
{
receipt
,
err
:=
t
.
backend
.
TransactionReceipt
(
ctx
,
txHash
)
receipt
,
err
:=
t
.
backend
.
TransactionReceipt
(
ctx
,
txHash
)
...
@@ -102,7 +130,7 @@ func (t *transactionService) WaitForReceipt(ctx context.Context, txHash common.H
...
@@ -102,7 +130,7 @@ func (t *transactionService) WaitForReceipt(ctx context.Context, txHash common.H
}
}
// prepareTransaction creates a signable transaction based on a request.
// prepareTransaction creates a signable transaction based on a request.
func
prepareTransaction
(
ctx
context
.
Context
,
request
*
TxRequest
,
from
common
.
Address
,
backend
Backend
)
(
tx
*
types
.
Transaction
,
err
error
)
{
func
prepareTransaction
(
ctx
context
.
Context
,
request
*
TxRequest
,
from
common
.
Address
,
backend
Backend
,
nonce
uint64
)
(
tx
*
types
.
Transaction
,
err
error
)
{
var
gasLimit
uint64
var
gasLimit
uint64
if
request
.
GasLimit
==
0
{
if
request
.
GasLimit
==
0
{
gasLimit
,
err
=
backend
.
EstimateGas
(
ctx
,
ethereum
.
CallMsg
{
gasLimit
,
err
=
backend
.
EstimateGas
(
ctx
,
ethereum
.
CallMsg
{
...
@@ -127,11 +155,6 @@ func prepareTransaction(ctx context.Context, request *TxRequest, from common.Add
...
@@ -127,11 +155,6 @@ func prepareTransaction(ctx context.Context, request *TxRequest, from common.Add
gasPrice
=
request
.
GasPrice
gasPrice
=
request
.
GasPrice
}
}
nonce
,
err
:=
backend
.
PendingNonceAt
(
ctx
,
from
)
if
err
!=
nil
{
return
nil
,
err
}
if
request
.
To
!=
nil
{
if
request
.
To
!=
nil
{
return
types
.
NewTransaction
(
return
types
.
NewTransaction
(
nonce
,
nonce
,
...
@@ -141,13 +164,45 @@ func prepareTransaction(ctx context.Context, request *TxRequest, from common.Add
...
@@ -141,13 +164,45 @@ func prepareTransaction(ctx context.Context, request *TxRequest, from common.Add
gasPrice
,
gasPrice
,
request
.
Data
,
request
.
Data
,
),
nil
),
nil
}
else
{
return
types
.
NewContractCreation
(
nonce
,
request
.
Value
,
gasLimit
,
gasPrice
,
request
.
Data
,
),
nil
}
}
return
types
.
NewContractCreation
(
nonce
,
request
.
Value
,
gasLimit
,
gasPrice
,
request
.
Data
,
),
nil
}
func
(
t
*
transactionService
)
nonceKey
()
string
{
return
fmt
.
Sprintf
(
"%s%x"
,
noncePrefix
,
t
.
sender
)
}
func
(
t
*
transactionService
)
nextNonce
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
onchainNonce
,
err
:=
t
.
backend
.
PendingNonceAt
(
ctx
,
t
.
sender
)
if
err
!=
nil
{
return
0
,
err
}
var
nonce
uint64
err
=
t
.
store
.
Get
(
t
.
nonceKey
(),
&
nonce
)
if
err
!=
nil
{
// If no nonce was found locally used whatever we get from the backend.
if
errors
.
Is
(
err
,
storage
.
ErrNotFound
)
{
return
onchainNonce
,
nil
}
return
0
,
err
}
// If the nonce onchain is larger than what we have there were external
// transactions and we need to update our nonce.
if
onchainNonce
>
nonce
{
return
onchainNonce
,
nil
}
return
nonce
,
nil
}
func
(
t
*
transactionService
)
putNonce
(
nonce
uint64
)
error
{
return
t
.
store
.
Put
(
t
.
nonceKey
(),
nonce
)
}
}
pkg/settlement/swap/transaction/transaction_test.go
View file @
9a2634d4
This diff is collapsed.
Click to expand it.
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