Skip to content

Commit

Permalink
Security testing: block limits and infinite loop (paritytech#124)
Browse files Browse the repository at this point in the history
* added tx/block test with contract creation

* added bytecode for infinite loop contract

* added infinite contract deployement

* serialized tests and reorganized files

* added fill block report

* added block contruction time

* added open zeppelin test helpers and infinite loop tests

* added different error ctaching testing processes

* test contract fonction calls

* added test notes

* reorganized test constants

* added deployContractManualSeal util

* added contract method manual seal util function

* added gas for infinite call

* separated block limit tests

* updated error messages

* changed createAndFinalizeBlock to use polkadot api instead of web3
  • Loading branch information
joelamouche authored Dec 15, 2020
1 parent dbe7bf6 commit 513947a
Show file tree
Hide file tree
Showing 21 changed files with 852 additions and 131 deletions.
3 changes: 2 additions & 1 deletion tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
"ts-node": "^8.10.2",
"typescript": "^3.9.6",
"web3": "^1.2.9"
}
},
"devDependencies": {}
}
68 changes: 68 additions & 0 deletions tests/tests/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
export {
TEST_CONTRACT_BYTECODE,
TEST_CONTRACT_ABI,
FIRST_CONTRACT_ADDRESS,
TEST_CONTRACT_BYTECODE_INCR,
TEST_CONTRACT_INCR_ABI,
INFINITE_CONTRACT_BYTECODE,
INFINITE_CONTRACT_ABI,
INFINITE_CONTRACT_BYTECODE_VAR,
INFINITE_CONTRACT_ABI_VAR,
FINITE_LOOP_CONTRACT_BYTECODE,
FINITE_LOOP_CONTRACT_ABI,
} from "./testContracts";

export { basicTransfertx, contractCreation } from "./transactionConfigs";

export const PORT = 19931;
export const RPC_PORT = 19932;
export const WS_PORT = 19933;
export const SPECS_PATH = `./moonbeam-test-specs`;

export const DISPLAY_LOG = process.env.MOONBEAM_LOG || false;
export const MOONBEAM_LOG = process.env.MOONBEAM_LOG || "info";

export const BINARY_PATH =
process.env.BINARY_PATH || `../node/standalone/target/release/moonbase-standalone`;
export const SPAWNING_TIME = 30000;

// Test variables
export const GENESIS_ACCOUNT = "0x6be02d1d3665660d22ff9624b7be0551ee1ac91b";
export const GENESIS_ACCOUNT_PRIVATE_KEY =
"0x99B3C12287537E38C90A9219D4CB074A89A16E9CDB20BF85728EBD97C343E342";
export const TEST_ACCOUNT = "0x1111111111111111111111111111111111111111";

// TESTING NOTES
//
// BLOCK TESTING
// - block dont seem to have gas limit but it's usually around 1500 tx per block
// and the time it takes to construct the block increases until around 12s for 1500tx
// - after the first 1500tx block, following block have around 100-300 tx per block
// until all blocks are incuded. 10 blockds for 3000tx
// - between 7k and 10k, for some reason block creation doesnt work and we get
// one Pool(ImmediatelyDropped) error
// and Pool(TooLowPriority { old: 0, new: 0 })': 819 for the contract creation

// 8192 is the number of tx that can be sent to the Pool before it throws an error and drops all tx
// from the pool (we can see in the logs that the ‘ready’ field goes from 8192 to zero)

// It does say however, 8182/20480kB ready, 819/2048kB future and I’m not sure what that means
//
// INFINITE LOOP
// - infinite loop contract should throw out of gas error, but they don't and
// they are included in the block.
// - there are some rpc errors sometimes
// - the state remains unchanged tho (test with infinite incremental contract)
//
// FINITE LOOP
// - making a 1000 loop incr on a smart contract doesnt pass but doesnt throw
// error either (although it does include the tx in a block)
// => is there a problem with out of gas error
// =>probably because we don't have the concept of gas?
// - posting a tx that goes over the gas limit/tx does throw an out of gas error
// in the debug log but not in js

//NB: https://github.com/paritytech/frontier/blob/master/frame/ethereum/src/lib.rs
// show that root=0 when error is thrown,
//which is something we can see when fethcing receipt
// also the current block limit is zero
192 changes: 192 additions & 0 deletions tests/tests/constants/testContracts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import { AbiItem } from "web3-utils";

