Skip to content

Commit c20ba7b

Browse files
authored
829 download escrow transaction (#834)
* #832 add one more persistance time cycle to check to avoid node stuck in small network * #829 include escrow detail of transaction in GetTransactionByBlockID * #829 include escrow detail of transaction in GetTransactionByBlockID * #833 add copy of block to broadcast before deleting the transactions * #829 fix test * #829 add test for escrow transaction query * #833 broadcasted wrong variable * #829 refactor escrow transaction query to be more generic
1 parent 625cd5e commit c20ba7b

File tree

6 files changed

+141
-22
lines changed

6 files changed

+141
-22
lines changed

common/query/escrowTransactionQuery.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ type (
2020
InsertEscrowTransaction(escrow *model.Escrow) [][]interface{}
2121
GetLatestEscrowTransactionByID(int64) (string, []interface{})
2222
GetEscrowTransactions(fields map[string]interface{}) (string, []interface{})
23+
GetEscrowTransactionsByTransactionIdsAndStatus(
24+
transactionIds []string, status model.EscrowStatus,
25+
) string
2326
ExpiringEscrowTransactions(blockHeight uint32) (string, []interface{})
2427
ExtractModel(*model.Escrow) []interface{}
2528
BuildModels(*sql.Rows) ([]*model.Escrow, error)
@@ -123,6 +126,18 @@ func (et *EscrowTransactionQuery) ExpiringEscrowTransactions(blockHeight uint32)
123126
}
124127
}
125128

129+
func (et *EscrowTransactionQuery) GetEscrowTransactionsByTransactionIdsAndStatus(
130+
transactionIds []string, status model.EscrowStatus,
131+
) string {
132+
return fmt.Sprintf(
133+
"SELECT %s FROM %s WHERE id IN (%s) AND status = %d",
134+
strings.Join(et.Fields, ", "),
135+
et.getTableName(),
136+
strings.Join(transactionIds, ", "),
137+
status,
138+
)
139+
}
140+
126141
// ExtractModel will extract values of escrow as []interface{}
127142
func (et *EscrowTransactionQuery) ExtractModel(escrow *model.Escrow) []interface{} {
128143
return []interface{}{

common/query/escrowTransactionQuery_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package query
22

33
import (
44
"database/sql"
5+
"fmt"
56
"reflect"
67
"sort"
78
"strings"
@@ -534,3 +535,15 @@ func TestEscrowTransactionQuery_TrimDataBeforeSnapshot(t *testing.T) {
534535
})
535536
}
536537
}
538+
539+
func TestEscrowTransactionQuery_GetEscrowTransactionsByTransactionIdsAndStatus(t *testing.T) {
540+
t.Run("GetPendingEscrowTransactionsByTransactionIds", func(t *testing.T) {
541+
escrowQuery := NewEscrowTransactionQuery()
542+
query := escrowQuery.GetEscrowTransactionsByTransactionIdsAndStatus([]string{"1", "2"}, model.EscrowStatus_Pending)
543+
expect := fmt.Sprintf("SELECT id, sender_address, recipient_address, approver_address, amount, commission, timeout, status, "+
544+
"block_height, latest, instruction FROM escrow_transaction WHERE id IN (1, 2) AND status = %d", model.EscrowStatus_Pending)
545+
if query != expect {
546+
t.Errorf("expect: %v\ngot: %v\n", expect, query)
547+
}
548+
})
549+
}

