Merge branch 'release/v1.20.0'

This commit is contained in:
Aayush 2023-02-14 20:09:09 -05:00
commit 75c744a438
22 changed files with 1024 additions and 118 deletions

View File

@ -1,2 +1,2 @@
/dns4/bootstrap-0.butterfly.fildev.network/tcp/1347/p2p/12D3KooWHkVVMJ1rfVLM5poNrgwTJiaDkpDLkPqQ9zVuNPQ7AJ6p
/dns4/bootstrap-1.butterfly.fildev.network/tcp/1347/p2p/12D3KooWRyzqeQd51HCvVK3nvegmnBsYYPLSZbxR3Q9XAoUrUZ18
/dns4/bootstrap-0.butterfly.fildev.network/tcp/1347/p2p/12D3KooWCa1wgMMBB9JjA2kYqaN1v5uh7xvcsc2gQJBHzPp7G57H
/dns4/bootstrap-1.butterfly.fildev.network/tcp/1347/p2p/12D3KooWD6fCvo1dyci6wsjTLyv7eJK73pCVz6RCQjbtPvbc8LYw

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -4,7 +4,6 @@
package build
import (
"math"
"os"
"strconv"
@ -71,7 +70,8 @@ const UpgradeSkyrHeight = 510
const UpgradeSharkHeight = 16800 // 6 days after genesis
const UpgradeHyggeHeight = math.MaxInt64
// 2023-02-21T16:30:00Z
const UpgradeHyggeHeight = 322354
var SupportedProofTypes = []abi.RegisteredSealProof{
abi.RegisteredSealProof_StackedDrg32GiBV1,

View File

@ -31,6 +31,10 @@ var ErrExpensiveFork = errors.New("refusing explicit call due to state fork at e
// tipset's parent. In the presence of null blocks, the height at which the message is invoked may
// be less than the specified tipset.
func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.InvocResult, error) {
// Copy the message as we modify it below.
msgCopy := *msg
msg = &msgCopy
if msg.GasLimit == 0 {
msg.GasLimit = build.BlockGasLimit
}

View File

@ -12,6 +12,7 @@ import (
"github.com/filecoin-project/go-address"
gocrypto "github.com/filecoin-project/go-crypto"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
builtintypes "github.com/filecoin-project/go-state-types/builtin"
typescrypto "github.com/filecoin-project/go-state-types/crypto"
@ -38,11 +39,19 @@ type EthTx struct {
MaxFeePerGas EthBigInt `json:"maxFeePerGas"`
MaxPriorityFeePerGas EthBigInt `json:"maxPriorityFeePerGas"`
AccessList []EthHash `json:"accessList"`
V EthBigInt `json:"yParity"`
V EthBigInt `json:"v"`
R EthBigInt `json:"r"`
S EthBigInt `json:"s"`
}
func (tx *EthTx) Reward(blkBaseFee big.Int) EthBigInt {
availablePriorityFee := big.Sub(big.Int(tx.MaxFeePerGas), blkBaseFee)
if big.Cmp(big.Int(tx.MaxPriorityFeePerGas), availablePriorityFee) <= 0 {
return tx.MaxPriorityFeePerGas
}
return EthBigInt(availablePriorityFee)
}
type EthTxArgs struct {
ChainID int `json:"chainId"`
Nonce int `json:"nonce"`
@ -97,24 +106,31 @@ func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) {
func EthTxArgsFromUnsignedEthMessage(msg *types.Message) (EthTxArgs, error) {
var (
to *EthAddress
params []byte
paramsReader = bytes.NewReader(msg.Params)
err error
to *EthAddress
params []byte
err error
)
if msg.Version != 0 {
return EthTxArgs{}, xerrors.Errorf("unsupported msg version: %d", msg.Version)
}
if len(msg.Params) > 0 {
paramsReader := bytes.NewReader(msg.Params)
params, err = cbg.ReadByteArray(paramsReader, uint64(len(msg.Params)))
if err != nil {
return EthTxArgs{}, xerrors.Errorf("failed to read params byte array: %w", err)
}
if paramsReader.Len() != 0 {
return EthTxArgs{}, xerrors.Errorf("extra data found in params")
}
if len(params) == 0 {
return EthTxArgs{}, xerrors.Errorf("non-empty params encode empty byte array")
}
}
if msg.To == builtintypes.EthereumAddressManagerActorAddr {
switch msg.Method {
case builtintypes.MethodsEAM.CreateExternal:
params, err = cbg.ReadByteArray(paramsReader, uint64(len(msg.Params)))
if err != nil {
return EthTxArgs{}, xerrors.Errorf("failed to read params byte array: %w", err)
}
default:
if msg.Method != builtintypes.MethodsEAM.CreateExternal {
return EthTxArgs{}, fmt.Errorf("unsupported EAM method")
}
} else if msg.Method == builtintypes.MethodsEVM.InvokeContract {
@ -123,23 +139,12 @@ func EthTxArgsFromUnsignedEthMessage(msg *types.Message) (EthTxArgs, error) {
return EthTxArgs{}, err
}
to = &addr
if len(msg.Params) > 0 {
params, err = cbg.ReadByteArray(paramsReader, uint64(len(msg.Params)))
if err != nil {
return EthTxArgs{}, xerrors.Errorf("failed to read params byte array: %w", err)
}
}
} else {
return EthTxArgs{},
xerrors.Errorf("invalid methodnum %d: only allowed method is InvokeContract(%d)",
msg.Method, builtintypes.MethodsEVM.InvokeContract)
}
if paramsReader.Len() != 0 {
return EthTxArgs{}, xerrors.Errorf("extra data found in params")
}
return EthTxArgs{
ChainID: build.Eip155ChainId,
Nonce: int(msg.Nonce),
@ -159,34 +164,26 @@ func (tx *EthTxArgs) ToUnsignedMessage(from address.Address) (*types.Message, er
var err error
var params []byte
var to address.Address
method := builtintypes.MethodsEVM.InvokeContract
// nil indicates the EAM, only CreateExternal is allowed
if tx.To == nil {
to = builtintypes.EthereumAddressManagerActorAddr
method = builtintypes.MethodsEAM.CreateExternal
if len(tx.Input) == 0 {
return nil, xerrors.New("cannot call CreateExternal without params")
}
if len(tx.Input) > 0 {
buf := new(bytes.Buffer)
if err = cbg.WriteByteArray(buf, tx.Input); err != nil {
return nil, xerrors.Errorf("failed to serialize Create params: %w", err)
return nil, xerrors.Errorf("failed to write input args: %w", err)
}
params = buf.Bytes()
}
var to address.Address
var method abi.MethodNum
// nil indicates the EAM, only CreateExternal is allowed
if tx.To == nil {
method = builtintypes.MethodsEAM.CreateExternal
to = builtintypes.EthereumAddressManagerActorAddr
} else {
method = builtintypes.MethodsEVM.InvokeContract
to, err = tx.To.ToFilecoinAddress()
if err != nil {
return nil, xerrors.Errorf("failed to convert To into filecoin addr: %w", err)
}
if len(tx.Input) > 0 {
buf := new(bytes.Buffer)
if err = cbg.WriteByteArray(buf, tx.Input); err != nil {
return nil, xerrors.Errorf("failed to write input args: %w", err)
}
params = buf.Bytes()
}
}
return &types.Message{

View File

@ -2643,7 +2643,7 @@ Response:
"accessList": [
"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"
],
"yParity": "0x0",
"v": "0x0",
"r": "0x0",
"s": "0x0"
}
@ -2682,7 +2682,7 @@ Response:
"accessList": [
"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"
],
"yParity": "0x0",
"v": "0x0",
"r": "0x0",
"s": "0x0"
}
@ -2720,7 +2720,7 @@ Response:
"accessList": [
"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"
],
"yParity": "0x0",
"v": "0x0",
"r": "0x0",
"s": "0x0"
}

2
extern/filecoin-ffi vendored

@ -1 +1 @@
Subproject commit 4c503e5e2291b5d541f89d982d975e7994536a54
Subproject commit 7efaa7b47fe9d4bdb4ba0b2a0fafa4e573864ee5

2
go.mod
View File

@ -44,7 +44,7 @@ require (
github.com/filecoin-project/go-legs v0.4.4
github.com/filecoin-project/go-padreader v0.0.1
github.com/filecoin-project/go-paramfetch v0.0.4
github.com/filecoin-project/go-state-types v0.10.0-rc2
github.com/filecoin-project/go-state-types v0.10.0-rc3
github.com/filecoin-project/go-statemachine v1.0.2
github.com/filecoin-project/go-statestore v0.2.0
github.com/filecoin-project/go-storedcounter v0.1.0

4
go.sum
View File

@ -356,8 +356,8 @@ github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psS
github.com/filecoin-project/go-state-types v0.1.6/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q=
github.com/filecoin-project/go-state-types v0.1.8/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q=
github.com/filecoin-project/go-state-types v0.1.10/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q=
github.com/filecoin-project/go-state-types v0.10.0-rc2 h1:nl92h86XridAoy0fjvW+8/8/eI0caVSm0fhAnIvtR64=
github.com/filecoin-project/go-state-types v0.10.0-rc2/go.mod h1:aLIas+W8BWAfpLWEPUOGMPBdhcVwoCG4pIQSQk26024=
github.com/filecoin-project/go-state-types v0.10.0-rc3 h1:qExCc2swTe5ndsiu9dEoMqIwppjuTNRbsAFgpzHnHbc=
github.com/filecoin-project/go-state-types v0.10.0-rc3/go.mod h1:aLIas+W8BWAfpLWEPUOGMPBdhcVwoCG4pIQSQk26024=
github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig=
github.com/filecoin-project/go-statemachine v1.0.2 h1:421SSWBk8GIoCoWYYTE/d+qCWccgmRH0uXotXRDjUbc=
github.com/filecoin-project/go-statemachine v1.0.2/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54=

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b5060b58061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063b6baffe314602d575b600080fd5b60336047565b604051603e91906066565b60405180910390f35b600044905090565b6000819050919050565b606081604f565b82525050565b6000602082019050607960008301846059565b9291505056fea2646970667358221220c113f1abaabaed6a0324d363896b0d15a8bca7b9a540948a5be5b636a12a534f64736f6c63430008110033

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17;
contract GetDifficulty {
function getDifficulty () public view returns (uint256) {
return block.difficulty;
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,590 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
abstract contract Initializable {
uint8 private _initialized;
bool private _initializing;
event Initialized(uint8 version);
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) ||
(!Address.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
modifier reinitializer(uint8 version) {
require(
!_initializing && _initialized < version,
"Initializable: contract is already initialized"
);
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}
contract Implementation4 is Initializable {
uint256 internal _value;
function initialize() public initializer {}
function setValue(uint256 _number) public {
_value = _number;
}
function getValue() public view returns (uint256) {
return _value;
}
fallback() external {
_value = 1;
}
}
contract Implementation2 is Initializable {
uint256 internal _value;
function initialize() public initializer {}
function setValue(uint256 _number) public {
_value = _number;
}
function getValue() public view returns (uint256) {
return _value;
}
}
abstract contract Proxy {
function _delegate(address implementation) internal virtual {
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(
gas(),
implementation,
0,
calldatasize(),
0,
0
)
returndatacopy(0, 0, returndatasize())
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
function _implementation() internal view virtual returns (address);
function _fallback() internal virtual {
_beforeFallback();
_delegate(_implementation());
}
fallback() external payable virtual {
_fallback();
}
receive() external payable virtual {
_fallback();
}
function _beforeFallback() internal virtual {}
}
interface IBeacon {
function implementation() external view returns (address);
}
interface IERC1822Proxiable {
function proxiableUUID() external view returns (bytes32);
}
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(
address(this).balance >= amount,
"Address: insufficient balance"
);
(bool success, ) = recipient.call{value: amount}("");
require(
success,
"Address: unable to send value, recipient may have reverted"
);
}
function functionCall(
address target,
bytes memory data
) internal returns (bytes memory) {
return
functionCallWithValue(
target,
data,
0,
"Address: low-level call failed"
);
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return
functionCallWithValue(
target,
data,
value,
"Address: low-level call with value failed"
);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(
address(this).balance >= value,
"Address: insufficient balance for call"
);
(bool success, bytes memory returndata) = target.call{value: value}(
data
);
return
verifyCallResultFromTarget(
target,
success,
returndata,
errorMessage
);
}
function functionStaticCall(
address target,
bytes memory data
) internal view returns (bytes memory) {
return
functionStaticCall(
target,
data,
"Address: low-level static call failed"
);
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return
verifyCallResultFromTarget(
target,
success,
returndata,
errorMessage
);
}
function functionDelegateCall(
address target,
bytes memory data
) internal returns (bytes memory) {
return
functionDelegateCall(
target,
data,
"Address: low-level delegate call failed"
);
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return
verifyCallResultFromTarget(
target,
success,
returndata,
errorMessage
);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(
bytes memory returndata,
string memory errorMessage
) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
function getAddressSlot(
bytes32 slot
) internal pure returns (AddressSlot storage r) {
assembly {
r.slot := slot
}
}
function getBooleanSlot(
bytes32 slot
) internal pure returns (BooleanSlot storage r) {
assembly {
r.slot := slot
}
}
function getBytes32Slot(
bytes32 slot
) internal pure returns (Bytes32Slot storage r) {
assembly {
r.slot := slot
}
}
function getUint256Slot(
bytes32 slot
) internal pure returns (Uint256Slot storage r) {
assembly {
r.slot := slot
}
}
}
abstract contract ERC1967Upgrade {
bytes32 private constant _ROLLBACK_SLOT =
0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
bytes32 internal constant _IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
event Upgraded(address indexed implementation);
function _getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
function _setImplementation(address newImplementation) private {
require(
Address.isContract(newImplementation),
"ERC1967: new implementation is not a contract"
);
StorageSlot
.getAddressSlot(_IMPLEMENTATION_SLOT)
.value = newImplementation;
}
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
function _upgradeToAndCall(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
_upgradeTo(newImplementation);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(newImplementation, data);
}
}
function _upgradeToAndCallUUPS(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
_setImplementation(newImplementation);
} else {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (
bytes32 slot
) {
require(
slot == _IMPLEMENTATION_SLOT,
"ERC1967Upgrade: unsupported proxiableUUID"
);
} catch {
revert("ERC1967Upgrade: new implementation is not UUPS");
}
_upgradeToAndCall(newImplementation, data, forceCall);
}
}
bytes32 internal constant _ADMIN_SLOT =
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
event AdminChanged(address previousAdmin, address newAdmin);
function _getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
}
function _setAdmin(address newAdmin) private {
require(
newAdmin != address(0),
"ERC1967: new admin is the zero address"
);
StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
}
function _changeAdmin(address newAdmin) internal {
emit AdminChanged(_getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
bytes32 internal constant _BEACON_SLOT =
0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
event BeaconUpgraded(address indexed beacon);
function _getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
}
function _setBeacon(address newBeacon) private {
require(
Address.isContract(newBeacon),
"ERC1967: new beacon is not a contract"
);
require(
Address.isContract(IBeacon(newBeacon).implementation()),
"ERC1967: beacon implementation is not a contract"
);
StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
}
function _upgradeBeaconToAndCall(
address newBeacon,
bytes memory data,
bool forceCall
) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(
IBeacon(newBeacon).implementation(),
data
);
}
}
}
contract ERC1967Proxy is Proxy, ERC1967Upgrade {
constructor(address _logic, bytes memory _data) payable {
_upgradeToAndCall(_logic, _data, false);
}
function _implementation()
internal
view
virtual
override
returns (address impl)
{
return ERC1967Upgrade._getImplementation();
}
}
contract TransparentUpgradeableProxy is ERC1967Proxy {
constructor(address _logic) payable ERC1967Proxy(_logic, "") {
_changeAdmin(msg.sender);
}
modifier ifAdmin() {
if (msg.sender == _getAdmin()) {
_;
} else {
_fallback();
}
}
function admin() external payable ifAdmin returns (address admin_) {
_requireZeroValue();
admin_ = _getAdmin();
}
function implementation()
external
payable
ifAdmin
returns (address implementation_)
{
_requireZeroValue();
implementation_ = _implementation();
}
function changeAdmin(address newAdmin) external payable virtual ifAdmin {
_requireZeroValue();
_changeAdmin(newAdmin);
}
function upgradeTo(address newImplementation) external payable ifAdmin {
_requireZeroValue();
_upgradeToAndCall(newImplementation, bytes(""), false);
}
function upgradeToAndCall(
address newImplementation,
bytes calldata data
) external payable ifAdmin {
_upgradeToAndCall(newImplementation, data, true);
}
function _admin() internal view virtual returns (address) {
return _getAdmin();
}
function _beforeFallback() internal virtual override {
require(
msg.sender != _getAdmin(),
"TransparentUpgradeableProxy: admin cannot fallback to proxy target"
);
super._beforeFallback();
}
function _requireZeroValue() private {
require(msg.value == 0);
}
}
contract TestHelper {
function getValue(address proxyAddress) public returns (uint256) {
Implementation2 proxyInstance2 = Implementation2(proxyAddress);
return proxyInstance2.getValue();
}
}
contract TransparentUpgradeableProxyTestRunner {
function test() public {
assert(0 == getValue());
}
function getValue() public returns (uint256) {
Implementation4 instance4 = new Implementation4();
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(instance4)
);
Implementation2 instance2 = new Implementation2();
proxy.upgradeTo(address(instance2));
//use helper because proxy admin can't call getValue()
TestHelper h = new TestHelper();
return h.getValue(address(proxy));
}
}

View File

@ -5,7 +5,7 @@ set -o pipefail
# to compile all of the .sol files to their corresponding evm binary files stored as .hex
# solc outputs to stdout a format that we just want to grab the last line of and then remove the trailing newline on that line
find . -name \*.sol -print0 |
find . -maxdepth 1 -name \*.sol -print0 |
xargs -0 -I{} bash -euc -o pipefail 'solc --bin {} |tail -n1 | tr -d "\n" > $(echo {} | sed -e s/.sol$/.hex/)'
@ -17,3 +17,7 @@ for filename in Constructor TestApp ValueSender Create2Factory DeployValueTest;
solc --bin $filename.sol | tail -n5|head -n1 | tr -d "\n" > $filename.hex
done
for filename in TransparentUpgradeableProxy ; do
echo $filename
solc --bin $filename.sol | tail -n1| tr -d "\n" > $filename.hex
done

View File

@ -28,7 +28,7 @@ func TestEthFeeHistory(t *testing.T) {
defer cancel()
// Wait for the network to create 20 blocks
<-time.After(20 * blockTime)
client.WaitTillChain(ctx, kit.HeightAtLeast(20))
history, err := client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{5, "0x10"}),
@ -37,6 +37,7 @@ func TestEthFeeHistory(t *testing.T) {
require.Equal(6, len(history.BaseFeePerGas))
require.Equal(5, len(history.GasUsedRatio))
require.Equal(ethtypes.EthUint64(16-5+1), history.OldestBlock)
require.Nil(history.Reward)
history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{"5", "0x10"}),
@ -45,6 +46,7 @@ func TestEthFeeHistory(t *testing.T) {
require.Equal(6, len(history.BaseFeePerGas))
require.Equal(5, len(history.GasUsedRatio))
require.Equal(ethtypes.EthUint64(16-5+1), history.OldestBlock)
require.Nil(history.Reward)
history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{"0x10", "0x12"}),
@ -53,6 +55,7 @@ func TestEthFeeHistory(t *testing.T) {
require.Equal(17, len(history.BaseFeePerGas))
require.Equal(16, len(history.GasUsedRatio))
require.Equal(ethtypes.EthUint64(18-16+1), history.OldestBlock)
require.Nil(history.Reward)
history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{5, "0x10"}),
@ -61,6 +64,7 @@ func TestEthFeeHistory(t *testing.T) {
require.Equal(6, len(history.BaseFeePerGas))
require.Equal(5, len(history.GasUsedRatio))
require.Equal(ethtypes.EthUint64(16-5+1), history.OldestBlock)
require.Nil(history.Reward)
history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{5, "10"}),
@ -69,19 +73,28 @@ func TestEthFeeHistory(t *testing.T) {
require.Equal(6, len(history.BaseFeePerGas))
require.Equal(5, len(history.GasUsedRatio))
require.Equal(ethtypes.EthUint64(10-5+1), history.OldestBlock)
require.Nil(history.Reward)
history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{5, "10", &[]float64{0.25, 0.50, 0.75}}),
json.Marshal([]interface{}{5, "10", &[]float64{25, 50, 75}}),
).Assert(require.NoError))
require.NoError(err)
require.Equal(6, len(history.BaseFeePerGas))
require.Equal(5, len(history.GasUsedRatio))
require.Equal(ethtypes.EthUint64(10-5+1), history.OldestBlock)
require.NotNil(history.Reward)
require.Equal(0, len(*history.Reward))
require.Equal(5, len(*history.Reward))
for _, arr := range *history.Reward {
require.Equal(3, len(arr))
}
history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{1025, "10", &[]float64{0.25, 0.50, 0.75}}),
json.Marshal([]interface{}{1025, "10", &[]float64{25, 50, 75}}),
).Assert(require.NoError))
require.Error(err)
history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{5, "10", &[]float64{}}),
).Assert(require.NoError))
require.NoError(err)
}

