Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add changes from geth's SendTxArg to Nitro's SignTxArgs #2348

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions arbnode/dataposter/data_poster.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ func externalSigner(ctx context.Context, opts *ExternalSignerCfg) (signerFn, com
var data hexutil.Bytes
args, err := externalsigner.TxToSignTxArgs(addr, tx)
if err != nil {
return nil, fmt.Errorf("error converting transaction to sendTxArgs: %w", err)
return nil, fmt.Errorf("error converting transaction to signTxArgs: %w", err)
}
if err := client.CallContext(ctx, &data, opts.Method, args); err != nil {
return nil, fmt.Errorf("making signing request to external signer: %w", err)
Expand All @@ -285,7 +285,11 @@ func externalSigner(ctx context.Context, opts *ExternalSignerCfg) (signerFn, com
return nil, fmt.Errorf("unmarshaling signed transaction: %w", err)
}
hasher := types.LatestSignerForChainID(tx.ChainId())
if h := hasher.Hash(args.ToTransaction()); h != hasher.Hash(signedTx) {
want, err := args.ToTransaction()
if err != nil {
return nil, fmt.Errorf("converting signTxArgs to transaction: %w", err)
}
if h := hasher.Hash(want); h != hasher.Hash(signedTx) {
return nil, fmt.Errorf("transaction: %x from external signer differs from request: %x", hasher.Hash(signedTx), h)
}
return signedTx, nil
Expand Down
73 changes: 52 additions & 21 deletions arbnode/dataposter/dataposter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -73,8 +74,27 @@ func signerTestCfg(addr common.Address) (*ExternalSignerCfg, error) {
}, nil
}

var (
blobTx = types.NewTx(
func blobCommitment(t *testing.T, b kzg4844.Blob) kzg4844.Commitment {
ret, err := kzg4844.BlobToCommitment(b)
if err != nil {
t.Fatalf("BlobToCommitment() unexpected error: %v", err)
}
return ret
}

func blobProof(t *testing.T, b kzg4844.Blob, c kzg4844.Commitment) kzg4844.Proof {
ret, err := kzg4844.ComputeBlobProof(b, c)
if err != nil {
t.Fatalf("BlobToCommitment() unexpected error: %v", err)
}
return ret
}

func blobTx(t *testing.T) *types.Transaction {

blobs := []kzg4844.Blob{{0, 1}, {0: 3}}
c0, c1 := blobCommitment(t, blobs[0]), blobCommitment(t, blobs[1])
return types.NewTx(
&types.BlobTx{
ChainID: uint256.NewInt(1337),
Nonce: 13,
Expand All @@ -85,25 +105,32 @@ var (
Value: uint256.NewInt(1),
Data: []byte{0x01, 0x02, 0x03},
BlobHashes: []common.Hash{
common.BigToHash(big.NewInt(1)),
common.BigToHash(big.NewInt(2)),
common.BigToHash(big.NewInt(3)),
common.HexToHash("0x018884c82d215fb1d789c9282c557f9890dac382b02eb33943ab7ff7203cf633"),
common.HexToHash("0x013510e1c7368ac800fbd7c2c63369ac853348deece23e17dd1d918ac4ff65e0"),
},
Sidecar: &types.BlobTxSidecar{
Blobs: blobs,
Commitments: []kzg4844.Commitment{c0, c1},
Proofs: []kzg4844.Proof{
blobProof(t, blobs[0], c0),
blobProof(t, blobs[1], c1),
},
},
Sidecar: &types.BlobTxSidecar{},
},
)
dynamicFeeTx = types.NewTx(
&types.DynamicFeeTx{
Nonce: 13,
GasTipCap: big.NewInt(1),
GasFeeCap: big.NewInt(1),
Gas: 3,
To: nil,
Value: big.NewInt(1),
Data: []byte{0x01, 0x02, 0x03},
},
)
)
}

func dynamicFeeTx() *types.Transaction {
return types.NewTx(&types.DynamicFeeTx{
Nonce: 13,
GasTipCap: big.NewInt(1),
GasFeeCap: big.NewInt(1),
Gas: 3,
To: nil,
Value: big.NewInt(1),
Data: []byte{0x01, 0x02, 0x03},
})
}

func TestExternalSigner(t *testing.T) {
httpSrv, srv := externalsignertest.NewServer(t)
Expand All @@ -130,11 +157,11 @@ func TestExternalSigner(t *testing.T) {
}{
{
desc: "blob transaction",
tx: blobTx,
tx: blobTx(t),
},
{
desc: "dynamic fee transaction",
tx: dynamicFeeTx,
tx: dynamicFeeTx(),
},
} {
t.Run(tc.desc, func(t *testing.T) {
Expand All @@ -147,7 +174,11 @@ func TestExternalSigner(t *testing.T) {
if err != nil {
t.Fatalf("Error converting transaction to sendTxArgs: %v", err)
}
want, err := srv.SignerFn(addr, args.ToTransaction())
tx, err := args.ToTransaction()
if err != nil {
t.Fatalf("ToTransaction() unexpected error: %v", err)
}
want, err := srv.SignerFn(addr, tx)
if err != nil {
t.Fatalf("Error signing transaction: %v", err)
}
Expand Down
200 changes: 162 additions & 38 deletions arbnode/dataposter/externalsigner/externalsigner.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package externalsigner

import (
"crypto/sha256"
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -26,46 +29,167 @@ type SignTxArgs struct {
Proofs []kzg4844.Proof `json:"proofs"`
}

func (a *SignTxArgs) ToTransaction() *types.Transaction {
if !a.isEIP4844() {
return a.SendTxArgs.ToTransaction()
}
to := common.Address{}
if a.To != nil {
to = a.To.Address()
}
var input []byte
if a.Input != nil {
input = *a.Input
} else if a.Data != nil {
input = *a.Data
}
al := types.AccessList{}
if a.AccessList != nil {
al = *a.AccessList
}
return types.NewTx(&types.BlobTx{
To: to,
Nonce: uint64(a.SendTxArgs.Nonce),
Gas: uint64(a.Gas),
GasFeeCap: uint256.NewInt(a.MaxFeePerGas.ToInt().Uint64()),
GasTipCap: uint256.NewInt(a.MaxPriorityFeePerGas.ToInt().Uint64()),
Value: uint256.NewInt(a.Value.ToInt().Uint64()),
Data: input,
AccessList: al,
BlobFeeCap: uint256.NewInt(a.BlobFeeCap.ToInt().Uint64()),
BlobHashes: a.BlobHashes,
Sidecar: &types.BlobTxSidecar{
Blobs: a.Blobs,
Commitments: a.Commitments,
Proofs: a.Proofs,
},
ChainID: uint256.NewInt(a.ChainID.ToInt().Uint64()),
})
// data retrieves the transaction calldata. Input field is preferred.
func (args *SignTxArgs) data() []byte {
if args.Input != nil {
return *args.Input
}
if args.Data != nil {
return *args.Data
}
return nil
}

func (a *SignTxArgs) isEIP4844() bool {
return a.BlobHashes != nil || a.BlobFeeCap != nil
// ToTransaction converts the arguments to a transaction.
func (args *SignTxArgs) ToTransaction() (*types.Transaction, error) {
// Add the To-field, if specified
var to *common.Address
if args.To != nil {
dstAddr := args.To.Address()
to = &dstAddr
}
if err := args.validateTxSidecar(); err != nil {
return nil, err
}
var data types.TxData
switch {
case args.BlobHashes != nil:
al := types.AccessList{}
if args.AccessList != nil {
al = *args.AccessList
}
data = &types.BlobTx{
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,
BlobHashes: args.BlobHashes,
BlobFeeCap: uint256.MustFromBig((*big.Int)(args.BlobFeeCap)),
}
if args.Blobs != nil {
data.(*types.BlobTx).Sidecar = &types.BlobTxSidecar{
Blobs: args.Blobs,
Commitments: args.Commitments,
Proofs: args.Proofs,
}
}

case args.MaxFeePerGas != nil:
al := types.AccessList{}
if args.AccessList != nil {
al = *args.AccessList
}
data = &types.DynamicFeeTx{
To: to,
ChainID: (*big.Int)(args.ChainID),
Nonce: uint64(args.Nonce),
Gas: uint64(args.Gas),
GasFeeCap: (*big.Int)(args.MaxFeePerGas),
GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas),
Value: (*big.Int)(&args.Value),
Data: args.data(),
AccessList: al,
}
case args.AccessList != nil:
data = &types.AccessListTx{
To: to,
ChainID: (*big.Int)(args.ChainID),
Nonce: uint64(args.Nonce),
Gas: uint64(args.Gas),
GasPrice: (*big.Int)(args.GasPrice),
Value: (*big.Int)(&args.Value),
Data: args.data(),
AccessList: *args.AccessList,
}
default:
data = &types.LegacyTx{
To: to,
Nonce: uint64(args.Nonce),
Gas: uint64(args.Gas),
GasPrice: (*big.Int)(args.GasPrice),
Value: (*big.Int)(&args.Value),
Data: args.data(),
}
}

return types.NewTx(data), nil
}

// validateTxSidecar validates blob data, if present
func (args *SignTxArgs) validateTxSidecar() error {
// No blobs, we're done.
if args.Blobs == nil {
return nil
}

n := len(args.Blobs)
// Assume user provides either only blobs (w/o hashes), or
// blobs together with commitments and proofs.
if args.Commitments == nil && args.Proofs != nil {
return errors.New(`blob proofs provided while commitments were not`)
} else if args.Commitments != nil && args.Proofs == nil {
return errors.New(`blob commitments provided while proofs were not`)
}

// len(blobs) == len(commitments) == len(proofs) == len(hashes)
if args.Commitments != nil && len(args.Commitments) != n {
return fmt.Errorf("number of blobs and commitments mismatch (have=%d, want=%d)", len(args.Commitments), n)
}
if args.Proofs != nil && len(args.Proofs) != n {
return fmt.Errorf("number of blobs and proofs mismatch (have=%d, want=%d)", len(args.Proofs), n)
}
if args.BlobHashes != nil && len(args.BlobHashes) != n {
return fmt.Errorf("number of blobs and hashes mismatch (have=%d, want=%d)", len(args.BlobHashes), n)
}

if args.Commitments == nil {
// Generate commitment and proof.
commitments := make([]kzg4844.Commitment, n)
proofs := make([]kzg4844.Proof, n)
for i, b := range args.Blobs {
c, err := kzg4844.BlobToCommitment(b)
if err != nil {
return fmt.Errorf("blobs[%d]: error computing commitment: %w", i, err)
}
commitments[i] = c
p, err := kzg4844.ComputeBlobProof(b, c)
if err != nil {
return fmt.Errorf("blobs[%d]: error computing proof: %w", i, err)
}
proofs[i] = p
}
args.Commitments = commitments
args.Proofs = proofs
} else {
for i, b := range args.Blobs {
b := b // avoid memeroy aliasing
if err := kzg4844.VerifyBlobProof(b, args.Commitments[i], args.Proofs[i]); err != nil {
return fmt.Errorf("failed to verify blob proof: %w", err)
}
}
}

hashes := make([]common.Hash, n)
hasher := sha256.New()
for i, c := range args.Commitments {
c := c // avoid memeroy aliasing
hashes[i] = kzg4844.CalcBlobHashV1(hasher, &c)
}
if args.BlobHashes != nil {
for i, h := range hashes {
if h != args.BlobHashes[i] {
return fmt.Errorf("blob hash verification failed (have=%s, want=%s)", args.BlobHashes[i], h)
}
}
} else {
args.BlobHashes = hashes
}
return nil
}

// TxToSignTxArgs converts transaction to SendTxArgs. This is needed for
Expand Down
5 changes: 4 additions & 1 deletion arbnode/dataposter/externalsigner/externalsigner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ func TestToTranssaction(t *testing.T) {
if err != nil {
t.Fatalf("TxToSignTxArgs() unexpected error: %v", err)
}
got := signTxArgs.ToTransaction()
got, err := signTxArgs.ToTransaction()
if err != nil {
t.Fatalf("ToTransaction() unexpected error: %v", err)
}
hasher := types.LatestSignerForChainID(nil)
if h, g := hasher.Hash(tc.tx), hasher.Hash(got); h != g {
t.Errorf("ToTransaction() got hash: %v want: %v", g, h)
Expand Down
6 changes: 5 additions & 1 deletion arbnode/dataposter/externalsignertest/externalsignertest.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,11 @@ func (a *SignerAPI) SignTransaction(ctx context.Context, req *externalsigner.Sig
if req == nil {
return nil, fmt.Errorf("nil request")
}
signedTx, err := a.SignerFn(a.Address, req.ToTransaction())
tx, err := req.ToTransaction()
if err != nil {
return nil, fmt.Errorf("converting transaction arguments into transaction: %w", err)
}
signedTx, err := a.SignerFn(a.Address, tx)
if err != nil {
return nil, fmt.Errorf("signing transaction: %w", err)
}
Expand Down
Loading