Skip to content

Commit 94dd1d5

Browse files
authored
add native transfer transaction log (iotexproject#2347)
1 parent 6a6be4b commit 94dd1d5

File tree

5 files changed

+151
-184
lines changed

5 files changed

+151
-184
lines changed

action/protocol/account/transfer.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,23 @@ func (p *Protocol) handleTransfer(ctx context.Context, act action.Action, sm pro
123123
}
124124
}
125125

126-
return &action.Receipt{
126+
transactionLog := &action.Log{
127+
TransactionData: &action.TransactionLog{
128+
Type: iotextypes.TransactionLogType_NATIVE_TRANSFER,
129+
Sender: actionCtx.Caller.String(),
130+
Recipient: tsf.Recipient(),
131+
Amount: tsf.Amount(),
132+
},
133+
}
134+
r := &action.Receipt{
127135
Status: uint64(iotextypes.ReceiptStatus_Success),
128136
BlockHeight: blkCtx.BlockHeight,
129137
ActionHash: actionCtx.ActionHash,
130138
GasConsumed: actionCtx.IntrinsicGas,
131139
ContractAddress: p.addr.String(),
132-
}, nil
140+
}
141+
r.AddLogs(transactionLog)
142+
return r, nil
133143
}
134144

135145
// validateTransfer validates a transfer

action/protocol/account/transfer_test.go

+108-140
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@ import (
1616
"github.com/stretchr/testify/require"
1717

1818
"github.com/iotexproject/go-pkgs/hash"
19+
"github.com/iotexproject/iotex-address/address"
20+
1921
"github.com/iotexproject/iotex-core/action"
2022
"github.com/iotexproject/iotex-core/action/protocol"
23+
accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util"
2124
"github.com/iotexproject/iotex-core/action/protocol/rewarding"
22-
"github.com/iotexproject/iotex-core/action/protocol/rolldpos"
25+
"github.com/iotexproject/iotex-core/blockchain/block"
2326
"github.com/iotexproject/iotex-core/config"
24-
"github.com/iotexproject/iotex-core/db/batch"
2527
"github.com/iotexproject/iotex-core/state"
2628
"github.com/iotexproject/iotex-core/test/identityset"
27-
"github.com/iotexproject/iotex-core/test/mock/mock_chainmanager"
2829
"github.com/iotexproject/iotex-core/testutil"
30+
"github.com/iotexproject/iotex-core/testutil/testdb"
2931
"github.com/iotexproject/iotex-proto/golang/iotextypes"
3032
)
3133

