Skip to content

Commit

Permalink
test: add TestDirtyStateAttack1
Browse files Browse the repository at this point in the history
  • Loading branch information
k-yang committed Jan 22, 2025
1 parent a58899e commit 9d231c3
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"_format": "hh-sol-artifact-1",
"contractName": "TestDirtyStateAttack1",
"sourceName": "contracts/TestDirtyStateAttack1.sol",
"abi": [
{
"inputs": [
{
"internalType": "address",
"name": "erc20_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address payable",
"name": "sendRecipient",
"type": "address"
},
{
"internalType": "string",
"name": "bech32Recipient",
"type": "string"
}
],
"name": "attack",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x608060405234801561001057600080fd5b506040516107da3803806107da833981810160405281019061003291906100db565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610108565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100a88261007d565b9050919050565b6100b88161009d565b81146100c357600080fd5b50565b6000815190506100d5816100af565b92915050565b6000602082840312156100f1576100f0610078565b5b60006100ff848285016100c6565b91505092915050565b6106c3806101176000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063e655beb414610030575b600080fd5b61004a6004803603810190610045919061040f565b61004c565b005b60008273ffffffffffffffffffffffffffffffffffffffff166108fc678ac7230489e800009081150290604051600060405180830381858888f193505050509050806100cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100c4906104c8565b60405180910390fd5b600061080073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff16629896808560405160240161012093929190610590565b6040516020818303038152906040527fe77a47bf000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516101aa9190610615565b6000604051808303816000865af19150503d80600081146101e7576040519150601f19603f3d011682016040523d82523d6000602084013e6101ec565b606091505b505090508060405160200161020090610652565b60405160208183030381529060405290610250576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610247919061066b565b60405180910390fd5b5050505050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102968261026b565b9050919050565b6102a68161028b565b81146102b157600080fd5b50565b6000813590506102c38161029d565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61031c826102d3565b810181811067ffffffffffffffff8211171561033b5761033a6102e4565b5b80604052505050565b600061034e610257565b905061035a8282610313565b919050565b600067ffffffffffffffff82111561037a576103796102e4565b5b610383826102d3565b9050602081019050919050565b82818337600083830152505050565b60006103b26103ad8461035f565b610344565b9050828152602081018484840111156103ce576103cd6102ce565b5b6103d9848285610390565b509392505050565b600082601f8301126103f6576103f56102c9565b5b813561040684826020860161039f565b91505092915050565b6000806040838503121561042657610425610261565b5b6000610434858286016102b4565b925050602083013567ffffffffffffffff81111561045557610454610266565b5b610461858286016103e1565b9150509250929050565b600082825260208201905092915050565b7f4661696c656420746f2073656e64206574686572000000000000000000000000600082015250565b60006104b260148361046b565b91506104bd8261047c565b602082019050919050565b600060208201905081810360008301526104e1816104a5565b9050919050565b60006104f38261026b565b9050919050565b610503816104e8565b82525050565b6000819050919050565b61051c81610509565b82525050565b600081519050919050565b60005b8381101561054b578082015181840152602081019050610530565b60008484015250505050565b600061056282610522565b61056c818561046b565b935061057c81856020860161052d565b610585816102d3565b840191505092915050565b60006060820190506105a560008301866104fa565b6105b26020830185610513565b81810360408301526105c48184610557565b9050949350505050565b600081519050919050565b600081905092915050565b60006105ef826105ce565b6105f981856105d9565b935061060981856020860161052d565b80840191505092915050565b600061062182846105e4565b915081905092915050565b7f4661696c656420746f2063616c6c2062616e6b53656e64000000000000000000815250565b600061065d8261062c565b601782019150819050919050565b600060208201905081810360008301526106858184610557565b90509291505056fea2646970667358221220b1d6754ff01a90b91186ccf26633cba5add088b2b0240f20b7d32cd876e2820764736f6c63430008180033",
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063e655beb414610030575b600080fd5b61004a6004803603810190610045919061040f565b61004c565b005b60008273ffffffffffffffffffffffffffffffffffffffff166108fc678ac7230489e800009081150290604051600060405180830381858888f193505050509050806100cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100c4906104c8565b60405180910390fd5b600061080073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff16629896808560405160240161012093929190610590565b6040516020818303038152906040527fe77a47bf000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516101aa9190610615565b6000604051808303816000865af19150503d80600081146101e7576040519150601f19603f3d011682016040523d82523d6000602084013e6101ec565b606091505b505090508060405160200161020090610652565b60405160208183030381529060405290610250576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610247919061066b565b60405180910390fd5b5050505050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102968261026b565b9050919050565b6102a68161028b565b81146102b157600080fd5b50565b6000813590506102c38161029d565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61031c826102d3565b810181811067ffffffffffffffff8211171561033b5761033a6102e4565b5b80604052505050565b600061034e610257565b905061035a8282610313565b919050565b600067ffffffffffffffff82111561037a576103796102e4565b5b610383826102d3565b9050602081019050919050565b82818337600083830152505050565b60006103b26103ad8461035f565b610344565b9050828152602081018484840111156103ce576103cd6102ce565b5b6103d9848285610390565b509392505050565b600082601f8301126103f6576103f56102c9565b5b813561040684826020860161039f565b91505092915050565b6000806040838503121561042657610425610261565b5b6000610434858286016102b4565b925050602083013567ffffffffffffffff81111561045557610454610266565b5b610461858286016103e1565b9150509250929050565b600082825260208201905092915050565b7f4661696c656420746f2073656e64206574686572000000000000000000000000600082015250565b60006104b260148361046b565b91506104bd8261047c565b602082019050919050565b600060208201905081810360008301526104e1816104a5565b9050919050565b60006104f38261026b565b9050919050565b610503816104e8565b82525050565b6000819050919050565b61051c81610509565b82525050565b600081519050919050565b60005b8381101561054b578082015181840152602081019050610530565b60008484015250505050565b600061056282610522565b61056c818561046b565b935061057c81856020860161052d565b610585816102d3565b840191505092915050565b60006060820190506105a560008301866104fa565b6105b26020830185610513565b81810360408301526105c48184610557565b9050949350505050565b600081519050919050565b600081905092915050565b60006105ef826105ce565b6105f981856105d9565b935061060981856020860161052d565b80840191505092915050565b600061062182846105e4565b915081905092915050565b7f4661696c656420746f2063616c6c2062616e6b53656e64000000000000000000815250565b600061065d8261062c565b601782019150819050919050565b600060208201905081810360008301526106858184610557565b90509291505056fea2646970667358221220b1d6754ff01a90b91186ccf26633cba5add088b2b0240f20b7d32cd876e2820764736f6c63430008180033",
"linkReferences": {},
"deployedLinkReferences": {}
}
34 changes: 34 additions & 0 deletions x/evm/embeds/contracts/TestDirtyStateAttack1.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

// Uncomment this line to use console.log
// import "hardhat/console.sol";
import "./IFunToken.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

contract TestDirtyStateAttack1 {
address erc20;

constructor(address erc20_) {
erc20 = erc20_;
}

function attack(
address payable sendRecipient,
string memory bech32Recipient
) public {
bool isSent = sendRecipient.send(10 ether); // 10 NIBI
require(isSent, "Failed to send ether");

(bool success, ) = FUNTOKEN_PRECOMPILE_ADDRESS.call(
abi.encodeWithSignature(
"sendToBank(address,uint256,string)",
erc20,
uint256(10e6), // 10 WNIBI
bech32Recipient
)
);

require(success, string.concat("Failed to call bankSend"));
}
}
10 changes: 8 additions & 2 deletions x/evm/embeds/embeds.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ var (
testMetadataBytes32 []byte
//go:embed artifacts/contracts/TestPrecompileSendToBankThenERC20Transfer.sol/TestPrecompileSendToBankThenERC20Transfer.json
testPrecompileSendToBankThenERC20Transfer []byte
//go:embed artifacts/contracts/TestDirtyStateAttack1.sol/TestDirtyStateAttack1.json
testDirtyStateAttack1 []byte
)

var (
Expand Down Expand Up @@ -118,7 +120,6 @@ var (
Name: "TestERC20TransferThenPrecompileSend.sol",
EmbedJSON: testERC20TransferThenPrecompileSendJson,
}

// SmartContract_TestPrecompileSelfCallRevert is a test contract
// that creates another instance of itself, calls the precompile method and then force reverts.
// It tests a race condition where the state DB commit
Expand Down Expand Up @@ -150,12 +151,16 @@ var (
Name: "MKR.sol",
EmbedJSON: testMetadataBytes32,
}

// SmartContract_TestPrecompileSendToBankThenERC20Transfer is a test contract that sends to bank then calls ERC20 transfer
SmartContract_TestPrecompileSendToBankThenERC20Transfer = CompiledEvmContract{
Name: "TestPrecompileSendToBankThenERC20Transfer.sol",
EmbedJSON: testPrecompileSendToBankThenERC20Transfer,
}
// SmartContract_TestDirtyStateAttack1 is a test contract that sends to bank then calls ERC20 transfer
SmartContract_TestDirtyStateAttack1 = CompiledEvmContract{
Name: "TestDirtyStateAttack1.sol",
EmbedJSON: testDirtyStateAttack1,
}
)

func init() {
Expand All @@ -175,6 +180,7 @@ func init() {
SmartContract_TestRandom.MustLoad()
SmartContract_TestBytes32Metadata.MustLoad()
SmartContract_TestPrecompileSendToBankThenERC20Transfer.MustLoad()
SmartContract_TestDirtyStateAttack1.MustLoad()
}

type CompiledEvmContract struct {
Expand Down
101 changes: 101 additions & 0 deletions x/evm/keeper/funtoken_from_coin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,107 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSendToBankThenErc20Transfer() {
}.Assert(s.T(), deps, evmObj)
}

// TestDirtyStateAttack1
// 1. Creates a funtoken from coin.
// 2. Calls the test contract
// a. manual send 10 NIBI to Alice
// b. FunToken.sendToBank 10 WNIBI to Alice
//
// INITIAL STATE:
// - Test contract funds: 10 WNIBI, 10 NIBI
// CONTRACT CALL:
// - Sends 10 NIBI to Alice manually
// - Sends 10 WNIBI to Alice via FunToken.sendToBank
// EXPECTED:
// - Test contract funds: 0 WNIBI, 0 NIBI
// - Alice: 20 NIBI
// - Module account: 0 NIBI escrowed
func (s *FunTokenFromCoinSuite) TestDirtyStateAttack1() {
deps := evmtest.NewTestDeps()

// Initial setup
funToken := s.fundAndCreateFunToken(deps, 10e6)

s.T().Log("Deploy Test Contract")
deployResp, err := evmtest.DeployContract(
&deps,
embeds.SmartContract_TestDirtyStateAttack1,
funToken.Erc20Addr.Address,
)
s.Require().NoError(err)
testContractAddr := deployResp.ContractAddr

s.Run("Convert bank coin to erc-20: give test contract 10 WNIBI (erc20)", func() {
_, err = deps.EvmKeeper.ConvertCoinToEvm(
sdk.WrapSDKContext(deps.Ctx),
&evm.MsgConvertCoinToEvm{
Sender: deps.Sender.NibiruAddr.String(),
BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6)),
ToEthAddr: eth.EIP55Addr{Address: testContractAddr},
},
)
s.Require().NoError(err)
})

s.Run("Send 10 NIBI to test contract manually", func() {
s.Require().NoError(testapp.FundAccount(
deps.App.BankKeeper,
deps.Ctx,
eth.EthAddrToNibiruAddr(testContractAddr),
sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6))),
))
})

