diff --git a/README.md b/README.md index 42ac502..a0bfa92 100644 --- a/README.md +++ b/README.md @@ -80,4 +80,4 @@ The callback can then use the slot number to determine whether to throw an error - `POST` `/mock/invalid/payload/{type}//{slot/epoch number}`: Enables specified [type](#payload-invalidation-types) of modification to payload built starting at the slot or epoch specified # Statistics -- `GET` `/mock/stats/validation_errors`: Returns a JSON containing all the errors encountered when validating the submitted signed blinded responses from the consensus client (e.g. Invalid signature on submitted blinded block, invalid signature on submitted blinded blob sidecar) \ No newline at end of file +- `GET` `/mock/stats/validation_errors`: Returns a JSON containing all the errors encountered when validating the submitted signed blinded responses from the consensus client (e.g. Invalid signature on submitted blinded block) \ No newline at end of file diff --git a/go.mod b/go.mod index 758d193..251462a 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/ethereum/go-ethereum v1.13.1 github.com/gorilla/mux v1.8.0 - github.com/marioevz/eth-clients v0.0.0-20230925172743-e379ee1ecd6e + github.com/marioevz/eth-clients v0.0.0-20230925201418-d5fbddd99b76 github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7 github.com/protolambda/eth2api v0.0.0-20230316214135-5f8afbd6d05d github.com/protolambda/zrnt v0.30.0 @@ -79,4 +79,6 @@ require ( replace github.com/protolambda/eth2api => github.com/marioevz/eth2api v0.0.0-20230922201437-72bd1301e033 -replace github.com/protolambda/zrnt => github.com/marioevz/zrnt v0.26.2-0.20230922170744-1bd341bc7f0f +replace github.com/protolambda/zrnt => github.com/marioevz/zrnt v0.26.2-0.20231109183115-d2098ec1f42c + +replace github.com/protolambda/ztyp => github.com/marioevz/ztyp v0.0.0-20231106221254-dd6f24f13fd9 diff --git a/go.sum b/go.sum index 0734f1c..be249a1 100644 --- a/go.sum +++ b/go.sum @@ -207,12 +207,14 @@ github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awS github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/marioevz/eth-clients v0.0.0-20230925172743-e379ee1ecd6e h1:uBr6Gn5jBHnyoS34HV4GOiYjuua6J/F2FST0T6MO89E= -github.com/marioevz/eth-clients v0.0.0-20230925172743-e379ee1ecd6e/go.mod h1:YVrdn57Q3rAzm3wus4T9tg1vMPca4csVbzorPhkGCW0= +github.com/marioevz/eth-clients v0.0.0-20230925201418-d5fbddd99b76 h1:fj+aeR3YLmSRUWjOGgTxNMdvzm72J5JbKGKosNO/jkE= +github.com/marioevz/eth-clients v0.0.0-20230925201418-d5fbddd99b76/go.mod h1:YVrdn57Q3rAzm3wus4T9tg1vMPca4csVbzorPhkGCW0= github.com/marioevz/eth2api v0.0.0-20230922201437-72bd1301e033 h1:sn57n+lbJrLS8FKYs08W7TEzraTGOCQGrSC4hni6rYw= github.com/marioevz/eth2api v0.0.0-20230922201437-72bd1301e033/go.mod h1:hcwWCT4sF1X7KsMZ535MvDZVk5M20Uyj+x2LARZjQsM= -github.com/marioevz/zrnt v0.26.2-0.20230922170744-1bd341bc7f0f h1:ysmQCP5asLCdFHib4ykgHVUSzCy9UEysElhMwJPmcg4= -github.com/marioevz/zrnt v0.26.2-0.20230922170744-1bd341bc7f0f/go.mod h1:MzAcHPo2QQIx+IEPdxXMiqKtYRUlszz7RA/fhAuOm1I= +github.com/marioevz/zrnt v0.26.2-0.20231109183115-d2098ec1f42c h1:ZwKLkGVKnAq1JZql6SBrQici0la1X7APcbBLwmrSsd4= +github.com/marioevz/zrnt v0.26.2-0.20231109183115-d2098ec1f42c/go.mod h1:ZctXHBa/2rlF85iao8oqQ8264DbEBBGWr6lwIOW2yv4= +github.com/marioevz/ztyp v0.0.0-20231106221254-dd6f24f13fd9 h1:e40k7kQLw2jyDg4+Mc+TVSSfm1TO51NxPZo9T7nVCzM= +github.com/marioevz/ztyp v0.0.0-20231106221254-dd6f24f13fd9/go.mod h1:9bYgKGqg3wJqT9ac1gI2hnVb0STQq7p/1lapqrqY1dU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -280,8 +282,6 @@ github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJf github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7 h1:cZC+usqsYgHtlBaGulVnZ1hfKAi8iWtujBnRLQE698c= github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7/go.mod h1:IToEjHuttnUzwZI5KBSM/LOOW3qLbbrHOEfp3SbECGY= -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/rauljordan/engine-proxy v0.0.0-20230316220057-4c80c36c4c3a h1:ZIfMLprHVdo2vs3WcSqSDEyz2ZsSzDhGeOyxh8VQThA= github.com/rauljordan/engine-proxy v0.0.0-20230316220057-4c80c36c4c3a/go.mod h1:9OVXfWYnIV+wj1/SqfdREmE5mzN/OANAgdOJRtFtvpo= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= diff --git a/mock/mock_builder.go b/mock/mock_builder.go index 55ea084..aa854c4 100644 --- a/mock/mock_builder.go +++ b/mock/mock_builder.go @@ -1171,7 +1171,7 @@ func (m *MockBuilder) HandleSubmitBlindedBlock( signedBeaconResponse common.SignedBeaconResponse ) if m.cfg.spec.SlotToEpoch(slot) >= m.cfg.spec.DENEB_FORK_EPOCH { - signedBeaconResponse = &deneb.SignedBlindedBlockContents{} + signedBeaconResponse = &deneb.SignedBeaconResponse{} } else if m.cfg.spec.SlotToEpoch(slot) >= m.cfg.spec.CAPELLA_FORK_EPOCH { signedBeaconResponse = &capella.SignedBeaconResponse{} } else if m.cfg.spec.SlotToEpoch(slot) >= m.cfg.spec.BELLATRIX_FORK_EPOCH { diff --git a/types/deneb/deneb.go b/types/deneb/deneb.go index 315ccf9..859273e 100644 --- a/types/deneb/deneb.go +++ b/types/deneb/deneb.go @@ -1,6 +1,7 @@ package deneb import ( + "bytes" "fmt" "math/big" @@ -16,36 +17,32 @@ import ( const Version = "deneb" -type SignedBlindedBlockContents struct { - // We use the unblinded version of the - SignedBlindedBeaconBlock deneb.SignedBlindedBeaconBlock `json:"signed_blinded_block" yaml:"signed_blinded_block"` - SignedBlindedBlobSidecars []deneb.SignedBlindedBlobSidecar `json:"signed_blinded_blob_sidecars" yaml:"signed_blinded_blob_sidecars"` -} +type SignedBeaconResponse deneb.SignedBlindedBeaconBlock -func (s *SignedBlindedBlockContents) ExecutionPayloadHash() el_common.Hash { +func (s *SignedBeaconResponse) ExecutionPayloadHash() el_common.Hash { var hash el_common.Hash - copy(hash[:], s.SignedBlindedBeaconBlock.Message.Body.ExecutionPayloadHeader.BlockHash[:]) + copy(hash[:], s.Message.Body.ExecutionPayloadHeader.BlockHash[:]) return hash } -func (s *SignedBlindedBlockContents) Root(spec *beacon.Spec) tree.Root { - return s.SignedBlindedBeaconBlock.Message.HashTreeRoot(spec, tree.GetHashFn()) +func (s *SignedBeaconResponse) Root(spec *beacon.Spec) tree.Root { + return s.Message.HashTreeRoot(spec, tree.GetHashFn()) } -func (s *SignedBlindedBlockContents) StateRoot() tree.Root { - return s.SignedBlindedBeaconBlock.Message.StateRoot +func (s *SignedBeaconResponse) StateRoot() tree.Root { + return s.Message.StateRoot } -func (s *SignedBlindedBlockContents) Slot() beacon.Slot { - return s.SignedBlindedBeaconBlock.Message.Slot +func (s *SignedBeaconResponse) Slot() beacon.Slot { + return s.Message.Slot } -func (s *SignedBlindedBlockContents) ProposerIndex() beacon.ValidatorIndex { - return s.SignedBlindedBeaconBlock.Message.ProposerIndex +func (s *SignedBeaconResponse) ProposerIndex() beacon.ValidatorIndex { + return s.Message.ProposerIndex } -func (s *SignedBlindedBlockContents) BlockSignature() *beacon.BLSSignature { - return &s.SignedBlindedBeaconBlock.Signature +func (s *SignedBeaconResponse) BlockSignature() *beacon.BLSSignature { + return &s.Signature } type UnblindedResponseData struct { @@ -54,18 +51,18 @@ type UnblindedResponseData struct { } func (b *BuilderBid) ValidateReveal(publicKey *blsu.Pubkey, signedBeaconResponse common.SignedBeaconResponse, spec *beacon.Spec, slot beacon.Slot, genesisValidatorsRoot *tree.Root) (*common.UnblindedResponse, error) { - sbb, ok := signedBeaconResponse.(*SignedBlindedBlockContents) + sbb, ok := signedBeaconResponse.(*SignedBeaconResponse) if !ok { return nil, fmt.Errorf("invalid signed beacon response") } - blockRoot := sbb.SignedBlindedBeaconBlock.Message.HashTreeRoot(spec, tree.GetHashFn()) - s, err := sbb.SignedBlindedBeaconBlock.Signature.Signature() + blockRoot := sbb.Message.HashTreeRoot(spec, tree.GetHashFn()) + s, err := sbb.Signature.Signature() if err != nil { return nil, fmt.Errorf("unable to validate block signature: %v", err) } - beaconBlock, err := sbb.SignedBlindedBeaconBlock.Message.Unblind(spec, b.Payload.ExecutionPayload) + beaconBlock, err := sbb.Message.Unblind(spec, b.Payload.ExecutionPayload) if err != nil { return nil, fmt.Errorf("failed to unblind block: %v", err) } @@ -82,36 +79,10 @@ func (b *BuilderBid) ValidateReveal(publicKey *blsu.Pubkey, signedBeaconResponse return nil, fmt.Errorf("invalid block signature") } - for i, signedBlindedBlobSidecar := range sbb.SignedBlindedBlobSidecars { - - blobSidecar := deneb.BlobSidecar{ - BlockRoot: blockRoot, - Index: deneb.BlobIndex(i), - Slot: slot, - BlockParentRoot: b.ParentBlockRoot, - ProposerIndex: b.ProposerIndex, - Blob: b.BlobsBundle.BlobsBundle.Blobs[i], - KZGCommitment: b.BlobsBundle.BlobsBundle.KZGCommitments[i], - KZGProof: b.BlobsBundle.BlobsBundle.KZGProofs[i], - } - - // Compare roots - rootWant := blobSidecar.HashTreeRoot(spec, tree.GetHashFn()) - // Calculating root of a blinded blob sidecar does not require the spec because only the blob length is spec-dependent - root := signedBlindedBlobSidecar.Message.HashTreeRoot(tree.GetHashFn()) - if root != rootWant { - return nil, fmt.Errorf("unblinded blob sidecar roots don't match: want: %s, got: %s", rootWant, root) - } - - s, err := signedBlindedBlobSidecar.Signature.Signature() - if err != nil { - return nil, fmt.Errorf("unable to validate blob sidecar signature: %v", err) - } - - dom := beacon.ComputeDomain(beacon.DOMAIN_BLOB_SIDECAR, forkVersion, *genesisValidatorsRoot) - signingRoot := beacon.ComputeSigningRoot(root, dom) - if !blsu.Verify(publicKey, signingRoot[:], s) { - return nil, fmt.Errorf("blob sidecar %d invalid signature", i) + // Verify kzg commitments in signed header + for i, kzgCommitment := range sbb.Message.Body.BlobKZGCommitments { + if !bytes.Equal(b.BlobsBundle.KZGCommitments[i][:], kzgCommitment[:]) { + return nil, fmt.Errorf("invalid kzg commitment") } } @@ -165,7 +136,7 @@ type BuilderBid struct { Payload *ExecutionPayload `json:"-" yaml:"-"` Header *deneb.ExecutionPayloadHeader `json:"header" yaml:"header"` BlobsBundle *BlobsBundle `json:"-" yaml:"-"` - BlindedBlobsBundle *deneb.BlindedBlobsBundle `json:"blinded_blobs_bundle" yaml:"blinded_blobs_bundle"` + BlobKZGCommitments *beacon.KZGCommitments `json:"blob_kzg_commitments" yaml:"blob_kzg_commitments"` Value view.Uint256View `json:"value" yaml:"value"` PubKey beacon.BLSPubkey `json:"pubkey" yaml:"pubkey"` common.BuilderBidContext `json:"-" yaml:"-"` @@ -180,7 +151,7 @@ func (b *BuilderBid) Version() string { func (b *BuilderBid) HashTreeRoot(spec *beacon.Spec, hFn tree.HashFn) tree.Root { return hFn.HashTreeRoot( b.Header, - spec.Wrap(b.BlindedBlobsBundle), + spec.Wrap(b.BlobKZGCommitments), &b.Value, &b.PubKey, ) @@ -215,7 +186,7 @@ func (b *BuilderBid) Build( return err } - b.BlindedBlobsBundle = b.BlobsBundle.Blinded(spec, tree.GetHashFn()) + b.BlobKZGCommitments = &b.BlobsBundle.KZGCommitments b.ParentBlockRoot = parentBlockRoot b.Slot = slot diff --git a/types/deneb/deneb_test.go b/types/deneb/deneb_test.go index eea7108..8691d4e 100644 --- a/types/deneb/deneb_test.go +++ b/types/deneb/deneb_test.go @@ -66,8 +66,8 @@ func TestDenebBidBuilding(t *testing.T) { t.Fatal("header not built") } - if bid.BlindedBlobsBundle == nil { - t.Fatal("blinded blobs bundle not built") + if bid.BlobKZGCommitments == nil { + t.Fatal("blob kzg commitments empty") } if bid.BlobsBundle == nil { @@ -83,19 +83,9 @@ func TestDenebBidBuilding(t *testing.T) { } } - if len(bid.BlindedBlobsBundle.BlobRoots) != len(bid.BlobsBundle.Blobs) { - t.Fatal("invalid blinded blobs bundle") - } - - for i, blobRoot := range bid.BlindedBlobsBundle.BlobRoots { - calcRoot := bid.BlobsBundle.Blobs[i].HashTreeRoot(spec, tree.GetHashFn()) - fmt.Printf("blob root %d: %s\n", i, calcRoot) - if !bytes.Equal(blobRoot[:], calcRoot[:]) { - t.Fatalf("hash tree root mismatch on root %d", i) - } - // Check also if the root is not zero - if blobRoot == tree.Root([32]byte{}) { - t.Fatalf("zero hash tree root mismatch on root %d: %s", i, blobRoot) + for i, blobKzg := range *bid.BlobKZGCommitments { + if !bytes.Equal(blobKzg[:], bid.BlobsBundle.KZGCommitments[i][:]) { + t.Fatalf("blob kzg commitment %d not set", i) } } } @@ -108,33 +98,31 @@ func TestBidHashTreeRoot(t *testing.T) { { jsonBid: `{ "header": { - "parent_hash": "0x190caa185cdeb86d0a7472e6447a92a39b762f4ea696d3d19d47bba4eb7fa935", - "fee_recipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", - "state_root": "0x2affac6b1bdc0a25fea0175fa6be328ebb16f880f0f1fdf458bd7bf19842fa60", - "receipts_root": "0x7aca796fcc5d37e5c8dd6705e01b315fa28619159a1347cd14aabef1d52ff035", - "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "prev_randao": "0xd361841f2f8648166b502b92bded23fcaf0610cc41705e2d64e7450500743511", - "block_number": "35", - "gas_limit": "30000000", - "gas_used": "840000", - "timestamp": "1693427860", - "extra_data": "0x6275696c646572207061796c6f616420747374", - "base_fee_per_gas": "11703946", - "block_hash": "0x8ed0f7bc908d61f7e453d3dc5ffc52694a3e584cc9119b63f12a30f193b068bf", - "transactions_root": "0x58ebe57ba95bd5d191bd94a8612be571f5b26e83dd2314c20caad3ddbb69799d", - "withdrawals_root": "0x792930bbd5baac43bcc798ee49aa8185ef76bb3b44ba62b91d86ae569e4bb535", - "blob_gas_used": "0", - "excess_blob_gas": "0" - }, - "blinded_blobs_bundle": { - "commitments": [], - "proofs": [], - "blob_roots": [] + "parent_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "fee_recipient": "0xabcf8e0d4e9587369b2301d0790347320302cc09", + "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "receipts_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "prev_randao": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "block_number": "1", + "gas_limit": "1", + "gas_used": "1", + "timestamp": "1", + "extra_data": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "base_fee_per_gas": "1", + "blob_gas_used": "1", + "excess_blob_gas": "1", + "block_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "transactions_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "withdrawals_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" }, - "value": "8400000000000000", - "pubkey": "0x95fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48" - }`, - expectedHTR: "0xed095772eca7c6c826a33e9945380a749b2d88f347f018ed46d97582c5f9a955", + "blob_kzg_commitments": [ + "0xa94170080872584e54a1cf092d845703b13907f2e6b3b1c0ad573b910530499e3bcd48c6378846b80d2bfa58c81cf3d5" + ], + "value": "1", + "pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a" + }`, + expectedHTR: "0xb8badff2ce16663a6a3100943d008c6a64d996ca6f7b4310a4c690015b3fb587", }, } { t.Run(fmt.Sprintf("htr-%d", i), func(t *testing.T) {