Files
scroll/rollup/internal/controller/sender/transaction_signer.go
2024-11-11 16:04:02 +07:00

157 lines
5.0 KiB
Go

package sender
import (
"context"
"fmt"
"math/big"
"github.com/scroll-tech/go-ethereum/accounts/abi/bind"
"github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/common/hexutil"
gethTypes "github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/crypto"
"github.com/scroll-tech/go-ethereum/log"
"github.com/scroll-tech/go-ethereum/rpc"
"scroll-tech/rollup/internal/config"
)
const (
// PrivateKeySignerType is the type of signer that uses a private key to sign transactions
PrivateKeySignerType = "PrivateKey"
// RemoteSignerType is the type of signer that uses a remote signer to sign transactions
RemoteSignerType = "RemoteSigner"
)
// TransactionSigner signs given transactions
type TransactionSigner struct {
config *config.SignerConfig
auth *bind.TransactOpts
rpcClient *rpc.Client
nonce uint64
addr common.Address
}
func NewTransactionSigner(config *config.SignerConfig, chainID *big.Int) (*TransactionSigner, error) {
switch config.SignerType {
case PrivateKeySignerType:
privKey, err := crypto.ToECDSA(common.FromHex(config.PrivateKeySignerConfig.PrivateKey))
if err != nil {
return nil, fmt.Errorf("parse sender private key failed: %w", err)
}
auth, err := bind.NewKeyedTransactorWithChainID(privKey, chainID)
if err != nil {
return nil, fmt.Errorf("failed to create transactor with chain ID %v, err: %w", chainID, err)
}
return &TransactionSigner{
config: config,
auth: auth,
addr: crypto.PubkeyToAddress(privKey.PublicKey),
}, nil
case RemoteSignerType:
if config.RemoteSignerConfig.SignerAddress == "" {
return nil, fmt.Errorf("failed to create RemoteSigner, signer address is empty")
}
rpcClient, err := rpc.Dial(config.RemoteSignerConfig.RemoteSignerUrl)
if err != nil {
return nil, fmt.Errorf("failed to dial rpc client, err: %w", err)
}
return &TransactionSigner{
config: config,
rpcClient: rpcClient,
addr: common.HexToAddress(config.RemoteSignerConfig.SignerAddress),
}, nil
default:
return nil, fmt.Errorf("failed to create new transaction signer, unknown type: %v", config.SignerType)
}
}
func (ts *TransactionSigner) SignTransaction(ctx context.Context, tx *gethTypes.Transaction) (*gethTypes.Transaction, error) {
switch ts.config.SignerType {
case PrivateKeySignerType:
signedTx, err := ts.auth.Signer(ts.addr, tx)
if err != nil {
log.Info("failed to sign tx", "address", ts.addr.String(), "err", err)
return nil, err
}
return signedTx, nil
case RemoteSignerType:
rpcTx, err := txDataToRpcTx(&ts.addr, tx)
if err != nil {
return nil, fmt.Errorf("failed to convert txData to rpc transaction, err: %w", err)
}
var result hexutil.Bytes
err = ts.rpcClient.CallContext(ctx, &result, "eth_signTransaction", rpcTx)
if err != nil {
log.Info("failed to call remote rpc", "err", err)
return nil, err
}
signedTx := new(gethTypes.Transaction)
if err := signedTx.UnmarshalBinary(result); err != nil {
return nil, err
}
return signedTx, nil
default:
// this shouldn't happen, because SignerType is checked during creation
return nil, fmt.Errorf("shouldn't happen, unknown signer type")
}
}
func (ts *TransactionSigner) SetNonce(nonce uint64) {
ts.nonce = nonce
}
func (ts *TransactionSigner) GetNonce() uint64 {
return ts.nonce
}
func (ts *TransactionSigner) GetAddr() common.Address {
return ts.addr
}
func (ts *TransactionSigner) GetType() string {
return ts.config.SignerType
}
// RpcTransaction transaction that will be send through rpc to web3Signer
type RpcTransaction struct {
From *common.Address `json:"from"`
To *common.Address `json:"to"`
Gas uint64 `json:"gas"`
GasPrice *big.Int `json:"gasPrice,omitempty"`
MaxPriorityFeePerGas *big.Int `json:"maxPriorityFeePerGas,omitempty"`
MaxFeePerGas *big.Int `json:"maxFeePerGas,omitempty"`
Nonce uint64 `json:"nonce"`
Value *big.Int `json:"value"`
Data string `json:"data"`
}
func txDataToRpcTx(from *common.Address, tx *gethTypes.Transaction) (*RpcTransaction, error) {
switch tx.Type() {
case gethTypes.LegacyTxType:
return &RpcTransaction{
From: from,
To: tx.To(),
Gas: tx.Gas(),
GasPrice: tx.GasPrice(),
Nonce: tx.Nonce(),
Value: tx.Value(),
Data: common.Bytes2Hex(tx.Data()),
}, nil
case gethTypes.DynamicFeeTxType:
return &RpcTransaction{
From: from,
To: tx.To(),
Gas: tx.Gas(),
MaxPriorityFeePerGas: tx.GasTipCap(),
MaxFeePerGas: tx.GasFeeCap(),
Nonce: tx.Nonce(),
Value: tx.Value(),
Data: common.Bytes2Hex(tx.Data()),
}, nil
default: // other tx types (BlobTx) currently not supported by web3signer
return nil, fmt.Errorf("failed to convert tx to RpcTransaction, unsupported tx type, %d", tx.Type())
}
}