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 blob verification function + hash validation for blob tx parsing #5

Merged
merged 1 commit into from
Jan 15, 2023
Merged
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
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ require (
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/herumi/bls-eth-go-binary v1.28.1 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/kilic/bls12-381 v0.1.1-0.20220929213557-ca162e8a70f4 // indirect
github.com/lispad/go-generics-tools v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
Expand All @@ -93,6 +95,7 @@ require (
github.com/pion/webrtc/v3 v3.1.42 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/protolambda/go-kzg v0.0.0-20221224134646-c91cee5e954e // indirect
github.com/protolambda/ztyp v0.2.2 // indirect
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 // indirect
github.com/valyala/fastrand v1.1.0 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/herumi/bls-eth-go-binary v1.28.1 h1:fcIZ48y5EE9973k05XjE8+P3YiQgjZz4JI/YabAm8KA=
github.com/herumi/bls-eth-go-binary v1.28.1/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/holiman/uint256 v1.2.1 h1:XRtyuda/zw2l+Bq/38n5XUoEF72aSOu/77Thd9pPp2o=
Expand All @@ -224,6 +226,8 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kilic/bls12-381 v0.1.1-0.20220929213557-ca162e8a70f4 h1:xWK4TZ4bRL05WQUU/3x6TG1l+IYAqdXpAeSLt/zZJc4=
github.com/kilic/bls12-381 v0.1.1-0.20220929213557-ca162e8a70f4/go.mod h1:tlkavyke+Ac7h8R3gZIjI5LKBcvMlSWnXNMgT3vZXo8=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
Expand Down Expand Up @@ -343,6 +347,8 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/protolambda/go-kzg v0.0.0-20221224134646-c91cee5e954e h1:Wed8Zc7HuSVNDJQy6ybac5/1fpRoyhMDcPvGlMjyIiY=
github.com/protolambda/go-kzg v0.0.0-20221224134646-c91cee5e954e/go.mod h1:7EhkBJFo/qJ9sToiW5baPqbyPo/TadVHn4iNdpwEW/w=
github.com/protolambda/ztyp v0.2.2 h1:rVcL3vBu9W/aV646zF6caLS/dyn9BN8NYiuJzicLNyY=
github.com/protolambda/ztyp v0.2.2/go.mod h1:9bYgKGqg3wJqT9ac1gI2hnVb0STQq7p/1lapqrqY1dU=
github.com/quasilyte/go-ruleguard/dsl v0.3.21 h1:vNkC6fC6qMLzCOGbnIHOd5ixUGgTbp3Z4fGnUgULlDA=
Expand Down Expand Up @@ -492,6 +498,7 @@ golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
55 changes: 54 additions & 1 deletion types/blob_txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ import (
"fmt"

"github.com/holiman/uint256"
"github.com/protolambda/go-kzg/eth"
"github.com/protolambda/ztyp/codec"
"github.com/protolambda/ztyp/view"
)

const (
FieldElementsPerBlob = 4096

BlobSize = FieldElementsPerBlob * 32 // blob size in bytes - each field element is 32 bytes
FieldElementSize = 32

BlobSize = FieldElementsPerBlob * FieldElementSize // blob size in bytes

ProofSize = 48 // kzg proof size

MaxBlobsPerBlock = 4
)

type BlobTxNetworkWrapper struct {
Expand Down Expand Up @@ -303,3 +308,51 @@ func (sig *ECDSASignature) Deserialize(dr *codec.DecodingReader) error {
copy(sig.S[:], data[33:len])
return nil
}

type blobSequence [][BlobSize]byte

func (s blobSequence) At(i int) eth.Blob { return blob(s[i]) }
func (s blobSequence) Len() int { return len(s) }

type blob [BlobSize]byte

func (s blob) At(i int) [FieldElementSize]byte {
r := [FieldElementSize]byte{}
if i*FieldElementSize+FieldElementSize > len(s) {
return r
}
copy(r[:], s[i*FieldElementSize:i*FieldElementSize+FieldElementSize])
return r
}
func (s blob) Len() int { return len(s) / FieldElementSize }

type kzgSequence [][ProofSize]byte

func (s kzgSequence) At(i int) eth.KZGCommitment { return eth.KZGCommitment(s[i]) }
func (s kzgSequence) Len() int { return len(s) }

func VerifyBlobs(blobKZGs [][ProofSize]byte, blobs [][BlobSize]byte, kzgAggregatedProof [ProofSize]byte, blobVersionedHashes [][32]byte) error {
l1 := len(blobKZGs)
l2 := len(blobs)
l3 := len(blobVersionedHashes)
if l1 != l2 || l2 != l3 {
return fmt.Errorf("lengths don't match %v %v %v", l1, l2, l3)
}
if l1 > MaxBlobsPerBlock {
return fmt.Errorf("number of blobs exceeds max: %v", l1)
}
ok, err := eth.VerifyAggregateKZGProof(blobSequence(blobs), kzgSequence(blobKZGs), eth.KZGProof(kzgAggregatedProof))
if err != nil {
return fmt.Errorf("error during proof verification: %v", err)
}
if !ok {
return fmt.Errorf("failed to verify kzg")
}
for i, h := range blobVersionedHashes {
computed := eth.KZGToVersionedHash(eth.KZGCommitment(blobKZGs[i]))
if computed != h {
return fmt.Errorf("versioned hash %d supposedly %s but does not match computed %s", i, h, computed)
}
}
return nil
}
21 changes: 21 additions & 0 deletions types/blob_txn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/protolambda/ztyp/codec"
"github.com/stretchr/testify/require"

"github.com/ledgerwatch/erigon-lib/common"
)
Expand Down Expand Up @@ -138,4 +139,24 @@ func TestParseBlobTxNetworkWrapper(t *testing.T) {
if l1 != 2 || l2 != 2 || l3 != 2 {
t.Errorf("Expected 2 each of kzgs / blobs / hashes, got: %v %v %v", l1, l2, l3)
}

// Test blob verification
err = VerifyBlobs(tx.BlobKZGs, tx.Blobs, tx.KZGAggregatedProof, tx.Tx.Message.BlobVersionedHashes)
require.NoError(t, err)

// Mangle one byte in a blob and make sure verification fails
oldByte := tx.Blobs[0][10]
tx.Blobs[0][10] = 0
err = VerifyBlobs(tx.BlobKZGs, tx.Blobs, tx.KZGAggregatedProof, tx.Tx.Message.BlobVersionedHashes)
if err == nil {
t.Fatal("Expected error when verifying invalid blob data, got none")
}
tx.Blobs[0][10] = oldByte

// Mangle one byte in the proof and make sure verification failse
tx.KZGAggregatedProof[10] = 0
err = VerifyBlobs(tx.BlobKZGs, tx.Blobs, tx.KZGAggregatedProof, tx.Tx.Message.BlobVersionedHashes)
if err == nil {
t.Fatal("Expected error when verifying invalid aggregated proof, got none")
}
}
9 changes: 7 additions & 2 deletions types/txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func (ctx *TxParseContext) ParseTransaction(payload []byte, pos int, slot *TxSlo
// since it is SSZ format. We assume the scope ends at the last byte of the payload
// argument; this currently seems to always be the case, but does not seem to be
// mandated by this function's contract.
return len(payload), ctx.ParseBlobTransaction(payload[p:], slot, sender)
return len(payload), ctx.ParseBlobTransaction(payload[p:], slot, sender, validateHash)
}
if _, err = ctx.Keccak1.Write(payload[p : p+1]); err != nil {
return 0, fmt.Errorf("%w: computing IdHash (hashing type Prefix): %s", ErrParseTxn, err)
Expand Down Expand Up @@ -480,10 +480,15 @@ func (ctx *TxParseContext) ParseTransaction(payload []byte, pos int, slot *TxSlo
return p, nil
}

func (ctx *TxParseContext) ParseBlobTransaction(payload []byte, slot *TxSlot, sender []byte) error {
func (ctx *TxParseContext) ParseBlobTransaction(payload []byte, slot *TxSlot, sender []byte, validateHash func([]byte) error) error {
slot.Rlp = payload // includes type byte
ctx.Keccak1.Write(payload)
ctx.Keccak1.(io.Reader).Read(slot.IDHash[:32])
if validateHash != nil {
if err := validateHash(slot.IDHash[:32]); err != nil {
return err
}
}

payload = payload[1:]
// payload should now include the SSZ encoded tx and nothing more
Expand Down
12 changes: 7 additions & 5 deletions types/txn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,19 @@ func TestParseBlobTransaction(t *testing.T) {
ctx := NewTxParseContext(*new(uint256.Int).SetUint64(1))
tx, txSender := &TxSlot{}, [20]byte{}

// Prepare the payload, which is an RLP encoded string consisting of the blob tx type byte
// followed by the ssz encoded transaction data.
ssz := common.MustDecodeHex(strings.TrimSpace(blobTxNetworkWrapperHex))
stringLen := rlp.StringLen(len(ssz) + 1)
payload := make([]byte, stringLen)
rlp.EncodeString(append([]byte{byte(BlobTxType)}, ssz...), payload)
payload := append([]byte{byte(BlobTxType)}, ssz...)
parseEnd, err := ctx.ParseTransaction(payload, 0, tx, txSender[:], false /* hasEnvelope */, nil)

require.NoError(err)
require.Equal(len(payload), parseEnd)

// Same test only with an rlp tx envelope.
stringLen := rlp.StringLen(len(ssz) + 1)
payload = make([]byte, stringLen)
rlp.EncodeString(append([]byte{byte(BlobTxType)}, ssz...), payload)
parseEnd, err = ctx.ParseTransaction(payload, 0, tx, txSender[:], true /* hasEnvelope */, nil)

// TODO: test that tx fields are set properly. Need a better input transaction as this example
// has zeros for all fields other than the blobs.
}
Expand Down