Skip to content

Commit ac5aa67

Browse files
authored
internal/ethapi: add support for blobs in eth_fillTransaction (#28839)
This change adds support for blob-transaction in certain API-endpoints, e.g. eth_fillTransaction. A follow-up PR will add support for signing such transactions.
1 parent 2732fb1 commit ac5aa67

File tree

5 files changed

+366
-13
lines changed

5 files changed

+366
-13
lines changed

core/types/transaction_marshalling.go

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"github.com/ethereum/go-ethereum/common"
2525
"github.com/ethereum/go-ethereum/common/hexutil"
26+
"github.com/ethereum/go-ethereum/crypto/kzg4844"
2627
"github.com/holiman/uint256"
2728
)
2829

@@ -47,6 +48,11 @@ type txJSON struct {
4748
S *hexutil.Big `json:"s"`
4849
YParity *hexutil.Uint64 `json:"yParity,omitempty"`
4950

51+
// Blob transaction sidecar encoding:
52+
Blobs []kzg4844.Blob `json:"blobs,omitempty"`
53+
Commitments []kzg4844.Commitment `json:"commitments,omitempty"`
54+
Proofs []kzg4844.Proof `json:"proofs,omitempty"`
55+
5056
// Only used for encoding:
5157
Hash common.Hash `json:"hash"`
5258
}
@@ -142,6 +148,11 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
142148
enc.S = (*hexutil.Big)(itx.S.ToBig())
143149
yparity := itx.V.Uint64()
144150
enc.YParity = (*hexutil.Uint64)(&yparity)
151+
if sidecar := itx.Sidecar; sidecar != nil {
152+
enc.Blobs = itx.Sidecar.Blobs
153+
enc.Commitments = itx.Sidecar.Commitments
154+
enc.Proofs = itx.Sidecar.Proofs
155+
}
145156
}
146157
return json.Marshal(&enc)
147158
}

crypto/kzg4844/kzg4844.go

+39
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,60 @@ import (
2121
"embed"
2222
"errors"
2323
"hash"
24+
"reflect"
2425
"sync/atomic"
26+
27+
"github.com/ethereum/go-ethereum/common/hexutil"
2528
)
2629

2730
//go:embed trusted_setup.json
2831
var content embed.FS
2932

33+
var (
34+
blobT = reflect.TypeOf(Blob{})
35+
commitmentT = reflect.TypeOf(Commitment{})
36+
proofT = reflect.TypeOf(Proof{})
37+
)
38+
3039
// Blob represents a 4844 data blob.
3140
type Blob [131072]byte
3241

42+
// UnmarshalJSON parses a blob in hex syntax.
43+
func (b *Blob) UnmarshalJSON(input []byte) error {
44+
return hexutil.UnmarshalFixedJSON(blobT, input, b[:])
45+
}
46+
47+
// MarshalText returns the hex representation of b.
48+
func (b Blob) MarshalText() ([]byte, error) {
49+
return hexutil.Bytes(b[:]).MarshalText()
50+
}
51+
3352
// Commitment is a serialized commitment to a polynomial.
3453
type Commitment [48]byte
3554

55+
// UnmarshalJSON parses a commitment in hex syntax.
56+
func (c *Commitment) UnmarshalJSON(input []byte) error {
57+
return hexutil.UnmarshalFixedJSON(commitmentT, input, c[:])
58+
}
59+
60+
// MarshalText returns the hex representation of c.
61+
func (c Commitment) MarshalText() ([]byte, error) {
62+
return hexutil.Bytes(c[:]).MarshalText()
63+
}
64+
3665
// Proof is a serialized commitment to the quotient polynomial.
3766
type Proof [48]byte
3867

68+
// UnmarshalJSON parses a proof in hex syntax.
69+
func (p *Proof) UnmarshalJSON(input []byte) error {
70+
return hexutil.UnmarshalFixedJSON(proofT, input, p[:])
71+
}
72+
73+
// MarshalText returns the hex representation of p.
74+
func (p Proof) MarshalText() ([]byte, error) {
75+
return hexutil.Bytes(p[:]).MarshalText()
76+
}
77+
3978
// Point is a BLS field element.
4079
type Point [32]byte
4180