View File

@ -136,6 +136,113 @@ func TestEthNewPendingTransactionFilter(t *testing.T) {
}
}
func TestEthNewPendingTransactionSub(t *testing.T) {
require := require.New(t)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
kit.QuietAllLogsExcept("events", "messagepool")
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.WithEthRPC())
ens.InterconnectAll().BeginMining(10 * time.Millisecond)
// create a new address where to send funds.
addr, err := client.WalletNew(ctx, types.KTBLS)
require.NoError(err)
// get the existing balance from the default wallet to then split it.
bal, err := client.WalletBalance(ctx, client.DefaultKey.Address)
require.NoError(err)
// install filter
subId, err := client.EthSubscribe(ctx, res.Wrap[jsonrpc.RawParams](json.Marshal(ethtypes.EthSubscribeParams{EventType: "newPendingTransactions"})).Assert(require.NoError))
require.NoError(err)
var subResponses []ethtypes.EthSubscriptionResponse
err = client.EthSubRouter.AddSub(ctx, subId, func(ctx context.Context, resp *ethtypes.EthSubscriptionResponse) error {
subResponses = append(subResponses, *resp)
return nil
})
require.NoError(err)
const iterations = 100
// we'll send half our balance (saving the other half for gas),
// in `iterations` increments.
toSend := big.Div(bal, big.NewInt(2))
each := big.Div(toSend, big.NewInt(iterations))
waitAllCh := make(chan struct{})
go func() {
headChangeCh, err := client.ChainNotify(ctx)
require.NoError(err)
<-headChangeCh // skip hccurrent
defer func() {
close(waitAllCh)
}()
count := 0
for {
select {
case <-ctx.Done():
return
case headChanges := <-headChangeCh:
for _, change := range headChanges {
if change.Type == store.HCApply {
msgs, err := client.ChainGetMessagesInTipset(ctx, change.Val.Key())
require.NoError(err)
count += len(msgs)
if count == iterations {
return
}
}
}
}
}
}()
var sms []*types.SignedMessage
for i := 0; i < iterations; i++ {
msg := &types.Message{
From: client.DefaultKey.Address,
To: addr,
Value: each,
}
sm, err := client.MpoolPushMessage(ctx, msg, nil)
require.NoError(err)
require.EqualValues(i, sm.Message.Nonce)
sms = append(sms, sm)
}
select {
case <-waitAllCh:
case <-ctx.Done():
t.Errorf("timeout waiting to pack messages")
}
expected := make(map[string]bool)
for _, sm := range sms {
hash, err := ethtypes.EthHashFromCid(sm.Cid())
require.NoError(err)
expected[hash.String()] = false
}
// expect to have seen iteration number of mpool messages
require.Equal(len(subResponses), len(expected), "expected number of filter results to equal number of messages")
for _, txid := range subResponses {
expected[txid.Result.(string)] = true
}
for _, found := range expected {
require.True(found)
}
}
func TestEthNewBlockFilter(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

View File

@ -843,3 +843,28 @@ func TestFEVMBareTransferTriggersSmartContractLogic(t *testing.T) {
// The receive() function emits one log, that's how we know we hit it.
require.Len(t, receipt.Logs, 1)
}
func TestFEVMProxyUpgradeable(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install transparently upgradeable proxy
proxyFilename := "contracts/TransparentUpgradeableProxy.hex"
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, proxyFilename)
_, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "test()", []byte{})
require.NoError(t, err)
}
func TestFEVMGetBlockDifficulty(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract
filenameActor := "contracts/GetDifficulty.hex"
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor)
ret, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "getDifficulty()", []byte{})
require.NoError(t, err)
require.Equal(t, len(ret), 32)
}