// Solidity: contract test {function multiply(uint a) public pure returns(uint d) {return a * 7;}}
export const TEST_CONTRACT_BYTECODE =
"0x6080604052348015600f57600080fd5b5060ae8061001e6000396000f3fe6080604052348015600f57600080fd" +
"5b506004361060285760003560e01c8063c6888fa114602d575b600080fd5b605660048036036020811015604157" +
"600080fd5b8101908080359060200190929190505050606c565b6040518082815260200191505060405180910390" +
"f35b600060078202905091905056fea265627a7a72315820f06085b229f27f9ad48b2ff3dd9714350c1698a37853" +
"a30136fa6c5a7762af7364736f6c63430005110032";

export const TEST_CONTRACT_ABI = {
constant: true,
inputs: [{ internalType: "uint256", name: "a", type: "uint256" }],
name: "multiply",
outputs: [{ internalType: "uint256", name: "d", type: "uint256" }],
payable: false,
stateMutability: "pure",
type: "function",
} as AbiItem;

export const FIRST_CONTRACT_ADDRESS = "0xc2bf5f29a4384b1ab0c063e1c666f02121b6084a";

// simple incremental count contract to test contract with state changes

// Solidity:
// contract Test3 {
// uint public count;

// constructor() public {
// count = 0;
// }

// function incr() public {
// count=count+1;
// }
// }
export const TEST_CONTRACT_BYTECODE_INCR =
"6080604052348015600f57600080fd5b506000808190555060a5806100256000396000f3fe608060405234801560" +
"0f57600080fd5b506004361060325760003560e01c806306661abd146037578063119fbbd4146053575b600080fd" +
"5b603d605b565b6040518082815260200191505060405180910390f35b60596061565b005b60005481565b600160" +
"00540160008190555056fea26469706673582212204780263fff0edc01286caed1851cc629033bc25ec1f84995a7" +
"1199017a4623dd64736f6c634300060b0033";

