diff --git a/ethapi/api.go b/ethapi/api.go index 0ac4013f7..f63e17510 100644 --- a/ethapi/api.go +++ b/ethapi/api.go @@ -947,7 +947,7 @@ func (diff *StateOverride) Apply(state *state.StateDB) error { return nil } -func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*evmcore.ExecutionResult, error) { +func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64, forceTransactionFeePaid bool) (*evmcore.ExecutionResult, error) { defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) @@ -989,6 +989,9 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash // Execute the message. gp := new(evmcore.GasPool).AddGas(math.MaxUint64) + if forceTransactionFeePaid { + evm.TransactionFeePaid = true + } result, err := evmcore.ApplyMessage(evm, msg, gp) if err := vmError(); err != nil { return nil, err @@ -1041,7 +1044,7 @@ func (e *revertError) ErrorData() interface{} { // Note, this function doesn't make and changes in the state/blockchain and is // useful to execute and retrieve values. func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Bytes, error) { - result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap()) + result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap(), true) if err != nil { return nil, err } @@ -1087,7 +1090,12 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr if state == nil || err != nil { return 0, err } - balance := state.GetBalance(*args.From) // from can't be nil + var balance *big.Int + if args.From.IsEntryPoint() { + balance = state.GetBalance(*args.To) // to can't be nil + } else { + balance = state.GetBalance(*args.From) // from can't be nil + } available := new(big.Int).Set(balance) if args.Value != nil { if args.Value.ToInt().Cmp(available) >= 0 { @@ -1119,7 +1127,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr executable := func(gas uint64) (bool, *evmcore.ExecutionResult, error) { args.Gas = (*hexutil.Uint64)(&gas) - result, err := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap) + result, err := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap, false) if err != nil { if errors.Is(err, evmcore.ErrIntrinsicGas) { return true, nil, nil // Special case, raise gas limit @@ -1779,29 +1787,41 @@ func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args TransactionArgs) (common.Hash, error) { // Look up the wallet containing the requested signer account := accounts.Account{Address: args.from()} - - wallet, err := s.b.AccountManager().Find(account) - if err != nil { - return common.Hash{}, err + isAATransaction := args.From.IsEntryPoint() + var ( + signed *types.Transaction + wallet accounts.Wallet + err error + ) + if !isAATransaction { + wallet, err = s.b.AccountManager().Find(account) + if err != nil { + return common.Hash{}, err + } } - if args.Nonce == nil { // Hold the addresse's mutex around signing to prevent concurrent assignment of // the same nonce to multiple accounts. s.nonceLock.LockAddr(args.from()) defer s.nonceLock.UnlockAddr(args.from()) } - - // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b); err != nil { - return common.Hash{}, err - } - // Assemble the transaction and sign with the wallet - tx := args.toTransaction() - - signed, err := wallet.SignTx(account, tx, s.b.ChainConfig().ChainID) - if err != nil { - return common.Hash{}, err + if isAATransaction { + if err = args.setAADefaults(ctx, s.b); err != nil { + return common.Hash{}, err + } + tx := args.toTransaction() + signed = tx.WithAASignature() + } else { + // Set some sanity defaults and terminate on failure + if err := args.setDefaults(ctx, s.b); err != nil { + return common.Hash{}, err + } + // Assemble the transaction and sign with the wallet + tx := args.toTransaction() + signed, err = wallet.SignTx(account, tx, s.b.ChainConfig().ChainID) + if err != nil { + return common.Hash{}, err + } } return SubmitTransaction(ctx, s.b, signed) } diff --git a/ethapi/transaction_args.go b/ethapi/transaction_args.go index 42363f283..1dbe9e7c7 100644 --- a/ethapi/transaction_args.go +++ b/ethapi/transaction_args.go @@ -146,6 +146,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { Value: args.Value, Data: args.Data, AccessList: args.AccessList, + Nonce: args.Nonce, } pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, b.RPCGasCap()) @@ -162,6 +163,58 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { return nil } +// setDefaults is a helper function that fills in default values for unspecified AA tx fields. +func (args *TransactionArgs) setAADefaults(ctx context.Context, b Backend) error { + if args.GasPrice == nil { + price := b.SuggestGasTipCap(ctx, gasprice.AsDefaultCertainty) + price.Add(price, b.MinGasPrice()) + args.GasPrice = (*hexutil.Big)(price) + } + if args.Value == nil { + args.Value = new(hexutil.Big) + } else if args.Value.ToInt().Sign() != 0 { + return errors.New(`"value" has to be 0 for account abstraction transactions`) + } + if args.To == nil { + return errors.New(`AA transactions require "to" address`) + } + if args.Nonce == nil { + nonce, err := b.GetPoolNonce(ctx, *args.To) + if err != nil { + return err + } + args.Nonce = (*hexutil.Uint64)(&nonce) + } + if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) { + return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`) + } + // Estimate the gas usage if necessary. + if args.Gas == nil { + // For backwards-compatibility reason, we try both input and data + // but input is preferred. + input := args.Input + if input == nil { + input = args.Data + } + callArgs := TransactionArgs{ + From: args.From, // From shouldn't be nil + To: args.To, + GasPrice: args.GasPrice, + Value: args.Value, + Nonce: args.Nonce, + Data: input, + } + pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) + estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, b.RPCGasCap()) + if err != nil { + return err + } + args.Gas = &estimated + log.Trace("Estimate gas usage automatically", "gas", args.Gas) + } + return nil +} + // ToMessage converts the transaction arguments to the Message type used by the // core evm. This method is used in calls and traces that do not require a real // live transaction. @@ -229,7 +282,11 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (t if args.AccessList != nil { accessList = *args.AccessList } - msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, gasFeeCap, gasTipCap, data, accessList, true) + nonce := uint64(0) + if args.Nonce != nil { + nonce = uint64(*args.Nonce) + } + msg := types.NewMessage(addr, args.To, nonce, value, gas, gasPrice, gasFeeCap, gasTipCap, data, accessList, true) return msg, nil } @@ -238,6 +295,15 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (t func (args *TransactionArgs) toTransaction() *types.Transaction { var data types.TxData switch { + case args.From.IsEntryPoint(): + data = &types.AccountAbstractionTx{ + To: args.To, + Nonce: uint64(*args.Nonce), + Gas: uint64(*args.Gas), + GasPrice: (*big.Int)(args.GasPrice), + Value: (*big.Int)(args.Value), + Data: args.data(), + } case args.MaxFeePerGas != nil: al := types.AccessList{} if args.AccessList != nil { diff --git a/eventcheck/basiccheck/basic_check.go b/eventcheck/basiccheck/basic_check.go index d9de061fb..0fe09c76f 100644 --- a/eventcheck/basiccheck/basic_check.go +++ b/eventcheck/basiccheck/basic_check.go @@ -60,7 +60,7 @@ func validateTx(tx *types.Transaction) error { return ErrNegativeValue } // Ensure the transaction has more gas than the basic tx fee. - intrGas, err := evmcore.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil) + intrGas, err := evmcore.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, tx.Type() == types.AccountAbstractionTxType) if err != nil { return err } diff --git a/eventcheck/epochcheck/epoch_check.go b/eventcheck/epochcheck/epoch_check.go index a4b51d7f7..114395907 100644 --- a/eventcheck/epochcheck/epoch_check.go +++ b/eventcheck/epochcheck/epoch_check.go @@ -87,7 +87,7 @@ func CheckTxs(txs types.Transactions, rules opera.Rules) error { maxType = 1 } if rules.Upgrades.London { - maxType = 2 + maxType = 3 } for _, tx := range txs { if tx.Type() > maxType { diff --git a/evmcore/bench_test.go b/evmcore/bench_test.go index b5952856d..2822b3ecd 100644 --- a/evmcore/bench_test.go +++ b/evmcore/bench_test.go @@ -78,7 +78,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) { return func(i int, gen *BlockGen) { toaddr := common.Address{} data := make([]byte, nbytes) - gas, _ := IntrinsicGas(data, types.AccessList{}, false) + gas, _ := IntrinsicGas(data, types.AccessList{}, false, false) tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey) gen.AddTx(tx) } diff --git a/evmcore/error.go b/evmcore/error.go index cba678133..9b50c5f95 100644 --- a/evmcore/error.go +++ b/evmcore/error.go @@ -84,4 +84,13 @@ var ( // ErrSenderNoEOA is returned if the sender of a transaction is a contract. ErrSenderNoEOA = errors.New("sender not an eoa") + + // ErrSenderNoContract is returned if the sender of a transaction is not a contract. + ErrSenderNoContract = errors.New("sender not a contract") + + // ErrNoRecipient is returned if a recipient is not specified + ErrNoRecipient = errors.New("no recipient specified for account abstraction transaction") + + // ErrMalformedAATransaction is returned if an AA transaction is malformed + ErrMalformedAATransaction = errors.New("AA transaction malformed") ) diff --git a/evmcore/evm.go b/evmcore/evm.go index 8958754f1..1046515d5 100644 --- a/evmcore/evm.go +++ b/evmcore/evm.go @@ -20,6 +20,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" ) @@ -60,9 +61,19 @@ func NewEVMBlockContext(header *EvmHeader, chain DummyChain, author *common.Addr // NewEVMTxContext creates a new transaction context for a single transaction. func NewEVMTxContext(msg Message) vm.TxContext { + var origin common.Address + if msg.IsAA() { + origin = types.AAEntryPoint + } else { + origin = msg.From() + } return vm.TxContext{ - Origin: msg.From(), - GasPrice: new(big.Int).Set(msg.GasPrice()), + Origin: origin, + GasPrice: new(big.Int).Set(msg.GasPrice()), + GasLimit: msg.Gas(), + Nonce: msg.Nonce(), + TransactionFeePaid: !msg.IsAA(), + AccountAbstraction: msg.IsAA(), } } diff --git a/evmcore/state_transition.go b/evmcore/state_transition.go index 1fee3d1f4..1861d147e 100644 --- a/evmcore/state_transition.go +++ b/evmcore/state_transition.go @@ -42,8 +42,10 @@ The state transitioning model does all the necessary work to work out a valid ne 3) Create a new state object if the recipient is \0*32 4) Value transfer == If contract creation == - 4a) Attempt to run transaction data - 4b) If valid, use result as code for the new state object + + 4a) Attempt to run transaction data + 4b) If valid, use result as code for the new state object + == end == 5) Run Script section 6) Derive new state root @@ -73,6 +75,7 @@ type Message interface { Nonce() uint64 IsFake() bool + IsAA() bool Data() []byte AccessList() types.AccessList } @@ -113,11 +116,13 @@ func (result *ExecutionResult) Revert() []byte { } // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool) (uint64, error) { +func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isAA bool) (uint64, error) { // Set the starting gas for the raw transaction var gas uint64 if isContractCreation { gas = params.TxGasContractCreation + } else if isAA { + gas = params.AATxGas } else { gas = params.TxGas } @@ -188,17 +193,31 @@ func (st *StateTransition) to() common.Address { func (st *StateTransition) buyGas() error { mgval := new(big.Int).SetUint64(st.msg.Gas()) mgval = mgval.Mul(mgval, st.gasPrice) - // Note: Opera doesn't need to check against gasFeeCap instead of gasPrice, as it's too aggressive in the asynchronous environment - if have, want := st.state.GetBalance(st.msg.From()), mgval; have.Cmp(want) < 0 { - return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want) + + if !st.msg.IsAA() { + // Note: Opera doesn't need to check against gasFeeCap instead of gasPrice, as it's too aggressive in the asynchronous environment + if have, want := st.state.GetBalance(st.msg.From()), mgval; have.Cmp(want) < 0 { + return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want) + } } if err := st.gp.SubGas(st.msg.Gas()); err != nil { return err } - st.gas += st.msg.Gas() + + // AA transactions can have no more than `params.VerificationGasCap` amount of gas until calling PAYGAS opcode. + // PAYGAS will return the initial gas limit. + if st.msg.IsAA() && st.msg.Gas() > params.VerificationGasCap { + st.gas += params.VerificationGasCap + } else { + st.gas += st.msg.Gas() + } st.initialGas = st.msg.Gas() - st.state.SubBalance(st.msg.From(), mgval) + + if !st.msg.IsAA() { + st.state.SubBalance(st.msg.From(), mgval) + } + return nil } @@ -214,11 +233,22 @@ func (st *StateTransition) preCheck() error { return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow, st.msg.From().Hex(), msgNonce, stNonce) } - // Make sure the sender is an EOA - if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) { + + isAA := st.msg.IsAA() + codeHash := st.state.GetCodeHash(st.msg.From()) + isContract := codeHash != emptyCodeHash && codeHash != (common.Hash{}) + if !isAA && isContract { return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA, st.msg.From().Hex(), codeHash) } + if isAA { + if st.value.Sign() != 0 { + return fmt.Errorf("value of AA transaction must be zero") + } + if !isContract { + return fmt.Errorf("%w: address %v", ErrSenderNoContract, st.msg.From().Hex()) + } + } } // Note: Opera doesn't need to check gasFeeCap >= BaseFee, because it's already checked by epochcheck return st.buyGas() @@ -232,13 +262,13 @@ func (st *StateTransition) internal() bool { // TransitionDb will transition the state by applying the current message and // returning the evm execution result with following fields. // -// - used gas: -// total gas used (including gas being refunded) -// - returndata: -// the returned data from evm -// - concrete execution error: -// various **EVM** error which aborts the execution, -// e.g. ErrOutOfGas, ErrExecutionReverted +// - used gas: +// total gas used (including gas being refunded) +// - returndata: +// the returned data from evm +// - concrete execution error: +// various **EVM** error which aborts the execution, +// e.g. ErrOutOfGas, ErrExecutionReverted // // However if any consensus issue encountered, return the error directly with // nil evm execution result. @@ -266,7 +296,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { london := st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) // Check clauses 4-5, subtract intrinsic gas if everything is correct - gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation) + gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, st.msg.IsAA()) if err != nil { return nil, err } @@ -319,9 +349,11 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { } st.gas += refund - // Return wei for remaining gas, exchanged at the original rate. - remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice) - st.state.AddBalance(st.msg.From(), remaining) + if st.evm.TransactionFeePaid { + // Return wei for remaining gas, exchanged at the original rate. + remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice) + st.state.AddBalance(st.msg.From(), remaining) + } // Also return remaining gas to the block gas counter so it is // available for the next transaction. diff --git a/evmcore/tx_pool.go b/evmcore/tx_pool.go index 8f07673a2..7228289c6 100644 --- a/evmcore/tx_pool.go +++ b/evmcore/tx_pool.go @@ -30,11 +30,13 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" notify "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" + "github.com/Fantom-foundation/go-opera/opera" "github.com/Fantom-foundation/go-opera/utils/signers/gsignercache" ) @@ -83,6 +85,9 @@ var ( // transaction with a negative value. ErrNegativeValue = errors.New("negative value") + // ErrPositiveValue is returned if an AA transaction value isn't equal to zero + ErrNonZeroValue = errors.New("nonzero value") + // ErrOversizedData is returned if the input data of a transaction is greater // than some meaningful limit a user might use. This is not a consensus error // making the transaction invalid, rather a DOS protection. @@ -136,6 +141,7 @@ const ( // some pre checks in tx pool and event subscribers. type StateReader interface { CurrentBlock() *EvmBlock + GetHeader(hash common.Hash, number uint64) *EvmHeader GetBlock(hash common.Hash, number uint64) *EvmBlock StateAt(root common.Hash) (*state.StateDB, error) MinGasPrice() *big.Int @@ -237,6 +243,7 @@ type TxPool struct { mu sync.RWMutex istanbul bool // Fork indicator whether we are in the istanbul stage. + eip2938 bool // Fork indicator whether we are using EIP-2938 type transactions. eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions. eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions. @@ -605,6 +612,10 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if !pool.eip1559 && tx.Type() == types.DynamicFeeTxType { return ErrTxTypeNotSupported } + // Reject account abstraction transactions until EIP-2938 activates. + if !pool.eip2938 && tx.Type() == types.AccountAbstractionTxType { + return ErrTxTypeNotSupported + } // Reject transactions over defined size to prevent DOS attacks if uint64(tx.Size()) > txMaxSize { return ErrOversizedData @@ -629,11 +640,45 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if tx.GasFeeCapIntCmp(tx.GasTipCap()) < 0 { return ErrTipAboveFeeCap } + if tx.Type() == types.AccountAbstractionTxType { + // Ensure the AA transaction has recipient + if tx.To() == nil { + return ErrNoRecipient + } + // Ensure the AA transaction has Value == 0 + if tx.Value().Sign() != 0 { + return ErrNonZeroValue + } + } // Make sure the transaction is signed properly. from, err := types.Sender(pool.signer, tx) if err != nil { return ErrInvalidSender } + block := pool.chain.CurrentBlock() + if tx.Type() == types.AccountAbstractionTxType && block != nil { + header := block.Header() + msg, err := tx.AsMessage(pool.signer, header.BaseFee) + if err != nil { + return ErrMalformedAATransaction + } + + blockContext := NewEVMBlockContext(header, pool.chain, nil) + txContext := NewEVMTxContext(msg) + evm := vm.NewEVM(blockContext, txContext, pool.currentState, pool.chain.Config(), opera.DefaultVMConfig) + evm.HaltOnPaygas() + + gp := new(GasPool).AddGas(msg.Gas()) + snapshot := pool.currentState.Snapshot() + result, err := ApplyMessage(evm, msg, gp) + pool.currentState.RevertToSnapshot(snapshot) + if err != nil { + return err + } + if result.Err != nil { + return result.Err + } + } // Drop non-local transactions under our own minimal accepted gas price or tip local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network if !local && tx.GasTipCapIntCmp(pool.gasPrice) < 0 { @@ -658,7 +703,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrInsufficientFunds } // Ensure the transaction has more gas than the basic tx fee. - intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil) + intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, tx.Type() == types.AccountAbstractionTxType) if err != nil { return err } @@ -1326,6 +1371,7 @@ func (pool *TxPool) reset(oldHead, newHead *EvmHeader) { pool.istanbul = pool.chainconfig.IsIstanbul(next) pool.eip2718 = pool.chainconfig.IsBerlin(next) pool.eip1559 = pool.chainconfig.IsLondon(next) + pool.eip2938 = pool.chainconfig.IsEIP2938(next) } // promoteExecutables moves transactions that have become processable from the diff --git a/evmcore/tx_pool_test.go b/evmcore/tx_pool_test.go index 11532fe4b..246d4385d 100644 --- a/evmcore/tx_pool_test.go +++ b/evmcore/tx_pool_test.go @@ -96,6 +96,10 @@ func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *EvmBlock { return bc.CurrentBlock() } +func (bc *testBlockChain) GetHeader(hash common.Hash, number uint64) *EvmHeader { + return bc.CurrentBlock().Header() +} + func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { return bc.statedb, nil } diff --git a/gossip/account_abstraction_test.go b/gossip/account_abstraction_test.go new file mode 100644 index 000000000..06f0496dc --- /dev/null +++ b/gossip/account_abstraction_test.go @@ -0,0 +1,373 @@ +package gossip + +import ( + "crypto/ecdsa" + "math/big" + "testing" + + "github.com/Fantom-foundation/go-opera/gossip/contract/aatester" + "github.com/Fantom-foundation/go-opera/gossip/contract/wallet" + "github.com/Fantom-foundation/go-opera/logger" + "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/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" + "github.com/stretchr/testify/require" +) + +var aaGasLimit uint64 = 80000 +var aaGasPrice = new(big.Int).SetUint64(1e12) + +func (env *testEnv) walletSignature(address common.Address, privateKey *ecdsa.PrivateKey) wallet.WalletSignature { + nonce := new(uint256.Int).SetUint64(env.State().GetNonce(address)) + hashedData := crypto.Keccak256(address.Bytes(), common.LeftPadBytes(nonce.Bytes(), 32)) + signature, _ := crypto.Sign(hashedData, privateKey) + var ( + r [32]byte + s [32]byte + ) + copy(r[:], signature[:32]) + copy(s[:], signature[32:64]) + return wallet.WalletSignature{ + V: uint8(signature[64]) + 27, + R: r, + S: s, + } +} + +func TestSimpleAAWallet(t *testing.T) { + logger.SetLevel("warn") + logger.SetTestMode(t) + require := require.New(t) + + env := newTestEnv(1, 1) + defer env.Close() + + payer := env.Pay(1) + owner := env.Address(2) + ownerKey := env.privateKey(2) + + initialBalance := uint64(1e18) + payer.Value = new(big.Int).SetUint64(initialBalance) + addr, tx, cWallet, err := wallet.DeployContract(payer, env, owner) + require.NoError(err) + require.NotNil(cWallet) + + receipts, err := env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + require.Equal(addr, receipts[0].ContractAddress) + + recipient := common.HexToAddress("0xcccccccccccccccccccccccccccccccccccccccc") + amount := new(big.Int).SetUint64(100) + gasPrice := new(big.Int).SetUint64(1e12) + gasLimit := uint64(80000) + + count := 10 + gasUsed := uint64(0) + for i := 0; i < count; i++ { + signature := env.walletSignature(addr, ownerKey) + data := cWallet.Transfer(recipient, amount, nil, signature) + state := env.State() + nonce := state.GetNonce(addr) + txdata := &types.AccountAbstractionTx{ + Nonce: nonce, + To: &addr, + Value: common.Big0, + Gas: gasLimit, + GasPrice: gasPrice, + Data: data, + } + + tx = types.NewTx(txdata) + signed := tx.WithAASignature() + + receipts, err = env.ApplyTxs(nextEpoch, signed) + require.NoError(err) + + gasUsed += receipts[0].GasUsed + } + + state := env.State() + sentAmount := new(big.Int).Mul(amount, new(big.Int).SetUint64(uint64(count))) + recipientBalance := state.GetBalance(recipient) + require.Equal(recipientBalance, sentAmount) + + expectedBalance := new(big.Int).SetUint64(initialBalance) + expectedBalance = expectedBalance.Sub(expectedBalance, new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(gasUsed))) + expectedBalance = expectedBalance.Sub(expectedBalance, sentAmount) + + contractBalance := state.GetBalance(addr) + require.Equal(contractBalance, expectedBalance) + + callOpts := &bind.CallOpts{From: types.AAEntryPoint} + returnedOwner, err := cWallet.Owner(callOpts) + require.NoError(err) + require.Equal(returnedOwner, owner) + + newOwner := env.Address(3) + newOwnerKey := env.privateKey(3) + + signature := env.walletSignature(addr, newOwnerKey) + data := cWallet.Transfer(recipient, amount, nil, signature) + nonce := env.State().GetNonce(addr) + txdata := &types.AccountAbstractionTx{ + Nonce: nonce, + To: &addr, + Value: common.Big0, + Gas: gasLimit, + GasPrice: gasPrice, + Data: data, + } + + tx = types.NewTx(txdata) + signed := tx.WithAASignature() + + // Execution reverted - wrong signature + receipts, err = env.ApplyTxs(nextEpoch, signed) + require.NoError(err) + + state = env.State() + recipientBalance = state.GetBalance(recipient) + require.Equal(recipientBalance, sentAmount) + + contractBalance = state.GetBalance(addr) + require.Equal(contractBalance, expectedBalance) + + signature = env.walletSignature(addr, ownerKey) + data = cWallet.ChangeOwner(newOwner, signature) + nonce = env.State().GetNonce(addr) + txdata = &types.AccountAbstractionTx{ + Nonce: nonce, + To: &addr, + Value: common.Big0, + Gas: gasLimit, + GasPrice: gasPrice, + Data: data, + } + + tx = types.NewTx(txdata) + signed = tx.WithAASignature() + + receipts, err = env.ApplyTxs(nextEpoch, signed) + require.NoError(err) + + returnedOwner, err = cWallet.Owner(callOpts) + require.NoError(err) + require.Equal(returnedOwner, newOwner) +} + +func (env *testEnv) buildAATx(to common.Address, data []byte) *types.Transaction { + state := env.State() + nonce := state.GetNonce(to) + + txdata := &types.AccountAbstractionTx{ + Nonce: nonce, + To: &to, + Value: common.Big0, + Gas: aaGasLimit, + GasPrice: aaGasPrice, + Data: data, + } + + tx := types.NewTx(txdata) + return tx.WithAASignature() +} + +func resetTester(env *testEnv, t *testing.T, contract common.Address) { + require := require.New(t) + cTester, err := aatester.NewContract(contract, env) + require.NoError(err) + + data := cTester.Reset() + tx := env.buildAATx(contract, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + callOpts := &bind.CallOpts{From: types.AAEntryPoint} + + nonce, err := cTester.Nonce(callOpts) + require.NoError(err) + require.Equal(nonce.Sign(), 0) + + balance, err := cTester.Balance(callOpts) + require.NoError(err) + require.Equal(balance.Sign(), 0) + + gasPrice, err := cTester.GasPrice(callOpts) + require.NoError(err) + require.Equal(gasPrice.Sign(), 0) + + gasLeft, err := cTester.GasLeft(callOpts) + require.NoError(err) + require.Equal(gasLeft.Sign(), 0) + + origin, err := cTester.Origin(callOpts) + require.NoError(err) + require.Equal(origin, common.Address{}) + + sender, err := cTester.Sender(callOpts) + require.NoError(err) + require.Equal(sender, common.Address{}) +} + +func TestAATransactions(t *testing.T) { + logger.SetLevel("warn") + logger.SetTestMode(t) + require := require.New(t) + + env := newTestEnv(1, 1) + defer env.Close() + + payer := env.Pay(1) + initialBalance := uint64(1e18) + payer.Value = new(big.Int).SetUint64(initialBalance) + addr, tx, cTester, err := aatester.DeployContract(payer, env) + require.NoError(err) + require.NotNil(cTester) + + receipts, err := env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + require.Equal(addr, receipts[0].ContractAddress) + + resetTester(env, t, addr) + + data := cTester.SetNonce() + tx = env.buildAATx(addr, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + expectedNonce := tx.Nonce() + + data = cTester.SetOriginBeforePaygas() + tx = env.buildAATx(addr, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + data = cTester.SetSenderBeforePaygas() + tx = env.buildAATx(addr, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + callOpts := &bind.CallOpts{From: types.AAEntryPoint} + + nonce, err := cTester.Nonce(callOpts) + require.NoError(err) + require.Equal(expectedNonce, nonce.Uint64()) + + origin, err := cTester.Origin(callOpts) + require.NoError(err) + require.Equal(types.AAEntryPoint, origin) + + sender, err := cTester.Sender(callOpts) + require.NoError(err) + require.Equal(types.AAEntryPoint, sender) + + resetTester(env, t, addr) + + data = cTester.SetNonceBeforePaygasAndRevert() + tx = env.buildAATx(addr, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + expectedNonce = tx.Nonce() + nonce, err = cTester.Nonce(callOpts) + require.NoError(err) + require.Equal(expectedNonce, nonce.Uint64()) + + state := env.State() + require.Equal(state.GetNonce(addr), expectedNonce+1) + + resetTester(env, t, addr) + + data = cTester.SetOriginAfterPaygas() + tx = env.buildAATx(addr, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + data = cTester.SetSenderAfterPaygas() + tx = env.buildAATx(addr, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + data = cTester.SetGasPrice() + tx = env.buildAATx(addr, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + origin, err = cTester.Origin(callOpts) + require.NoError(err) + require.Equal(types.AAEntryPoint, origin) + + sender, err = cTester.Sender(callOpts) + require.NoError(err) + require.Equal(types.AAEntryPoint, sender) + + gasPrice, err := cTester.GasPrice(callOpts) + require.NoError(err) + require.Equal(aaGasPrice, gasPrice) + + resetTester(env, t, addr) + + data = cTester.SetBalanceBeforePaygas() + tx = env.buildAATx(addr, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + balance, err := cTester.Balance(callOpts) + require.NoError(err) + require.Equal(balance.Sign(), 0) + + data = cTester.SetBalanceAfterPaygas() + tx = env.buildAATx(addr, data) + + state = env.State() + receipts, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + balance, err = cTester.Balance(callOpts) + require.NoError(err) + require.Equal(balance.Cmp(common.Big0), 1) + + resetTester(env, t, addr) + + data = cTester.SetGasLeftBeforePaygas() + tx = env.buildAATx(addr, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + gasLeftBefore, err := cTester.GasLeft(callOpts) + require.NoError(err) + + data = cTester.SetGasLeftAfterPaygas() + tx = env.buildAATx(addr, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + gasLeftAfter, err := cTester.GasLeft(callOpts) + require.NoError(err) + + limitDifference := aaGasLimit - params.VerificationGasCap + require.True(new(big.Int).Sub(gasLeftAfter, gasLeftBefore).Uint64() < limitDifference+uint64(100)) + + resetTester(env, t, addr) + + data = cTester.CallSetOrigin(addr) + tx = env.buildAATx(addr, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + origin, err = cTester.Origin(callOpts) + require.NoError(err) + require.Equal(types.AAEntryPoint, origin) + + data = cTester.CallSetSender(addr) + tx = env.buildAATx(addr, data) + _, err = env.ApplyTxs(nextEpoch, tx) + require.NoError(err) + + sender, err = cTester.Sender(callOpts) + require.NoError(err) + require.Equal(sender, addr) +} diff --git a/gossip/common_test.go b/gossip/common_test.go index 5d2c4b82c..08feddece 100644 --- a/gossip/common_test.go +++ b/gossip/common_test.go @@ -542,5 +542,6 @@ func (m callmsg) Gas() uint64 { return m.CallMsg.Gas } func (m callmsg) Value() *big.Int { return m.CallMsg.Value } func (m callmsg) Nonce() uint64 { return 0 } func (m callmsg) IsFake() bool { return true } +func (m callmsg) IsAA() bool { return false } func (m callmsg) Data() []byte { return m.CallMsg.Data } func (m callmsg) AccessList() types.AccessList { return nil } diff --git a/gossip/contract/aatester/AAContract.sol b/gossip/contract/aatester/AAContract.sol new file mode 100644 index 000000000..cc26d5cd1 --- /dev/null +++ b/gossip/contract/aatester/AAContract.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.8.0; +pragma experimental AccountAbstraction; + +account contract AccountAbstractionTester { + uint256 public nonce; + uint256 public gasPrice; + uint256 public gasLeft; + address public sender; + address public origin; + uint256 public balance; + + constructor() payable {} + + function revertBeforePaygas() external { + nonce = tx.nonce; + revert(); + } + + function setNonceBeforePaygasAndRevert() external { + nonce = tx.nonce; + paygas(); + revert(); + } + + function gasLeftBeforePaygas() external { + gasLeft = gasleft(); + paygas(); + } + + function gasLeftAfterPaygas() external { + paygas(); + gasLeft = gasleft(); + } + + function setSenderBeforePaygas() external { + sender = msg.sender; + paygas(); + } + + function setSenderAfterPaygas() external { + paygas(); + sender = msg.sender; + } + + function setOriginBeforePaygas() external { + origin = tx.origin; + paygas(); + } + + function setOriginAfterPaygas() external { + paygas(); + origin = tx.origin; + } + + function setNonce() external { + nonce = tx.nonce; + paygas(); + } + + function setBalanceBeforePaygas() external { + balance = address(this).balance; + paygas(); + } + + function setBalanceAfterPaygas() external { + paygas(); + balance = address(this).balance; + } + + function setGasPrice() external { + paygas(); + gasPrice = tx.gasprice; + } + + function call(address _contract, string calldata _method) external { + paygas(); + (bool success,) = _contract.call(abi.encodeWithSignature(_method)); + require(success); + } + + function reset() external { + paygas(); + nonce = 0; + gasPrice = 0; + gasLeft = 0; + sender = address(0); + origin = address(0); + balance = 0; + } +} diff --git a/gossip/contract/aatester/contract.go b/gossip/contract/aatester/contract.go new file mode 100644 index 000000000..1b6400fae --- /dev/null +++ b/gossip/contract/aatester/contract.go @@ -0,0 +1,461 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package aatester + +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 +) + +// ContractMetaData contains all meta data concerning the Contract contract. +var ContractMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"stateMutability\":\"payable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"balance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_contract\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"_method\",\"type\":\"string\"}],\"name\":\"call\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasLeft\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasLeftAfterPaygas\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasLeftBeforePaygas\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasPrice\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nonce\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"origin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"reset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"revertBeforePaygas\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"sender\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setBalanceAfterPaygas\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setBalanceBeforePaygas\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setGasPrice\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setNonce\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setNonceBeforePaygasAndRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setOriginAfterPaygas\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setOriginBeforePaygas\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setSenderAfterPaygas\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setSenderBeforePaygas\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60806040526108b0806100136000396000f3fe3273ffffffffffffffffffffffffffffffffffffffff1460245736601f57005b600080fd5b608060405234801561003557600080fd5b50600436106101515760003560e01c8063b4adeaff116100d2578063d826f88f11610096578063d826f88f14610262578063e6409c6f1461026c578063ea1bdf4d14610276578063f3f403fb14610280578063fe173b971461028a57610151565b8063b4adeaff1461021c578063b69ef8a814610226578063c32bafd114610244578063c69fa5771461024e578063cae67e791461025857610151565b80638f67cd3c116101195780638f67cd3c146101c25780639099df1b146101cc578063938b5f32146101d6578063ae224cbf146101f4578063affed0e0146101fe57610151565b806309ee8dba146101565780632ddb301b1461016057806344e1ce0d1461017e57806367e404ce1461019a5780637078aa28146101b8575b600080fd5b61015e6102a8565b005b6101686102b3565b6040516101759190610628565b60405180910390f35b61019860048036038101906101939190610710565b6102b9565b005b6101a26103b2565b6040516101af919061077f565b60405180910390f35b6101c06103d8565b005b6101ca6103e2565b005b6101d46103ef565b005b6101de6103f9565b6040516101eb919061077f565b60405180910390f35b6101fc61041f565b005b610206610429565b6040516102139190610628565b60405180910390f35b61022461042f565b005b61022e610473565b60405161023b9190610628565b60405180910390f35b61024c610479565b005b6102566104bd565b005b6102606104c7565b005b61026a6104d1565b005b610274610577565b005b61027e6105bb565b005b6102886105c5565b005b610292610609565b60405161029f9190610628565b60405180910390f35b496000819055600080fd5b60025481565b5c60008373ffffffffffffffffffffffffffffffffffffffff16838360405160240160405160208183030381529060405291906040516102fa9291906107d9565b60405180910390207bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161035c9190610863565b6000604051808303816000865af19150503d8060008114610399576040519150601f19603f3d011682016040523d82523d6000602084013e61039e565b606091505b50509050806103ac57600080fd5b50505050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5c3a600181905550565b496000819055505c600080fd5b496000819055505c565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5c5a600281905550565b60005481565b33600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505c565b60055481565b5c33600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b476005819055505c565b5c47600581905550565b5c60008081905550600060018190555060006002819055506000600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600581905550565b32600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505c565b5a6002819055505c565b5c32600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60015481565b6000819050919050565b6106228161060f565b82525050565b600060208201905061063d6000830184610619565b92915050565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006106788261064d565b9050919050565b6106888161066d565b811461069357600080fd5b50565b6000813590506106a58161067f565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126106d0576106cf6106ab565b5b8235905067ffffffffffffffff8111156106ed576106ec6106b0565b5b602083019150836001820283011115610709576107086106b5565b5b9250929050565b60008060006040848603121561072957610728610643565b5b600061073786828701610696565b935050602084013567ffffffffffffffff81111561075857610757610648565b5b610764868287016106ba565b92509250509250925092565b6107798161066d565b82525050565b60006020820190506107946000830184610770565b92915050565b600081905092915050565b82818337600083830152505050565b60006107c0838561079a565b93506107cd8385846107a5565b82840190509392505050565b60006107e68284866107b4565b91508190509392505050565b600081519050919050565b600081905092915050565b60005b8381101561082657808201518184015260208101905061080b565b60008484015250505050565b600061083d826107f2565b61084781856107fd565b9350610857818560208601610808565b80840191505092915050565b600061086f8284610832565b91508190509291505056fea264697066735822122055449b6ae02445607e4d1d25278fb510e39e3892355fc7ebd3e72bbb96ecb96964736f6c63430008130033", +} + +var ( + sAbi, _ = abi.JSON(strings.NewReader(ContractMetaData.ABI)) +) + +// ContractABI is the input ABI used to generate the binding from. +// Deprecated: Use ContractMetaData.ABI instead. +var ContractABI = ContractMetaData.ABI + +// ContractBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use ContractMetaData.Bin instead. +var ContractBin = ContractMetaData.Bin + +// DeployContract deploys a new Ethereum contract, binding an instance of Contract to it. +func DeployContract(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Contract, error) { + parsed, err := ContractMetaData.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(ContractBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Contract{ContractCaller: ContractCaller{contract: contract}, ContractTransactor: ContractTransactor{contract: contract}, ContractFilterer: ContractFilterer{contract: contract}}, nil +} + +// Contract is an auto generated Go binding around an Ethereum contract. +type Contract struct { + ContractCaller // Read-only binding to the contract + ContractTransactor // Write-only binding to the contract + ContractFilterer // Log filterer for contract events +} + +// ContractCaller is an auto generated read-only Go binding around an Ethereum contract. +type ContractCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ContractTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ContractTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ContractFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ContractFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ContractSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ContractSession struct { + Contract *Contract // 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 +} + +// ContractCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ContractCallerSession struct { + Contract *ContractCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ContractTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ContractTransactorSession struct { + Contract *ContractTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ContractRaw is an auto generated low-level Go binding around an Ethereum contract. +type ContractRaw struct { + Contract *Contract // Generic contract binding to access the raw methods on +} + +// ContractCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ContractCallerRaw struct { + Contract *ContractCaller // Generic read-only contract binding to access the raw methods on +} + +// ContractTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ContractTransactorRaw struct { + Contract *ContractTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewContract creates a new instance of Contract, bound to a specific deployed contract. +func NewContract(address common.Address, backend bind.ContractBackend) (*Contract, error) { + contract, err := bindContract(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Contract{ContractCaller: ContractCaller{contract: contract}, ContractTransactor: ContractTransactor{contract: contract}, ContractFilterer: ContractFilterer{contract: contract}}, nil +} + +// NewContractCaller creates a new read-only instance of Contract, bound to a specific deployed contract. +func NewContractCaller(address common.Address, caller bind.ContractCaller) (*ContractCaller, error) { + contract, err := bindContract(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ContractCaller{contract: contract}, nil +} + +// NewContractTransactor creates a new write-only instance of Contract, bound to a specific deployed contract. +func NewContractTransactor(address common.Address, transactor bind.ContractTransactor) (*ContractTransactor, error) { + contract, err := bindContract(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ContractTransactor{contract: contract}, nil +} + +// NewContractFilterer creates a new log filterer instance of Contract, bound to a specific deployed contract. +func NewContractFilterer(address common.Address, filterer bind.ContractFilterer) (*ContractFilterer, error) { + contract, err := bindContract(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ContractFilterer{contract: contract}, nil +} + +// bindContract binds a generic wrapper to an already deployed contract. +func bindContract(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ContractABI)) + 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 (_Contract *ContractRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Contract.Contract.ContractCaller.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 (_Contract *ContractRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Contract.Contract.ContractTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Contract *ContractRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Contract.Contract.ContractTransactor.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 (_Contract *ContractCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Contract.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 (_Contract *ContractTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Contract.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Contract *ContractTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Contract.Contract.contract.Transact(opts, method, params...) +} + +// Balance is a free data retrieval call binding the contract method 0xb69ef8a8. +// +// Solidity: function balance() view returns(uint256) +func (_Contract *ContractCaller) Balance(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "balance") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Balance is a free data retrieval call binding the contract method 0xb69ef8a8. +// +// Solidity: function balance() view returns(uint256) +func (_Contract *ContractSession) Balance() (*big.Int, error) { + return _Contract.Contract.Balance(&_Contract.CallOpts) +} + +// Balance is a free data retrieval call binding the contract method 0xb69ef8a8. +// +// Solidity: function balance() view returns(uint256) +func (_Contract *ContractCallerSession) Balance() (*big.Int, error) { + return _Contract.Contract.Balance(&_Contract.CallOpts) +} + +// GasLeft is a free data retrieval call binding the contract method 0x2ddb301b. +// +// Solidity: function gasLeft() view returns(uint256) +func (_Contract *ContractCaller) GasLeft(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "gasLeft") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GasLeft is a free data retrieval call binding the contract method 0x2ddb301b. +// +// Solidity: function gasLeft() view returns(uint256) +func (_Contract *ContractSession) GasLeft() (*big.Int, error) { + return _Contract.Contract.GasLeft(&_Contract.CallOpts) +} + +// GasLeft is a free data retrieval call binding the contract method 0x2ddb301b. +// +// Solidity: function gasLeft() view returns(uint256) +func (_Contract *ContractCallerSession) GasLeft() (*big.Int, error) { + return _Contract.Contract.GasLeft(&_Contract.CallOpts) +} + +// GasPrice is a free data retrieval call binding the contract method 0xfe173b97. +// +// Solidity: function gasPrice() view returns(uint256) +func (_Contract *ContractCaller) GasPrice(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "gasPrice") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GasPrice is a free data retrieval call binding the contract method 0xfe173b97. +// +// Solidity: function gasPrice() view returns(uint256) +func (_Contract *ContractSession) GasPrice() (*big.Int, error) { + return _Contract.Contract.GasPrice(&_Contract.CallOpts) +} + +// GasPrice is a free data retrieval call binding the contract method 0xfe173b97. +// +// Solidity: function gasPrice() view returns(uint256) +func (_Contract *ContractCallerSession) GasPrice() (*big.Int, error) { + return _Contract.Contract.GasPrice(&_Contract.CallOpts) +} + +// Nonce is a free data retrieval call binding the contract method 0xaffed0e0. +// +// Solidity: function nonce() view returns(uint256) +func (_Contract *ContractCaller) Nonce(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "nonce") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Nonce is a free data retrieval call binding the contract method 0xaffed0e0. +// +// Solidity: function nonce() view returns(uint256) +func (_Contract *ContractSession) Nonce() (*big.Int, error) { + return _Contract.Contract.Nonce(&_Contract.CallOpts) +} + +// Nonce is a free data retrieval call binding the contract method 0xaffed0e0. +// +// Solidity: function nonce() view returns(uint256) +func (_Contract *ContractCallerSession) Nonce() (*big.Int, error) { + return _Contract.Contract.Nonce(&_Contract.CallOpts) +} + +// Origin is a free data retrieval call binding the contract method 0x938b5f32. +// +// Solidity: function origin() view returns(address) +func (_Contract *ContractCaller) Origin(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "origin") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Origin is a free data retrieval call binding the contract method 0x938b5f32. +// +// Solidity: function origin() view returns(address) +func (_Contract *ContractSession) Origin() (common.Address, error) { + return _Contract.Contract.Origin(&_Contract.CallOpts) +} + +// Origin is a free data retrieval call binding the contract method 0x938b5f32. +// +// Solidity: function origin() view returns(address) +func (_Contract *ContractCallerSession) Origin() (common.Address, error) { + return _Contract.Contract.Origin(&_Contract.CallOpts) +} + +// Sender is a free data retrieval call binding the contract method 0x67e404ce. +// +// Solidity: function sender() view returns(address) +func (_Contract *ContractCaller) Sender(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "sender") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Sender is a free data retrieval call binding the contract method 0x67e404ce. +// +// Solidity: function sender() view returns(address) +func (_Contract *ContractSession) Sender() (common.Address, error) { + return _Contract.Contract.Sender(&_Contract.CallOpts) +} + +// Sender is a free data retrieval call binding the contract method 0x67e404ce. +// +// Solidity: function sender() view returns(address) +func (_Contract *ContractCallerSession) Sender() (common.Address, error) { + return _Contract.Contract.Sender(&_Contract.CallOpts) +} + +func (_Contract *Contract) CallSetOrigin(contract common.Address) []byte { + data, _ := sAbi.Pack("call", contract, "setOriginAfterPaygas()") + return data +} + +func (_Contract *Contract) CallSetSender(contract common.Address) []byte { + data, _ := sAbi.Pack("call", contract, "setSenderAfterPaygas()") + return data +} + +func (_Contract *Contract) SetGasLeftAfterPaygas() []byte { + data, _ := sAbi.Pack("gasLeftAfterPaygas") + return data +} + +func (_Contract *Contract) SetGasLeftBeforePaygas() []byte { + data, _ := sAbi.Pack("gasLeftBeforePaygas") + return data +} + +func (_Contract *Contract) SetGasPrice() []byte { + data, _ := sAbi.Pack("setGasPrice") + return data +} + +func (_Contract *Contract) SetBalanceAfterPaygas() []byte { + data, _ := sAbi.Pack("setBalanceAfterPaygas") + return data +} + +func (_Contract *Contract) SetBalanceBeforePaygas() []byte { + data, _ := sAbi.Pack("setBalanceBeforePaygas") + return data +} + +func (_Contract *Contract) SetNonce() []byte { + data, _ := sAbi.Pack("setNonce") + return data +} + +func (_Contract *Contract) SetNonceBeforePaygasAndRevert() []byte { + data, _ := sAbi.Pack("setNonceBeforePaygasAndRevert") + return data +} +func (_Contract *Contract) SetOriginAfterPaygas() []byte { + data, _ := sAbi.Pack("setOriginAfterPaygas") + return data +} + +func (_Contract *Contract) SetOriginBeforePaygas() []byte { + data, _ := sAbi.Pack("setOriginBeforePaygas") + return data +} + +func (_Contract *Contract) SetSenderAfterPaygas() []byte { + data, _ := sAbi.Pack("setSenderAfterPaygas") + return data +} + +func (_Contract *Contract) SetSenderBeforePaygas() []byte { + data, _ := sAbi.Pack("setSenderAfterPaygas") + return data +} + +func (_Contract *Contract) Reset() []byte { + data, _ := sAbi.Pack("reset") + return data +} diff --git a/gossip/contract/wallet/wallet.go b/gossip/contract/wallet/wallet.go new file mode 100644 index 000000000..abd69baa5 --- /dev/null +++ b/gossip/contract/wallet/wallet.go @@ -0,0 +1,215 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package wallet + +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 +) + +// WalletSignature is an auto generated low-level Go binding around an user-defined struct. +type WalletSignature struct { + V uint8 + R [32]byte + S [32]byte +} + +// ContractMetaData contains all meta data concerning the Contract contract. +var ContractMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"stateMutability\":\"payable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"structWallet.Signature\",\"name\":\"signature\",\"type\":\"tuple\"}],\"name\":\"changeOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"structWallet.Signature\",\"name\":\"signature\",\"type\":\"tuple\"}],\"name\":\"transfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60806040526040516109ea3803806109ea833981810160405281019061002591906100ce565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506100fb565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061009b82610070565b9050919050565b6100ab81610090565b81146100b657600080fd5b50565b6000815190506100c8816100a2565b92915050565b6000602082840312156100e4576100e361006b565b5b60006100f2848285016100b9565b91505092915050565b6108e08061010a6000396000f3fe3273ffffffffffffffffffffffffffffffffffffffff1460245736601f57005b600080fd5b608060405234801561003557600080fd5b50600436106100665760003560e01c806354a1a1671461006b5780638d5da5b6146100875780638da5cb5b146100a3575b600080fd5b610085600480360381019061008091906104ed565b6100c1565b005b6100a1600480360381019061009c91906105b3565b610217565b005b6100ab61032f565b6040516100b89190610602565b60405180910390f35b80600030496040516020016100d79291906106d6565b604051602081830303815290604052905060008180519060200120905060006101008285610353565b90508073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610190576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101879061075f565b60405180910390fd5b5c60008973ffffffffffffffffffffffffffffffffffffffff168989896040516101bb9291906107be565b60006040518083038185875af1925050503d80600081146101f8576040519150601f19603f3d011682016040523d82523d6000602084013e6101fd565b606091505b505090508061020b57600080fd5b50505050505050505050565b806000304960405160200161022d9291906106d6565b604051602081830303815290604052905060008180519060200120905060006102568285610353565b90508073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146102e6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102dd9061075f565b60405180910390fd5b5c856000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060018383600001602081019061036b9190610810565b84602001358560400135604051600081526020016040526040516103929493929190610865565b6020604051602081039080840390855afa1580156103b4573d6000803e3d6000fd5b50505060206040510351905092915050565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006103fb826103d0565b9050919050565b61040b816103f0565b811461041657600080fd5b50565b60008135905061042881610402565b92915050565b6000819050919050565b6104418161042e565b811461044c57600080fd5b50565b60008135905061045e81610438565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261048957610488610464565b5b8235905067ffffffffffffffff8111156104a6576104a5610469565b5b6020830191508360018202830111156104c2576104c161046e565b5b9250929050565b600080fd5b6000606082840312156104e4576104e36104c9565b5b81905092915050565b600080600080600060c08688031215610509576105086103c6565b5b600061051788828901610419565b95505060206105288882890161044f565b945050604086013567ffffffffffffffff811115610549576105486103cb565b5b61055588828901610473565b93509350506060610568888289016104ce565b9150509295509295909350565b6000610580826103d0565b9050919050565b61059081610575565b811461059b57600080fd5b50565b6000813590506105ad81610587565b92915050565b600080608083850312156105ca576105c96103c6565b5b60006105d88582860161059e565b92505060206105e9858286016104ce565b9150509250929050565b6105fc81610575565b82525050565b600060208201905061061760008301846105f3565b92915050565b6000819050919050565b600061064261063d610638846103d0565b61061d565b6103d0565b9050919050565b600061065482610627565b9050919050565b600061066682610649565b9050919050565b60008160601b9050919050565b60006106858261066d565b9050919050565b60006106978261067a565b9050919050565b6106af6106aa8261065b565b61068c565b82525050565b6000819050919050565b6106d06106cb8261042e565b6106b5565b82525050565b60006106e2828561069e565b6014820191506106f282846106bf565b6020820191508190509392505050565b600082825260208201905092915050565b7f4e6f74206f776e65720000000000000000000000000000000000000000000000600082015250565b6000610749600983610702565b915061075482610713565b602082019050919050565b600060208201905081810360008301526107788161073c565b9050919050565b600081905092915050565b82818337600083830152505050565b60006107a5838561077f565b93506107b283858461078a565b82840190509392505050565b60006107cb828486610799565b91508190509392505050565b600060ff82169050919050565b6107ed816107d7565b81146107f857600080fd5b50565b60008135905061080a816107e4565b92915050565b600060208284031215610826576108256103c6565b5b6000610834848285016107fb565b91505092915050565b6000819050919050565b6108508161083d565b82525050565b61085f816107d7565b82525050565b600060808201905061087a6000830187610847565b6108876020830186610856565b6108946040830185610847565b6108a16060830184610847565b9594505050505056fea2646970667358221220fc4d60b963fb07591e356b1c0ce07cc87c7f147f36a86b6b98c5fc7c0c80159e64736f6c63430008130033", +} + +var ( + sAbi, _ = abi.JSON(strings.NewReader(ContractMetaData.ABI)) +) + +// ContractABI is the input ABI used to generate the binding from. +// Deprecated: Use ContractMetaData.ABI instead. +var ContractABI = ContractMetaData.ABI + +// ContractBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use ContractMetaData.Bin instead. +var ContractBin = ContractMetaData.Bin + +// DeployContract deploys a new Ethereum contract, binding an instance of Contract to it. +func DeployContract(auth *bind.TransactOpts, backend bind.ContractBackend, _owner common.Address) (common.Address, *types.Transaction, *Contract, error) { + parsed, err := ContractMetaData.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(ContractBin), backend, _owner) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Contract{ContractCaller: ContractCaller{contract: contract}, ContractTransactor: ContractTransactor{contract: contract}, ContractFilterer: ContractFilterer{contract: contract}}, nil +} + +// Contract is an auto generated Go binding around an Ethereum contract. +type Contract struct { + ContractCaller // Read-only binding to the contract + ContractTransactor // Write-only binding to the contract + ContractFilterer // Log filterer for contract events +} + +// ContractCaller is an auto generated read-only Go binding around an Ethereum contract. +type ContractCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ContractTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ContractTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ContractFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ContractFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ContractSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ContractSession struct { + Contract *Contract // 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 +} + +// ContractCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ContractCallerSession struct { + Contract *ContractCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ContractTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ContractTransactorSession struct { + Contract *ContractTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ContractRaw is an auto generated low-level Go binding around an Ethereum contract. +type ContractRaw struct { + Contract *Contract // Generic contract binding to access the raw methods on +} + +// ContractCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ContractCallerRaw struct { + Contract *ContractCaller // Generic read-only contract binding to access the raw methods on +} + +// ContractTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ContractTransactorRaw struct { + Contract *ContractTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewContract creates a new instance of Contract, bound to a specific deployed contract. +func NewContract(address common.Address, backend bind.ContractBackend) (*Contract, error) { + contract, err := bindContract(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Contract{ContractCaller: ContractCaller{contract: contract}, ContractTransactor: ContractTransactor{contract: contract}, ContractFilterer: ContractFilterer{contract: contract}}, nil +} + +// NewContractCaller creates a new read-only instance of Contract, bound to a specific deployed contract. +func NewContractCaller(address common.Address, caller bind.ContractCaller) (*ContractCaller, error) { + contract, err := bindContract(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ContractCaller{contract: contract}, nil +} + +// NewContractTransactor creates a new write-only instance of Contract, bound to a specific deployed contract. +func NewContractTransactor(address common.Address, transactor bind.ContractTransactor) (*ContractTransactor, error) { + contract, err := bindContract(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ContractTransactor{contract: contract}, nil +} + +// NewContractFilterer creates a new log filterer instance of Contract, bound to a specific deployed contract. +func NewContractFilterer(address common.Address, filterer bind.ContractFilterer) (*ContractFilterer, error) { + contract, err := bindContract(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ContractFilterer{contract: contract}, nil +} + +// bindContract binds a generic wrapper to an already deployed contract. +func bindContract(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ContractABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Contract *ContractCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Contract.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 (_Contract *ContractSession) Owner() (common.Address, error) { + return _Contract.Contract.Owner(&_Contract.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Contract *ContractCallerSession) Owner() (common.Address, error) { + return _Contract.Contract.Owner(&_Contract.CallOpts) +} + +func (_Contract *Contract) Transfer(to common.Address, amount *big.Int, payload []byte, signature WalletSignature) []byte { + data, _ := sAbi.Pack("transfer", to, amount, payload, signature) + return data +} + +func (_Contract *Contract) ChangeOwner(newOwner common.Address, signature WalletSignature) []byte { + data, _ := sAbi.Pack("changeOwner", newOwner, signature) + return data +} diff --git a/gossip/contract/wallet/wallet.sol b/gossip/contract/wallet/wallet.sol new file mode 100644 index 000000000..ef4be7a3a --- /dev/null +++ b/gossip/contract/wallet/wallet.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.4.22; +pragma experimental AccountAbstraction; + +account contract Wallet { + address public owner; + + struct Signature { + uint8 v; + bytes32 r; + bytes32 s; + } + + constructor(address _owner) payable { + owner = _owner; + } + + modifier unlock(Signature calldata signature) { + bytes memory hash = abi.encodePacked( + this, + tx.nonce + ); + bytes32 digest = keccak256(hash); + address signer = recover(digest, signature); + require(owner == signer, "Not owner"); + paygas(); + _; + } + + function transfer( + address payable to, + uint256 amount, + bytes calldata payload, + Signature calldata signature + ) external unlock(signature) { + (bool success,) = to.call{value: amount}(payload); + require(success); + } + + function changeOwner( + address newOwner, + Signature calldata signature + ) external unlock(signature) { + owner = newOwner; + } + + function recover( + bytes32 digest, + Signature calldata signature + ) private pure returns (address) { + return ecrecover( + digest, + signature.v, + signature.r, + signature.s + ); + } +} diff --git a/inter/transaction_serializer.go b/inter/transaction_serializer.go index ad7a3cd84..653468342 100644 --- a/inter/transaction_serializer.go +++ b/inter/transaction_serializer.go @@ -25,7 +25,7 @@ func decodeSig(sig [64]byte) (r, s *big.Int) { } func TransactionMarshalCSER(w *cser.Writer, tx *types.Transaction) error { - if tx.Type() != types.LegacyTxType && tx.Type() != types.AccessListTxType && tx.Type() != types.DynamicFeeTxType { + if tx.Type() != types.LegacyTxType && tx.Type() != types.AccessListTxType && tx.Type() != types.DynamicFeeTxType && tx.Type() != types.AccountAbstractionTxType { return ErrUnknownTxType } if tx.Type() != types.LegacyTxType { diff --git a/utils/signers/internaltx/internaltx.go b/utils/signers/internaltx/internaltx.go index d65b2890f..b9c8c9db2 100644 --- a/utils/signers/internaltx/internaltx.go +++ b/utils/signers/internaltx/internaltx.go @@ -7,7 +7,7 @@ import ( func IsInternal(tx *types.Transaction) bool { v, r, _ := tx.RawSignatureValues() - return v.Sign() == 0 && r.Sign() == 0 + return tx.Type() != types.AccountAbstractionTxType && v.Sign() == 0 && r.Sign() == 0 } func InternalSender(tx *types.Transaction) common.Address {