Skip to content

Commit

Permalink
core/validatorapi: workaround for Gnosis proposals
Browse files Browse the repository at this point in the history
If the "gnosis_block_hotfix" feature flag is enabled, use the Gnosis-compatible representation of the proposal when calculating its hash tree root.  This should fix Gnosis proposals failing due to bad signature.
  • Loading branch information
gsora committed Aug 23, 2024
1 parent a79d57b commit 320438b
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 2 deletions.
3 changes: 3 additions & 0 deletions app/featureset/featureset.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ const (

// JSONRequests enables JSON requests for eth2 client.
JSONRequests Feature = "json_requests"

GnosisBlockHotfix Feature = "gnosis_block_hotfix"
)

var (
Expand All @@ -50,6 +52,7 @@ var (
MockAlpha: statusAlpha,
AggSigDBV2: statusAlpha,
JSONRequests: statusAlpha,
GnosisBlockHotfix: statusAlpha,
// Add all features and there status here.
}

Expand Down
8 changes: 8 additions & 0 deletions core/signeddata.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import (
"github.com/attestantio/go-eth2-client/spec/altair"
"github.com/attestantio/go-eth2-client/spec/bellatrix"
"github.com/attestantio/go-eth2-client/spec/capella"
"github.com/attestantio/go-eth2-client/spec/deneb"
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
ssz "github.com/ferranbt/fastssz"

"github.com/obolnetwork/charon/app/errors"
"github.com/obolnetwork/charon/app/eth2wrap"
"github.com/obolnetwork/charon/app/featureset"
"github.com/obolnetwork/charon/eth2util"
"github.com/obolnetwork/charon/eth2util/eth2exp"
"github.com/obolnetwork/charon/eth2util/signing"
Expand Down Expand Up @@ -320,6 +322,12 @@ func (p VersionedSignedProposal) MessageRoot() ([32]byte, error) {
return p.DenebBlinded.Message.HashTreeRoot()
}

if featureset.Enabled(featureset.GnosisBlockHotfix) {
// translate p.Deneb.SignedBlock to its Gnosis associate and return its hash tree root
sbGnosis := deneb.BeaconBlockToGnosis(*p.Deneb.SignedBlock.Message)
return sbGnosis.HashTreeRoot()
}

return p.Deneb.SignedBlock.Message.HashTreeRoot()
default:
panic("unknown version") // Note this is avoided by using `NewVersionedSignedProposal`.
Expand Down
64 changes: 64 additions & 0 deletions core/signeddata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package core_test

import (
"encoding/hex"
"encoding/json"
"fmt"
"testing"
Expand All @@ -15,9 +16,11 @@ import (
"github.com/attestantio/go-eth2-client/spec/altair"
"github.com/attestantio/go-eth2-client/spec/bellatrix"
"github.com/attestantio/go-eth2-client/spec/capella"
"github.com/attestantio/go-eth2-client/spec/deneb"
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/stretchr/testify/require"

"github.com/obolnetwork/charon/app/featureset"
"github.com/obolnetwork/charon/core"
"github.com/obolnetwork/charon/testutil"
)
Expand Down Expand Up @@ -347,3 +350,64 @@ func TestVersionedSignedProposal(t *testing.T) {
})
}
}

func TestGnosisProposals(t *testing.T) {
baseProposal := eth2api.VersionedSignedProposal{
Version: eth2spec.DataVersionDeneb,
Deneb: testutil.RandomDenebVersionedSignedProposal().Deneb,
}

rawGnosisProposal, err := core.NewVersionedSignedProposal(&baseProposal)
require.NoError(t, err)

rawStdProposal, err := core.NewVersionedSignedProposal(&baseProposal)
require.NoError(t, err)

featureset.EnableForT(t, featureset.GnosisBlockHotfix)

gnosisProp := core.ParSignedData{
SignedData: rawGnosisProposal,
ShareIdx: 42,
}

gnosisRoot, err := gnosisProp.MessageRoot()
require.NoError(t, err)

featureset.DisableForT(t, featureset.GnosisBlockHotfix)

stdProp := core.ParSignedData{
SignedData: rawStdProposal,
ShareIdx: 42,
}

stdRoot, err := stdProp.MessageRoot()
require.NoError(t, err)

require.NotEqual(t, stdRoot, gnosisRoot)
}

