fixes issue when non-originator can complete a tx
This commit is contained in:
parent
80510c02d2
commit
87e345c1cc
|
@ -529,6 +529,12 @@ func testCompleteTransaction(t *testing.T) bool {
|
|||
// reset queue
|
||||
backend.TransactionQueue().Reset()
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := geth.SelectAccount(testAddress, testAddressPassword); err != nil {
|
||||
t.Errorf("cannot select account: %v", testAddress)
|
||||
return false
|
||||
}
|
||||
|
||||
// make sure you panic if transaction complete doesn't return
|
||||
queuedTxCompleted := make(chan struct{}, 1)
|
||||
abortPanic := make(chan struct{}, 1)
|
||||
|
@ -608,6 +614,12 @@ func testCompleteMultipleQueuedTransactions(t *testing.T) bool {
|
|||
// reset queue
|
||||
backend.TransactionQueue().Reset()
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := geth.SelectAccount(testAddress, testAddressPassword); err != nil {
|
||||
t.Errorf("cannot select account: %v", testAddress)
|
||||
return false
|
||||
}
|
||||
|
||||
// make sure you panic if transaction complete doesn't return
|
||||
testTxCount := 3
|
||||
txIds := make(chan string, testTxCount)
|
||||
|
@ -737,6 +749,12 @@ func testDiscardTransaction(t *testing.T) bool {
|
|||
// reset queue
|
||||
backend.TransactionQueue().Reset()
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := geth.SelectAccount(testAddress, testAddressPassword); err != nil {
|
||||
t.Errorf("cannot select account: %v", testAddress)
|
||||
return false
|
||||
}
|
||||
|
||||
// make sure you panic if transaction complete doesn't return
|
||||
completeQueuedTransaction := make(chan struct{}, 1)
|
||||
geth.PanicAfter(20*time.Second, completeQueuedTransaction, "TestDiscardQueuedTransactions")
|
||||
|
@ -851,6 +869,12 @@ func testDiscardMultipleQueuedTransactions(t *testing.T) bool {
|
|||
// reset queue
|
||||
backend.TransactionQueue().Reset()
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := geth.SelectAccount(testAddress, testAddressPassword); err != nil {
|
||||
t.Errorf("cannot select account: %v", testAddress)
|
||||
return false
|
||||
}
|
||||
|
||||
// make sure you panic if transaction complete doesn't return
|
||||
testTxCount := 3
|
||||
txIds := make(chan string, testTxCount)
|
||||
|
|
|
@ -320,3 +320,11 @@ func (m *NodeManager) populateStaticPeers() {
|
|||
m.AddPeer(enode)
|
||||
}
|
||||
}
|
||||
|
||||
func (k *SelectedExtKey) Hex() string {
|
||||
if k == nil {
|
||||
return "0x0"
|
||||
}
|
||||
|
||||
return k.Address.Hex()
|
||||
}
|
||||
|
|
|
@ -86,7 +86,10 @@ func CompleteTransaction(id, password string) (common.Hash, error) {
|
|||
|
||||
backend := lightEthereum.StatusBackend
|
||||
|
||||
return backend.CompleteQueuedTransaction(status.QueuedTxId(id), password)
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, status.SelectedAccountKey, NodeManagerInstance().SelectedAccount.Hex())
|
||||
|
||||
return backend.CompleteQueuedTransaction(ctx, status.QueuedTxId(id), password)
|
||||
}
|
||||
|
||||
func CompleteTransactions(ids, password string) map[string]RawCompleteTransactionResult {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/les/status"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
@ -28,6 +29,13 @@ func TestQueuedTransactions(t *testing.T) {
|
|||
}
|
||||
backend := lightEthereum.StatusBackend
|
||||
|
||||
// create an account
|
||||
sampleAddress, _, _, err := geth.CreateAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("could not create account: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// make sure you panic if transaction complete doesn't return
|
||||
completeQueuedTransaction := make(chan struct{}, 1)
|
||||
geth.PanicAfter(20*time.Second, completeQueuedTransaction, "TestQueuedTransactions")
|
||||
|
@ -45,6 +53,27 @@ func TestQueuedTransactions(t *testing.T) {
|
|||
t.Logf("transaction queued (will be completed in 5 secs): {id: %s}\n", event["id"].(string))
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// the first call will fail (we are not logged in, but trying to complete tx)
|
||||
if txHash, err = geth.CompleteTransaction(event["id"].(string), testAddressPassword); err != status.ErrInvalidCompleteTxSender {
|
||||
t.Errorf("expected error on queued transation[%v] not thrown: expected %v, got %v", event["id"], status.ErrInvalidCompleteTxSender, err)
|
||||
return
|
||||
}
|
||||
|
||||
// the second call will also fail (we are logged in as different user)
|
||||
if err := geth.SelectAccount(sampleAddress, newAccountPassword); err != nil {
|
||||
t.Errorf("cannot select account: %v", sampleAddress)
|
||||
return
|
||||
}
|
||||
if txHash, err = geth.CompleteTransaction(event["id"].(string), testAddressPassword); err != status.ErrInvalidCompleteTxSender {
|
||||
t.Errorf("expected error on queued transation[%v] not thrown: expected %v, got %v", event["id"], status.ErrInvalidCompleteTxSender, err)
|
||||
return
|
||||
}
|
||||
|
||||
// the third call will work as expected (as we are logged in with correct credentials)
|
||||
if err := geth.SelectAccount(testAddress, testAddressPassword); err != nil {
|
||||
t.Errorf("cannot select account: %v", testAddress)
|
||||
return
|
||||
}
|
||||
if txHash, err = geth.CompleteTransaction(event["id"].(string), testAddressPassword); err != nil {
|
||||
t.Errorf("cannot complete queued transation[%v]: %v", event["id"], err)
|
||||
return
|
||||
|
@ -98,6 +127,12 @@ func TestDoubleCompleteQueuedTransactions(t *testing.T) {
|
|||
}
|
||||
backend := lightEthereum.StatusBackend
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := geth.SelectAccount(testAddress, testAddressPassword); err != nil {
|
||||
t.Errorf("cannot select account: %v", testAddress)
|
||||
return
|
||||
}
|
||||
|
||||
// make sure you panic if transaction complete doesn't return
|
||||
completeQueuedTransaction := make(chan struct{}, 1)
|
||||
geth.PanicAfter(20*time.Second, completeQueuedTransaction, "TestQueuedTransactions")
|
||||
|
@ -119,8 +154,8 @@ func TestDoubleCompleteQueuedTransactions(t *testing.T) {
|
|||
|
||||
// try with wrong password
|
||||
// make sure that tx is NOT removed from the queue (by re-trying with the correct password)
|
||||
if _, err = geth.CompleteTransaction(txId, testAddressPassword+"wrong"); err == nil {
|
||||
t.Error("expects wrong password error, but call succeeded")
|
||||
if _, err = geth.CompleteTransaction(txId, testAddressPassword+"wrong"); err != accounts.ErrDecrypt {
|
||||
t.Errorf("expects wrong password error, but call succeeded (or got another error: %v)", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -222,6 +257,12 @@ func TestDiscardQueuedTransactions(t *testing.T) {
|
|||
// reset queue
|
||||
backend.TransactionQueue().Reset()
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := geth.SelectAccount(testAddress, testAddressPassword); err != nil {
|
||||
t.Errorf("cannot select account: %v", testAddress)
|
||||
return
|
||||
}
|
||||
|
||||
// make sure you panic if transaction complete doesn't return
|
||||
completeQueuedTransaction := make(chan struct{}, 1)
|
||||
geth.PanicAfter(20*time.Second, completeQueuedTransaction, "TestDiscardQueuedTransactions")
|
||||
|
@ -337,6 +378,12 @@ func TestCompleteMultipleQueuedTransactions(t *testing.T) {
|
|||
// reset queue
|
||||
backend.TransactionQueue().Reset()
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := geth.SelectAccount(testAddress, testAddressPassword); err != nil {
|
||||
t.Errorf("cannot select account: %v", testAddress)
|
||||
return
|
||||
}
|
||||
|
||||
// make sure you panic if transaction complete doesn't return
|
||||
testTxCount := 3
|
||||
txIds := make(chan string, testTxCount)
|
||||
|
@ -462,6 +509,12 @@ func TestDiscardMultipleQueuedTransactions(t *testing.T) {
|
|||
// reset queue
|
||||
backend.TransactionQueue().Reset()
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := geth.SelectAccount(testAddress, testAddressPassword); err != nil {
|
||||
t.Errorf("cannot select account: %v", testAddress)
|
||||
return
|
||||
}
|
||||
|
||||
// make sure you panic if transaction complete doesn't return
|
||||
testTxCount := 3
|
||||
txIds := make(chan string, testTxCount)
|
||||
|
@ -606,6 +659,12 @@ func TestNonExistentQueuedTransactions(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := geth.SelectAccount(testAddress, testAddressPassword); err != nil {
|
||||
t.Errorf("cannot select account: %v", testAddress)
|
||||
return
|
||||
}
|
||||
|
||||
// make sure you panic if transaction complete doesn't return
|
||||
completeQueuedTransaction := make(chan struct{}, 1)
|
||||
geth.PanicAfter(20*time.Second, completeQueuedTransaction, "TestQueuedTransactions")
|
||||
|
@ -660,6 +719,12 @@ func TestEvictionOfQueuedTransactions(t *testing.T) {
|
|||
}
|
||||
backend := lightEthereum.StatusBackend
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := geth.SelectAccount(testAddress, testAddressPassword); err != nil {
|
||||
t.Errorf("cannot select account: %v", testAddress)
|
||||
return
|
||||
}
|
||||
|
||||
// make sure you panic if transaction complete doesn't return
|
||||
completeQueuedTransaction := make(chan struct{}, 1)
|
||||
geth.PanicAfter(20*time.Second, completeQueuedTransaction, "TestQueuedTransactions")
|
||||
|
|
|
@ -208,6 +208,12 @@ func TestJailSendQueuedTransaction(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := geth.SelectAccount(TEST_ADDRESS, TEST_ADDRESS_PASSWORD); err != nil {
|
||||
t.Errorf("cannot select account: %v", TEST_ADDRESS)
|
||||
return
|
||||
}
|
||||
|
||||
txParams := `{
|
||||
"from": "` + TEST_ADDRESS + `",
|
||||
"to": "0xf82da7547534045b4e00442bc89e16186cf8c272",
|
||||
|
|
|
@ -1126,6 +1126,16 @@ func (s *PublicTransactionPoolAPI) CompleteQueuedTransaction(ctx context.Context
|
|||
return common.Hash{}, err
|
||||
}
|
||||
|
||||
// make sure that only account which created the tx can complete it
|
||||
selectedAccountAddress := "0x0"
|
||||
if address, ok := ctx.Value(status.SelectedAccountKey).(string); ok {
|
||||
selectedAccountAddress = address
|
||||
}
|
||||
if args.From.Hex() != selectedAccountAddress {
|
||||
glog.V(logger.Info).Infof("Failed to complete tx by %s (when logged in as %s)", args.From.Hex(), selectedAccountAddress)
|
||||
return common.Hash{}, status.ErrInvalidCompleteTxSender
|
||||
}
|
||||
|
||||
nonce, err := s.b.GetPoolNonce(ctx, args.From)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
|
|
|
@ -85,13 +85,13 @@ func (b *StatusBackend) SendTransaction(ctx context.Context, args status.SendTxA
|
|||
}
|
||||
|
||||
// CompleteQueuedTransaction wraps call to PublicTransactionPoolAPI.CompleteQueuedTransaction
|
||||
func (b *StatusBackend) CompleteQueuedTransaction(id status.QueuedTxId, passphrase string) (common.Hash, error) {
|
||||
func (b *StatusBackend) CompleteQueuedTransaction(ctx context.Context, id status.QueuedTxId, passphrase string) (common.Hash, error) {
|
||||
queuedTx, err := b.txQueue.Get(id)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
|
||||
hash, err := b.txapi.CompleteQueuedTransaction(context.Background(), SendTxArgs(queuedTx.Args), passphrase)
|
||||
hash, err := b.txapi.CompleteQueuedTransaction(ctx, SendTxArgs(queuedTx.Args), passphrase)
|
||||
|
||||
// on password error, notify the app, and keep tx in queue (so that CompleteQueuedTransaction() can be resent)
|
||||
if err == accounts.ErrDecrypt {
|
||||
|
@ -99,6 +99,12 @@ func (b *StatusBackend) CompleteQueuedTransaction(id status.QueuedTxId, passphra
|
|||
return hash, err // SendTransaction is still blocked
|
||||
}
|
||||
|
||||
// when incorrect sender tries to complete the account, notify and keep tx in queue (so that correct sender can complete)
|
||||
if err == status.ErrInvalidCompleteTxSender {
|
||||
b.NotifyOnQueuedTxReturn(queuedTx, err)
|
||||
return hash, err // SendTransaction is still blocked
|
||||
}
|
||||
|
||||
// allow SendTransaction to return
|
||||
queuedTx.Hash = hash
|
||||
queuedTx.Err = err
|
||||
|
|
|
@ -14,12 +14,14 @@ const (
|
|||
DefaultTxQueueCap = int(35) // how many items can be queued
|
||||
DefaultTxSendQueueCap = int(70) // how many items can be passed to sendTransaction() w/o blocking
|
||||
DefaultTxSendCompletionTimeout = 300 // how many seconds to wait before returning result in sentTransaction()
|
||||
SelectedAccountKey = "selected_account"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrQueuedTxIdNotFound = errors.New("transaction hash not found")
|
||||
ErrQueuedTxTimedOut = errors.New("transaction sending timed out")
|
||||
ErrQueuedTxDiscarded = errors.New("transaction has been discarded")
|
||||
ErrQueuedTxIdNotFound = errors.New("transaction hash not found")
|
||||
ErrQueuedTxTimedOut = errors.New("transaction sending timed out")
|
||||
ErrQueuedTxDiscarded = errors.New("transaction has been discarded")
|
||||
ErrInvalidCompleteTxSender = errors.New("transaction can only be completed by the same account which created it")
|
||||
)
|
||||
|
||||
// TxQueue is capped container that holds pending transactions
|
||||
|
@ -177,8 +179,12 @@ func (q *TxQueue) NotifyOnQueuedTxReturn(queuedTx *QueuedTx, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// remove from queue on any error (except for password related one) and propagate
|
||||
if err != accounts.ErrDecrypt {
|
||||
// remove from queue on any error (except for transient ones) and propagate
|
||||
transientErrs := map[error]bool{
|
||||
accounts.ErrDecrypt: true, // wrong password
|
||||
ErrInvalidCompleteTxSender: true, // completing tx create from another account
|
||||
}
|
||||
if !transientErrs[err] { // remove only on unrecoverable errors
|
||||
q.Remove(queuedTx.Id)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue