Skip to content

Commit e01d855

Browse files
CoderZhidustinxie
authored andcommitted
actpool changes
1 parent 35f5489 commit e01d855

File tree

9 files changed

+622
-171
lines changed

9 files changed

+622
-171
lines changed

actpool/accountpool.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Copyright (c) 2019 IoTeX Foundation
2+
// This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
3+
// or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
4+
// This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
5+
6+
package actpool
7+
8+
import (
9+
"container/heap"
10+
"math/big"
11+
"time"
12+
13+
"github.com/iotexproject/iotex-core/action"
14+
)
15+
16+
type (
17+
accountItem struct {
18+
index int
19+
actQueue ActQueue
20+
}
21+
22+
accountPriorityQueue []*accountItem
23+
24+
accountPool struct {
25+
accounts map[string]*accountItem
26+
priorityQueue accountPriorityQueue
27+
}
28+
)
29+
30+
func newAccountPool() *accountPool {
31+
ap := &accountPool{
32+
priorityQueue: accountPriorityQueue{},
33+
accounts: map[string]*accountItem{},
34+
}
35+
heap.Init(&ap.priorityQueue)
36+
37+
return ap
38+
}
39+
40+
func (ap *accountPool) Account(addr string) ActQueue {
41+
if account, ok := ap.accounts[addr]; ok {
42+
return account.actQueue
43+
}
44+
return nil
45+
}
46+
47+
func (ap *accountPool) PopAccount(addr string) ActQueue {
48+
if account, ok := ap.accounts[addr]; ok {
49+
heap.Remove(&ap.priorityQueue, account.index)
50+
delete(ap.accounts, addr)
51+
return account.actQueue
52+
}
53+
54+
return nil
55+
}
56+
57+
func (ap *accountPool) PutAction(
58+
addr string,
59+
actpool *actPool,
60+
pendingNonce uint64,
61+
confirmedBalance *big.Int,
62+
expiry time.Duration,
63+
act action.SealedEnvelope,
64+
) error {
65+
account, ok := ap.accounts[addr]
66+
if !ok {
67+
queue := NewActQueue(
68+
actpool,
69+
addr,
70+
pendingNonce,
71+
confirmedBalance,
72+
WithTimeOut(expiry),
73+
)
74+
if err := queue.Put(act); err != nil {
75+
return err
76+
}
77+
ap.accounts[addr] = &accountItem{
78+
index: len(ap.accounts),
79+
actQueue: queue,
80+
}
81+
heap.Push(&ap.priorityQueue, ap.accounts[addr])
82+
return nil
83+
}
84+
85+
if err := account.actQueue.Put(act); err != nil {
86+
return err
87+
}
88+
heap.Fix(&ap.priorityQueue, account.index)
89+
90+
return nil
91+
}
92+
93+
func (ap *accountPool) PopPeek() *action.SealedEnvelope {
94+
if len(ap.accounts) == 0 {
95+
return nil
96+
}
97+
act := ap.priorityQueue[0].actQueue.PopActionWithLargestNonce()
98+
heap.Fix(&ap.priorityQueue, 0)
99+
100+
return act
101+
}
102+
103+
func (ap *accountPool) Range(callback func(addr string, acct ActQueue)) {
104+
for addr, account := range ap.accounts {
105+
callback(addr, account.actQueue)
106+
heap.Fix(&ap.priorityQueue, account.index)
107+
}
108+
}
109+
110+
func (ap *accountPool) DeleteIfEmpty(addr string) {
111+
account, ok := ap.accounts[addr]
112+
if !ok {
113+
return
114+
}
115+
if account.actQueue.Empty() {
116+
heap.Remove(&ap.priorityQueue, account.index)
117+
delete(ap.accounts, addr)
118+
}
119+
}
120+
121+
func (aq accountPriorityQueue) Len() int { return len(aq) }
122+
func (aq accountPriorityQueue) Less(i, j int) bool {
123+
is, igp := aq[i].actQueue.NextAction()
124+
js, jgp := aq[j].actQueue.NextAction()
125+
if jgp == nil {
126+
return true
127+
}
128+
if igp == nil {
129+
return false
130+
}
131+
if !is && js {
132+
return true
133+
}
134+
if !js && is {
135+
return false
136+
}
137+
138+
return igp.Cmp(jgp) < 0
139+
}
140+
141+
func (aq accountPriorityQueue) Swap(i, j int) {
142+
aq[i], aq[j] = aq[j], aq[i]
143+
aq[i].index = i
144+
aq[j].index = j
145+
}
146+
147+
func (aq *accountPriorityQueue) Push(x interface{}) {
148+
if in, ok := x.(*accountItem); ok {
149+
in.index = len(*aq)
150+
*aq = append(*aq, in)
151+
}
152+
}
153+
154+
func (aq *accountPriorityQueue) Pop() interface{} {
155+
old := *aq
156+
n := len(old)
157+
if n == 0 {
158+
return nil
159+
}
160+
x := old[n-1]
161+
old[n-1] = nil // avoid memory leak
162+
*aq = old[0 : n-1]
163+
return x
164+
}

actpool/accountpool_test.go

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package actpool
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/iotexproject/iotex-core/action"
11+
)
12+
13+
var (
14+
_balance = big.NewInt(0).SetBytes([]byte("100000000000000000000000"))
15+
_expireTime = time.Hour
16+
)
17+
18+
func TestAccountPool_PopPeek(t *testing.T) {
19+
r := require.New(t)
20+
t.Run("empty pool", func(t *testing.T) {
21+
ap := newAccountPool()
22+
r.Nil(ap.PopPeek())
23+
})
24+
t.Run("one action", func(t *testing.T) {
25+
ap := newAccountPool()
26+
tsf1, err := action.SignedTransfer(_addr2, _priKey1, 1, big.NewInt(100), nil, uint64(0), big.NewInt(1))
27+
r.NoError(err)
28+
r.NoError(ap.PutAction(_addr1, nil, 0, _balance, _expireTime, tsf1))
29+
r.Equal(&tsf1, ap.PopPeek())
30+
r.Equal(0, ap.Account(_addr1).Len())
31+
})
32+
t.Run("multiple actions in one account", func(t *testing.T) {
33+
ap := newAccountPool()
34+
tsf1, err := action.SignedTransfer(_addr1, _priKey1, 1, big.NewInt(100), nil, uint64(0), big.NewInt(1))
35+
r.NoError(err)
36+
tsf2, err := action.SignedTransfer(_addr1, _priKey1, 2, big.NewInt(100), nil, uint64(0), big.NewInt(1))
37+
r.NoError(err)
38+
r.NoError(ap.PutAction(_addr1, nil, 1, _balance, _expireTime, tsf1))
39+
r.NoError(ap.PutAction(_addr1, nil, 1, _balance, _expireTime, tsf2))
40+
r.Equal(&tsf2, ap.PopPeek())
41+
r.Equal(&tsf1, ap.PopPeek())
42+
r.Nil(ap.PopPeek())
43+
r.Equal(0, ap.Account(_addr1).Len())
44+
})
45+
t.Run("peek with pending nonce", func(t *testing.T) {
46+
ap := newAccountPool()
47+
tsf1, err := action.SignedTransfer(_addr1, _priKey1, 1, big.NewInt(100), nil, uint64(0), big.NewInt(1))
48+
r.NoError(err)
49+
tsf2, err := action.SignedTransfer(_addr2, _priKey2, 2, big.NewInt(100), nil, uint64(0), big.NewInt(1))
50+
r.NoError(err)
51+
r.NoError(ap.PutAction(_addr1, nil, 1, _balance, _expireTime, tsf1))
52+
r.NoError(ap.PutAction(_addr2, nil, 1, _balance, _expireTime, tsf2))
53+
r.Equal(&tsf2, ap.PopPeek())
54+
t.Run("even if with higher price", func(t *testing.T) {
55+
tsf2, err := action.SignedTransfer(_addr2, _priKey2, 2, big.NewInt(100), nil, uint64(0), big.NewInt(2))
56+
r.NoError(err)
57+
r.NoError(ap.PutAction(_addr2, nil, 1, _balance, _expireTime, tsf2))
58+
r.Equal(&tsf2, ap.PopPeek())
59+
})
60+
})
61+
t.Run("peek with lower gas price", func(t *testing.T) {
62+
ap := newAccountPool()
63+
tsf1, err := action.SignedTransfer(_addr1, _priKey1, 2, big.NewInt(100), nil, uint64(0), big.NewInt(2))
64+
r.NoError(err)
65+
tsf2, err := action.SignedTransfer(_addr2, _priKey2, 2, big.NewInt(100), nil, uint64(0), big.NewInt(1))
66+
r.NoError(err)
67+
r.NoError(ap.PutAction(_addr1, nil, 1, _balance, _expireTime, tsf1))
68+
r.NoError(ap.PutAction(_addr2, nil, 1, _balance, _expireTime, tsf2))
69+
r.Equal(&tsf2, ap.PopPeek())
70+
r.Equal(&tsf1, ap.PopPeek())
71+
t.Run("peek with pending nonce even if has higher price ", func(t *testing.T) {
72+
tsf1, err = action.SignedTransfer(_addr1, _priKey1, 1, big.NewInt(100), nil, uint64(0), big.NewInt(2))
73+
r.NoError(err)
74+
tsf2, err = action.SignedTransfer(_addr1, _priKey2, 1, big.NewInt(100), nil, uint64(0), big.NewInt(1))
75+
r.NoError(err)
76+
r.NoError(ap.PutAction(_addr1, nil, 1, _balance, _expireTime, tsf1))
77+
r.NoError(ap.PutAction(_addr2, nil, 1, _balance, _expireTime, tsf2))
78+
r.Equal(&tsf2, ap.PopPeek())
79+
r.Equal(&tsf1, ap.PopPeek())
80+
})
81+
})
82+
t.Run("multiple actions in multiple accounts", func(t *testing.T) {
83+
ap := newAccountPool()
84+
tsf1, err := action.SignedTransfer(_addr1, _priKey1, 1, big.NewInt(100), nil, uint64(0), big.NewInt(2))
85+
r.NoError(err)
86+
tsf2, err := action.SignedTransfer(_addr2, _priKey1, 2, big.NewInt(100), nil, uint64(0), big.NewInt(1))
87+
r.NoError(err)
88+
tsf3, err := action.SignedTransfer(_addr2, _priKey2, 2, big.NewInt(100), nil, uint64(0), big.NewInt(1))
89+
r.NoError(err)
90+
tsf4, err := action.SignedTransfer(_addr2, _priKey2, 3, big.NewInt(100), nil, uint64(0), big.NewInt(1))
91+
r.NoError(err)
92+
tsf5, err := action.SignedTransfer(_addr2, _priKey3, 1, big.NewInt(100), nil, uint64(0), big.NewInt(1))
93+
r.NoError(err)
94+
tsf6, err := action.SignedTransfer(_addr2, _priKey3, 3, big.NewInt(100), nil, uint64(0), big.NewInt(1))
95+
r.NoError(err)
96+
tsf7, err := action.SignedTransfer(_addr2, _priKey4, 2, big.NewInt(100), nil, uint64(0), big.NewInt(1))
97+
r.NoError(err)
98+
tsf8, err := action.SignedTransfer(_addr2, _priKey4, 3, big.NewInt(100), nil, uint64(0), big.NewInt(1))
99+
r.NoError(err)
100+
r.NoError(ap.PutAction(_addr1, nil, 1, _balance, _expireTime, tsf1))
101+
r.NoError(ap.PutAction(_addr1, nil, 1, _balance, _expireTime, tsf2))
102+
r.NoError(ap.PutAction(_addr2, nil, 1, _balance, _expireTime, tsf3))
103+
r.NoError(ap.PutAction(_addr2, nil, 1, _balance, _expireTime, tsf4))
104+
r.NoError(ap.PutAction(_addr3, nil, 1, _balance, _expireTime, tsf5))
105+
r.NoError(ap.PutAction(_addr3, nil, 1, _balance, _expireTime, tsf6))
106+
r.NoError(ap.PutAction(_addr4, nil, 1, _balance, _expireTime, tsf7))
107+
r.NoError(ap.PutAction(_addr4, nil, 1, _balance, _expireTime, tsf8))
108+
r.Equal(&tsf4, ap.PopPeek())
109+
r.Equal(&tsf3, ap.PopPeek())
110+
r.Equal(&tsf8, ap.PopPeek())
111+
r.Equal(&tsf7, ap.PopPeek())
112+
r.Equal(&tsf6, ap.PopPeek())
113+
r.Equal(&tsf5, ap.PopPeek())
114+
r.Equal(&tsf2, ap.PopPeek())
115+
r.Equal(&tsf1, ap.PopPeek())
116+
r.Nil(ap.PopPeek())
117+
})
118+
}
119+
120+
func TestAccountPool_PopAccount(t *testing.T) {
121+
r := require.New(t)
122+
ap := newAccountPool()
123+
124+
// Create a sample account
125+
tsf1, err := action.SignedTransfer(_addr1, _priKey1, 1, big.NewInt(100), nil, uint64(0), big.NewInt(1))
126+
r.NoError(err)
127+
r.NoError(ap.PutAction(_addr1, nil, 1, _balance, _expireTime, tsf1))
128+
129+
// Test when the account exists
130+
result := ap.PopAccount(_addr1)
131+
r.Equal(1, result.Len())
132+
r.Equal(tsf1, result.AllActs()[0])
133+
r.Nil(ap.Account(_addr1))
134+
135+
// Test when the account does not exist
136+
result = ap.PopAccount("nonExistentAddress")
137+
r.Nil(result)
138+
}
139+
140+
func TestAccountPool_Range(t *testing.T) {
141+
r := require.New(t)
142+
ap := newAccountPool()
143+
144+
// Create a sample account
145+
tsf1, err := action.SignedTransfer(_addr1, _priKey1, 1, big.NewInt(100), nil, uint64(0), big.NewInt(1))
146+
r.NoError(err)
147+
tsf2, err := action.SignedTransfer(_addr1, _priKey1, 2, big.NewInt(100), nil, uint64(0), big.NewInt(1))
148+
r.NoError(err)
149+
tsf3, err := action.SignedTransfer(_addr1, _priKey2, 2, big.NewInt(100), nil, uint64(0), big.NewInt(1))
150+
r.NoError(err)
151+
tsf4, err := action.SignedTransfer(_addr1, _priKey2, 1, big.NewInt(100), nil, uint64(0), big.NewInt(2))
152+
r.NoError(err)
153+
r.NoError(ap.PutAction(_addr1, nil, 1, _balance, _expireTime, tsf1))
154+
r.NoError(ap.PutAction(_addr1, nil, 1, _balance, _expireTime, tsf2))
155+
r.NoError(ap.PutAction(_addr2, nil, 1, _balance, _expireTime, tsf3))
156+
r.Equal(2, ap.Account(_addr1).Len())
157+
r.Equal(1, ap.Account(_addr2).Len())
158+
// Define a callback function
159+
callback := func(addr string, acct ActQueue) {
160+
if addr == _addr2 {
161+
r.NoError(acct.Put(tsf4))
162+
} else if addr == _addr1 {
163+
acct.PopActionWithLargestNonce()
164+
}
165+
}
166+
// Call the Range method
167+
ap.Range(callback)
168+
// Verify the results
169+
r.Equal(1, ap.Account(_addr1).Len())
170+
r.Equal(2, ap.Account(_addr2).Len())
171+
r.Equal(&tsf1, ap.PopPeek())
172+
r.Equal(&tsf3, ap.PopPeek())
173+
r.Equal(&tsf4, ap.PopPeek())
174+
r.Nil(ap.PopPeek())
175+
}
176+
177+
func TestAccountPool_DeleteIfEmpty(t *testing.T) {
178+
r := require.New(t)
179+
ap := newAccountPool()
180+
181+
// Create a sample account
182+
tsf1, err := action.SignedTransfer(_addr1, _priKey1, 1, big.NewInt(100), nil, uint64(0), big.NewInt(1))
183+
r.NoError(err)
184+
r.NoError(ap.PutAction(_addr1, nil, 1, _balance, _expireTime, tsf1))
185+
186+
// Test when the account is not empty
187+
ap.DeleteIfEmpty(_addr1)
188+
r.NotNil(ap.Account(_addr1))
189+
190+
// Test when the account is empty
191+
ap.PopPeek()
192+
ap.DeleteIfEmpty(_addr1)
193+
r.Nil(ap.Account(_addr1))
194+
}

0 commit comments

Comments
 (0)