Preserve set-code auth list in external signer

This commit is contained in:
二狗同学 2026-07-06 11:26:09 +08:00
parent e3b6d0c86f
commit 47622451dc
4 changed files with 197 additions and 7 deletions

View file

@ -198,6 +198,19 @@ type signTransactionResult struct {
// by the external signer. For non-legacy transactions, the chain ID of the
// transaction overrides the chainID parameter.
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
args, err := signTxArgs(account, tx, chainID)
if err != nil {
return nil, err
}
var res signTransactionResult
if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
return nil, err
}
return res.Tx, nil
}
func signTxArgs(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*apitypes.SendTxArgs, error) {
data := hexutil.Bytes(tx.Data())
var to *common.MixedcaseAddress
if tx.To() != nil {
@ -215,9 +228,13 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
switch tx.Type() {
case types.LegacyTxType, types.AccessListTxType:
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
case types.DynamicFeeTxType, types.BlobTxType, types.SetCodeTxType:
case types.DynamicFeeTxType, types.BlobTxType:
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
case types.SetCodeTxType:
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
args.AuthorizationList = tx.SetCodeAuthorizations()
default:
return nil, fmt.Errorf("unsupported tx type %d", tx.Type())
}
@ -246,12 +263,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
args.Commitments = sidecar.Commitments
args.Proofs = sidecar.Proofs
}
var res signTransactionResult
if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
return nil, err
}
return res.Tx, nil
return args, nil
}
func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {

81
accounts/external/backend_test.go vendored Normal file
View file

@ -0,0 +1,81 @@
// Copyright 2026 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package external
import (
"bytes"
"encoding/json"
"math/big"
"reflect"
"testing"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/holiman/uint256"
)
func TestSignTxArgsSetCodeAuthorizationList(t *testing.T) {
account := accounts.Account{Address: common.HexToAddress("0x1000000000000000000000000000000000000001")}
to := common.HexToAddress("0x2000000000000000000000000000000000000002")
auth := types.SetCodeAuthorization{
ChainID: *uint256.NewInt(5),
Address: common.HexToAddress("0x3000000000000000000000000000000000000003"),
Nonce: 9,
V: 1,
R: *uint256.NewInt(10),
S: *uint256.NewInt(11),
}
tx := types.NewTx(&types.SetCodeTx{
ChainID: uint256.NewInt(5),
Nonce: 4,
GasTipCap: uint256.NewInt(3),
GasFeeCap: uint256.NewInt(30),
Gas: 21000,
To: to,
Value: uint256.NewInt(7),
Data: []byte{0x01, 0x02},
AccessList: types.AccessList{{
Address: common.HexToAddress("0x4000000000000000000000000000000000000004"),
StorageKeys: []common.Hash{common.HexToHash("0x01")},
}},
AuthList: []types.SetCodeAuthorization{auth},
})
args, err := signTxArgs(account, tx, big.NewInt(1))
if err != nil {
t.Fatal(err)
}
if args.ChainID == nil || (*big.Int)(args.ChainID).Cmp(big.NewInt(5)) != 0 {
t.Fatalf("have chain id %v, want 5", args.ChainID)
}
if args.MaxFeePerGas == nil || (*big.Int)(args.MaxFeePerGas).Cmp(big.NewInt(30)) != 0 {
t.Fatalf("have max fee %v, want 30", args.MaxFeePerGas)
}
if args.MaxPriorityFeePerGas == nil || (*big.Int)(args.MaxPriorityFeePerGas).Cmp(big.NewInt(3)) != 0 {
t.Fatalf("have max priority fee %v, want 3", args.MaxPriorityFeePerGas)
}
if !reflect.DeepEqual(args.AuthorizationList, []types.SetCodeAuthorization{auth}) {
t.Fatalf("have auth list %#v, want %#v", args.AuthorizationList, []types.SetCodeAuthorization{auth})
}
blob, err := json.Marshal(args)
if err != nil {
t.Fatal(err)
}
if !bytes.Contains(blob, []byte(`"authorizationList"`)) {
t.Fatalf("marshaled args missing authorizationList: %s", blob)
}
}

View file

@ -112,6 +112,9 @@ type SendTxArgs struct {
Blobs []kzg4844.Blob `json:"blobs,omitempty"`
Commitments []kzg4844.Commitment `json:"commitments,omitempty"`
Proofs []kzg4844.Proof `json:"proofs,omitempty"`
// For SetCodeTxType
AuthorizationList []types.SetCodeAuthorization `json:"authorizationList,omitempty"`
}
func (args SendTxArgs) String() string {
@ -146,6 +149,27 @@ func (args *SendTxArgs) ToTransaction() (*types.Transaction, error) {
}
var data types.TxData
switch {
case args.AuthorizationList != nil:
al := types.AccessList{}
if args.AccessList != nil {
al = *args.AccessList
}
if to == nil {
return nil, errors.New("transaction recipient must be set for set-code transactions")
}
data = &types.SetCodeTx{
To: *to,
ChainID: uint256.MustFromBig((*big.Int)(args.ChainID)),
Nonce: uint64(args.Nonce),
Gas: uint64(args.Gas),
GasFeeCap: uint256.MustFromBig((*big.Int)(args.MaxFeePerGas)),
GasTipCap: uint256.MustFromBig((*big.Int)(args.MaxPriorityFeePerGas)),
Value: uint256.MustFromBig((*big.Int)(&args.Value)),
Data: args.data(),
AccessList: al,
AuthList: args.AuthorizationList,
}
case args.BlobHashes != nil:
al := types.AccessList{}
if args.AccessList != nil {

View file

@ -17,12 +17,16 @@
package apitypes
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"encoding/json"
"math/big"
"reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/holiman/uint256"
@ -99,6 +103,75 @@ func TestTxArgs(t *testing.T) {
}
}
func TestSetCodeTxArgs(t *testing.T) {
to := common.HexToAddress("0x1000000000000000000000000000000000000001")
from := common.NewMixedcaseAddress(common.HexToAddress("0x9000000000000000000000000000000000000009"))
mixedTo := common.NewMixedcaseAddress(to)
input := hexutil.Bytes{0x01, 0x02}
chainID := hexutil.Big(*big.NewInt(5))
maxFee := hexutil.Big(*big.NewInt(30))
maxTip := hexutil.Big(*big.NewInt(3))
value := hexutil.Big(*big.NewInt(7))
accessList := types.AccessList{{
Address: common.HexToAddress("0x2000000000000000000000000000000000000002"),
StorageKeys: []common.Hash{common.HexToHash("0x01")},
}}
auth := types.SetCodeAuthorization{
ChainID: *uint256.NewInt(5),
Address: common.HexToAddress("0x3000000000000000000000000000000000000003"),
Nonce: 9,
V: 1,
R: *uint256.NewInt(10),
S: *uint256.NewInt(11),
}
args := SendTxArgs{
From: from,
To: &mixedTo,
Gas: 21000,
MaxFeePerGas: &maxFee,
MaxPriorityFeePerGas: &maxTip,
Value: value,
Nonce: 4,
Input: &input,
AccessList: &accessList,
ChainID: &chainID,
AuthorizationList: []types.SetCodeAuthorization{auth},
}
blob, err := json.Marshal(args)
if err != nil {
t.Fatal(err)
}
if !bytes.Contains(blob, []byte(`"authorizationList"`)) {
t.Fatalf("marshaled args missing authorizationList: %s", blob)
}
var decoded SendTxArgs
if err := json.Unmarshal(blob, &decoded); err != nil {
t.Fatal(err)
}
tx, err := decoded.ToTransaction()
if err != nil {
t.Fatal(err)
}
if have, want := tx.Type(), uint8(types.SetCodeTxType); have != want {
t.Fatalf("have tx type %d, want %d", have, want)
}
if have := tx.To(); have == nil || *have != to {
t.Fatalf("have to %v, want %v", have, to)
}
if have, want := tx.ChainId(), big.NewInt(5); have.Cmp(want) != 0 {
t.Fatalf("have chain id %v, want %v", have, want)
}
if !bytes.Equal(tx.Data(), input) {
t.Fatalf("have input %x, want %x", tx.Data(), input)
}
if !reflect.DeepEqual(tx.AccessList(), accessList) {
t.Fatalf("have access list %#v, want %#v", tx.AccessList(), accessList)
}
if !reflect.DeepEqual(tx.SetCodeAuthorizations(), []types.SetCodeAuthorization{auth}) {
t.Fatalf("have auth list %#v, want %#v", tx.SetCodeAuthorizations(), []types.SetCodeAuthorization{auth})
}
}
func TestBlobTxs(t *testing.T) {
blob := kzg4844.Blob{0x1}
commitment, err := kzg4844.BlobToCommitment(&blob)