func TestGnosisRealBlockHash(t *testing.T) {
const (
realSszStr = "f476b10000000000dc1c000000000000414c00276374153218243eba1b9c92821d64a83b1b7c5dd3f7c051f7b404e873079d1b1a97f18a191fbdf457f809e958473aed36593981bd990b9ba0103775425400000090a2e40b2745cdbcc797856161cebc85ca517f2c956a28682b4539fc1bd051355b39836bb73502fefe3c9683b6a899c802e3511378915c06dfb4cb5b218261ddc8abe0e5e4b7701272b7cdfb525764f580ad561ead7964bc40c188a609ea40458e289cb0e746320595ef0b1a6f1118b4949f016818c1457beb0d90f3ca06ff55cf05000000000000fc6c3b6e91805bfd2a224716830ab1644c4fdcfaad82aad5a4ac3d0ad33d6d20636861726f6e2f76312e312e302d6465762d38306635613236000000000000008801000088010000880100008805000088050000fffbbffffffff7ffff7ffe7ffffffef6ffffbfbfffeffffbddfffffffffff7ffff9ffffffbfffdffffefffffdfffffbfffbffdfffffdffffafffdffffdfff7bf98cca8f4c0aed716d5f6352e2e4adf58d398d132fadf9352abe53971b8dceba4a7c3ba29e176a4bfd9154736f370e83404bb55af4be453f72c066cbc05e882e29caebc707a0c38573d15c2e0c94e9f65f5c71743a4f24b683a9c5843e06aa2c988050000bc0c0000bc0c0000100000000c0100000802000004030000e4000000f376b100000000000100000000000000414c00276374153218243eba1b9c92821d64a83b1b7c5dd3f7c051f7b404e8736e170b0000000000aad68b53dc54fb14d6ae23decfd7b80b026e044d98f7537eb7a7a599e289d4c96f170b00000000004b57e3fcb8a9098573721e35021ccf5a03a52e5b6d0df29c6410ac3179d98198864379f3d48d26b4567bda0b95be3d4fb4977dc83bb360f117b64c8792ae82697690c5a8e4f66dbff6ea87ef089f9ee000a94c948d638228eb02acbd360bd0018b000496dabd4a0eb6af94a2700b1195d9bbb9c94aa6947a2cd13f7e460b15b7ffffffffffffffffdffeffffffefeffffffcffffffffff03e4000000f376b100000000000000000000000000414c00276374153218243eba1b9c92821d64a83b1b7c5dd3f7c051f7b404e8736e170b0000000000aad68b53dc54fb14d6ae23decfd7b80b026e044d98f7537eb7a7a599e289d4c96f170b00000000004b57e3fcb8a9098573721e35021ccf5a03a52e5b6d0df29c6410ac3179d98198a8823ae587250ebb681e5041b0e98177da564588e681fc9f03413dede12cb736edb0894d92d8acd453610a44faef61820884f08b95825263b6eb85ae3a1a9b7e372f24886f14f51f960a8ed7701621bc391518a771d82479159600dcf5fb472effffffffdffffffffeffffffffbdffffbf7fffffdfffff01e4000000f176b1000000000001000000000000002ab5a55abbffb1d54156aedc8f532ddc8e20b52ad418d274976c84bed8c6dfb96e170b0000000000aad68b53dc54fb14d6ae23decfd7b80b026e044d98f7537eb7a7a599e289d4c96f170b00000000004b57e3fcb8a9098573721e35021ccf5a03a52e5b6d0df29c6410ac3179d9819892abafc310b9960acd95315361d9fbf7d6dcace2525f614ac6202d34e09639b5fa654a80ad33cf1587faac32ee8799870475e38f3f34a7e8916692f2957c3bdc02e29b00c3bc236a690677165f015435f4995aa3fc24e8d4d757b6b410a694c3000000000000000000000000000000000000000000001002e4000000f176b1000000000000000000000000002ab5a55abbffb1d54156aedc8f532ddc8e20b52ad418d274976c84bed8c6dfb96e170b0000000000aad68b53dc54fb14d6ae23decfd7b80b026e044d98f7537eb7a7a599e289d4c96f170b00000000004b57e3fcb8a9098573721e35021ccf5a03a52e5b6d0df29c6410ac3179d981988accfe8cd3246977e00443e4096ee201b57f3ebc03df78ce067ef40a994baf40f020ffb197c339773f807cddea4f9c51036909facf3fcf763998ae703c8c70aacc6519948bd20283800f199b08ff6005b6c4ec096bd3aef1cb93efc17f0724b4000000000000000000000000000000000000000004000001b1b6d67608054e32ca56184f8bf612f9c0e8df9f99fc7607ff1fe865e80f04317ce7390c41ce3416c4a0a297761c71763d89ca3baa3f345a5c48d0e9470c650b41e38dfdae05db0df098fae94a61cd1fca5b6fdac426de6f735cfb1799cbc9729fa60a19de90b88201b1ba442cc5624a17f6a3590000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000026f600f4172341b485e1fb406f5334b3d4887d896ca54fd71ba2590c752574ef3905ac000000000062690401000000003ddd0100000000001041bb66000000001002000007000000000000000000000000000000000000000000000000000000000000008eb2a7234864cd0d17fe81e4c229130ee883dce5b577801526e355d4a44a397b1a020000d4050000000000000000000000000000000000004e65746865726d696e6408000000e101000002f901d58227d8825e9784b2d05e0084b2d05e00831632a8948448e15d0e706c0298deca99f0b4744030e59d7d80b90164e7a2c01f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000118000500000735a05d7e98453b1abcedec7918072d3d6f5ec20000000000000d3ec6755144d60548f3dd420f47cf48dae553bbf0423f5929bee6a59661d6ccc9c4eb751048009ce11b0007a120030200aa36a727d869f5590300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000187290cd402054b514751d3cfb037c8ee0eda175f7c6b19c0b0dd529dff22054800000000000000000000000000000000000000000000000000000000000000012cda73507147b818a330a53afea6536c40ea9fb3e20107a1147fdb1e798417d90000000000000000c001a019a3a3e71b2e3418e753bbee132a4dbfebf6fe54c09a5efafcaafd48a797f2e6a044d24ef0e9d489da2f1cf5969a2a639506f133e8a35dc7b546fe84977c6473a202f901d58227d8825e9884b2d05e0084b2d05e00831632a8948448e15d0e706c0298deca99f0b4744030e59d7d80b90164e7a2c01f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000118000500000735a05d7e98453b1abcedec7918072d3d6f5ec20000000000000d54c6755144d60548f3dd420f47cf48dae553bbf0423f5929bee6a59661d6ccc9c4eb751048009ce11b0007a120030200aa36a727d869f55903000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001ca21552ef9a14f965d7fd033812e635db7b1f5d5a25f42d5749d804653ac80dd0000000000000000000000000000000000000000000000000000000000000001bc4cd07038c77b7e752abf26ceaa64d2d9c71d05b6436a95bc2b3f5a2173eb920000000000000000c001a075bd587dcc1b039a8ad587050af69ec56714a570d7cb90f1de673cff7d2f2151a0540002cf0799372453b7d752fe235bd5d83c30cf8fdb8eb3fefd5060e093b2825e27050300000000960f000000000000cc4e00a72d871d6c328bcfe9025ad93d0a26df5173f50e00000000005f27050300000000970f000000000000cc4e00a72d871d6c328bcfe9025ad93d0a26df5196470f00000000006027050300000000980f000000000000cc4e00a72d871d6c328bcfe9025ad93d0a26df5196470f00000000006127050300000000990f000000000000cc4e00a72d871d6c328bcfe9025ad93d0a26df51b01b0f000000000062270503000000009a0f000000000000cc4e00a72d871d6c328bcfe9025ad93d0a26df51d4293c000000000063270503000000009b0f000000000000cc4e00a72d871d6c328bcfe9025ad93d0a26df51f51e0f000000000064270503000000009d0f000000000000cc4e00a72d871d6c328bcfe9025ad93d0a26df51b6f20e000000000065270503000000009e0f000000000000cc4e00a72d871d6c328bcfe9025ad93d0a26df51a283150000000000"
expectedGnosisHashStr = "9ddaf2f91ad6b426603286c98aa71a659b13475c4e71c9b5603b86528a072137"
expectedStdHashStr = "bdf303daf3b3f1735460c0ee0de2646a39247daadf091d3cfc7f7cb70c696426"
)
realSsz, err := hex.DecodeString(realSszStr)
require.NoError(t, err)

m := &deneb.GnosisBeaconBlock{}
err = m.UnmarshalSSZ(realSsz)
require.NoError(t, err)

realHash, err := m.HashTreeRoot()
require.NoError(t, err)
require.Equal(t, expectedGnosisHashStr, hex.EncodeToString(realHash[:]))

mStd := &deneb.BeaconBlock{}
err = mStd.UnmarshalSSZ(realSsz)
require.NoError(t, err)

realHashStd, err := mStd.HashTreeRoot()
require.NoError(t, err)
require.Equal(t, expectedStdHashStr, hex.EncodeToString(realHashStd[:]))
}
92 changes: 92 additions & 0 deletions core/validatorapi/validatorapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ import (
"github.com/attestantio/go-eth2-client/spec/altair"
"github.com/attestantio/go-eth2-client/spec/bellatrix"
"github.com/attestantio/go-eth2-client/spec/capella"
"github.com/attestantio/go-eth2-client/spec/deneb"
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/prysmaticlabs/go-bitfield"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"

"github.com/obolnetwork/charon/app/errors"
"github.com/obolnetwork/charon/app/eth2wrap"
"github.com/obolnetwork/charon/app/featureset"
"github.com/obolnetwork/charon/core"
"github.com/obolnetwork/charon/core/validatorapi"
"github.com/obolnetwork/charon/eth2util"
Expand Down Expand Up @@ -491,6 +493,96 @@ func TestComponent_SubmitProposal(t *testing.T) {
require.NoError(t, err)
}

func TestComponent_SubmitProposal_Gnosis(t *testing.T) {
ctx := context.Background()

featureset.EnableForT(t, featureset.GnosisBlockHotfix)
defer featureset.DisableForT(t, featureset.GnosisBlockHotfix)

// Create keys (just use normal keys, not split tbls)
secret, err := tbls.GenerateSecretKey()
require.NoError(t, err)

pubkey, err := tbls.SecretToPublicKey(secret)
require.NoError(t, err)

const (
vIdx = 1
shareIdx = 1
slot = 123
epoch = eth2p0.Epoch(3)
)

// Convert pubkey
corePubKey, err := core.PubKeyFromBytes(pubkey[:])
require.NoError(t, err)
allPubSharesByKey := map[core.PubKey]map[int]tbls.PublicKey{corePubKey: {shareIdx: pubkey}} // Maps self to self since not tbls

// Configure beacon mock
bmock, err := beaconmock.New()
require.NoError(t, err)

// Construct the validator api component
vapi, err := validatorapi.NewComponent(bmock, allPubSharesByKey, shareIdx, nil, testutil.BuilderFalse, nil)
require.NoError(t, err)

// Prepare unsigned beacon block
msg := []byte("randao reveal")
sig, err := tbls.Sign(secret, msg)
require.NoError(t, err)

randao := eth2p0.BLSSignature(sig)
unsignedBlock := &eth2spec.VersionedBeaconBlock{
Version: eth2spec.DataVersionDeneb,
Deneb: testutil.RandomDenebBeaconBlock(),
}
unsignedBlock.Deneb.Body.RANDAOReveal = randao
unsignedBlock.Deneb.Slot = slot
unsignedBlock.Deneb.ProposerIndex = vIdx

vapi.RegisterGetDutyDefinition(func(ctx context.Context, duty core.Duty) (core.DutyDefinitionSet, error) {
return core.DutyDefinitionSet{corePubKey: nil}, nil
})

gnosisBlock := deneb.BeaconBlockToGnosis(*unsignedBlock.Deneb)
// Sign beacon block
sigRoot, err := gnosisBlock.HashTreeRoot()
require.NoError(t, err)

domain, err := signing.GetDomain(ctx, bmock, signing.DomainBeaconProposer, epoch)
require.NoError(t, err)

sigData, err := (&eth2p0.SigningData{ObjectRoot: sigRoot, Domain: domain}).HashTreeRoot()
require.NoError(t, err)

s, err := tbls.Sign(secret, sigData[:])
require.NoError(t, err)

signedBlock := &eth2api.VersionedSignedProposal{
Version: unsignedBlock.Version,
Deneb: &eth2deneb.SignedBlockContents{
SignedBlock: &deneb.SignedBeaconBlock{
Message: unsignedBlock.Deneb,
Signature: eth2p0.BLSSignature(s),
},
},
}

// Register subscriber
vapi.Subscribe(func(ctx context.Context, duty core.Duty, set core.ParSignedDataSet) error {
block, ok := set[corePubKey].SignedData.(core.VersionedSignedProposal)
require.True(t, ok)
require.Equal(t, *signedBlock, block.VersionedSignedProposal)

return nil
})

err = vapi.SubmitProposal(ctx, &eth2api.SubmitProposalOpts{
Proposal: signedBlock,
})
require.NoError(t, err)
}

func TestComponent_SubmitProposalInvalidSignature(t *testing.T) {
ctx := context.Background()

Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,7 @@ require (

// We're replacing kryptology with our own fork, which fixes dependencies' security vulnerabilities.
replace github.com/coinbase/kryptology => github.com/ObolNetwork/kryptology v0.0.0-20231016091344-eed023b6cac8

// We're replacing go-eth2-client with a branch off our fork, at version v0.21.10.
// This is needed to ensure Gnosis compatibility.
replace github.com/attestantio/go-eth2-client => github.com/ObolNetwork/go-eth2-client v0.21.11-0.20240822135044-f0a5b21e02c6
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/ObolNetwork/go-eth2-client v0.21.11-0.20240822135044-f0a5b21e02c6 h1:VEBrga7Dn5SwvJQEG3i2K7IAUQQvEmUulWxXoBDimnM=
github.com/ObolNetwork/go-eth2-client v0.21.11-0.20240822135044-f0a5b21e02c6/go.mod h1:d7ZPNrMX8jLfIgML5u7QZxFo2AukLM+5m08iMaLdqb8=
github.com/ObolNetwork/kryptology v0.0.0-20231016091344-eed023b6cac8 h1:IXoKQKGzebwtIzKADtZyAjL3MIr0m3zQFxlSxxWIdCU=
github.com/ObolNetwork/kryptology v0.0.0-20231016091344-eed023b6cac8/go.mod h1:qcn33Qgj0WVLH1nuXqPt8JHY+yZabhyKxI5dHRk0fbo=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/attestantio/go-eth2-client v0.21.10 h1:1DWn42WKjk8mR8jKkjbaDCGNMVnh2IfAWRUmt7iemRo=
github.com/attestantio/go-eth2-client v0.21.10/go.mod h1:d7ZPNrMX8jLfIgML5u7QZxFo2AukLM+5m08iMaLdqb8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
Expand Down

0 comments on commit 320438b

Please sign in to comment.