From 36c94fc1d112f28b794828ac3bd8d7d8c4cf808c Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Thu, 19 Oct 2023 11:48:28 +0200 Subject: [PATCH 1/4] Move things to custom package --- suave/{backends => cstore}/am_signer.go | 2 +- suave/{backends => cstore}/backend_testing.go | 2 +- suave/cstore/engine.go | 429 ++++++++++++++++++ suave/cstore/engine_test.go | 126 +++++ .../local_store_backend.go | 2 +- .../local_store_backend_test.go | 2 +- .../redis_backends_test.go | 2 +- .../redis_store_backend.go | 2 +- .../redis_store_backend_test.go | 2 +- suave/{backends => cstore}/redis_transport.go | 2 +- suave/cstore/transactional_store.go | 114 +++++ .../transactional_store_test.go | 2 +- 12 files changed, 678 insertions(+), 9 deletions(-) rename suave/{backends => cstore}/am_signer.go (98%) rename suave/{backends => cstore}/backend_testing.go (98%) create mode 100644 suave/cstore/engine.go create mode 100644 suave/cstore/engine_test.go rename suave/{backends => cstore}/local_store_backend.go (99%) rename suave/{backends => cstore}/local_store_backend_test.go (88%) rename suave/{backends => cstore}/redis_backends_test.go (99%) rename suave/{backends => cstore}/redis_store_backend.go (99%) rename suave/{backends => cstore}/redis_store_backend_test.go (92%) rename suave/{backends => cstore}/redis_transport.go (99%) create mode 100644 suave/cstore/transactional_store.go rename suave/{backends => cstore}/transactional_store_test.go (99%) diff --git a/suave/backends/am_signer.go b/suave/cstore/am_signer.go similarity index 98% rename from suave/backends/am_signer.go rename to suave/cstore/am_signer.go index 07d513e8a5..046a296888 100644 --- a/suave/backends/am_signer.go +++ b/suave/cstore/am_signer.go @@ -1,4 +1,4 @@ -package backends +package cstore import ( "github.com/ethereum/go-ethereum/accounts" diff --git a/suave/backends/backend_testing.go b/suave/cstore/backend_testing.go similarity index 98% rename from suave/backends/backend_testing.go rename to suave/cstore/backend_testing.go index 49232b2dbc..f4dc09a0a4 100644 --- a/suave/backends/backend_testing.go +++ b/suave/cstore/backend_testing.go @@ -1,4 +1,4 @@ -package backends +package cstore import ( "testing" diff --git a/suave/cstore/engine.go b/suave/cstore/engine.go new file mode 100644 index 0000000000..9970ca1e71 --- /dev/null +++ b/suave/cstore/engine.go @@ -0,0 +1,429 @@ +package cstore + +import ( + "context" + "encoding/json" + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + suave "github.com/ethereum/go-ethereum/suave/core" + "github.com/google/uuid" + "golang.org/x/exp/slices" +) + +type ConfidentialStoreEngine struct { + ctx context.Context + cancel context.CancelFunc + + backend suave.ConfidentialStoreBackend + transportTopic suave.StoreTransportTopic + + daSigner suave.DASigner + chainSigner suave.ChainSigner + + storeUUID uuid.UUID + localAddresses map[common.Address]struct{} +} + +func NewConfidentialStoreEngine(backend suave.ConfidentialStoreBackend, transportTopic suave.StoreTransportTopic, daSigner suave.DASigner, chainSigner suave.ChainSigner) *ConfidentialStoreEngine { + localAddresses := make(map[common.Address]struct{}) + for _, addr := range daSigner.LocalAddresses() { + localAddresses[addr] = struct{}{} + } + + return &ConfidentialStoreEngine{ + backend: backend, + transportTopic: transportTopic, + daSigner: daSigner, + chainSigner: chainSigner, + storeUUID: uuid.New(), + localAddresses: localAddresses, + } +} + +func (e *ConfidentialStoreEngine) NewTransactionalStore(sourceTx *types.Transaction) *TransactionalStore { + return &TransactionalStore{ + sourceTx: sourceTx, + engine: e, + pendingBids: make(map[suave.BidId]suave.Bid), + } +} + +func (e *ConfidentialStoreEngine) Start() error { + if err := e.backend.Start(); err != nil { + return err + } + + if err := e.transportTopic.Start(); err != nil { + return err + } + + if e.cancel != nil { + e.cancel() + } + + ctx, cancel := context.WithCancel(context.Background()) + e.cancel = cancel + e.ctx = ctx + go e.ProcessMessages() + + return nil +} + +func (e *ConfidentialStoreEngine) Stop() error { + if e.cancel == nil { + return errors.New("Confidential engine: Stop() called before Start()") + } + + e.cancel() + + if err := e.transportTopic.Stop(); err != nil { + log.Warn("Confidential engine: error while stopping transport", "err", err) + } + + if err := e.backend.Stop(); err != nil { + log.Warn("Confidential engine: error while stopping transport", "err", err) + } + + return nil +} + +// For testing purposes! +func (e *ConfidentialStoreEngine) Backend() suave.ConfidentialStoreBackend { + return e.backend +} + +func (e *ConfidentialStoreEngine) ProcessMessages() { + ch, cancel := e.transportTopic.Subscribe() + defer cancel() + + for { + select { + case <-e.ctx.Done(): // Stop() called + return + case msg := <-ch: + err := e.NewMessage(msg) + if err != nil { + log.Info("could not process new store message", "err", err) + } else { + log.Info("Message processed", "msg", msg) + } + } + } +} + +func (e *ConfidentialStoreEngine) InitializeBid(bid types.Bid, creationTx *types.Transaction) (suave.Bid, error) { + expectedId, err := calculateBidId(bid) + if err != nil { + return suave.Bid{}, fmt.Errorf("confidential engine: could not initialize new bid: %w", err) + } + + if bid.Id == emptyId { + bid.Id = expectedId + } else if bid.Id != expectedId { + // True in some tests, might be time to rewrite them + return suave.Bid{}, errors.New("confidential engine:incorrect bid id passed") + } + + initializedBid := suave.Bid{ + Id: bid.Id, + Salt: bid.Salt, + DecryptionCondition: bid.DecryptionCondition, + AllowedPeekers: bid.AllowedPeekers, + AllowedStores: bid.AllowedStores, + Version: bid.Version, + CreationTx: creationTx, + } + + bidBytes, err := SerializeBidForSigning(&initializedBid) + if err != nil { + return suave.Bid{}, fmt.Errorf("confidential engine: could not hash bid for signing: %w", err) + } + + signingAccount, err := ExecutionNodeFromTransaction(creationTx) + if err != nil { + return suave.Bid{}, fmt.Errorf("confidential engine: could not recover execution node from creation transaction: %w", err) + } + + initializedBid.Signature, err = e.daSigner.Sign(signingAccount, bidBytes) + if err != nil { + return suave.Bid{}, fmt.Errorf("confidential engine: could not sign initialized bid: %w", err) + } + + return initializedBid, nil +} + +func (e *ConfidentialStoreEngine) FetchBidById(bidId suave.BidId) (suave.Bid, error) { + return e.backend.FetchBidById(bidId) +} + +func (e *ConfidentialStoreEngine) FetchBidsByProtocolAndBlock(blockNumber uint64, namespace string) []suave.Bid { + return e.backend.FetchBidsByProtocolAndBlock(blockNumber, namespace) +} + +func (e *ConfidentialStoreEngine) Retrieve(bidId suave.BidId, caller common.Address, key string) ([]byte, error) { + bid, err := e.backend.FetchBidById(bidId) + if err != nil { + return []byte{}, fmt.Errorf("confidential engine: could not fetch bid %x while retrieving: %w", bidId, err) + } + + if !slices.Contains(bid.AllowedPeekers, caller) { + return []byte{}, fmt.Errorf("confidential engine: %x not allowed to retrieve %s on %x", caller, key, bidId) + } + + return e.backend.Retrieve(bid, caller, key) +} + +func (e *ConfidentialStoreEngine) Finalize(tx *types.Transaction, newBids map[suave.BidId]suave.Bid, stores []suave.StoreWrite) error { + // + for _, bid := range newBids { + err := e.backend.InitializeBid(bid) + if err != nil { + // TODO: deinitialize! + return fmt.Errorf("confidential engine: store backend failed to initialize bid: %w", err) + } + } + + for _, sw := range stores { + if _, err := e.backend.Store(sw.Bid, sw.Caller, sw.Key, sw.Value); err != nil { + // TODO: deinitialize and deStore! + return fmt.Errorf("failed to store data: %w", err) + } + } + + // Sign and propagate the message + pwMsg := suave.DAMessage{ + SourceTx: tx, + StoreWrites: stores, + StoreUUID: e.storeUUID, + } + + if _, sigErr := e.chainSigner.Sender(tx); sigErr != nil { + log.Info("confidential engine: refusing to send writes based on unsigned transaction", "hash", tx.Hash().Hex(), "err", sigErr) + return suave.ErrUnsignedFinalize + } + + msgBytes, err := SerializeMessageForSigning(&pwMsg) + if err != nil { + return fmt.Errorf("confidential engine: could not hash message for signing: %w", err) + } + + signingAccount, err := ExecutionNodeFromTransaction(tx) + if err != nil { + return fmt.Errorf("confidential engine: could not recover execution node from source transaction: %w", err) + } + + pwMsg.Signature, err = e.daSigner.Sign(signingAccount, msgBytes) + if err != nil { + return fmt.Errorf("confidential engine: could not sign message: %w", err) + } + + // TODO: avoid marshalling twice + go e.transportTopic.Publish(pwMsg) + + return nil +} + +func (e *ConfidentialStoreEngine) NewMessage(message suave.DAMessage) error { + // Note the validation is a work in progress and not guaranteed to be correct! + + // Message-level validation + msgBytes, err := SerializeMessageForSigning(&message) + if err != nil { + return fmt.Errorf("confidential engine: could not hash received message: %w", err) + } + recoveredMessageSigner, err := e.daSigner.Sender(msgBytes, message.Signature) + if err != nil { + return fmt.Errorf("confidential engine: incorrect message signature: %w", err) + } + expectedMessageSigner, err := ExecutionNodeFromTransaction(message.SourceTx) + if err != nil { + return fmt.Errorf("confidential engine: could not recover signer from message: %w", err) + } + if recoveredMessageSigner != expectedMessageSigner { + return fmt.Errorf("confidential engine: message signer %x, expected %x", recoveredMessageSigner, expectedMessageSigner) + } + + if message.StoreUUID == e.storeUUID { + if _, found := e.localAddresses[recoveredMessageSigner]; found { + return nil + } + // Message from self! + log.Info("Confidential engine: message is spoofing our storeUUID, processing anyway", "message", message) + } + + _, err = e.chainSigner.Sender(message.SourceTx) + if err != nil { + return fmt.Errorf("confidential engine: source tx for message is not signed properly: %w", err) + } + + // TODO: check if message.SourceTx is valid and insert it into the mempool! + + // Bid level validation + + for _, sw := range message.StoreWrites { + expectedId, err := calculateBidId(types.Bid{ + Id: sw.Bid.Id, + Salt: sw.Bid.Salt, + DecryptionCondition: sw.Bid.DecryptionCondition, + AllowedPeekers: sw.Bid.AllowedPeekers, + AllowedStores: sw.Bid.AllowedStores, + Version: sw.Bid.Version, + }) + if err != nil { + return fmt.Errorf("confidential engine: could not calculate received bids id: %w", err) + } + + if expectedId != sw.Bid.Id { + return fmt.Errorf("confidential engine: received bids id (%x) does not match the expected (%x)", sw.Bid.Id, expectedId) + } + + bidBytes, err := SerializeBidForSigning(&sw.Bid) + if err != nil { + return fmt.Errorf("confidential engine: could not hash received bid: %w", err) + } + recoveredBidSigner, err := e.daSigner.Sender(bidBytes, sw.Bid.Signature) + if err != nil { + return fmt.Errorf("confidential engine: incorrect bid signature: %w", err) + } + expectedBidSigner, err := ExecutionNodeFromTransaction(sw.Bid.CreationTx) + if err != nil { + return fmt.Errorf("confidential engine: could not recover signer from bid: %w", err) + } + if recoveredBidSigner != expectedBidSigner { + return fmt.Errorf("confidential engine: bid signer %x, expected %x", recoveredBidSigner, expectedBidSigner) + } + + if !slices.Contains(sw.Bid.AllowedStores, recoveredMessageSigner) { + return fmt.Errorf("confidential engine: sw signer %x not allowed to store on bid %x", recoveredMessageSigner, sw.Bid.Id) + } + + if !slices.Contains(sw.Bid.AllowedPeekers, sw.Caller) { + return fmt.Errorf("confidential engine: caller %x not allowed on bid %x", sw.Caller, sw.Bid.Id) + } + + // TODO: move to types.Sender() + _, err = e.chainSigner.Sender(sw.Bid.CreationTx) + if err != nil { + return fmt.Errorf("confidential engine: creation tx for bid id %x is not signed properly: %w", sw.Bid.Id, err) + } + } + + for _, sw := range message.StoreWrites { + err = e.backend.InitializeBid(sw.Bid) + if err != nil { + if !errors.Is(err, suave.ErrBidAlreadyPresent) { + log.Error("confidential engine: unexpected error while initializing bid from transport: %w", err) + continue // Don't abandon! + } + } + + _, err = e.backend.Store(sw.Bid, sw.Caller, sw.Key, sw.Value) + if err != nil { + log.Error("confidential engine: unexpected error while storing: %w", err) + continue // Don't abandon! + } + } + + return nil +} + +func SerializeBidForSigning(bid *suave.Bid) ([]byte, error) { + bidBytes, err := json.Marshal(suave.Bid{ + Id: bid.Id, + Salt: bid.Salt, + DecryptionCondition: bid.DecryptionCondition, + AllowedPeekers: bid.AllowedPeekers, + AllowedStores: bid.AllowedStores, + Version: bid.Version, + CreationTx: bid.CreationTx, + }) + if err != nil { + return []byte{}, err + } + + return []byte(fmt.Sprintf("\x19Suave Signed Message:\n%d%s", len(bidBytes), string(bidBytes))), nil +} + +func SerializeMessageForSigning(message *suave.DAMessage) ([]byte, error) { + msgBytes, err := json.Marshal(suave.DAMessage{ + SourceTx: message.SourceTx, + StoreWrites: message.StoreWrites, + StoreUUID: message.StoreUUID, + Signature: nil, + }) + if err != nil { + return []byte{}, err + } + + return []byte(fmt.Sprintf("\x19Suave Signed Message:\n%d%s", len(msgBytes), string(msgBytes))), nil +} + +type MockTransport struct{} + +func (MockTransport) Start() error { return nil } +func (MockTransport) Stop() error { return nil } + +func (MockTransport) Subscribe() (<-chan suave.DAMessage, context.CancelFunc) { + return nil, func() {} +} +func (MockTransport) Publish(suave.DAMessage) {} + +type MockSigner struct{} + +func (MockSigner) Sign(account common.Address, data []byte) ([]byte, error) { + return account.Bytes(), nil +} + +func (MockSigner) Sender(data []byte, signature []byte) (common.Address, error) { + return common.BytesToAddress(signature), nil +} + +func (MockSigner) LocalAddresses() []common.Address { + return []common.Address{} +} + +type MockChainSigner struct{} + +func (MockChainSigner) Sender(tx *types.Transaction) (common.Address, error) { + if tx == nil { + return common.Address{}, nil + } + + return types.NewSuaveSigner(tx.ChainId()).Sender(tx) +} + +func ExecutionNodeFromTransaction(tx *types.Transaction) (common.Address, error) { + innerExecutedTx, ok := types.CastTxInner[*types.SuaveTransaction](tx) + if ok { + return innerExecutedTx.ExecutionNode, nil + } + + innerRequestTx, ok := types.CastTxInner[*types.ConfidentialComputeRequest](tx) + if ok { + return innerRequestTx.ExecutionNode, nil + } + + return common.Address{}, fmt.Errorf("transaction is not of confidential type") +} + +var emptyId [16]byte + +var bidUuidSpace = uuid.UUID{0x42} + +func calculateBidId(bid types.Bid) (types.BidId, error) { + copy(bid.Id[:], emptyId[:]) + + body, err := json.Marshal(bid) + if err != nil { + return types.BidId{}, fmt.Errorf("could not marshal bid to calculate its id: %w", err) + } + + uuidv5 := uuid.NewSHA1(bidUuidSpace, body) + copy(bid.Id[:], uuidv5[:]) + + return bid.Id, nil +} diff --git a/suave/cstore/engine_test.go b/suave/cstore/engine_test.go new file mode 100644 index 0000000000..a0bf00bd8b --- /dev/null +++ b/suave/cstore/engine_test.go @@ -0,0 +1,126 @@ +package cstore + +import ( + "errors" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + suave "github.com/ethereum/go-ethereum/suave/core" + "github.com/google/uuid" + "github.com/stretchr/testify/require" +) + +type FakeDASigner struct { + localAddresses []common.Address +} + +func (FakeDASigner) Sign(account common.Address, data []byte) ([]byte, error) { + return account.Bytes(), nil +} +func (FakeDASigner) Sender(data []byte, signature []byte) (common.Address, error) { + return common.BytesToAddress(signature), nil +} +func (f FakeDASigner) LocalAddresses() []common.Address { return f.localAddresses } + +type FakeStoreBackend struct { + OnStore func(bid suave.Bid, caller common.Address, key string, value []byte) (suave.Bid, error) +} + +func (*FakeStoreBackend) Start() error { return nil } +func (*FakeStoreBackend) Stop() error { return nil } + +func (*FakeStoreBackend) InitializeBid(bid suave.Bid) error { return nil } +func (*FakeStoreBackend) FetchEngineBidById(bidId suave.BidId) (suave.Bid, error) { + return suave.Bid{}, errors.New("not implemented") +} + +func (b *FakeStoreBackend) Store(bid suave.Bid, caller common.Address, key string, value []byte) (suave.Bid, error) { + return b.OnStore(bid, caller, key, value) +} +func (*FakeStoreBackend) Retrieve(bid suave.Bid, caller common.Address, key string) ([]byte, error) { + return nil, errors.New("not implemented") +} + +func (*FakeStoreBackend) FetchBidById(suave.BidId) (suave.Bid, error) { + return suave.Bid{}, nil +} + +func (*FakeStoreBackend) FetchBidsByProtocolAndBlock(blockNumber uint64, namespace string) []suave.Bid { + return nil +} + +func (*FakeStoreBackend) SubmitBid(types.Bid) error { + return nil +} + +func TestOwnMessageDropping(t *testing.T) { + var wasCalled *bool = new(bool) + fakeStore := FakeStoreBackend{OnStore: func(bid suave.Bid, caller common.Address, key string, value []byte) (suave.Bid, error) { + *wasCalled = true + return bid, nil + }} + + fakeDaSigner := FakeDASigner{localAddresses: []common.Address{{0x42}}} + engine := NewConfidentialStoreEngine(&fakeStore, MockTransport{}, fakeDaSigner, MockChainSigner{}) + + testKey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + // testKeyAddress := crypto.PubkeyToAddress(testKey.PublicKey) + dummyCreationTx, err := types.SignTx(types.NewTx(&types.ConfidentialComputeRequest{ + ConfidentialComputeRecord: types.ConfidentialComputeRecord{ + ExecutionNode: common.Address{0x42}, + }, + }), types.NewSuaveSigner(new(big.Int)), testKey) + require.NoError(t, err) + + bidId, err := calculateBidId(types.Bid{ + AllowedStores: []common.Address{{0x42}}, + AllowedPeekers: []common.Address{{}}, + }) + require.NoError(t, err) + testBid := suave.Bid{ + Id: bidId, + CreationTx: dummyCreationTx, + AllowedStores: []common.Address{{0x42}}, + AllowedPeekers: []common.Address{{}}, + } + + testBidBytes, err := SerializeBidForSigning(&testBid) + require.NoError(t, err) + + testBid.Signature, err = fakeDaSigner.Sign(common.Address{0x42}, testBidBytes) + require.NoError(t, err) + + *wasCalled = false + + daMessage := suave.DAMessage{ + SourceTx: dummyCreationTx, + StoreUUID: engine.storeUUID, + StoreWrites: []suave.StoreWrite{{Bid: testBid}}, + } + + daMessageBytes, err := SerializeMessageForSigning(&daMessage) + require.NoError(t, err) + + daMessage.Signature, err = fakeDaSigner.Sign(common.Address{0x42}, daMessageBytes) + require.NoError(t, err) + + *wasCalled = false + err = engine.NewMessage(daMessage) + require.NoError(t, err) + // require.True(t, *wasCalled) + + daMessage.StoreUUID = uuid.New() + daMessageBytes, err = SerializeMessageForSigning(&daMessage) + require.NoError(t, err) + + daMessage.Signature, err = fakeDaSigner.Sign(common.Address{0x42}, daMessageBytes) + require.NoError(t, err) + + *wasCalled = false + err = engine.NewMessage(daMessage) + require.NoError(t, err) + require.True(t, *wasCalled) +} diff --git a/suave/backends/local_store_backend.go b/suave/cstore/local_store_backend.go similarity index 99% rename from suave/backends/local_store_backend.go rename to suave/cstore/local_store_backend.go index 6ed97cd7b7..1a147a4a76 100644 --- a/suave/backends/local_store_backend.go +++ b/suave/cstore/local_store_backend.go @@ -1,4 +1,4 @@ -package backends +package cstore import ( "errors" diff --git a/suave/backends/local_store_backend_test.go b/suave/cstore/local_store_backend_test.go similarity index 88% rename from suave/backends/local_store_backend_test.go rename to suave/cstore/local_store_backend_test.go index 9da3bb1e35..8d7b6be29e 100644 --- a/suave/backends/local_store_backend_test.go +++ b/suave/cstore/local_store_backend_test.go @@ -1,4 +1,4 @@ -package backends +package cstore import ( "testing" diff --git a/suave/backends/redis_backends_test.go b/suave/cstore/redis_backends_test.go similarity index 99% rename from suave/backends/redis_backends_test.go rename to suave/cstore/redis_backends_test.go index 6c9ed81ade..fc0d399633 100644 --- a/suave/backends/redis_backends_test.go +++ b/suave/cstore/redis_backends_test.go @@ -1,4 +1,4 @@ -package backends +package cstore import ( "encoding/json" diff --git a/suave/backends/redis_store_backend.go b/suave/cstore/redis_store_backend.go similarity index 99% rename from suave/backends/redis_store_backend.go rename to suave/cstore/redis_store_backend.go index d108ca7909..268b77c30f 100644 --- a/suave/backends/redis_store_backend.go +++ b/suave/cstore/redis_store_backend.go @@ -1,4 +1,4 @@ -package backends +package cstore import ( "context" diff --git a/suave/backends/redis_store_backend_test.go b/suave/cstore/redis_store_backend_test.go similarity index 92% rename from suave/backends/redis_store_backend_test.go rename to suave/cstore/redis_store_backend_test.go index 07db156de7..17431c4a59 100644 --- a/suave/backends/redis_store_backend_test.go +++ b/suave/cstore/redis_store_backend_test.go @@ -1,4 +1,4 @@ -package backends +package cstore import ( "testing" diff --git a/suave/backends/redis_transport.go b/suave/cstore/redis_transport.go similarity index 99% rename from suave/backends/redis_transport.go rename to suave/cstore/redis_transport.go index 542fe23355..41261e60d5 100644 --- a/suave/backends/redis_transport.go +++ b/suave/cstore/redis_transport.go @@ -1,4 +1,4 @@ -package backends +package cstore import ( "context" diff --git a/suave/cstore/transactional_store.go b/suave/cstore/transactional_store.go new file mode 100644 index 0000000000..fc69cdc05c --- /dev/null +++ b/suave/cstore/transactional_store.go @@ -0,0 +1,114 @@ +package cstore + +import ( + "errors" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + suave "github.com/ethereum/go-ethereum/suave/core" + "golang.org/x/exp/slices" +) + +type TransactionalStore struct { + sourceTx *types.Transaction + engine *ConfidentialStoreEngine + + pendingLock sync.Mutex + pendingBids map[suave.BidId]suave.Bid + pendingWrites []suave.StoreWrite +} + +func (s *TransactionalStore) FetchBidById(bidId suave.BidId) (suave.Bid, error) { + s.pendingLock.Lock() + bid, ok := s.pendingBids[bidId] + s.pendingLock.Unlock() + + if ok { + return bid, nil + } + + return s.engine.FetchBidById(bidId) +} + +func (s *TransactionalStore) FetchBidsByProtocolAndBlock(blockNumber uint64, namespace string) []suave.Bid { + bids := s.engine.FetchBidsByProtocolAndBlock(blockNumber, namespace) + + s.pendingLock.Lock() + defer s.pendingLock.Unlock() + for _, bid := range s.pendingBids { + if bid.Version == namespace && bid.DecryptionCondition == blockNumber { + bids = append(bids, bid) + } + } + + return bids +} + +func (s *TransactionalStore) Store(bidId suave.BidId, caller common.Address, key string, value []byte) (suave.Bid, error) { + bid, err := s.FetchBidById(bidId) + if err != nil { + return suave.Bid{}, err + } + + if !slices.Contains(bid.AllowedPeekers, caller) { + return suave.Bid{}, fmt.Errorf("confidential store transaction: %x not allowed to store %s on %x", caller, key, bidId) + } + + s.pendingLock.Lock() + defer s.pendingLock.Unlock() + s.pendingWrites = append(s.pendingWrites, suave.StoreWrite{ + Bid: bid, + Caller: caller, + Key: key, + Value: common.CopyBytes(value), + }) + + return bid, nil +} + +func (s *TransactionalStore) Retrieve(bidId suave.BidId, caller common.Address, key string) ([]byte, error) { + bid, err := s.FetchBidById(bidId) + if err != nil { + return nil, err + } + + if !slices.Contains(bid.AllowedPeekers, caller) { + return nil, fmt.Errorf("confidential store transaction: %x not allowed to retrieve %s on %x", caller, key, bidId) + } + + s.pendingLock.Lock() + + for _, sw := range s.pendingWrites { + if sw.Bid.Id == bid.Id && sw.Key == key { + s.pendingLock.Unlock() + return common.CopyBytes(sw.Value), nil + } + } + + s.pendingLock.Unlock() + return s.engine.Retrieve(bidId, caller, key) +} + +func (s *TransactionalStore) InitializeBid(rawBid types.Bid) (types.Bid, error) { + bid, err := s.engine.InitializeBid(rawBid, s.sourceTx) + if err != nil { + return types.Bid{}, err + } + + s.pendingLock.Lock() + _, found := s.pendingBids[bid.Id] + if found { + s.pendingLock.Unlock() + return types.Bid{}, errors.New("bid with this id already exists") + } + s.pendingBids[bid.Id] = bid + s.pendingLock.Unlock() + + return bid.ToInnerBid(), nil +} + +func (s *TransactionalStore) Finalize() error { + return s.engine.Finalize(s.sourceTx, s.pendingBids, s.pendingWrites) +} diff --git a/suave/backends/transactional_store_test.go b/suave/cstore/transactional_store_test.go similarity index 99% rename from suave/backends/transactional_store_test.go rename to suave/cstore/transactional_store_test.go index 251b5d90a5..4dbe153efc 100644 --- a/suave/backends/transactional_store_test.go +++ b/suave/cstore/transactional_store_test.go @@ -1,4 +1,4 @@ -package backends +package cstore import ( "math/big" From 579e0006799fde99ac24016cd2444521c06d1a0b Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Thu, 19 Oct 2023 12:06:56 +0200 Subject: [PATCH 2/4] Move core types to cstore --- core/vm/contracts_suave_test.go | 12 +- eth/api_backend.go | 5 +- eth/backend.go | 15 +- suave/core/engine.go | 410 ----------------------- suave/core/engine_test.go | 125 ------- suave/core/transactional_store.go | 113 ------- suave/core/types.go | 31 -- suave/cstore/engine.go | 57 +++- suave/cstore/engine_test.go | 4 +- suave/cstore/redis_backends_test.go | 10 +- suave/cstore/redis_transport.go | 9 +- suave/cstore/transactional_store.go | 4 +- suave/cstore/transactional_store_test.go | 4 +- suave/e2e/workflow_test.go | 5 +- 14 files changed, 81 insertions(+), 723 deletions(-) delete mode 100644 suave/core/engine.go delete mode 100644 suave/core/engine_test.go delete mode 100644 suave/core/transactional_store.go diff --git a/core/vm/contracts_suave_test.go b/core/vm/contracts_suave_test.go index f232564f60..f86bb8493d 100644 --- a/core/vm/contracts_suave_test.go +++ b/core/vm/contracts_suave_test.go @@ -14,8 +14,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/suave/artifacts" - "github.com/ethereum/go-ethereum/suave/backends" suave "github.com/ethereum/go-ethereum/suave/core" + "github.com/ethereum/go-ethereum/suave/cstore" "github.com/stretchr/testify/require" ) @@ -61,11 +61,11 @@ func (m *mockSuaveBackend) BuildEthBlockFromBundles(ctx context.Context, args *s return nil, nil } -func (m *mockSuaveBackend) Subscribe() (<-chan suave.DAMessage, context.CancelFunc) { +func (m *mockSuaveBackend) Subscribe() (<-chan cstore.DAMessage, context.CancelFunc) { return nil, func() {} } -func (m *mockSuaveBackend) Publish(suave.DAMessage) {} +func (m *mockSuaveBackend) Publish(cstore.DAMessage) {} var dummyBlockContext = BlockContext{ CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, @@ -77,7 +77,7 @@ func TestSuavePrecompileStub(t *testing.T) { // This test ensures that the Suave precompile stubs work as expected // for encoding/decoding. mockSuaveBackend := &mockSuaveBackend{} - stubEngine := suave.NewConfidentialStoreEngine(mockSuaveBackend, mockSuaveBackend, suave.MockSigner{}, suave.MockChainSigner{}) + stubEngine := cstore.NewConfidentialStoreEngine(mockSuaveBackend, mockSuaveBackend, cstore.MockSigner{}, cstore.MockChainSigner{}) reqTx := types.NewTx(&types.ConfidentialComputeRequest{ ConfidentialComputeRecord: types.ConfidentialComputeRecord{ @@ -142,8 +142,8 @@ func TestSuavePrecompileStub(t *testing.T) { } func newTestBackend(t *testing.T) *suaveRuntime { - confStore := backends.NewLocalConfidentialStore() - confEngine := suave.NewConfidentialStoreEngine(confStore, &suave.MockTransport{}, suave.MockSigner{}, suave.MockChainSigner{}) + confStore := cstore.NewLocalConfidentialStore() + confEngine := cstore.NewConfidentialStoreEngine(confStore, &cstore.MockTransport{}, cstore.MockSigner{}, cstore.MockChainSigner{}) require.NoError(t, confEngine.Start()) t.Cleanup(func() { confEngine.Stop() }) diff --git a/eth/api_backend.go b/eth/api_backend.go index 3e5cade3e1..dd9cf068f6 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -41,6 +41,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" suave "github.com/ethereum/go-ethereum/suave/core" + "github.com/ethereum/go-ethereum/suave/cstore" ) // EthAPIBackend implements ethapi.Backend for full nodes @@ -49,12 +50,12 @@ type EthAPIBackend struct { allowUnprotectedTxs bool eth *Ethereum gpo *gasprice.Oracle - suaveEngine *suave.ConfidentialStoreEngine + suaveEngine *cstore.ConfidentialStoreEngine suaveEthBackend suave.ConfidentialEthBackend } // For testing purposes -func (b *EthAPIBackend) SuaveEngine() *suave.ConfidentialStoreEngine { +func (b *EthAPIBackend) SuaveEngine() *cstore.ConfidentialStoreEngine { return b.suaveEngine } diff --git a/eth/backend.go b/eth/backend.go index d0cf690ca4..cf6bb44a4c 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -58,6 +58,7 @@ import ( "github.com/ethereum/go-ethereum/suave/backends" suave_backends "github.com/ethereum/go-ethereum/suave/backends" suave "github.com/ethereum/go-ethereum/suave/core" + "github.com/ethereum/go-ethereum/suave/cstore" ) // Config contains the configuration options of the ETH protocol. @@ -232,16 +233,16 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { var confidentialStoreBackend suave.ConfidentialStoreBackend if config.Suave.RedisStoreUri != "" { - confidentialStoreBackend = suave_backends.NewRedisStoreBackend(config.Suave.RedisStoreUri) + confidentialStoreBackend = cstore.NewRedisStoreBackend(config.Suave.RedisStoreUri) } else { - confidentialStoreBackend = suave_backends.NewLocalConfidentialStore() + confidentialStoreBackend = cstore.NewLocalConfidentialStore() } - var confidentialStoreTransport suave.StoreTransportTopic + var confidentialStoreTransport cstore.StoreTransportTopic if config.Suave.RedisStorePubsubUri != "" { - confidentialStoreTransport = suave_backends.NewRedisPubSubTransport(config.Suave.RedisStorePubsubUri) + confidentialStoreTransport = cstore.NewRedisPubSubTransport(config.Suave.RedisStorePubsubUri) } else { - confidentialStoreTransport = suave.MockTransport{} + confidentialStoreTransport = cstore.MockTransport{} } var suaveEthBackend suave.ConfidentialEthBackend @@ -251,9 +252,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { suaveEthBackend = &suave_backends.EthMock{} } - suaveDaSigner := &suave_backends.AccountManagerDASigner{Manager: eth.AccountManager()} + suaveDaSigner := &cstore.AccountManagerDASigner{Manager: eth.AccountManager()} - confidentialStoreEngine := suave.NewConfidentialStoreEngine(confidentialStoreBackend, confidentialStoreTransport, suaveDaSigner, types.LatestSigner(chainConfig)) + confidentialStoreEngine := cstore.NewConfidentialStoreEngine(confidentialStoreBackend, confidentialStoreTransport, suaveDaSigner, types.LatestSigner(chainConfig)) eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil, confidentialStoreEngine, suaveEthBackend} if eth.APIBackend.allowUnprotectedTxs { diff --git a/suave/core/engine.go b/suave/core/engine.go deleted file mode 100644 index 49bc91484e..0000000000 --- a/suave/core/engine.go +++ /dev/null @@ -1,410 +0,0 @@ -package suave - -import ( - "context" - "encoding/json" - "errors" - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/log" - "github.com/google/uuid" - "golang.org/x/exp/slices" -) - -type ConfidentialStoreEngine struct { - ctx context.Context - cancel context.CancelFunc - - backend ConfidentialStoreBackend - transportTopic StoreTransportTopic - - daSigner DASigner - chainSigner ChainSigner - - storeUUID uuid.UUID - localAddresses map[common.Address]struct{} -} - -func NewConfidentialStoreEngine(backend ConfidentialStoreBackend, transportTopic StoreTransportTopic, daSigner DASigner, chainSigner ChainSigner) *ConfidentialStoreEngine { - localAddresses := make(map[common.Address]struct{}) - for _, addr := range daSigner.LocalAddresses() { - localAddresses[addr] = struct{}{} - } - - return &ConfidentialStoreEngine{ - backend: backend, - transportTopic: transportTopic, - daSigner: daSigner, - chainSigner: chainSigner, - storeUUID: uuid.New(), - localAddresses: localAddresses, - } -} - -func (e *ConfidentialStoreEngine) NewTransactionalStore(sourceTx *types.Transaction) *TransactionalStore { - return &TransactionalStore{ - sourceTx: sourceTx, - engine: e, - pendingBids: make(map[BidId]Bid), - } -} - -func (e *ConfidentialStoreEngine) Start() error { - if err := e.backend.Start(); err != nil { - return err - } - - if err := e.transportTopic.Start(); err != nil { - return err - } - - if e.cancel != nil { - e.cancel() - } - - ctx, cancel := context.WithCancel(context.Background()) - e.cancel = cancel - e.ctx = ctx - go e.ProcessMessages() - - return nil -} - -func (e *ConfidentialStoreEngine) Stop() error { - if e.cancel == nil { - return errors.New("Confidential engine: Stop() called before Start()") - } - - e.cancel() - - if err := e.transportTopic.Stop(); err != nil { - log.Warn("Confidential engine: error while stopping transport", "err", err) - } - - if err := e.backend.Stop(); err != nil { - log.Warn("Confidential engine: error while stopping transport", "err", err) - } - - return nil -} - -// For testing purposes! -func (e *ConfidentialStoreEngine) Backend() ConfidentialStoreBackend { - return e.backend -} - -func (e *ConfidentialStoreEngine) ProcessMessages() { - ch, cancel := e.transportTopic.Subscribe() - defer cancel() - - for { - select { - case <-e.ctx.Done(): // Stop() called - return - case msg := <-ch: - err := e.NewMessage(msg) - if err != nil { - log.Info("could not process new store message", "err", err) - } else { - log.Info("Message processed", "msg", msg) - } - } - } -} - -func (e *ConfidentialStoreEngine) InitializeBid(bid types.Bid, creationTx *types.Transaction) (Bid, error) { - expectedId, err := calculateBidId(bid) - if err != nil { - return Bid{}, fmt.Errorf("confidential engine: could not initialize new bid: %w", err) - } - - if bid.Id == emptyId { - bid.Id = expectedId - } else if bid.Id != expectedId { - // True in some tests, might be time to rewrite them - return Bid{}, errors.New("confidential engine:incorrect bid id passed") - } - - initializedBid := Bid{ - Id: bid.Id, - Salt: bid.Salt, - DecryptionCondition: bid.DecryptionCondition, - AllowedPeekers: bid.AllowedPeekers, - AllowedStores: bid.AllowedStores, - Version: bid.Version, - CreationTx: creationTx, - } - - bidBytes, err := SerializeBidForSigning(&initializedBid) - if err != nil { - return Bid{}, fmt.Errorf("confidential engine: could not hash bid for signing: %w", err) - } - - signingAccount, err := ExecutionNodeFromTransaction(creationTx) - if err != nil { - return Bid{}, fmt.Errorf("confidential engine: could not recover execution node from creation transaction: %w", err) - } - - initializedBid.Signature, err = e.daSigner.Sign(signingAccount, bidBytes) - if err != nil { - return Bid{}, fmt.Errorf("confidential engine: could not sign initialized bid: %w", err) - } - - return initializedBid, nil -} - -func (e *ConfidentialStoreEngine) FetchBidById(bidId BidId) (Bid, error) { - return e.backend.FetchBidById(bidId) -} - -func (e *ConfidentialStoreEngine) FetchBidsByProtocolAndBlock(blockNumber uint64, namespace string) []Bid { - return e.backend.FetchBidsByProtocolAndBlock(blockNumber, namespace) -} - -func (e *ConfidentialStoreEngine) Retrieve(bidId BidId, caller common.Address, key string) ([]byte, error) { - bid, err := e.backend.FetchBidById(bidId) - if err != nil { - return []byte{}, fmt.Errorf("confidential engine: could not fetch bid %x while retrieving: %w", bidId, err) - } - - if !slices.Contains(bid.AllowedPeekers, caller) { - return []byte{}, fmt.Errorf("confidential engine: %x not allowed to retrieve %s on %x", caller, key, bidId) - } - - return e.backend.Retrieve(bid, caller, key) -} - -func (e *ConfidentialStoreEngine) Finalize(tx *types.Transaction, newBids map[BidId]Bid, stores []StoreWrite) error { - // - for _, bid := range newBids { - err := e.backend.InitializeBid(bid) - if err != nil { - // TODO: deinitialize! - return fmt.Errorf("confidential engine: store backend failed to initialize bid: %w", err) - } - } - - for _, sw := range stores { - if _, err := e.backend.Store(sw.Bid, sw.Caller, sw.Key, sw.Value); err != nil { - // TODO: deinitialize and deStore! - return fmt.Errorf("failed to store data: %w", err) - } - } - - // Sign and propagate the message - pwMsg := DAMessage{ - SourceTx: tx, - StoreWrites: stores, - StoreUUID: e.storeUUID, - } - - if _, sigErr := e.chainSigner.Sender(tx); sigErr != nil { - log.Info("confidential engine: refusing to send writes based on unsigned transaction", "hash", tx.Hash().Hex(), "err", sigErr) - return ErrUnsignedFinalize - } - - msgBytes, err := SerializeMessageForSigning(&pwMsg) - if err != nil { - return fmt.Errorf("confidential engine: could not hash message for signing: %w", err) - } - - signingAccount, err := ExecutionNodeFromTransaction(tx) - if err != nil { - return fmt.Errorf("confidential engine: could not recover execution node from source transaction: %w", err) - } - - pwMsg.Signature, err = e.daSigner.Sign(signingAccount, msgBytes) - if err != nil { - return fmt.Errorf("confidential engine: could not sign message: %w", err) - } - - // TODO: avoid marshalling twice - go e.transportTopic.Publish(pwMsg) - - return nil -} - -func (e *ConfidentialStoreEngine) NewMessage(message DAMessage) error { - // Note the validation is a work in progress and not guaranteed to be correct! - - // Message-level validation - msgBytes, err := SerializeMessageForSigning(&message) - if err != nil { - return fmt.Errorf("confidential engine: could not hash received message: %w", err) - } - recoveredMessageSigner, err := e.daSigner.Sender(msgBytes, message.Signature) - if err != nil { - return fmt.Errorf("confidential engine: incorrect message signature: %w", err) - } - expectedMessageSigner, err := ExecutionNodeFromTransaction(message.SourceTx) - if err != nil { - return fmt.Errorf("confidential engine: could not recover signer from message: %w", err) - } - if recoveredMessageSigner != expectedMessageSigner { - return fmt.Errorf("confidential engine: message signer %x, expected %x", recoveredMessageSigner, expectedMessageSigner) - } - - if message.StoreUUID == e.storeUUID { - if _, found := e.localAddresses[recoveredMessageSigner]; found { - return nil - } - // Message from self! - log.Info("Confidential engine: message is spoofing our storeUUID, processing anyway", "message", message) - } - - _, err = e.chainSigner.Sender(message.SourceTx) - if err != nil { - return fmt.Errorf("confidential engine: source tx for message is not signed properly: %w", err) - } - - // TODO: check if message.SourceTx is valid and insert it into the mempool! - - // Bid level validation - - for _, sw := range message.StoreWrites { - expectedId, err := calculateBidId(types.Bid{ - Id: sw.Bid.Id, - Salt: sw.Bid.Salt, - DecryptionCondition: sw.Bid.DecryptionCondition, - AllowedPeekers: sw.Bid.AllowedPeekers, - AllowedStores: sw.Bid.AllowedStores, - Version: sw.Bid.Version, - }) - if err != nil { - return fmt.Errorf("confidential engine: could not calculate received bids id: %w", err) - } - - if expectedId != sw.Bid.Id { - return fmt.Errorf("confidential engine: received bids id (%x) does not match the expected (%x)", sw.Bid.Id, expectedId) - } - - bidBytes, err := SerializeBidForSigning(&sw.Bid) - if err != nil { - return fmt.Errorf("confidential engine: could not hash received bid: %w", err) - } - recoveredBidSigner, err := e.daSigner.Sender(bidBytes, sw.Bid.Signature) - if err != nil { - return fmt.Errorf("confidential engine: incorrect bid signature: %w", err) - } - expectedBidSigner, err := ExecutionNodeFromTransaction(sw.Bid.CreationTx) - if err != nil { - return fmt.Errorf("confidential engine: could not recover signer from bid: %w", err) - } - if recoveredBidSigner != expectedBidSigner { - return fmt.Errorf("confidential engine: bid signer %x, expected %x", recoveredBidSigner, expectedBidSigner) - } - - if !slices.Contains(sw.Bid.AllowedStores, recoveredMessageSigner) { - return fmt.Errorf("confidential engine: sw signer %x not allowed to store on bid %x", recoveredMessageSigner, sw.Bid.Id) - } - - if !slices.Contains(sw.Bid.AllowedPeekers, sw.Caller) { - return fmt.Errorf("confidential engine: caller %x not allowed on bid %x", sw.Caller, sw.Bid.Id) - } - - // TODO: move to types.Sender() - _, err = e.chainSigner.Sender(sw.Bid.CreationTx) - if err != nil { - return fmt.Errorf("confidential engine: creation tx for bid id %x is not signed properly: %w", sw.Bid.Id, err) - } - } - - for _, sw := range message.StoreWrites { - err = e.backend.InitializeBid(sw.Bid) - if err != nil { - if !errors.Is(err, ErrBidAlreadyPresent) { - log.Error("confidential engine: unexpected error while initializing bid from transport: %w", err) - continue // Don't abandon! - } - } - - _, err = e.backend.Store(sw.Bid, sw.Caller, sw.Key, sw.Value) - if err != nil { - log.Error("confidential engine: unexpected error while storing: %w", err) - continue // Don't abandon! - } - } - - return nil -} - -func SerializeBidForSigning(bid *Bid) ([]byte, error) { - bidBytes, err := json.Marshal(Bid{ - Id: bid.Id, - Salt: bid.Salt, - DecryptionCondition: bid.DecryptionCondition, - AllowedPeekers: bid.AllowedPeekers, - AllowedStores: bid.AllowedStores, - Version: bid.Version, - CreationTx: bid.CreationTx, - }) - if err != nil { - return []byte{}, err - } - - return []byte(fmt.Sprintf("\x19Suave Signed Message:\n%d%s", len(bidBytes), string(bidBytes))), nil -} - -func SerializeMessageForSigning(message *DAMessage) ([]byte, error) { - msgBytes, err := json.Marshal(DAMessage{ - SourceTx: message.SourceTx, - StoreWrites: message.StoreWrites, - StoreUUID: message.StoreUUID, - Signature: nil, - }) - if err != nil { - return []byte{}, err - } - - return []byte(fmt.Sprintf("\x19Suave Signed Message:\n%d%s", len(msgBytes), string(msgBytes))), nil -} - -type MockTransport struct{} - -func (MockTransport) Start() error { return nil } -func (MockTransport) Stop() error { return nil } - -func (MockTransport) Subscribe() (<-chan DAMessage, context.CancelFunc) { - return nil, func() {} -} -func (MockTransport) Publish(DAMessage) {} - -type MockSigner struct{} - -func (MockSigner) Sign(account common.Address, data []byte) ([]byte, error) { - return account.Bytes(), nil -} - -func (MockSigner) Sender(data []byte, signature []byte) (common.Address, error) { - return common.BytesToAddress(signature), nil -} - -func (MockSigner) LocalAddresses() []common.Address { - return []common.Address{} -} - -type MockChainSigner struct{} - -func (MockChainSigner) Sender(tx *types.Transaction) (common.Address, error) { - if tx == nil { - return common.Address{}, nil - } - - return types.NewSuaveSigner(tx.ChainId()).Sender(tx) -} - -func ExecutionNodeFromTransaction(tx *types.Transaction) (common.Address, error) { - innerExecutedTx, ok := types.CastTxInner[*types.SuaveTransaction](tx) - if ok { - return innerExecutedTx.ExecutionNode, nil - } - - innerRequestTx, ok := types.CastTxInner[*types.ConfidentialComputeRequest](tx) - if ok { - return innerRequestTx.ExecutionNode, nil - } - - return common.Address{}, fmt.Errorf("transaction is not of confidential type") -} diff --git a/suave/core/engine_test.go b/suave/core/engine_test.go deleted file mode 100644 index 9466d2e413..0000000000 --- a/suave/core/engine_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package suave - -import ( - "errors" - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/google/uuid" - "github.com/stretchr/testify/require" -) - -type FakeDASigner struct { - localAddresses []common.Address -} - -func (FakeDASigner) Sign(account common.Address, data []byte) ([]byte, error) { - return account.Bytes(), nil -} -func (FakeDASigner) Sender(data []byte, signature []byte) (common.Address, error) { - return common.BytesToAddress(signature), nil -} -func (f FakeDASigner) LocalAddresses() []common.Address { return f.localAddresses } - -type FakeStoreBackend struct { - OnStore func(bid Bid, caller common.Address, key string, value []byte) (Bid, error) -} - -func (*FakeStoreBackend) Start() error { return nil } -func (*FakeStoreBackend) Stop() error { return nil } - -func (*FakeStoreBackend) InitializeBid(bid Bid) error { return nil } -func (*FakeStoreBackend) FetchEngineBidById(bidId BidId) (Bid, error) { - return Bid{}, errors.New("not implemented") -} - -func (b *FakeStoreBackend) Store(bid Bid, caller common.Address, key string, value []byte) (Bid, error) { - return b.OnStore(bid, caller, key, value) -} -func (*FakeStoreBackend) Retrieve(bid Bid, caller common.Address, key string) ([]byte, error) { - return nil, errors.New("not implemented") -} - -func (*FakeStoreBackend) FetchBidById(BidId) (Bid, error) { - return Bid{}, nil -} - -func (*FakeStoreBackend) FetchBidsByProtocolAndBlock(blockNumber uint64, namespace string) []Bid { - return nil -} - -func (*FakeStoreBackend) SubmitBid(types.Bid) error { - return nil -} - -func TestOwnMessageDropping(t *testing.T) { - var wasCalled *bool = new(bool) - fakeStore := FakeStoreBackend{OnStore: func(bid Bid, caller common.Address, key string, value []byte) (Bid, error) { - *wasCalled = true - return bid, nil - }} - - fakeDaSigner := FakeDASigner{localAddresses: []common.Address{{0x42}}} - engine := NewConfidentialStoreEngine(&fakeStore, MockTransport{}, fakeDaSigner, MockChainSigner{}) - - testKey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - // testKeyAddress := crypto.PubkeyToAddress(testKey.PublicKey) - dummyCreationTx, err := types.SignTx(types.NewTx(&types.ConfidentialComputeRequest{ - ConfidentialComputeRecord: types.ConfidentialComputeRecord{ - ExecutionNode: common.Address{0x42}, - }, - }), types.NewSuaveSigner(new(big.Int)), testKey) - require.NoError(t, err) - - bidId, err := calculateBidId(types.Bid{ - AllowedStores: []common.Address{{0x42}}, - AllowedPeekers: []common.Address{{}}, - }) - require.NoError(t, err) - testBid := Bid{ - Id: bidId, - CreationTx: dummyCreationTx, - AllowedStores: []common.Address{{0x42}}, - AllowedPeekers: []common.Address{{}}, - } - - testBidBytes, err := SerializeBidForSigning(&testBid) - require.NoError(t, err) - - testBid.Signature, err = fakeDaSigner.Sign(common.Address{0x42}, testBidBytes) - require.NoError(t, err) - - *wasCalled = false - - daMessage := DAMessage{ - SourceTx: dummyCreationTx, - StoreUUID: engine.storeUUID, - StoreWrites: []StoreWrite{{Bid: testBid}}, - } - - daMessageBytes, err := SerializeMessageForSigning(&daMessage) - require.NoError(t, err) - - daMessage.Signature, err = fakeDaSigner.Sign(common.Address{0x42}, daMessageBytes) - require.NoError(t, err) - - *wasCalled = false - err = engine.NewMessage(daMessage) - require.NoError(t, err) - // require.True(t, *wasCalled) - - daMessage.StoreUUID = uuid.New() - daMessageBytes, err = SerializeMessageForSigning(&daMessage) - require.NoError(t, err) - - daMessage.Signature, err = fakeDaSigner.Sign(common.Address{0x42}, daMessageBytes) - require.NoError(t, err) - - *wasCalled = false - err = engine.NewMessage(daMessage) - require.NoError(t, err) - require.True(t, *wasCalled) -} diff --git a/suave/core/transactional_store.go b/suave/core/transactional_store.go deleted file mode 100644 index 1709c77f31..0000000000 --- a/suave/core/transactional_store.go +++ /dev/null @@ -1,113 +0,0 @@ -package suave - -import ( - "errors" - "fmt" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "golang.org/x/exp/slices" -) - -type TransactionalStore struct { - sourceTx *types.Transaction - engine *ConfidentialStoreEngine - - pendingLock sync.Mutex - pendingBids map[BidId]Bid - pendingWrites []StoreWrite -} - -func (s *TransactionalStore) FetchBidById(bidId BidId) (Bid, error) { - s.pendingLock.Lock() - bid, ok := s.pendingBids[bidId] - s.pendingLock.Unlock() - - if ok { - return bid, nil - } - - return s.engine.FetchBidById(bidId) -} - -func (s *TransactionalStore) FetchBidsByProtocolAndBlock(blockNumber uint64, namespace string) []Bid { - bids := s.engine.FetchBidsByProtocolAndBlock(blockNumber, namespace) - - s.pendingLock.Lock() - defer s.pendingLock.Unlock() - for _, bid := range s.pendingBids { - if bid.Version == namespace && bid.DecryptionCondition == blockNumber { - bids = append(bids, bid) - } - } - - return bids -} - -func (s *TransactionalStore) Store(bidId BidId, caller common.Address, key string, value []byte) (Bid, error) { - bid, err := s.FetchBidById(bidId) - if err != nil { - return Bid{}, err - } - - if !slices.Contains(bid.AllowedPeekers, caller) { - return Bid{}, fmt.Errorf("confidential store transaction: %x not allowed to store %s on %x", caller, key, bidId) - } - - s.pendingLock.Lock() - defer s.pendingLock.Unlock() - s.pendingWrites = append(s.pendingWrites, StoreWrite{ - Bid: bid, - Caller: caller, - Key: key, - Value: common.CopyBytes(value), - }) - - return bid, nil -} - -func (s *TransactionalStore) Retrieve(bidId BidId, caller common.Address, key string) ([]byte, error) { - bid, err := s.FetchBidById(bidId) - if err != nil { - return nil, err - } - - if !slices.Contains(bid.AllowedPeekers, caller) { - return nil, fmt.Errorf("confidential store transaction: %x not allowed to retrieve %s on %x", caller, key, bidId) - } - - s.pendingLock.Lock() - - for _, sw := range s.pendingWrites { - if sw.Bid.Id == bid.Id && sw.Key == key { - s.pendingLock.Unlock() - return common.CopyBytes(sw.Value), nil - } - } - - s.pendingLock.Unlock() - return s.engine.Retrieve(bidId, caller, key) -} - -func (s *TransactionalStore) InitializeBid(rawBid types.Bid) (types.Bid, error) { - bid, err := s.engine.InitializeBid(rawBid, s.sourceTx) - if err != nil { - return types.Bid{}, err - } - - s.pendingLock.Lock() - _, found := s.pendingBids[bid.Id] - if found { - s.pendingLock.Unlock() - return types.Bid{}, errors.New("bid with this id already exists") - } - s.pendingBids[bid.Id] = bid - s.pendingLock.Unlock() - - return bid.ToInnerBid(), nil -} - -func (s *TransactionalStore) Finalize() error { - return s.engine.Finalize(s.sourceTx, s.pendingBids, s.pendingWrites) -} diff --git a/suave/core/types.go b/suave/core/types.go index 89c5d9e238..e1694b81ce 100644 --- a/suave/core/types.go +++ b/suave/core/types.go @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/node" - "github.com/google/uuid" ) type Bytes = hexutil.Bytes @@ -48,16 +47,6 @@ var ( ErrUnsignedFinalize = errors.New("finalize called with unsigned transaction, refusing to propagate") ) -type DASigner interface { - Sign(account common.Address, data []byte) ([]byte, error) - Sender(data []byte, signature []byte) (common.Address, error) - LocalAddresses() []common.Address -} - -type ChainSigner interface { - Sender(tx *types.Transaction) (common.Address, error) -} - type ConfidentialStoreBackend interface { node.Lifecycle @@ -72,23 +61,3 @@ type ConfidentialEthBackend interface { BuildEthBlock(ctx context.Context, args *BuildBlockArgs, txs types.Transactions) (*engine.ExecutionPayloadEnvelope, error) BuildEthBlockFromBundles(ctx context.Context, args *BuildBlockArgs, bundles []types.SBundle) (*engine.ExecutionPayloadEnvelope, error) } - -type StoreTransportTopic interface { - node.Lifecycle - Subscribe() (<-chan DAMessage, context.CancelFunc) - Publish(DAMessage) -} - -type DAMessage struct { - SourceTx *types.Transaction `json:"sourceTx"` - StoreWrites []StoreWrite `json:"storeWrites"` - StoreUUID uuid.UUID `json:"storeUUID"` - Signature Bytes `json:"signature"` -} - -type StoreWrite struct { - Bid Bid `json:"bid"` - Caller common.Address `json:"caller"` - Key string `json:"key"` - Value Bytes `json:"value"` -} diff --git a/suave/cstore/engine.go b/suave/cstore/engine.go index 9970ca1e71..d25870f36f 100644 --- a/suave/cstore/engine.go +++ b/suave/cstore/engine.go @@ -9,26 +9,57 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" suave "github.com/ethereum/go-ethereum/suave/core" "github.com/google/uuid" "golang.org/x/exp/slices" ) +type StoreTransportTopic interface { + node.Lifecycle + Subscribe() (<-chan DAMessage, context.CancelFunc) + Publish(DAMessage) +} + +type DAMessage struct { + SourceTx *types.Transaction `json:"sourceTx"` + StoreWrites []StoreWrite `json:"storeWrites"` + StoreUUID uuid.UUID `json:"storeUUID"` + Signature suave.Bytes `json:"signature"` +} + +type StoreWrite struct { + Bid suave.Bid `json:"bid"` + Caller common.Address `json:"caller"` + Key string `json:"key"` + Value suave.Bytes `json:"value"` +} + +type DASigner interface { + Sign(account common.Address, data []byte) ([]byte, error) + Sender(data []byte, signature []byte) (common.Address, error) + LocalAddresses() []common.Address +} + +type ChainSigner interface { + Sender(tx *types.Transaction) (common.Address, error) +} + type ConfidentialStoreEngine struct { ctx context.Context cancel context.CancelFunc backend suave.ConfidentialStoreBackend - transportTopic suave.StoreTransportTopic + transportTopic StoreTransportTopic - daSigner suave.DASigner - chainSigner suave.ChainSigner + daSigner DASigner + chainSigner ChainSigner storeUUID uuid.UUID localAddresses map[common.Address]struct{} } -func NewConfidentialStoreEngine(backend suave.ConfidentialStoreBackend, transportTopic suave.StoreTransportTopic, daSigner suave.DASigner, chainSigner suave.ChainSigner) *ConfidentialStoreEngine { +func NewConfidentialStoreEngine(backend suave.ConfidentialStoreBackend, transportTopic StoreTransportTopic, daSigner DASigner, chainSigner ChainSigner) *ConfidentialStoreEngine { localAddresses := make(map[common.Address]struct{}) for _, addr := range daSigner.LocalAddresses() { localAddresses[addr] = struct{}{} @@ -177,7 +208,7 @@ func (e *ConfidentialStoreEngine) Retrieve(bidId suave.BidId, caller common.Addr return e.backend.Retrieve(bid, caller, key) } -func (e *ConfidentialStoreEngine) Finalize(tx *types.Transaction, newBids map[suave.BidId]suave.Bid, stores []suave.StoreWrite) error { +func (e *ConfidentialStoreEngine) Finalize(tx *types.Transaction, newBids map[suave.BidId]suave.Bid, stores []StoreWrite) error { // for _, bid := range newBids { err := e.backend.InitializeBid(bid) @@ -195,7 +226,7 @@ func (e *ConfidentialStoreEngine) Finalize(tx *types.Transaction, newBids map[su } // Sign and propagate the message - pwMsg := suave.DAMessage{ + pwMsg := DAMessage{ SourceTx: tx, StoreWrites: stores, StoreUUID: e.storeUUID, @@ -227,7 +258,7 @@ func (e *ConfidentialStoreEngine) Finalize(tx *types.Transaction, newBids map[su return nil } -func (e *ConfidentialStoreEngine) NewMessage(message suave.DAMessage) error { +func (e *ConfidentialStoreEngine) NewMessage(message DAMessage) error { // Note the validation is a work in progress and not guaranteed to be correct! // Message-level validation @@ -348,8 +379,8 @@ func SerializeBidForSigning(bid *suave.Bid) ([]byte, error) { return []byte(fmt.Sprintf("\x19Suave Signed Message:\n%d%s", len(bidBytes), string(bidBytes))), nil } -func SerializeMessageForSigning(message *suave.DAMessage) ([]byte, error) { - msgBytes, err := json.Marshal(suave.DAMessage{ +func SerializeMessageForSigning(message *DAMessage) ([]byte, error) { + msgBytes, err := json.Marshal(DAMessage{ SourceTx: message.SourceTx, StoreWrites: message.StoreWrites, StoreUUID: message.StoreUUID, @@ -367,10 +398,10 @@ type MockTransport struct{} func (MockTransport) Start() error { return nil } func (MockTransport) Stop() error { return nil } -func (MockTransport) Subscribe() (<-chan suave.DAMessage, context.CancelFunc) { +func (MockTransport) Subscribe() (<-chan DAMessage, context.CancelFunc) { return nil, func() {} } -func (MockTransport) Publish(suave.DAMessage) {} +func (MockTransport) Publish(DAMessage) {} type MockSigner struct{} @@ -427,3 +458,7 @@ func calculateBidId(bid types.Bid) (types.BidId, error) { return bid.Id, nil } + +func RandomBidId() types.BidId { + return types.BidId(uuid.New()) +} diff --git a/suave/cstore/engine_test.go b/suave/cstore/engine_test.go index a0bf00bd8b..5037d872fe 100644 --- a/suave/cstore/engine_test.go +++ b/suave/cstore/engine_test.go @@ -95,10 +95,10 @@ func TestOwnMessageDropping(t *testing.T) { *wasCalled = false - daMessage := suave.DAMessage{ + daMessage := DAMessage{ SourceTx: dummyCreationTx, StoreUUID: engine.storeUUID, - StoreWrites: []suave.StoreWrite{{Bid: testBid}}, + StoreWrites: []StoreWrite{{Bid: testBid}}, } daMessageBytes, err := SerializeMessageForSigning(&daMessage) diff --git a/suave/cstore/redis_backends_test.go b/suave/cstore/redis_backends_test.go index fc0d399633..2b6e23fa3e 100644 --- a/suave/cstore/redis_backends_test.go +++ b/suave/cstore/redis_backends_test.go @@ -24,8 +24,8 @@ func TestRedisTransport(t *testing.T) { msgSub, cancel := redisPubSub.Subscribe() t.Cleanup(cancel) - daMsg := suave.DAMessage{ - StoreWrites: []suave.StoreWrite{{ + daMsg := DAMessage{ + StoreWrites: []StoreWrite{{ Bid: suave.Bid{ Id: suave.BidId{0x42}, DecryptionCondition: uint64(13), @@ -71,14 +71,14 @@ func TestEngineOnRedis(t *testing.T) { redisPubSub1 := NewRedisPubSubTransport(mrPubSub.Addr()) redisStoreBackend1 := NewRedisStoreBackend(mrStore1.Addr()) - engine1 := suave.NewConfidentialStoreEngine(redisStoreBackend1, redisPubSub1, suave.MockSigner{}, suave.MockChainSigner{}) + engine1 := NewConfidentialStoreEngine(redisStoreBackend1, redisPubSub1, MockSigner{}, MockChainSigner{}) require.NoError(t, engine1.Start()) t.Cleanup(func() { engine1.Stop() }) redisPubSub2 := NewRedisPubSubTransport(mrPubSub.Addr()) redisStoreBackend2 := NewRedisStoreBackend(mrStore2.Addr()) - engine2 := suave.NewConfidentialStoreEngine(redisStoreBackend2, redisPubSub2, suave.MockSigner{}, suave.MockChainSigner{}) + engine2 := NewConfidentialStoreEngine(redisStoreBackend2, redisPubSub2, MockSigner{}, MockChainSigner{}) require.NoError(t, engine2.Start()) t.Cleanup(func() { engine2.Stop() }) @@ -108,7 +108,7 @@ func TestEngineOnRedis(t *testing.T) { t.Cleanup(cancel) // Trigger propagation - err = engine1.Finalize(dummyCreationTx, nil, []suave.StoreWrite{{ + err = engine1.Finalize(dummyCreationTx, nil, []StoreWrite{{ Bid: bid, Caller: bid.AllowedPeekers[0], Key: "xx", diff --git a/suave/cstore/redis_transport.go b/suave/cstore/redis_transport.go index 41261e60d5..f631365412 100644 --- a/suave/cstore/redis_transport.go +++ b/suave/cstore/redis_transport.go @@ -12,7 +12,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - suave "github.com/ethereum/go-ethereum/suave/core" ) var ( @@ -67,8 +66,8 @@ func (r *RedisPubSubTransport) Stop() error { return nil } -func (r *RedisPubSubTransport) Subscribe() (<-chan suave.DAMessage, context.CancelFunc) { - ch := make(chan suave.DAMessage, 16) +func (r *RedisPubSubTransport) Subscribe() (<-chan DAMessage, context.CancelFunc) { + ch := make(chan DAMessage, 16) ctx, cancel := context.WithCancel(r.ctx) // Each subscriber has its own PubSub as it blocks on receive! @@ -92,7 +91,7 @@ func (r *RedisPubSubTransport) Subscribe() (<-chan suave.DAMessage, context.Canc continue } - var msg suave.DAMessage + var msg DAMessage msgBytes := common.Hex2Bytes(rmsg.Payload) if err != nil { log.Trace("Redis pubsub: could not decode message from subscription", "err", err, "msg", rmsg.Payload) @@ -137,7 +136,7 @@ func (r *RedisPubSubTransport) Subscribe() (<-chan suave.DAMessage, context.Canc return ch, cancel } -func (r *RedisPubSubTransport) Publish(message suave.DAMessage) { +func (r *RedisPubSubTransport) Publish(message DAMessage) { log.Trace("Redis pubsub: publishing", "message", message) data, err := json.Marshal(message) if err != nil { diff --git a/suave/cstore/transactional_store.go b/suave/cstore/transactional_store.go index fc69cdc05c..e4c68f40be 100644 --- a/suave/cstore/transactional_store.go +++ b/suave/cstore/transactional_store.go @@ -17,7 +17,7 @@ type TransactionalStore struct { pendingLock sync.Mutex pendingBids map[suave.BidId]suave.Bid - pendingWrites []suave.StoreWrite + pendingWrites []StoreWrite } func (s *TransactionalStore) FetchBidById(bidId suave.BidId) (suave.Bid, error) { @@ -58,7 +58,7 @@ func (s *TransactionalStore) Store(bidId suave.BidId, caller common.Address, key s.pendingLock.Lock() defer s.pendingLock.Unlock() - s.pendingWrites = append(s.pendingWrites, suave.StoreWrite{ + s.pendingWrites = append(s.pendingWrites, StoreWrite{ Bid: bid, Caller: caller, Key: key, diff --git a/suave/cstore/transactional_store_test.go b/suave/cstore/transactional_store_test.go index 4dbe153efc..f86b3380bf 100644 --- a/suave/cstore/transactional_store_test.go +++ b/suave/cstore/transactional_store_test.go @@ -12,7 +12,7 @@ import ( ) func TestTransactionalStore(t *testing.T) { - engine := suave.NewConfidentialStoreEngine(NewLocalConfidentialStore(), suave.MockTransport{}, suave.MockSigner{}, suave.MockChainSigner{}) + engine := NewConfidentialStoreEngine(NewLocalConfidentialStore(), MockTransport{}, MockSigner{}, MockChainSigner{}) testKey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") dummyCreationTx, err := types.SignTx(types.NewTx(&types.ConfidentialComputeRequest{ @@ -25,7 +25,7 @@ func TestTransactionalStore(t *testing.T) { tstore := engine.NewTransactionalStore(dummyCreationTx) testBid, err := tstore.InitializeBid(types.Bid{ - Salt: suave.RandomBidId(), + Salt: RandomBidId(), DecryptionCondition: 46, AllowedStores: []common.Address{{0x42}}, AllowedPeekers: []common.Address{{0x43}}, diff --git a/suave/e2e/workflow_test.go b/suave/e2e/workflow_test.go index bc6c6015a2..bea9dc8531 100644 --- a/suave/e2e/workflow_test.go +++ b/suave/e2e/workflow_test.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" suave "github.com/ethereum/go-ethereum/suave/core" + "github.com/ethereum/go-ethereum/suave/cstore" "github.com/ethereum/go-ethereum/suave/sdk" "github.com/flashbots/go-boost-utils/ssz" "github.com/mitchellh/mapstructure" @@ -522,7 +523,7 @@ func TestBlockBuildingPrecompiles(t *testing.T) { }, dummyCreationTx) require.NoError(t, err) - err = fr.ConfidentialEngine().Finalize(dummyCreationTx, map[suave.BidId]suave.Bid{bid.Id: bid}, []suave.StoreWrite{{ + err = fr.ConfidentialEngine().Finalize(dummyCreationTx, map[suave.BidId]suave.Bid{bid.Id: bid}, []cstore.StoreWrite{{ Bid: bid, Caller: common.Address{0x41, 0x42, 0x43}, @@ -880,7 +881,7 @@ func (f *framework) ConfidentialStoreBackend() suave.ConfidentialStoreBackend { return f.suethSrv.service.APIBackend.SuaveEngine().Backend() } -func (f *framework) ConfidentialEngine() *suave.ConfidentialStoreEngine { +func (f *framework) ConfidentialEngine() *cstore.ConfidentialStoreEngine { return f.suethSrv.service.APIBackend.SuaveEngine() } From 208d77bb2f1a588c5753767ee64c8367b093641c Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Fri, 20 Oct 2023 11:56:26 +0200 Subject: [PATCH 3/4] Finish --- eth/backend.go | 7 ++-- suave/cstore/backend_testing.go | 2 +- suave/cstore/engine.go | 45 ++++++++++++++---------- suave/cstore/local_store_backend.go | 6 +--- suave/cstore/redis_backends_test.go | 6 ++-- suave/cstore/redis_store_backend.go | 12 ++++--- suave/cstore/redis_store_backend_test.go | 6 +--- suave/e2e/workflow_test.go | 2 +- 8 files changed, 47 insertions(+), 39 deletions(-) diff --git a/eth/backend.go b/eth/backend.go index cf6bb44a4c..db249455b1 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -231,9 +231,12 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { eth.miner = miner.New(eth, &config.Miner, eth.blockchain.Config(), eth.EventMux(), eth.engine, eth.isLocalBlock) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) - var confidentialStoreBackend suave.ConfidentialStoreBackend + var confidentialStoreBackend cstore.ConfidentialStorageBackend if config.Suave.RedisStoreUri != "" { - confidentialStoreBackend = cstore.NewRedisStoreBackend(config.Suave.RedisStoreUri) + confidentialStoreBackend, err = cstore.NewRedisStoreBackend(config.Suave.RedisStoreUri) + if err != nil { + return nil, err + } } else { confidentialStoreBackend = cstore.NewLocalConfidentialStore() } diff --git a/suave/cstore/backend_testing.go b/suave/cstore/backend_testing.go index f4dc09a0a4..2adebffd4d 100644 --- a/suave/cstore/backend_testing.go +++ b/suave/cstore/backend_testing.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" ) -func testBackendStore(t *testing.T, store suave.ConfidentialStoreBackend) { +func testBackendStore(t *testing.T, store ConfidentialStorageBackend) { bid := suave.Bid{ Id: suave.RandomBidId(), DecryptionCondition: 10, diff --git a/suave/cstore/engine.go b/suave/cstore/engine.go index d25870f36f..6104a56959 100644 --- a/suave/cstore/engine.go +++ b/suave/cstore/engine.go @@ -15,6 +15,19 @@ import ( "golang.org/x/exp/slices" ) +// ConfidentialStorageBackend is the interface that must be implemented by a +// storage backend for the confidential storage engine. +type ConfidentialStorageBackend interface { + InitializeBid(bid suave.Bid) error + Store(bid suave.Bid, caller common.Address, key string, value []byte) (suave.Bid, error) + Retrieve(bid suave.Bid, caller common.Address, key string) ([]byte, error) + FetchBidById(suave.BidId) (suave.Bid, error) + FetchBidsByProtocolAndBlock(blockNumber uint64, namespace string) []suave.Bid + Stop() error +} + +// StoreTransportTopic is the interface that must be implemented by a +// transport engine for the confidential storage engine. type StoreTransportTopic interface { node.Lifecycle Subscribe() (<-chan DAMessage, context.CancelFunc) @@ -49,7 +62,7 @@ type ConfidentialStoreEngine struct { ctx context.Context cancel context.CancelFunc - backend suave.ConfidentialStoreBackend + storage ConfidentialStorageBackend transportTopic StoreTransportTopic daSigner DASigner @@ -59,14 +72,14 @@ type ConfidentialStoreEngine struct { localAddresses map[common.Address]struct{} } -func NewConfidentialStoreEngine(backend suave.ConfidentialStoreBackend, transportTopic StoreTransportTopic, daSigner DASigner, chainSigner ChainSigner) *ConfidentialStoreEngine { +func NewConfidentialStoreEngine(backend ConfidentialStorageBackend, transportTopic StoreTransportTopic, daSigner DASigner, chainSigner ChainSigner) *ConfidentialStoreEngine { localAddresses := make(map[common.Address]struct{}) for _, addr := range daSigner.LocalAddresses() { localAddresses[addr] = struct{}{} } return &ConfidentialStoreEngine{ - backend: backend, + storage: backend, transportTopic: transportTopic, daSigner: daSigner, chainSigner: chainSigner, @@ -84,10 +97,6 @@ func (e *ConfidentialStoreEngine) NewTransactionalStore(sourceTx *types.Transact } func (e *ConfidentialStoreEngine) Start() error { - if err := e.backend.Start(); err != nil { - return err - } - if err := e.transportTopic.Start(); err != nil { return err } @@ -115,7 +124,7 @@ func (e *ConfidentialStoreEngine) Stop() error { log.Warn("Confidential engine: error while stopping transport", "err", err) } - if err := e.backend.Stop(); err != nil { + if err := e.storage.Stop(); err != nil { log.Warn("Confidential engine: error while stopping transport", "err", err) } @@ -123,8 +132,8 @@ func (e *ConfidentialStoreEngine) Stop() error { } // For testing purposes! -func (e *ConfidentialStoreEngine) Backend() suave.ConfidentialStoreBackend { - return e.backend +func (e *ConfidentialStoreEngine) Backend() ConfidentialStorageBackend { + return e.storage } func (e *ConfidentialStoreEngine) ProcessMessages() { @@ -188,15 +197,15 @@ func (e *ConfidentialStoreEngine) InitializeBid(bid types.Bid, creationTx *types } func (e *ConfidentialStoreEngine) FetchBidById(bidId suave.BidId) (suave.Bid, error) { - return e.backend.FetchBidById(bidId) + return e.storage.FetchBidById(bidId) } func (e *ConfidentialStoreEngine) FetchBidsByProtocolAndBlock(blockNumber uint64, namespace string) []suave.Bid { - return e.backend.FetchBidsByProtocolAndBlock(blockNumber, namespace) + return e.storage.FetchBidsByProtocolAndBlock(blockNumber, namespace) } func (e *ConfidentialStoreEngine) Retrieve(bidId suave.BidId, caller common.Address, key string) ([]byte, error) { - bid, err := e.backend.FetchBidById(bidId) + bid, err := e.storage.FetchBidById(bidId) if err != nil { return []byte{}, fmt.Errorf("confidential engine: could not fetch bid %x while retrieving: %w", bidId, err) } @@ -205,13 +214,13 @@ func (e *ConfidentialStoreEngine) Retrieve(bidId suave.BidId, caller common.Addr return []byte{}, fmt.Errorf("confidential engine: %x not allowed to retrieve %s on %x", caller, key, bidId) } - return e.backend.Retrieve(bid, caller, key) + return e.storage.Retrieve(bid, caller, key) } func (e *ConfidentialStoreEngine) Finalize(tx *types.Transaction, newBids map[suave.BidId]suave.Bid, stores []StoreWrite) error { // for _, bid := range newBids { - err := e.backend.InitializeBid(bid) + err := e.storage.InitializeBid(bid) if err != nil { // TODO: deinitialize! return fmt.Errorf("confidential engine: store backend failed to initialize bid: %w", err) @@ -219,7 +228,7 @@ func (e *ConfidentialStoreEngine) Finalize(tx *types.Transaction, newBids map[su } for _, sw := range stores { - if _, err := e.backend.Store(sw.Bid, sw.Caller, sw.Key, sw.Value); err != nil { + if _, err := e.storage.Store(sw.Bid, sw.Caller, sw.Key, sw.Value); err != nil { // TODO: deinitialize and deStore! return fmt.Errorf("failed to store data: %w", err) } @@ -344,7 +353,7 @@ func (e *ConfidentialStoreEngine) NewMessage(message DAMessage) error { } for _, sw := range message.StoreWrites { - err = e.backend.InitializeBid(sw.Bid) + err = e.storage.InitializeBid(sw.Bid) if err != nil { if !errors.Is(err, suave.ErrBidAlreadyPresent) { log.Error("confidential engine: unexpected error while initializing bid from transport: %w", err) @@ -352,7 +361,7 @@ func (e *ConfidentialStoreEngine) NewMessage(message DAMessage) error { } } - _, err = e.backend.Store(sw.Bid, sw.Caller, sw.Key, sw.Value) + _, err = e.storage.Store(sw.Bid, sw.Caller, sw.Key, sw.Value) if err != nil { log.Error("confidential engine: unexpected error while storing: %w", err) continue // Don't abandon! diff --git a/suave/cstore/local_store_backend.go b/suave/cstore/local_store_backend.go index 1a147a4a76..872a92d383 100644 --- a/suave/cstore/local_store_backend.go +++ b/suave/cstore/local_store_backend.go @@ -10,7 +10,7 @@ import ( suave "github.com/ethereum/go-ethereum/suave/core" ) -var _ suave.ConfidentialStoreBackend = &LocalConfidentialStore{} +var _ ConfidentialStorageBackend = &LocalConfidentialStore{} type LocalConfidentialStore struct { lock sync.Mutex @@ -27,10 +27,6 @@ func NewLocalConfidentialStore() *LocalConfidentialStore { } } -func (l *LocalConfidentialStore) Start() error { - return nil -} - func (l *LocalConfidentialStore) Stop() error { return nil } diff --git a/suave/cstore/redis_backends_test.go b/suave/cstore/redis_backends_test.go index 2b6e23fa3e..c7186c455f 100644 --- a/suave/cstore/redis_backends_test.go +++ b/suave/cstore/redis_backends_test.go @@ -69,14 +69,14 @@ func TestEngineOnRedis(t *testing.T) { mrPubSub := mrStore1 redisPubSub1 := NewRedisPubSubTransport(mrPubSub.Addr()) - redisStoreBackend1 := NewRedisStoreBackend(mrStore1.Addr()) + redisStoreBackend1, _ := NewRedisStoreBackend(mrStore1.Addr()) engine1 := NewConfidentialStoreEngine(redisStoreBackend1, redisPubSub1, MockSigner{}, MockChainSigner{}) require.NoError(t, engine1.Start()) t.Cleanup(func() { engine1.Stop() }) redisPubSub2 := NewRedisPubSubTransport(mrPubSub.Addr()) - redisStoreBackend2 := NewRedisStoreBackend(mrStore2.Addr()) + redisStoreBackend2, _ := NewRedisStoreBackend(mrStore2.Addr()) engine2 := NewConfidentialStoreEngine(redisStoreBackend2, redisPubSub2, MockSigner{}, MockChainSigner{}) require.NoError(t, engine2.Start()) @@ -94,7 +94,7 @@ func TestEngineOnRedis(t *testing.T) { bid, err := engine1.InitializeBid(types.Bid{ DecryptionCondition: uint64(13), AllowedPeekers: []common.Address{{0x41, 0x39}}, - AllowedStores: []common.Address{common.Address{}}, + AllowedStores: []common.Address{{}}, Version: string("vv"), }, dummyCreationTx) require.NoError(t, err) diff --git a/suave/cstore/redis_store_backend.go b/suave/cstore/redis_store_backend.go index 268b77c30f..55ac60aa81 100644 --- a/suave/cstore/redis_store_backend.go +++ b/suave/cstore/redis_store_backend.go @@ -15,7 +15,7 @@ import ( "github.com/go-redis/redis/v8" ) -var _ suave.ConfidentialStoreBackend = &RedisStoreBackend{} +var _ ConfidentialStorageBackend = &RedisStoreBackend{} var ( formatRedisBidKey = func(bidId suave.BidId) string { @@ -37,15 +37,19 @@ type RedisStoreBackend struct { local *miniredis.Miniredis } -func NewRedisStoreBackend(redisUri string) *RedisStoreBackend { +func NewRedisStoreBackend(redisUri string) (*RedisStoreBackend, error) { r := &RedisStoreBackend{ cancel: nil, redisUri: redisUri, } - return r + + if err := r.start(); err != nil { + return nil, err + } + return r, nil } -func (r *RedisStoreBackend) Start() error { +func (r *RedisStoreBackend) start() error { if r.redisUri == "" { // create a mini-redis instance localRedis, err := miniredis.Run() diff --git a/suave/cstore/redis_store_backend_test.go b/suave/cstore/redis_store_backend_test.go index 17431c4a59..2962584c8f 100644 --- a/suave/cstore/redis_store_backend_test.go +++ b/suave/cstore/redis_store_backend_test.go @@ -2,13 +2,9 @@ package cstore import ( "testing" - - "github.com/stretchr/testify/require" ) func TestRedis_StoreSuite(t *testing.T) { - store := NewRedisStoreBackend("") - require.NoError(t, store.Start()) - + store, _ := NewRedisStoreBackend("") testBackendStore(t, store) } diff --git a/suave/e2e/workflow_test.go b/suave/e2e/workflow_test.go index bea9dc8531..ed8b32ec9a 100644 --- a/suave/e2e/workflow_test.go +++ b/suave/e2e/workflow_test.go @@ -877,7 +877,7 @@ func (f *framework) NewSDKClient() *sdk.Client { return sdk.NewClient(f.suethSrv.RPCNode(), testKey, f.ExecutionNode()) } -func (f *framework) ConfidentialStoreBackend() suave.ConfidentialStoreBackend { +func (f *framework) ConfidentialStoreBackend() cstore.ConfidentialStorageBackend { return f.suethSrv.service.APIBackend.SuaveEngine().Backend() } From 7f8a8c9086474383b9a09d9bb541f7895130c74f Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Fri, 20 Oct 2023 12:06:41 +0200 Subject: [PATCH 4/4] Pass lint --- suave/core/utils.go | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/suave/core/utils.go b/suave/core/utils.go index 18f1937bc3..ba4b59b994 100644 --- a/suave/core/utils.go +++ b/suave/core/utils.go @@ -2,29 +2,11 @@ package suave import ( "encoding/json" - "fmt" "github.com/ethereum/go-ethereum/core/types" "github.com/google/uuid" ) -var bidUuidSpace = uuid.UUID{0x42} -var emptyId [16]byte - -func calculateBidId(bid types.Bid) (types.BidId, error) { - copy(bid.Id[:], emptyId[:]) - - body, err := json.Marshal(bid) - if err != nil { - return types.BidId{}, fmt.Errorf("could not marshal bid to calculate its id: %w", err) - } - - uuidv5 := uuid.NewSHA1(bidUuidSpace, body) - copy(bid.Id[:], uuidv5[:]) - - return bid.Id, nil -} - func RandomBidId() types.BidId { return types.BidId(uuid.New()) }