core/service/blockMainService.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -611,10 +611,12 @@ func (bs *BlockService) PushBlock(previousBlock, block *model.Block, broadcast,
611611
bs.Logger.Error(rollbackErr.Error())
612612
}
613613
if broadcast {
614+
// create copy of the block to avoid reference update on block pool
615+
blockToBroadcast := *block
614616
// add transactionIDs and remove transaction before broadcast
615-
block.TransactionIDs = transactionIDs
616-
block.Transactions = []*model.Transaction{}
617-
bs.Observer.Notify(observer.BroadcastBlock, block, bs.Chaintype)
617+
blockToBroadcast.TransactionIDs = transactionIDs
618+
blockToBroadcast.Transactions = []*model.Transaction{}
619+
bs.Observer.Notify(observer.BroadcastBlock, blockToBroadcast, bs.Chaintype)
618620
}
619621
return nil
620622
}
@@ -664,6 +666,7 @@ func (bs *BlockService) ScanBlockPool() error {
664666
)
665667
}
666668
err = bs.PushBlock(previousBlock, block, true, true)
669+
667670
if err != nil {
668671
bs.Logger.Warnf("ScanBlockPool:PushBlockFail: %v\n", blocker.NewBlocker(blocker.PushMainBlockErr, err.Error(), block, previousBlock))
669672
return blocker.NewBlocker(

core/service/transactionCoreService.go

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package service
22

33
import (
44
"database/sql"
5+
"strconv"
56

67
"github.com/sirupsen/logrus"
78
"github.com/zoobc/zoobc-core/common/blocker"
@@ -76,18 +77,54 @@ func (tg *TransactionCoreService) GetTransactionsByIds(transactionIds []int64) (
7677

7778
// GetTransactionsByBlockID get transactions of the block
7879
func (tg *TransactionCoreService) GetTransactionsByBlockID(blockID int64) ([]*model.Transaction, error) {
79-
var transactions []*model.Transaction
80+
var (
81+
transactions []*model.Transaction
82+
escrows []*model.Escrow
83+
txIdsStr []string
84+
err error
85+
)
8086

8187
// get transaction of the block
82-
transactionQ, transactionArg := tg.TransactionQuery.GetTransactionsByBlockID(blockID)
83-
rows, err := tg.QueryExecutor.ExecuteSelect(transactionQ, false, transactionArg...)
84-
88+
transactions, err = func() ([]*model.Transaction, error) {
89+
transactionQ, transactionArg := tg.TransactionQuery.GetTransactionsByBlockID(blockID)
90+
rows, err := tg.QueryExecutor.ExecuteSelect(transactionQ, false, transactionArg...)
91+
if err != nil {
92+
return nil, blocker.NewBlocker(blocker.DBErr, err.Error())
93+
}
94+
defer rows.Close()
95+
return tg.TransactionQuery.BuildModel(transactions, rows)
96+
}()
8597
if err != nil {
8698
return nil, blocker.NewBlocker(blocker.DBErr, err.Error())
8799
}
88-
defer rows.Close()
89-
90-
return tg.TransactionQuery.BuildModel(transactions, rows)
100+
// fetch escrow if exist
101+
for _, tx := range transactions {
102+
txIdsStr = append(txIdsStr, "'"+strconv.FormatInt(tx.ID, 10)+"'")
103+
}
104+
if len(txIdsStr) > 0 {
105+
escrows, err = func() ([]*model.Escrow, error) {
106+
escrowQ := tg.EscrowTransactionQuery.GetEscrowTransactionsByTransactionIdsAndStatus(
107+
txIdsStr, model.EscrowStatus_Pending,
108+
)
109+
rows, err := tg.QueryExecutor.ExecuteSelect(escrowQ, false)
110+
if err != nil {
111+
return nil, err
112+
}
113+
defer rows.Close()
114+
return tg.EscrowTransactionQuery.BuildModels(rows)
115+
}()
116+
if err != nil {
117+
return nil, blocker.NewBlocker(blocker.DBErr, err.Error())
118+
}
119+
for _, escrow := range escrows {
120+
for _, tx := range transactions {
121+
if tx.ID == escrow.ID {
122+
tx.Escrow = escrow
123+
}
124+
}
125+
}
126+
}
127+
return transactions, nil
91128
}
92129

93130
// ExpiringEscrowTransactions push an observer event that is ExpiringEscrowTransactions,

core/service/transactionCoreService_test.go

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ type (
4242
mockGetTransactionsByBlockIDTransactionQueryBuildSuccess struct {
4343
query.TransactionQuery
4444
}
45+
mockGetTransactionsByBlockIDEscrowTransactionQueryBuildSuccessOne struct {
46+
query.EscrowTransactionQuery
47+
}
48+
mockGetTransactionsByBlockIDEscrowTransactionQueryBuildSuccessEmpty struct {
49+
query.EscrowTransactionQuery
50+
}
4551
// GetTransactionsByBlockID mocks
4652
)
4753

@@ -57,6 +63,18 @@ var (
5763
TransactionHash: make([]byte, 32),
5864
},
5965
}
66+
mockGetTransactionsByBlockIDResultWithEscrow = []*model.Transaction{
67+
{
68+
TransactionHash: make([]byte, 32),
69+
Escrow: mockGetTransactionByBlockIDEscrowTransactionResultOne[0],
70+
},
71+
}
72+
mockGetTransactionByBlockIDEscrowTransactionResultOne = []*model.Escrow{
73+
{
74+
ID: 0,
75+
},
76+
}
77+
mockGetTransactionByBlockIDEscrowTransactionResultEmpty = make([]*model.Escrow, 0)
6078
)
6179

6280
func (*mockGetTransactionsByIdsExecutorFail) ExecuteSelect(query string, tx bool, args ...interface{}) (*sql.Rows, error) {
@@ -102,6 +120,16 @@ func (*mockGetTransactionsByBlockIDTransactionQueryBuildFail) BuildModel(
102120
return nil, errors.New("mockedError")
103121
}
104122

123+
func (*mockGetTransactionsByBlockIDEscrowTransactionQueryBuildSuccessOne) BuildModels(
124+
rows *sql.Rows) ([]*model.Escrow, error) {
125+
return mockGetTransactionByBlockIDEscrowTransactionResultOne, nil
126+
}
127+
128+
func (*mockGetTransactionsByBlockIDEscrowTransactionQueryBuildSuccessEmpty) BuildModels(
129+
rows *sql.Rows) ([]*model.Escrow, error) {
130+
return mockGetTransactionByBlockIDEscrowTransactionResultEmpty, nil
131+
}
132+
105133
func (*mockGetTransactionsByBlockIDTransactionQueryBuildSuccess) BuildModel(
106134
txs []*model.Transaction, rows *sql.Rows) ([]*model.Transaction, error) {
107135
return mockGetTransactionsByBlockIDResult, nil
@@ -179,8 +207,9 @@ func TestTransactionCoreService_GetTransactionsByIds(t *testing.T) {
179207

180208
func TestTransactionCoreService_GetTransactionsByBlockID(t *testing.T) {
181209
type fields struct {
182-
TransactionQuery query.TransactionQueryInterface
183-
QueryExecutor query.ExecutorInterface
210+
TransactionQuery query.TransactionQueryInterface
211+
EscrowTransactionQuery query.EscrowTransactionQueryInterface
212+
QueryExecutor query.ExecutorInterface
184213
}
185214
type args struct {
186215
blockID int64
@@ -217,10 +246,24 @@ func TestTransactionCoreService_GetTransactionsByBlockID(t *testing.T) {
217246
wantErr: true,
218247
},
219248
{
220-
name: "GetTransactionsByBlockID-BuildModel-Success",
249+
name: "GetTransactionsByBlockID-BuildModel-Success-EscrowOneResult",
221250
fields: fields{
222-
TransactionQuery: &mockGetTransactionsByBlockIDTransactionQueryBuildSuccess{},
223-
QueryExecutor: &mockGetTransactionsByBlockIDExecutorSuccess{},
251+
TransactionQuery: &mockGetTransactionsByBlockIDTransactionQueryBuildSuccess{},
252+
EscrowTransactionQuery: &mockGetTransactionsByBlockIDEscrowTransactionQueryBuildSuccessOne{},
253+
QueryExecutor: &mockGetTransactionsByBlockIDExecutorSuccess{},
254+
},
255+
args: args{
256+
blockID: 1,
257+
},
258+
want: mockGetTransactionsByBlockIDResultWithEscrow,
259+
wantErr: false,
260+
},
261+
{
262+
name: "GetTransactionsByBlockID-BuildModel-Success-EscrowEmptyResult",
263+
fields: fields{
264+
TransactionQuery: &mockGetTransactionsByBlockIDTransactionQueryBuildSuccess{},
265+
EscrowTransactionQuery: &mockGetTransactionsByBlockIDEscrowTransactionQueryBuildSuccessEmpty{},
266+
QueryExecutor: &mockGetTransactionsByBlockIDExecutorSuccess{},
224267
},
225268
args: args{
226269
blockID: 1,
@@ -232,8 +275,9 @@ func TestTransactionCoreService_GetTransactionsByBlockID(t *testing.T) {
232275
for _, tt := range tests {
233276
t.Run(tt.name, func(t *testing.T) {
234277
tg := &TransactionCoreService{
235-
TransactionQuery: tt.fields.TransactionQuery,
236-
QueryExecutor: tt.fields.QueryExecutor,
278+
TransactionQuery: tt.fields.TransactionQuery,
279+
EscrowTransactionQuery: tt.fields.EscrowTransactionQuery,
280+
QueryExecutor: tt.fields.QueryExecutor,
237281
}
238282
got, err := tg.GetTransactionsByBlockID(tt.args.blockID)
239283
if (err != nil) != tt.wantErr {

core/smith/strategy/blocksmithStrategyMain.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,10 @@ func (bss *BlocksmithStrategyMain) CanPersistBlock(
226226
previousBlock *model.Block,
227227
) error {
228228
var (
229-
err error
230-
ct = &chaintype.MainChain{}
231-
currentTime = time.Now().Unix()
232-
remainder, prevRoundBegin, prevRoundExpired int64
229+
err error
230+
ct = &chaintype.MainChain{}
231+
currentTime = time.Now().Unix()
232+
remainder, prevRoundBegin, prevRoundExpired, prevRound2Begin, prevRound2Expired int64
233233
)
234234
// always return true for the first block | keeping in mind genesis block's timestamps is far behind, let fork processor
235235
// handle to get highest cum-diff block
@@ -260,13 +260,20 @@ func (bss *BlocksmithStrategyMain) CanPersistBlock(
260260
prevRoundExpired = prevRoundBegin + ct.GetBlocksmithBlockCreationTime() +
261261
ct.GetBlocksmithNetworkTolerance()
262262
}
263+
if timeRound > 1 { // handle small network, go one more round
264+
prevRound2Start := nearestRoundBeginning - 2*timeForOneRound
265+
prevRound2Begin = prevRound2Start + blocksmithIndex*ct.GetBlocksmithTimeGap()
266+
prevRound2Expired = prevRound2Begin + ct.GetBlocksmithBlockCreationTime() +
267+
ct.GetBlocksmithNetworkTolerance()
268+
}
263269
// calculate current round begin and expiry time
264270
allowedBeginTime := blocksmithIndex*ct.GetBlocksmithTimeGap() + nearestRoundBeginning
265271
expiredTime := allowedBeginTime + ct.GetBlocksmithBlockCreationTime() +
266272
ct.GetBlocksmithNetworkTolerance()
267273
// check if current time is in {(expire-timeGap) < x < (expire)} in either previous round or current round
268274
if (currentTime > (expiredTime-ct.GetBlocksmithTimeGap()) && currentTime <= expiredTime) ||
269-
(currentTime > (prevRoundExpired-ct.GetBlocksmithTimeGap()) && currentTime <= prevRoundExpired) {
275+
(currentTime > (prevRoundExpired-ct.GetBlocksmithTimeGap()) && currentTime <= prevRoundExpired) ||
276+
(currentTime > (prevRound2Expired-ct.GetBlocksmithTimeGap()) && currentTime <= prevRound2Expired) {
270277
return nil
271278
}
272279
return blocker.NewBlocker(blocker.BlockErr, "CannotPersistBlock")

0 commit comments

Comments
 (0)