internal/ethapi/api.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -1812,13 +1812,14 @@ func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionAr
18121812
// on a given unsigned transaction, and returns it to the caller for further
18131813
// processing (signing + broadcast).
18141814
func (s *TransactionAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
1815+
args.blobSidecarAllowed = true
1816+
18151817
// Set some sanity defaults and terminate on failure
18161818
if err := args.setDefaults(ctx, s.b); err != nil {
18171819
return nil, err
18181820
}
18191821
// Assemble the transaction and obtain rlp
18201822
tx := args.toTransaction()
1821-
// TODO(s1na): fill in blob proofs, commitments
18221823
data, err := tx.MarshalBinary()
18231824
if err != nil {
18241825
return nil, err

internal/ethapi/api_test.go

+191
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"bytes"
2121
"context"
2222
"crypto/ecdsa"
23+
"crypto/sha256"
2324
"encoding/json"
2425
"errors"
2526
"fmt"
@@ -45,6 +46,7 @@ import (
4546
"github.com/ethereum/go-ethereum/core/types"
4647
"github.com/ethereum/go-ethereum/core/vm"
4748
"github.com/ethereum/go-ethereum/crypto"
49+
"github.com/ethereum/go-ethereum/crypto/kzg4844"
4850
"github.com/ethereum/go-ethereum/ethdb"
4951
"github.com/ethereum/go-ethereum/event"
5052
"github.com/ethereum/go-ethereum/internal/blocktest"
@@ -1079,6 +1081,195 @@ func TestSendBlobTransaction(t *testing.T) {
10791081
}
10801082
}
10811083

1084+
func TestFillBlobTransaction(t *testing.T) {
1085+
t.Parallel()
1086+
// Initialize test accounts
1087+
var (
1088+
key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
1089+
to = crypto.PubkeyToAddress(key.PublicKey)
1090+
genesis = &core.Genesis{
1091+
Config: params.MergedTestChainConfig,
1092+
Alloc: core.GenesisAlloc{},
1093+
}
1094+
emptyBlob = kzg4844.Blob{}
1095+
emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
1096+
emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
1097+
emptyBlobHash common.Hash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit)
1098+
)
1099+
b := newTestBackend(t, 1, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
1100+
b.SetPoS()
1101+
})
1102+
api := NewTransactionAPI(b, nil)
1103+
type result struct {
1104+
Hashes []common.Hash
1105+
Sidecar *types.BlobTxSidecar
1106+
}
1107+
suite := []struct {
1108+
name string
1109+
args TransactionArgs
1110+
err string
1111+
want *result
1112+
}{
1113+
{
1114+
name: "TestInvalidParamsCombination1",
1115+
args: TransactionArgs{
1116+
From: &b.acc.Address,
1117+
To: &to,
1118+
Value: (*hexutil.Big)(big.NewInt(1)),
1119+
Blobs: []kzg4844.Blob{{}},
1120+
Proofs: []kzg4844.Proof{{}},
1121+
},
1122+
err: `blob proofs provided while commitments were not`,
1123+
},
1124+
{
1125+
name: "TestInvalidParamsCombination2",
1126+
args: TransactionArgs{
1127+
From: &b.acc.Address,
1128+
To: &to,
1129+
Value: (*hexutil.Big)(big.NewInt(1)),
1130+
Blobs: []kzg4844.Blob{{}},
1131+
Commitments: []kzg4844.Commitment{{}},
1132+
},
1133+
err: `blob commitments provided while proofs were not`,
1134+
},
1135+
{
1136+
name: "TestInvalidParamsCount1",
1137+
args: TransactionArgs{
1138+
From: &b.acc.Address,
1139+
To: &to,
1140+
Value: (*hexutil.Big)(big.NewInt(1)),
1141+
Blobs: []kzg4844.Blob{{}},
1142+
Commitments: []kzg4844.Commitment{{}, {}},
1143+
Proofs: []kzg4844.Proof{{}, {}},
1144+
},
1145+
err: `number of blobs and commitments mismatch (have=2, want=1)`,
1146+
},
1147+
{
1148+
name: "TestInvalidParamsCount2",
1149+
args: TransactionArgs{
1150+
From: &b.acc.Address,
1151+
To: &to,
1152+
Value: (*hexutil.Big)(big.NewInt(1)),
1153+
Blobs: []kzg4844.Blob{{}, {}},
1154+
Commitments: []kzg4844.Commitment{{}, {}},
1155+
Proofs: []kzg4844.Proof{{}},
1156+
},
1157+
err: `number of blobs and proofs mismatch (have=1, want=2)`,
1158+
},
1159+
{
1160+
name: "TestInvalidProofVerification",
1161+
args: TransactionArgs{
1162+
From: &b.acc.Address,
1163+
To: &to,
1164+
Value: (*hexutil.Big)(big.NewInt(1)),
1165+
Blobs: []kzg4844.Blob{{}, {}},
1166+
Commitments: []kzg4844.Commitment{{}, {}},
1167+
Proofs: []kzg4844.Proof{{}, {}},
1168+
},
1169+
err: `failed to verify blob proof: short buffer`,
1170+
},
1171+
{
1172+
name: "TestGenerateBlobHashes",
1173+
args: TransactionArgs{
1174+
From: &b.acc.Address,
1175+
To: &to,
1176+
Value: (*hexutil.Big)(big.NewInt(1)),
1177+
Blobs: []kzg4844.Blob{emptyBlob},
1178+
Commitments: []kzg4844.Commitment{emptyBlobCommit},
1179+
Proofs: []kzg4844.Proof{emptyBlobProof},
1180+
},
1181+
want: &result{
1182+
Hashes: []common.Hash{emptyBlobHash},
1183+
Sidecar: &types.BlobTxSidecar{
1184+
Blobs: []kzg4844.Blob{emptyBlob},
1185+
Commitments: []kzg4844.Commitment{emptyBlobCommit},
1186+
Proofs: []kzg4844.Proof{emptyBlobProof},
1187+
},
1188+
},
1189+
},
1190+
{
1191+
name: "TestValidBlobHashes",
1192+
args: TransactionArgs{
1193+
From: &b.acc.Address,
1194+
To: &to,
1195+
Value: (*hexutil.Big)(big.NewInt(1)),
1196+
BlobHashes: []common.Hash{emptyBlobHash},
1197+
Blobs: []kzg4844.Blob{emptyBlob},
1198+
Commitments: []kzg4844.Commitment{emptyBlobCommit},
1199+
Proofs: []kzg4844.Proof{emptyBlobProof},
1200+
},
1201+
want: &result{
1202+
Hashes: []common.Hash{emptyBlobHash},
1203+
Sidecar: &types.BlobTxSidecar{
1204+
Blobs: []kzg4844.Blob{emptyBlob},
1205+
Commitments: []kzg4844.Commitment{emptyBlobCommit},
1206+
Proofs: []kzg4844.Proof{emptyBlobProof},
1207+
},
1208+
},
1209+
},
1210+
{
1211+
name: "TestInvalidBlobHashes",
1212+
args: TransactionArgs{
1213+
From: &b.acc.Address,
1214+
To: &to,
1215+
Value: (*hexutil.Big)(big.NewInt(1)),
1216+
BlobHashes: []common.Hash{{0x01, 0x22}},
1217+
Blobs: []kzg4844.Blob{emptyBlob},
1218+
Commitments: []kzg4844.Commitment{emptyBlobCommit},
1219+
Proofs: []kzg4844.Proof{emptyBlobProof},
1220+
},
1221+
err: fmt.Sprintf("blob hash verification failed (have=%s, want=%s)", common.Hash{0x01, 0x22}, emptyBlobHash),
1222+
},
1223+
{
1224+
name: "TestGenerateBlobProofs",
1225+
args: TransactionArgs{
1226+
From: &b.acc.Address,
1227+
To: &to,
1228+
Value: (*hexutil.Big)(big.NewInt(1)),
1229+
Blobs: []kzg4844.Blob{emptyBlob},
1230+
},
1231+
want: &result{
1232+
Hashes: []common.Hash{emptyBlobHash},
1233+
Sidecar: &types.BlobTxSidecar{
1234+
Blobs: []kzg4844.Blob{emptyBlob},
1235+
Commitments: []kzg4844.Commitment{emptyBlobCommit},
1236+
Proofs: []kzg4844.Proof{emptyBlobProof},
1237+
},
1238+
},
1239+
},
1240+
}
1241+
for _, tc := range suite {
1242+
t.Run(tc.name, func(t *testing.T) {
1243+
res, err := api.FillTransaction(context.Background(), tc.args)
1244+
if len(tc.err) > 0 {
1245+
if err == nil {
1246+
t.Fatalf("missing error. want: %s", tc.err)
1247+
} else if err != nil && err.Error() != tc.err {
1248+
t.Fatalf("error mismatch. want: %s, have: %s", tc.err, err.Error())
1249+
}
1250+
return
1251+
}
1252+
if err != nil && len(tc.err) == 0 {
1253+
t.Fatalf("expected no error. have: %s", err)
1254+
}
1255+
if res == nil {
1256+
t.Fatal("result missing")
1257+
}
1258+
want, err := json.Marshal(tc.want)
1259+
if err != nil {
1260+
t.Fatalf("failed to encode expected: %v", err)
1261+
}
1262+
have, err := json.Marshal(result{Hashes: res.Tx.BlobHashes(), Sidecar: res.Tx.BlobTxSidecar()})
1263+
if err != nil {
1264+
t.Fatalf("failed to encode computed sidecar: %v", err)
1265+
}
1266+
if !bytes.Equal(have, want) {
1267+
t.Errorf("blob sidecar mismatch. Have: %s, want: %s", have, want)
1268+
}
1269+
})
1270+
}
1271+
}
1272+
10821273
func argsFromTransaction(tx *types.Transaction, from common.Address) TransactionArgs {
10831274
var (
10841275
gas = tx.Gas()

0 commit comments

Comments
 (0)