View File

@ -178,7 +178,7 @@
"s",
"type",
"value",
"yParity"
"v"
],
"properties": {
"type": {
@ -253,8 +253,8 @@
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "Chain ID that this transaction is valid on."
},
"yParity": {
"title": "yParity",
"v": {
"title": "v",
"type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature."
@ -285,7 +285,7 @@
"s",
"type",
"value",
"yParity"
"v"
],
"properties": {
"type": {
@ -354,8 +354,8 @@
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "Chain ID that this transaction is valid on."
},
"yParity": {
"title": "yParity",
"v": {
"title": "v",
"type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature."
@ -649,7 +649,7 @@
"s",
"type",
"value",
"yParity"
"v"
],
"properties": {
"type": {
@ -724,8 +724,8 @@
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "Chain ID that this transaction is valid on."
},
"yParity": {
"title": "yParity",
"v": {
"title": "v",
"type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature."
@ -756,7 +756,7 @@
"s",
"type",
"value",
"yParity"
"v"
],
"properties": {
"type": {
@ -825,8 +825,8 @@
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "Chain ID that this transaction is valid on."
},
"yParity": {
"title": "yParity",
"v": {
"title": "v",
"type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature."
@ -3009,7 +3009,7 @@
"s",
"type",
"value",
"yParity"
"v"
],
"properties": {
"type": {
@ -3084,8 +3084,8 @@
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "Chain ID that this transaction is valid on."
},
"yParity": {
"title": "yParity",
"v": {
"title": "v",
"type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature."
@ -3116,7 +3116,7 @@
"s",
"type",
"value",
"yParity"
"v"
],
"properties": {
"type": {
@ -3185,8 +3185,8 @@
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "Chain ID that this transaction is valid on."
},
"yParity": {
"title": "yParity",
"v": {
"title": "v",
"type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature."
@ -3359,7 +3359,7 @@
"s",
"type",
"value",
"yParity"
"v"
],
"properties": {
"type": {
@ -3434,8 +3434,8 @@
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "Chain ID that this transaction is valid on."
},
"yParity": {
"title": "yParity",
"v": {
"title": "v",
"type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature."
@ -3466,7 +3466,7 @@
"s",
"type",
"value",
"yParity"
"v"
],
"properties": {
"type": {
@ -3535,8 +3535,8 @@
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "Chain ID that this transaction is valid on."
},
"yParity": {
"title": "yParity",
"v": {
"title": "v",
"type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature."
@ -3726,7 +3726,7 @@
"s",
"type",
"value",
"yParity"
"v"
],
"properties": {
"type": {
@ -3801,8 +3801,8 @@
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "Chain ID that this transaction is valid on."
},
"yParity": {
"title": "yParity",
"v": {
"title": "v",
"type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature."
@ -3833,7 +3833,7 @@
"s",
"type",
"value",
"yParity"
"v"
],
"properties": {
"type": {
@ -3902,8 +3902,8 @@
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "Chain ID that this transaction is valid on."
},
"yParity": {
"title": "yParity",
"v": {
"title": "v",
"type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
"description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature."

View File

@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"sort"
"strconv"
"sync"
"time"
@ -525,7 +526,7 @@ func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAdd
}
params, err := actors.SerializeParams(&evm.GetStorageAtParams{
StorageKey: position,
StorageKey: *(*[32]byte)(position),
})
if err != nil {
return nil, fmt.Errorf("failed to serialize parameters: %w", err)
@ -601,6 +602,18 @@ func (a *EthModule) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (eth
if params.BlkCount > 1024 {
return ethtypes.EthFeeHistory{}, fmt.Errorf("block count should be smaller than 1024")
}
rewardPercentiles := make([]float64, 0)
if params.RewardPercentiles != nil {
rewardPercentiles = append(rewardPercentiles, *params.RewardPercentiles...)
}
for i, rp := range rewardPercentiles {
if rp < 0 || rp > 100 {
return ethtypes.EthFeeHistory{}, fmt.Errorf("invalid reward percentile: %f should be between 0 and 100", rp)
}
if i > 0 && rp < rewardPercentiles[i-1] {
return ethtypes.EthFeeHistory{}, fmt.Errorf("invalid reward percentile: %f should be larger than %f", rp, rewardPercentiles[i-1])
}
}
ts, err := a.parseBlkParam(ctx, params.NewestBlkNum)
if err != nil {
@ -619,18 +632,40 @@ func (a *EthModule) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (eth
// we can do is duplicate the last value.
baseFeeArray := []ethtypes.EthBigInt{ethtypes.EthBigInt(ts.Blocks()[0].ParentBaseFee)}
gasUsedRatioArray := []float64{}
rewardsArray := make([][]ethtypes.EthBigInt, 0)
for ts.Height() >= abi.ChainEpoch(oldestBlkHeight) {
// Unfortunately we need to rebuild the full message view so we can
// totalize gas used in the tipset.
block, err := newEthBlockFromFilecoinTipSet(ctx, ts, false, a.Chain, a.StateAPI)
msgs, err := a.Chain.MessagesForTipset(ctx, ts)
if err != nil {
return ethtypes.EthFeeHistory{}, fmt.Errorf("cannot create eth block: %v", err)
return ethtypes.EthFeeHistory{}, xerrors.Errorf("error loading messages for tipset: %v: %w", ts, err)
}
// both arrays should be reversed at the end
txGasRewards := gasRewardSorter{}
for txIdx, msg := range msgs {
msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, msg.Cid(), api.LookbackNoLimit, false)
if err != nil || msgLookup == nil {
return ethtypes.EthFeeHistory{}, nil
}
tx, err := newEthTxFromMessageLookup(ctx, msgLookup, txIdx, a.Chain, a.StateAPI)
if err != nil {
return ethtypes.EthFeeHistory{}, nil
}
txGasRewards = append(txGasRewards, gasRewardTuple{
reward: tx.Reward(ts.Blocks()[0].ParentBaseFee),
gas: uint64(msgLookup.Receipt.GasUsed),
})
}
rewards, totalGasUsed := calculateRewardsAndGasUsed(rewardPercentiles, txGasRewards)
// arrays should be reversed at the end
baseFeeArray = append(baseFeeArray, ethtypes.EthBigInt(ts.Blocks()[0].ParentBaseFee))
gasUsedRatioArray = append(gasUsedRatioArray, float64(block.GasUsed)/float64(build.BlockGasLimit))
gasUsedRatioArray = append(gasUsedRatioArray, float64(totalGasUsed)/float64(build.BlockGasLimit))
rewardsArray = append(rewardsArray, rewards)
parentTsKey := ts.Parents()
ts, err = a.Chain.LoadTipSet(ctx, parentTsKey)
@ -646,6 +681,9 @@ func (a *EthModule) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (eth
for i, j := 0, len(gasUsedRatioArray)-1; i < j; i, j = i+1, j-1 {
gasUsedRatioArray[i], gasUsedRatioArray[j] = gasUsedRatioArray[j], gasUsedRatioArray[i]
}
for i, j := 0, len(rewardsArray)-1; i < j; i, j = i+1, j-1 {
rewardsArray[i], rewardsArray[j] = rewardsArray[j], rewardsArray[i]
}
ret := ethtypes.EthFeeHistory{
OldestBlock: ethtypes.EthUint64(oldestBlkHeight),
@ -653,13 +691,7 @@ func (a *EthModule) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (eth
GasUsedRatio: gasUsedRatioArray,
}
if params.RewardPercentiles != nil {
// TODO: Populate reward percentiles
// https://github.com/filecoin-project/lotus/issues/10236
// We need to calculate the requested percentiles of effective gas premium
// based on the newest block (I presume it's the newest, we need to dig in
// as it's underspecified). Effective means we're clamped at the gas_fee_cap - base_fee.
reward := make([][]ethtypes.EthBigInt, 0)
ret.Reward = &reward
ret.Reward = &rewardsArray
}
return ret, nil
}
@ -751,18 +783,20 @@ func (a *EthModule) ethCallToFilecoinMessage(ctx context.Context, tx ethtypes.Et
}
var params []byte
if len(tx.Data) > 0 {
initcode := abi.CborBytes(tx.Data)
params2, err := actors.SerializeParams(&initcode)
if err != nil {
return nil, fmt.Errorf("failed to serialize params: %w", err)
}
params = params2
}
var to address.Address
var method abi.MethodNum
if tx.To == nil {
// this is a contract creation
to = builtintypes.EthereumAddressManagerActorAddr
initcode := abi.CborBytes(tx.Data)
params2, err := actors.SerializeParams(&initcode)
if err != nil {
return nil, fmt.Errorf("failed to serialize Create params: %w", err)
}
params = params2
method = builtintypes.MethodsEAM.CreateExternal
} else {
addr, err := tx.To.ToFilecoinAddress()
@ -770,15 +804,6 @@ func (a *EthModule) ethCallToFilecoinMessage(ctx context.Context, tx ethtypes.Et
return nil, xerrors.Errorf("cannot get Filecoin address: %w", err)
}
to = addr
if len(tx.Data) > 0 {
var buf bytes.Buffer
if err := cbg.WriteByteArray(&buf, tx.Data); err != nil {
return nil, fmt.Errorf("failed to encode tx input into a cbor byte-string")
}
params = buf.Bytes()
}
method = builtintypes.MethodsEVM.InvokeContract
}
@ -1246,8 +1271,9 @@ func (e *EthEvent) uninstallFilter(ctx context.Context, f filter.Filter) error {
}
const (
EthSubscribeEventTypeHeads = "newHeads"
EthSubscribeEventTypeLogs = "logs"
EthSubscribeEventTypeHeads = "newHeads"
EthSubscribeEventTypeLogs = "logs"
EthSubscribeEventTypePendingTransactions = "newPendingTransactions"
)
func (e *EthEvent) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
@ -1309,6 +1335,15 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethty
_, _ = e.EthUnsubscribe(ctx, sub.id)
return ethtypes.EthSubscriptionID{}, err
}
sub.addFilter(ctx, f)
case EthSubscribeEventTypePendingTransactions:
f, err := e.MemPoolFilterManager.Install(ctx)
if err != nil {
// clean up any previous filters added and stop the sub
_, _ = e.EthUnsubscribe(ctx, sub.id)
return ethtypes.EthSubscriptionID{}, err
}
sub.addFilter(ctx, f)
default:
return ethtypes.EthSubscriptionID{}, xerrors.Errorf("unsupported event type: %s", params.EventType)
@ -1629,6 +1664,15 @@ func (e *ethSubscription) start(ctx context.Context) {
}
e.send(ctx, ev)
case *types.SignedMessage: // mpool txid
evs, err := ethFilterResultFromMessages([]*types.SignedMessage{vt}, e.StateAPI)
if err != nil {
continue
}
for _, r := range evs.Results {
e.send(ctx, r)
}
default:
log.Warnf("unexpected subscription value type: %T", vt)
}
@ -2135,3 +2179,50 @@ func parseEthTopics(topics ethtypes.EthTopicSpec) (map[string][][]byte, error) {
}
return keys, nil
}
func calculateRewardsAndGasUsed(rewardPercentiles []float64, txGasRewards gasRewardSorter) ([]ethtypes.EthBigInt, uint64) {
var totalGasUsed uint64
for _, tx := range txGasRewards {
totalGasUsed += tx.gas
}
rewards := make([]ethtypes.EthBigInt, len(rewardPercentiles))
for i := range rewards {
rewards[i] = ethtypes.EthBigIntZero
}
if len(txGasRewards) == 0 {
return rewards, totalGasUsed
}
sort.Stable(txGasRewards)
var idx int
var sum uint64
for i, percentile := range rewardPercentiles {
threshold := uint64(float64(totalGasUsed) * percentile / 100)
for sum < threshold && idx < len(txGasRewards)-1 {
sum += txGasRewards[idx].gas
idx++
}
rewards[i] = txGasRewards[idx].reward
}
return rewards, totalGasUsed
}
type gasRewardTuple struct {
gas uint64
reward ethtypes.EthBigInt
}
// sorted in ascending order
type gasRewardSorter []gasRewardTuple
func (g gasRewardSorter) Len() int { return len(g) }
func (g gasRewardSorter) Swap(i, j int) {
g[i], g[j] = g[j], g[i]
}
func (g gasRewardSorter) Less(i, j int) bool {
return g[i].reward.Int.Cmp(g[j].reward.Int) == -1
}

View File

@ -6,6 +6,8 @@ import (
"github.com/ipfs/go-cid"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
)
@ -100,3 +102,65 @@ func TestEthLogFromEvent(t *testing.T) {
require.Len(t, topics, 1)
require.Equal(t, topics[0], ethtypes.EthHash{})
}
func TestReward(t *testing.T) {
baseFee := big.NewInt(100)
testcases := []struct {
maxFeePerGas, maxPriorityFeePerGas big.Int
answer big.Int
}{
{maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(200), answer: big.NewInt(200)},
{maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(300), answer: big.NewInt(300)},
{maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(500), answer: big.NewInt(500)},
{maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(600), answer: big.NewInt(500)},
{maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(1000), answer: big.NewInt(500)},
{maxFeePerGas: big.NewInt(50), maxPriorityFeePerGas: big.NewInt(200), answer: big.NewInt(-50)},
}
for _, tc := range testcases {
tx := ethtypes.EthTx{
MaxFeePerGas: ethtypes.EthBigInt(tc.maxFeePerGas),
MaxPriorityFeePerGas: ethtypes.EthBigInt(tc.maxPriorityFeePerGas),
}
reward := tx.Reward(baseFee)
require.Equal(t, 0, reward.Int.Cmp(tc.answer.Int), reward, tc.answer)
}
}
func TestRewardPercentiles(t *testing.T) {
testcases := []struct {
percentiles []float64
txGasRewards gasRewardSorter
answer []int64
}{
{
percentiles: []float64{25, 50, 75},
txGasRewards: []gasRewardTuple{},
answer: []int64{0, 0, 0},
},
{
percentiles: []float64{25, 50, 75, 100},
txGasRewards: []gasRewardTuple{
{gas: uint64(0), reward: ethtypes.EthBigInt(big.NewInt(300))},
{gas: uint64(100), reward: ethtypes.EthBigInt(big.NewInt(200))},
{gas: uint64(350), reward: ethtypes.EthBigInt(big.NewInt(100))},
{gas: uint64(500), reward: ethtypes.EthBigInt(big.NewInt(600))},
{gas: uint64(300), reward: ethtypes.EthBigInt(big.NewInt(700))},
},
answer: []int64{200, 700, 700, 700},
},
}
for _, tc := range testcases {
rewards, totalGasUsed := calculateRewardsAndGasUsed(tc.percentiles, tc.txGasRewards)
gasUsed := uint64(0)
for _, tx := range tc.txGasRewards {
gasUsed += tx.gas
}
ans := []ethtypes.EthBigInt{}
for _, bi := range tc.answer {
ans = append(ans, ethtypes.EthBigInt(big.NewInt(bi)))
}
require.Equal(t, totalGasUsed, gasUsed)
require.Equal(t, len(ans), len(tc.percentiles))
require.Equal(t, ans, rewards)
}
}