export const TEST_CONTRACT_INCR_ABI = [
{
inputs: [],
stateMutability: "nonpayable",
type: "constructor",
},
{
inputs: [],
name: "count",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "incr",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
] as AbiItem[];

// infinite loop call

// Solidity:
// contract test {
// function infinite(uint a) public pure returns(uint d) {while (true) {}}
// }
export const INFINITE_CONTRACT_BYTECODE =
"6080604052348015600f57600080fd5b5060788061001e6000396000f3fe6080604052348015600f57600080fd5b" +
"506004361060285760003560e01c80635bec9e6714602d575b600080fd5b60336035565b005b5b60011560405760" +
"36565b56fea264697066735822122015c7d339c1118112e1d9b33ea79ded52efa22f4e3cefe34097578a63e128f8" +
"a264736f6c63430007040033";

export const INFINITE_CONTRACT_ABI = {
inputs: [],
name: "infinite",
outputs: [],
stateMutability: "pure",
type: "function",
} as AbiItem;

// infinite loop call with variable alocation

// Solidity:
// contract test {
// function infinite(uint a) public pure returns(uint d) {while (true) {data=data+1;}}
// }
export const INFINITE_CONTRACT_BYTECODE_VAR =
"608060405234801561001057600080fd5b50600160008190555060b0806100276000396000f3fe60806040523480" +
"15600f57600080fd5b506004361060325760003560e01c80635bec9e6714603757806373d4a13a14603f575b6000" +
"80fd5b603d605b565b005b60456074565b6040518082815260200191505060405180910390f35b5b600115607257" +
"600160005401600081905550605c565b565b6000548156fea264697066735822122053e7fd0d4629f7d9cd16b045" +
"6521ea0cf78e595e9627c45ee8a4f27f4119f39c64736f6c634300060b0033";

export const INFINITE_CONTRACT_ABI_VAR = [
{
inputs: [],
stateMutability: "nonpayable",
type: "constructor",
},
{
inputs: [],
name: "data",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "infinite",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
] as AbiItem[];

// definite loop call with variable alocation

// Solidity:
// contract Test4 {
// uint public count;

// constructor() public {
// count = 0;
// }

// function incr(uint n) public {
// uint i=0;
// while (i<n) {
// count=count+1;
// i+=1;
// }
// }
// }

export const FINITE_LOOP_CONTRACT_BYTECODE =
"608060405234801561001057600080fd5b506000808190555060e1806100266000396000f3fe6080604052348015" +
"600f57600080fd5b506004361060325760003560e01c806306661abd14603757806321b13c48146053575b600080" +
"fd5b603d607e565b6040518082815260200191505060405180910390f35b607c6004803603602081101560675760" +
"0080fd5b81019080803590602001909291905050506084565b005b60005481565b60008090505b8181101560a757" +
"600160005401600081905550600181019050608a565b505056fea264697066735822122055c3057e9a4de212a728" +
"58fab41c167c7c616b47ec2ce4e7e1ebf152e8f83dc464736f6c634300060b0033";

export const FINITE_LOOP_CONTRACT_ABI = [
{
inputs: [],
stateMutability: "nonpayable",
type: "constructor",
},
{
inputs: [],
name: "count",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "n",
type: "uint256",
},
],
name: "incr",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
] as AbiItem[];
21 changes: 21 additions & 0 deletions tests/tests/constants/transactionConfigs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// +++ TransactionConfig +++

import { GENESIS_ACCOUNT, TEST_ACCOUNT, TEST_CONTRACT_BYTECODE } from ".";

import { TransactionConfig } from "web3-core";

export const basicTransfertx: TransactionConfig = {
from: GENESIS_ACCOUNT,
to: TEST_ACCOUNT,
value: "0x200", // =512 Must me higher than ExistentialDeposit (500)
gasPrice: "0x01",
gas: "0x100000",
};

export const contractCreation: TransactionConfig = {
from: GENESIS_ACCOUNT,
data: TEST_CONTRACT_BYTECODE,
value: "0x00",
gasPrice: "0x01",
gas: "0x100000",
};
8 changes: 4 additions & 4 deletions tests/tests/test-balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { step } from "mocha-steps";

import { createAndFinalizeBlock, describeWithMoonbeam, customRequest } from "./util";

describeWithMoonbeam("Frontier RPC (Balance)", `simple-specs.json`, (context) => {
describeWithMoonbeam("Moonbeam RPC (Balance)", `simple-specs.json`, (context) => {
const GENESIS_ACCOUNT = "0x6be02d1d3665660d22ff9624b7be0551ee1ac91b";
const GENESIS_ACCOUNT_BALANCE = "340282366920938463463374607431768211455";
const GENESIS_ACCOUNT_PRIVATE_KEY =
Expand Down Expand Up @@ -33,7 +33,7 @@ describeWithMoonbeam("Frontier RPC (Balance)", `simple-specs.json`, (context) =>
GENESIS_ACCOUNT_PRIVATE_KEY
);
await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction]);
await createAndFinalizeBlock(context.web3);
await createAndFinalizeBlock(context.polkadotApi);
expect(await context.web3.eth.getBalance(GENESIS_ACCOUNT)).to.equal(
"340282366920938463463374607431768189943"
);
Expand All @@ -54,7 +54,7 @@ describeWithMoonbeam("Frontier RPC (Balance)", `simple-specs.json`, (context) =>
GENESIS_ACCOUNT_PRIVATE_KEY
);
await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction]);
await createAndFinalizeBlock(context.web3);
await createAndFinalizeBlock(context.polkadotApi);
expect(await context.web3.eth.getBalance(GENESIS_ACCOUNT)).to.equal(
(await context.polkadotApi.query.system.account(GENESIS_ACCOUNT)).data.free.toString()
);
Expand All @@ -68,7 +68,7 @@ describeWithMoonbeam("Frontier RPC (Balance)", `simple-specs.json`, (context) =>
const testAccount = await keyring.addFromUri(GENESIS_ACCOUNT_PRIVATE_KEY, null, "ethereum");
await context.polkadotApi.tx.balances.transfer(TEST_ACCOUNT_2, 123).signAndSend(testAccount);

await createAndFinalizeBlock(context.web3);
await createAndFinalizeBlock(context.polkadotApi);
expect(await context.web3.eth.getBalance(TEST_ACCOUNT_2)).to.equal("123");
});
});
Loading

0 comments on commit 513947a

Please sign in to comment.