// Create Alice and Bob. Contract will try to send Alice native coins and
// send Bob ERC20 tokens.
alice := evmtest.NewEthPrivAcc()

s.T().Log("call test contract")
s.Run("call test contract", func() {
contractInput, err := embeds.SmartContract_TestDirtyStateAttack1.ABI.Pack(
"attack",
alice.EthAddr,
alice.NibiruAddr.String(),
)
s.Require().NoError(err)
evmObj, _ := deps.NewEVM()
_, err = deps.EvmKeeper.CallContractWithInput(
deps.Ctx,
evmObj,
deps.Sender.EthAddr,
&testContractAddr,
true,
contractInput,
evmtest.FunTokenGasLimitSendToEvm,
)
s.Require().NoError(err)

evmtest.FunTokenBalanceAssert{
FunToken: funToken,
Account: alice.EthAddr,
BalanceBank: big.NewInt(20e6),
BalanceERC20: big.NewInt(0),
Description: "Alice has 20 NIBI / 0 WNIBI",
}.Assert(s.T(), deps, evmObj)

evmtest.FunTokenBalanceAssert{
FunToken: funToken,
Account: testContractAddr,
BalanceBank: big.NewInt(0),
BalanceERC20: big.NewInt(0),
Description: "Test contract has 0 WNIBI / 0 NIBI",
}.Assert(s.T(), deps, evmObj)

evmtest.FunTokenBalanceAssert{
FunToken: funToken,
Account: evm.EVM_MODULE_ADDRESS,
BalanceBank: big.NewInt(0),
BalanceERC20: big.NewInt(0),
Description: "Module account has 0 NIBI escrowed",
}.Assert(s.T(), deps, evmObj)
})
}

// fundAndCreateFunToken creates initial setup for tests
func (s *FunTokenFromCoinSuite) fundAndCreateFunToken(deps evmtest.TestDeps, unibiAmount int64) evm.FunToken {
bankDenom := evm.EVMBankDenom
Expand Down

0 comments on commit 9d231c3

Please sign in to comment.