Skip to content

Commit f23e666

Browse files
Merge pull request #327 from OffchainLabs/cherry-pick-sendTxArgs
Cherry pick ethereum/go-ethereum#28976
2 parents b14a09a + 0d841e4 commit f23e666

File tree

11 files changed

+302
-28
lines changed

11 files changed

+302
-28
lines changed

accounts/external/backend.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
205205
to = &t
206206
}
207207
args := &apitypes.SendTxArgs{
208-
Data: &data,
208+
Input: &data,
209209
Nonce: hexutil.Uint64(tx.Nonce()),
210210
Value: hexutil.Big(*tx.Value()),
211211
Gas: hexutil.Uint64(tx.Gas()),
@@ -215,7 +215,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
215215
switch tx.Type() {
216216
case types.LegacyTxType, types.AccessListTxType:
217217
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
218-
case types.DynamicFeeTxType:
218+
case types.DynamicFeeTxType, types.BlobTxType:
219219
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
220220
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
221221
default:
@@ -235,6 +235,17 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
235235
accessList := tx.AccessList()
236236
args.AccessList = &accessList
237237
}
238+
if tx.Type() == types.BlobTxType {
239+
args.BlobHashes = tx.BlobHashes()
240+
sidecar := tx.BlobTxSidecar()
241+
if sidecar == nil {
242+
return nil, fmt.Errorf("blobs must be present for signing")
243+
}
244+
args.Blobs = sidecar.Blobs
245+
args.Commitments = sidecar.Commitments
246+
args.Proofs = sidecar.Proofs
247+
}
248+
238249
var res signTransactionResult
239250
if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
240251
return nil, err

core/types/transaction.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,26 @@ func (tx *Transaction) WithoutBlobTxSidecar() *Transaction {
485485
return cpy
486486
}
487487

488+
// WithBlobTxSidecar returns a copy of tx with the blob sidecar added.
489+
func (tx *Transaction) WithBlobTxSidecar(sideCar *BlobTxSidecar) *Transaction {
490+
blobtx, ok := tx.inner.(*BlobTx)
491+
if !ok {
492+
return tx
493+
}
494+
cpy := &Transaction{
495+
inner: blobtx.withSidecar(sideCar),
496+
time: tx.time,
497+
}
498+
// Note: tx.size cache not carried over because the sidecar is included in size!
499+
if h := tx.hash.Load(); h != nil {
500+
cpy.hash.Store(h)
501+
}
502+
if f := tx.from.Load(); f != nil {
503+
cpy.from.Store(f)
504+
}
505+
return cpy
506+
}
507+
488508
// SetTime sets the decoding time of a transaction. This is used by tests to set
489509
// arbitrary times and by persistent transaction pools when loading old txs from
490510
// disk.

core/types/tx_blob.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,12 @@ func (tx *BlobTx) withoutSidecar() *BlobTx {
192192
return &cpy
193193
}
194194

195+
func (tx *BlobTx) withSidecar(sideCar *BlobTxSidecar) *BlobTx {
196+
cpy := *tx
197+
cpy.Sidecar = sideCar
198+
return &cpy
199+
}
200+
195201
func (tx *BlobTx) encode(b *bytes.Buffer) error {
196202
if tx.Sidecar == nil {
197203
return rlp.Encode(b, tx)

internal/ethapi/api.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2110,15 +2110,14 @@ type SignTransactionResult struct {
21102110
// The node needs to have the private key of the account corresponding with
21112111
// the given from address and it needs to be unlocked.
21122112
func (s *TransactionAPI) SignTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
2113+
args.blobSidecarAllowed = true
2114+
21132115
if args.Gas == nil {
21142116
return nil, errors.New("gas not specified")
21152117
}
21162118
if args.GasPrice == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) {
21172119
return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
21182120
}
2119-
if args.IsEIP4844() {
2120-
return nil, errBlobTxNotSupported
2121-
}
21222121
if args.Nonce == nil {
21232122
return nil, errors.New("nonce not specified")
21242123
}
@@ -2134,6 +2133,16 @@ func (s *TransactionAPI) SignTransaction(ctx context.Context, args TransactionAr
21342133
if err != nil {
21352134
return nil, err
21362135
}
2136+
// If the transaction-to-sign was a blob transaction, then the signed one
2137+
// no longer retains the blobs, only the blob hashes. In this step, we need
2138+
// to put back the blob(s).
2139+
if args.IsEIP4844() {
2140+
signed = signed.WithBlobTxSidecar(&types.BlobTxSidecar{
2141+
Blobs: args.Blobs,
2142+
Commitments: args.Commitments,
2143+
Proofs: args.Proofs,
2144+
})
2145+
}
21372146
data, err := signed.MarshalBinary()
21382147
if err != nil {
21392148
return nil, err

internal/ethapi/api_test.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,11 +1049,8 @@ func TestSignBlobTransaction(t *testing.T) {
10491049
}
10501050

10511051
_, err = api.SignTransaction(context.Background(), argsFromTransaction(res.Tx, b.acc.Address))
1052-
if err == nil {
1053-
t.Fatalf("should fail on blob transaction")
1054-
}
1055-
if !errors.Is(err, errBlobTxNotSupported) {
1056-
t.Errorf("error mismatch. Have: %v, want: %v", err, errBlobTxNotSupported)
1052+
if err != nil {
1053+
t.Fatalf("should not fail on blob transaction")
10571054
}
10581055
}
10591056

internal/ethapi/transaction_args.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func (args *TransactionArgs) data() []byte {
9999

100100
// setDefaults fills in default values for unspecified tx fields.
101101
func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
102-
if err := args.setBlobTxSidecar(ctx, b); err != nil {
102+
if err := args.setBlobTxSidecar(ctx); err != nil {
103103
return err
104104
}
105105
if err := args.setFeeDefaults(ctx, b); err != nil {
@@ -285,7 +285,7 @@ func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *typ
285285
}
286286

287287
// setBlobTxSidecar adds the blob tx
288-
func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, b Backend) error {
288+
func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context) error {
289289
// No blobs, we're done.
290290
if args.Blobs == nil {
291291
return nil

signer/core/api.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,10 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args apitypes.SendTxA
590590
return nil, err
591591
}
592592
// Convert fields into a real transaction
593-
var unsignedTx = result.Transaction.ToTransaction()
593+
unsignedTx, err := result.Transaction.ToTransaction()
594+
if err != nil {
595+
return nil, err
596+
}
594597
// Get the password for the transaction
595598
pw, err := api.lookupOrQueryPassword(acc.Address, "Account password",
596599
fmt.Sprintf("Please enter the password for account %s", acc.Address.String()))

signer/core/apitypes/types.go

Lines changed: 128 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package apitypes
1818

1919
import (
2020
"bytes"
21+
"crypto/sha256"
2122
"encoding/json"
2223
"errors"
2324
"fmt"
@@ -34,6 +35,8 @@ import (
3435
"github.com/ethereum/go-ethereum/common/math"
3536
"github.com/ethereum/go-ethereum/core/types"
3637
"github.com/ethereum/go-ethereum/crypto"
38+
"github.com/ethereum/go-ethereum/crypto/kzg4844"
39+
"github.com/holiman/uint256"
3740
)
3841

3942
var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Za-z](\w*)(\[\])?$`)
@@ -92,12 +95,21 @@ type SendTxArgs struct {
9295
// We accept "data" and "input" for backwards-compatibility reasons.
9396
// "input" is the newer name and should be preferred by clients.
9497
// Issue detail: https://github.com/ethereum/go-ethereum/issues/15628
95-
Data *hexutil.Bytes `json:"data"`
98+
Data *hexutil.Bytes `json:"data,omitempty"`
9699
Input *hexutil.Bytes `json:"input,omitempty"`
97100

98101
// For non-legacy transactions
99102
AccessList *types.AccessList `json:"accessList,omitempty"`
100103
ChainID *hexutil.Big `json:"chainId,omitempty"`
104+
105+
// For BlobTxType
106+
BlobFeeCap *hexutil.Big `json:"maxFeePerBlobGas,omitempty"`
107+
BlobHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
108+
109+
// For BlobTxType transactions with blob sidecar
110+
Blobs []kzg4844.Blob `json:"blobs,omitempty"`
111+
Commitments []kzg4844.Commitment `json:"commitments,omitempty"`
112+
Proofs []kzg4844.Proof `json:"proofs,omitempty"`
101113
}
102114

103115
func (args SendTxArgs) String() string {
@@ -108,24 +120,56 @@ func (args SendTxArgs) String() string {
108120
return err.Error()
109121
}
110122

123+
// data retrieves the transaction calldata. Input field is preferred.
124+
func (args *SendTxArgs) data() []byte {
125+
if args.Input != nil {
126+
return *args.Input
127+
}
128+
if args.Data != nil {
129+
return *args.Data
130+
}
131+
return nil
132+
}
133+
111134
// ToTransaction converts the arguments to a transaction.
112-
func (args *SendTxArgs) ToTransaction() *types.Transaction {
135+
func (args *SendTxArgs) ToTransaction() (*types.Transaction, error) {
113136
// Add the To-field, if specified
114137
var to *common.Address
115138
if args.To != nil {
116139
dstAddr := args.To.Address()
117140
to = &dstAddr
118141
}
119-
120-
var input []byte
121-
if args.Input != nil {
122-
input = *args.Input
123-
} else if args.Data != nil {
124-
input = *args.Data
142+
if err := args.validateTxSidecar(); err != nil {
143+
return nil, err
125144
}
126-
127145
var data types.TxData
128146
switch {
147+
case args.BlobHashes != nil:
148+
al := types.AccessList{}
149+
if args.AccessList != nil {
150+
al = *args.AccessList
151+
}
152+
data = &types.BlobTx{
153+
To: *to,
154+
ChainID: uint256.MustFromBig((*big.Int)(args.ChainID)),
155+
Nonce: uint64(args.Nonce),
156+
Gas: uint64(args.Gas),
157+
GasFeeCap: uint256.MustFromBig((*big.Int)(args.MaxFeePerGas)),
158+
GasTipCap: uint256.MustFromBig((*big.Int)(args.MaxPriorityFeePerGas)),
159+
Value: uint256.MustFromBig((*big.Int)(&args.Value)),
160+
Data: args.data(),
161+
AccessList: al,
162+
BlobHashes: args.BlobHashes,
163+
BlobFeeCap: uint256.MustFromBig((*big.Int)(args.BlobFeeCap)),
164+
}
165+
if args.Blobs != nil {
166+
data.(*types.BlobTx).Sidecar = &types.BlobTxSidecar{
167+
Blobs: args.Blobs,
168+
Commitments: args.Commitments,
169+
Proofs: args.Proofs,
170+
}
171+
}
172+
129173
case args.MaxFeePerGas != nil:
130174
al := types.AccessList{}
131175
if args.AccessList != nil {
@@ -139,7 +183,7 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction {
139183
GasFeeCap: (*big.Int)(args.MaxFeePerGas),
140184
GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas),
141185
Value: (*big.Int)(&args.Value),
142-
Data: input,
186+
Data: args.data(),
143187
AccessList: al,
144188
}
145189
case args.AccessList != nil:
@@ -150,7 +194,7 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction {
150194
Gas: uint64(args.Gas),
151195
GasPrice: (*big.Int)(args.GasPrice),
152196
Value: (*big.Int)(&args.Value),
153-
Data: input,
197+
Data: args.data(),
154198
AccessList: *args.AccessList,
155199
}
156200
default:
@@ -160,10 +204,81 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction {
160204
Gas: uint64(args.Gas),
161205
GasPrice: (*big.Int)(args.GasPrice),
162206
Value: (*big.Int)(&args.Value),
163-
Data: input,
207+
Data: args.data(),
164208
}
165209
}
166-
return types.NewTx(data)
210+
211+
return types.NewTx(data), nil
212+
}
213+
214+
// validateTxSidecar validates blob data, if present
215+
func (args *SendTxArgs) validateTxSidecar() error {
216+
// No blobs, we're done.
217+
if args.Blobs == nil {
218+
return nil
219+
}
220+
221+
n := len(args.Blobs)
222+
// Assume user provides either only blobs (w/o hashes), or
223+
// blobs together with commitments and proofs.
224+
if args.Commitments == nil && args.Proofs != nil {
225+
return errors.New(`blob proofs provided while commitments were not`)
226+
} else if args.Commitments != nil && args.Proofs == nil {
227+
return errors.New(`blob commitments provided while proofs were not`)
228+
}
229+
230+
// len(blobs) == len(commitments) == len(proofs) == len(hashes)
231+
if args.Commitments != nil && len(args.Commitments) != n {
232+
return fmt.Errorf("number of blobs and commitments mismatch (have=%d, want=%d)", len(args.Commitments), n)
233+
}
234+
if args.Proofs != nil && len(args.Proofs) != n {
235+
return fmt.Errorf("number of blobs and proofs mismatch (have=%d, want=%d)", len(args.Proofs), n)
236+
}
237+
if args.BlobHashes != nil && len(args.BlobHashes) != n {
238+
return fmt.Errorf("number of blobs and hashes mismatch (have=%d, want=%d)", len(args.BlobHashes), n)
239+
}
240+
241+
if args.Commitments == nil {
242+
// Generate commitment and proof.
243+
commitments := make([]kzg4844.Commitment, n)
244+
proofs := make([]kzg4844.Proof, n)
245+
for i, b := range args.Blobs {
246+
c, err := kzg4844.BlobToCommitment(b)
247+
if err != nil {
248+
return fmt.Errorf("blobs[%d]: error computing commitment: %v", i, err)
249+
}
250+
commitments[i] = c
251+
p, err := kzg4844.ComputeBlobProof(b, c)
252+
if err != nil {
253+
return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err)
254+
}
255+
proofs[i] = p
256+
}
257+
args.Commitments = commitments
258+
args.Proofs = proofs
259+
} else {
260+
for i, b := range args.Blobs {
261+
if err := kzg4844.VerifyBlobProof(b, args.Commitments[i], args.Proofs[i]); err != nil {
262+
return fmt.Errorf("failed to verify blob proof: %v", err)
263+
}
264+
}
265+
}
266+
267+
hashes := make([]common.Hash, n)
268+
hasher := sha256.New()
269+
for i, c := range args.Commitments {
270+
hashes[i] = kzg4844.CalcBlobHashV1(hasher, &c)
271+
}
272+
if args.BlobHashes != nil {
273+
for i, h := range hashes {
274+
if h != args.BlobHashes[i] {
275+
return fmt.Errorf("blob hash verification failed (have=%s, want=%s)", args.BlobHashes[i], h)
276+
}
277+
}
278+
} else {
279+
args.BlobHashes = hashes
280+
}
281+
return nil
167282
}
168283

169284
type SigFormat struct {

0 commit comments

Comments
 (0)