From e3fd4af127fb926c24de835266632508895b4825 Mon Sep 17 00:00:00 2001 From: healthykim Date: Tue, 3 Feb 2026 01:29:22 +0900 Subject: [PATCH] add auction and abi binding --- core/TicketAllocator.go | 603 ++++++++++++++++++++++++++ core/TicketAllocator.sol | 193 +++++++-- core/blockchain.go | 22 +- core/blockchain_reader.go | 54 ++- core/blockchain_test.go | 14 +- core/contract_caller.go | 45 ++ core/state_processor.go | 11 +- core/txpool/blobpool/blobpool.go | 12 +- core/txpool/blobpool/blobpool_test.go | 4 +- core/txpool/blobpool/interface.go | 2 +- eth/catalyst/api_test.go | 9 +- eth/protocols/eth/handler_test.go | 5 +- ethclient/simulated/rollback_test.go | 8 +- miner/worker.go | 2 +- params/protocol_params.go | 5 +- 15 files changed, 881 insertions(+), 108 deletions(-) create mode 100644 core/TicketAllocator.go create mode 100644 core/contract_caller.go diff --git a/core/TicketAllocator.go b/core/TicketAllocator.go new file mode 100644 index 0000000000..8680352e08 --- /dev/null +++ b/core/TicketAllocator.go @@ -0,0 +1,603 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package core + +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 +) + +// TicketAllocatorMetaData contains all meta data concerning the TicketAllocator contract. +var TicketAllocatorMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"name\":\"InvalidBlockNumber\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughTickets\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotSystemContract\",\"type\":\"error\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"GetBalance\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"uint16[]\",\"name\":\"\",\"type\":\"uint16[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"numTickets\",\"type\":\"uint16\"},{\"internalType\":\"uint256\",\"name\":\"bidPerTicket\",\"type\":\"uint256\"}],\"name\":\"RequestTickets\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"TIMEOUT\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"TOTAL_TICKETS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"balance\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"head\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"pendingBids\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"requestor\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"amount\",\"type\":\"uint16\"},{\"internalType\":\"uint256\",\"name\":\"bidPerTicket\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"queue\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"amount\",\"type\":\"uint16\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"bidPerTicket\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"requestor\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"senders\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"withdrawable\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", +} + +// TicketAllocatorABI is the input ABI used to generate the binding from. +// Deprecated: Use TicketAllocatorMetaData.ABI instead. +var TicketAllocatorABI = TicketAllocatorMetaData.ABI + +// TicketAllocator is an auto generated Go binding around an Ethereum contract. +type TicketAllocator struct { + TicketAllocatorCaller // Read-only binding to the contract + TicketAllocatorTransactor // Write-only binding to the contract + TicketAllocatorFilterer // Log filterer for contract events +} + +// TicketAllocatorCaller is an auto generated read-only Go binding around an Ethereum contract. +type TicketAllocatorCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TicketAllocatorTransactor is an auto generated write-only Go binding around an Ethereum contract. +type TicketAllocatorTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TicketAllocatorFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type TicketAllocatorFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TicketAllocatorSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type TicketAllocatorSession struct { + Contract *TicketAllocator // 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 +} + +// TicketAllocatorCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type TicketAllocatorCallerSession struct { + Contract *TicketAllocatorCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// TicketAllocatorTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type TicketAllocatorTransactorSession struct { + Contract *TicketAllocatorTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TicketAllocatorRaw is an auto generated low-level Go binding around an Ethereum contract. +type TicketAllocatorRaw struct { + Contract *TicketAllocator // Generic contract binding to access the raw methods on +} + +// TicketAllocatorCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type TicketAllocatorCallerRaw struct { + Contract *TicketAllocatorCaller // Generic read-only contract binding to access the raw methods on +} + +// TicketAllocatorTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type TicketAllocatorTransactorRaw struct { + Contract *TicketAllocatorTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewTicketAllocator creates a new instance of TicketAllocator, bound to a specific deployed contract. +func NewTicketAllocator(address common.Address, backend bind.ContractBackend) (*TicketAllocator, error) { + contract, err := bindTicketAllocator(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &TicketAllocator{TicketAllocatorCaller: TicketAllocatorCaller{contract: contract}, TicketAllocatorTransactor: TicketAllocatorTransactor{contract: contract}, TicketAllocatorFilterer: TicketAllocatorFilterer{contract: contract}}, nil +} + +// NewTicketAllocatorCaller creates a new read-only instance of TicketAllocator, bound to a specific deployed contract. +func NewTicketAllocatorCaller(address common.Address, caller bind.ContractCaller) (*TicketAllocatorCaller, error) { + contract, err := bindTicketAllocator(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &TicketAllocatorCaller{contract: contract}, nil +} + +// NewTicketAllocatorTransactor creates a new write-only instance of TicketAllocator, bound to a specific deployed contract. +func NewTicketAllocatorTransactor(address common.Address, transactor bind.ContractTransactor) (*TicketAllocatorTransactor, error) { + contract, err := bindTicketAllocator(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &TicketAllocatorTransactor{contract: contract}, nil +} + +// NewTicketAllocatorFilterer creates a new log filterer instance of TicketAllocator, bound to a specific deployed contract. +func NewTicketAllocatorFilterer(address common.Address, filterer bind.ContractFilterer) (*TicketAllocatorFilterer, error) { + contract, err := bindTicketAllocator(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &TicketAllocatorFilterer{contract: contract}, nil +} + +// bindTicketAllocator binds a generic wrapper to an already deployed contract. +func bindTicketAllocator(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := TicketAllocatorMetaData.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 (_TicketAllocator *TicketAllocatorRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _TicketAllocator.Contract.TicketAllocatorCaller.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 (_TicketAllocator *TicketAllocatorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TicketAllocator.Contract.TicketAllocatorTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_TicketAllocator *TicketAllocatorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _TicketAllocator.Contract.TicketAllocatorTransactor.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 (_TicketAllocator *TicketAllocatorCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _TicketAllocator.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 (_TicketAllocator *TicketAllocatorTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TicketAllocator.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_TicketAllocator *TicketAllocatorTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _TicketAllocator.Contract.contract.Transact(opts, method, params...) +} + +// GetBalance is a free data retrieval call binding the contract method 0xf8f8a912. +// +// Solidity: function GetBalance() view returns(address[], uint16[]) +func (_TicketAllocator *TicketAllocatorCaller) GetBalance(opts *bind.CallOpts) ([]common.Address, []uint16, error) { + var out []interface{} + err := _TicketAllocator.contract.Call(opts, &out, "GetBalance") + + if err != nil { + return *new([]common.Address), *new([]uint16), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + out1 := *abi.ConvertType(out[1], new([]uint16)).(*[]uint16) + + return out0, out1, err + +} + +// GetBalance is a free data retrieval call binding the contract method 0xf8f8a912. +// +// Solidity: function GetBalance() view returns(address[], uint16[]) +func (_TicketAllocator *TicketAllocatorSession) GetBalance() ([]common.Address, []uint16, error) { + return _TicketAllocator.Contract.GetBalance(&_TicketAllocator.CallOpts) +} + +// GetBalance is a free data retrieval call binding the contract method 0xf8f8a912. +// +// Solidity: function GetBalance() view returns(address[], uint16[]) +func (_TicketAllocator *TicketAllocatorCallerSession) GetBalance() ([]common.Address, []uint16, error) { + return _TicketAllocator.Contract.GetBalance(&_TicketAllocator.CallOpts) +} + +// TIMEOUT is a free data retrieval call binding the contract method 0xf56f48f2. +// +// Solidity: function TIMEOUT() view returns(uint16) +func (_TicketAllocator *TicketAllocatorCaller) TIMEOUT(opts *bind.CallOpts) (uint16, error) { + var out []interface{} + err := _TicketAllocator.contract.Call(opts, &out, "TIMEOUT") + + if err != nil { + return *new(uint16), err + } + + out0 := *abi.ConvertType(out[0], new(uint16)).(*uint16) + + return out0, err + +} + +// TIMEOUT is a free data retrieval call binding the contract method 0xf56f48f2. +// +// Solidity: function TIMEOUT() view returns(uint16) +func (_TicketAllocator *TicketAllocatorSession) TIMEOUT() (uint16, error) { + return _TicketAllocator.Contract.TIMEOUT(&_TicketAllocator.CallOpts) +} + +// TIMEOUT is a free data retrieval call binding the contract method 0xf56f48f2. +// +// Solidity: function TIMEOUT() view returns(uint16) +func (_TicketAllocator *TicketAllocatorCallerSession) TIMEOUT() (uint16, error) { + return _TicketAllocator.Contract.TIMEOUT(&_TicketAllocator.CallOpts) +} + +// TOTALTICKETS is a free data retrieval call binding the contract method 0xafbd1782. +// +// Solidity: function TOTAL_TICKETS() view returns(uint16) +func (_TicketAllocator *TicketAllocatorCaller) TOTALTICKETS(opts *bind.CallOpts) (uint16, error) { + var out []interface{} + err := _TicketAllocator.contract.Call(opts, &out, "TOTAL_TICKETS") + + if err != nil { + return *new(uint16), err + } + + out0 := *abi.ConvertType(out[0], new(uint16)).(*uint16) + + return out0, err + +} + +// TOTALTICKETS is a free data retrieval call binding the contract method 0xafbd1782. +// +// Solidity: function TOTAL_TICKETS() view returns(uint16) +func (_TicketAllocator *TicketAllocatorSession) TOTALTICKETS() (uint16, error) { + return _TicketAllocator.Contract.TOTALTICKETS(&_TicketAllocator.CallOpts) +} + +// TOTALTICKETS is a free data retrieval call binding the contract method 0xafbd1782. +// +// Solidity: function TOTAL_TICKETS() view returns(uint16) +func (_TicketAllocator *TicketAllocatorCallerSession) TOTALTICKETS() (uint16, error) { + return _TicketAllocator.Contract.TOTALTICKETS(&_TicketAllocator.CallOpts) +} + +// Balance is a free data retrieval call binding the contract method 0xe3d670d7. +// +// Solidity: function balance(address ) view returns(uint16) +func (_TicketAllocator *TicketAllocatorCaller) Balance(opts *bind.CallOpts, arg0 common.Address) (uint16, error) { + var out []interface{} + err := _TicketAllocator.contract.Call(opts, &out, "balance", arg0) + + if err != nil { + return *new(uint16), err + } + + out0 := *abi.ConvertType(out[0], new(uint16)).(*uint16) + + return out0, err + +} + +// Balance is a free data retrieval call binding the contract method 0xe3d670d7. +// +// Solidity: function balance(address ) view returns(uint16) +func (_TicketAllocator *TicketAllocatorSession) Balance(arg0 common.Address) (uint16, error) { + return _TicketAllocator.Contract.Balance(&_TicketAllocator.CallOpts, arg0) +} + +// Balance is a free data retrieval call binding the contract method 0xe3d670d7. +// +// Solidity: function balance(address ) view returns(uint16) +func (_TicketAllocator *TicketAllocatorCallerSession) Balance(arg0 common.Address) (uint16, error) { + return _TicketAllocator.Contract.Balance(&_TicketAllocator.CallOpts, arg0) +} + +// Head is a free data retrieval call binding the contract method 0xbda4fec5. +// +// Solidity: function head(address ) view returns(uint256) +func (_TicketAllocator *TicketAllocatorCaller) Head(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _TicketAllocator.contract.Call(opts, &out, "head", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Head is a free data retrieval call binding the contract method 0xbda4fec5. +// +// Solidity: function head(address ) view returns(uint256) +func (_TicketAllocator *TicketAllocatorSession) Head(arg0 common.Address) (*big.Int, error) { + return _TicketAllocator.Contract.Head(&_TicketAllocator.CallOpts, arg0) +} + +// Head is a free data retrieval call binding the contract method 0xbda4fec5. +// +// Solidity: function head(address ) view returns(uint256) +func (_TicketAllocator *TicketAllocatorCallerSession) Head(arg0 common.Address) (*big.Int, error) { + return _TicketAllocator.Contract.Head(&_TicketAllocator.CallOpts, arg0) +} + +// PendingBids is a free data retrieval call binding the contract method 0xcf8589b9. +// +// Solidity: function pendingBids(uint256 ) view returns(address sender, address requestor, uint16 amount, uint256 bidPerTicket) +func (_TicketAllocator *TicketAllocatorCaller) PendingBids(opts *bind.CallOpts, arg0 *big.Int) (struct { + Sender common.Address + Requestor common.Address + Amount uint16 + BidPerTicket *big.Int +}, error) { + var out []interface{} + err := _TicketAllocator.contract.Call(opts, &out, "pendingBids", arg0) + + outstruct := new(struct { + Sender common.Address + Requestor common.Address + Amount uint16 + BidPerTicket *big.Int + }) + if err != nil { + return *outstruct, err + } + + outstruct.Sender = *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + outstruct.Requestor = *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + outstruct.Amount = *abi.ConvertType(out[2], new(uint16)).(*uint16) + outstruct.BidPerTicket = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +// PendingBids is a free data retrieval call binding the contract method 0xcf8589b9. +// +// Solidity: function pendingBids(uint256 ) view returns(address sender, address requestor, uint16 amount, uint256 bidPerTicket) +func (_TicketAllocator *TicketAllocatorSession) PendingBids(arg0 *big.Int) (struct { + Sender common.Address + Requestor common.Address + Amount uint16 + BidPerTicket *big.Int +}, error) { + return _TicketAllocator.Contract.PendingBids(&_TicketAllocator.CallOpts, arg0) +} + +// PendingBids is a free data retrieval call binding the contract method 0xcf8589b9. +// +// Solidity: function pendingBids(uint256 ) view returns(address sender, address requestor, uint16 amount, uint256 bidPerTicket) +func (_TicketAllocator *TicketAllocatorCallerSession) PendingBids(arg0 *big.Int) (struct { + Sender common.Address + Requestor common.Address + Amount uint16 + BidPerTicket *big.Int +}, error) { + return _TicketAllocator.Contract.PendingBids(&_TicketAllocator.CallOpts, arg0) +} + +// PendingBlock is a free data retrieval call binding the contract method 0xd33d9969. +// +// Solidity: function pendingBlock() view returns(uint256) +func (_TicketAllocator *TicketAllocatorCaller) PendingBlock(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _TicketAllocator.contract.Call(opts, &out, "pendingBlock") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// PendingBlock is a free data retrieval call binding the contract method 0xd33d9969. +// +// Solidity: function pendingBlock() view returns(uint256) +func (_TicketAllocator *TicketAllocatorSession) PendingBlock() (*big.Int, error) { + return _TicketAllocator.Contract.PendingBlock(&_TicketAllocator.CallOpts) +} + +// PendingBlock is a free data retrieval call binding the contract method 0xd33d9969. +// +// Solidity: function pendingBlock() view returns(uint256) +func (_TicketAllocator *TicketAllocatorCallerSession) PendingBlock() (*big.Int, error) { + return _TicketAllocator.Contract.PendingBlock(&_TicketAllocator.CallOpts) +} + +// Queue is a free data retrieval call binding the contract method 0x44287113. +// +// Solidity: function queue(address , uint256 ) view returns(uint16 amount, uint256 blockNumber, uint256 bidPerTicket, address requestor) +func (_TicketAllocator *TicketAllocatorCaller) Queue(opts *bind.CallOpts, arg0 common.Address, arg1 *big.Int) (struct { + Amount uint16 + BlockNumber *big.Int + BidPerTicket *big.Int + Requestor common.Address +}, error) { + var out []interface{} + err := _TicketAllocator.contract.Call(opts, &out, "queue", arg0, arg1) + + outstruct := new(struct { + Amount uint16 + BlockNumber *big.Int + BidPerTicket *big.Int + Requestor common.Address + }) + if err != nil { + return *outstruct, err + } + + outstruct.Amount = *abi.ConvertType(out[0], new(uint16)).(*uint16) + outstruct.BlockNumber = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + outstruct.BidPerTicket = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + outstruct.Requestor = *abi.ConvertType(out[3], new(common.Address)).(*common.Address) + + return *outstruct, err + +} + +// Queue is a free data retrieval call binding the contract method 0x44287113. +// +// Solidity: function queue(address , uint256 ) view returns(uint16 amount, uint256 blockNumber, uint256 bidPerTicket, address requestor) +func (_TicketAllocator *TicketAllocatorSession) Queue(arg0 common.Address, arg1 *big.Int) (struct { + Amount uint16 + BlockNumber *big.Int + BidPerTicket *big.Int + Requestor common.Address +}, error) { + return _TicketAllocator.Contract.Queue(&_TicketAllocator.CallOpts, arg0, arg1) +} + +// Queue is a free data retrieval call binding the contract method 0x44287113. +// +// Solidity: function queue(address , uint256 ) view returns(uint16 amount, uint256 blockNumber, uint256 bidPerTicket, address requestor) +func (_TicketAllocator *TicketAllocatorCallerSession) Queue(arg0 common.Address, arg1 *big.Int) (struct { + Amount uint16 + BlockNumber *big.Int + BidPerTicket *big.Int + Requestor common.Address +}, error) { + return _TicketAllocator.Contract.Queue(&_TicketAllocator.CallOpts, arg0, arg1) +} + +// Senders is a free data retrieval call binding the contract method 0x9977c78a. +// +// Solidity: function senders(uint256 ) view returns(address) +func (_TicketAllocator *TicketAllocatorCaller) Senders(opts *bind.CallOpts, arg0 *big.Int) (common.Address, error) { + var out []interface{} + err := _TicketAllocator.contract.Call(opts, &out, "senders", arg0) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Senders is a free data retrieval call binding the contract method 0x9977c78a. +// +// Solidity: function senders(uint256 ) view returns(address) +func (_TicketAllocator *TicketAllocatorSession) Senders(arg0 *big.Int) (common.Address, error) { + return _TicketAllocator.Contract.Senders(&_TicketAllocator.CallOpts, arg0) +} + +// Senders is a free data retrieval call binding the contract method 0x9977c78a. +// +// Solidity: function senders(uint256 ) view returns(address) +func (_TicketAllocator *TicketAllocatorCallerSession) Senders(arg0 *big.Int) (common.Address, error) { + return _TicketAllocator.Contract.Senders(&_TicketAllocator.CallOpts, arg0) +} + +// Withdrawable is a free data retrieval call binding the contract method 0xce513b6f. +// +// Solidity: function withdrawable(address ) view returns(uint256) +func (_TicketAllocator *TicketAllocatorCaller) Withdrawable(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _TicketAllocator.contract.Call(opts, &out, "withdrawable", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Withdrawable is a free data retrieval call binding the contract method 0xce513b6f. +// +// Solidity: function withdrawable(address ) view returns(uint256) +func (_TicketAllocator *TicketAllocatorSession) Withdrawable(arg0 common.Address) (*big.Int, error) { + return _TicketAllocator.Contract.Withdrawable(&_TicketAllocator.CallOpts, arg0) +} + +// Withdrawable is a free data retrieval call binding the contract method 0xce513b6f. +// +// Solidity: function withdrawable(address ) view returns(uint256) +func (_TicketAllocator *TicketAllocatorCallerSession) Withdrawable(arg0 common.Address) (*big.Int, error) { + return _TicketAllocator.Contract.Withdrawable(&_TicketAllocator.CallOpts, arg0) +} + +// RequestTickets is a paid mutator transaction binding the contract method 0x91ef4c9d. +// +// Solidity: function RequestTickets(address sender, uint16 numTickets, uint256 bidPerTicket) payable returns() +func (_TicketAllocator *TicketAllocatorTransactor) RequestTickets(opts *bind.TransactOpts, sender common.Address, numTickets uint16, bidPerTicket *big.Int) (*types.Transaction, error) { + return _TicketAllocator.contract.Transact(opts, "RequestTickets", sender, numTickets, bidPerTicket) +} + +// RequestTickets is a paid mutator transaction binding the contract method 0x91ef4c9d. +// +// Solidity: function RequestTickets(address sender, uint16 numTickets, uint256 bidPerTicket) payable returns() +func (_TicketAllocator *TicketAllocatorSession) RequestTickets(sender common.Address, numTickets uint16, bidPerTicket *big.Int) (*types.Transaction, error) { + return _TicketAllocator.Contract.RequestTickets(&_TicketAllocator.TransactOpts, sender, numTickets, bidPerTicket) +} + +// RequestTickets is a paid mutator transaction binding the contract method 0x91ef4c9d. +// +// Solidity: function RequestTickets(address sender, uint16 numTickets, uint256 bidPerTicket) payable returns() +func (_TicketAllocator *TicketAllocatorTransactorSession) RequestTickets(sender common.Address, numTickets uint16, bidPerTicket *big.Int) (*types.Transaction, error) { + return _TicketAllocator.Contract.RequestTickets(&_TicketAllocator.TransactOpts, sender, numTickets, bidPerTicket) +} + +// Withdraw is a paid mutator transaction binding the contract method 0x3ccfd60b. +// +// Solidity: function withdraw() returns() +func (_TicketAllocator *TicketAllocatorTransactor) Withdraw(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TicketAllocator.contract.Transact(opts, "withdraw") +} + +// Withdraw is a paid mutator transaction binding the contract method 0x3ccfd60b. +// +// Solidity: function withdraw() returns() +func (_TicketAllocator *TicketAllocatorSession) Withdraw() (*types.Transaction, error) { + return _TicketAllocator.Contract.Withdraw(&_TicketAllocator.TransactOpts) +} + +// Withdraw is a paid mutator transaction binding the contract method 0x3ccfd60b. +// +// Solidity: function withdraw() returns() +func (_TicketAllocator *TicketAllocatorTransactorSession) Withdraw() (*types.Transaction, error) { + return _TicketAllocator.Contract.Withdraw(&_TicketAllocator.TransactOpts) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() returns() +func (_TicketAllocator *TicketAllocatorTransactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) { + return _TicketAllocator.contract.RawTransact(opts, calldata) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() returns() +func (_TicketAllocator *TicketAllocatorSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _TicketAllocator.Contract.Fallback(&_TicketAllocator.TransactOpts, calldata) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() returns() +func (_TicketAllocator *TicketAllocatorTransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _TicketAllocator.Contract.Fallback(&_TicketAllocator.TransactOpts, calldata) +} diff --git a/core/TicketAllocator.sol b/core/TicketAllocator.sol index da202ae891..499a6af9b2 100644 --- a/core/TicketAllocator.sol +++ b/core/TicketAllocator.sol @@ -6,67 +6,168 @@ contract TicketAllocator { error NotEnoughTickets(); error NotSystemContract(); + error InvalidPayment(); struct Ticket { uint16 amount; uint256 blockNumber; + uint256 bidPerTicket; + address requestor; // address that requested the tickets + } + + struct PendingBid { + address sender; + address requestor; // address that called RequestTickets + uint16 amount; + uint256 bidPerTicket; } mapping(address => Ticket[]) public queue; mapping(address => uint256) public head; mapping(address => uint16) public balance; + mapping(address => uint256) public withdrawable; // amount available for withdrawal address[] public senders; // list of senders with >0 balance uint16 public constant TOTAL_TICKETS = 21; uint16 public constant TIMEOUT = 2; - - uint256 private leftTickets; - uint256 private lastRequestBlock; - function requestTickets(address sender, uint16 numTickets) external { - uint256 currentBlock = block.number; + PendingBid[] public pendingBids; + uint256 public pendingBlock; - if (currentBlock != lastRequestBlock) { - leftTickets = TOTAL_TICKETS; - lastRequestBlock = currentBlock; + function GetBalance() public view returns (address[] memory, uint16[] memory){ + uint16[] memory res; + res = new uint16[](senders.length); + for (uint256 i = 0; i < senders.length; i++) { + res[i] = balance[senders[i]]; + } + return (senders, res); + } + + function _addModified( + address sender, + address[] memory modified, + uint256 modifiedCount + ) private pure returns (uint256) { + for (uint256 i = 0; i < modifiedCount; i++) { + if (modified[i] == sender) { + return modifiedCount; + } + } + modified[modifiedCount] = sender; + return modifiedCount + 1; + } + + function RequestTickets(address sender, uint16 numTickets, uint256 bidPerTicket) external payable { + // Check payment matches the declared bid + if (msg.value != uint256(numTickets) * bidPerTicket) { + revert InvalidPayment(); } - if (numTickets > leftTickets) { - revert NotEnoughTickets(); + // Reset pending bids if we moved to a new block + if (block.number != pendingBlock) { + delete pendingBids; + pendingBlock = block.number; } - if (balance[sender] == 0) { - senders.push(sender); - } - - queue[sender].push(Ticket({ + pendingBids.push(PendingBid({ + sender: sender, + requestor: msg.sender, amount: numTickets, - blockNumber: currentBlock + bidPerTicket: bidPerTicket })); - - balance[sender] += numTickets; - leftTickets -= numTickets; } - function checkBalance(address sender) public view returns(uint) { - return balance[sender]; + function withdraw() external { + uint256 amount = withdrawable[msg.sender]; + if (amount == 0) { + return; + } + withdrawable[msg.sender] = 0; + (bool success, ) = payable(msg.sender).call{value: amount}(""); + require(success, "withdrawal failed"); } - + // System call: - // 1. remove expired and used tickets - // - for used tickets, the oldest ticket is removed first - // 3. return the overall sender balances + // 1. allocate tickets for this block based on bids + // 2. process used and expired tickets, refunding bids for used tickets + // 3. return modified addresses and their balances fallback(bytes calldata input) external returns (bytes memory) { if (msg.sender != SYSTEM_ADDRESS) revert NotSystemContract(); address[] memory usedSenders; uint16[] memory usedAmounts; - + if (input.length > 0) { (usedSenders, usedAmounts) = abi.decode(input, (address[], uint16[])); } + // Track modified addresses for this system call + address[] memory modified = new address[](senders.length + pendingBids.length); + uint256 modifiedCount = 0; + + // Step 1: allocate tickets for this block based on bids + if (pendingBlock == block.number && pendingBids.length > 0) { + // Sort pending bids by bidPerTicket in descending order (bubble sort on storage) + uint256 n = pendingBids.length; + for (uint256 i = 0; i < n; i++) { + for (uint256 j = i + 1; j < n; j++) { + if (pendingBids[j].bidPerTicket > pendingBids[i].bidPerTicket) { + PendingBid memory tmp = pendingBids[i]; + pendingBids[i] = pendingBids[j]; + pendingBids[j] = tmp; + } + } + } + + uint16 remaining = TOTAL_TICKETS; + + for (uint256 i = 0; i < n; i++) { + PendingBid storage bid = pendingBids[i]; + if (bid.amount == 0) { + continue; + } + + uint16 alloc = bid.amount; + if (alloc > remaining) { + alloc = remaining; + } + + if (alloc > 0) { + // Allocate tickets and keep the payment locked until used or expired + if (balance[bid.sender] == 0) { + senders.push(bid.sender); + } + queue[bid.sender].push(Ticket({ + amount: alloc, + blockNumber: block.number, + bidPerTicket: bid.bidPerTicket, + requestor: bid.requestor + })); + balance[bid.sender] += alloc; + remaining -= alloc; + + // mark modified + modifiedCount = _addModified(bid.sender, modified, modifiedCount); + + // Add unallocated portion to withdrawal, if any + if (bid.amount > alloc) { + uint16 unfilled = bid.amount - alloc; + uint256 refund = uint256(unfilled) * bid.bidPerTicket; + withdrawable[bid.requestor] += refund; + } + } else { + // No tickets left, full refund to withdrawal + uint256 refundAll = uint256(bid.amount) * bid.bidPerTicket; + withdrawable[bid.requestor] += refundAll; + } + } + + delete pendingBids; + pendingBlock = 0; + } + + // Step 2: process used and expired tickets, refunding bids for used tickets for (uint256 s = 0; s < senders.length; ) { address sender = senders[s]; Ticket[] storage senderQueue = queue[sender]; @@ -89,23 +190,29 @@ contract TicketAllocator { continue; } - + // Expired tickets: burn the funds paid for these tickets if (ticket.blockNumber + TIMEOUT < block.number) { - // remove expired tickets (comsume used tickets if possible) balance[sender] -= ticket.amount; - if (used > 0) { - // used = max(0, used - t.amount) - uint16 deduct = used > ticket.amount ? ticket.amount : used; - used -= deduct; - } ticket.amount = 0; senderHead++; + + // Burn by sending to address(0) + (bool success, ) = payable(address(0)).call{value: ticket.amount * ticket.bidPerTicket}(""); + require(success, "burn failed"); + + // mark modified + modifiedCount = _addModified(sender, modified, modifiedCount); } else if (used > 0) { - // remove used ticekts (for now we don't revert when the ticket amount is less than used tickets) + // Used tickets: add refund to withdrawal for the requestor uint16 deduct = used > ticket.amount ? ticket.amount : used; - ticket.amount -= uint16(deduct); + ticket.amount -= deduct; balance[sender] -= deduct; used -= deduct; + uint256 refund = uint256(deduct) * ticket.bidPerTicket; + withdrawable[ticket.requestor] += refund; + + // mark modified + modifiedCount = _addModified(sender, modified, modifiedCount); if (ticket.amount == 0) senderHead++; } else { @@ -124,15 +231,13 @@ contract TicketAllocator { s++; } - // return the active senders and their balances - address[] memory activeSenders = new address[](senders.length); - uint16[] memory activeBalances = new uint16[](senders.length); - - for (uint256 i = 0; i < senders.length; i++) { - activeSenders[i] = senders[i]; - activeBalances[i] = balance[senders[i]]; + // Step 3: return modified addresses and their balances + address[] memory modifiedAddress = new address[](modifiedCount); + uint16[] memory modifiedTickets = new uint16[](modifiedCount); + for (uint256 i = 0; i < modifiedCount; i++) { + modifiedAddress[i] = modified[i]; + modifiedTickets[i] = balance[modified[i]]; } - - return abi.encode(activeSenders, activeBalances); + return abi.encode(modifiedAddress, modifiedTickets); } } diff --git a/core/blockchain.go b/core/blockchain.go index a86e987af9..85d488ce85 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2280,22 +2280,18 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s } // Add ticket balances of this block and remove the parent block data - bc.tickets[block.Hash()] = res.Tickets - if _, ok := bc.ticketBlocks[block.Number()]; !ok { - bc.ticketBlocks[block.Number()] = make([]common.Hash, 0) - } - bc.ticketBlocks[block.Number()] = append(bc.ticketBlocks[block.Number()], block.Hash()) - - if _, ok := bc.tickets[block.ParentHash()]; ok { + if parent, ok := bc.tickets[block.ParentHash()]; ok { delete(bc.tickets, block.ParentHash()) - if hashes := bc.ticketBlocks[block.Number().Sub(block.Number(), big.NewInt(1))]; len(hashes) != 0 { - for i, hash := range hashes { - if hash.Cmp(block.ParentHash()) == 0 { - hashes[i] = hashes[len(hashes)-1] - hashes = hashes[:len(hashes)-1] - } + bc.tickets[block.Hash()] = parent + for addr, value := range res.Tickets { + if value == 0 { + delete(bc.tickets[block.Hash()], addr) } + bc.tickets[block.Hash()][addr] = value } + } else { + res, _ := bc.GetTicketBalance(block.Header()) + bc.tickets[block.Hash()] = res } // Report the collected witness statistics diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 9534c10533..357d066761 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -21,6 +21,7 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" @@ -29,7 +30,6 @@ import ( "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -354,33 +354,43 @@ func (bc *BlockChain) GetCanonicalTransaction(hash common.Hash) (*rawdb.LegacyTx return lookup, tx } -func (bc *BlockChain) GetTicketBalance(hash common.Hash, statedb *state.StateDB) map[common.Address]uint16 { - if bc.tickets[hash] != nil { - return bc.tickets[hash] +func (bc *BlockChain) GetTicketBalance(header *types.Header) (map[common.Address]uint16, error) { + if bc.tickets[header.Hash()] != nil { + return bc.tickets[header.Hash()], nil + } + + ctx := NewEVMBlockContext(header, bc.HeaderChain(), nil) + statedb, err := bc.StateAt(header.Root) + if err != nil { + return nil, err + } + + evm := vm.NewEVM(ctx, statedb, bc.chainConfig, bc.cfg.VmConfig) + cc := contractCaller{addr: params.BlobTicketAllocationAddress, evm: evm} + caller, err := NewTicketAllocatorCaller(params.BlobTicketAllocationAddress, cc) + if err != nil { + return nil, err + } + + senders, balances, err := caller.GetBalance(&bind.CallOpts{BlockNumber: header.Number}) + if err != nil { + // when the contract is not deployed, return empty map + if errors.Is(err, bind.ErrNoCode) { + result := make(map[common.Address]uint16) + bc.tickets[header.Hash()] = result + return result, nil + } + return nil, err } result := make(map[common.Address]uint16) - senders := statedb.GetState(params.BlobTicketAllocationAddress, common.BigToHash(params.BlobTicketSenderSlot)).Big().Int64() - base := crypto.Keccak256Hash(common.LeftPadBytes(params.BlobTicketSenderSlot.Bytes(), 32)).Big() - - for i := range senders { - key := common.BigToHash(base.Add(base, big.NewInt(i))) - sender := common.BytesToAddress(statedb.GetState(params.BlobTicketAllocationAddress, key).Bytes()) - - key = crypto.Keccak256Hash( - append( - common.LeftPadBytes(sender.Bytes(), 32), - common.LeftPadBytes(params.BlobTicketBalanceSlot.Bytes(), 32)..., - ), - ) - - balance := statedb.GetState(params.BlobTicketAllocationAddress, key).Big().Uint64() - - result[sender] = uint16(balance) + for i, sender := range senders { + result[sender] = uint16(balances[i]) } + bc.tickets[header.Hash()] = result - return result + return result, nil } // TxIndexDone returns true if the transaction indexer has finished indexing. diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 51ecad3382..215e6ec1af 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -4211,11 +4211,12 @@ func TestBlobTickets(t *testing.T) { _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 4, func(i int, bg *BlockGen) { switch i { case 0: // create ticket request - selector := crypto.Keccak256([]byte("requestTickets(address,uint16)"))[:4] + selector := crypto.Keccak256([]byte("RequestTickets(address,uint16,uint256)"))[:4] uint16Type, _ := abi.NewType("uint16", "", nil) addressType, _ := abi.NewType("address", "", nil) - args := abi.Arguments{{Type: addressType}, {Type: uint16Type}} - data, _ := args.Pack(addr1, ticketAmount) + uint256Type, _ := abi.NewType("uint256", "", nil) + args := abi.Arguments{{Type: addressType}, {Type: uint16Type}, {Type: uint256Type}} + data, _ := args.Pack(addr1, ticketAmount, big.NewInt(1)) data = append(selector, data...) @@ -4227,6 +4228,7 @@ func TestBlobTickets(t *testing.T) { GasFeeCap: big.NewInt(875000000), GasTipCap: big.NewInt(1), Data: data, + Value: big.NewInt(int64(ticketAmount)), } tx := types.MustSignNewTx(key1, signer, txdata) @@ -4297,15 +4299,13 @@ func TestBlobTickets(t *testing.T) { } // previous block ticket balance - state, _ := chain.StateAt(blocks[0].Root()) - balance = chain.GetTicketBalance(blocks[0].Hash(), state) + balance, _ = chain.GetTicketBalance(blocks[0].Header()) amount = balance[addr1] if amount != ticketAmount { t.Fatalf("wrong ticket amount: expected %d, got %d", ticketAmount, amount) } - state, _ = chain.StateAt(blocks[1].Root()) - balance = chain.GetTicketBalance(blocks[1].Hash(), state) + balance, _ = chain.GetTicketBalance(blocks[1].Header()) amount = balance[addr1] if amount != ticketAmount-1 { t.Fatalf("wrong ticket amount: expected %d, got %d", ticketAmount-1, amount) diff --git a/core/contract_caller.go b/core/contract_caller.go new file mode 100644 index 0000000000..2bca6cec02 --- /dev/null +++ b/core/contract_caller.go @@ -0,0 +1,45 @@ +package core + +import ( + "context" + "math" + "math/big" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" +) + +type contractCaller struct { + evm *vm.EVM + addr common.Address +} + +func (c contractCaller) CodeAt(ctx context.Context, addr common.Address, blockNumber *big.Int) ([]byte, error) { + return c.evm.StateDB.GetCode(addr), nil +} + +func (c contractCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + value := new(uint256.Int) + if call.Value != nil { + value.SetFromBig(call.Value) + } + + from := call.From + if from == (common.Address{}) { + from = params.SystemAddress + } + + // when gas is set to zero, it should use nearly infinite gas, + // according to the CallMsg definition + gas := call.Gas + if gas == 0 { + gas = uint64(math.MaxUint64) + } + + res, _, err := c.evm.Call(from, c.addr, call.Data, gas, value) + + return res, err +} diff --git a/core/state_processor.go b/core/state_processor.go index 0ca6d14b39..6e571b83a8 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" ) @@ -384,9 +385,17 @@ func ProcessTickets(usedAddress []common.Address, usedAmount []uint16, evm *vm.E } evm.SetTxContext(NewEVMTxContext(msg)) - res, _, _ := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560) + res, _, err := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560) evm.StateDB.Finalise(true) + if err != nil { + log.Error("system call failed to execute", "error", err) + return nil + } + + if len(res) == 0 { + return make(map[common.Address]uint16) + } arguments := abi.Arguments{ {Type: addressType}, diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 7eb0a141b2..3b82fe4fd9 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -419,7 +419,10 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser p.head.Store(head) p.state = state - p.tickets = p.chain.GetTicketBalance(head.Hash(), state) + p.tickets, err = p.chain.GetTicketBalance(head) + if err != nil { + return err + } // Create new slotter for pre-Osaka blob configuration. slotter := newSlotter(params.BlobTxMaxBlobs) @@ -862,8 +865,11 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) { p.head.Store(newHead) p.state = statedb - p.tickets = p.chain.GetTicketBalance(newHead.Hash(), p.state) - + p.tickets, err = p.chain.GetTicketBalance(newHead) + if err != nil { + log.Error("Failed to get ticket balances", "err", err) + return + } // Run the reorg between the old and new head and figure out which accounts // need to be rechecked and which transactions need to be readded if reinject, inclusions := p.reorg(oldHead, newHead); reinject != nil { diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index e1dd5dc14f..ef38179140 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -186,8 +186,8 @@ func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { return bc.statedb, nil } -func (bc *testBlockChain) GetTicketBalance(common.Hash, *state.StateDB) map[common.Address]uint16 { - return bc.tickets +func (bc *testBlockChain) GetTicketBalance(*types.Header) (map[common.Address]uint16, error) { + return bc.tickets, nil } // reserver is a utility struct to sanity check that accounts are diff --git a/core/txpool/blobpool/interface.go b/core/txpool/blobpool/interface.go index b79f0951f3..331048ed32 100644 --- a/core/txpool/blobpool/interface.go +++ b/core/txpool/blobpool/interface.go @@ -42,5 +42,5 @@ type BlockChain interface { // StateAt returns a state database for a given root hash (generally the head). StateAt(root common.Hash) (*state.StateDB, error) - GetTicketBalance(hash common.Hash, statedb *state.StateDB) map[common.Address]uint16 + GetTicketBalance(header *types.Header) (map[common.Address]uint16, error) } diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 5a87ec1f25..8b72d7acd5 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -1916,11 +1916,12 @@ func newGetBlobEnv(t testing.TB, version byte) (*node.Node, *ConsensusAPI) { {addr3, key3}, } for _, signer := range signers { - selector := crypto.Keccak256([]byte("requestTickets(address,uint16)"))[:4] + selector := crypto.Keccak256([]byte("RequestTickets(address,uint16,uint256)"))[:4] uint16Type, _ := abi.NewType("uint16", "", nil) addressType, _ := abi.NewType("address", "", nil) - args := abi.Arguments{{Type: addressType}, {Type: uint16Type}} - data, _ := args.Pack(signer.addr, uint16(2)) + uint256Type, _ := abi.NewType("uint256", "", nil) + args := abi.Arguments{{Type: addressType}, {Type: uint16Type}, {Type: uint256Type}} + data, _ := args.Pack(signer.addr, uint16(2), big.NewInt(1)) data = append(selector, data...) txdata := &types.DynamicFeeTx{ @@ -1931,12 +1932,12 @@ func newGetBlobEnv(t testing.TB, version byte) (*node.Node, *ConsensusAPI) { GasFeeCap: big.NewInt(params.InitialBaseFee * 2), GasTipCap: big.NewInt(params.GWei), Data: data, + Value: big.NewInt(2), } tx := types.MustSignNewTx(signer.key, types.LatestSigner(&config), txdata) b.AddTx(tx) } }) - n, ethServ := startEthService(t, gspec, ticketBlocks) // fill blob txs into the pool diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index 6dc96ad71e..d1ba710c4d 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -31,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" @@ -74,8 +73,8 @@ type testChain struct { *core.BlockChain } -func (c *testChain) GetTicketBalance(hash common.Hash, statedb *state.StateDB) map[common.Address]uint16 { - return map[common.Address]uint16{testAddr: 1000} +func (c *testChain) GetTicketBalance(header *types.Header) (map[common.Address]uint16, error) { + return map[common.Address]uint16{testAddr: 1000}, nil } // newTestBackendWithGenerator creates a chain with a number of explicitly defined blocks and diff --git a/ethclient/simulated/rollback_test.go b/ethclient/simulated/rollback_test.go index fba878e282..121a99eee9 100644 --- a/ethclient/simulated/rollback_test.go +++ b/ethclient/simulated/rollback_test.go @@ -100,11 +100,12 @@ func allocateTickets(t *testing.T, key *ecdsa.PrivateKey, sim *Backend, nonce ui chainid, _ := client.ChainID(ctx) signer := types.LatestSignerForChainID(chainid) - selector := crypto.Keccak256([]byte("requestTickets(address,uint16)"))[:4] + selector := crypto.Keccak256([]byte("RequestTickets(address,uint16,uint256)"))[:4] uint16Type, _ := abi.NewType("uint16", "", nil) addressType, _ := abi.NewType("address", "", nil) - args := abi.Arguments{{Type: addressType}, {Type: uint16Type}} - data, _ := args.Pack(addr, amount) + uint256Type, _ := abi.NewType("uint256", "", nil) + args := abi.Arguments{{Type: addressType}, {Type: uint16Type}, {Type: uint256Type}} + data, _ := args.Pack(addr, amount, big.NewInt(1)) data = append(selector, data...) // Get current head @@ -118,6 +119,7 @@ func allocateTickets(t *testing.T, key *ecdsa.PrivateKey, sim *Backend, nonce ui GasFeeCap: new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei)), GasTipCap: big.NewInt(params.GWei), Data: data, + Value: big.NewInt(int64(amount)), } tx := types.MustSignNewTx(key, signer, txdata) diff --git a/miner/worker.go b/miner/worker.go index e6c5e9efea..4ef2af5c35 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -169,8 +169,8 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay if err := core.ProcessConsolidationQueue(&requests, work.evm); err != nil { return &newPayloadResult{err: err} } + core.ProcessTickets(work.usedAddress, work.usedAmount, work.evm) } - core.ProcessTickets(work.usedAddress, work.usedAmount, work.evm) if requests != nil { reqHash := types.CalcRequestsHash(requests) diff --git a/params/protocol_params.go b/params/protocol_params.go index 6654e88caa..a050fbf88f 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -222,8 +222,5 @@ var ( // Blob tickets (random address for test) BlobTicketAllocationAddress = common.HexToAddress("0x8fd501b55bf41f51460815f0a1f00b541fc161") - BlobTicketAllocationCode = common.FromHex("608060405234801561000f575f5ffd5b506004361061008a575f3560e01c8063afbd178211610059578063afbd1782146109dd578063bda4fec5146109fb578063e3d670d714610a2b578063f56f48f214610a5b5761008b565b80630309cc7b14610930578063442871131461094c5780635f5152261461097d5780639977c78a146109ad5761008b565b5b5f36606073fffffffffffffffffffffffffffffffffffffffe73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610108576040517fdd169cfb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060805f85859050111561012e5784848101906101259190611092565b80925081935050505b5f5f90505b600380549050811015610702575f6003828154811061015557610154611108565b5b905f5260205f20015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505f5f5f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2090505f60015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205490505f5f90505f5f90505b87518161ffff16101561029f578473ffffffffffffffffffffffffffffffffffffffff16888261ffff168151811061024357610242611108565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff160361028c57868161ffff168151811061027d5761027c611108565b5b6020026020010151915061029f565b808061029790611162565b915050610208565b505b8280549050821015610560575f8383815481106102c1576102c0611108565b5b905f5260205f20906002020190505f815f015f9054906101000a900461ffff1661ffff16036102fe5782806102f590611194565b935050506102a1565b43600261ffff16826001015461031491906111db565b101561042357805f015f9054906101000a900461ffff1660025f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282829054906101000a900461ffff16610383919061120e565b92506101000a81548161ffff021916908361ffff1602179055505f8261ffff1611156103f3575f815f015f9054906101000a900461ffff1661ffff168361ffff16116103cf57826103e1565b815f015f9054906101000a900461ffff165b905080836103ef919061120e565b9250505b5f815f015f6101000a81548161ffff021916908361ffff160217905550828061041b90611194565b93505061055a565b5f8261ffff161115610553575f815f015f9054906101000a900461ffff1661ffff168361ffff16116104555782610467565b815f015f9054906101000a900461ffff165b905080825f015f8282829054906101000a900461ffff16610488919061120e565b92506101000a81548161ffff021916908361ffff1602179055508060025f8873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282829054906101000a900461ffff166104fb919061120e565b92506101000a81548161ffff021916908361ffff1602179055508083610521919061120e565b92505f825f015f9054906101000a900461ffff1661ffff160361054d57838061054990611194565b9450505b50610559565b50610560565b5b506102a1565b8160015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20819055505f60025f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900461ffff1661ffff16036106ea576003600160038054905061060b9190611243565b8154811061061c5761061b611108565b5b905f5260205f20015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166003868154811061065857610657611108565b5b905f5260205f20015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060038054806106af576106ae611276565b5b600190038181905f5260205f20015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690559055505050506106fd565b84806106f590611194565b955050505050505b610133565b505f60038054905067ffffffffffffffff81111561072357610722610e05565b5b6040519080825280602002602001820160405280156107515781602001602082028036833780820191505090505b5090505f60038054905067ffffffffffffffff81111561077457610773610e05565b5b6040519080825280602002602001820160405280156107a25781602001602082028036833780820191505090505b5090505f5f90505b6003805490508110156108fc57600381815481106107cb576107ca611108565b5b905f5260205f20015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683828151811061080657610805611108565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060025f6003838154811061085757610856611108565b5b905f5260205f20015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900461ffff168282815181106108d9576108d8611108565b5b602002602001019061ffff16908161ffff168152505080806001019150506107aa565b508181604051602001610910929190611411565b604051602081830303815290604052945050505050915050805190602001f35b61094a60048036038101906109459190611446565b610a79565b005b610966600480360381019061096191906114ae565b610ccb565b60405161097492919061150a565b60405180910390f35b61099760048036038101906109929190611531565b610d11565b6040516109a4919061155c565b60405180910390f35b6109c760048036038101906109c29190611575565b610d68565b6040516109d491906115af565b60405180910390f35b6109e5610da3565b6040516109f291906115c8565b60405180910390f35b610a156004803603810190610a109190611531565b610da8565b604051610a22919061155c565b60405180910390f35b610a456004803603810190610a409190611531565b610dbd565b604051610a5291906115c8565b60405180910390f35b610a63610ddb565b604051610a7091906115c8565b60405180910390f35b5f4390506005548114610a9a57601561ffff16600481905550806005819055505b6004548261ffff161115610ada576040517fa8d8f34900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f60025f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900461ffff1661ffff1603610b9057600383908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5f5f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2060405180604001604052808461ffff16815260200183815250908060018154018082558091505060019003905f5260205f2090600202015f909190919091505f820151815f015f6101000a81548161ffff021916908361ffff1602179055506020820151816001015550508160025f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282829054906101000a900461ffff16610c9091906115e1565b92506101000a81548161ffff021916908361ffff1602179055508161ffff1660045f828254610cbf9190611243565b92505081905550505050565b5f602052815f5260405f208181548110610ce3575f80fd5b905f5260205f2090600202015f9150915050805f015f9054906101000a900461ffff16908060010154905082565b5f60025f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900461ffff1661ffff169050919050565b60038181548110610d77575f80fd5b905f5260205f20015f915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b601581565b6001602052805f5260405f205f915090505481565b6002602052805f5260405f205f915054906101000a900461ffff1681565b600281565b5f604051905090565b5f5ffd5b5f5ffd5b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610e3b82610df5565b810181811067ffffffffffffffff82111715610e5a57610e59610e05565b5b80604052505050565b5f610e6c610de0565b9050610e788282610e32565b919050565b5f67ffffffffffffffff821115610e9757610e96610e05565b5b602082029050602081019050919050565b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610ed582610eac565b9050919050565b610ee581610ecb565b8114610eef575f5ffd5b50565b5f81359050610f0081610edc565b92915050565b5f610f18610f1384610e7d565b610e63565b90508083825260208201905060208402830185811115610f3b57610f3a610ea8565b5b835b81811015610f645780610f508882610ef2565b845260208401935050602081019050610f3d565b5050509392505050565b5f82601f830112610f8257610f81610df1565b5b8135610f92848260208601610f06565b91505092915050565b5f67ffffffffffffffff821115610fb557610fb4610e05565b5b602082029050602081019050919050565b5f61ffff82169050919050565b610fdc81610fc6565b8114610fe6575f5ffd5b50565b5f81359050610ff781610fd3565b92915050565b5f61100f61100a84610f9b565b610e63565b9050808382526020820190506020840283018581111561103257611031610ea8565b5b835b8181101561105b57806110478882610fe9565b845260208401935050602081019050611034565b5050509392505050565b5f82601f83011261107957611078610df1565b5b8135611089848260208601610ffd565b91505092915050565b5f5f604083850312156110a8576110a7610de9565b5b5f83013567ffffffffffffffff8111156110c5576110c4610ded565b5b6110d185828601610f6e565b925050602083013567ffffffffffffffff8111156110f2576110f1610ded565b5b6110fe85828601611065565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61116c82610fc6565b915061ffff82036111805761117f611135565b5b600182019050919050565b5f819050919050565b5f61119e8261118b565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036111d0576111cf611135565b5b600182019050919050565b5f6111e58261118b565b91506111f08361118b565b925082820190508082111561120857611207611135565b5b92915050565b5f61121882610fc6565b915061122383610fc6565b9250828203905061ffff81111561123d5761123c611135565b5b92915050565b5f61124d8261118b565b91506112588361118b565b92508282039050818111156112705761126f611135565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b6112d581610ecb565b82525050565b5f6112e683836112cc565b60208301905092915050565b5f602082019050919050565b5f611308826112a3565b61131281856112ad565b935061131d836112bd565b805f5b8381101561134d57815161133488826112db565b975061133f836112f2565b925050600181019050611320565b5085935050505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b61138c81610fc6565b82525050565b5f61139d8383611383565b60208301905092915050565b5f602082019050919050565b5f6113bf8261135a565b6113c98185611364565b93506113d483611374565b805f5b838110156114045781516113eb8882611392565b97506113f6836113a9565b9250506001810190506113d7565b5085935050505092915050565b5f6040820190508181035f83015261142981856112fe565b9050818103602083015261143d81846113b5565b90509392505050565b5f5f6040838503121561145c5761145b610de9565b5b5f61146985828601610ef2565b925050602061147a85828601610fe9565b9150509250929050565b61148d8161118b565b8114611497575f5ffd5b50565b5f813590506114a881611484565b92915050565b5f5f604083850312156114c4576114c3610de9565b5b5f6114d185828601610ef2565b92505060206114e28582860161149a565b9150509250929050565b6114f581610fc6565b82525050565b6115048161118b565b82525050565b5f60408201905061151d5f8301856114ec565b61152a60208301846114fb565b9392505050565b5f6020828403121561154657611545610de9565b5b5f61155384828501610ef2565b91505092915050565b5f60208201905061156f5f8301846114fb565b92915050565b5f6020828403121561158a57611589610de9565b5b5f6115978482850161149a565b91505092915050565b6115a981610ecb565b82525050565b5f6020820190506115c25f8301846115a0565b92915050565b5f6020820190506115db5f8301846114ec565b92915050565b5f6115eb82610fc6565b91506115f683610fc6565b9250828201905061ffff8111156116105761160f611135565b5b9291505056fea2646970667358221220025e215254a4422cde6449804ceea203d39ea9932929ad229bc6b740ae44d67964736f6c63430008210033") - - BlobTicketSenderSlot = big.NewInt(3) - BlobTicketBalanceSlot = big.NewInt(2) + BlobTicketAllocationCode = common.FromHex("6080604052600436106100aa575f3560e01c8063ce513b6f11610063578063ce513b6f146114d7578063cf8589b914611513578063d33d996914611552578063e3d670d71461157c578063f56f48f2146115b8578063f8f8a912146115e2576100ab565b80633ccfd60b146113c457806344287113146113da57806391ef4c9d146114195780639977c78a14611435578063afbd178214611471578063bda4fec51461149b576100ab565b5b3480156100b6575f5ffd5b505f36606073fffffffffffffffffffffffffffffffffffffffe73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610134576040517fdd169cfb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060805f85859050111561015a5784848101906101519190612052565b80925081935050505b5f60058054905060048054905061017191906120fe565b67ffffffffffffffff81111561018a57610189611dc5565b5b6040519080825280602002602001820160405280156101b85781602001602082028036833780820191505090505b5090505f5f9050436006541480156101d457505f600580549050115b15610af3575f60058054905090505f5f90505b81811015610595575f6001826101fd91906120fe565b90505b82811015610587576005828154811061021c5761021b612131565b5b905f5260205f20906003020160020154600582815481106102405761023f612131565b5b905f5260205f20906003020160020154111561057a575f6005838154811061026b5761026a612131565b5b905f5260205f2090600302016040518060800160405290815f82015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600182015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160149054906101000a900461ffff1661ffff1661ffff16815260200160028201548152505090506005828154811061036b5761036a612131565b5b905f5260205f2090600302016005848154811061038b5761038a612131565b5b905f5260205f2090600302015f82015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600182015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001820160149054906101000a900461ffff168160010160146101000a81548161ffff021916908361ffff1602179055506002820154816002015590505080600583815481106104b2576104b1612131565b5b905f5260205f2090600302015f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160010160146101000a81548161ffff021916908361ffff16021790555060608201518160020155905050505b8080600101915050610200565b5080806001019150506101e7565b505f601590505f5f90505b82811015610adb575f600582815481106105bd576105bc612131565b5b905f5260205f20906003020190505f8160010160149054906101000a900461ffff1661ffff16036105ee5750610ace565b5f8160010160149054906101000a900461ffff1690508361ffff168161ffff161115610618578390505b5f8161ffff161115610a2a575f60025f845f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900461ffff1661ffff160361071e576004825f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5f5f835f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2060405180608001604052808361ffff16815260200143815260200184600201548152602001846001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815250908060018154018082558091505060019003905f5260205f2090600402015f909190919091505f820151815f015f6101000a81548161ffff021916908361ffff16021790555060208201518160010155604082015181600201556060820151816003015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050508060025f845f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282829054906101000a900461ffff166108fb919061215e565b92506101000a81548161ffff021916908361ffff16021790555080846109219190612193565b9350610950825f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16888861160d565b95508061ffff168260010160149054906101000a900461ffff1661ffff161115610a25575f818360010160149054906101000a900461ffff166109939190612193565b90505f83600201548261ffff166109aa91906121c8565b90508060035f866001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254610a1b91906120fe565b9250508190555050505b610acb565b5f82600201548360010160149054906101000a900461ffff1661ffff16610a5191906121c8565b90508060035f856001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254610ac291906120fe565b92505081905550505b50505b80806001019150506105a0565b5060055f610ae99190611cfe565b5f60068190555050505b5f5f90505b6004805490508110156111e5575f60048281548110610b1a57610b19612131565b5b905f5260205f20015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505f5f5f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2090505f60015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205490505f5f90505f5f90505b89518161ffff161015610c64578473ffffffffffffffffffffffffffffffffffffffff168a8261ffff1681518110610c0857610c07612131565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603610c5157888161ffff1681518110610c4257610c41612131565b5b60200260200101519150610c64565b8080610c5c90612209565b915050610bcd565b505b8280549050821015611043575f838381548110610c8657610c85612131565b5b905f5260205f20906004020190505f815f015f9054906101000a900461ffff1661ffff1603610cc3578280610cba90612232565b93505050610c66565b43600261ffff168260010154610cd991906120fe565b1015610e6b57805f015f9054906101000a900461ffff1660025f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282829054906101000a900461ffff16610d489190612193565b92506101000a81548161ffff021916908361ffff1602179055505f815f015f6101000a81548161ffff021916908361ffff1602179055508280610d8a90612232565b9350505f5f73ffffffffffffffffffffffffffffffffffffffff168260020154835f015f9054906101000a900461ffff1661ffff16610dc991906121c8565b604051610dd5906122a6565b5f6040518083038185875af1925050503d805f8114610e0f576040519150601f19603f3d011682016040523d82523d5f602084013e610e14565b606091505b5050905080610e58576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e4f90612314565b60405180910390fd5b610e63868a8a61160d565b97505061103d565b5f8261ffff161115611036575f815f015f9054906101000a900461ffff1661ffff168361ffff1611610e9d5782610eaf565b815f015f9054906101000a900461ffff165b905080825f015f8282829054906101000a900461ffff16610ed09190612193565b92506101000a81548161ffff021916908361ffff1602179055508060025f8873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282829054906101000a900461ffff16610f439190612193565b92506101000a81548161ffff021916908361ffff1602179055508083610f699190612193565b92505f82600201548261ffff16610f8091906121c8565b90508060035f856003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254610ff191906120fe565b92505081905550611003878b8b61160d565b98505f835f015f9054906101000a900461ffff1661ffff160361102f57848061102b90612232565b9550505b505061103c565b50611043565b5b50610c66565b8160015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20819055505f60025f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900461ffff1661ffff16036111cd57600460016004805490506110ee9190612332565b815481106110ff576110fe612131565b5b905f5260205f20015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166004868154811061113b5761113a612131565b5b905f5260205f20015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600480548061119257611191612365565b5b600190038181905f5260205f20015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690559055505050506111e0565b84806111d890612232565b955050505050505b610af8565b505f8167ffffffffffffffff81111561120157611200611dc5565b5b60405190808252806020026020018201604052801561122f5781602001602082028036833780820191505090505b5090505f8267ffffffffffffffff81111561124d5761124c611dc5565b5b60405190808252806020026020018201604052801561127b5781602001602082028036833780820191505090505b5090505f5f90505b8381101561138e5784818151811061129e5761129d612131565b5b60200260200101518382815181106112b9576112b8612131565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060025f86838151811061130957611308612131565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900461ffff1682828151811061136b5761136a612131565b5b602002602001019061ffff16908161ffff16815250508080600101915050611283565b5081816040516020016113a2929190612500565b6040516020818303038152906040529650505050505050915050805190602001f35b3480156113cf575f5ffd5b506113d86116e4565b005b3480156113e5575f5ffd5b5061140060048036038101906113fb919061255f565b611821565b60405161141094939291906125ca565b60405180910390f35b611433600480360381019061142e919061260d565b611892565b005b348015611440575f5ffd5b5061145b6004803603810190611456919061265d565b611a2f565b6040516114689190612688565b60405180910390f35b34801561147c575f5ffd5b50611485611a6a565b60405161149291906126a1565b60405180910390f35b3480156114a6575f5ffd5b506114c160048036038101906114bc91906126ba565b611a6f565b6040516114ce91906126e5565b60405180910390f35b3480156114e2575f5ffd5b506114fd60048036038101906114f891906126ba565b611a84565b60405161150a91906126e5565b60405180910390f35b34801561151e575f5ffd5b506115396004803603810190611534919061265d565b611a99565b60405161154994939291906126fe565b60405180910390f35b34801561155d575f5ffd5b50611566611b20565b60405161157391906126e5565b60405180910390f35b348015611587575f5ffd5b506115a2600480360381019061159d91906126ba565b611b26565b6040516115af91906126a1565b60405180910390f35b3480156115c3575f5ffd5b506115cc611b44565b6040516115d991906126a1565b60405180910390f35b3480156115ed575f5ffd5b506115f6611b49565b604051611604929190612500565b60405180910390f35b5f5f5f90505b8281101561167e578473ffffffffffffffffffffffffffffffffffffffff1684828151811061164557611644612131565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff160361167157829150506116dd565b8080600101915050611613565b508383838151811061169357611692612131565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001826116da91906120fe565b90505b9392505050565b5f60035f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205490505f8103611732575061181f565b5f60035f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20819055505f3373ffffffffffffffffffffffffffffffffffffffff1682604051611799906122a6565b5f6040518083038185875af1925050503d805f81146117d3576040519150601f19603f3d011682016040523d82523d5f602084013e6117d8565b606091505b505090508061181c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118139061278b565b60405180910390fd5b50505b565b5f602052815f5260405f208181548110611839575f80fd5b905f5260205f2090600402015f9150915050805f015f9054906101000a900461ffff1690806001015490806002015490806003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905084565b808261ffff166118a291906121c8565b34146118da576040517f3c6b4b2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60065443146118f85760055f6118f09190611cfe565b436006819055505b600560405180608001604052808573ffffffffffffffffffffffffffffffffffffffff1681526020013373ffffffffffffffffffffffffffffffffffffffff1681526020018461ffff16815260200183815250908060018154018082558091505060019003905f5260205f2090600302015f909190919091505f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160010160146101000a81548161ffff021916908361ffff160217905550606082015181600201555050505050565b60048181548110611a3e575f80fd5b905f5260205f20015f915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b601581565b6001602052805f5260405f205f915090505481565b6003602052805f5260405f205f915090505481565b60058181548110611aa8575f80fd5b905f5260205f2090600302015f91509050805f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160149054906101000a900461ffff16908060020154905084565b60065481565b6002602052805f5260405f205f915054906101000a900461ffff1681565b600281565b606080606060048054905067ffffffffffffffff811115611b6d57611b6c611dc5565b5b604051908082528060200260200182016040528015611b9b5781602001602082028036833780820191505090505b5090505f5f90505b600480549050811015611c6c5760025f60048381548110611bc757611bc6612131565b5b905f5260205f20015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900461ffff16828281518110611c4957611c48612131565b5b602002602001019061ffff16908161ffff16815250508080600101915050611ba3565b5060048181805480602002602001604051908101604052809291908181526020018280548015611cee57602002820191905f5260205f20905b815f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611ca5575b5050505050915092509250509091565b5080545f8255600302905f5260205f2090611d199190611d1c565b50565b5f5b80821115611d9b578281015f5f82015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600182015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160146101000a81549061ffff0219169055600282015f90555050600301611d1e565b505090565b5f604051905090565b5f5ffd5b5f5ffd5b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b611dfb82611db5565b810181811067ffffffffffffffff82111715611e1a57611e19611dc5565b5b80604052505050565b5f611e2c611da0565b9050611e388282611df2565b919050565b5f67ffffffffffffffff821115611e5757611e56611dc5565b5b602082029050602081019050919050565b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f611e9582611e6c565b9050919050565b611ea581611e8b565b8114611eaf575f5ffd5b50565b5f81359050611ec081611e9c565b92915050565b5f611ed8611ed384611e3d565b611e23565b90508083825260208201905060208402830185811115611efb57611efa611e68565b5b835b81811015611f245780611f108882611eb2565b845260208401935050602081019050611efd565b5050509392505050565b5f82601f830112611f4257611f41611db1565b5b8135611f52848260208601611ec6565b91505092915050565b5f67ffffffffffffffff821115611f7557611f74611dc5565b5b602082029050602081019050919050565b5f61ffff82169050919050565b611f9c81611f86565b8114611fa6575f5ffd5b50565b5f81359050611fb781611f93565b92915050565b5f611fcf611fca84611f5b565b611e23565b90508083825260208201905060208402830185811115611ff257611ff1611e68565b5b835b8181101561201b57806120078882611fa9565b845260208401935050602081019050611ff4565b5050509392505050565b5f82601f83011261203957612038611db1565b5b8135612049848260208601611fbd565b91505092915050565b5f5f6040838503121561206857612067611da9565b5b5f83013567ffffffffffffffff81111561208557612084611dad565b5b61209185828601611f2e565b925050602083013567ffffffffffffffff8111156120b2576120b1611dad565b5b6120be85828601612025565b9150509250929050565b5f819050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f612108826120c8565b9150612113836120c8565b925082820190508082111561212b5761212a6120d1565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f61216882611f86565b915061217383611f86565b9250828201905061ffff81111561218d5761218c6120d1565b5b92915050565b5f61219d82611f86565b91506121a883611f86565b9250828203905061ffff8111156121c2576121c16120d1565b5b92915050565b5f6121d2826120c8565b91506121dd836120c8565b92508282026121eb816120c8565b91508282048414831517612202576122016120d1565b5b5092915050565b5f61221382611f86565b915061ffff8203612227576122266120d1565b5b600182019050919050565b5f61223c826120c8565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361226e5761226d6120d1565b5b600182019050919050565b5f81905092915050565b50565b5f6122915f83612279565b915061229c82612283565b5f82019050919050565b5f6122b082612286565b9150819050919050565b5f82825260208201905092915050565b7f6275726e206661696c65640000000000000000000000000000000000000000005f82015250565b5f6122fe600b836122ba565b9150612309826122ca565b602082019050919050565b5f6020820190508181035f83015261232b816122f2565b9050919050565b5f61233c826120c8565b9150612347836120c8565b925082820390508181111561235f5761235e6120d1565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b6123c481611e8b565b82525050565b5f6123d583836123bb565b60208301905092915050565b5f602082019050919050565b5f6123f782612392565b612401818561239c565b935061240c836123ac565b805f5b8381101561243c57815161242388826123ca565b975061242e836123e1565b92505060018101905061240f565b5085935050505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b61247b81611f86565b82525050565b5f61248c8383612472565b60208301905092915050565b5f602082019050919050565b5f6124ae82612449565b6124b88185612453565b93506124c383612463565b805f5b838110156124f35781516124da8882612481565b97506124e583612498565b9250506001810190506124c6565b5085935050505092915050565b5f6040820190508181035f83015261251881856123ed565b9050818103602083015261252c81846124a4565b90509392505050565b61253e816120c8565b8114612548575f5ffd5b50565b5f8135905061255981612535565b92915050565b5f5f6040838503121561257557612574611da9565b5b5f61258285828601611eb2565b92505060206125938582860161254b565b9150509250929050565b6125a681611f86565b82525050565b6125b5816120c8565b82525050565b6125c481611e8b565b82525050565b5f6080820190506125dd5f83018761259d565b6125ea60208301866125ac565b6125f760408301856125ac565b61260460608301846125bb565b95945050505050565b5f5f5f6060848603121561262457612623611da9565b5b5f61263186828701611eb2565b935050602061264286828701611fa9565b92505060406126538682870161254b565b9150509250925092565b5f6020828403121561267257612671611da9565b5b5f61267f8482850161254b565b91505092915050565b5f60208201905061269b5f8301846125bb565b92915050565b5f6020820190506126b45f83018461259d565b92915050565b5f602082840312156126cf576126ce611da9565b5b5f6126dc84828501611eb2565b91505092915050565b5f6020820190506126f85f8301846125ac565b92915050565b5f6080820190506127115f8301876125bb565b61271e60208301866125bb565b61272b604083018561259d565b61273860608301846125ac565b95945050505050565b7f7769746864726177616c206661696c65640000000000000000000000000000005f82015250565b5f6127756011836122ba565b915061278082612741565b602082019050919050565b5f6020820190508181035f8301526127a281612769565b905091905056fea26469706673582212201f4c46e4e6bbc7359267d75781fe193947753726b2128e3820cf35d35f3189c264736f6c63430008210033") )