@@ -46,153 +48,119 @@ func TestProtocol_HandleTransfer(t *testing.T) {
4648

4749
ctrl := gomock.NewController(t)
4850
defer ctrl.Finish()
51+
sm := testdb.NewMockStateManager(ctrl)
4952

50-
cfg := config.Default
51-
ctx := context.Background()
52-
sm := mock_chainmanager.NewMockStateManager(ctrl)
53-
cb := batch.NewCachedBatch()
54-
sm.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(
55-
func(account interface{}, opts ...protocol.StateOption) (uint64, error) {
56-
cfg, err := protocol.CreateStateConfig(opts...)
57-
if err != nil {
58-
return 0, err
59-
}
60-
val, err := cb.Get("state", cfg.Key)
61-
if err != nil {
62-
return 0, state.ErrStateNotExist
63-
}
64-
return 0, state.Deserialize(account, val)
65-
}).AnyTimes()
66-
sm.EXPECT().PutState(gomock.Any(), gomock.Any()).DoAndReturn(
67-
func(account interface{}, opts ...protocol.StateOption) (uint64, error) {
68-
cfg, err := protocol.CreateStateConfig(opts...)
69-
if err != nil {
70-
return 0, err
71-
}
72-
ss, err := state.Serialize(account)
73-
if err != nil {
74-
return 0, err
75-
}
76-
cb.Put("state", cfg.Key, ss, "failed to put state")
77-
return 0, nil
78-
}).AnyTimes()
79-
53+
// set-up protocol and genesis states
8054
p := NewProtocol(rewarding.DepositGas)
8155
reward := rewarding.NewProtocol(0, 0)
8256
registry := protocol.NewRegistry()
8357
require.NoError(reward.Register(registry))
84-
rp := rolldpos.NewProtocol(1, 1, 1)
85-
require.NoError(rp.Register(registry))
86-
cfg.Genesis.Rewarding.InitBalanceStr = "0"
87-
cfg.Genesis.Rewarding.BlockRewardStr = "0"
88-
cfg.Genesis.Rewarding.EpochRewardStr = "0"
89-
cfg.Genesis.Rewarding.NumDelegatesForEpochReward = 1
90-
cfg.Genesis.Rewarding.ExemptAddrStrsFromEpochReward = []string{}
91-
cfg.Genesis.Rewarding.FoundationBonusStr = "0"
92-
cfg.Genesis.Rewarding.NumDelegatesForFoundationBonus = 0
93-
cfg.Genesis.Rewarding.FoundationBonusLastEpoch = 0
94-
cfg.Genesis.Rewarding.ProductivityThreshold = 0
95-
ctx = protocol.WithBlockchainCtx(
96-
protocol.WithRegistry(ctx, registry),
97-
protocol.BlockchainCtx{
98-
Genesis: cfg.Genesis,
99-
},
100-
)
101-
ctx = protocol.WithBlockCtx(ctx,
102-
protocol.BlockCtx{
103-
BlockHeight: 0,
104-
Producer: identityset.Address(27),
105-
GasLimit: testutil.TestGasLimit,
106-
})
107-
ctx = protocol.WithActionCtx(ctx,
108-
protocol.ActionCtx{
109-
Caller: identityset.Address(28),
110-
})
111-
require.NoError(
112-
reward.CreateGenesisStates(
113-
ctx,
114-
sm,
115-
),
58+
chainCtx := protocol.WithBlockchainCtx(
59+
protocol.WithRegistry(context.Background(), registry),
60+
protocol.BlockchainCtx{Genesis: config.Default.Genesis},
11661
)
117-
118-
accountAlfa := state.Account{
62+
ctx := protocol.WithBlockCtx(chainCtx, protocol.BlockCtx{})
63+
require.NoError(reward.CreateGenesisStates(ctx, sm))
64+
65+
// initial deposit to alfa and charlie (as a contract)
66+
alfa := identityset.Address(28)
67+
bravo := identityset.Address(29)
68+
charlie := identityset.Address(30)
69+
require.NoError(accountutil.StoreAccount(sm, alfa, &state.Account{
11970
Balance: big.NewInt(50005),
120-
}
121-
accountBravo := state.Account{}
122-
accountCharlie := state.Account{}
123-
pubKeyAlfa := hash.BytesToHash160(identityset.Address(28).Bytes())
124-
pubKeyBravo := hash.BytesToHash160(identityset.Address(29).Bytes())
125-
pubKeyCharlie := hash.BytesToHash160(identityset.Address(30).Bytes())
126-
127-
_, err := sm.PutState(&accountAlfa, protocol.LegacyKeyOption(pubKeyAlfa))
128-
require.NoError(err)
129-
_, err = sm.PutState(&accountBravo, protocol.LegacyKeyOption(pubKeyBravo))
130-
require.NoError(err)
131-
_, err = sm.PutState(&accountCharlie, protocol.LegacyKeyOption(pubKeyCharlie))
132-
require.NoError(err)
133-
134-
transfer, err := action.NewTransfer(
135-
uint64(1),
136-
big.NewInt(2),
137-
identityset.Address(29).String(),
138-
[]byte{},
139-
uint64(10000),
140-
big.NewInt(1),
141-
)
142-
require.NoError(err)
143-
gas, err := transfer.IntrinsicGas()
144-
require.NoError(err)
145-
146-
ctx = protocol.WithActionCtx(context.Background(), protocol.ActionCtx{
147-
Caller: identityset.Address(28),
148-
IntrinsicGas: gas,
149-
})
150-
ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{
151-
BlockHeight: 1,
152-
Producer: identityset.Address(27),
153-
GasLimit: testutil.TestGasLimit,
154-
})
155-
ctx = protocol.WithBlockchainCtx(
156-
protocol.WithRegistry(ctx, registry),
157-
protocol.BlockchainCtx{
158-
Genesis: cfg.Genesis,
71+
}))
72+
require.NoError(accountutil.StoreAccount(sm, charlie, &state.Account{
73+
CodeHash: []byte("codeHash"),
74+
}))
75+
76+
tests := []struct {
77+
caller address.Address
78+
nonce uint64
79+
amount *big.Int
80+
recipient string
81+
gasLimit uint64
82+
gasPrice *big.Int
83+
isContract bool
84+
err error
85+
status uint64
86+
contractLog uint64
87+
}{
88+
{
89+
alfa, 1, big.NewInt(2), bravo.String(), 10000, big.NewInt(1), false, nil, uint64(iotextypes.ReceiptStatus_Success), 1,
15990
},
160-
)
91+
// transfer to contract address only charges gas fee
92+
{
93+
alfa, 2, big.NewInt(20), charlie.String(), 10000, big.NewInt(1), true, nil, uint64(iotextypes.ReceiptStatus_Failure), 0,
94+
},
95+
// not enough balance
96+
{
97+
alfa, 3, big.NewInt(30000), bravo.String(), 10000, big.NewInt(1), false, state.ErrNotEnoughBalance, uint64(iotextypes.ReceiptStatus_Failure), 0,
98+
},
99+
}
161100

162-
receipt, err := p.Handle(ctx, transfer, sm)
163-
require.NoError(err)
164-
require.Equal(uint64(iotextypes.ReceiptStatus_Success), receipt.Status)
101+
for _, v := range tests {
102+
tsf, err := action.NewTransfer(v.nonce, v.amount, v.recipient, []byte{}, v.gasLimit, v.gasPrice)
103+
require.NoError(err)
104+
gas, err := tsf.IntrinsicGas()
105+
require.NoError(err)
165106

166-
var acct state.Account
167-
_, err = sm.State(&acct, protocol.LegacyKeyOption(pubKeyAlfa))
168-
require.NoError(err)
169-
require.Equal("40003", acct.Balance.String())
170-
require.Equal(uint64(1), acct.Nonce)
171-
_, err = sm.State(&acct, protocol.LegacyKeyOption(pubKeyBravo))
172-
require.NoError(err)
173-
require.Equal("2", acct.Balance.String())
107+
ctx = protocol.WithActionCtx(chainCtx, protocol.ActionCtx{
108+
Caller: v.caller,
109+
IntrinsicGas: gas,
110+
})
111+
ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{
112+
BlockHeight: 1,
113+
Producer: identityset.Address(27),
114+
GasLimit: testutil.TestGasLimit,
115+
})
174116

175-
contractAcct := state.Account{
176-
CodeHash: []byte("codeHash"),
117+
sender, err := accountutil.AccountState(sm, v.caller.String())
118+
require.NoError(err)
119+
recipient, err := accountutil.AccountState(sm, v.recipient)
120+
require.NoError(err)
121+
gasFee := new(big.Int).Mul(v.gasPrice, new(big.Int).SetUint64(gas))
122+
123+
receipt, err := p.Handle(ctx, tsf, sm)
124+
require.Equal(v.err, errors.Cause(err))
125+
if err != nil {
126+
require.Nil(receipt)
127+
// sender balance/nonce remains the same in case of error
128+
newSender, err := accountutil.AccountState(sm, v.caller.String())
129+
require.NoError(err)
130+
require.Equal(sender.Balance, newSender.Balance)
131+
require.Equal(sender.Nonce, newSender.Nonce)
132+
continue
133+
}
134+
require.Equal(v.status, receipt.Status)
135+
136+
// amount is transferred only upon success and for non-contract recipient
137+
if receipt.Status == uint64(iotextypes.ReceiptStatus_Success) && !v.isContract {
138+
gasFee.Add(gasFee, v.amount)
139+
// verify recipient
140+
newRecipient, err := accountutil.AccountState(sm, v.recipient)
141+
require.NoError(err)
142+
recipient.AddBalance(v.amount)
143+
require.Equal(recipient.Balance, newRecipient.Balance)
144+
}
145+
// verify sender balance/nonce
146+
newSender, err := accountutil.AccountState(sm, v.caller.String())
147+
require.NoError(err)
148+
sender.SubBalance(gasFee)
149+
require.Equal(sender.Balance, newSender.Balance)
150+
require.Equal(v.nonce, newSender.Nonce)
151+
152+
// verify transaction log
153+
tLog := block.ReceiptTransactionLog(receipt)
154+
if tLog != nil {
155+
require.NotNil(tLog)
156+
pbLog := tLog.Proto()
157+
require.Equal(tsf.Hash(), hash.BytesToHash256(pbLog.ActionHash))
158+
require.EqualValues(v.contractLog, pbLog.NumTransactions)
159+
rec := pbLog.Transactions[0]
160+
require.Equal(v.amount.String(), rec.Amount)
161+
require.Equal(v.caller.String(), rec.Sender)
162+
require.Equal(v.recipient, rec.Recipient)
163+
require.Equal(iotextypes.TransactionLogType_NATIVE_TRANSFER, rec.Type)
164+
}
177165
}
178-
contractAddr := hash.BytesToHash160(identityset.Address(32).Bytes())
179-
_, err = sm.PutState(&contractAcct, protocol.LegacyKeyOption(contractAddr))
180-
require.NoError(err)
181-
transfer, err = action.NewTransfer(
182-
uint64(2),
183-
big.NewInt(3),
184-
identityset.Address(32).String(),
185-
[]byte{},
186-
uint64(10000),
187-
big.NewInt(2),
188-
)
189-
require.NoError(err)
190-
// Assume that the gas of this transfer is the same as previous one
191-
receipt, err = p.Handle(ctx, transfer, sm)
192-
require.NoError(err)
193-
require.Equal(uint64(iotextypes.ReceiptStatus_Failure), receipt.Status)
194-
_, err = sm.State(&acct, protocol.LegacyKeyOption(pubKeyAlfa))
195-
require.NoError(err)
196-
require.Equal(uint64(2), acct.Nonce)
197-
require.Equal("20003", acct.Balance.String())
198166
}

api/api_test.go

+20-6
Original file line numberDiff line numberDiff line change
@@ -730,22 +730,21 @@ var (
730730
getImplicitLogByBlockHeightTest = []struct {
731731
height uint64
732732
code codes.Code
733-
log *block.BlkTransactionLog
734733
}{
735734
{
736-
1, codes.NotFound, nil,
735+
1, codes.OK,
737736
},
738737
{
739-
2, codes.OK, &block.BlkTransactionLog{},
738+
2, codes.OK,
740739
},
741740
{
742-
3, codes.NotFound, nil,
741+
3, codes.NotFound,
743742
},
744743
{
745-
4, codes.OK, &block.BlkTransactionLog{},
744+
4, codes.OK,
746745
},
747746
{
748-
5, codes.InvalidArgument, nil,
747+
5, codes.InvalidArgument,
749748
},
750749
}
751750
)
@@ -1904,6 +1903,9 @@ func addTestingBlocks(bc blockchain.Blockchain, ap actpool.ActPool) error {
19041903
if err != nil {
19051904
return err
19061905
}
1906+
implicitLogs[tsf.Hash()] = block.NewTransactionLog(tsf.Hash(),
1907+
[]*block.TokenTxRecord{block.NewTokenTxRecord(iotextypes.TransactionLogType_NATIVE_TRANSFER, "10", addr0, addr3)},
1908+
)
19071909

19081910
blk1Time := testutil.TimestampNow()
19091911
if err := ap.Add(context.Background(), tsf); err != nil {
@@ -1932,11 +1934,17 @@ func addTestingBlocks(bc blockchain.Blockchain, ap actpool.ActPool) error {
19321934
if err := ap.Add(context.Background(), selp); err != nil {
19331935
return err
19341936
}
1937+
implicitLogs[selp.Hash()] = block.NewTransactionLog(selp.Hash(),
1938+
[]*block.TokenTxRecord{block.NewTokenTxRecord(iotextypes.TransactionLogType_NATIVE_TRANSFER, "1", addr3, recipient)},
1939+
)
19351940
}
19361941
selp, err := testutil.SignedTransfer(addr3, priKey3, uint64(5), big.NewInt(2), []byte{}, testutil.TestGasLimit, big.NewInt(testutil.TestGasPriceInt64))
19371942
if err != nil {
19381943
return err
19391944
}
1945+
implicitLogs[selp.Hash()] = block.NewTransactionLog(selp.Hash(),
1946+
[]*block.TokenTxRecord{block.NewTokenTxRecord(iotextypes.TransactionLogType_NATIVE_TRANSFER, "2", addr3, addr3)},
1947+
)
19401948
if err := ap.Add(context.Background(), selp); err != nil {
19411949
return err
19421950
}
@@ -1981,13 +1989,19 @@ func addTestingBlocks(bc blockchain.Blockchain, ap actpool.ActPool) error {
19811989
if err != nil {
19821990
return err
19831991
}
1992+
implicitLogs[tsf1.Hash()] = block.NewTransactionLog(tsf1.Hash(),
1993+
[]*block.TokenTxRecord{block.NewTokenTxRecord(iotextypes.TransactionLogType_NATIVE_TRANSFER, "1", addr3, addr3)},
1994+
)
19841995
if err := ap.Add(context.Background(), tsf1); err != nil {
19851996
return err
19861997
}
19871998
tsf2, err := testutil.SignedTransfer(addr1, priKey1, uint64(1), big.NewInt(1), []byte{}, testutil.TestGasLimit, big.NewInt(testutil.TestGasPriceInt64))
19881999
if err != nil {
19892000
return err
19902001
}
2002+
implicitLogs[tsf2.Hash()] = block.NewTransactionLog(tsf2.Hash(),
2003+
[]*block.TokenTxRecord{block.NewTokenTxRecord(iotextypes.TransactionLogType_NATIVE_TRANSFER, "1", addr1, addr1)},
2004+
)
19912005
if err := ap.Add(context.Background(), tsf2); err != nil {
19922006
return err
19932007
}

0 commit comments

Comments
 (0)