diff --git a/espresso/bindings/batch_authenticator.go b/espresso/bindings/batch_authenticator.go new file mode 100644 index 00000000000..3e72edfe49c --- /dev/null +++ b/espresso/bindings/batch_authenticator.go @@ -0,0 +1,2277 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindings + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// BatchAuthenticatorMetaData contains all meta data concerning the BatchAuthenticator contract. +var BatchAuthenticatorMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"acceptOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"activeIsEspresso\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"addGuardian\",\"inputs\":[{\"name\":\"guardian\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"authenticateBatchInfo\",\"inputs\":[{\"name\":\"_commitment\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"_signature\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"espressoBatcher\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"espressoBatcherAt\",\"inputs\":[{\"name\":\"_index\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[{\"name\":\"batcher_\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"fromBlock_\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"espressoBatcherAtBlock\",\"inputs\":[{\"name\":\"_l1Block\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"espressoBatcherHistoryLength\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"espressoTEEVerifier\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractIEspressoTEEVerifier\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getGuardians\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address[]\",\"internalType\":\"address[]\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"guardianCount\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initVersion\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_espressoTEEVerifier\",\"type\":\"address\",\"internalType\":\"contractIEspressoTEEVerifier\"},{\"name\":\"_espressoBatcher\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_systemConfig\",\"type\":\"address\",\"internalType\":\"contractISystemConfig\"},{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_activeIsEspresso\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isGuardian\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"nitroValidator\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pendingOwner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proxyAdmin\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractIProxyAdmin\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proxyAdminOwner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"registerSigner\",\"inputs\":[{\"name\":\"_verificationData\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"_data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"removeGuardian\",\"inputs\":[{\"name\":\"guardian\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setActiveIsEspresso\",\"inputs\":[{\"name\":\"_desired\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setEspressoBatcher\",\"inputs\":[{\"name\":\"_newEspressoBatcher\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"systemConfig\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractISystemConfig\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"version\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"BatchInfoAuthenticated\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"caller\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"BatcherSwitched\",\"inputs\":[{\"name\":\"activeIsEspresso\",\"type\":\"bool\",\"indexed\":true,\"internalType\":\"bool\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"EspressoBatcherUpdated\",\"inputs\":[{\"name\":\"oldEspressoBatcher\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newEspressoBatcher\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"fromBlock\",\"type\":\"uint64\",\"indexed\":true,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"GuardianAdded\",\"inputs\":[{\"name\":\"guardian\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"GuardianRemoved\",\"inputs\":[{\"name\":\"guardian\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferStarted\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SignerRegistrationInitiated\",\"inputs\":[{\"name\":\"caller\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"CheckpointUnorderedInsertion\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidAddress\",\"inputs\":[{\"name\":\"contract_\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"InvalidGuardianAddress\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidInitialization\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidSignature\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NoChange\",\"inputs\":[{\"name\":\"batcher\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"NotGuardian\",\"inputs\":[{\"name\":\"caller\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"NotGuardianOrOwner\",\"inputs\":[{\"name\":\"caller\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"NotInitializing\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OwnableInvalidOwner\",\"inputs\":[{\"name\":\"owner\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OwnableUnauthorizedAccount\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OwnerCantBeGuardian\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ProxyAdminOwnedBase_NotProxyAdmin\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ProxyAdminOwnedBase_NotProxyAdminOrProxyAdminOwner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ProxyAdminOwnedBase_NotProxyAdminOwner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ProxyAdminOwnedBase_NotResolvedDelegateProxy\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ProxyAdminOwnedBase_NotSharedProxyAdminOwner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ProxyAdminOwnedBase_ProxyAdminNotFound\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ReinitializableBase_ZeroInitVersion\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UnauthorizedEspressoBatcher\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"expected\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"UnauthorizedFallbackBatcher\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"expected\",\"type\":\"address\",\"internalType\":\"address\"}]}]", + Bin: "0x60a060405234801561000f575f5ffd5b50600160805261001d610022565b6100d4565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156100725760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146100d15780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b60805161250d6100f35f395f818161025e0152610e51015261250d5ff3fe608060405234801561000f575f5ffd5b50600436106101b0575f3560e01c806379ba5097116100f3578063e30c397811610093578063fa14fe6d1161006e578063fa14fe6d146103bd578063fc5b5fda146103dc578063fc619e41146103ef578063fd402af714610402575f5ffd5b8063e30c39781461037e578063eca919df14610386578063f2fde38b146103aa575f5ffd5b80638da5cb5b116100ce5780638da5cb5b14610348578063a526d83b14610350578063ba58e82a14610363578063dad544e014610376575f5ffd5b806379ba5097146103255780637d531a781461032d57806388da3bb714610340575f5ffd5b80633e47158c1161015e57806354fd4d501161013957806354fd4d50146102ae5780636c076871146102f7578063714041561461030a578063715018a61461031d575f5ffd5b80633e47158c146102885780634268ecaa1461029057806354387ad7146102a6575f5ffd5b80632ce532471161018e5780632ce532471461022257806333d7e2bd1461023757806338d38c9714610257575f5ffd5b80630665f04b146101b45780630c68ba21146101d25780631b076a4c146101f5575b5f5ffd5b6101bc61044a565b6040516101c99190611fda565b60405180910390f35b6101e56101e0366004612053565b61047a565b60405190151581526020016101c9565b6101fd6104d5565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101c9565b610235610230366004612053565b610563565b005b6001546101fd9073ffffffffffffffffffffffffffffffffffffffff1681565b60405160ff7f00000000000000000000000000000000000000000000000000000000000000001681526020016101c9565b6101fd610671565b610298610877565b6040519081526020016101c9565b610298610881565b6102ea6040518060400160405280600581526020017f312e322e3000000000000000000000000000000000000000000000000000000081525081565b6040516101c9919061206e565b6102356103053660046120ce565b6108ab565b610235610318366004612053565b6109e9565b610235610a68565b610235610a7b565b6101fd61033b3660046120e9565b610af3565b6101fd610b09565b6101fd610b14565b61023561035e366004612053565b610b1d565b610235610371366004612155565b610c91565b6101fd610d4b565b6101fd610d9c565b5f546101e59074010000000000000000000000000000000000000000900460ff1681565b6102356103b8366004612053565b610ddd565b5f546101fd9073ffffffffffffffffffffffffffffffffffffffff1681565b6102356103ea3660046121c1565b610e4f565b6102356103fd36600461222e565b6111bd565b610415610410366004612276565b611468565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835267ffffffffffffffff9091166020830152016101c9565b60606104757f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f00611488565b905090565b5f6104cf827f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f005b9073ffffffffffffffffffffffffffffffffffffffff165f9081526001919091016020526040902054151590565b92915050565b5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d80a4c286040518163ffffffff1660e01b8152600401602060405180830381865afa15801561053f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104759190612299565b61056b61149b565b5f610574610b09565b90508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036105f8576040517f81efeac100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526024015b60405180910390fd5b43610605600282856114f3565b50508067ffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fcf8f6f62babb05dd1d159c090ad8429ee0df72e16c82701004a5405f908cb0f560405160405180910390a4505050565b5f8061069b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff8116156106be57919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e67657200000000000081525051600261070191906122e1565b604080513060208201525f918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e676572000000000000919091179061075b906060015b604051602081830303815290604052805190602001205490565b14610792576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080513060208201526001918101919091525f906107b390606001610741565b905073ffffffffffffffffffffffffffffffffffffffff811615610845578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561081a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061083e9190612299565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61047560025490565b5f6104757f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f0061150d565b6108d5337f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f006104a1565b15801561091557506108e5610b14565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614155b1561094e576040517fd53780c40000000000000000000000000000000000000000000000000000000081523360048201526024016105ef565b5f5460ff74010000000000000000000000000000000000000000909104161515811515146109e6575f80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000083151590810291909117825560405190917fb957d7fc29e5974594db2f2e132076d52f42c0734eae05fd5ea080d1ba175ad391a25b50565b6109f161149b565b7f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f00610a1c8183611516565b610a24575050565b60405173ffffffffffffffffffffffffffffffffffffffff8316907fb8107d0c6b40be480ce3172ee66ba6d64b71f6b1685a851340036e6e2e3e3c52905f90a25050565b610a7061149b565b610a795f611537565b565b3380610a85610d9c565b73ffffffffffffffffffffffffffffffffffffffff1614610aea576040517f118cdaa700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016105ef565b6109e681611537565b5f6104cf600267ffffffffffffffff8416611587565b5f6104756002611653565b5f6104756116a0565b610b2561149b565b73ffffffffffffffffffffffffffffffffffffffff8116610b72576040517f1b08105400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b7a610b14565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610be55750610bb6610d9c565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b15610c1c576040517f3af3c41c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f00610c4781836116c8565b15610c8d5760405173ffffffffffffffffffffffffffffffffffffffff8316907f038596bb31e2e7d3d9f184d4c98b310103f6d7f5830e5eec32bffe6f1728f969905f90a25b5050565b5f80546040517fdac79fc800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169163dac79fc891610cee91889188918891889190600401612378565b5f604051808303815f87803b158015610d05575f5ffd5b505af1158015610d17573d5f5f3e3d5ffd5b50506040513392507f665b016a0ac50d1280744eaaff1cf21254d0fd30e4c3987d291913c32163416c91505f90a250505050565b5f610d54610671565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561053f573d5f5f3e3d5ffd5b5f807f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c005b5473ffffffffffffffffffffffffffffffffffffffff1692915050565b610de561149b565b610e0f817f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f006104a1565b15610e46576040517f3af3c41c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109e6816116e9565b7f000000000000000000000000000000000000000000000000000000000000000060ff165f610e7c6117a0565b805490915068010000000000000000900460ff1680610ea95750805467ffffffffffffffff808416911610155b15610ee0576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000001667ffffffffffffffff83161768010000000000000000178155610f256117c8565b610f2e84611849565b73ffffffffffffffffffffffffffffffffffffffff8616610f93576040517f8e4c8aa600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201526024016105ef565b73ffffffffffffffffffffffffffffffffffffffff8516610ff8576040517f8e4c8aa600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff861660048201526024016105ef565b73ffffffffffffffffffffffffffffffffffffffff871661105d576040517f8e4c8aa600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff881660048201526024016105ef565b5f8054600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8981169190911790915589167fffffffffffffffffffffff0000000000000000000000000000000000000000009091161774010000000000000000000000000000000000000000851515021790556002545f0361115157436110ff600282896114f3565b505060405167ffffffffffffffff82169073ffffffffffffffffffffffffffffffffffffffff8916905f907fcf8f6f62babb05dd1d159c090ad8429ee0df72e16c82701004a5405f908cb0f5908290a4505b80547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16815560405167ffffffffffffffff831681527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a150505050505050565b5f5474010000000000000000000000000000000000000000900460ff161561132c575f6111e8610b09565b90503373ffffffffffffffffffffffffffffffffffffffff821614611257576040517f8d1db98a00000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff821660248201526044016105ef565b5f80546040517fa81d9c5c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169163a81d9c5c916112b191879187918a916004016123b8565b602060405180830381865afa1580156112cc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112f091906123e9565b611326576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5061142e565b600154604080517fe81b2c6d00000000000000000000000000000000000000000000000000000000815290515f9273ffffffffffffffffffffffffffffffffffffffff169163e81b2c6d9160048083019260209291908290030181865afa158015611399573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113bd9190612404565b90503373ffffffffffffffffffffffffffffffffffffffff82161461142c576040517f51f905ea00000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff821660248201526044016105ef565b505b60405183815233907f731978a77d438b0ea35a9034fb28d9cf9372e1649f18c213110adcfab65c5c5c9060200160405180910390a2505050565b5f808061147660028561185a565b60208101519051909590945092505050565b60605f611494836118e3565b9392505050565b336114a4610b14565b73ffffffffffffffffffffffffffffffffffffffff1614610a79576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016105ef565b5f8061150085858561193c565b915091505b935093915050565b5f6104cf825490565b5f6114948373ffffffffffffffffffffffffffffffffffffffff8416611b41565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155610c8d82611c24565b81545f90818160058111156115e9575f6115a084611cb9565b6115aa908561241b565b5f888152602090209091508101546bffffffffffffffffffffffff90811690871610156115d9578091506115e7565b6115e481600161242e565b92505b505b5f6115f687878585611d9d565b905080156116465761161a8761160d60018461241b565b5f91825260209091200190565b546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16611648565b5f5b979650505050505050565b80545f9080156116985761166c8361160d60018461241b565b546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16611494565b5f9392505050565b5f807f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300610dc0565b5f6114948373ffffffffffffffffffffffffffffffffffffffff8416611e08565b6116f161149b565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8316908117825561175a610b14565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006104cf565b336117d1610671565b73ffffffffffffffffffffffffffffffffffffffff16141580156118125750336117f9610d4b565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610a79576040517fc4050a2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611851611e54565b6109e681611e92565b604080518082019091525f8082526020820152825f018263ffffffff168154811061188757611887612441565b5f918252602091829020604080518082019091529101546bffffffffffffffffffffffff811682526c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16918101919091529392505050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561193057602002820191905f5260205f20905b81548152602001906001019080831161191c575b50505050509050919050565b82545f9081908015611acb575f6119588761160d60018561241b565b6040805180820190915290546bffffffffffffffffffffffff8082168084526c0100000000000000000000000090920473ffffffffffffffffffffffffffffffffffffffff16602084015291925090871610156119e1576040517f2520601d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516bffffffffffffffffffffffff808816911603611a4f5784611a0a8861160d60018661241b565b805473ffffffffffffffffffffffffffffffffffffffff929092166c01000000000000000000000000026bffffffffffffffffffffffff909216919091179055611abb565b604080518082019091526bffffffffffffffffffffffff808816825273ffffffffffffffffffffffffffffffffffffffff80881660208085019182528b54600181018d555f8d815291909120945191519092166c01000000000000000000000000029216919091179101555b6020015192508391506115059050565b5050604080518082019091526bffffffffffffffffffffffff808516825273ffffffffffffffffffffffffffffffffffffffff80851660208085019182528854600181018a555f8a8152918220955192519093166c01000000000000000000000000029190931617920191909155905081611505565b5f8181526001830160205260408120548015611c1b575f611b6360018361241b565b85549091505f90611b769060019061241b565b9050808214611bd5575f865f018281548110611b9457611b94612441565b905f5260205f200154905080875f018481548110611bb457611bb4612441565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080611be657611be661246e565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506104cf565b5f9150506104cf565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080547fffffffffffffffffffffffff0000000000000000000000000000000000000000811673ffffffffffffffffffffffffffffffffffffffff848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f815f03611cc857505f919050565b5f6001611cd484611ea3565b901c6001901b90506001818481611ced57611ced61249b565b048201901c90506001818481611d0557611d0561249b565b048201901c90506001818481611d1d57611d1d61249b565b048201901c90506001818481611d3557611d3561249b565b048201901c90506001818481611d4d57611d4d61249b565b048201901c90506001818481611d6557611d6561249b565b048201901c90506001818481611d7d57611d7d61249b565b048201901c905061149481828581611d9757611d9761249b565b04611f36565b5f5b81831015611e00575f611db28484611f4b565b5f878152602090209091506bffffffffffffffffffffffff8616908201546bffffffffffffffffffffffff161115611dec57809250611dfa565b611df781600161242e565b93505b50611d9f565b509392505050565b5f818152600183016020526040812054611e4d57508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556104cf565b505f6104cf565b611e5c611f65565b610a79576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e9a611e54565b6109e681611f83565b5f80608083901c15611eb757608092831c92015b604083901c15611ec957604092831c92015b602083901c15611edb57602092831c92015b601083901c15611eed57601092831c92015b600883901c15611eff57600892831c92015b600483901c15611f1157600492831c92015b600283901c15611f2357600292831c92015b600183901c156104cf5760010192915050565b5f818310611f445781611494565b5090919050565b5f611f5960028484186124c8565b6114949084841661242e565b5f611f6e6117a0565b5468010000000000000000900460ff16919050565b611f8b611e54565b73ffffffffffffffffffffffffffffffffffffffff8116610aea576040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081525f60048201526024016105ef565b602080825282518282018190525f918401906040840190835b8181101561202757835173ffffffffffffffffffffffffffffffffffffffff16835260209384019390920191600101611ff3565b509095945050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146109e6575f5ffd5b5f60208284031215612063575f5ffd5b813561149481612032565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b80151581146109e6575f5ffd5b5f602082840312156120de575f5ffd5b8135611494816120c1565b5f602082840312156120f9575f5ffd5b813567ffffffffffffffff81168114611494575f5ffd5b5f5f83601f840112612120575f5ffd5b50813567ffffffffffffffff811115612137575f5ffd5b60208301915083602082850101111561214e575f5ffd5b9250929050565b5f5f5f5f60408587031215612168575f5ffd5b843567ffffffffffffffff81111561217e575f5ffd5b61218a87828801612110565b909550935050602085013567ffffffffffffffff8111156121a9575f5ffd5b6121b587828801612110565b95989497509550505050565b5f5f5f5f5f60a086880312156121d5575f5ffd5b85356121e081612032565b945060208601356121f081612032565b9350604086013561220081612032565b9250606086013561221081612032565b91506080860135612220816120c1565b809150509295509295909350565b5f5f5f60408486031215612240575f5ffd5b83359250602084013567ffffffffffffffff81111561225d575f5ffd5b61226986828701612110565b9497909650939450505050565b5f60208284031215612286575f5ffd5b813563ffffffff81168114611494575f5ffd5b5f602082840312156122a9575f5ffd5b815161149481612032565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820281158282048414176104cf576104cf6122b4565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60018110612374577f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b9052565b606081525f61238b6060830187896122f8565b828103602084015261239e8186886122f8565b9150506123ae604083018461233f565b9695505050505050565b606081525f6123cb6060830186886122f8565b90508360208301526123e0604083018461233f565b95945050505050565b5f602082840312156123f9575f5ffd5b8151611494816120c1565b5f60208284031215612414575f5ffd5b5051919050565b818103818111156104cf576104cf6122b4565b808201808211156104cf576104cf6122b4565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f826124fb577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b50049056fea164736f6c634300081c000a", +} + +// BatchAuthenticatorABI is the input ABI used to generate the binding from. +// Deprecated: Use BatchAuthenticatorMetaData.ABI instead. +var BatchAuthenticatorABI = BatchAuthenticatorMetaData.ABI + +// BatchAuthenticatorBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use BatchAuthenticatorMetaData.Bin instead. +var BatchAuthenticatorBin = BatchAuthenticatorMetaData.Bin + +// DeployBatchAuthenticator deploys a new Ethereum contract, binding an instance of BatchAuthenticator to it. +func DeployBatchAuthenticator(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *BatchAuthenticator, error) { + parsed, err := BatchAuthenticatorMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(BatchAuthenticatorBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &BatchAuthenticator{BatchAuthenticatorCaller: BatchAuthenticatorCaller{contract: contract}, BatchAuthenticatorTransactor: BatchAuthenticatorTransactor{contract: contract}, BatchAuthenticatorFilterer: BatchAuthenticatorFilterer{contract: contract}}, nil +} + +// BatchAuthenticator is an auto generated Go binding around an Ethereum contract. +type BatchAuthenticator struct { + BatchAuthenticatorCaller // Read-only binding to the contract + BatchAuthenticatorTransactor // Write-only binding to the contract + BatchAuthenticatorFilterer // Log filterer for contract events +} + +// BatchAuthenticatorCaller is an auto generated read-only Go binding around an Ethereum contract. +type BatchAuthenticatorCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BatchAuthenticatorTransactor is an auto generated write-only Go binding around an Ethereum contract. +type BatchAuthenticatorTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BatchAuthenticatorFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type BatchAuthenticatorFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BatchAuthenticatorSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type BatchAuthenticatorSession struct { + Contract *BatchAuthenticator // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BatchAuthenticatorCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type BatchAuthenticatorCallerSession struct { + Contract *BatchAuthenticatorCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// BatchAuthenticatorTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type BatchAuthenticatorTransactorSession struct { + Contract *BatchAuthenticatorTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BatchAuthenticatorRaw is an auto generated low-level Go binding around an Ethereum contract. +type BatchAuthenticatorRaw struct { + Contract *BatchAuthenticator // Generic contract binding to access the raw methods on +} + +// BatchAuthenticatorCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type BatchAuthenticatorCallerRaw struct { + Contract *BatchAuthenticatorCaller // Generic read-only contract binding to access the raw methods on +} + +// BatchAuthenticatorTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type BatchAuthenticatorTransactorRaw struct { + Contract *BatchAuthenticatorTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewBatchAuthenticator creates a new instance of BatchAuthenticator, bound to a specific deployed contract. +func NewBatchAuthenticator(address common.Address, backend bind.ContractBackend) (*BatchAuthenticator, error) { + contract, err := bindBatchAuthenticator(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &BatchAuthenticator{BatchAuthenticatorCaller: BatchAuthenticatorCaller{contract: contract}, BatchAuthenticatorTransactor: BatchAuthenticatorTransactor{contract: contract}, BatchAuthenticatorFilterer: BatchAuthenticatorFilterer{contract: contract}}, nil +} + +// NewBatchAuthenticatorCaller creates a new read-only instance of BatchAuthenticator, bound to a specific deployed contract. +func NewBatchAuthenticatorCaller(address common.Address, caller bind.ContractCaller) (*BatchAuthenticatorCaller, error) { + contract, err := bindBatchAuthenticator(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &BatchAuthenticatorCaller{contract: contract}, nil +} + +// NewBatchAuthenticatorTransactor creates a new write-only instance of BatchAuthenticator, bound to a specific deployed contract. +func NewBatchAuthenticatorTransactor(address common.Address, transactor bind.ContractTransactor) (*BatchAuthenticatorTransactor, error) { + contract, err := bindBatchAuthenticator(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &BatchAuthenticatorTransactor{contract: contract}, nil +} + +// NewBatchAuthenticatorFilterer creates a new log filterer instance of BatchAuthenticator, bound to a specific deployed contract. +func NewBatchAuthenticatorFilterer(address common.Address, filterer bind.ContractFilterer) (*BatchAuthenticatorFilterer, error) { + contract, err := bindBatchAuthenticator(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &BatchAuthenticatorFilterer{contract: contract}, nil +} + +// bindBatchAuthenticator binds a generic wrapper to an already deployed contract. +func bindBatchAuthenticator(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := BatchAuthenticatorMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_BatchAuthenticator *BatchAuthenticatorRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _BatchAuthenticator.Contract.BatchAuthenticatorCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_BatchAuthenticator *BatchAuthenticatorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.BatchAuthenticatorTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_BatchAuthenticator *BatchAuthenticatorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.BatchAuthenticatorTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_BatchAuthenticator *BatchAuthenticatorCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _BatchAuthenticator.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_BatchAuthenticator *BatchAuthenticatorTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_BatchAuthenticator *BatchAuthenticatorTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.contract.Transact(opts, method, params...) +} + +// ActiveIsEspresso is a free data retrieval call binding the contract method 0xeca919df. +// +// Solidity: function activeIsEspresso() view returns(bool) +func (_BatchAuthenticator *BatchAuthenticatorCaller) ActiveIsEspresso(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "activeIsEspresso") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// ActiveIsEspresso is a free data retrieval call binding the contract method 0xeca919df. +// +// Solidity: function activeIsEspresso() view returns(bool) +func (_BatchAuthenticator *BatchAuthenticatorSession) ActiveIsEspresso() (bool, error) { + return _BatchAuthenticator.Contract.ActiveIsEspresso(&_BatchAuthenticator.CallOpts) +} + +// ActiveIsEspresso is a free data retrieval call binding the contract method 0xeca919df. +// +// Solidity: function activeIsEspresso() view returns(bool) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) ActiveIsEspresso() (bool, error) { + return _BatchAuthenticator.Contract.ActiveIsEspresso(&_BatchAuthenticator.CallOpts) +} + +// EspressoBatcher is a free data retrieval call binding the contract method 0x88da3bb7. +// +// Solidity: function espressoBatcher() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCaller) EspressoBatcher(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "espressoBatcher") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// EspressoBatcher is a free data retrieval call binding the contract method 0x88da3bb7. +// +// Solidity: function espressoBatcher() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorSession) EspressoBatcher() (common.Address, error) { + return _BatchAuthenticator.Contract.EspressoBatcher(&_BatchAuthenticator.CallOpts) +} + +// EspressoBatcher is a free data retrieval call binding the contract method 0x88da3bb7. +// +// Solidity: function espressoBatcher() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) EspressoBatcher() (common.Address, error) { + return _BatchAuthenticator.Contract.EspressoBatcher(&_BatchAuthenticator.CallOpts) +} + +// EspressoBatcherAt is a free data retrieval call binding the contract method 0xfd402af7. +// +// Solidity: function espressoBatcherAt(uint32 _index) view returns(address batcher_, uint64 fromBlock_) +func (_BatchAuthenticator *BatchAuthenticatorCaller) EspressoBatcherAt(opts *bind.CallOpts, _index uint32) (struct { + Batcher common.Address + FromBlock uint64 +}, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "espressoBatcherAt", _index) + + outstruct := new(struct { + Batcher common.Address + FromBlock uint64 + }) + if err != nil { + return *outstruct, err + } + + outstruct.Batcher = *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + outstruct.FromBlock = *abi.ConvertType(out[1], new(uint64)).(*uint64) + + return *outstruct, err + +} + +// EspressoBatcherAt is a free data retrieval call binding the contract method 0xfd402af7. +// +// Solidity: function espressoBatcherAt(uint32 _index) view returns(address batcher_, uint64 fromBlock_) +func (_BatchAuthenticator *BatchAuthenticatorSession) EspressoBatcherAt(_index uint32) (struct { + Batcher common.Address + FromBlock uint64 +}, error) { + return _BatchAuthenticator.Contract.EspressoBatcherAt(&_BatchAuthenticator.CallOpts, _index) +} + +// EspressoBatcherAt is a free data retrieval call binding the contract method 0xfd402af7. +// +// Solidity: function espressoBatcherAt(uint32 _index) view returns(address batcher_, uint64 fromBlock_) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) EspressoBatcherAt(_index uint32) (struct { + Batcher common.Address + FromBlock uint64 +}, error) { + return _BatchAuthenticator.Contract.EspressoBatcherAt(&_BatchAuthenticator.CallOpts, _index) +} + +// EspressoBatcherAtBlock is a free data retrieval call binding the contract method 0x7d531a78. +// +// Solidity: function espressoBatcherAtBlock(uint64 _l1Block) view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCaller) EspressoBatcherAtBlock(opts *bind.CallOpts, _l1Block uint64) (common.Address, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "espressoBatcherAtBlock", _l1Block) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// EspressoBatcherAtBlock is a free data retrieval call binding the contract method 0x7d531a78. +// +// Solidity: function espressoBatcherAtBlock(uint64 _l1Block) view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorSession) EspressoBatcherAtBlock(_l1Block uint64) (common.Address, error) { + return _BatchAuthenticator.Contract.EspressoBatcherAtBlock(&_BatchAuthenticator.CallOpts, _l1Block) +} + +// EspressoBatcherAtBlock is a free data retrieval call binding the contract method 0x7d531a78. +// +// Solidity: function espressoBatcherAtBlock(uint64 _l1Block) view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) EspressoBatcherAtBlock(_l1Block uint64) (common.Address, error) { + return _BatchAuthenticator.Contract.EspressoBatcherAtBlock(&_BatchAuthenticator.CallOpts, _l1Block) +} + +// EspressoBatcherHistoryLength is a free data retrieval call binding the contract method 0x4268ecaa. +// +// Solidity: function espressoBatcherHistoryLength() view returns(uint256) +func (_BatchAuthenticator *BatchAuthenticatorCaller) EspressoBatcherHistoryLength(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "espressoBatcherHistoryLength") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// EspressoBatcherHistoryLength is a free data retrieval call binding the contract method 0x4268ecaa. +// +// Solidity: function espressoBatcherHistoryLength() view returns(uint256) +func (_BatchAuthenticator *BatchAuthenticatorSession) EspressoBatcherHistoryLength() (*big.Int, error) { + return _BatchAuthenticator.Contract.EspressoBatcherHistoryLength(&_BatchAuthenticator.CallOpts) +} + +// EspressoBatcherHistoryLength is a free data retrieval call binding the contract method 0x4268ecaa. +// +// Solidity: function espressoBatcherHistoryLength() view returns(uint256) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) EspressoBatcherHistoryLength() (*big.Int, error) { + return _BatchAuthenticator.Contract.EspressoBatcherHistoryLength(&_BatchAuthenticator.CallOpts) +} + +// EspressoTEEVerifier is a free data retrieval call binding the contract method 0xfa14fe6d. +// +// Solidity: function espressoTEEVerifier() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCaller) EspressoTEEVerifier(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "espressoTEEVerifier") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// EspressoTEEVerifier is a free data retrieval call binding the contract method 0xfa14fe6d. +// +// Solidity: function espressoTEEVerifier() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorSession) EspressoTEEVerifier() (common.Address, error) { + return _BatchAuthenticator.Contract.EspressoTEEVerifier(&_BatchAuthenticator.CallOpts) +} + +// EspressoTEEVerifier is a free data retrieval call binding the contract method 0xfa14fe6d. +// +// Solidity: function espressoTEEVerifier() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) EspressoTEEVerifier() (common.Address, error) { + return _BatchAuthenticator.Contract.EspressoTEEVerifier(&_BatchAuthenticator.CallOpts) +} + +// GetGuardians is a free data retrieval call binding the contract method 0x0665f04b. +// +// Solidity: function getGuardians() view returns(address[]) +func (_BatchAuthenticator *BatchAuthenticatorCaller) GetGuardians(opts *bind.CallOpts) ([]common.Address, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "getGuardians") + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + +} + +// GetGuardians is a free data retrieval call binding the contract method 0x0665f04b. +// +// Solidity: function getGuardians() view returns(address[]) +func (_BatchAuthenticator *BatchAuthenticatorSession) GetGuardians() ([]common.Address, error) { + return _BatchAuthenticator.Contract.GetGuardians(&_BatchAuthenticator.CallOpts) +} + +// GetGuardians is a free data retrieval call binding the contract method 0x0665f04b. +// +// Solidity: function getGuardians() view returns(address[]) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) GetGuardians() ([]common.Address, error) { + return _BatchAuthenticator.Contract.GetGuardians(&_BatchAuthenticator.CallOpts) +} + +// GuardianCount is a free data retrieval call binding the contract method 0x54387ad7. +// +// Solidity: function guardianCount() view returns(uint256) +func (_BatchAuthenticator *BatchAuthenticatorCaller) GuardianCount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "guardianCount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GuardianCount is a free data retrieval call binding the contract method 0x54387ad7. +// +// Solidity: function guardianCount() view returns(uint256) +func (_BatchAuthenticator *BatchAuthenticatorSession) GuardianCount() (*big.Int, error) { + return _BatchAuthenticator.Contract.GuardianCount(&_BatchAuthenticator.CallOpts) +} + +// GuardianCount is a free data retrieval call binding the contract method 0x54387ad7. +// +// Solidity: function guardianCount() view returns(uint256) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) GuardianCount() (*big.Int, error) { + return _BatchAuthenticator.Contract.GuardianCount(&_BatchAuthenticator.CallOpts) +} + +// InitVersion is a free data retrieval call binding the contract method 0x38d38c97. +// +// Solidity: function initVersion() view returns(uint8) +func (_BatchAuthenticator *BatchAuthenticatorCaller) InitVersion(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "initVersion") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// InitVersion is a free data retrieval call binding the contract method 0x38d38c97. +// +// Solidity: function initVersion() view returns(uint8) +func (_BatchAuthenticator *BatchAuthenticatorSession) InitVersion() (uint8, error) { + return _BatchAuthenticator.Contract.InitVersion(&_BatchAuthenticator.CallOpts) +} + +// InitVersion is a free data retrieval call binding the contract method 0x38d38c97. +// +// Solidity: function initVersion() view returns(uint8) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) InitVersion() (uint8, error) { + return _BatchAuthenticator.Contract.InitVersion(&_BatchAuthenticator.CallOpts) +} + +// IsGuardian is a free data retrieval call binding the contract method 0x0c68ba21. +// +// Solidity: function isGuardian(address account) view returns(bool) +func (_BatchAuthenticator *BatchAuthenticatorCaller) IsGuardian(opts *bind.CallOpts, account common.Address) (bool, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "isGuardian", account) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsGuardian is a free data retrieval call binding the contract method 0x0c68ba21. +// +// Solidity: function isGuardian(address account) view returns(bool) +func (_BatchAuthenticator *BatchAuthenticatorSession) IsGuardian(account common.Address) (bool, error) { + return _BatchAuthenticator.Contract.IsGuardian(&_BatchAuthenticator.CallOpts, account) +} + +// IsGuardian is a free data retrieval call binding the contract method 0x0c68ba21. +// +// Solidity: function isGuardian(address account) view returns(bool) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) IsGuardian(account common.Address) (bool, error) { + return _BatchAuthenticator.Contract.IsGuardian(&_BatchAuthenticator.CallOpts, account) +} + +// NitroValidator is a free data retrieval call binding the contract method 0x1b076a4c. +// +// Solidity: function nitroValidator() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCaller) NitroValidator(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "nitroValidator") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// NitroValidator is a free data retrieval call binding the contract method 0x1b076a4c. +// +// Solidity: function nitroValidator() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorSession) NitroValidator() (common.Address, error) { + return _BatchAuthenticator.Contract.NitroValidator(&_BatchAuthenticator.CallOpts) +} + +// NitroValidator is a free data retrieval call binding the contract method 0x1b076a4c. +// +// Solidity: function nitroValidator() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) NitroValidator() (common.Address, error) { + return _BatchAuthenticator.Contract.NitroValidator(&_BatchAuthenticator.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorSession) Owner() (common.Address, error) { + return _BatchAuthenticator.Contract.Owner(&_BatchAuthenticator.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) Owner() (common.Address, error) { + return _BatchAuthenticator.Contract.Owner(&_BatchAuthenticator.CallOpts) +} + +// PendingOwner is a free data retrieval call binding the contract method 0xe30c3978. +// +// Solidity: function pendingOwner() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCaller) PendingOwner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "pendingOwner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// PendingOwner is a free data retrieval call binding the contract method 0xe30c3978. +// +// Solidity: function pendingOwner() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorSession) PendingOwner() (common.Address, error) { + return _BatchAuthenticator.Contract.PendingOwner(&_BatchAuthenticator.CallOpts) +} + +// PendingOwner is a free data retrieval call binding the contract method 0xe30c3978. +// +// Solidity: function pendingOwner() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) PendingOwner() (common.Address, error) { + return _BatchAuthenticator.Contract.PendingOwner(&_BatchAuthenticator.CallOpts) +} + +// ProxyAdmin is a free data retrieval call binding the contract method 0x3e47158c. +// +// Solidity: function proxyAdmin() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCaller) ProxyAdmin(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "proxyAdmin") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// ProxyAdmin is a free data retrieval call binding the contract method 0x3e47158c. +// +// Solidity: function proxyAdmin() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorSession) ProxyAdmin() (common.Address, error) { + return _BatchAuthenticator.Contract.ProxyAdmin(&_BatchAuthenticator.CallOpts) +} + +// ProxyAdmin is a free data retrieval call binding the contract method 0x3e47158c. +// +// Solidity: function proxyAdmin() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) ProxyAdmin() (common.Address, error) { + return _BatchAuthenticator.Contract.ProxyAdmin(&_BatchAuthenticator.CallOpts) +} + +// ProxyAdminOwner is a free data retrieval call binding the contract method 0xdad544e0. +// +// Solidity: function proxyAdminOwner() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCaller) ProxyAdminOwner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "proxyAdminOwner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// ProxyAdminOwner is a free data retrieval call binding the contract method 0xdad544e0. +// +// Solidity: function proxyAdminOwner() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorSession) ProxyAdminOwner() (common.Address, error) { + return _BatchAuthenticator.Contract.ProxyAdminOwner(&_BatchAuthenticator.CallOpts) +} + +// ProxyAdminOwner is a free data retrieval call binding the contract method 0xdad544e0. +// +// Solidity: function proxyAdminOwner() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) ProxyAdminOwner() (common.Address, error) { + return _BatchAuthenticator.Contract.ProxyAdminOwner(&_BatchAuthenticator.CallOpts) +} + +// SystemConfig is a free data retrieval call binding the contract method 0x33d7e2bd. +// +// Solidity: function systemConfig() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCaller) SystemConfig(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "systemConfig") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// SystemConfig is a free data retrieval call binding the contract method 0x33d7e2bd. +// +// Solidity: function systemConfig() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorSession) SystemConfig() (common.Address, error) { + return _BatchAuthenticator.Contract.SystemConfig(&_BatchAuthenticator.CallOpts) +} + +// SystemConfig is a free data retrieval call binding the contract method 0x33d7e2bd. +// +// Solidity: function systemConfig() view returns(address) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) SystemConfig() (common.Address, error) { + return _BatchAuthenticator.Contract.SystemConfig(&_BatchAuthenticator.CallOpts) +} + +// Version is a free data retrieval call binding the contract method 0x54fd4d50. +// +// Solidity: function version() view returns(string) +func (_BatchAuthenticator *BatchAuthenticatorCaller) Version(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _BatchAuthenticator.contract.Call(opts, &out, "version") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Version is a free data retrieval call binding the contract method 0x54fd4d50. +// +// Solidity: function version() view returns(string) +func (_BatchAuthenticator *BatchAuthenticatorSession) Version() (string, error) { + return _BatchAuthenticator.Contract.Version(&_BatchAuthenticator.CallOpts) +} + +// Version is a free data retrieval call binding the contract method 0x54fd4d50. +// +// Solidity: function version() view returns(string) +func (_BatchAuthenticator *BatchAuthenticatorCallerSession) Version() (string, error) { + return _BatchAuthenticator.Contract.Version(&_BatchAuthenticator.CallOpts) +} + +// AcceptOwnership is a paid mutator transaction binding the contract method 0x79ba5097. +// +// Solidity: function acceptOwnership() returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BatchAuthenticator.contract.Transact(opts, "acceptOwnership") +} + +// AcceptOwnership is a paid mutator transaction binding the contract method 0x79ba5097. +// +// Solidity: function acceptOwnership() returns() +func (_BatchAuthenticator *BatchAuthenticatorSession) AcceptOwnership() (*types.Transaction, error) { + return _BatchAuthenticator.Contract.AcceptOwnership(&_BatchAuthenticator.TransactOpts) +} + +// AcceptOwnership is a paid mutator transaction binding the contract method 0x79ba5097. +// +// Solidity: function acceptOwnership() returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _BatchAuthenticator.Contract.AcceptOwnership(&_BatchAuthenticator.TransactOpts) +} + +// AddGuardian is a paid mutator transaction binding the contract method 0xa526d83b. +// +// Solidity: function addGuardian(address guardian) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactor) AddGuardian(opts *bind.TransactOpts, guardian common.Address) (*types.Transaction, error) { + return _BatchAuthenticator.contract.Transact(opts, "addGuardian", guardian) +} + +// AddGuardian is a paid mutator transaction binding the contract method 0xa526d83b. +// +// Solidity: function addGuardian(address guardian) returns() +func (_BatchAuthenticator *BatchAuthenticatorSession) AddGuardian(guardian common.Address) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.AddGuardian(&_BatchAuthenticator.TransactOpts, guardian) +} + +// AddGuardian is a paid mutator transaction binding the contract method 0xa526d83b. +// +// Solidity: function addGuardian(address guardian) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactorSession) AddGuardian(guardian common.Address) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.AddGuardian(&_BatchAuthenticator.TransactOpts, guardian) +} + +// AuthenticateBatchInfo is a paid mutator transaction binding the contract method 0xfc619e41. +// +// Solidity: function authenticateBatchInfo(bytes32 _commitment, bytes _signature) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactor) AuthenticateBatchInfo(opts *bind.TransactOpts, _commitment [32]byte, _signature []byte) (*types.Transaction, error) { + return _BatchAuthenticator.contract.Transact(opts, "authenticateBatchInfo", _commitment, _signature) +} + +// AuthenticateBatchInfo is a paid mutator transaction binding the contract method 0xfc619e41. +// +// Solidity: function authenticateBatchInfo(bytes32 _commitment, bytes _signature) returns() +func (_BatchAuthenticator *BatchAuthenticatorSession) AuthenticateBatchInfo(_commitment [32]byte, _signature []byte) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.AuthenticateBatchInfo(&_BatchAuthenticator.TransactOpts, _commitment, _signature) +} + +// AuthenticateBatchInfo is a paid mutator transaction binding the contract method 0xfc619e41. +// +// Solidity: function authenticateBatchInfo(bytes32 _commitment, bytes _signature) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactorSession) AuthenticateBatchInfo(_commitment [32]byte, _signature []byte) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.AuthenticateBatchInfo(&_BatchAuthenticator.TransactOpts, _commitment, _signature) +} + +// Initialize is a paid mutator transaction binding the contract method 0xfc5b5fda. +// +// Solidity: function initialize(address _espressoTEEVerifier, address _espressoBatcher, address _systemConfig, address _owner, bool _activeIsEspresso) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactor) Initialize(opts *bind.TransactOpts, _espressoTEEVerifier common.Address, _espressoBatcher common.Address, _systemConfig common.Address, _owner common.Address, _activeIsEspresso bool) (*types.Transaction, error) { + return _BatchAuthenticator.contract.Transact(opts, "initialize", _espressoTEEVerifier, _espressoBatcher, _systemConfig, _owner, _activeIsEspresso) +} + +// Initialize is a paid mutator transaction binding the contract method 0xfc5b5fda. +// +// Solidity: function initialize(address _espressoTEEVerifier, address _espressoBatcher, address _systemConfig, address _owner, bool _activeIsEspresso) returns() +func (_BatchAuthenticator *BatchAuthenticatorSession) Initialize(_espressoTEEVerifier common.Address, _espressoBatcher common.Address, _systemConfig common.Address, _owner common.Address, _activeIsEspresso bool) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.Initialize(&_BatchAuthenticator.TransactOpts, _espressoTEEVerifier, _espressoBatcher, _systemConfig, _owner, _activeIsEspresso) +} + +// Initialize is a paid mutator transaction binding the contract method 0xfc5b5fda. +// +// Solidity: function initialize(address _espressoTEEVerifier, address _espressoBatcher, address _systemConfig, address _owner, bool _activeIsEspresso) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactorSession) Initialize(_espressoTEEVerifier common.Address, _espressoBatcher common.Address, _systemConfig common.Address, _owner common.Address, _activeIsEspresso bool) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.Initialize(&_BatchAuthenticator.TransactOpts, _espressoTEEVerifier, _espressoBatcher, _systemConfig, _owner, _activeIsEspresso) +} + +// RegisterSigner is a paid mutator transaction binding the contract method 0xba58e82a. +// +// Solidity: function registerSigner(bytes _verificationData, bytes _data) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactor) RegisterSigner(opts *bind.TransactOpts, _verificationData []byte, _data []byte) (*types.Transaction, error) { + return _BatchAuthenticator.contract.Transact(opts, "registerSigner", _verificationData, _data) +} + +// RegisterSigner is a paid mutator transaction binding the contract method 0xba58e82a. +// +// Solidity: function registerSigner(bytes _verificationData, bytes _data) returns() +func (_BatchAuthenticator *BatchAuthenticatorSession) RegisterSigner(_verificationData []byte, _data []byte) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.RegisterSigner(&_BatchAuthenticator.TransactOpts, _verificationData, _data) +} + +// RegisterSigner is a paid mutator transaction binding the contract method 0xba58e82a. +// +// Solidity: function registerSigner(bytes _verificationData, bytes _data) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactorSession) RegisterSigner(_verificationData []byte, _data []byte) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.RegisterSigner(&_BatchAuthenticator.TransactOpts, _verificationData, _data) +} + +// RemoveGuardian is a paid mutator transaction binding the contract method 0x71404156. +// +// Solidity: function removeGuardian(address guardian) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactor) RemoveGuardian(opts *bind.TransactOpts, guardian common.Address) (*types.Transaction, error) { + return _BatchAuthenticator.contract.Transact(opts, "removeGuardian", guardian) +} + +// RemoveGuardian is a paid mutator transaction binding the contract method 0x71404156. +// +// Solidity: function removeGuardian(address guardian) returns() +func (_BatchAuthenticator *BatchAuthenticatorSession) RemoveGuardian(guardian common.Address) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.RemoveGuardian(&_BatchAuthenticator.TransactOpts, guardian) +} + +// RemoveGuardian is a paid mutator transaction binding the contract method 0x71404156. +// +// Solidity: function removeGuardian(address guardian) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactorSession) RemoveGuardian(guardian common.Address) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.RemoveGuardian(&_BatchAuthenticator.TransactOpts, guardian) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BatchAuthenticator.contract.Transact(opts, "renounceOwnership") +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_BatchAuthenticator *BatchAuthenticatorSession) RenounceOwnership() (*types.Transaction, error) { + return _BatchAuthenticator.Contract.RenounceOwnership(&_BatchAuthenticator.TransactOpts) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactorSession) RenounceOwnership() (*types.Transaction, error) { + return _BatchAuthenticator.Contract.RenounceOwnership(&_BatchAuthenticator.TransactOpts) +} + +// SetActiveIsEspresso is a paid mutator transaction binding the contract method 0x6c076871. +// +// Solidity: function setActiveIsEspresso(bool _desired) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactor) SetActiveIsEspresso(opts *bind.TransactOpts, _desired bool) (*types.Transaction, error) { + return _BatchAuthenticator.contract.Transact(opts, "setActiveIsEspresso", _desired) +} + +// SetActiveIsEspresso is a paid mutator transaction binding the contract method 0x6c076871. +// +// Solidity: function setActiveIsEspresso(bool _desired) returns() +func (_BatchAuthenticator *BatchAuthenticatorSession) SetActiveIsEspresso(_desired bool) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.SetActiveIsEspresso(&_BatchAuthenticator.TransactOpts, _desired) +} + +// SetActiveIsEspresso is a paid mutator transaction binding the contract method 0x6c076871. +// +// Solidity: function setActiveIsEspresso(bool _desired) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactorSession) SetActiveIsEspresso(_desired bool) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.SetActiveIsEspresso(&_BatchAuthenticator.TransactOpts, _desired) +} + +// SetEspressoBatcher is a paid mutator transaction binding the contract method 0x2ce53247. +// +// Solidity: function setEspressoBatcher(address _newEspressoBatcher) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactor) SetEspressoBatcher(opts *bind.TransactOpts, _newEspressoBatcher common.Address) (*types.Transaction, error) { + return _BatchAuthenticator.contract.Transact(opts, "setEspressoBatcher", _newEspressoBatcher) +} + +// SetEspressoBatcher is a paid mutator transaction binding the contract method 0x2ce53247. +// +// Solidity: function setEspressoBatcher(address _newEspressoBatcher) returns() +func (_BatchAuthenticator *BatchAuthenticatorSession) SetEspressoBatcher(_newEspressoBatcher common.Address) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.SetEspressoBatcher(&_BatchAuthenticator.TransactOpts, _newEspressoBatcher) +} + +// SetEspressoBatcher is a paid mutator transaction binding the contract method 0x2ce53247. +// +// Solidity: function setEspressoBatcher(address _newEspressoBatcher) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactorSession) SetEspressoBatcher(_newEspressoBatcher common.Address) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.SetEspressoBatcher(&_BatchAuthenticator.TransactOpts, _newEspressoBatcher) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { + return _BatchAuthenticator.contract.Transact(opts, "transferOwnership", newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_BatchAuthenticator *BatchAuthenticatorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.TransferOwnership(&_BatchAuthenticator.TransactOpts, newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_BatchAuthenticator *BatchAuthenticatorTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _BatchAuthenticator.Contract.TransferOwnership(&_BatchAuthenticator.TransactOpts, newOwner) +} + +// BatchAuthenticatorBatchInfoAuthenticatedIterator is returned from FilterBatchInfoAuthenticated and is used to iterate over the raw logs and unpacked data for BatchInfoAuthenticated events raised by the BatchAuthenticator contract. +type BatchAuthenticatorBatchInfoAuthenticatedIterator struct { + Event *BatchAuthenticatorBatchInfoAuthenticated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchAuthenticatorBatchInfoAuthenticatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorBatchInfoAuthenticated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorBatchInfoAuthenticated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchAuthenticatorBatchInfoAuthenticatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchAuthenticatorBatchInfoAuthenticatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchAuthenticatorBatchInfoAuthenticated represents a BatchInfoAuthenticated event raised by the BatchAuthenticator contract. +type BatchAuthenticatorBatchInfoAuthenticated struct { + Commitment [32]byte + Caller common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterBatchInfoAuthenticated is a free log retrieval operation binding the contract event 0x731978a77d438b0ea35a9034fb28d9cf9372e1649f18c213110adcfab65c5c5c. +// +// Solidity: event BatchInfoAuthenticated(bytes32 commitment, address indexed caller) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) FilterBatchInfoAuthenticated(opts *bind.FilterOpts, caller []common.Address) (*BatchAuthenticatorBatchInfoAuthenticatedIterator, error) { + + var callerRule []interface{} + for _, callerItem := range caller { + callerRule = append(callerRule, callerItem) + } + + logs, sub, err := _BatchAuthenticator.contract.FilterLogs(opts, "BatchInfoAuthenticated", callerRule) + if err != nil { + return nil, err + } + return &BatchAuthenticatorBatchInfoAuthenticatedIterator{contract: _BatchAuthenticator.contract, event: "BatchInfoAuthenticated", logs: logs, sub: sub}, nil +} + +// WatchBatchInfoAuthenticated is a free log subscription operation binding the contract event 0x731978a77d438b0ea35a9034fb28d9cf9372e1649f18c213110adcfab65c5c5c. +// +// Solidity: event BatchInfoAuthenticated(bytes32 commitment, address indexed caller) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) WatchBatchInfoAuthenticated(opts *bind.WatchOpts, sink chan<- *BatchAuthenticatorBatchInfoAuthenticated, caller []common.Address) (event.Subscription, error) { + + var callerRule []interface{} + for _, callerItem := range caller { + callerRule = append(callerRule, callerItem) + } + + logs, sub, err := _BatchAuthenticator.contract.WatchLogs(opts, "BatchInfoAuthenticated", callerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchAuthenticatorBatchInfoAuthenticated) + if err := _BatchAuthenticator.contract.UnpackLog(event, "BatchInfoAuthenticated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseBatchInfoAuthenticated is a log parse operation binding the contract event 0x731978a77d438b0ea35a9034fb28d9cf9372e1649f18c213110adcfab65c5c5c. +// +// Solidity: event BatchInfoAuthenticated(bytes32 commitment, address indexed caller) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) ParseBatchInfoAuthenticated(log types.Log) (*BatchAuthenticatorBatchInfoAuthenticated, error) { + event := new(BatchAuthenticatorBatchInfoAuthenticated) + if err := _BatchAuthenticator.contract.UnpackLog(event, "BatchInfoAuthenticated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchAuthenticatorBatcherSwitchedIterator is returned from FilterBatcherSwitched and is used to iterate over the raw logs and unpacked data for BatcherSwitched events raised by the BatchAuthenticator contract. +type BatchAuthenticatorBatcherSwitchedIterator struct { + Event *BatchAuthenticatorBatcherSwitched // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchAuthenticatorBatcherSwitchedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorBatcherSwitched) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorBatcherSwitched) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchAuthenticatorBatcherSwitchedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchAuthenticatorBatcherSwitchedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchAuthenticatorBatcherSwitched represents a BatcherSwitched event raised by the BatchAuthenticator contract. +type BatchAuthenticatorBatcherSwitched struct { + ActiveIsEspresso bool + Raw types.Log // Blockchain specific contextual infos +} + +// FilterBatcherSwitched is a free log retrieval operation binding the contract event 0xb957d7fc29e5974594db2f2e132076d52f42c0734eae05fd5ea080d1ba175ad3. +// +// Solidity: event BatcherSwitched(bool indexed activeIsEspresso) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) FilterBatcherSwitched(opts *bind.FilterOpts, activeIsEspresso []bool) (*BatchAuthenticatorBatcherSwitchedIterator, error) { + + var activeIsEspressoRule []interface{} + for _, activeIsEspressoItem := range activeIsEspresso { + activeIsEspressoRule = append(activeIsEspressoRule, activeIsEspressoItem) + } + + logs, sub, err := _BatchAuthenticator.contract.FilterLogs(opts, "BatcherSwitched", activeIsEspressoRule) + if err != nil { + return nil, err + } + return &BatchAuthenticatorBatcherSwitchedIterator{contract: _BatchAuthenticator.contract, event: "BatcherSwitched", logs: logs, sub: sub}, nil +} + +// WatchBatcherSwitched is a free log subscription operation binding the contract event 0xb957d7fc29e5974594db2f2e132076d52f42c0734eae05fd5ea080d1ba175ad3. +// +// Solidity: event BatcherSwitched(bool indexed activeIsEspresso) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) WatchBatcherSwitched(opts *bind.WatchOpts, sink chan<- *BatchAuthenticatorBatcherSwitched, activeIsEspresso []bool) (event.Subscription, error) { + + var activeIsEspressoRule []interface{} + for _, activeIsEspressoItem := range activeIsEspresso { + activeIsEspressoRule = append(activeIsEspressoRule, activeIsEspressoItem) + } + + logs, sub, err := _BatchAuthenticator.contract.WatchLogs(opts, "BatcherSwitched", activeIsEspressoRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchAuthenticatorBatcherSwitched) + if err := _BatchAuthenticator.contract.UnpackLog(event, "BatcherSwitched", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseBatcherSwitched is a log parse operation binding the contract event 0xb957d7fc29e5974594db2f2e132076d52f42c0734eae05fd5ea080d1ba175ad3. +// +// Solidity: event BatcherSwitched(bool indexed activeIsEspresso) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) ParseBatcherSwitched(log types.Log) (*BatchAuthenticatorBatcherSwitched, error) { + event := new(BatchAuthenticatorBatcherSwitched) + if err := _BatchAuthenticator.contract.UnpackLog(event, "BatcherSwitched", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchAuthenticatorEspressoBatcherUpdatedIterator is returned from FilterEspressoBatcherUpdated and is used to iterate over the raw logs and unpacked data for EspressoBatcherUpdated events raised by the BatchAuthenticator contract. +type BatchAuthenticatorEspressoBatcherUpdatedIterator struct { + Event *BatchAuthenticatorEspressoBatcherUpdated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchAuthenticatorEspressoBatcherUpdatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorEspressoBatcherUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorEspressoBatcherUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchAuthenticatorEspressoBatcherUpdatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchAuthenticatorEspressoBatcherUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchAuthenticatorEspressoBatcherUpdated represents a EspressoBatcherUpdated event raised by the BatchAuthenticator contract. +type BatchAuthenticatorEspressoBatcherUpdated struct { + OldEspressoBatcher common.Address + NewEspressoBatcher common.Address + FromBlock uint64 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterEspressoBatcherUpdated is a free log retrieval operation binding the contract event 0xcf8f6f62babb05dd1d159c090ad8429ee0df72e16c82701004a5405f908cb0f5. +// +// Solidity: event EspressoBatcherUpdated(address indexed oldEspressoBatcher, address indexed newEspressoBatcher, uint64 indexed fromBlock) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) FilterEspressoBatcherUpdated(opts *bind.FilterOpts, oldEspressoBatcher []common.Address, newEspressoBatcher []common.Address, fromBlock []uint64) (*BatchAuthenticatorEspressoBatcherUpdatedIterator, error) { + + var oldEspressoBatcherRule []interface{} + for _, oldEspressoBatcherItem := range oldEspressoBatcher { + oldEspressoBatcherRule = append(oldEspressoBatcherRule, oldEspressoBatcherItem) + } + var newEspressoBatcherRule []interface{} + for _, newEspressoBatcherItem := range newEspressoBatcher { + newEspressoBatcherRule = append(newEspressoBatcherRule, newEspressoBatcherItem) + } + var fromBlockRule []interface{} + for _, fromBlockItem := range fromBlock { + fromBlockRule = append(fromBlockRule, fromBlockItem) + } + + logs, sub, err := _BatchAuthenticator.contract.FilterLogs(opts, "EspressoBatcherUpdated", oldEspressoBatcherRule, newEspressoBatcherRule, fromBlockRule) + if err != nil { + return nil, err + } + return &BatchAuthenticatorEspressoBatcherUpdatedIterator{contract: _BatchAuthenticator.contract, event: "EspressoBatcherUpdated", logs: logs, sub: sub}, nil +} + +// WatchEspressoBatcherUpdated is a free log subscription operation binding the contract event 0xcf8f6f62babb05dd1d159c090ad8429ee0df72e16c82701004a5405f908cb0f5. +// +// Solidity: event EspressoBatcherUpdated(address indexed oldEspressoBatcher, address indexed newEspressoBatcher, uint64 indexed fromBlock) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) WatchEspressoBatcherUpdated(opts *bind.WatchOpts, sink chan<- *BatchAuthenticatorEspressoBatcherUpdated, oldEspressoBatcher []common.Address, newEspressoBatcher []common.Address, fromBlock []uint64) (event.Subscription, error) { + + var oldEspressoBatcherRule []interface{} + for _, oldEspressoBatcherItem := range oldEspressoBatcher { + oldEspressoBatcherRule = append(oldEspressoBatcherRule, oldEspressoBatcherItem) + } + var newEspressoBatcherRule []interface{} + for _, newEspressoBatcherItem := range newEspressoBatcher { + newEspressoBatcherRule = append(newEspressoBatcherRule, newEspressoBatcherItem) + } + var fromBlockRule []interface{} + for _, fromBlockItem := range fromBlock { + fromBlockRule = append(fromBlockRule, fromBlockItem) + } + + logs, sub, err := _BatchAuthenticator.contract.WatchLogs(opts, "EspressoBatcherUpdated", oldEspressoBatcherRule, newEspressoBatcherRule, fromBlockRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchAuthenticatorEspressoBatcherUpdated) + if err := _BatchAuthenticator.contract.UnpackLog(event, "EspressoBatcherUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseEspressoBatcherUpdated is a log parse operation binding the contract event 0xcf8f6f62babb05dd1d159c090ad8429ee0df72e16c82701004a5405f908cb0f5. +// +// Solidity: event EspressoBatcherUpdated(address indexed oldEspressoBatcher, address indexed newEspressoBatcher, uint64 indexed fromBlock) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) ParseEspressoBatcherUpdated(log types.Log) (*BatchAuthenticatorEspressoBatcherUpdated, error) { + event := new(BatchAuthenticatorEspressoBatcherUpdated) + if err := _BatchAuthenticator.contract.UnpackLog(event, "EspressoBatcherUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchAuthenticatorGuardianAddedIterator is returned from FilterGuardianAdded and is used to iterate over the raw logs and unpacked data for GuardianAdded events raised by the BatchAuthenticator contract. +type BatchAuthenticatorGuardianAddedIterator struct { + Event *BatchAuthenticatorGuardianAdded // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchAuthenticatorGuardianAddedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorGuardianAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorGuardianAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchAuthenticatorGuardianAddedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchAuthenticatorGuardianAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchAuthenticatorGuardianAdded represents a GuardianAdded event raised by the BatchAuthenticator contract. +type BatchAuthenticatorGuardianAdded struct { + Guardian common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterGuardianAdded is a free log retrieval operation binding the contract event 0x038596bb31e2e7d3d9f184d4c98b310103f6d7f5830e5eec32bffe6f1728f969. +// +// Solidity: event GuardianAdded(address indexed guardian) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) FilterGuardianAdded(opts *bind.FilterOpts, guardian []common.Address) (*BatchAuthenticatorGuardianAddedIterator, error) { + + var guardianRule []interface{} + for _, guardianItem := range guardian { + guardianRule = append(guardianRule, guardianItem) + } + + logs, sub, err := _BatchAuthenticator.contract.FilterLogs(opts, "GuardianAdded", guardianRule) + if err != nil { + return nil, err + } + return &BatchAuthenticatorGuardianAddedIterator{contract: _BatchAuthenticator.contract, event: "GuardianAdded", logs: logs, sub: sub}, nil +} + +// WatchGuardianAdded is a free log subscription operation binding the contract event 0x038596bb31e2e7d3d9f184d4c98b310103f6d7f5830e5eec32bffe6f1728f969. +// +// Solidity: event GuardianAdded(address indexed guardian) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) WatchGuardianAdded(opts *bind.WatchOpts, sink chan<- *BatchAuthenticatorGuardianAdded, guardian []common.Address) (event.Subscription, error) { + + var guardianRule []interface{} + for _, guardianItem := range guardian { + guardianRule = append(guardianRule, guardianItem) + } + + logs, sub, err := _BatchAuthenticator.contract.WatchLogs(opts, "GuardianAdded", guardianRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchAuthenticatorGuardianAdded) + if err := _BatchAuthenticator.contract.UnpackLog(event, "GuardianAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseGuardianAdded is a log parse operation binding the contract event 0x038596bb31e2e7d3d9f184d4c98b310103f6d7f5830e5eec32bffe6f1728f969. +// +// Solidity: event GuardianAdded(address indexed guardian) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) ParseGuardianAdded(log types.Log) (*BatchAuthenticatorGuardianAdded, error) { + event := new(BatchAuthenticatorGuardianAdded) + if err := _BatchAuthenticator.contract.UnpackLog(event, "GuardianAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchAuthenticatorGuardianRemovedIterator is returned from FilterGuardianRemoved and is used to iterate over the raw logs and unpacked data for GuardianRemoved events raised by the BatchAuthenticator contract. +type BatchAuthenticatorGuardianRemovedIterator struct { + Event *BatchAuthenticatorGuardianRemoved // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchAuthenticatorGuardianRemovedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorGuardianRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorGuardianRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchAuthenticatorGuardianRemovedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchAuthenticatorGuardianRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchAuthenticatorGuardianRemoved represents a GuardianRemoved event raised by the BatchAuthenticator contract. +type BatchAuthenticatorGuardianRemoved struct { + Guardian common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterGuardianRemoved is a free log retrieval operation binding the contract event 0xb8107d0c6b40be480ce3172ee66ba6d64b71f6b1685a851340036e6e2e3e3c52. +// +// Solidity: event GuardianRemoved(address indexed guardian) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) FilterGuardianRemoved(opts *bind.FilterOpts, guardian []common.Address) (*BatchAuthenticatorGuardianRemovedIterator, error) { + + var guardianRule []interface{} + for _, guardianItem := range guardian { + guardianRule = append(guardianRule, guardianItem) + } + + logs, sub, err := _BatchAuthenticator.contract.FilterLogs(opts, "GuardianRemoved", guardianRule) + if err != nil { + return nil, err + } + return &BatchAuthenticatorGuardianRemovedIterator{contract: _BatchAuthenticator.contract, event: "GuardianRemoved", logs: logs, sub: sub}, nil +} + +// WatchGuardianRemoved is a free log subscription operation binding the contract event 0xb8107d0c6b40be480ce3172ee66ba6d64b71f6b1685a851340036e6e2e3e3c52. +// +// Solidity: event GuardianRemoved(address indexed guardian) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) WatchGuardianRemoved(opts *bind.WatchOpts, sink chan<- *BatchAuthenticatorGuardianRemoved, guardian []common.Address) (event.Subscription, error) { + + var guardianRule []interface{} + for _, guardianItem := range guardian { + guardianRule = append(guardianRule, guardianItem) + } + + logs, sub, err := _BatchAuthenticator.contract.WatchLogs(opts, "GuardianRemoved", guardianRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchAuthenticatorGuardianRemoved) + if err := _BatchAuthenticator.contract.UnpackLog(event, "GuardianRemoved", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseGuardianRemoved is a log parse operation binding the contract event 0xb8107d0c6b40be480ce3172ee66ba6d64b71f6b1685a851340036e6e2e3e3c52. +// +// Solidity: event GuardianRemoved(address indexed guardian) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) ParseGuardianRemoved(log types.Log) (*BatchAuthenticatorGuardianRemoved, error) { + event := new(BatchAuthenticatorGuardianRemoved) + if err := _BatchAuthenticator.contract.UnpackLog(event, "GuardianRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchAuthenticatorInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the BatchAuthenticator contract. +type BatchAuthenticatorInitializedIterator struct { + Event *BatchAuthenticatorInitialized // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchAuthenticatorInitializedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchAuthenticatorInitializedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchAuthenticatorInitializedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchAuthenticatorInitialized represents a Initialized event raised by the BatchAuthenticator contract. +type BatchAuthenticatorInitialized struct { + Version uint64 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInitialized is a free log retrieval operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) FilterInitialized(opts *bind.FilterOpts) (*BatchAuthenticatorInitializedIterator, error) { + + logs, sub, err := _BatchAuthenticator.contract.FilterLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return &BatchAuthenticatorInitializedIterator{contract: _BatchAuthenticator.contract, event: "Initialized", logs: logs, sub: sub}, nil +} + +// WatchInitialized is a free log subscription operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *BatchAuthenticatorInitialized) (event.Subscription, error) { + + logs, sub, err := _BatchAuthenticator.contract.WatchLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchAuthenticatorInitialized) + if err := _BatchAuthenticator.contract.UnpackLog(event, "Initialized", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseInitialized is a log parse operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) ParseInitialized(log types.Log) (*BatchAuthenticatorInitialized, error) { + event := new(BatchAuthenticatorInitialized) + if err := _BatchAuthenticator.contract.UnpackLog(event, "Initialized", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchAuthenticatorOwnershipTransferStartedIterator is returned from FilterOwnershipTransferStarted and is used to iterate over the raw logs and unpacked data for OwnershipTransferStarted events raised by the BatchAuthenticator contract. +type BatchAuthenticatorOwnershipTransferStartedIterator struct { + Event *BatchAuthenticatorOwnershipTransferStarted // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchAuthenticatorOwnershipTransferStartedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorOwnershipTransferStarted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorOwnershipTransferStarted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchAuthenticatorOwnershipTransferStartedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchAuthenticatorOwnershipTransferStartedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchAuthenticatorOwnershipTransferStarted represents a OwnershipTransferStarted event raised by the BatchAuthenticator contract. +type BatchAuthenticatorOwnershipTransferStarted struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferStarted is a free log retrieval operation binding the contract event 0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700. +// +// Solidity: event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) FilterOwnershipTransferStarted(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*BatchAuthenticatorOwnershipTransferStartedIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _BatchAuthenticator.contract.FilterLogs(opts, "OwnershipTransferStarted", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &BatchAuthenticatorOwnershipTransferStartedIterator{contract: _BatchAuthenticator.contract, event: "OwnershipTransferStarted", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferStarted is a free log subscription operation binding the contract event 0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700. +// +// Solidity: event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) WatchOwnershipTransferStarted(opts *bind.WatchOpts, sink chan<- *BatchAuthenticatorOwnershipTransferStarted, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _BatchAuthenticator.contract.WatchLogs(opts, "OwnershipTransferStarted", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchAuthenticatorOwnershipTransferStarted) + if err := _BatchAuthenticator.contract.UnpackLog(event, "OwnershipTransferStarted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferStarted is a log parse operation binding the contract event 0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700. +// +// Solidity: event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) ParseOwnershipTransferStarted(log types.Log) (*BatchAuthenticatorOwnershipTransferStarted, error) { + event := new(BatchAuthenticatorOwnershipTransferStarted) + if err := _BatchAuthenticator.contract.UnpackLog(event, "OwnershipTransferStarted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchAuthenticatorOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the BatchAuthenticator contract. +type BatchAuthenticatorOwnershipTransferredIterator struct { + Event *BatchAuthenticatorOwnershipTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchAuthenticatorOwnershipTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchAuthenticatorOwnershipTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchAuthenticatorOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchAuthenticatorOwnershipTransferred represents a OwnershipTransferred event raised by the BatchAuthenticator contract. +type BatchAuthenticatorOwnershipTransferred struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*BatchAuthenticatorOwnershipTransferredIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _BatchAuthenticator.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &BatchAuthenticatorOwnershipTransferredIterator{contract: _BatchAuthenticator.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *BatchAuthenticatorOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _BatchAuthenticator.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchAuthenticatorOwnershipTransferred) + if err := _BatchAuthenticator.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) ParseOwnershipTransferred(log types.Log) (*BatchAuthenticatorOwnershipTransferred, error) { + event := new(BatchAuthenticatorOwnershipTransferred) + if err := _BatchAuthenticator.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchAuthenticatorSignerRegistrationInitiatedIterator is returned from FilterSignerRegistrationInitiated and is used to iterate over the raw logs and unpacked data for SignerRegistrationInitiated events raised by the BatchAuthenticator contract. +type BatchAuthenticatorSignerRegistrationInitiatedIterator struct { + Event *BatchAuthenticatorSignerRegistrationInitiated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchAuthenticatorSignerRegistrationInitiatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorSignerRegistrationInitiated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchAuthenticatorSignerRegistrationInitiated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchAuthenticatorSignerRegistrationInitiatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchAuthenticatorSignerRegistrationInitiatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchAuthenticatorSignerRegistrationInitiated represents a SignerRegistrationInitiated event raised by the BatchAuthenticator contract. +type BatchAuthenticatorSignerRegistrationInitiated struct { + Caller common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterSignerRegistrationInitiated is a free log retrieval operation binding the contract event 0x665b016a0ac50d1280744eaaff1cf21254d0fd30e4c3987d291913c32163416c. +// +// Solidity: event SignerRegistrationInitiated(address indexed caller) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) FilterSignerRegistrationInitiated(opts *bind.FilterOpts, caller []common.Address) (*BatchAuthenticatorSignerRegistrationInitiatedIterator, error) { + + var callerRule []interface{} + for _, callerItem := range caller { + callerRule = append(callerRule, callerItem) + } + + logs, sub, err := _BatchAuthenticator.contract.FilterLogs(opts, "SignerRegistrationInitiated", callerRule) + if err != nil { + return nil, err + } + return &BatchAuthenticatorSignerRegistrationInitiatedIterator{contract: _BatchAuthenticator.contract, event: "SignerRegistrationInitiated", logs: logs, sub: sub}, nil +} + +// WatchSignerRegistrationInitiated is a free log subscription operation binding the contract event 0x665b016a0ac50d1280744eaaff1cf21254d0fd30e4c3987d291913c32163416c. +// +// Solidity: event SignerRegistrationInitiated(address indexed caller) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) WatchSignerRegistrationInitiated(opts *bind.WatchOpts, sink chan<- *BatchAuthenticatorSignerRegistrationInitiated, caller []common.Address) (event.Subscription, error) { + + var callerRule []interface{} + for _, callerItem := range caller { + callerRule = append(callerRule, callerItem) + } + + logs, sub, err := _BatchAuthenticator.contract.WatchLogs(opts, "SignerRegistrationInitiated", callerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchAuthenticatorSignerRegistrationInitiated) + if err := _BatchAuthenticator.contract.UnpackLog(event, "SignerRegistrationInitiated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseSignerRegistrationInitiated is a log parse operation binding the contract event 0x665b016a0ac50d1280744eaaff1cf21254d0fd30e4c3987d291913c32163416c. +// +// Solidity: event SignerRegistrationInitiated(address indexed caller) +func (_BatchAuthenticator *BatchAuthenticatorFilterer) ParseSignerRegistrationInitiated(log types.Log) (*BatchAuthenticatorSignerRegistrationInitiated, error) { + event := new(BatchAuthenticatorSignerRegistrationInitiated) + if err := _BatchAuthenticator.contract.UnpackLog(event, "SignerRegistrationInitiated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/op-batcher/batcher/driver.go b/op-batcher/batcher/driver.go index 6924440757e..728e03a9b51 100644 --- a/op-batcher/batcher/driver.go +++ b/op-batcher/batcher/driver.go @@ -51,6 +51,18 @@ type txRef struct { size int } +// newTxRef builds the txRef that identifies a batch submission across the txmgr +// queue and receipt handling. +func newTxRef(txdata txData, isCancel bool) txRef { + return txRef{ + id: txdata.ID(), + isCancel: isCancel, + isBlob: txdata.daType == DaTypeBlob, + daType: txdata.daType, + size: txdata.Len(), + } +} + func (r txRef) String() string { return r.string(func(id txID) string { return id.String() }) } @@ -124,6 +136,15 @@ type BatchSubmitter struct { throttleController *throttler.ThrottleController publishSignal chan pubInfo + + // authGroup tracks the fallback batcher's receipt-watcher goroutines (one + // per auth+batch pair) so the publishing loop can drain them via + // waitForAuthGroup before closing receiptsCh. New watchers are back-pressured + // (not hard-bounded) by the txmgr Queue: queue.Send blocks at + // MaxPendingTransactions, so watchers are created no faster than txs drain, + // though a slow receipts loop can briefly leave more than that parked on their + // final receiptsCh send. + authGroup sync.WaitGroup } // NewBatchSubmitter initializes the BatchSubmitter driver from a preconfigured DriverSetup @@ -516,6 +537,12 @@ func (l *BatchSubmitter) publishingLoop(ctx context.Context, wg *sync.WaitGroup, } } + // Wait for all in-flight fallback-auth submissions to complete to prevent + // new transactions being queued. No-op when the rollup is not configured + // with a BatchAuthenticator or when the EspressoTime hardfork has not + // activated. + l.waitForAuthGroup() + // We _must_ wait for all senders on receiptsCh to finish before we can close it. if err := txQueue.Wait(); err != nil { if !errors.Is(err, context.Canceled) { @@ -1022,6 +1049,9 @@ func (l *BatchSubmitter) sendTransaction(txdata txData, queue *txmgr.Queue[txRef type TxSender[T any] interface { Send(id T, candidate txmgr.TxCandidate, receiptCh chan txmgr.TxReceipt[T]) + // SendPair submits an ordered pair of transactions holding a single + // max-pending slot; see txmgr.Queue.SendPair. + SendPair(firstID T, first txmgr.TxCandidate, firstCh chan txmgr.TxReceipt[T], secondID T, second txmgr.TxCandidate, secondCh chan txmgr.TxReceipt[T]) } // sendTx uses the txmgr queue to send the given transaction candidate after setting its @@ -1035,7 +1065,14 @@ func (l *BatchSubmitter) sendTx(txdata txData, isCancel bool, candidate *txmgr.T candidate.GasLimit = floorDataGas } - queue.Send(txRef{id: txdata.ID(), isCancel: isCancel, isBlob: txdata.daType == DaTypeBlob, daType: txdata.daType, size: txdata.Len()}, *candidate, receiptsCh) + // Route through the fallback-auth path when a BatchAuthenticator is + // configured and the EspressoTime hardfork is active. Falls through to + // the upstream queue.Send path otherwise. + if l.dispatchAuthenticatedSendTx(txdata, isCancel, candidate, queue, receiptsCh) { + return + } + + queue.Send(newTxRef(txdata, isCancel), *candidate, receiptsCh) } func (l *BatchSubmitter) blobTxCandidate(data txData) (*txmgr.TxCandidate, error) { diff --git a/op-batcher/batcher/driver_test.go b/op-batcher/batcher/driver_test.go index 5ae3ade33fe..a1fd5e2b535 100644 --- a/op-batcher/batcher/driver_test.go +++ b/op-batcher/batcher/driver_test.go @@ -158,6 +158,11 @@ func (q *MockTxQueue) Send(ref txRef, candidate txmgr.TxCandidate, receiptCh cha q.m.Store(ref.id.String(), candidate) } +func (q *MockTxQueue) SendPair(firstRef txRef, first txmgr.TxCandidate, firstCh chan txmgr.TxReceipt[txRef], secondRef txRef, second txmgr.TxCandidate, secondCh chan txmgr.TxReceipt[txRef]) { + q.m.Store(firstRef.id.String(), first) + q.m.Store(secondRef.id.String(), second) +} + func (q *MockTxQueue) Load(id string) txmgr.TxCandidate { c, _ := q.m.Load(id) return c.(txmgr.TxCandidate) @@ -480,8 +485,7 @@ func TestBatchSubmitter_CriticalError(t *testing.T) { // ======= ALTDA TESTS ======= // fakeL1Client is just a dummy struct. All fault injection is done via the fakeTxMgr (which doesn't interact with this fakeL1Client). -type fakeL1Client struct { -} +type fakeL1Client struct{} func (f *fakeL1Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { if number == nil { diff --git a/op-batcher/batcher/espresso_fallback_gate.go b/op-batcher/batcher/espresso_fallback_gate.go new file mode 100644 index 00000000000..1157950bca5 --- /dev/null +++ b/op-batcher/batcher/espresso_fallback_gate.go @@ -0,0 +1,69 @@ +package batcher + +import ( + "context" + "fmt" + + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum-optimism/optimism/op-service/txmgr" +) + +// dispatchAuthenticatedSendTx routes sendTx through the fallback-batcher +// post-fork auth path, returning true when the tx has been handed off to +// authGroup. Returns false to mean "fall through to the upstream queue.Send +// path" — pre-fork operation and any cancel tx. +// +// The fallback batcher consults isFallbackAuthRequired to gate authentication +// behind the EspressoTime hardfork: pre-fork the verifier accepts plain +// sender-authenticated batches, and the BatchAuthenticator contract is +// irrelevant. +func (l *BatchSubmitter) dispatchAuthenticatedSendTx(txdata txData, isCancel bool, candidate *txmgr.TxCandidate, queue TxSender[txRef], receiptsCh chan txmgr.TxReceipt[txRef]) bool { + if isCancel { + return false + } + fallbackAuthRequired, err := l.isFallbackAuthRequired(l.killCtx) + if err != nil { + receiptsCh <- txmgr.TxReceipt[txRef]{ + ID: newTxRef(txdata, isCancel), + Err: fmt.Errorf("failed to evaluate fallback-auth gate: %w", err), + } + return true + } + if !fallbackAuthRequired { + return false + } + l.sendTxWithFallbackAuth(txdata, isCancel, candidate, queue, receiptsCh) + return true +} + +// isFallbackAuthRequired reports whether the fallback (non-TEE) batcher must +// route its batch txs through BatchAuthenticator.authenticateBatchInfo before +// posting to the BatchInbox. It returns false if the rollup config has a +// zero BatchAuthenticatorAddress, indicating that the BatchAuthenticator-based +// authentication path is not in use. +// +// The gate switches at plain Espresso activation (tip.Time >= EspressoTime), a full grace +// period before the verifier enforces event-based authentication. See +// derive.BatchAuthEnforcementDelaySecs for the full grace-period mechanism and why the +// batcher and verifier gates are offset. +func (l *BatchSubmitter) isFallbackAuthRequired(ctx context.Context) (bool, error) { + if l.RollupConfig.BatchAuthenticatorAddress == (common.Address{}) { + return false, nil + } + tip, err := l.l1Tip(ctx) + if err != nil { + return false, fmt.Errorf("failed to fetch L1 tip for fallback-auth gate: %w", err) + } + return l.RollupConfig.IsEspresso(tip.Time), nil +} + +// waitForAuthGroup blocks until all in-flight fallback-auth watcher goroutines +// have finished. publishingLoop calls it before closing receiptsCh: each watcher +// is a sender on receiptsCh, so the receipts loop must still be draining +// receiptsCh at this point or a watcher's final send would block forever. Each +// watcher always terminates because the txmgr Queue emits exactly one receipt per +// Send, even on context cancellation. +func (l *BatchSubmitter) waitForAuthGroup() { + l.authGroup.Wait() +} diff --git a/op-batcher/batcher/espresso_fallback_gate_test.go b/op-batcher/batcher/espresso_fallback_gate_test.go new file mode 100644 index 00000000000..fc9453e42bd --- /dev/null +++ b/op-batcher/batcher/espresso_fallback_gate_test.go @@ -0,0 +1,106 @@ +package batcher + +import ( + "context" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-batcher/metrics" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/testlog" +) + +type mockFixedTimeL1Client struct { + time uint64 +} + +func (f *mockFixedTimeL1Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + return &types.Header{Number: big.NewInt(0), Time: f.time}, nil +} + +func (f *mockFixedTimeL1Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + return 0, nil +} + +func u64(v uint64) *uint64 { return &v } + +type fallbackAuthGateCase struct { + name string + authAddr common.Address + espressoTime *uint64 + tipTime uint64 + want bool +} + +// TestIsFallbackAuthRequired_ForkBoundary locks the batcher's own side of the +// Espresso fork boundary: the fallback batcher must begin authenticating exactly +// at EspressoTime activation and never before, and only when a BatchAuthenticator +// is configured. This is the counterpart to the derivation-side +// TestDataAndHashesFromTxsForkBoundary — a silent divergence here would strand +// batches (auth expected by the verifier but never posted, or posted needlessly). +func TestIsFallbackAuthRequired_ForkBoundary(t *testing.T) { + const espressoTime = 1000 + authAddr := common.HexToAddress("0x00000000000000000000000000000000000000aa") + + tests := []fallbackAuthGateCase{ + { + name: "no authenticator configured is never gated", + authAddr: common.Address{}, + espressoTime: u64(espressoTime), + tipTime: espressoTime, // even at/after activation + want: false, + }, + { + name: "espresso not scheduled is never gated", + authAddr: authAddr, + espressoTime: nil, + tipTime: espressoTime, + want: false, + }, + { + name: "one second before activation is not gated", + authAddr: authAddr, + espressoTime: u64(espressoTime), + tipTime: espressoTime - 1, + want: false, + }, + { + name: "exactly at activation is gated", + authAddr: authAddr, + espressoTime: u64(espressoTime), + tipTime: espressoTime, + want: true, + }, + { + name: "after activation is gated", + authAddr: authAddr, + espressoTime: u64(espressoTime), + tipTime: espressoTime + 1, + want: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + l := &BatchSubmitter{} + l.Log = testlog.Logger(t, log.LevelDebug) + l.Metr = metrics.NoopMetrics + l.RollupConfig = &rollup.Config{ + BatchAuthenticatorAddress: test.authAddr, + EspressoTime: test.espressoTime, + } + l.Config.NetworkTimeout = time.Second + l.L1Client = &mockFixedTimeL1Client{time: test.tipTime} + + got, err := l.isFallbackAuthRequired(context.Background()) + require.NoError(t, err) + require.Equal(t, test.want, got) + }) + } +} diff --git a/op-batcher/batcher/fallback_auth.go b/op-batcher/batcher/fallback_auth.go new file mode 100644 index 00000000000..419fb68d36e --- /dev/null +++ b/op-batcher/batcher/fallback_auth.go @@ -0,0 +1,209 @@ +package batcher + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/ethereum-optimism/optimism/espresso/bindings" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/txmgr" +) + +// computeCommitment computes the batch commitment hash from a transaction +// candidate. It delegates to the same functions the verifier uses so the two +// provably agree on the bytes that get authenticated: +// - calldata transactions: keccak256(calldata). +// - blob transactions: keccak256(concat(blobVersionedHashes)). +func computeCommitment(candidate *txmgr.TxCandidate) ([32]byte, error) { + if len(candidate.Blobs) == 0 { + return derive.ComputeCalldataBatchHash(candidate.TxData), nil + } + + blobHashes := make([]common.Hash, len(candidate.Blobs)) + for i, blob := range candidate.Blobs { + blobCommitment, err := blob.ComputeKZGCommitment() + if err != nil { + return [32]byte{}, fmt.Errorf("failed to compute KZG commitment for blob: %w", err) + } + blobHashes[i] = eth.KZGToVersionedHash(blobCommitment) + } + return derive.ComputeBlobBatchHash(blobHashes), nil +} + +// sendTxWithFallbackAuth authenticates a batch transaction via the BatchAuthenticator contract +// using the fallback batcher's sender identity (msg.sender check on-chain), then sends the +// batch data to the BatchInbox address. +// +// The contract's fallback path checks msg.sender against systemConfig.batcherHash(), so no +// separate signature is needed — the L1 transaction is already signed by the TxManager's key. +func (l *BatchSubmitter) sendTxWithFallbackAuth(txdata txData, isCancel bool, candidate *txmgr.TxCandidate, queue TxSender[txRef], receiptsCh chan txmgr.TxReceipt[txRef]) { + transactionReference := newTxRef(txdata, isCancel) + // The auth tx shares the batch txdata's identity (so a failure requeues the right frames) + // but is always a calldata tx. Auth failures must carry its real type: an ErrAlreadyReserved + // receipt labeled with the batch's blob type would make cancelBlockingTx cancel the wrong + // pool, leaving the reserving tx stuck. + authReference := transactionReference + authReference.isBlob = false + authReference.daType = DaTypeCalldata + l.Log.Debug("Sending fallback-authenticated L1 transaction", "txRef", transactionReference) + + commitment, err := computeCommitment(candidate) + if err != nil { + receiptsCh <- txmgr.TxReceipt[txRef]{ + ID: transactionReference, + Err: fmt.Errorf("failed to compute commitment: %w", err), + } + return + } + l.Log.Debug("Computed fallback batch commitment", "txRef", transactionReference, "commitment", hexutil.Encode(commitment[:])) + + batchAuthenticatorAbi, err := bindings.BatchAuthenticatorMetaData.GetAbi() + if err != nil { + receiptsCh <- txmgr.TxReceipt[txRef]{ + ID: transactionReference, + Err: fmt.Errorf("failed to get batch authenticator ABI: %w", err), + } + return + } + + // Pass an empty signature — the contract checks msg.sender for the fallback path. + authenticateBatchCalldata, err := batchAuthenticatorAbi.Pack("authenticateBatchInfo", commitment, []byte{}) + if err != nil { + receiptsCh <- txmgr.TxReceipt[txRef]{ + ID: transactionReference, + Err: fmt.Errorf("failed to pack authenticateBatchInfo calldata: %w", err), + } + return + } + + verifyCandidate := txmgr.TxCandidate{ + TxData: authenticateBatchCalldata, + To: &l.RollupConfig.BatchAuthenticatorAddress, + } + + l.Log.Debug( + "Sending fallback authenticateBatchInfo transaction", + "txRef", transactionReference, + "commitment", hexutil.Encode(commitment[:]), + "address", l.RollupConfig.BatchAuthenticatorAddress.String(), + ) + + if len(candidate.Blobs) > 0 { + // SendPair doesn't support blobs. + l.sendFallbackAuthSerialized(transactionReference, authReference, verifyCandidate, candidate, queue, receiptsCh) + return + } + + // Private buffered channels — Queue.SendPair forwards exactly one receipt to + // each, so the watcher reads exactly once per channel. These never reach + // handleReceipt; only the synthetic receipt from the watcher does. + authReceiptCh := make(chan txmgr.TxReceipt[txRef], 1) + batchReceiptCh := make(chan txmgr.TxReceipt[txRef], 1) + + // Calldata pair: pipelined, auth leg first; see TxManager.SendPairAsync for the + // contract (ordering, slot accounting, cancellation of the batch leg on failure). + queue.SendPair(authReference, verifyCandidate, authReceiptCh, transactionReference, *candidate, batchReceiptCh) + + l.authGroup.Add(1) + go func() { + defer l.authGroup.Done() + l.watchFallbackAuthReceipts(transactionReference, authReference, authReceiptCh, batchReceiptCh, receiptsCh) + }() +} + +// sendFallbackAuthSerialized submits an auth+batch pair serially: the auth tx is sent +// and confirmed first, then the batch tx. It runs on the publishing-loop goroutine and +// blocks it for the pair's full confirmation cycle, so consecutive pairs never overlap. +func (l *BatchSubmitter) sendFallbackAuthSerialized(transactionReference, authReference txRef, verifyCandidate txmgr.TxCandidate, candidate *txmgr.TxCandidate, queue TxSender[txRef], receiptsCh chan txmgr.TxReceipt[txRef]) { + authReceiptCh := make(chan txmgr.TxReceipt[txRef], 1) + queue.Send(authReference, verifyCandidate, authReceiptCh) + authResult := <-authReceiptCh + if err := fallbackAuthResultError(authResult); err != nil { + l.Log.Error("Fallback authenticateBatchInfo transaction failed", "txRef", transactionReference, "err", err) + receiptsCh <- txmgr.TxReceipt[txRef]{ + ID: authReference, + Err: err, + } + return + } + + // The auth tx is confirmed on L1, releasing the account's reservation in the + // calldata pool. Now send the blob batch tx. + batchReceiptCh := make(chan txmgr.TxReceipt[txRef], 1) + queue.Send(transactionReference, *candidate, batchReceiptCh) + batchResult := <-batchReceiptCh + l.resolveFallbackAuthPair(transactionReference, authReference, authResult, batchResult, receiptsCh) +} + +// watchFallbackAuthReceipts collects the auth and batch receipts of a pipelined fallback-auth +// pair and resolves them into a single synthetic receipt (see resolveFallbackAuthPair). +func (l *BatchSubmitter) watchFallbackAuthReceipts(transactionReference, authReference txRef, authReceiptCh, batchReceiptCh chan txmgr.TxReceipt[txRef], receiptsCh chan txmgr.TxReceipt[txRef]) { + authResult := <-authReceiptCh + batchResult := <-batchReceiptCh + l.resolveFallbackAuthPair(transactionReference, authReference, authResult, batchResult, receiptsCh) +} + +// fallbackAuthResultError interprets the auth leg's receipt: a send failure or a +// mined-but-reverted auth tx both fail the pair, since a reverted authenticateBatchInfo call +// emits no BatchInfoAuthenticated event and the verifier would drop the batch (txmgr returns a +// receipt as soon as the tx is mined, regardless of execution status). The batch inbox tx +// needs no such status check: derivation reads its data by L1 inclusion, not by execution +// status. +func fallbackAuthResultError(authResult txmgr.TxReceipt[txRef]) error { + if authResult.Err != nil { + return fmt.Errorf("failed to send fallback authenticateBatchInfo transaction: %w", authResult.Err) + } + if authResult.Receipt.Status != types.ReceiptStatusSuccessful { + return fmt.Errorf("fallback authenticateBatchInfo transaction reverted: %s", authResult.Receipt.TxHash) + } + return nil +} + +// resolveFallbackAuthPair validates the receipts of an auth+batch pair — auth success, batch +// success, and that the batch tx landed within the lookback window of the auth tx — and +// forwards a single synthetic receipt keyed to the batch txData onto receiptsCh. Any failure +// produces an error receipt so the channel manager rewinds and resubmits the frame set. Auth +// failures are reported under authReference (see its construction in sendTxWithFallbackAuth +// for why the calldata typing matters). +func (l *BatchSubmitter) resolveFallbackAuthPair(transactionReference, authReference txRef, authResult, batchResult txmgr.TxReceipt[txRef], receiptsCh chan txmgr.TxReceipt[txRef]) { + if err := fallbackAuthResultError(authResult); err != nil { + l.Log.Error("Fallback authenticateBatchInfo transaction failed", "txRef", transactionReference, "err", err) + receiptsCh <- txmgr.TxReceipt[txRef]{ + ID: authReference, + Err: err, + } + return + } + + if batchResult.Err != nil { + l.Log.Error("Failed to send batch inbox transaction", "txRef", transactionReference, "err", batchResult.Err) + receiptsCh <- txmgr.TxReceipt[txRef]{ + ID: transactionReference, + Err: fmt.Errorf("failed to send batch inbox transaction: %w", batchResult.Err), + } + return + } + + distance := new(big.Int).Sub(batchResult.Receipt.BlockNumber, authResult.Receipt.BlockNumber) + lookbackWindow := new(big.Int).SetUint64(derive.BatchAuthLookbackWindow) + if distance.Sign() < 0 || distance.Cmp(lookbackWindow) > 0 { + l.Metr.RecordFallbackAuthWindowExceeded() + l.Log.Error("authenticateBatchInfo transaction too far from batch inbox transaction", "txRef", transactionReference, "distance", distance) + receiptsCh <- txmgr.TxReceipt[txRef]{ + ID: transactionReference, + Err: fmt.Errorf("authenticateBatchInfo transaction too far from batch inbox transaction: %s", distance), + } + return + } + + receiptsCh <- txmgr.TxReceipt[txRef]{ + ID: transactionReference, + Receipt: batchResult.Receipt, + Err: nil, + } +} diff --git a/op-batcher/batcher/fallback_auth_test.go b/op-batcher/batcher/fallback_auth_test.go new file mode 100644 index 00000000000..980c910bf0b --- /dev/null +++ b/op-batcher/batcher/fallback_auth_test.go @@ -0,0 +1,431 @@ +package batcher + +import ( + "errors" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-batcher/metrics" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum-optimism/optimism/op-service/txmgr" +) + +var errSendFailed = errors.New("send failed") + +// recordedSend captures a single queued transaction, whether it arrived via +// Send or as a leg of SendPair. +type recordedSend struct { + candidate txmgr.TxCandidate + receiptCh chan txmgr.TxReceipt[txRef] + viaPair bool +} + +// fakeTxSender records queued transactions in order and immediately delivers a +// canned response (by index) on the receipt channel, mimicking the txmgr +// Queue, which forwards exactly one receipt per queued transaction. SendPair +// records its two legs as two consecutive sends consuming two consecutive +// canned responses. +type fakeTxSender struct { + sends []recordedSend + pairCalls int + responses []txmgr.TxReceipt[txRef] +} + +func (f *fakeTxSender) deliver(id txRef, candidate txmgr.TxCandidate, receiptCh chan txmgr.TxReceipt[txRef], viaPair bool) { + idx := len(f.sends) + f.sends = append(f.sends, recordedSend{candidate: candidate, receiptCh: receiptCh, viaPair: viaPair}) + resp := f.responses[idx] + resp.ID = id + receiptCh <- resp +} + +func (f *fakeTxSender) Send(id txRef, candidate txmgr.TxCandidate, receiptCh chan txmgr.TxReceipt[txRef]) { + f.deliver(id, candidate, receiptCh, false) +} + +func (f *fakeTxSender) SendPair(firstID txRef, first txmgr.TxCandidate, firstCh chan txmgr.TxReceipt[txRef], secondID txRef, second txmgr.TxCandidate, secondCh chan txmgr.TxReceipt[txRef]) { + f.pairCalls++ + f.deliver(firstID, first, firstCh, true) + f.deliver(secondID, second, secondCh, true) +} + +type fallbackAuthMetricsSpy struct { + metrics.Metricer + windowExceeded int +} + +func (s *fallbackAuthMetricsSpy) RecordFallbackAuthWindowExceeded() { s.windowExceeded++ } + +func newFallbackAuthSubmitter(t *testing.T) *BatchSubmitter { + l := &BatchSubmitter{} + l.Log = testlog.Logger(t, log.LevelDebug) + l.Metr = metrics.NoopMetrics + l.RollupConfig = &rollup.Config{ + BatchAuthenticatorAddress: common.HexToAddress("0x00000000000000000000000000000000000000aa"), + } + return l +} + +func testFallbackTxData() txData { + return singleFrameTxData(frameData{data: []byte("frame-data")}) +} + +// testBlobCandidate returns a tx candidate carrying one (zero) blob, which +// routes sendTxWithFallbackAuth onto the serialized blob path. +func testBlobCandidate() *txmgr.TxCandidate { + return &txmgr.TxCandidate{Blobs: []*eth.Blob{{}}} +} + +func receiptWithBlock(num int64) *types.Receipt { + return &types.Receipt{BlockNumber: big.NewInt(num), Status: types.ReceiptStatusSuccessful} +} + +func revertedReceiptWithBlock(num int64) *types.Receipt { + return &types.Receipt{BlockNumber: big.NewInt(num), Status: types.ReceiptStatusFailed} +} + +// TestFallbackAuth_OrderingAndSuccess verifies a calldata batch is submitted +// as a single pipelined pair — the auth tx first (so it takes the lower nonce +// and lands first, as Espresso requires), the batch tx second — and that a +// single success receipt for the batch txData is emitted when both txs land +// within the lookback window. +func TestFallbackAuth_OrderingAndSuccess(t *testing.T) { + l := newFallbackAuthSubmitter(t) + txdata := testFallbackTxData() + candidate := &txmgr.TxCandidate{TxData: []byte("batch-calldata")} + + queue := &fakeTxSender{ + responses: []txmgr.TxReceipt[txRef]{ + {Receipt: receiptWithBlock(100)}, // auth + {Receipt: receiptWithBlock(101)}, // batch + }, + } + receiptsCh := make(chan txmgr.TxReceipt[txRef], 1) + + l.sendTxWithFallbackAuth(txdata, false, candidate, queue, receiptsCh) + + require.Len(t, queue.sends, 2) + require.Equal(t, 1, queue.pairCalls, "calldata pair must be submitted via SendPair") + require.True(t, queue.sends[0].viaPair) + require.True(t, queue.sends[1].viaPair) + // First leg must target the BatchAuthenticator (the auth tx), giving it the + // lower, earlier-mined nonce. + require.NotNil(t, queue.sends[0].candidate.To) + require.Equal(t, l.RollupConfig.BatchAuthenticatorAddress, *queue.sends[0].candidate.To) + // Second leg is the batch tx itself. + require.Equal(t, candidate.TxData, queue.sends[1].candidate.TxData) + + got := <-receiptsCh + require.NoError(t, got.Err) + require.Equal(t, receiptWithBlock(101).BlockNumber, got.Receipt.BlockNumber) + require.Equal(t, txdata.ID().String(), got.ID.id.String()) +} + +// TestFallbackAuth_AuthFailureRetried verifies that an auth-leg failure of a +// pipelined pair produces an error receipt keyed to the batch txData so the +// frames are re-queued. Both legs enter the queue back-to-back (pipelined); +// the tx manager cancels the batch leg when the auth leg fails, surfacing as +// an ErrPairLegCancelled response on the batch channel. +func TestFallbackAuth_AuthFailureRetried(t *testing.T) { + l := newFallbackAuthSubmitter(t) + txdata := testFallbackTxData() + candidate := &txmgr.TxCandidate{TxData: []byte("batch-calldata")} + + queue := &fakeTxSender{ + responses: []txmgr.TxReceipt[txRef]{ + {Err: errSendFailed}, // auth leg fails + {Err: txmgr.ErrPairLegCancelled}, // batch leg cancelled by the pair + }, + } + receiptsCh := make(chan txmgr.TxReceipt[txRef], 1) + + l.sendTxWithFallbackAuth(txdata, false, candidate, queue, receiptsCh) + + got := <-receiptsCh + require.Error(t, got.Err) + require.Equal(t, txdata.ID().String(), got.ID.id.String()) + require.Len(t, queue.sends, 2) + require.Equal(t, 1, queue.pairCalls) +} + +// TestFallbackAuth_AuthRevertedRetried verifies that an authenticateBatchInfo tx +// that mines but reverts (no event emitted for the verifier) produces an error +// receipt so the frames are re-queued, rather than being confirmed as success. +// The tx manager treats a reverted auth leg as a pair failure and cancels the +// batch leg. +func TestFallbackAuth_AuthRevertedRetried(t *testing.T) { + l := newFallbackAuthSubmitter(t) + txdata := testFallbackTxData() + candidate := &txmgr.TxCandidate{TxData: []byte("batch-calldata")} + + queue := &fakeTxSender{ + responses: []txmgr.TxReceipt[txRef]{ + {Receipt: revertedReceiptWithBlock(100)}, // auth mined but reverted + {Err: txmgr.ErrPairLegCancelled}, // batch leg cancelled by the pair + }, + } + receiptsCh := make(chan txmgr.TxReceipt[txRef], 1) + + l.sendTxWithFallbackAuth(txdata, false, candidate, queue, receiptsCh) + + got := <-receiptsCh + require.Error(t, got.Err) + require.Equal(t, txdata.ID().String(), got.ID.id.String()) + require.Len(t, queue.sends, 2) + require.Equal(t, 1, queue.pairCalls) +} + +// TestFallbackAuth_BatchFailureRetried verifies a batch-leg failure produces an +// error receipt keyed to the batch txData. +func TestFallbackAuth_BatchFailureRetried(t *testing.T) { + l := newFallbackAuthSubmitter(t) + txdata := testFallbackTxData() + candidate := &txmgr.TxCandidate{TxData: []byte("batch-calldata")} + + queue := &fakeTxSender{ + responses: []txmgr.TxReceipt[txRef]{ + {Receipt: receiptWithBlock(100)}, // auth lands + {Err: errSendFailed}, // batch fails + }, + } + receiptsCh := make(chan txmgr.TxReceipt[txRef], 1) + + l.sendTxWithFallbackAuth(txdata, false, candidate, queue, receiptsCh) + + got := <-receiptsCh + require.Error(t, got.Err) + require.Equal(t, txdata.ID().String(), got.ID.id.String()) + require.Equal(t, 1, queue.pairCalls) +} + +// TestFallbackAuth_BlobSerialized verifies a blob batch takes the serialized +// path: the auth tx is sent alone (not as a pair) and the blob batch tx is +// only sent after the auth tx confirms, because geth's cross-pool account +// reservation cannot hold a calldata auth tx and a blob batch tx at once. +func TestFallbackAuth_BlobSerialized(t *testing.T) { + l := newFallbackAuthSubmitter(t) + txdata := testFallbackTxData() + txdata.daType = DaTypeBlob + candidate := testBlobCandidate() + + queue := &fakeTxSender{ + responses: []txmgr.TxReceipt[txRef]{ + {Receipt: receiptWithBlock(100)}, // auth + {Receipt: receiptWithBlock(101)}, // batch, sent only after auth confirmed + }, + } + receiptsCh := make(chan txmgr.TxReceipt[txRef], 1) + + l.sendTxWithFallbackAuth(txdata, false, candidate, queue, receiptsCh) + + require.Len(t, queue.sends, 2) + require.Equal(t, 0, queue.pairCalls, "blob pair must not be pipelined") + require.False(t, queue.sends[0].viaPair) + require.False(t, queue.sends[1].viaPair) + require.Equal(t, l.RollupConfig.BatchAuthenticatorAddress, *queue.sends[0].candidate.To) + + got := <-receiptsCh + require.NoError(t, got.Err) + require.Equal(t, txdata.ID().String(), got.ID.id.String()) +} + +// TestFallbackAuth_BlobAuthFailureGatesBatch verifies the serialized blob path +// never sends the batch tx when the auth tx fails: the batch would be crafted +// at the next nonce while the failed auth send resets the txmgr nonce, leaving +// a nonce gap (and an unauthenticated blob batch) behind. +func TestFallbackAuth_BlobAuthFailureGatesBatch(t *testing.T) { + l := newFallbackAuthSubmitter(t) + txdata := testFallbackTxData() + txdata.daType = DaTypeBlob + candidate := testBlobCandidate() + + queue := &fakeTxSender{ + responses: []txmgr.TxReceipt[txRef]{ + {Err: errSendFailed}, // auth fails. No batch response, it must never be sent + }, + } + receiptsCh := make(chan txmgr.TxReceipt[txRef], 1) + + l.sendTxWithFallbackAuth(txdata, false, candidate, queue, receiptsCh) + + got := <-receiptsCh + require.Error(t, got.Err) + require.Equal(t, txdata.ID().String(), got.ID.id.String()) + require.Len(t, queue.sends, 1) +} + +// TestFallbackAuth_BlobAuthRevertGatesBatch is the revert variant: a mined but +// reverted auth emits no BatchInfoAuthenticated event, so the blob batch would +// be unverifiable and must not be submitted at all. +func TestFallbackAuth_BlobAuthRevertGatesBatch(t *testing.T) { + l := newFallbackAuthSubmitter(t) + txdata := testFallbackTxData() + txdata.daType = DaTypeBlob + candidate := testBlobCandidate() + + queue := &fakeTxSender{ + responses: []txmgr.TxReceipt[txRef]{ + {Receipt: revertedReceiptWithBlock(100)}, // auth mined but reverted + }, + } + receiptsCh := make(chan txmgr.TxReceipt[txRef], 1) + + l.sendTxWithFallbackAuth(txdata, false, candidate, queue, receiptsCh) + + got := <-receiptsCh + require.Error(t, got.Err) + require.Equal(t, txdata.ID().String(), got.ID.id.String()) + require.Len(t, queue.sends, 1) +} + +// TestFallbackAuth_AuthFailureTxRefType verifies that an auth-tx failure is +// reported under a calldata-typed txRef even when the batch txdata is blob. +// The auth tx is always calldata; if its ErrAlreadyReserved failure were +// labeled with the batch's blob type, cancelBlockingTx would send a calldata +// cancel against a blobpool reservation, which is rejected the same way, +// looping forever without ever displacing the stuck blob tx. +func TestFallbackAuth_AuthFailureTxRefType(t *testing.T) { + l := newFallbackAuthSubmitter(t) + txdata := testFallbackTxData() + txdata.daType = DaTypeBlob + candidate := testBlobCandidate() + + queue := &fakeTxSender{ + responses: []txmgr.TxReceipt[txRef]{ + {Err: errSendFailed}, // auth fails. No batch response, it must never be sent + }, + } + receiptsCh := make(chan txmgr.TxReceipt[txRef], 1) + + l.sendTxWithFallbackAuth(txdata, false, candidate, queue, receiptsCh) + + got := <-receiptsCh + require.Error(t, got.Err) + require.Equal(t, txdata.ID().String(), got.ID.id.String()) + require.Len(t, queue.sends, 1) + require.False(t, got.ID.isBlob) + require.Equal(t, DaTypeCalldata, got.ID.daType) +} + +// TestFallbackAuth_BatchFailureTxRefType verifies the converse: a batch-tx +// failure keeps the batch txdata's own type on the forwarded receipt. +func TestFallbackAuth_BatchFailureTxRefType(t *testing.T) { + l := newFallbackAuthSubmitter(t) + txdata := testFallbackTxData() + txdata.daType = DaTypeBlob + candidate := testBlobCandidate() + + queue := &fakeTxSender{ + responses: []txmgr.TxReceipt[txRef]{ + {Receipt: receiptWithBlock(100)}, // auth lands + {Err: errSendFailed}, // batch fails + }, + } + receiptsCh := make(chan txmgr.TxReceipt[txRef], 1) + + l.sendTxWithFallbackAuth(txdata, false, candidate, queue, receiptsCh) + + got := <-receiptsCh + require.Error(t, got.Err) + require.Equal(t, txdata.ID().String(), got.ID.id.String()) + require.Len(t, queue.sends, 2) + require.True(t, got.ID.isBlob) + require.Equal(t, DaTypeBlob, got.ID.daType) +} + +// TestFallbackAuth_WindowViolationRetried verifies that a batch tx landing +// outside the lookback window of the auth tx produces an error receipt (so the +// channel manager rewinds and resubmits), rather than being confirmed. +func TestFallbackAuth_WindowViolationRetried(t *testing.T) { + l := newFallbackAuthSubmitter(t) + metr := &fallbackAuthMetricsSpy{Metricer: metrics.NoopMetrics} + l.Metr = metr + txdata := testFallbackTxData() + candidate := &txmgr.TxCandidate{TxData: []byte("batch-calldata")} + + tooFar := int64(100 + derive.BatchAuthLookbackWindow + 1) + queue := &fakeTxSender{ + responses: []txmgr.TxReceipt[txRef]{ + {Receipt: receiptWithBlock(100)}, // auth + {Receipt: receiptWithBlock(tooFar)}, // batch too far away + }, + } + receiptsCh := make(chan txmgr.TxReceipt[txRef], 1) + + l.sendTxWithFallbackAuth(txdata, false, candidate, queue, receiptsCh) + + got := <-receiptsCh + require.Error(t, got.Err) + require.Equal(t, txdata.ID().String(), got.ID.id.String()) + require.Equal(t, 1, metr.windowExceeded, "window violation should record the fallback_auth_window_exceeded metric") +} + +// TestFallbackAuth_WindowBoundaryAccepted pins the inclusive bound of the lookback +// window: a batch landing exactly BatchAuthLookbackWindow blocks after the auth tx is +// still accepted by the verifier (CollectAuthenticatedBatches scans +// [batchBlock - BatchAuthLookbackWindow, batchBlock]), so the batcher must not +// re-queue it. +func TestFallbackAuth_WindowBoundaryAccepted(t *testing.T) { + l := newFallbackAuthSubmitter(t) + txdata := testFallbackTxData() + candidate := &txmgr.TxCandidate{TxData: []byte("batch-calldata")} + + boundary := int64(100 + derive.BatchAuthLookbackWindow) + queue := &fakeTxSender{ + responses: []txmgr.TxReceipt[txRef]{ + {Receipt: receiptWithBlock(100)}, // auth + {Receipt: receiptWithBlock(boundary)}, // batch at the exact edge of the window + }, + } + receiptsCh := make(chan txmgr.TxReceipt[txRef], 1) + + l.sendTxWithFallbackAuth(txdata, false, candidate, queue, receiptsCh) + + got := <-receiptsCh + require.NoError(t, got.Err) + require.Equal(t, receiptWithBlock(boundary).BlockNumber, got.Receipt.BlockNumber) + require.Equal(t, txdata.ID().String(), got.ID.id.String()) +} + +// TestComputeCommitment_Parity locks the batcher's batch-commitment computation to +// the verifier's. The batcher must hash exactly what op-node derivation hashes, or +// post-fork batches fail the commitment match and are silently dropped. It checks +// both paths against derive.ComputeCalldataBatchHash / derive.ComputeBlobBatchHash; +// for blobs it uses the real versioned hashes the tx will carry (via MakeSidecar, +// the same hashes the verifier reads from tx.BlobHashes()). +func TestComputeCommitment_Parity(t *testing.T) { + t.Run("calldata", func(t *testing.T) { + for _, data := range [][]byte{[]byte("batch calldata payload"), {}, nil} { + got, err := computeCommitment(&txmgr.TxCandidate{TxData: data}) + require.NoError(t, err) + require.Equal(t, derive.ComputeCalldataBatchHash(data), common.Hash(got)) + } + }) + + t.Run("blobs", func(t *testing.T) { + for _, n := range []int{1, 3} { + blobs := make([]*eth.Blob, n) + for i := range blobs { + var blob eth.Blob + // Distinct first byte per blob so the versioned hashes differ and the + // concatenation order is actually exercised. + require.NoError(t, blob.FromData(eth.Data{byte(i), 0xab, 0xcd})) + blobs[i] = &blob + } + _, blobHashes, err := txmgr.MakeSidecar(blobs, false) + require.NoError(t, err) + + got, err := computeCommitment(&txmgr.TxCandidate{Blobs: blobs}) + require.NoError(t, err) + require.Equal(t, derive.ComputeBlobBatchHash(blobHashes), common.Hash(got)) + } + }) +} diff --git a/op-batcher/batcher/service.go b/op-batcher/batcher/service.go index 02759ca5156..4c8892c6c74 100644 --- a/op-batcher/batcher/service.go +++ b/op-batcher/batcher/service.go @@ -8,6 +8,7 @@ import ( "sync/atomic" "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" @@ -19,6 +20,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/params" "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-service/cliapp" "github.com/ethereum-optimism/optimism/op-service/dial" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -169,6 +171,9 @@ func (bs *BatcherService) initFromCLIConfig(ctx context.Context, closeApp contex if err := bs.initRollupConfig(ctx); err != nil { return fmt.Errorf("failed to load rollup config: %w", err) } + if err := bs.checkFallbackAuthConfirmations(cfg); err != nil { + return err + } if err := bs.initTxManager(ctx, cfg); err != nil { return fmt.Errorf("failed to init Tx manager: %w", err) } @@ -254,6 +259,37 @@ func (bs *BatcherService) initRollupConfig(ctx context.Context) error { return nil } +// checkFallbackAuthConfirmations validates that the configured number of L1 +// confirmations leaves enough headroom inside BatchAuthLookbackWindow for the +// batch tx to land after its auth tx (see sendTxWithFallbackAuth). The bound +// only applies when the BatchAuthenticator is configured on the chain, which +// is only known once the rollup config is loaded +func (bs *BatcherService) checkFallbackAuthConfirmations(cfg *CLIConfig) error { + if bs.RollupConfig.BatchAuthenticatorAddress == (common.Address{}) { + return nil + } + // Fallback auth is gated behind the EspressoTime hardfork + // (dispatchAuthenticatedSendTx): with no activation scheduled no + // auth→batch pair can be emitted, so the bound does not apply. A future + // activation must still be checked — it switches the send path mid-run. + if bs.RollupConfig.EspressoTime == nil { + return nil + } + // Only blob pairs serialize auth→batch on NumConfirmations + // (sendFallbackAuthSerialized); calldata pairs broadcast back-to-back. + if cfg.DataAvailabilityType == flags.CalldataType { + return nil + } + // The auth→batch distance (num-confirmations + inclusion delay) must fit within + // BatchAuthLookbackWindow, so reserve room for the batch to land or the safe head stalls. + const fallbackAuthInclusionReserve = 75 // blocks (~15 min at 12s L1 slots) + if authLookback := derive.BatchAuthLookbackWindow; authLookback < cfg.TxMgrConfig.NumConfirmations+fallbackAuthInclusionReserve { + return fmt.Errorf("NumConfirmations (%d) too high for BatchAuthLookbackWindow (%d): need %d blocks of inclusion headroom", + cfg.TxMgrConfig.NumConfirmations, authLookback, fallbackAuthInclusionReserve) + } + return nil +} + func (bs *BatcherService) initChannelConfig(cfg *CLIConfig) error { channelTimeout := bs.RollupConfig.ChannelTimeoutBedrock // Use lower channel timeout if granite is scheduled. diff --git a/op-batcher/batcher/service_test.go b/op-batcher/batcher/service_test.go new file mode 100644 index 00000000000..1fe7ba29a8e --- /dev/null +++ b/op-batcher/batcher/service_test.go @@ -0,0 +1,55 @@ +package batcher + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum-optimism/optimism/op-batcher/flags" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/txmgr" +) + +// TestCheckFallbackAuthConfirmations: the NumConfirmations headroom bound only +// applies when a blob/auto DA batcher can actually emit auth→batch pairs — a +// BatchAuthenticator is configured AND the EspressoTime fork is scheduled. +func TestCheckFallbackAuthConfirmations(t *testing.T) { + espressoTime := uint64(0) + authAddr := common.Address{0x01} + + tests := []struct { + name string + authenticator common.Address + espressoTime *uint64 + daType flags.DataAvailabilityType + numConfs uint64 + wantErr bool + }{ + {"no authenticator", common.Address{}, &espressoTime, flags.BlobsType, 100, false}, + {"espresso not scheduled", authAddr, nil, flags.BlobsType, 100, false}, + {"calldata exempt", authAddr, &espressoTime, flags.CalldataType, 100, false}, + {"blob within headroom", authAddr, &espressoTime, flags.BlobsType, 25, false}, + {"blob exceeds headroom", authAddr, &espressoTime, flags.BlobsType, 26, true}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + bs := &BatcherService{RollupConfig: &rollup.Config{ + BatchAuthenticatorAddress: test.authenticator, + EspressoTime: test.espressoTime, + }} + cfg := &CLIConfig{ + DataAvailabilityType: test.daType, + TxMgrConfig: txmgr.CLIConfig{NumConfirmations: test.numConfs}, + } + err := bs.checkFallbackAuthConfirmations(cfg) + if test.wantErr { + require.ErrorContains(t, err, "NumConfirmations") + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/op-batcher/flags/flags.go b/op-batcher/flags/flags.go index 0d894fad2e5..f17faf14c52 100644 --- a/op-batcher/flags/flags.go +++ b/op-batcher/flags/flags.go @@ -160,7 +160,6 @@ var ( Value: false, EnvVars: prefixEnvVars("WAIT_NODE_SYNC"), } - // Legacy Flags SequencerHDPathFlag = txmgr.SequencerHDPathFlag ) diff --git a/op-batcher/metrics/metrics.go b/op-batcher/metrics/metrics.go index 93c8efd2808..84057122a0b 100644 --- a/op-batcher/metrics/metrics.go +++ b/op-batcher/metrics/metrics.go @@ -69,6 +69,8 @@ type Metricer interface { RecordBatchDataSizeBytes(daType string, size int) RecordFailoverToEthDA() + RecordFallbackAuthWindowExceeded() + Document() []opmetrics.DocumentedMetric PendingDABytes() float64 @@ -110,9 +112,10 @@ type Metrics struct { channelOutputBytesTotal prometheus.Counter channelQueueLength prometheus.Gauge - batchSentDATypeTotal prometheus.CounterVec - batchStoredDataSizeBytesTotal prometheus.CounterVec - altDaFailoverTotal prometheus.Counter + batchSentDATypeTotal prometheus.CounterVec + batchStoredDataSizeBytesTotal prometheus.CounterVec + altDaFailoverTotal prometheus.Counter + fallbackAuthWindowExceededTotal prometheus.Counter batcherTxEvs opmetrics.EventVec @@ -255,6 +258,11 @@ func NewMetrics(procName string) *Metrics { Name: "alt_da_failover_total", Help: "Total number of batches that could not be stored in AltDA and were sent to L1 instead", }), + fallbackAuthWindowExceededTotal: factory.NewCounter(prometheus.CounterOpts{ + Namespace: ns, + Name: "fallback_auth_window_exceeded_total", + Help: "Total number of fallback-auth submissions dropped because the batch tx landed beyond BatchAuthLookbackWindow blocks after its auth tx", + }), blobUsedBytes: factory.NewHistogram(prometheus.HistogramOpts{ Namespace: ns, Name: "blob_used_bytes", @@ -483,6 +491,10 @@ func (m *Metrics) RecordFailoverToEthDA() { m.altDaFailoverTotal.Inc() } +func (m *Metrics) RecordFallbackAuthWindowExceeded() { + m.fallbackAuthWindowExceededTotal.Inc() +} + func (m *Metrics) RecordChannelQueueLength(len int) { m.channelQueueLength.Set(float64(len)) } diff --git a/op-batcher/metrics/noop.go b/op-batcher/metrics/noop.go index 31002349fe2..b44bb8950f1 100644 --- a/op-batcher/metrics/noop.go +++ b/op-batcher/metrics/noop.go @@ -64,6 +64,7 @@ func (*noopMetrics) RecordBlobUsedBytes(int) {} func (*noopMetrics) RecordBatchDaType(string) {} func (*noopMetrics) RecordBatchDataSizeBytes(string, int) {} func (*noopMetrics) RecordFailoverToEthDA() {} +func (*noopMetrics) RecordFallbackAuthWindowExceeded() {} func (*noopMetrics) StartBalanceMetrics(log.Logger, *ethclient.Client, common.Address) io.Closer { return nil diff --git a/op-challenger/sender/sender_test.go b/op-challenger/sender/sender_test.go index 4852ef3e664..31029cf46a5 100644 --- a/op-challenger/sender/sender_test.go +++ b/op-challenger/sender/sender_test.go @@ -141,6 +141,10 @@ func (s *stubTxMgr) SendAsync(ctx context.Context, candidate txmgr.TxCandidate, }() } +func (s *stubTxMgr) SendPairAsync(ctx context.Context, first txmgr.TxCandidate, second txmgr.TxCandidate, firstCh chan txmgr.SendResponse, secondCh chan txmgr.SendResponse) { + panic("unimplemented") +} + func (s *stubTxMgr) recordTx(candidate txmgr.TxCandidate) chan *types.Receipt { s.m.Lock() defer s.m.Unlock() diff --git a/op-e2e/actions/helpers/l2_proposer.go b/op-e2e/actions/helpers/l2_proposer.go index 2df068d077d..50e002ba852 100644 --- a/op-e2e/actions/helpers/l2_proposer.go +++ b/op-e2e/actions/helpers/l2_proposer.go @@ -81,6 +81,10 @@ func (f fakeTxMgr) SendAsync(ctx context.Context, candidate txmgr.TxCandidate, c panic("unimplemented") } +func (f fakeTxMgr) SendPairAsync(ctx context.Context, first txmgr.TxCandidate, second txmgr.TxCandidate, firstCh chan txmgr.SendResponse, secondCh chan txmgr.SendResponse) { + panic("unimplemented") +} + func (f fakeTxMgr) Close() { } diff --git a/op-node/rollup/derive/batch_authenticator.go b/op-node/rollup/derive/batch_authenticator.go index e2c91de9520..52e9f801608 100644 --- a/op-node/rollup/derive/batch_authenticator.go +++ b/op-node/rollup/derive/batch_authenticator.go @@ -13,9 +13,19 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" ) +// isEspressoAuthEnforced returns true once event-based batch authentication is enforced +// at the given L1 origin time: the Espresso fork is active AND at least +// BatchAuthEnforcementDelaySecs has elapsed since activation. Before that, derivation +// keeps accepting sender-authenticated batches. See BatchAuthEnforcementDelaySecs +// (params.go) for the full grace-period mechanism. +func isEspressoAuthEnforced(cfg *rollup.Config, l1OriginTime uint64) bool { + return cfg.IsEspresso(l1OriginTime) && l1OriginTime >= *cfg.EspressoTime+BatchAuthEnforcementDelaySecs +} + var ( // BatchInfoAuthenticatedABI is the event signature for // BatchInfoAuthenticated(bytes32 commitment, address indexed caller). diff --git a/op-node/rollup/derive/blob_data_source.go b/op-node/rollup/derive/blob_data_source.go index c0bfa701135..2f6cfcf9692 100644 --- a/op-node/rollup/derive/blob_data_source.go +++ b/op-node/rollup/derive/blob_data_source.go @@ -119,21 +119,21 @@ func (ds *BlobDataSource) open(ctx context.Context) ([]blobOrCalldata, error) { // creates a placeholder blobOrCalldata element for each returned blob hash that must be populated // by fillBlobPointers after blob bodies are retrieved. // -// Pre-Espresso (the L1 origin time of `ref` is < *EspressoTime, or unset), -// this runs upstream Optimism semantics: filter by batch inbox + sender == -// batcher. +// Before Espresso event-auth is enforced (Espresso inactive at the L1 origin time of +// `ref`, or within BatchAuthEnforcementDelaySecs of activation), this runs upstream +// Optimism semantics: filter by batch inbox + sender == batcher. // -// Post-Espresso, it collects all authenticated batch hashes from a lookback +// Once enforced, it collects all authenticated batch hashes from a lookback // window once and rejects any batch whose commitment hash is not in the // authenticated set. For blob transactions, the batch hash is computed from // the concatenated blob versioned hashes. func dataAndHashesFromTxs(ctx context.Context, txs types.Transactions, config *DataSourceConfig, batcherAddr common.Address, fetcher L1Fetcher, ref eth.L1BlockRef, logger log.Logger) ([]blobOrCalldata, []common.Hash, error) { - // Only collect authenticated batch commitments when the Espresso fork is - // active at the L1 origin time of the block we're scanning. Pre-fork, the - // upstream sender-based authorization path is used and authenticatedHashes - // is unused. + // Only collect authenticated batch commitments once event-based authentication is + // enforced at the L1 origin time of the block we're scanning (Espresso active plus + // the enforcement grace period). Before that, the upstream sender-based + // authorization path is used and authenticatedHashes is unused. var authenticatedHashes map[common.Hash]common.Address - if config.rollupCfg.IsEspresso(ref.Time) { + if isEspressoAuthEnforced(config.rollupCfg, ref.Time) { var err error authenticatedHashes, err = CollectAuthenticatedBatches( ctx, fetcher, ref, config.rollupCfg.BatchAuthenticatorAddress, config.batchAuthCaches, logger, @@ -159,7 +159,7 @@ func dataAndHashesFromTxs(ctx context.Context, txs types.Transactions, config *D batchHash = ComputeCalldataBatchHash(tx.Data()) } - // Check authorization (sender-based pre-fork; event-based post-fork). + // Check authorization (sender-based before enforcement; event-based once enforced). if !isBatchTxAuthorized(tx, *config, batcherAddr, batchHash, authenticatedHashes, ref.Time, logger) { continue } diff --git a/op-node/rollup/derive/data_source.go b/op-node/rollup/derive/data_source.go index 74b50335b2f..ae84f2ad942 100644 --- a/op-node/rollup/derive/data_source.go +++ b/op-node/rollup/derive/data_source.go @@ -165,12 +165,13 @@ func isAuthorizedBatchSender(tx *types.Transaction, l1Signer types.Signer, batch // block (passed as l1OriginTime), mirroring the data-source layer's ecotoneTime // treatment. // -// Pre-Espresso (l1OriginTime < *EspressoTime, or unset): +// Before enforcement (Espresso inactive, or within BatchAuthEnforcementDelaySecs of +// activation): // // upstream behavior — the L1 sender of the transaction must match the configured // batcher address. The authenticatedHashes map is unused. // -// Post-Espresso: +// Once enforced: // // the batch's commitment hash must appear in authenticatedHashes (i.e. a // BatchInfoAuthenticated event was emitted for this commitment within the @@ -188,11 +189,13 @@ func isBatchTxAuthorized( l1OriginTime uint64, logger log.Logger, ) bool { - if !dsCfg.rollupCfg.IsEspresso(l1OriginTime) { - // Pre-fork: upstream sender-based authorization. + if !isEspressoAuthEnforced(dsCfg.rollupCfg, l1OriginTime) { + // Pre-enforcement (pre-fork or within the grace period): upstream + // sender-based authorization. return isAuthorizedBatchSender(tx, dsCfg.l1Signer, batcherAddr, logger) } - // Post-fork: the commitment must have been authenticated within the lookback window. + // Once enforced (fork active and past the grace period): the commitment must have been + // authenticated within the lookback window. authCaller, ok := authenticatedHashes[batchHash] if !ok { logger.Warn("batch not authenticated", diff --git a/op-node/rollup/derive/espresso_blob_data_source_test.go b/op-node/rollup/derive/espresso_blob_data_source_test.go index 052801d263f..e38d2af21a3 100644 --- a/op-node/rollup/derive/espresso_blob_data_source_test.go +++ b/op-node/rollup/derive/espresso_blob_data_source_test.go @@ -101,9 +101,10 @@ func mockAuthEvents(l1F *testutils.MockL1Source, rng *rand.Rand, ref eth.L1Block // TestDataAndHashesFromTxsEventAuth tests event-based batch authentication for both // calldata and blob transactions in the blob data source path. // -// Event-based authentication is only active post-Espresso; the fixture -// activates the fork at L1 origin time 0 (genesis) so all test refs satisfy -// ref.Time >= *EspressoTime. +// Event-based authentication is only enforced once the fork has been active for +// BatchAuthEnforcementDelaySecs; the fixture activates the fork at L1 origin time 0 +// (genesis) and all test refs use Time = BatchAuthEnforcementDelaySecs — the first +// enforced timestamp. func TestDataAndHashesFromTxsEventAuth(t *testing.T) { rng := rand.New(rand.NewSource(9999)) privateKey := testutils.InsecureRandomKey(rng) @@ -141,7 +142,7 @@ func TestDataAndHashesFromTxsEventAuth(t *testing.T) { } calldataTx, _ := types.SignNewTx(privateKey, signer, txData) - ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)} + ref := eth.L1BlockRef{Number: 1, Time: BatchAuthEnforcementDelaySecs, Hash: testutils.RandomHash(rng)} batchHash := ComputeCalldataBatchHash(calldataTx.Data()) ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, []common.Hash{batchHash}) @@ -166,7 +167,7 @@ func TestDataAndHashesFromTxsEventAuth(t *testing.T) { } blobTx, _ := types.SignNewTx(privateKey, signer, blobTxData) - ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)} + ref := eth.L1BlockRef{Number: 1, Time: BatchAuthEnforcementDelaySecs, Hash: testutils.RandomHash(rng)} batchHash := ComputeBlobBatchHash([]common.Hash{blobHash}) ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, []common.Hash{batchHash}) @@ -193,7 +194,7 @@ func TestDataAndHashesFromTxsEventAuth(t *testing.T) { // Signed by an unknown key (not batcherAddr), no auth event — should be rejected calldataTx, _ := types.SignNewTx(altKey, signer, txData) - ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)} + ref := eth.L1BlockRef{Number: 1, Time: BatchAuthEnforcementDelaySecs, Hash: testutils.RandomHash(rng)} ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, nil) // no auth events data, blobHashes, err := dataAndHashesFromTxs(ctx, types.Transactions{calldataTx}, &config, batcherAddr, l1F, ref, logger) @@ -217,7 +218,7 @@ func TestDataAndHashesFromTxsEventAuth(t *testing.T) { // because all batchers now require event-based authentication calldataTx, _ := types.SignNewTx(privateKey, signer, txData) - ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)} + ref := eth.L1BlockRef{Number: 1, Time: BatchAuthEnforcementDelaySecs, Hash: testutils.RandomHash(rng)} ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, nil) // no auth events data, blobHashes, err := dataAndHashesFromTxs(ctx, types.Transactions{calldataTx}, &config, batcherAddr, l1F, ref, logger) @@ -241,7 +242,7 @@ func TestDataAndHashesFromTxsEventAuth(t *testing.T) { // emitted by that same alt address — should be accepted. calldataTx, _ := types.SignNewTx(altKey, signer, txData) - ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)} + ref := eth.L1BlockRef{Number: 1, Time: BatchAuthEnforcementDelaySecs, Hash: testutils.RandomHash(rng)} batchHash := ComputeCalldataBatchHash(calldataTx.Data()) ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, altAddr, []common.Hash{batchHash}) @@ -268,7 +269,7 @@ func TestDataAndHashesFromTxsEventAuth(t *testing.T) { // The submitter must match the auth caller — should be rejected. calldataTx, _ := types.SignNewTx(altKey, signer, txData) - ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)} + ref := eth.L1BlockRef{Number: 1, Time: BatchAuthEnforcementDelaySecs, Hash: testutils.RandomHash(rng)} batchHash := ComputeCalldataBatchHash(calldataTx.Data()) ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, []common.Hash{batchHash}) @@ -302,7 +303,7 @@ func TestDataAndHashesFromTxsEventAuth(t *testing.T) { tx2 := newInboxTx(privateKey) tx3 := newInboxTx(altKey) - ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)} + ref := eth.L1BlockRef{Number: 1, Time: BatchAuthEnforcementDelaySecs, Hash: testutils.RandomHash(rng)} ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, []common.Hash{ComputeCalldataBatchHash(tx1.Data())}) @@ -337,7 +338,7 @@ func TestDataAndHashesFromTxsEventAuth(t *testing.T) { BlobHashes: []common.Hash{blobHash}, }) - ref := eth.L1BlockRef{Number: 1, Hash: testutils.RandomHash(rng)} + ref := eth.L1BlockRef{Number: 1, Time: BatchAuthEnforcementDelaySecs, Hash: testutils.RandomHash(rng)} ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, []common.Hash{ ComputeCalldataBatchHash(calldataTx.Data()), ComputeBlobBatchHash([]common.Hash{blobHash}), @@ -360,9 +361,11 @@ func TestDataAndHashesFromTxsEventAuth(t *testing.T) { // blob data source path (dataAndHashesFromTxs) across a single fixed DataSourceConfig. // // This is the path a chain with Ecotone active actually runs: OpenData always selects the -// blob source, and calldata (type-2) batches flow through its non-blob branch. Pre-Espresso -// (L1 origin time < EspressoTime) must use upstream sender-based authorization with no event -// scanning; at and after activation it must switch to event-based authentication. +// blob source, and calldata (type-2) batches flow through its non-blob branch. It walks the +// gate across the fork boundary: sender-based authorization (no event scanning) both pre-fork +// AND through the grace window [EspressoTime, EspressoTime+BatchAuthEnforcementDelaySecs), then +// event-based authentication from EspressoTime+BatchAuthEnforcementDelaySecs onward. See +// BatchAuthEnforcementDelaySecs (params.go) for the full grace-period mechanism. func TestDataAndHashesFromTxsForkBoundary(t *testing.T) { rng := rand.New(rand.NewSource(7777)) privateKey := testutils.InsecureRandomKey(rng) @@ -432,29 +435,45 @@ func TestDataAndHashesFromTxsForkBoundary(t *testing.T) { l1F.AssertExpectations(t) }) - t.Run("activation block: same batcher tx rejected without auth event", func(t *testing.T) { - // At the exact activation time (ref.Time == EspressoTime) the event-based path is - // active, so a sender-only batcher tx is no longer sufficient. + t.Run("grace window: batcher accepted via sender auth, no event scan", func(t *testing.T) { l1F := &testutils.MockL1Source{} txData := testutils.RandomData(rng, 200) tx := newCalldataBatchTx(t, privateKey, txData) - ref := eth.L1BlockRef{Number: 1, Time: espressoTime, Hash: testutils.RandomHash(rng)} + for _, refTime := range []uint64{espressoTime, espressoTime + BatchAuthEnforcementDelaySecs - 1} { + ref := eth.L1BlockRef{Number: 1, Time: refTime, Hash: testutils.RandomHash(rng)} + data, hashes, err := dataAndHashesFromTxs(ctx, types.Transactions{tx}, &config, batcherAddr, l1F, ref, logger) + require.NoError(t, err) + require.Equal(t, 1, len(data), "grace-window batcher tx should be accepted via sender-based auth") + require.Equal(t, 0, len(hashes)) + require.Equal(t, eth.Data(txData), *data[0].calldata) + } + l1F.AssertExpectations(t) + }) + + t.Run("enforcement time: same batcher tx rejected without auth event", func(t *testing.T) { + // At ref.Time == EspressoTime+BatchAuthEnforcementDelaySecs the event-based path is + // enforced, so a sender-only batcher tx is no longer sufficient. + l1F := &testutils.MockL1Source{} + txData := testutils.RandomData(rng, 200) + tx := newCalldataBatchTx(t, privateKey, txData) + + ref := eth.L1BlockRef{Number: 1, Time: espressoTime + BatchAuthEnforcementDelaySecs, Hash: testutils.RandomHash(rng)} ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, nil) data, hashes, err := dataAndHashesFromTxs(ctx, types.Transactions{tx}, &config, batcherAddr, l1F, ref, logger) require.NoError(t, err) - require.Equal(t, 0, len(data), "post-fork batcher tx without an auth event must be rejected") + require.Equal(t, 0, len(data), "enforced batcher tx without an auth event must be rejected") require.Equal(t, 0, len(hashes)) l1F.AssertExpectations(t) }) - t.Run("activation block: same batcher tx accepted with auth event", func(t *testing.T) { + t.Run("enforcement time: same batcher tx accepted with auth event", func(t *testing.T) { l1F := &testutils.MockL1Source{} txData := testutils.RandomData(rng, 200) tx := newCalldataBatchTx(t, privateKey, txData) - ref := eth.L1BlockRef{Number: 1, Time: espressoTime, Hash: testutils.RandomHash(rng)} + ref := eth.L1BlockRef{Number: 1, Time: espressoTime + BatchAuthEnforcementDelaySecs, Hash: testutils.RandomHash(rng)} batchHash := ComputeCalldataBatchHash(tx.Data()) ref = mockAuthEvents(l1F, rng, ref, authenticatorAddr, batcherAddr, []common.Hash{batchHash}) diff --git a/op-node/rollup/derive/params.go b/op-node/rollup/derive/params.go index 010e84fe46f..cc250eabfd7 100644 --- a/op-node/rollup/derive/params.go +++ b/op-node/rollup/derive/params.go @@ -21,7 +21,7 @@ func frameSize(frame Frame) uint64 { // or transaction per block allowed in a span batch. const MaxSpanBatchElementCount = 10_000_000 -// BatchAuthLookbackWindow is the number of L1 blocks before a batch submission to +// BatchAuthLookbackWindow is the maximum number of L1 blocks before a batch submission to // scan for a BatchInfoAuthenticated event. The authentication transaction must land // in this window (or in the same block as the batch submission) for the batch to be // considered valid post-Espresso. @@ -31,6 +31,39 @@ const MaxSpanBatchElementCount = 10_000_000 // L1 congestion or batcher restarts. const BatchAuthLookbackWindow uint64 = 100 +// BatchAuthEnforcementDelaySecs is the number of seconds after the EspressoTime activation +// during which derivation still accepts sender-authenticated batches. Event-based batch +// authentication is only enforced for L1 blocks with origin time >= EspressoTime + +// BatchAuthEnforcementDelaySecs. +// +// This is the canonical description of the grace-period mechanism; isEspressoAuthEnforced +// (batch_authenticator.go), the op-batcher fallback-auth gate (isFallbackAuthRequired in +// espresso_fallback_gate.go), and the fork-boundary tests all defer here. +// +// Behavior by L1 origin time t relative to EspressoTime (E): +// - before enforcement (t < E+delay, i.e. pre-fork or within the grace window): +// upstream sender-based authorization — the batch tx's L1 sender must equal the +// configured batcher address. Auth events are not scanned. +// - enforced (t >= E+delay): event-based authentication only — the batch's commitment +// must have a BatchInfoAuthenticated event within BatchAuthLookbackWindow AND the tx's +// L1 sender must equal the caller that emitted it. Sender-only authorization is rejected. +// +// The grace period lets the batcher switch to authenticated submission at activation +// without a configured lead time. The batcher's gate flips at plain activation (L1 tip +// time >= EspressoTime), a full grace period before the verifier enforces. Because the +// batcher decides on the L1 tip time while the verifier judges by the batch tx's +// containing-block time, a batch decided pre-fork can land in a post-activation block; +// accepting sender auth through the window keeps it valid as long as its inclusion delay +// stays below the grace period. The reverse asymmetry (an authenticated tx landing before +// enforcement) is harmless: pre-enforcement the verifier uses sender-based authorization +// and the auth event is just an unrelated L1 tx that does not affect derivation. +// +// Sized to the duration of one full BatchAuthLookbackWindow at the nominal 12s L1 slot +// time (~20 minutes) — far above any realistic L1 inclusion delay. Under missed L1 +// slots the delay covers fewer than BatchAuthLookbackWindow blocks, which is fine: the +// bound that matters is time (inclusion delay), not block count. +const BatchAuthEnforcementDelaySecs uint64 = BatchAuthLookbackWindow * 12 + // DuplicateErr is returned when a newly read frame is already known var DuplicateErr = errors.New("duplicate frame") diff --git a/op-service/testutils/fake_txmgr.go b/op-service/testutils/fake_txmgr.go index 3f8d309f09d..a049b55ede9 100644 --- a/op-service/testutils/fake_txmgr.go +++ b/op-service/testutils/fake_txmgr.go @@ -60,6 +60,10 @@ func (f *FakeTxMgr) SendAsync(ctx context.Context, candidate txmgr.TxCandidate, } ch <- sendResponse } +func (f *FakeTxMgr) SendPairAsync(ctx context.Context, first txmgr.TxCandidate, second txmgr.TxCandidate, firstCh chan txmgr.SendResponse, secondCh chan txmgr.SendResponse) { + f.SendAsync(ctx, first, firstCh) + f.SendAsync(ctx, second, secondCh) +} func (f *FakeTxMgr) ChainID() eth.ChainID { return f.chainId } diff --git a/op-service/txmgr/espresso_txmgr.go b/op-service/txmgr/espresso_txmgr.go new file mode 100644 index 00000000000..275e205037c --- /dev/null +++ b/op-service/txmgr/espresso_txmgr.go @@ -0,0 +1,264 @@ +package txmgr + +import ( + "context" + "errors" + "fmt" + "sync" + "time" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + + "github.com/ethereum-optimism/optimism/op-service/retry" +) + +// ErrPairLegCancelled is delivered as the second leg's response error when the +// first leg of a SendPairAsync pair fails permanently and the second leg is +// cancelled as a result. +var ErrPairLegCancelled = errors.New("paired transaction cancelled: first leg failed") + +// errPairBlobsUnsupported is returned when a pair candidate carries blobs; +// see the blob paragraph in SendPairAsync's doc for why. +var errPairBlobsUnsupported = errors.New("SendPairAsync does not support blob candidates") + +func getContext(ctx context.Context, txTimeout time.Duration) (context.Context, context.CancelFunc) { + if txTimeout == 0 { + return context.WithCancel(ctx) + } + return context.WithTimeout(ctx, txTimeout) +} + +// sendPair drives the pair to resolution on its own goroutine: it broadcasts +// both legs concurrently, cancels the second leg if the first fails permanently, +// and delivers exactly one response per channel. A second leg cancelled this way +// has its error wrapped in ErrPairLegCancelled so callers can tell it did not +// fail on its own. +// +// Before any response is delivered, the pair repairs the nonce of each leg that +// resolved with an error (repairPair): the nonce is consumed on chain by a +// fee-bumped no-op, and sendPair blocks until that settles. Why: an errored leg +// is in an unknown state — possibly never broadcast, possibly in the pool at +// heavily bumped fees, possibly even mined unseen. Stock recovery (resetNonce, +// then re-signing the same nonces) recovers by collision with those orphans, +// which is safe for self-contained txs (a stale winner is at worst a duplicate) +// but not for pairs: a failure makes the batcher rewind and re-pack, so resent +// legs carry a different commitment than the orphans they contest. A stale leg +// winning one nonce while a fresh leg wins the adjacent one mines a torn pair — +// auth and batch with mismatched commitments, ignored by derivation — and the +// recovery round has itself half-failed, leaving fresh orphans behind. +// +// Repairing before responding keeps the failure self-contained: the error +// cannot cancel the queue's error group, and so cannot trigger resubmission, +// until this pair's nonces are already consumed — the resubmission's nonce +// query starts above everything the pair left behind. +// +// Aborted siblings get no such guarantee. A pair cancelled by the group abort +// (parent context already dead) only resets the nonce — see repairPair's +// parent-context branch — leaving its legs wherever cancellation caught them: +// That spoilage is self-limiting: a stale leg can only cause damage by +// contesting a slot, and a contested slot that fails a round becomes an +// errored leg of that round's pair — so repair settles the very slots the +// damage occurred at, and each abort's leftovers are cleaned up by the first +// round they hurt. It is accepted because settling every aborted sibling +// eagerly would stall the queue's drain barrier a block per abort, scaling +// with the pipeline depth. +func (m *SimpleTxManager) sendPair(parentCtx context.Context, tx1, tx2 *types.Transaction, ch1, ch2 chan SendResponse) { + defer func() { m.metr.RecordPendingTx(m.pending.Add(-2)) }() + ctx1, cancel1 := getContext(parentCtx, m.cfg.TxSendTimeout) + ctx2, cancel2 := getContext(parentCtx, m.cfg.TxSendTimeout) + defer cancel1() + defer cancel2() + + res2 := make(chan SendResponse, 1) + go func() { + receipt, err := m.sendTx(ctx2, tx2) + res2 <- SendResponse{Receipt: receipt, Nonce: tx2.Nonce(), Err: err} + }() + + receipt1, err1 := m.sendTx(ctx1, tx1) + + // A mined-but-reverted first leg also fails the pair, not just a failed + // send: the second leg depends on the first leg's successful execution. + leg1Failed := err1 != nil || (receipt1 != nil && receipt1.Status != types.ReceiptStatusSuccessful) + if leg1Failed { + m.l.Warn("pair first leg failed; cancelling second leg", + "firstNonce", tx1.Nonce(), "secondNonce", tx2.Nonce(), + "err", err1, "reverted", err1 == nil) + cancel2() + } + + resp2 := <-res2 + + m.repairPair(parentCtx, tx1, err1, tx2, resp2.Err) + + // A second leg that errored after the first leg failed did not fail on + // its own — it was cancelled because the pair failed; report it as such. + if leg1Failed && resp2.Err != nil { + resp2.Err = fmt.Errorf("%w: %w", ErrPairLegCancelled, resp2.Err) + } + ch1 <- SendResponse{Receipt: receipt1, Nonce: tx1.Nonce(), Err: err1} + ch2 <- resp2 +} + +// repairPair consumes any nonce the pair would otherwise leave unmined: a leg +// that resolved with an error never mined, so its nonce gets a cancellation +// no-op (see cancelPairNonce) +func (m *SimpleTxManager) repairPair(parentCtx context.Context, tx1 *types.Transaction, err1 error, tx2 *types.Transaction, err2 error) { + // Nothing to repair when both legs mined + if err1 == nil && err2 == nil { + return + } + + if parentCtx.Err() != nil { + // Canceled parent (shutdown, or a sibling send failing the queue's + // error group): don't publish no-ops, but reset the nonce so the next + // send re-queries the chain and refills any gap the pair leaves. + m.l.Info("pair send context cancelled; resetting nonce and leaving in-flight legs to resolve in the pool", + "firstNonce", tx1.Nonce(), "secondNonce", tx2.Nonce()) + m.resetNonce() + return + } + + repairCtx, cancel := getContext(context.WithoutCancel(parentCtx), m.cfg.TxSendTimeout) + defer cancel() + + var repairs sync.WaitGroup + if err1 != nil { + repairs.Add(1) + go func() { + defer repairs.Done() + m.cancelPairNonce(repairCtx, tx1) + }() + } + if err2 != nil { + repairs.Add(1) + go func() { + defer repairs.Done() + m.cancelPairNonce(repairCtx, tx2) + }() + } + repairs.Wait() +} + +// SendPairAsync implements TxManager.SendPairAsync — see the interface doc +// for the pair contract. It crafts both legs synchronously and hands them to +// sendPair; cancellation and nonce repair live in repairPair and +// cancelPairNonce. +func (m *SimpleTxManager) SendPairAsync( + ctx context.Context, + firstCandidate TxCandidate, + secondCandidate TxCandidate, + firstRespCh chan SendResponse, + secondRespCh chan SendResponse, +) { + if cap(firstRespCh) == 0 || cap(secondRespCh) == 0 { + panic("SendPairAsync: channels must be buffered") + } + + respondErr := func(err1 error, err2 error) { + firstRespCh <- SendResponse{Err: err1} + secondRespCh <- SendResponse{Err: err2} + } + + if len(firstCandidate.Blobs) > 0 || len(secondCandidate.Blobs) > 0 { + respondErr(errPairBlobsUnsupported, errPairBlobsUnsupported) + return + } + + // refuse new requests if the tx manager is closed + if m.closed.Load() { + respondErr(ErrClosed, ErrClosed) + return + } + + prepareCtx, cancel := getContext(ctx, m.cfg.TxSendTimeout) + tx1, err := m.prepare(prepareCtx, firstCandidate) + cancel() + if err != nil { + m.resetNonce() + respondErr(err, fmt.Errorf("%w: first leg preparation failed: %w", ErrPairLegCancelled, err)) + return + } + + prepareCtx, cancel = getContext(ctx, m.cfg.TxSendTimeout) + tx2, err := m.prepare(prepareCtx, secondCandidate) + cancel() + if err != nil { + m.resetNonce() + respondErr(fmt.Errorf("pair aborted before broadcast: second leg preparation failed: %w", err), err) + return + } + + m.metr.RecordPendingTx(m.pending.Add(2)) + + go m.sendPair(ctx, tx1, tx2, firstRespCh, secondRespCh) +} + +// cancelPairNonce consumes target's nonce on chain by publishing a fee-bumped +// no-op (zero-value self-send) at that exact nonce, and waits for the nonce +// to be consumed — by the no-op, or by target itself if it wins the race +// (both outcomes end with the nonce deterministically spent). This is what +// lets a failed pair guarantee it leaves no gap and no live orphan behind +// without a global nonce reset. +// +// Cancellation is a replacement: the no-op's fees start bumped above target's +// original fees (satisfying the pool's replacement rules), and sendTx's +// underpriced handling keeps bumping if a higher-fee version of target is +// still floating. If the fee limit prevents out-pricing target, target itself +// is high-fee enough to mine, which also consumes the nonce. +// +// On failure (e.g. RPC down for the whole attempt) it falls back to +// resetNonce, restoring the legacy behavior where the next send re-queries +// the chain nonce and refills the gap. +func (m *SimpleTxManager) cancelPairNonce(ctx context.Context, target *types.Transaction) { + l := m.l.New("nonce", target.Nonce(), "replaces", target.Hash()) + noopTx, err := retry.Do(ctx, 5, retry.Fixed(2*time.Second), func() (*types.Transaction, error) { + return m.craftPairCancelTx(ctx, target) + }) + if err != nil { + l.Error("failed to craft pair cancellation tx; falling back to nonce reset", "err", err) + m.resetNonce() + return + } + + if _, err := m.sendTx(ctx, noopTx); err != nil { + if errors.Is(err, core.ErrNonceTooLow) { + // The nonce was consumed by another transaction — target itself, + // or an earlier cancellation. Either way it is spent, which is + // all the pair needs. + l.Info("pair nonce consumed by another transaction before cancellation") + return + } + l.Error("failed to cancel pair nonce; falling back to nonce reset", "err", err) + m.resetNonce() + return + } + l.Info("pair nonce cancelled", "cancelTx", noopTx.Hash()) +} + +// craftPairCancelTx builds and signs the no-op replacement +func (m *SimpleTxManager) craftPairCancelTx(ctx context.Context, target *types.Transaction) (*types.Transaction, error) { + tip, baseFee, _, _, err := m.SuggestGasPriceCaps(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get gas price info: %w", err) + } + bumpedTip, bumpedFee := updateFees(target.GasTipCap(), target.GasFeeCap(), tip, baseFee, false, m.l) + if err := m.checkLimits(tip, baseFee, bumpedTip, bumpedFee); err != nil { + return nil, fmt.Errorf("fee limits exceeded for pair cancellation tx: %w", err) + } + + to := m.cfg.From + msg := &types.DynamicFeeTx{ + ChainID: m.chainID, + Nonce: target.Nonce(), + To: &to, + GasTipCap: bumpedTip, + GasFeeCap: bumpedFee, + Gas: params.TxGas, + } + signerCtx, cancel := context.WithTimeout(ctx, m.cfg.NetworkTimeout) + defer cancel() + return m.cfg.Signer(signerCtx, m.cfg.From, types.NewTx(msg)) +} diff --git a/op-service/txmgr/espresso_txmgr_test.go b/op-service/txmgr/espresso_txmgr_test.go new file mode 100644 index 00000000000..fe1e2881f36 --- /dev/null +++ b/op-service/txmgr/espresso_txmgr_test.go @@ -0,0 +1,309 @@ +package txmgr + +import ( + "context" + "slices" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics" +) + +// pairTestHarness runs a real SimpleTxManager against the mock backend and +// records every broadcast so tests can assert on cancellation no-ops and nonce +// hygiene — the failure modes SendPairAsync exists to contain. +type pairTestHarness struct { + mgr *SimpleTxManager + backend *mockBackendWithNonce + + mu sync.Mutex + broadcasts []*types.Transaction +} + +// pairLegBehavior tells the harness's tx sender what to do with a leg, +// identified by its first data byte. +type pairLegBehavior struct { + sendErr error // returned from every SendTransaction call for this leg + mine bool // mine the tx (at its current fees) as soon as it is sent + mineStatus uint64 +} + +func newPairTestHarness(t *testing.T, behaviors map[byte]pairLegBehavior) *pairTestHarness { + backend := newMockBackendWithNonce(newGasPricer(1)) + conf := configWithNumConfs(1) + conf.Backend = backend + + mgr, err := NewSimpleTxManagerFromConfig("TEST", testlog.Logger(t, log.LevelCrit), &metrics.NoopTxMetrics{}, conf) + require.NoError(t, err) + + h := &pairTestHarness{mgr: mgr, backend: backend} + backend.setTxSender(func(ctx context.Context, tx *types.Transaction) error { + h.mu.Lock() + h.broadcasts = append(h.broadcasts, tx) + h.mu.Unlock() + + // Cancellation no-ops (empty-data self-sends crafted by + // cancelPairNonce) are always accepted and mined immediately. + if len(tx.Data()) == 0 { + hash := tx.Hash() + backend.mine(&hash, tx.GasFeeCap(), nil) + return nil + } + b, ok := behaviors[tx.Data()[0]] + require.True(t, ok, "unexpected tx broadcast with marker %d", tx.Data()[0]) + if b.sendErr != nil { + return b.sendErr + } + if b.mine { + hash := tx.Hash() + backend.mineWithStatus(&hash, tx.GasFeeCap(), nil, b.mineStatus) + } + return nil + }) + return h +} + +// noopsAt returns the nonces of broadcast cancellation no-ops (empty-data +// self-sends), sorted. +func (h *pairTestHarness) noopsAt() []uint64 { + h.mu.Lock() + defer h.mu.Unlock() + var nonces []uint64 + for _, tx := range h.broadcasts { + if len(tx.Data()) == 0 && tx.To() != nil && *tx.To() == h.mgr.cfg.From { + nonces = append(nonces, tx.Nonce()) + } + } + slices.Sort(nonces) + return slices.Compact(nonces) +} + +func pairCandidate(marker byte) TxCandidate { + return TxCandidate{TxData: []byte{marker}, To: &common.Address{0xbb}} +} + +func sendPair(mgr *SimpleTxManager) (SendResponse, SendResponse) { + ch1 := make(chan SendResponse, 1) + ch2 := make(chan SendResponse, 1) + mgr.SendPairAsync(context.Background(), pairCandidate(1), pairCandidate(2), ch1, ch2) + return <-ch1, <-ch2 +} + +// mockBackendWithNonce initializes the manager's nonce from len(minedTxs), so +// the first pair takes nonces 0 and 1 and a follow-up send takes 2. +const pairBaseNonce = uint64(0) + +// TestSendPair_Success: both legs mine; consecutive nonces in argument order; +// no cancellation no-ops are broadcast. +func TestSendPair_Success(t *testing.T) { + h := newPairTestHarness(t, map[byte]pairLegBehavior{ + 1: {mine: true, mineStatus: types.ReceiptStatusSuccessful}, + 2: {mine: true, mineStatus: types.ReceiptStatusSuccessful}, + }) + + r1, r2 := sendPair(h.mgr) + + require.NoError(t, r1.Err) + require.NoError(t, r2.Err) + require.Equal(t, pairBaseNonce, r1.Nonce) + require.Equal(t, pairBaseNonce+1, r2.Nonce, "pair legs must take consecutive nonces in argument order") + require.Empty(t, h.noopsAt(), "a healthy pair must not broadcast cancellation no-ops") +} + +// TestSendPair_FirstLegSendFailure is the nonce-gap hazard the pair exists to +// contain: the auth-style first leg fails permanently (a critical nonce error +// — txmgr treats unknown RPC errors as transient and retries them forever, so +// permanent failures are SendState critical errors, timeouts, or +// cancellations) after the second leg was already broadcast at the next +// nonce. The pair must (a) cancel the orphaned second leg with a no-op at its +// exact nonce, (b) fill the first leg's own nonce hole the same way, and (c) +// leave the manager unwedged: the next send gets the next fresh nonce with no +// reset and confirms normally. +func TestSendPair_FirstLegSendFailure(t *testing.T) { + h := newPairTestHarness(t, map[byte]pairLegBehavior{ + 1: {sendErr: core.ErrNonceTooLow}, + 2: {}, // accepted into the pool, never mined (gapped behind leg 1) + 3: {mine: true, mineStatus: types.ReceiptStatusSuccessful}, // follow-up send + }) + + r1, r2 := sendPair(h.mgr) + + require.Error(t, r1.Err) + require.ErrorIs(t, r2.Err, ErrPairLegCancelled) + require.Equal(t, []uint64{pairBaseNonce, pairBaseNonce + 1}, h.noopsAt(), + "both pair nonces must be consumed by cancellation no-ops") + + // No wedge and no nonce reset: the next send continues the sequence at the + // nonce after the repaired pair and confirms. + ch := make(chan SendResponse, 1) + h.mgr.SendAsync(context.Background(), pairCandidate(3), ch) + r3 := <-ch + require.NoError(t, r3.Err) + require.Equal(t, pairBaseNonce+2, r3.Nonce) +} + +// TestSendPair_FirstLegReverted: a mined-but-reverted first leg also fails the +// pair (its event never fired), so the second leg is cancelled — but the first +// leg's nonce was consumed on chain, so only the second leg's nonce gets a +// no-op. The first leg's response keeps SendAsync semantics: a receipt with +// failed status and no error. +func TestSendPair_FirstLegReverted(t *testing.T) { + h := newPairTestHarness(t, map[byte]pairLegBehavior{ + 1: {mine: true, mineStatus: types.ReceiptStatusFailed}, + 2: {}, // accepted, never mined; must be cancelled + 3: {mine: true, mineStatus: types.ReceiptStatusSuccessful}, // follow-up send + }) + + r1, r2 := sendPair(h.mgr) + + require.NoError(t, r1.Err, "a mined-but-reverted leg is a receipt, not a send error") + require.NotNil(t, r1.Receipt) + require.Equal(t, types.ReceiptStatusFailed, r1.Receipt.Status) + require.ErrorIs(t, r2.Err, ErrPairLegCancelled) + require.Equal(t, []uint64{pairBaseNonce + 1}, h.noopsAt(), + "only the unmined second leg's nonce needs a cancellation no-op") + + ch := make(chan SendResponse, 1) + h.mgr.SendAsync(context.Background(), pairCandidate(3), ch) + r3 := <-ch + require.NoError(t, r3.Err) + require.Equal(t, pairBaseNonce+2, r3.Nonce) +} + +// TestSendPair_ParentContextCanceled: a pair canceled via its parent context +// (shutdown, or a sibling failing the queue's error group) must not broadcast +// no-ops, but must reset the cached nonce so the next send refills the gap. +func TestSendPair_ParentContextCanceled(t *testing.T) { + h := newPairTestHarness(t, map[byte]pairLegBehavior{ + 1: {}, // broadcast accepted, never mines + 2: {}, + }) + + ctx, cancel := context.WithCancel(context.Background()) + ch1 := make(chan SendResponse, 1) + ch2 := make(chan SendResponse, 1) + h.mgr.SendPairAsync(ctx, pairCandidate(1), pairCandidate(2), ch1, ch2) + cancel() + + r1, r2 := <-ch1, <-ch2 + require.Error(t, r1.Err) + require.Error(t, r2.Err) + + require.Empty(t, h.noopsAt(), "no cancellation no-ops may be broadcast from a canceled parent context") + + h.mgr.nonceLock.RLock() + nonce := h.mgr.nonce + h.mgr.nonceLock.RUnlock() + require.Nil(t, nonce, "nonce must be reset so the next send re-queries the chain and refills the gap") +} + +// TestQueue_SendPair_Pipelines proves a pair holds exactly one maxPending +// slot, in both directions. At most one: with maxPending=2, two pairs (four +// txs) are all broadcast before anything confirms — per-tx slot accounting or +// per-pair serialization would block this. At least one: a third pair is not +// admitted (SendPair stays blocked, no nonces assigned) until a first pair +// confirms and frees its slot. +func TestQueue_SendPair_Pipelines(t *testing.T) { + const maxPending = 2 // queue slots + const numPairs = 2 // one slot each; 2*numPairs txs in flight at once + const numTxs = 2 * numPairs + + wg := sync.WaitGroup{} + backend := newMockBackendWithConfirmationDelay(newGasPricer(3), &wg) + conf := configWithNumConfs(1) + conf.Backend = backend + // Keep resubmission fee-bumps out of the test's timing windows: a bumped + // tx is a new hash, which the delay backend would count against the + // WaitGroup unexpectedly. + conf.RebroadcastInterval.Store(int64(time.Minute)) + conf.ResubmissionTimeout.Store(int64(time.Minute)) + mgr, err := NewSimpleTxManagerFromConfig("TEST", testlog.Logger(t, log.LevelCrit), &metrics.NoopTxMetrics{}, conf) + require.NoError(t, err) + + q := NewQueue[int](context.Background(), mgr, maxPending) + + emptyCandidate := TxCandidate{To: &common.Address{}} + + receiptChs := make([]chan TxReceipt[int], numTxs) + wg.Add(numTxs) + for pair := range numPairs { + id := 2 * pair + receiptChs[id] = make(chan TxReceipt[int], 1) + receiptChs[id+1] = make(chan TxReceipt[int], 1) + q.SendPair( + id, emptyCandidate, receiptChs[id], + id+1, emptyCandidate, receiptChs[id+1], + ) + } + + // cachedNonces returns the sorted nonces of every tx the backend has seen; + // nonceSeq builds the expected contiguous sequence from startingNonce. + cachedNonces := func() []uint64 { + nonces := make([]uint64, 0, len(backend.cachedTxs)) + for _, tx := range backend.cachedTxs { + nonces = append(nonces, tx.Nonce()) + } + slices.Sort(nonces) + return nonces + } + nonceSeq := func(n int) []uint64 { + seq := make([]uint64, n) + for i := range seq { + seq[i] = startingNonce + uint64(i) + } + return seq + } + + // All four txs reach the backend while nothing has confirmed yet: the two + // pairs are genuinely in flight concurrently. + wg.Wait() + require.Equal(t, nonceSeq(numTxs), cachedNonces()) + + // Both slots are held, so a third pair must block in SendPair without + // being admitted (and without nonces assigned — they would land at +4/+5 + // only after admission). + var thirdAdmitted atomic.Bool + thirdCh1 := make(chan TxReceipt[int], 1) + thirdCh2 := make(chan TxReceipt[int], 1) + wg.Add(2) + go func() { + q.SendPair( + numTxs, emptyCandidate, thirdCh1, + numTxs+1, emptyCandidate, thirdCh2, + ) + thirdAdmitted.Store(true) + }() + require.Never(t, thirdAdmitted.Load, 1*time.Second, 10*time.Millisecond, + "a third pair was admitted while both slots were held: a pair must cost one slot") + + // Confirming the first two pairs frees their slots; the third pair is then + // admitted and broadcasts at the next nonces. + backend.MineAll() + require.Eventually(t, thirdAdmitted.Load, 10*time.Second, 10*time.Millisecond, + "third pair was not admitted after slots freed") + wg.Wait() // third pair's txs reach the backend + backend.MineAll() + require.NoError(t, q.Wait()) + + for i, ch := range append(receiptChs, thirdCh1, thirdCh2) { + select { + case r := <-ch: + require.NoError(t, r.Err, "receipt %d", i) + case <-time.After(10 * time.Second): + t.Fatalf("timed out waiting for receipt %d", i) + } + } + + // The third pair's nonces continue the sequence, assigned only after + // admission. + require.Equal(t, nonceSeq(numTxs+2), cachedNonces()) +} diff --git a/op-service/txmgr/mocks/TxManager.go b/op-service/txmgr/mocks/TxManager.go index db3acedf199..0f282b9abb9 100644 --- a/op-service/txmgr/mocks/TxManager.go +++ b/op-service/txmgr/mocks/TxManager.go @@ -168,6 +168,11 @@ func (_m *TxManager) SendAsync(ctx context.Context, candidate txmgr.TxCandidate, _m.Called(ctx, candidate, ch) } +// SendPairAsync provides a mock function with given fields: ctx, first, second, firstCh, secondCh +func (_m *TxManager) SendPairAsync(ctx context.Context, first txmgr.TxCandidate, second txmgr.TxCandidate, firstCh chan txmgr.SendResponse, secondCh chan txmgr.SendResponse) { + _m.Called(ctx, first, second, firstCh, secondCh) +} + // SuggestGasPriceCaps provides a mock function with given fields: ctx func (_m *TxManager) SuggestGasPriceCaps(ctx context.Context) (*big.Int, *big.Int, *big.Int, *big.Int, error) { ret := _m.Called(ctx) diff --git a/op-service/txmgr/queue.go b/op-service/txmgr/queue.go index 220b98567c1..da9823ebcde 100644 --- a/op-service/txmgr/queue.go +++ b/op-service/txmgr/queue.go @@ -2,6 +2,7 @@ package txmgr import ( "context" + "errors" "math" "sync" @@ -83,6 +84,25 @@ func (q *Queue[T]) Send(id T, candidate TxCandidate, receiptCh chan TxReceipt[T] q.txMgr.SendAsync(ctx, candidate, responseChan) // Nonce management handled synchronously, i.e. before this returns } +// SendPair sends an ordered pair of transactions — see TxManager.SendPairAsync +// for the pair contract — while holding a single maxPending slot for the whole +// pair, so pairs pipeline at the same depth as individual txs sent via Send. +// +// Exactly one receipt is forwarded on each receipt channel. Like Send, this +// blocks while the queue is at its maxPending limit, and a pair failure +// cancels the queue's shared error group. +func (q *Queue[T]) SendPair(firstID T, first TxCandidate, firstCh chan TxReceipt[T], secondID T, second TxCandidate, secondCh chan TxReceipt[T]) { + group, ctx := q.groupContext() + r1 := make(chan SendResponse, 1) + r2 := make(chan SendResponse, 1) + group.Go(func() error { // one slot for the whole pair + err1 := handleResponse(ctx, r1, firstCh, firstID) + err2 := handleResponse(ctx, r2, secondCh, secondID) + return errors.Join(err1, err2) + }) + q.txMgr.SendPairAsync(ctx, first, second, r1, r2) // nonces for both legs assigned synchronously, in order +} + // TrySend sends the next tx, but only if the number of pending txs is below the // max pending. // diff --git a/op-service/txmgr/txmgr.go b/op-service/txmgr/txmgr.go index d0a99606dac..53d6e8d72bb 100644 --- a/op-service/txmgr/txmgr.go +++ b/op-service/txmgr/txmgr.go @@ -91,6 +91,34 @@ type TxManager interface { // the order of nonce increments. SendAsync(ctx context.Context, candidate TxCandidate, ch chan SendResponse) + // SendPairAsync submits two transactions as an ordered pair: both are crafted + // synchronously with nonces assigned in argument order (so the first leg + // precedes the second on chain), then broadcast back-to-back without waiting + // for the first to confirm, so pairs pipeline like individual SendAsync calls. + // + // Use a pair when the second transaction is only valid if the first executes + // successfully. If the first leg fails permanently (send failure, or mined + // but reverted), the second leg is cancelled with a fee-bumped no-op at its + // exact nonce; cancellation is best-effort, so callers must tolerate an + // orphaned second leg still landing on chain. A leg that fails in flight + // (broadcast but unmined) has its nonce consumed by that no-op rather than by + // a global nonce reset, so it leaves no gap and does not disturb concurrently + // pending transactions at higher nonces. The nonce IS reset — falling back to + // the usual "next send re-queries the chain" behavior — in three cases: a leg + // fails during preparation (before broadcast), the parent ctx is cancelled + // (shutdown, or a sibling send failing the queue's error group), or the no-op + // repair itself cannot be crafted or sent. + // + // Both response channels must be buffered; exactly one response is delivered + // on each, and only once both nonces are consumed on chain (repairs + // included). Repairs survive cancellation of ctx (bounded by TxSendTimeout + // when configured); if ctx is already cancelled no no-ops are published — the + // nonce is reset and in-flight legs are left to resolve in the pool. + // + // Blob candidates are not supported: the cancellation no-op cannot replace a + // blob transaction. + SendPairAsync(ctx context.Context, first TxCandidate, second TxCandidate, firstCh chan SendResponse, secondCh chan SendResponse) + // ChainID returns the chain this tx-manager is connected to ChainID() eth.ChainID diff --git a/op-service/txmgr/txmgr_test.go b/op-service/txmgr/txmgr_test.go index 9b1020c876b..f070ba65be5 100644 --- a/op-service/txmgr/txmgr_test.go +++ b/op-service/txmgr/txmgr_test.go @@ -217,6 +217,7 @@ type minedTxInfo struct { gasFeeCap *big.Int blobFeeCap *big.Int blockNumber uint64 + status uint64 } // mockBackend implements ReceiptSource that tracks mined transactions @@ -251,6 +252,12 @@ func (b *mockBackend) setTxSender(s sendTransactionFunc) { // TransactionReceipt with a matching txHash will result in a non-nil receipt. // If a nil txHash is supplied this has the effect of mining an empty block. func (b *mockBackend) mine(txHash *common.Hash, gasFeeCap, blobFeeCap *big.Int) { + b.mineWithStatus(txHash, gasFeeCap, blobFeeCap, types.ReceiptStatusSuccessful) +} + +// mineWithStatus is mine with an explicit receipt execution status, to +// simulate mined-but-reverted transactions. +func (b *mockBackend) mineWithStatus(txHash *common.Hash, gasFeeCap, blobFeeCap *big.Int, status uint64) { b.mu.Lock() defer b.mu.Unlock() @@ -260,6 +267,7 @@ func (b *mockBackend) mine(txHash *common.Hash, gasFeeCap, blobFeeCap *big.Int) gasFeeCap: gasFeeCap, blobFeeCap: blobFeeCap, blockNumber: b.blockHeight, + status: status, } } } @@ -349,6 +357,7 @@ func (b *mockBackend) TransactionReceipt(ctx context.Context, txHash common.Hash GasUsed: bigs.Uint64Strict(txInfo.gasFeeCap), CumulativeGasUsed: blobFeeCap, BlockNumber: big.NewInt(int64(txInfo.blockNumber)), + Status: txInfo.status, }, nil } diff --git a/ops/docker/op-stack-go/Dockerfile.dockerignore b/ops/docker/op-stack-go/Dockerfile.dockerignore index 9800d149f6a..6193c683bf2 100644 --- a/ops/docker/op-stack-go/Dockerfile.dockerignore +++ b/ops/docker/op-stack-go/Dockerfile.dockerignore @@ -13,6 +13,7 @@ !/op-dispute-mon !/op-conductor !/op-node +!/espresso !/op-preimage !/op-program !/op-proposer