Skip to content

Commit

Permalink
add datacopy contract test (#2788)
Browse files Browse the repository at this point in the history
* add datacopy test

* modify contract

* remove debug log

* update testdata

* add attack bytecode

* update datacopy.json

* correct code format

* remove printStore() in datacopy.sol

* update contract test data

Co-authored-by: dustinxie <dahuaxie@gmail.com>
  • Loading branch information
Liuhaai and dustinxie authored Sep 17, 2021
1 parent 44e0a68 commit e0683e8
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 52 deletions.
143 changes: 91 additions & 52 deletions action/protocol/execution/protocol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,52 @@ import (
"github.com/iotexproject/iotex-core/testutil"
)

// ExpectedBalance defines an account-balance pair
type ExpectedBalance struct {
Account string `json:"account"`
RawBalance string `json:"rawBalance"`
}
type (
// ExpectedBalance defines an account-balance pair
ExpectedBalance struct {
Account string `json:"account"`
RawBalance string `json:"rawBalance"`
}

// GenesisBlockHeight defines an genesis blockHeight
type GenesisBlockHeight struct {
IsBering bool `json:"isBering"`
IsIceland bool `json:"isIceland"`
}
ExpectedBlockInfo struct {
TxRootHash string `json:"txRootHash"`
StateRootHash string `json:"stateRootHash"`
ReceiptRootHash string `json:"receiptRootHash"`
}

// GenesisBlockHeight defines an genesis blockHeight
GenesisBlockHeight struct {
IsBering bool `json:"isBering"`
IsIceland bool `json:"isIceland"`
}

Log struct {
Topics []string `json:"topics"`
Data string `json:"data"`
}

ExecutionConfig struct {
Comment string `json:"comment"`
ContractIndex int `json:"contractIndex"`
AppendContractAddress bool `json:"appendContractAddress"`
ContractIndexToAppend int `json:"contractIndexToAppend"`
ContractAddressToAppend string `json:"contractAddressToAppend"`
ReadOnly bool `json:"readOnly"`
RawPrivateKey string `json:"rawPrivateKey"`
RawByteCode string `json:"rawByteCode"`
RawAmount string `json:"rawAmount"`
RawGasLimit uint `json:"rawGasLimit"`
RawGasPrice string `json:"rawGasPrice"`
Failed bool `json:"failed"`
RawReturnValue string `json:"rawReturnValue"`
RawExpectedGasConsumed uint `json:"rawExpectedGasConsumed"`
ExpectedStatus uint64 `json:"expectedStatus"`
ExpectedBalances []ExpectedBalance `json:"expectedBalances"`
ExpectedLogs []Log `json:"expectedLogs"`
ExpectedErrorMsg string `json:"expectedErrorMsg"`
ExpectedBlockInfos ExpectedBlockInfo `json:"expectedBlockInfos"`
}
)

func (eb *ExpectedBalance) Balance() *big.Int {
balance, ok := new(big.Int).SetString(eb.RawBalance, 10)
Expand All @@ -78,32 +113,6 @@ func readCode(sr protocol.StateReader, addr []byte) ([]byte, error) {
return c[:], err
}

type Log struct {
Topics []string `json:"topics"`
Data string `json:"data"`
}

type ExecutionConfig struct {
Comment string `json:"comment"`
ContractIndex int `json:"contractIndex"`
AppendContractAddress bool `json:"appendContractAddress"`
ContractIndexToAppend int `json:"contractIndexToAppend"`
ContractAddressToAppend string `json:"contractAddressToAppend"`
ReadOnly bool `json:"readOnly"`
RawPrivateKey string `json:"rawPrivateKey"`
RawByteCode string `json:"rawByteCode"`
RawAmount string `json:"rawAmount"`
RawGasLimit uint `json:"rawGasLimit"`
RawGasPrice string `json:"rawGasPrice"`
Failed bool `json:"failed"`
RawReturnValue string `json:"rawReturnValue"`
RawExpectedGasConsumed uint `json:"rawExpectedGasConsumed"`
ExpectedStatus uint64 `json:"expectedStatus"`
ExpectedBalances []ExpectedBalance `json:"expectedBalances"`
ExpectedLogs []Log `json:"expectedLogs"`
ExpectedErrorMsg string `json:"expectedErrorMsg"`
}

func (cfg *ExecutionConfig) PrivateKey() crypto.PrivateKey {
priKey, err := crypto.HexStringToPrivateKey(cfg.RawPrivateKey)
if err != nil {
Expand Down Expand Up @@ -258,7 +267,7 @@ func runExecutions(
ap actpool.ActPool,
ecfgs []*ExecutionConfig,
contractAddrs []string,
) ([]*action.Receipt, error) {
) ([]*action.Receipt, *ExpectedBlockInfo, error) {
nonces := map[string]uint64{}
hashes := []hash.Hash256{}
for i, ecfg := range ecfgs {
Expand All @@ -269,7 +278,7 @@ func runExecutions(
if nonce, ok = nonces[executor]; !ok {
state, err := accountutil.AccountState(sf, executor)
if err != nil {
return nil, err
return nil, nil, err
}
nonce = state.Nonce
}
Expand All @@ -284,7 +293,7 @@ func runExecutions(
ecfg.ByteCode(),
)
if err != nil {
return nil, err
return nil, nil, err
}
builder := &action.EnvelopeBuilder{}
elp := builder.SetAction(exec).
Expand All @@ -294,34 +303,41 @@ func runExecutions(
Build()
selp, err := action.Sign(elp, ecfg.PrivateKey())
if err != nil {
return nil, err
return nil, nil, err
}
if err := ap.Add(context.Background(), selp); err != nil {
return nil, err
return nil, nil, err
}
selpHash, err := selp.Hash()
if err != nil {
return nil, err
return nil, nil, err
}
hashes = append(hashes, selpHash)
}
blk, err := bc.MintNewBlock(testutil.TimestampNow())
if err != nil {
return nil, err
return nil, nil, err
}

if err := bc.CommitBlock(blk); err != nil {
return nil, err
return nil, nil, err
}
receipts := []*action.Receipt{}
for _, hash := range hashes {
receipt, err := dao.GetReceiptByActionHash(hash, blk.Height())
if err != nil {
return nil, err
return nil, nil, err
}
receipts = append(receipts, receipt)
}
stateRootHash, txRootHash, receiptRootHash := blk.DeltaStateDigest(), blk.TxRoot(), blk.ReceiptRoot()
blkInfo := &ExpectedBlockInfo{
hex.EncodeToString(txRootHash[:]),
hex.EncodeToString(stateRootHash[:]),
hex.EncodeToString(receiptRootHash[:]),
}

return receipts, nil
return receipts, blkInfo, nil
}

func (sct *SmartContractTest) prepareBlockchain(
Expand Down Expand Up @@ -414,7 +430,7 @@ func (sct *SmartContractTest) deployContracts(
if contract.AppendContractAddress {
contract.ContractAddressToAppend = contractAddresses[contract.ContractIndexToAppend]
}
receipts, err := runExecutions(bc, sf, dao, ap, []*ExecutionConfig{&contract}, []string{action.EmptyAddress})
receipts, _, err := runExecutions(bc, sf, dao, ap, []*ExecutionConfig{&contract}, []string{action.EmptyAddress})
r.NoError(err)
r.Equal(1, len(receipts))
receipt := receipts[0]
Expand Down Expand Up @@ -455,6 +471,7 @@ func (sct *SmartContractTest) run(r *require.Assertions) {
// prepare blockchain
ctx := context.Background()
cfg := config.Default
cfg.Chain.ProducerPrivKey = identityset.PrivateKey(28).HexString()
cfg.Chain.EnableTrielessStateDB = false
bc, sf, dao, ap := sct.prepareBlockchain(ctx, cfg, r)
defer func() {
Expand All @@ -475,6 +492,7 @@ func (sct *SmartContractTest) run(r *require.Assertions) {
}
var retval []byte
var receipt *action.Receipt
var blkInfo *ExpectedBlockInfo
var err error
if exec.ReadOnly {
retval, receipt, err = readExecution(bc, sf, dao, ap, &exec, contractAddr)
Expand All @@ -486,7 +504,8 @@ func (sct *SmartContractTest) run(r *require.Assertions) {
r.Equal(expected, retval)
}
} else {
receipts, err := runExecutions(bc, sf, dao, ap, []*ExecutionConfig{&exec}, []string{contractAddr})
var receipts []*action.Receipt
receipts, blkInfo, err = runExecutions(bc, sf, dao, ap, []*ExecutionConfig{&exec}, []string{contractAddr})
r.NoError(err)
r.Equal(1, len(receipts))
receipt = receipts[0]
Expand All @@ -506,6 +525,11 @@ func (sct *SmartContractTest) run(r *require.Assertions) {
if exec.ExpectedGasConsumed() != 0 {
r.Equal(exec.ExpectedGasConsumed(), receipt.GasConsumed, i)
}
if exec.ExpectedBlockInfos != (ExpectedBlockInfo{}) {
r.Equal(exec.ExpectedBlockInfos.ReceiptRootHash, blkInfo.ReceiptRootHash)
r.Equal(exec.ExpectedBlockInfos.TxRootHash, blkInfo.TxRootHash)
r.Equal(exec.ExpectedBlockInfos.StateRootHash, blkInfo.StateRootHash)
}
for _, expectedBalance := range exec.ExpectedBalances {
account := expectedBalance.Account
if account == "" {
Expand Down Expand Up @@ -872,6 +896,15 @@ func TestProtocol_Handle(t *testing.T) {
t.Run("self-destruct", func(t *testing.T) {
NewSmartContractTest(t, "testdata/self-destruct.json")
})
// datacopy
t.Run("datacopy", func(t *testing.T) {
NewSmartContractTest(t, "testdata/datacopy.json")
})
// this test replay CVE-2021-39137 attack, see attack details
// at https://github.com/ethereum/go-ethereum/blob/master/docs/postmortems/2021-08-22-split-postmortem.md
t.Run("CVE-2021-39137-attack-replay", func(t *testing.T) {
NewSmartContractTest(t, "testdata/CVE-2021-39137-attack-replay.json")
})
}

func TestMaxTime(t *testing.T) {
Expand Down Expand Up @@ -956,6 +989,12 @@ func TestIstanbulEVM(t *testing.T) {
t.Run("self-destruct", func(t *testing.T) {
NewSmartContractTest(t, "testdata-istanbul/self-destruct.json")
})
t.Run("datacopy", func(t *testing.T) {
NewSmartContractTest(t, "testdata-istanbul/datacopy.json")
})
t.Run("CVE-2021-39137-attack-replay", func(t *testing.T) {
NewSmartContractTest(t, "testdata/CVE-2021-39137-attack-replay.json")
})
}

func benchmarkHotContractWithFactory(b *testing.B, async bool) {
Expand Down Expand Up @@ -996,7 +1035,7 @@ func benchmarkHotContractWithFactory(b *testing.B, async bool) {
contractAddr := contractAddresses[0]
b.ResetTimer()
for i := 0; i < b.N; i++ {
receipts, err := runExecutions(
receipts, _, err := runExecutions(
bc, sf, dao, ap, []*ExecutionConfig{
{
RawPrivateKey: "cfa6ef757dee2e50351620dca002d32b9c090cfda55fb81f37f1d26b273743f1",
Expand Down Expand Up @@ -1027,7 +1066,7 @@ func benchmarkHotContractWithFactory(b *testing.B, async bool) {
})
contractAddrs = append(contractAddrs, contractAddr)
}
receipts, err = runExecutions(bc, sf, dao, ap, ecfgs, contractAddrs)
receipts, _, err = runExecutions(bc, sf, dao, ap, ecfgs, contractAddrs)
r.NoError(err)
for _, receipt := range receipts {
r.Equal(uint64(1), receipt.Status)
Expand Down Expand Up @@ -1073,7 +1112,7 @@ func benchmarkHotContractWithStateDB(b *testing.B, cachedStateDBOption bool) {
contractAddr := contractAddresses[0]
b.ResetTimer()
for i := 0; i < b.N; i++ {
receipts, err := runExecutions(
receipts, _, err := runExecutions(
bc, sf, dao, ap, []*ExecutionConfig{
{
RawPrivateKey: "cfa6ef757dee2e50351620dca002d32b9c090cfda55fb81f37f1d26b273743f1",
Expand Down Expand Up @@ -1104,7 +1143,7 @@ func benchmarkHotContractWithStateDB(b *testing.B, cachedStateDBOption bool) {
})
contractAddrs = append(contractAddrs, contractAddr)
}
receipts, err = runExecutions(bc, sf, dao, ap, ecfgs, contractAddrs)
receipts, _, err = runExecutions(bc, sf, dao, ap, ecfgs, contractAddrs)
r.NoError(err)
for _, receipt := range receipts {
r.Equal(uint64(1), receipt.Status)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"initBalances": [{
"account": "io1mflp9m6hcgm2qcghchsdqj3z3eccrnekx9p0ms",
"rawBalance": "1000000000000000000000000000"
}],
"deployments": [{
"rawByteCode": "0000000000000000000000008eae784e072e961f76948a785b62c9a950fb17ae62c9a950fb17ae00000000000000000000000000000000000000000000000000",
"rawPrivateKey": "cfa6ef757dee2e50351620dca002d32b9c090cfda55fb81f37f1d26b273743f1",
"rawAmount": "0",
"rawGasLimit": 5000000,
"rawGasPrice": "0",
"rawExpectedGasConsumed": 16400,
"expectedBalances": [],
"comment": "deploy attack contract(https://etherscan.io/address/0x8eae784e072e961f76948a785b62c9a950fb17ae)"
}],
"executions": [{
"rawPrivateKey": "cfa6ef757dee2e50351620dca002d32b9c090cfda55fb81f37f1d26b273743f1",
"rawByteCode": "3034526020600760203460045afa602034343e604034f3",
"rawAmount": "0",
"rawGasLimit": 1000000,
"rawGasPrice": "0",
"rawExpectedGasConsumed": 12300,
"comment": "launch attack(https://etherscan.io/tx/0x1cb6fb36633d270edefc04d048145b4298e67b8aa82a9e5ec4aa1435dd770ce4)",
"expectedBlockInfos" : {
"txRootHash" : "a59ead74a3870e9b5ca352c5f59108df402ca203ef2109799fe2d8e1da49c83d",
"stateRootHash" : "ed9bd589ee5ab5660a3d5d863bbeea13020a0aacab18e8655a626beaf9a54713",
"receiptRootHash" : "3285579efa8521fbf95829b868ff5d37632c4feac6167e9ab2dc4961004c9272"
}
}]
}
30 changes: 30 additions & 0 deletions action/protocol/execution/testdata-istanbul/datacopy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"initBalances": [{
"account": "io1mflp9m6hcgm2qcghchsdqj3z3eccrnekx9p0ms",
"rawBalance": "1000000000000000000000000000"
}],
"deployments": [{
"rawByteCode": "608060405234801561001057600080fd5b50610315806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063574c807814610046575b600080fd5b34801561005257600080fd5b5061005b61005d565b005b606060006060600060036040519080825280601f01601f1916602001820160405280156100995781602001602082028038833980820191505090505b50935060117f0100000000000000000000000000000000000000000000000000000000000000028460008151811015156100cf57fe5b9060200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060227f01000000000000000000000000000000000000000000000000000000000000000284600181518110151561013257fe5b9060200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060337f01000000000000000000000000000000000000000000000000000000000000000284600281518110151561019557fe5b9060200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508351925060036040519080825280601f01601f1916602001820160405280156101fd5781602001602082028038833980820191505090505b50915082602185018460208701600462010000fa9050826000602084013e6102248261022a565b50505050565b8060009080519060200190610240929190610244565b5050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061028557805160ff19168380011785556102b3565b828001600101855582156102b3579182015b828111156102b2578251825591602001919060010190610297565b5b5090506102c091906102c4565b5090565b6102e691905b808211156102e25760008160009055506001016102ca565b5090565b905600a165627a7a723058208a35fa3071989284284d5ed558296837ad55fae1b176b939e5b493485c4794170029",
"rawPrivateKey": "cfa6ef757dee2e50351620dca002d32b9c090cfda55fb81f37f1d26b273743f1",
"rawAmount": "0",
"rawGasLimit": 5000000,
"rawGasPrice": "0",
"rawExpectedGasConsumed": 250102,
"expectedBalances": [],
"comment": "deploy datacopy contract"
}],
"executions": [{
"rawPrivateKey": "cfa6ef757dee2e50351620dca002d32b9c090cfda55fb81f37f1d26b273743f1",
"rawByteCode": "574c8078",
"rawAmount": "0",
"rawGasLimit": 1000000,
"rawGasPrice": "0",
"rawExpectedGasConsumed": 32504,
"comment": "the data of return is [0x11, 0x22, 0x33]",
"expectedBlockInfos" : {
"txRootHash" : "3672945ca662bea4dd977e799374c5ce36c0c3e9ecbe98f1655f33439bbfe40c",
"stateRootHash" : "6fb3dc7f5ef66ce9b384f034191d6a67801e01961ecd824061c4101035844667",
"receiptRootHash" : "0dfe7718f029aaffccd9c20bfaa50a1fb3405014de68af4f89516160de53d2f2"
}
}]
}
24 changes: 24 additions & 0 deletions action/protocol/execution/testdata-istanbul/datacopy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
pragma solidity ^0.8.3;

contract Datacopy {
bytes store;
function dataCopy() public {
bytes memory arr = new bytes(3);
arr[0] = 0x11;
arr[1] = 0x22;
arr[2] = 0x33;
uint length = arr.length;
bytes memory result = new bytes(3);
bool ret;
assembly {
// Call precompiled contract to copy data
ret := staticcall(0x10000, 0x04, add(arr, 0x20), length, add(arr, 0x21), length)
returndatacopy(add(result, 0x20), 0x00, length)
}
updateStore(result);
}

function updateStore(bytes memory ret) internal {
store = ret;
}
}
Loading

0 comments on commit e0683e8

Please sign in to comment.