mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-14 16:37:56 -05:00
157 lines
5.0 KiB
Go
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())
|
|
}
|
|
}
|