Commit c47fe5d9 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into optional-governance

parents 8f7ac67a 34fae45a
......@@ -30,8 +30,8 @@ var (
// DisputeGameFactoryMetaData contains all meta data concerning the DisputeGameFactory contract.
var DisputeGameFactoryMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"Hash\",\"name\":\"uuid\",\"type\":\"bytes32\"}],\"name\":\"GameAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"enumGameType\",\"name\":\"gameType\",\"type\":\"uint8\"}],\"name\":\"NoImplementation\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"disputeProxy\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"enumGameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"indexed\":true,\"internalType\":\"Claim\",\"name\":\"rootClaim\",\"type\":\"bytes32\"}],\"name\":\"DisputeGameCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"impl\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"enumGameType\",\"name\":\"gameType\",\"type\":\"uint8\"}],\"name\":\"ImplementationSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"enumGameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"internalType\":\"Claim\",\"name\":\"rootClaim\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"create\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"proxy\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enumGameType\",\"name\":\"\",\"type\":\"uint8\"}],\"name\":\"gameImpls\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enumGameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"internalType\":\"Claim\",\"name\":\"rootClaim\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"games\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"_proxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enumGameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"internalType\":\"Claim\",\"name\":\"rootClaim\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"getGameUUID\",\"outputs\":[{\"internalType\":\"Hash\",\"name\":\"_uuid\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enumGameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"internalType\":\"contractIDisputeGame\",\"name\":\"impl\",\"type\":\"address\"}],\"name\":\"setImplementation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
Bin: "0x608060405234801561001057600080fd5b50604051610d8b380380610d8b83398101604081905261002f91610171565b61003833610047565b61004181610097565b506101a1565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61009f610115565b6001600160a01b0381166101095760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61011281610047565b50565b6000546001600160a01b0316331461016f5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610100565b565b60006020828403121561018357600080fd5b81516001600160a01b038116811461019a57600080fd5b9392505050565b610bdb806101b06000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80638da5cb5b1161005b5780638da5cb5b14610194578063c49d5271146101b2578063dfa162d3146101c5578063f2fde38b146101fb57600080fd5b806326daafbe1461008d5780633142e55e1461013f57806345583b7a14610177578063715018a61461018c575b600080fd5b61012c61009b36600461093c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0810180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0830180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08086018051988652968352606087529451609f0190941683209190925291905291905290565b6040519081526020015b60405180910390f35b61015261014d366004610a25565b61020e565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610136565b61018a610185366004610ace565b6104ba565b005b61018a61058d565b60005473ffffffffffffffffffffffffffffffffffffffff16610152565b6101526101c0366004610a25565b6105a1565b6101526101d3366004610b05565b60016020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b61018a610209366004610b27565b610618565b6000806001600087600281111561022757610227610b44565b600281111561023857610238610b44565b815260208101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff169050806102a357856040517f44265d6f00000000000000000000000000000000000000000000000000000000815260040161029a9190610b73565b60405180910390fd5b6103068585856040516020016102bb93929190610bb4565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905273ffffffffffffffffffffffffffffffffffffffff8316906106cf565b91508173ffffffffffffffffffffffffffffffffffffffff16638129fc1c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561035057600080fd5b505af1158015610364573d6000803e3d6000fd5b5050505060006103ab878787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061009b92505050565b60008181526002602052604090205490915073ffffffffffffffffffffffffffffffffffffffff161561040d576040517f014f6fe50000000000000000000000000000000000000000000000000000000081526004810182905260240161029a565b600081815260026020819052604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff86161790558690889081111561046f5761046f610b44565b60405173ffffffffffffffffffffffffffffffffffffffff8616907ffad0599ff449d8d9685eadecca8cb9e00924c5fd8367c1c09469824939e1ffec90600090a45050949350505050565b6104c2610803565b80600160008460028111156104d9576104d9610b44565b60028111156104ea576104ea610b44565b815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600281111561054857610548610b44565b60405173ffffffffffffffffffffffffffffffffffffffff8316907f623713f72f6e427a8044bb8b3bd6834357cf285decbaa21bcc73c1d0632c4d8490600090a35050565b610595610803565b61059f6000610884565b565b6000600260006105e8878787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061009b92505050565b815260208101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff1695945050505050565b610620610803565b73ffffffffffffffffffffffffffffffffffffffff81166106c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161029a565b6106cc81610884565b50565b60006002825101603f8101600a81036040518360581b8260e81b177f6100003d81600a3d39f3363d3d373d3d3d3d610000806035363936013d7300001781528660601b601e8201527f5af43d3d93803e603357fd5bf300000000000000000000000000000000000000603282015285519150603f8101602087015b6020841061078757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909301926020918201910161074a565b517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602085900360031b1b16815260f085901b9083015282816000f09450846107f4577febfef1880000000000000000000000000000000000000000000000000000000060005260206000fd5b90910160405250909392505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461059f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161029a565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80356003811061090857600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561095157600080fd5b61095a846108f9565b925060208401359150604084013567ffffffffffffffff8082111561097e57600080fd5b818601915086601f83011261099257600080fd5b8135818111156109a4576109a461090d565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156109ea576109ea61090d565b81604052828152896020848701011115610a0357600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60008060008060608587031215610a3b57600080fd5b610a44856108f9565b935060208501359250604085013567ffffffffffffffff80821115610a6857600080fd5b818701915087601f830112610a7c57600080fd5b813581811115610a8b57600080fd5b886020828501011115610a9d57600080fd5b95989497505060200194505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106cc57600080fd5b60008060408385031215610ae157600080fd5b610aea836108f9565b91506020830135610afa81610aac565b809150509250929050565b600060208284031215610b1757600080fd5b610b20826108f9565b9392505050565b600060208284031215610b3957600080fd5b8135610b2081610aac565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6020810160038310610bae577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91905290565b83815281836020830137600091016020019081529291505056fea164736f6c634300080f000a",
ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"Hash\",\"name\":\"uuid\",\"type\":\"bytes32\"}],\"name\":\"GameAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"}],\"name\":\"NoImplementation\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"disputeProxy\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"indexed\":true,\"internalType\":\"Claim\",\"name\":\"rootClaim\",\"type\":\"bytes32\"}],\"name\":\"DisputeGameCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"impl\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"}],\"name\":\"ImplementationSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"internalType\":\"Claim\",\"name\":\"rootClaim\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"create\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"proxy\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"disputeGameList\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gameCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"_gameCount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"\",\"type\":\"uint8\"}],\"name\":\"gameImpls\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"internalType\":\"Claim\",\"name\":\"rootClaim\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"games\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"_proxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"internalType\":\"Claim\",\"name\":\"rootClaim\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"getGameUUID\",\"outputs\":[{\"internalType\":\"Hash\",\"name\":\"_uuid\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"internalType\":\"contractIDisputeGame\",\"name\":\"impl\",\"type\":\"address\"}],\"name\":\"setImplementation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
Bin: "0x608060405234801561001057600080fd5b50604051610d5b380380610d5b83398101604081905261002f91610171565b61003833610047565b61004181610097565b506101a1565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61009f610115565b6001600160a01b0381166101095760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61011281610047565b50565b6000546001600160a01b0316331461016f5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610100565b565b60006020828403121561018357600080fd5b81516001600160a01b038116811461019a57600080fd5b9392505050565b610bab806101b06000396000f3fe608060405234801561001057600080fd5b50600436106100be5760003560e01c8063763014a611610076578063c49d52711161005b578063c49d527114610203578063dfa162d314610216578063f2fde38b1461024c57600080fd5b8063763014a6146101d25780638da5cb5b146101e557600080fd5b806345583b7a116100a757806345583b7a146101ad5780634d1975b4146101c2578063715018a6146101ca57600080fd5b806326daafbe146100c35780633142e55e14610175575b600080fd5b6101626100d1366004610963565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0810180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0830180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08086018051988652968352606087529451609f0190941683209190925291905291905290565b6040519081526020015b60405180910390f35b610188610183366004610a4c565b61025f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161016c565b6101c06101bb366004610af5565b6104f4565b005b600354610162565b6101c061057b565b6101886101e0366004610b2c565b61058f565b60005473ffffffffffffffffffffffffffffffffffffffff16610188565b610188610211366004610a4c565b6105c6565b610188610224366004610b45565b60016020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6101c061025a366004610b67565b61063d565b60ff841660009081526001602052604081205473ffffffffffffffffffffffffffffffffffffffff16806102c9576040517f44265d6f00000000000000000000000000000000000000000000000000000000815260ff871660048201526024015b60405180910390fd5b61032c8585856040516020016102e193929190610b84565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905273ffffffffffffffffffffffffffffffffffffffff8316906106f4565b91508173ffffffffffffffffffffffffffffffffffffffff16638129fc1c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561037657600080fd5b505af115801561038a573d6000803e3d6000fd5b5050505060006103d1878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506100d192505050565b60008181526002602052604090205490915073ffffffffffffffffffffffffffffffffffffffff1615610433576040517f014f6fe5000000000000000000000000000000000000000000000000000000008152600481018290526024016102c0565b600081815260026020526040808220805473ffffffffffffffffffffffffffffffffffffffff87167fffffffffffffffffffffffff00000000000000000000000000000000000000009182168117909255600380546001810182559085527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b018054909116821790559051889260ff8b1692917ffad0599ff449d8d9685eadecca8cb9e00924c5fd8367c1c09469824939e1ffec9190a45050949350505050565b6104fc610828565b60ff821660008181526001602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616908117909155905190917f623713f72f6e427a8044bb8b3bd6834357cf285decbaa21bcc73c1d0632c4d8491a35050565b610583610828565b61058d60006108a9565b565b6003818154811061059f57600080fd5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b60006002600061060d878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506100d192505050565b815260208101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff1695945050505050565b610645610828565b73ffffffffffffffffffffffffffffffffffffffff81166106e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016102c0565b6106f1816108a9565b50565b60006002825101603f8101600a81036040518360581b8260e81b177f6100003d81600a3d39f3363d3d373d3d3d3d610000806035363936013d7300001781528660601b601e8201527f5af43d3d93803e603357fd5bf300000000000000000000000000000000000000603282015285519150603f8101602087015b602084106107ac57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909301926020918201910161076f565b517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602085900360031b1b16815260f085901b9083015282816000f0945084610819577febfef1880000000000000000000000000000000000000000000000000000000060005260206000fd5b90910160405250909392505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461058d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016102c0565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b803560ff8116811461092f57600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561097857600080fd5b6109818461091e565b925060208401359150604084013567ffffffffffffffff808211156109a557600080fd5b818601915086601f8301126109b957600080fd5b8135818111156109cb576109cb610934565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715610a1157610a11610934565b81604052828152896020848701011115610a2a57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60008060008060608587031215610a6257600080fd5b610a6b8561091e565b935060208501359250604085013567ffffffffffffffff80821115610a8f57600080fd5b818701915087601f830112610aa357600080fd5b813581811115610ab257600080fd5b886020828501011115610ac457600080fd5b95989497505060200194505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106f157600080fd5b60008060408385031215610b0857600080fd5b610b118361091e565b91506020830135610b2181610ad3565b809150509250929050565b600060208284031215610b3e57600080fd5b5035919050565b600060208284031215610b5757600080fd5b610b608261091e565b9392505050565b600060208284031215610b7957600080fd5b8135610b6081610ad3565b83815281836020830137600091016020019081529291505056fea164736f6c634300080f000a",
}
// DisputeGameFactoryABI is the input ABI used to generate the binding from.
......@@ -201,6 +201,68 @@ func (_DisputeGameFactory *DisputeGameFactoryTransactorRaw) Transact(opts *bind.
return _DisputeGameFactory.Contract.contract.Transact(opts, method, params...)
}
// DisputeGameList is a free data retrieval call binding the contract method 0x763014a6.
//
// Solidity: function disputeGameList(uint256 ) view returns(address)
func (_DisputeGameFactory *DisputeGameFactoryCaller) DisputeGameList(opts *bind.CallOpts, arg0 *big.Int) (common.Address, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "disputeGameList", arg0)
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// DisputeGameList is a free data retrieval call binding the contract method 0x763014a6.
//
// Solidity: function disputeGameList(uint256 ) view returns(address)
func (_DisputeGameFactory *DisputeGameFactorySession) DisputeGameList(arg0 *big.Int) (common.Address, error) {
return _DisputeGameFactory.Contract.DisputeGameList(&_DisputeGameFactory.CallOpts, arg0)
}
// DisputeGameList is a free data retrieval call binding the contract method 0x763014a6.
//
// Solidity: function disputeGameList(uint256 ) view returns(address)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) DisputeGameList(arg0 *big.Int) (common.Address, error) {
return _DisputeGameFactory.Contract.DisputeGameList(&_DisputeGameFactory.CallOpts, arg0)
}
// GameCount is a free data retrieval call binding the contract method 0x4d1975b4.
//
// Solidity: function gameCount() view returns(uint256 _gameCount)
func (_DisputeGameFactory *DisputeGameFactoryCaller) GameCount(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "gameCount")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// GameCount is a free data retrieval call binding the contract method 0x4d1975b4.
//
// Solidity: function gameCount() view returns(uint256 _gameCount)
func (_DisputeGameFactory *DisputeGameFactorySession) GameCount() (*big.Int, error) {
return _DisputeGameFactory.Contract.GameCount(&_DisputeGameFactory.CallOpts)
}
// GameCount is a free data retrieval call binding the contract method 0x4d1975b4.
//
// Solidity: function gameCount() view returns(uint256 _gameCount)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) GameCount() (*big.Int, error) {
return _DisputeGameFactory.Contract.GameCount(&_DisputeGameFactory.CallOpts)
}
// GameImpls is a free data retrieval call binding the contract method 0xdfa162d3.
//
// Solidity: function gameImpls(uint8 ) view returns(address)
......
......@@ -2,15 +2,20 @@ package bindings
import (
"fmt"
"strings"
"github.com/ethereum-optimism/optimism/op-bindings/solc"
"github.com/ethereum/go-ethereum/common"
)
// layouts respresents the set of storage layouts. It is populated in an init function.
var layouts = make(map[string]*solc.StorageLayout)
// deployedBytecodes represents the set of deployed bytecodes. It is populated
// in an init function.
var deployedBytecodes = make(map[string]string)
// GetStorageLayout returns the storage layout of a contract by name.
func GetStorageLayout(name string) (*solc.StorageLayout, error) {
layout := layouts[name]
if layout == nil {
......@@ -19,11 +24,36 @@ func GetStorageLayout(name string) (*solc.StorageLayout, error) {
return layout, nil
}
// GetDeployedBytecode returns the deployed bytecode of a contract by name.
func GetDeployedBytecode(name string) ([]byte, error) {
bc := deployedBytecodes[name]
if bc == "" {
return nil, fmt.Errorf("%s: deployed bytecode not found", name)
}
if !isHex(bc) {
return nil, fmt.Errorf("%s: invalid deployed bytecode", name)
}
return common.FromHex(bc), nil
}
// isHexCharacter returns bool of c being a valid hexadecimal.
func isHexCharacter(c byte) bool {
return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
}
// isHex validates whether each byte is valid hexadecimal string.
func isHex(str string) bool {
if len(str)%2 != 0 {
return false
}
str = strings.TrimPrefix(str, "0x")
for _, c := range []byte(str) {
if !isHexCharacter(c) {
return false
}
}
return true
}
......@@ -816,7 +816,7 @@ func TestSystemDenseTopology(t *testing.T) {
params, err := p2p.GetPeerScoreParams("light", 2)
require.NoError(t, err)
node.P2P = &p2p.Config{
PeerScoring: params,
PeerScoring: &params,
BanningEnabled: false,
}
}
......
......@@ -98,7 +98,7 @@ func loadTopicScoringParams(conf *p2p.Config, ctx *cli.Context, blockTime uint64
if err != nil {
return err
}
conf.TopicScoring = topicScoreParams
conf.TopicScoring = &topicScoreParams
}
return nil
......@@ -114,7 +114,7 @@ func loadPeerScoringParams(conf *p2p.Config, ctx *cli.Context, blockTime uint64)
if err != nil {
return err
}
conf.PeerScoring = peerScoreParams
conf.PeerScoring = &peerScoreParams
}
return nil
......
......@@ -63,8 +63,8 @@ type Config struct {
AltSync bool
// Pubsub Scoring Parameters
PeerScoring pubsub.PeerScoreParams
TopicScoring pubsub.TopicScoreParams
PeerScoring *pubsub.PeerScoreParams
TopicScoring *pubsub.TopicScoreParams
// Whether to ban peers based on their [PeerScoring] score. Should be negative.
BanningEnabled bool
......@@ -135,7 +135,7 @@ func (conf *Config) Disabled() bool {
}
func (conf *Config) PeerScoringParams() *pubsub.PeerScoreParams {
return &conf.PeerScoring
return conf.PeerScoring
}
func (conf *Config) BanPeers() bool {
......@@ -151,7 +151,7 @@ func (conf *Config) BanDuration() time.Duration {
}
func (conf *Config) TopicScoringParams() *pubsub.TopicScoreParams {
return &conf.TopicScoring
return conf.TopicScoring
}
func (conf *Config) ReqRespSyncEnabled() bool {
......
......@@ -443,10 +443,7 @@ func JoinGossip(p2pCtx context.Context, self peer.ID, topicScoreParams *pubsub.T
}
go LogTopicEvents(p2pCtx, log.New("topic", "blocks"), blocksTopicEvents)
// A [TimeInMeshQuantum] value of 0 means the topic score is disabled.
// If we passed a topicScoreParams with [TimeInMeshQuantum] set to 0,
// libp2p errors since the params will be rejected.
if topicScoreParams != nil && topicScoreParams.TimeInMeshQuantum != 0 {
if topicScoreParams != nil {
if err = blocksTopic.SetScoreParams(topicScoreParams); err != nil {
return nil, fmt.Errorf("failed to set topic score params: %w", err)
}
......
......@@ -146,7 +146,15 @@ func (conf *Config) Host(log log.Logger, reporter metrics.Reporter, metrics Host
}
peerScoreParams := conf.PeerScoringParams()
ps, err := store.NewExtendedPeerstore(context.Background(), log, clock.SystemClock, basePs, conf.Store, peerScoreParams.RetainScore)
var scoreRetention time.Duration
if peerScoreParams != nil {
// Use the same retention period as gossip will if available
scoreRetention = peerScoreParams.RetainScore
} else {
// Disable score GC if peer scoring is disabled
scoreRetention = 0
}
ps, err := store.NewExtendedPeerstore(context.Background(), log, clock.SystemClock, basePs, conf.Store, scoreRetention)
if err != nil {
return nil, fmt.Errorf("failed to open extended peerstore: %w", err)
}
......
......@@ -12,14 +12,13 @@ func ConfigurePeerScoring(gossipConf GossipSetupConfigurables, scorer Scorer, lo
peerScoreParams := gossipConf.PeerScoringParams()
peerScoreThresholds := NewPeerScoreThresholds()
opts := []pubsub.Option{}
// Check the app specific score since libp2p doesn't export it's [validate] function :/
if peerScoreParams != nil && peerScoreParams.AppSpecificScore != nil {
if peerScoreParams != nil {
opts = []pubsub.Option{
pubsub.WithPeerScore(peerScoreParams, &peerScoreThresholds),
pubsub.WithPeerScoreInspect(scorer.SnapshotHook(), peerScoreInspectFrequency),
}
} else {
log.Warn("Proceeding with no peer scoring...\nMissing AppSpecificScore in peer scoring params")
log.Info("Peer scoring disabled")
}
return opts
}
......@@ -102,7 +102,7 @@ func newGossipSubs(testSuite *PeerScoresTestSuite, ctx context.Context, hosts []
&rollup.Config{L2ChainID: big.NewInt(123)},
extPeerStore, testSuite.mockMetricer, logger)
opts = append(opts, ConfigurePeerScoring(&Config{
PeerScoring: pubsub.PeerScoreParams{
PeerScoring: &pubsub.PeerScoreParams{
AppSpecificScore: func(p peer.ID) float64 {
if p == hosts[0].ID() {
return -1000
......
......@@ -142,7 +142,7 @@ func (cfg *Config) CheckL1ChainID(ctx context.Context, client L1Client) error {
return err
}
if cfg.L1ChainID.Cmp(id) != 0 {
return fmt.Errorf("incorrect L1 RPC chain id %d, expected %d", cfg.L1ChainID, id)
return fmt.Errorf("incorrect L1 RPC chain id %d, expected %d", id, cfg.L1ChainID)
}
return nil
}
......@@ -154,7 +154,7 @@ func (cfg *Config) CheckL1GenesisBlockHash(ctx context.Context, client L1Client)
return err
}
if l1GenesisBlockRef.Hash != cfg.Genesis.L1.Hash {
return fmt.Errorf("incorrect L1 genesis block hash %d, expected %d", cfg.Genesis.L1.Hash, l1GenesisBlockRef.Hash)
return fmt.Errorf("incorrect L1 genesis block hash %s, expected %s", l1GenesisBlockRef.Hash, cfg.Genesis.L1.Hash)
}
return nil
}
......@@ -171,7 +171,7 @@ func (cfg *Config) CheckL2ChainID(ctx context.Context, client L2Client) error {
return err
}
if cfg.L2ChainID.Cmp(id) != 0 {
return fmt.Errorf("incorrect L2 RPC chain id, expected from config %d, obtained from client %d", cfg.L2ChainID, id)
return fmt.Errorf("incorrect L2 RPC chain id %d, expected %d", id, cfg.L2ChainID)
}
return nil
}
......@@ -183,7 +183,7 @@ func (cfg *Config) CheckL2GenesisBlockHash(ctx context.Context, client L2Client)
return err
}
if l2GenesisBlockRef.Hash != cfg.Genesis.L2.Hash {
return fmt.Errorf("incorrect L2 genesis block hash %d, expected %d", cfg.Genesis.L2.Hash, l2GenesisBlockRef.Hash)
return fmt.Errorf("incorrect L2 genesis block hash %s, expected %s", l2GenesisBlockRef.Hash, cfg.Genesis.L2.Hash)
}
return nil
}
......
......@@ -27,11 +27,11 @@ CrossDomainOwnable_Test:test_onlyOwner_notOwner_reverts() (gas: 10597)
CrossDomainOwnable_Test:test_onlyOwner_succeeds() (gas: 34883)
DeployerWhitelist_Test:test_owner_succeeds() (gas: 7582)
DeployerWhitelist_Test:test_storageSlots_succeeds() (gas: 33395)
DisputeGameFactory_Test:test_owner_succeeds() (gas: 7582)
DisputeGameFactory_Test:test_setImplementation_notOwner_reverts() (gas: 11191)
DisputeGameFactory_Test:test_setImplementation_succeeds() (gas: 38765)
DisputeGameFactory_Test:test_owner_succeeds() (gas: 7627)
DisputeGameFactory_Test:test_setImplementation_notOwner_reverts() (gas: 11077)
DisputeGameFactory_Test:test_setImplementation_succeeds() (gas: 38320)
DisputeGameFactory_Test:test_transferOwnership_notOwner_reverts() (gas: 10979)
DisputeGameFactory_Test:test_transferOwnership_succeeds() (gas: 13180)
DisputeGameFactory_Test:test_transferOwnership_succeeds() (gas: 13225)
FeeVault_Test:test_constructor_succeeds() (gas: 18185)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 352135)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2950342)
......
......@@ -253,11 +253,12 @@
➡ contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory
=======================
| Name | Type | Slot | Offset | Bytes | Contract |
|--------------|-------------------------------------------------|------|--------|-------|-------------------------------------------------------------|
| _owner | address | 0 | 0 | 20 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| gameImpls | mapping(enum GameType => contract IDisputeGame) | 1 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| disputeGames | mapping(Hash => contract IDisputeGame) | 2 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| Name | Type | Slot | Offset | Bytes | Contract |
|-----------------|--------------------------------------------|------|--------|-------|-------------------------------------------------------------|
| _owner | address | 0 | 0 | 20 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| gameImpls | mapping(GameType => contract IDisputeGame) | 1 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| disputeGames | mapping(Hash => contract IDisputeGame) | 2 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| disputeGameList | contract IDisputeGame[] | 3 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
=======================
➡ contracts/dispute/BondManager.sol:BondManager
......
......@@ -83,7 +83,7 @@ contract BondManager is IBondManager {
IDisputeGame caller = IDisputeGame(msg.sender);
IDisputeGame game = DISPUTE_GAME_FACTORY.games(
GameType.ATTESTATION,
GameTypes.ATTESTATION,
caller.rootClaim(),
caller.extraData()
);
......@@ -108,7 +108,7 @@ contract BondManager is IBondManager {
IDisputeGame caller = IDisputeGame(msg.sender);
IDisputeGame game = DISPUTE_GAME_FACTORY.games(
GameType.ATTESTATION,
GameTypes.ATTESTATION,
caller.rootClaim(),
caller.extraData()
);
......
......@@ -32,6 +32,13 @@ contract DisputeGameFactory is Ownable, IDisputeGameFactory {
*/
mapping(Hash => IDisputeGame) internal disputeGames;
/**
* @notice An append-only array of disputeGames that have been created.
* @dev This accessor is used by offchain game solvers to efficiently
* track dispute games
*/
IDisputeGame[] public disputeGameList;
/**
* @notice Constructs a new DisputeGameFactory contract.
* @param _owner The owner of the contract.
......@@ -40,6 +47,13 @@ contract DisputeGameFactory is Ownable, IDisputeGameFactory {
transferOwnership(_owner);
}
/**
* @inheritdoc IDisputeGameFactory
*/
function gameCount() external view returns (uint256 _gameCount) {
_gameCount = disputeGameList.length;
}
/**
* @inheritdoc IDisputeGameFactory
*/
......@@ -81,6 +95,7 @@ contract DisputeGameFactory is Ownable, IDisputeGameFactory {
// Store the dispute game in the mapping & emit the `DisputeGameCreated` event.
disputeGames[uuid] = proxy;
disputeGameList.push(proxy);
emit DisputeGameCreated(address(proxy), gameType, rootClaim);
}
......
......@@ -29,6 +29,12 @@ interface IDisputeGameFactory {
*/
event ImplementationSet(address indexed impl, GameType indexed gameType);
/**
* @notice the total number of dispute games created by this factory.
* @return _gameCount The total number of dispute games created by this factory.
*/
function gameCount() external view returns (uint256 _gameCount);
/**
* @notice `games` queries an internal a mapping that maps the hash of
* `gameType ++ rootClaim ++ extraData` to the deployed `DisputeGame` clone.
......
......@@ -57,6 +57,11 @@ type Clock is uint256;
*/
type Position is uint256;
/**
* @notice A `GameType` represents the type of game being played.
*/
type GameType is uint8;
/**
* @notice The current status of the dispute game.
*/
......@@ -70,13 +75,22 @@ enum GameStatus {
}
/**
* @notice The type of proof system being used.
* @title GameTypes
* @notice A library that defines the IDs of games that can be played.
*/
enum GameType {
// The game will use a `IDisputeGame` implementation that utilizes fault proofs.
FAULT,
// The game will use a `IDisputeGame` implementation that utilizes validity proofs.
VALIDITY,
// The game will use a `IDisputeGame` implementation that utilizes attestation proofs.
ATTESTATION
library GameTypes {
/**
* @dev The game will use a `IDisputeGame` implementation that utilizes fault proofs.
*/
GameType internal constant FAULT = GameType.wrap(0);
/**
* @dev The game will use a `IDisputeGame` implementation that utilizes validity proofs.
*/
GameType internal constant VALIDITY = GameType.wrap(1);
/**
* @dev The game will use a `IDisputeGame` implementation that utilizes attestation proofs.
*/
GameType internal constant ATTESTATION = GameType.wrap(2);
}
......@@ -208,7 +208,7 @@ contract BondManager_Test is Test {
{
rootClaim = Claim.wrap(bytes32(""));
MockAttestationDisputeGame implementation = new MockAttestationDisputeGame();
GameType gt = GameType.ATTESTATION;
GameType gt = GameTypes.ATTESTATION;
factory.setImplementation(gt, IDisputeGame(address(implementation)));
vm.expectEmit(false, true, true, false);
emit DisputeGameCreated(address(0), gt, rootClaim);
......@@ -261,7 +261,7 @@ contract BondManager_Test is Test {
{
rootClaim = Claim.wrap(bytes32(""));
MockAttestationDisputeGame implementation = new MockAttestationDisputeGame();
GameType gt = GameType.ATTESTATION;
GameType gt = GameTypes.ATTESTATION;
factory.setImplementation(gt, IDisputeGame(address(implementation)));
vm.expectEmit(false, true, true, false);
emit DisputeGameCreated(address(0), gt, rootClaim);
......@@ -418,7 +418,7 @@ contract MockAttestationDisputeGame {
}
function gameType() external pure returns (GameType _gameType) {
return GameType.ATTESTATION;
return GameTypes.ATTESTATION;
}
function rootClaim() external view returns (Claim _rootClaim) {
......
......@@ -35,11 +35,11 @@ contract DisputeGameFactory_Test is Test {
bytes calldata extraData
) public {
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType(uint8(bound(gameType, 0, 2)));
GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
// Set all three implementations to the same `FakeClone` contract.
for (uint8 i; i < 3; i++) {
factory.setImplementation(GameType(i), IDisputeGame(address(fakeClone)));
factory.setImplementation(GameType.wrap(i), IDisputeGame(address(fakeClone)));
}
vm.expectEmit(false, true, true, false);
......@@ -48,6 +48,8 @@ contract DisputeGameFactory_Test is Test {
// Ensure that the dispute game was assigned to the `disputeGames` mapping.
assertEq(address(factory.games(gt, rootClaim, extraData)), address(proxy));
assertEq(factory.gameCount(), 1);
assertEq(address(factory.disputeGameList(0)), address(proxy));
}
/**
......@@ -60,7 +62,7 @@ contract DisputeGameFactory_Test is Test {
bytes calldata extraData
) public {
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType(uint8(bound(gameType, 0, 2)));
GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
vm.expectRevert(abi.encodeWithSelector(NoImplementation.selector, gt));
factory.create(gt, rootClaim, extraData);
......@@ -75,11 +77,11 @@ contract DisputeGameFactory_Test is Test {
bytes calldata extraData
) public {
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType(uint8(bound(gameType, 0, 2)));
GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
// Set all three implementations to the same `FakeClone` contract.
for (uint8 i; i < 3; i++) {
factory.setImplementation(GameType(i), IDisputeGame(address(fakeClone)));
factory.setImplementation(GameType.wrap(i), IDisputeGame(address(fakeClone)));
}
// Create our first dispute game - this should succeed.
......@@ -104,17 +106,17 @@ contract DisputeGameFactory_Test is Test {
* @dev Tests that the `setImplementation` function properly sets the implementation for a given `GameType`.
*/
function test_setImplementation_succeeds() public {
// There should be no implementation for the `GameType.FAULT` enum value, it has not been set.
assertEq(address(factory.gameImpls(GameType.FAULT)), address(0));
// There should be no implementation for the `GameTypes.FAULT` enum value, it has not been set.
assertEq(address(factory.gameImpls(GameTypes.FAULT)), address(0));
vm.expectEmit(true, true, true, true, address(factory));
emit ImplementationSet(address(1), GameType.FAULT);
emit ImplementationSet(address(1), GameTypes.FAULT);
// Set the implementation for the `GameType.FAULT` enum value.
factory.setImplementation(GameType.FAULT, IDisputeGame(address(1)));
// Set the implementation for the `GameTypes.FAULT` enum value.
factory.setImplementation(GameTypes.FAULT, IDisputeGame(address(1)));
// Ensure that the implementation for the `GameType.FAULT` enum value is set.
assertEq(address(factory.gameImpls(GameType.FAULT)), address(1));
// Ensure that the implementation for the `GameTypes.FAULT` enum value is set.
assertEq(address(factory.gameImpls(GameTypes.FAULT)), address(1));
}
/**
......@@ -124,7 +126,7 @@ contract DisputeGameFactory_Test is Test {
// Ensure that the `setImplementation` function reverts when called by a non-owner.
vm.prank(address(0));
vm.expectRevert("Ownable: caller is not the owner");
factory.setImplementation(GameType.FAULT, IDisputeGame(address(1)));
factory.setImplementation(GameTypes.FAULT, IDisputeGame(address(1)));
}
/**
......@@ -137,7 +139,7 @@ contract DisputeGameFactory_Test is Test {
bytes calldata extraData
) public {
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType(uint8(bound(gameType, 0, 2)));
GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
assertEq(
Hash.unwrap(factory.getGameUUID(gt, rootClaim, extraData)),
......
......@@ -89,6 +89,52 @@ Cache use Redis and can be enabled for the following immutable methods:
* `eth_getUncleByBlockHashAndIndex`
* `debug_getRawReceipts` (block hash only)
## Meta method `consensus_getReceipts`
To support backends with different specifications in the same backend group,
proxyd exposes a convenient method to fetch receipts abstracting away
what specific backend will serve the request.
Each backend specifies their preferred method to fetch receipts with `consensus_receipts_target` config,
which will be translated from `consensus_getReceipts`.
This method takes a `blockNumberOrHash` (i.e. `tag|qty|hash`)
and returns the receipts for all transactions in the block.
Request example
```json
{
"jsonrpc":"2.0",
"id": 1,
"params": ["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"]
}
```
It currently supports translation to the following targets:
* `debug_getRawReceipts(blockOrHash)` (default)
* `alchemy_getTransactionReceipts(blockOrHash)`
* `parity_getBlockReceipts(blockOrHash)`
* `eth_getBlockReceipts(blockOrHash)`
The selected target is returned in the response, in a wrapped result.
Response example
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"method": "debug_getRawReceipts",
"result": {
// the actual raw result from backend
}
}
}
```
See [op-node receipt fetcher](https://github.com/ethereum-optimism/optimism/blob/186e46a47647a51a658e699e9ff047d39444c2de/op-node/sources/receipts.go#L186-L253).
## Metrics
See `metrics.go` for a list of all available metrics.
......
......@@ -18,6 +18,8 @@ import (
"time"
sw "github.com/ethereum-optimism/optimism/proxyd/pkg/avg-sliding-window"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/log"
"github.com/gorilla/websocket"
......@@ -97,6 +99,9 @@ var (
}
ErrBackendUnexpectedJSONRPC = errors.New("backend returned an unexpected JSON-RPC response")
ErrConsensusGetReceiptsCantBeBatched = errors.New("consensus_getReceipts cannot be batched")
ErrConsensusGetReceiptsInvalidTarget = errors.New("unsupported consensus_receipts_target")
)
func ErrInvalidRequest(msg string) *RPCErr {
......@@ -118,6 +123,7 @@ func ErrInvalidParams(msg string) *RPCErr {
type Backend struct {
Name string
rpcURL string
receiptsTarget string
wsURL string
authUsername string
authPassword string
......@@ -208,7 +214,7 @@ func WithProxydIP(ip string) BackendOpt {
}
}
func WithSkipPeerCountCheck(skipPeerCountCheck bool) BackendOpt {
func WithConsensusSkipPeerCountCheck(skipPeerCountCheck bool) BackendOpt {
return func(b *Backend) {
b.skipPeerCountCheck = skipPeerCountCheck
}
......@@ -232,12 +238,36 @@ func WithMaxErrorRateThreshold(maxErrorRateThreshold float64) BackendOpt {
}
}
func WithConsensusReceiptTarget(receiptsTarget string) BackendOpt {
return func(b *Backend) {
b.receiptsTarget = receiptsTarget
}
}
type indexedReqRes struct {
index int
req *RPCReq
res *RPCRes
}
const ConsensusGetReceiptsMethod = "consensus_getReceipts"
const ReceiptsTargetDebugGetRawReceipts = "debug_getRawReceipts"
const ReceiptsTargetAlchemyGetTransactionReceipts = "alchemy_getTransactionReceipts"
const ReceiptsTargetParityGetTransactionReceipts = "parity_getBlockReceipts"
const ReceiptsTargetEthGetTransactionReceipts = "eth_getBlockReceipts"
type ConsensusGetReceiptsResult struct {
Method string `json:"method"`
Result interface{} `json:"result"`
}
// BlockHashOrNumberParameter is a non-conventional wrapper used by alchemy_getTransactionReceipts
type BlockHashOrNumberParameter struct {
BlockHash *common.Hash `json:"blockHash"`
BlockNumber *rpc.BlockNumber `json:"blockNumber"`
}
func NewBackend(
name string,
rpcURL string,
......@@ -266,9 +296,7 @@ func NewBackend(
networkErrorsSlidingWindow: sw.NewSlidingWindow(),
}
for _, opt := range opts {
opt(backend)
}
backend.Override(opts...)
if !backend.stripTrailingXFF && backend.proxydIP == "" {
log.Warn("proxied requests' XFF header will not contain the proxyd ip address")
......@@ -277,6 +305,12 @@ func NewBackend(
return backend
}
func (b *Backend) Override(opts ...BackendOpt) {
for _, opt := range opts {
opt(b)
}
}
func (b *Backend) Forward(ctx context.Context, reqs []*RPCReq, isBatch bool) ([]*RPCRes, error) {
var lastError error
// <= to account for the first attempt not technically being
......@@ -298,6 +332,20 @@ func (b *Backend) Forward(ctx context.Context, reqs []*RPCReq, isBatch bool) ([]
res, err := b.doForward(ctx, reqs, isBatch)
switch err {
case nil: // do nothing
case ErrConsensusGetReceiptsCantBeBatched:
log.Warn(
"Received unsupported batch request for consensus_getReceipts",
"name", b.Name,
"req_id", GetReqID(ctx),
"err", err,
)
case ErrConsensusGetReceiptsInvalidTarget:
log.Error(
"Unsupported consensus_receipts_target for consensus_getReceipts",
"name", b.Name,
"req_id", GetReqID(ctx),
"err", err,
)
// ErrBackendUnexpectedJSONRPC occurs because infura responds with a single JSON-RPC object
// to a batch request whenever any Request Object in the batch would induce a partial error.
// We don't label the backend offline in this case. But the error is still returned to
......@@ -375,11 +423,63 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool
// we are concerned about network error rates, so we record 1 request independently of how many are in the batch
b.networkRequestsSlidingWindow.Incr()
translatedReqs := make(map[string]*RPCReq, len(rpcReqs))
// translate consensus_getReceipts to receipts target
// right now we only support non-batched
if isBatch {
for _, rpcReq := range rpcReqs {
if rpcReq.Method == ConsensusGetReceiptsMethod {
return nil, ErrConsensusGetReceiptsCantBeBatched
}
}
} else {
for _, rpcReq := range rpcReqs {
if rpcReq.Method == ConsensusGetReceiptsMethod {
translatedReqs[string(rpcReq.ID)] = rpcReq
rpcReq.Method = b.receiptsTarget
var reqParams []rpc.BlockNumberOrHash
err := json.Unmarshal(rpcReq.Params, &reqParams)
if err != nil {
return nil, ErrInvalidRequest("invalid request")
}
var translatedParams []byte
switch rpcReq.Method {
case ReceiptsTargetDebugGetRawReceipts,
ReceiptsTargetEthGetTransactionReceipts,
ReceiptsTargetParityGetTransactionReceipts:
// conventional methods use an array of strings having either block number or block hash
// i.e. ["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"]
params := make([]string, 1)
if reqParams[0].BlockNumber != nil {
params[0] = reqParams[0].BlockNumber.String()
} else {
params[0] = reqParams[0].BlockHash.Hex()
}
translatedParams = mustMarshalJSON(params)
case ReceiptsTargetAlchemyGetTransactionReceipts:
// alchemy uses an array of object with either block number or block hash
// i.e. [{ blockHash: "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b" }]
params := make([]BlockHashOrNumberParameter, 1)
if reqParams[0].BlockNumber != nil {
params[0].BlockNumber = reqParams[0].BlockNumber
} else {
params[0].BlockHash = reqParams[0].BlockHash
}
translatedParams = mustMarshalJSON(params)
default:
return nil, ErrConsensusGetReceiptsInvalidTarget
}
rpcReq.Params = translatedParams
}
}
}
isSingleElementBatch := len(rpcReqs) == 1
// Single element batches are unwrapped before being sent
// since Alchemy handles single requests better than batches.
var body []byte
if isSingleElementBatch {
body = mustMarshalJSON(rpcReqs[0])
......@@ -443,17 +543,17 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool
return nil, wrapErr(err, "error reading response body")
}
var res []*RPCRes
var rpcRes []*RPCRes
if isSingleElementBatch {
var singleRes RPCRes
if err := json.Unmarshal(resB, &singleRes); err != nil {
return nil, ErrBackendBadResponse
}
res = []*RPCRes{
rpcRes = []*RPCRes{
&singleRes,
}
} else {
if err := json.Unmarshal(resB, &res); err != nil {
if err := json.Unmarshal(resB, &rpcRes); err != nil {
// Infura may return a single JSON-RPC response if, for example, the batch contains a request for an unsupported method
if responseIsNotBatched(resB) {
b.networkErrorsSlidingWindow.Incr()
......@@ -466,7 +566,7 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool
}
}
if len(rpcReqs) != len(res) {
if len(rpcReqs) != len(rpcRes) {
b.networkErrorsSlidingWindow.Incr()
RecordBackendNetworkErrorRateSlidingWindow(b, b.ErrorRate())
return nil, ErrBackendUnexpectedJSONRPC
......@@ -475,7 +575,7 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool
// capture the HTTP status code in the response. this will only
// ever be 400 given the status check on line 318 above.
if httpRes.StatusCode != 200 {
for _, res := range res {
for _, res := range rpcRes {
res.Error.HTTPErrorCode = httpRes.StatusCode
}
}
......@@ -484,8 +584,20 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool
RecordBackendNetworkLatencyAverageSlidingWindow(b, time.Duration(b.latencySlidingWindow.Avg()))
RecordBackendNetworkErrorRateSlidingWindow(b, b.ErrorRate())
sortBatchRPCResponse(rpcReqs, res)
return res, nil
// enrich the response with the actual request method
for _, res := range rpcRes {
translatedReq, exist := translatedReqs[string(res.ID)]
if exist {
res.Result = ConsensusGetReceiptsResult{
Method: translatedReq.Method,
Result: res.Result,
}
}
}
sortBatchRPCResponse(rpcReqs, rpcRes)
return rpcRes, nil
}
// IsHealthy checks if the backend is able to serve traffic, based on dynamic parameters
......@@ -604,7 +716,9 @@ func (bg *BackendGroup) Forward(ctx context.Context, rpcReqs []*RPCReq, isBatch
if len(rpcReqs) > 0 {
res, err = back.Forward(ctx, rpcReqs, isBatch)
if errors.Is(err, ErrMethodNotWhitelisted) {
if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) ||
errors.Is(err, ErrConsensusGetReceiptsInvalidTarget) ||
errors.Is(err, ErrMethodNotWhitelisted) {
return nil, err
}
if errors.Is(err, ErrBackendOffline) {
......
......@@ -79,18 +79,20 @@ type BackendOptions struct {
}
type BackendConfig struct {
Username string `toml:"username"`
Password string `toml:"password"`
RPCURL string `toml:"rpc_url"`
WSURL string `toml:"ws_url"`
WSPort int `toml:"ws_port"`
MaxRPS int `toml:"max_rps"`
MaxWSConns int `toml:"max_ws_conns"`
CAFile string `toml:"ca_file"`
ClientCertFile string `toml:"client_cert_file"`
ClientKeyFile string `toml:"client_key_file"`
StripTrailingXFF bool `toml:"strip_trailing_xff"`
SkipPeerCountCheck bool `toml:"consensus_skip_peer_count"`
Username string `toml:"username"`
Password string `toml:"password"`
RPCURL string `toml:"rpc_url"`
WSURL string `toml:"ws_url"`
WSPort int `toml:"ws_port"`
MaxRPS int `toml:"max_rps"`
MaxWSConns int `toml:"max_ws_conns"`
CAFile string `toml:"ca_file"`
ClientCertFile string `toml:"client_cert_file"`
ClientKeyFile string `toml:"client_key_file"`
StripTrailingXFF bool `toml:"strip_trailing_xff"`
ConsensusSkipPeerCountCheck bool `toml:"consensus_skip_peer_count"`
ConsensusReceiptsTarget string `toml:"consensus_receipts_target"`
}
type BackendsConfig map[string]*BackendConfig
......
......@@ -74,7 +74,9 @@ client_cert_file = ""
client_key_file = ""
# Allows backends to skip peer count checking, default false
# consensus_skip_peer_count = true
# Specified the target method to get receipts, default "debug_getRawReceipts"
# See https://github.com/ethereum-optimism/optimism/blob/186e46a47647a51a658e699e9ff047d39444c2de/op-node/sources/receipts.go#L186-L253
consensus_receipts_target = "eth_getBlockReceipts"
[backends.alchemy]
rpc_url = ""
......@@ -83,6 +85,7 @@ username = ""
password = ""
max_rps = 3
max_ws_conns = 1
consensus_receipts_target = "alchemy_getTransactionReceipts"
[backend_groups]
[backend_groups.main]
......
......@@ -9,6 +9,7 @@ require (
github.com/ethereum/go-ethereum v1.12.0
github.com/go-redis/redis/v8 v8.11.4
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
......
......@@ -157,6 +157,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
......
......@@ -784,6 +784,211 @@ func TestConsensus(t *testing.T) {
// dont rewrite for 0xe1
require.Equal(t, "0xe1", jsonMap[2]["result"].(map[string]interface{})["number"])
})
t.Run("translate consensus_getReceipts to debug_getRawReceipts", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var jsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &jsonMap)
require.NoError(t, err)
require.Equal(t, "debug_getRawReceipts", jsonMap["method"])
require.Equal(t, "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", jsonMap["params"].([]interface{})[0])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to debug_getRawReceipts with latest block tag", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"latest"})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var jsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &jsonMap)
require.NoError(t, err)
require.Equal(t, "debug_getRawReceipts", jsonMap["method"])
require.Equal(t, "0x101", jsonMap["params"].([]interface{})[0])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to debug_getRawReceipts with block number", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"0x55"})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var jsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &jsonMap)
require.NoError(t, err)
require.Equal(t, "debug_getRawReceipts", jsonMap["method"])
require.Equal(t, "0x55", jsonMap["params"].([]interface{})[0])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to alchemy_getTransactionReceipts with block hash", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("alchemy_getTransactionReceipts"))
defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts"))
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var reqJsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &reqJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", reqJsonMap["method"])
require.Equal(t, "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", reqJsonMap["params"].([]interface{})[0].(map[string]interface{})["blockHash"])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to alchemy_getTransactionReceipts with block number", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("alchemy_getTransactionReceipts"))
defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts"))
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"0x55"})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var reqJsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &reqJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", reqJsonMap["method"])
require.Equal(t, "0x55", reqJsonMap["params"].([]interface{})[0].(map[string]interface{})["blockNumber"])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to alchemy_getTransactionReceipts with latest block tag", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("alchemy_getTransactionReceipts"))
defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts"))
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"latest"})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var reqJsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &reqJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", reqJsonMap["method"])
require.Equal(t, "0x101", reqJsonMap["params"].([]interface{})[0].(map[string]interface{})["blockNumber"])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to unsupported consensus_receipts_target", func(t *testing.T) {
reset()
useOnlyNode1()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("unsupported_consensus_receipts_target"))
defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts"))
_, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"latest"})
require.NoError(t, err)
require.Equal(t, 400, statusCode)
})
t.Run("consensus_getReceipts should not be used in a batch", func(t *testing.T) {
reset()
useOnlyNode1()
_, statusCode, err := client.SendBatchRPC(
NewRPCReq("1", "eth_getBlockByNumber", []interface{}{"latest"}),
NewRPCReq("2", "consensus_getReceipts", []interface{}{"0x55"}),
NewRPCReq("3", "eth_getBlockByNumber", []interface{}{"0xe1"}))
require.NoError(t, err)
require.Equal(t, 400, statusCode)
})
}
func buildResponse(result interface{}) string {
......
......@@ -27,3 +27,4 @@ eth_call = "node"
eth_chainId = "node"
eth_blockNumber = "node"
eth_getBlockByNumber = "node"
consensus_getReceipts = "node"
......@@ -184,3 +184,30 @@
"number": "0xd1"
}
}
- method: debug_getRawReceipts
response: >
{
"jsonrpc": "2.0",
"id": 67,
"result": {
"_": "debug_getRawReceipts"
}
}
- method: eth_getTransactionReceipt
response: >
{
"jsonrpc": "2.0",
"id": 67,
"result": {
"_": "eth_getTransactionReceipt"
}
}
- method: alchemy_getTransactionReceipts
response: >
{
"jsonrpc": "2.0",
"id": 67,
"result": {
"_": "alchemy_getTransactionReceipts"
}
}
......@@ -141,7 +141,17 @@ func Start(config *Config) (*Server, func(), error) {
opts = append(opts, WithStrippedTrailingXFF())
}
opts = append(opts, WithProxydIP(os.Getenv("PROXYD_IP")))
opts = append(opts, WithSkipPeerCountCheck(cfg.SkipPeerCountCheck))
opts = append(opts, WithConsensusSkipPeerCountCheck(cfg.ConsensusSkipPeerCountCheck))
receiptsTarget, err := ReadFromEnvOrConfig(cfg.ConsensusReceiptsTarget)
if err != nil {
return nil, nil, err
}
receiptsTarget, err = validateReceiptsTarget(receiptsTarget)
if err != nil {
return nil, nil, err
}
opts = append(opts, WithConsensusReceiptTarget(receiptsTarget))
back := NewBackend(name, rpcURL, wsURL, rpcRequestSemaphore, opts...)
backendNames = append(backendNames, name)
......@@ -316,6 +326,21 @@ func Start(config *Config) (*Server, func(), error) {
return srv, shutdownFunc, nil
}
func validateReceiptsTarget(val string) (string, error) {
if val == "" {
val = ReceiptsTargetDebugGetRawReceipts
}
switch val {
case ReceiptsTargetDebugGetRawReceipts,
ReceiptsTargetAlchemyGetTransactionReceipts,
ReceiptsTargetEthGetTransactionReceipts,
ReceiptsTargetParityGetTransactionReceipts:
return val, nil
default:
return "", fmt.Errorf("invalid receipts target: %s", val)
}
}
func secondsToDuration(seconds int) time.Duration {
return time.Duration(seconds) * time.Second
}
......
......@@ -63,7 +63,7 @@ func RewriteRequest(rctx RewriteContext, req *RPCReq, res *RPCRes) (RewriteResul
case "eth_getLogs",
"eth_newFilter":
return rewriteRange(rctx, req, res, 0)
case "debug_getRawReceipts":
case "debug_getRawReceipts", "consensus_getReceipts":
return rewriteParam(rctx, req, res, 0, true)
case "eth_getBalance",
"eth_getCode",
......
......@@ -347,6 +347,11 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
writeRPCError(ctx, w, nil, ErrGatewayTimeout)
return
}
if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) ||
errors.Is(err, ErrConsensusGetReceiptsInvalidTarget) {
writeRPCError(ctx, w, nil, ErrInvalidRequest(err.Error()))
return
}
if err != nil {
writeRPCError(ctx, w, nil, ErrInternal)
return
......@@ -360,6 +365,11 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
rawBody := json.RawMessage(body)
backendRes, cached, err := s.handleBatchRPC(ctx, []json.RawMessage{rawBody}, isLimited, false)
if err != nil {
if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) ||
errors.Is(err, ErrConsensusGetReceiptsInvalidTarget) {
writeRPCError(ctx, w, nil, ErrInvalidRequest(err.Error()))
return
}
writeRPCError(ctx, w, nil, ErrInternal)
return
}
......@@ -485,6 +495,10 @@ func (s *Server) handleBatchRPC(ctx context.Context, reqs []json.RawMessage, isL
elems := cacheMisses[start:end]
res, err := s.BackendGroups[group.backendGroup].Forward(ctx, createBatchRequest(elems), isBatch)
if err != nil {
if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) ||
errors.Is(err, ErrConsensusGetReceiptsInvalidTarget) {
return nil, false, err
}
log.Error(
"error forwarding RPC batch",
"batch_size", len(elems),
......
......@@ -88,7 +88,15 @@ func (mh *MockedHandler) Handler(w http.ResponseWriter, req *http.Request) {
}
}
if selectedResponse != "" {
responses = append(responses, selectedResponse)
var rpcRes proxyd.RPCRes
err = json.Unmarshal([]byte(selectedResponse), &rpcRes)
if err != nil {
panic(err)
}
idJson, _ := json.Marshal(r["id"])
rpcRes.ID = idJson
res, _ := json.Marshal(rpcRes)
responses = append(responses, string(res))
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment