Skip to content

Commit

Permalink
add blob verification function, hash validation for blob tx parsing, …
Browse files Browse the repository at this point in the history
…and test tweaks
  • Loading branch information
roberto-bayardo committed Jan 15, 2023
1 parent d6f23fb commit b5711e2
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 8 deletions.
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

0 comments on commit b5711e2

Please sign in to comment.