From 9087f6c16d02675e75b297d173c477dad68e9b15 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Wed, 14 Jul 2021 16:38:58 -0700 Subject: [PATCH 1/7] Revert "Update relic" This reverts commit a060972b5480f26f962e8a00e74200d0bbf1d255. --- crypto/relic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/relic b/crypto/relic index 7a9bba7fea0..9206ae50b66 160000 --- a/crypto/relic +++ b/crypto/relic @@ -1 +1 @@ -Subproject commit 7a9bba7fea01b022e8874f1b1ba7af19f06fa025 +Subproject commit 9206ae50b667de160fcc385ba3dc2c920143ab0a From 03112449d9559f4f95b711ae71a4c71ad48316e0 Mon Sep 17 00:00:00 2001 From: Justin Kan Date: Wed, 14 Jul 2021 17:15:46 -0700 Subject: [PATCH 2/7] Update SECURITY.md --- SECURITY.md | 83 +---------------------------------------------------- 1 file changed, 1 insertion(+), 82 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 29b153e59f0..fb39955f717 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -9,85 +9,4 @@ If you care about making a difference, please follow the guidelines below. # **Guidelines For Responsible Disclosure** -We ask that all researchers adhere to these guidelines. - -## **Rules of Engagement** - -- Make every effort to avoid unauthorized access, use, and disclosure of personal information. -- Avoid actions which could impact user experience, disrupt production systems, change, or destroy data during security testing. -- Don’t perform any attack that is intended to cause Denial of Service to the network, hosts, or services on any port or using any protocol. -- Use our provided communication channels to securely report vulnerability information to us. -- Keep information about any bug or vulnerability you discover confidential between us until we publicly disclose it. -- Please don’t use scanners to crawl us and hammer endpoints. They’re noisy and we already do this. If you find anything this way, we have likely already identified it. -- Never attempt non-technical attacks such as social engineering, phishing, or physical attacks against our employees, users, or infrastructure. - -## **In Scope URIs** - -Be careful that you're looking at domains and systems that belong to us and not someone else. When in doubt, please ask us. Maybe ask us anyway. - -Bottom line, we suggest that you limit your testing to infrastructure that is clearly ours. - -## **Out of Scope URIs** - -The following base URIs are explicitly out of scope: - -- None - -## **Things Not To Do** - -In the interests of your safety, our safety, and for our customers, the following test types are prohibited: - -- Physical testing such as office and data-centre access (e.g. open doors, tailgating, card reader attacks, physically destructive testing) -- Social engineering (e.g. phishing, vishing) -- Testing of applications or systems NOT covered by the ‘In Scope’ section, or that are explicitly out of scope. -- Network level Denial of Service (DoS/DDoS) attacks - -## **Sensitive Data** - -In the interests of protecting privacy, we never want to receive: - -- Personally identifiable information (PII) -- Payment card (e.g. credit card) data -- Financial information (e.g. bank records) -- Health or medical information -- Accessed or cracked credentials in cleartext - -## **Our Commitment To You** - -If you follow these guidelines when researching and reporting an issue to us, we commit to: - -- Not send lawyers after you related to your research under this policy; -- Work with you to understand and resolve any issues within a reasonable timeframe, including an initial confirmation of your report within 72 hours of submission; and -- At a minimum, we will recognize your contribution in our Disclosure Acknowledgements if you are the first to report the issue and we make a code or configuration change based on the issue. - -## **Disclosure Acknowledgements** - -We're happy to acknowledge contributors. Security acknowledgements can be found here. - -## Rewards - -We run closed bug bounty programs, but beyond that we also pay out rewards, once per eligible bug, to the first responsibly disclosing third party. Rewards are based on the seriousness of the bug, but the minimum is $100 and we have and are willing to pay $5,000 or more at our sole discretion. - -### **Elligibility** - -To qualify, the bug must fall within our scope and rules and meet the following criteria: - -1. **Previously unknown** - When reported, we must not have already known of the issue, either by internal discovery or separate disclosure. -2. **Material impact** - Demonstrable exploitability where, if exploited, the bug would materially affect the confidentiality, integrity, or availability of our services. -3. **Requires action** - The bug requires some mitigation. It is both valid and actionable. - -## **Reporting Security Findings To Us** - -Reports are welcome! Please definitely reach out to us if you have a security concern. - -We prefer you to please send us an email: security@onflow.org - -Note: If you believe you may have found a security vulnerability in our open source repos, to be on the safe side, do NOT open a public issue. - -We encourage you to encrypt the information you send us using our PGP key at [keys.openpgp.org/security@onflow.org](https://keys.openpgp.org/vks/v1/by-fingerprint/AE3264F330AB51F7DBC52C400BB5D3D7516D168C) - -Please include the following details with your report: - -- A description of the location and potential impact of the finding(s); -- A detailed description of the steps required to reproduce the issue; and -- Any POC scripts, screenshots, and compressed screen captures, where feasible. +We ask that all researchers adhere to these guidelines [here](https://docs.onflow.org/bounties/responsible-disclosure/) From 4ec3bd0379712abc580971cb031fff76ce0768cb Mon Sep 17 00:00:00 2001 From: Vishal <1117327+vishalchangrani@users.noreply.github.com> Date: Thu, 15 Jul 2021 10:25:02 -0700 Subject: [PATCH 3/7] adding a utility to generate an x509 certificate from a private key (#944) --- go.mod | 1 + network/p2p/keyTranslator.go | 4 +-- network/p2p/keyTranslator_test.go | 2 +- network/p2p/libp2pNode.go | 2 +- network/p2p/libp2pNode_test.go | 2 +- utils/grpc/grpc.go | 49 ++++++++++++++++++++++++++++++ utils/grpc/grpc_test.go | 50 +++++++++++++++++++++++++++++++ 7 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 utils/grpc/grpc_test.go diff --git a/go.mod b/go.mod index 5590e95caee..67043aef7f3 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/libp2p/go-libp2p-discovery v0.5.0 github.com/libp2p/go-libp2p-pubsub v0.4.1 github.com/libp2p/go-libp2p-swarm v0.5.0 + github.com/libp2p/go-libp2p-tls v0.1.3 github.com/libp2p/go-libp2p-transport-upgrader v0.4.2 github.com/libp2p/go-tcp-transport v0.2.1 github.com/m4ksio/wal v1.0.0 diff --git a/network/p2p/keyTranslator.go b/network/p2p/keyTranslator.go index 14c9d56ac1b..e810da6351f 100644 --- a/network/p2p/keyTranslator.go +++ b/network/p2p/keyTranslator.go @@ -34,8 +34,8 @@ func setPubKey(c elliptic.Curve, x *big.Int, y *big.Int) *goecdsa.PublicKey { // Both Flow and LibP2P define a crypto package with their own abstraction of Keys // These utility functions convert a Flow crypto key to a LibP2P key (Flow --> LibP2P) -// privKey converts a Flow private key to a LibP2P Private key -func privKey(fpk fcrypto.PrivateKey) (lcrypto.PrivKey, error) { +// PrivKey converts a Flow private key to a LibP2P Private key +func PrivKey(fpk fcrypto.PrivateKey) (lcrypto.PrivKey, error) { // get the signature algorithm keyType, err := keyType(fpk.Algorithm()) if err != nil { diff --git a/network/p2p/keyTranslator_test.go b/network/p2p/keyTranslator_test.go index 1a8d77df2ae..898df3ebf6d 100644 --- a/network/p2p/keyTranslator_test.go +++ b/network/p2p/keyTranslator_test.go @@ -45,7 +45,7 @@ func (k *KeyTranslatorTestSuite) TestPrivateKeyConversion() { require.NoError(k.T(), err) // convert it to a LibP2P private key - lpk, err := privKey(fpk) + lpk, err := PrivKey(fpk) require.NoError(k.T(), err) // get the raw bytes of both the keys diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index de91f4123f8..338d76cecae 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -91,7 +91,7 @@ func NewLibP2PNode(logger zerolog.Logger, pingInfoProvider PingInfoProvider, psOption ...pubsub.Option) (*Node, error) { - libp2pKey, err := privKey(key) + libp2pKey, err := PrivKey(key) if err != nil { return nil, fmt.Errorf("could not generate libp2p key: %w", err) } diff --git a/network/p2p/libp2pNode_test.go b/network/p2p/libp2pNode_test.go index 516a15ba3ff..e25fb7fd6b0 100644 --- a/network/p2p/libp2pNode_test.go +++ b/network/p2p/libp2pNode_test.go @@ -672,7 +672,7 @@ func generateNetworkingAndLibP2PKeys(t *testing.T) (crypto.PrivKey, fcrypto.Priv key := generateNetworkingKey(t) // translates flow key into libp2p key - libP2Pkey, err := privKey(key) + libP2Pkey, err := PrivKey(key) require.NoError(t, err) return libP2Pkey, key diff --git a/utils/grpc/grpc.go b/utils/grpc/grpc.go index f843c0e6366..0849f3bb35e 100644 --- a/utils/grpc/grpc.go +++ b/utils/grpc/grpc.go @@ -1,5 +1,54 @@ package grpcutils +import ( + "crypto/tls" + "fmt" + + libp2ptls "github.com/libp2p/go-libp2p-tls" + + "github.com/onflow/flow-go/crypto" + "github.com/onflow/flow-go/network/p2p" +) + // DefaultMaxMsgSize use 16MB as the default message size limit. // grpc library default is 4MB const DefaultMaxMsgSize = 1024 * 1024 * 16 + +// X509Certificate generates a self-signed x509 TLS certificate from the given key. The generated certificate +// includes a libp2p extension that specifies the public key and the signature. The certificate does not include any +// SAN extension. +func X509Certificate(privKey crypto.PrivateKey) (*tls.Certificate, error) { + + // convert the Flow crypto private key to a Libp2p private crypto key + libP2PKey, err := p2p.PrivKey(privKey) + if err != nil { + return nil, fmt.Errorf("could not convert Flow key to libp2p key: %w", err) + } + + // create a libp2p Identity from the libp2p private key + id, err := libp2ptls.NewIdentity(libP2PKey) + if err != nil { + return nil, fmt.Errorf("could not generate identity: %w", err) + } + + // extract the TLSConfig from it which will contain the generated x509 certificate + // (ignore the public key that is returned - it is the public key of the private key used to generate the ID) + libp2pTlsConfig, _ := id.ConfigForAny() + + // verify that exactly one certificate was generated for the given key + certCount := len(libp2pTlsConfig.Certificates) + if certCount != 1 { + return nil, fmt.Errorf("invalid count for the generated x509 certificate: %d", certCount) + } + + return &libp2pTlsConfig.Certificates[0], nil +} + +// DefaultServerTLSConfig returns the default TLS config with the given cert for a secure GRPC server +func DefaultServerTLSConfig(cert *tls.Certificate) *tls.Config { + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{*cert}, + ClientAuth: tls.NoClientCert, + } + return tlsConfig +} diff --git a/utils/grpc/grpc_test.go b/utils/grpc/grpc_test.go new file mode 100644 index 00000000000..930e140b97d --- /dev/null +++ b/utils/grpc/grpc_test.go @@ -0,0 +1,50 @@ +package grpcutils + +import ( + "crypto/x509" + "testing" + "time" + + libp2ptls "github.com/libp2p/go-libp2p-tls" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/utils/unittest" +) + +const year = 365 * 24 * time.Hour + +// TestCertificateGeneration tests the X509Certificate certificate generation +func TestCertificateGeneration(t *testing.T) { + // test key + key, err := unittest.NetworkingKey() + require.NoError(t, err) + + // generate the certificate from the key + certs, err := X509Certificate(key) + require.NoError(t, err) + + // assert that only one certificate is generated + require.Len(t, certs.Certificate, 1) + + // parse the cert + cert, err := x509.ParseCertificate(certs.Certificate[0]) + require.NoError(t, err) + + // extract the public key from the cert + pubKey, err := libp2ptls.PubKeyFromCertChain([]*x509.Certificate{cert}) + require.NoError(t, err) + + // convert the test key to a libp2p key for easy comparision + libp2pKey, err := p2p.PrivKey(key) + expectedKey := libp2pKey.GetPublic() + require.NoError(t, err) + + // assert that the public key in the cert matches the test public key + require.True(t, expectedKey.Equals(pubKey)) + + // assert that the the cert is valid for at least an year starting from now + now := time.Now() + require.True(t, now.After(cert.NotBefore)) + require.True(t, cert.NotAfter.After(now.Add(year))) +} From b21610ae3490d472528232603674a494e4afffe1 Mon Sep 17 00:00:00 2001 From: Simon Zhu Date: Thu, 15 Jul 2021 11:55:19 -0700 Subject: [PATCH 4/7] remove parseability check from AN transaction validator --- engine/access/rpc/backend/backend.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 709fe4b6830..66931ffd984 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -176,7 +176,7 @@ func configureTransactionValidator(state protocol.State, chainID flow.ChainID) * ExpiryBuffer: flow.DefaultTransactionExpiryBuffer, AllowEmptyReferenceBlockID: false, AllowUnknownReferenceBlockID: false, - CheckScriptsParse: true, + CheckScriptsParse: false, MaxGasLimit: flow.DefaultMaxTransactionGasLimit, MaxTransactionByteSize: flow.DefaultMaxTransactionByteSize, MaxCollectionByteSize: flow.DefaultMaxCollectionByteSize, From 9ae7a18c949d2eb7507459b3ecb8e72b7fb42f2c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 15 Jul 2021 16:45:44 -0700 Subject: [PATCH 5/7] [Verification] Adds functional test for sealing and verification (#922) * enables verification integration test * wip * adds two execution nodes to test suite * refactors tests * adds qualifier for receipts state * enables sealing based on result approvals * fixes sealing happy path test * updates integration test * fixes a comment * updates go mod * fixes finalization height update with block state tracker * refactors testnet tacker * fixes integration test based on block state tracker * fixes compile errors * fixes merge issue * Update integration/tests/verification/sealing_happypath_test.go Co-authored-by: Simon Zhu Co-authored-by: Simon Zhu --- go.mod | 3 + go.sum | 8 +++ integration/Makefile | 2 +- integration/tests/common/receipt_state.go | 17 ++++- .../verification/result_approval_test.go | 2 +- .../verification/sealing_happypath_test.go | 63 +++++++++++++++++++ integration/tests/verification/suite.go | 19 ++++-- .../tests/verification/system_chunk_test.go | 4 +- 8 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 integration/tests/verification/sealing_happypath_test.go diff --git a/go.mod b/go.mod index 67043aef7f3..35c1f6fd2ee 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/improbable-eng/grpc-web v0.12.0 github.com/ipfs/go-log v1.0.4 github.com/jrick/bitset v1.0.0 + github.com/kr/text v0.2.0 // indirect github.com/libp2p/go-addr-util v0.0.2 github.com/libp2p/go-libp2p v0.14.1 github.com/libp2p/go-libp2p-core v0.8.5 @@ -38,6 +39,7 @@ require ( github.com/libp2p/go-tcp-transport v0.2.1 github.com/m4ksio/wal v1.0.0 github.com/marten-seemann/qtls-go1-15 v0.1.4 // indirect + github.com/mitchellh/mapstructure v1.3.3 // indirect github.com/multiformats/go-multiaddr v0.3.1 github.com/onflow/cadence v0.18.1-0.20210629025749-ffe98816b2f1 github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.3 @@ -45,6 +47,7 @@ require ( github.com/onflow/flow-go/crypto v0.18.0 github.com/onflow/flow/protobuf/go/flow v0.2.0 github.com/opentracing/opentracing-go v1.2.0 + github.com/pelletier/go-toml v1.7.0 // indirect github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.7.1 github.com/rs/zerolog v1.19.0 diff --git a/go.sum b/go.sum index 383d0724cf5..b2018a5f1d3 100644 --- a/go.sum +++ b/go.sum @@ -164,6 +164,7 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -509,6 +510,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU= @@ -770,6 +773,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -899,6 +904,8 @@ github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7ir github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= @@ -1547,6 +1554,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/integration/Makefile b/integration/Makefile index 814eca0add0..84b42bff477 100644 --- a/integration/Makefile +++ b/integration/Makefile @@ -12,7 +12,7 @@ integration-test: common-tests execution-tests verification-tests collection-tes # NOTE: Currently skipping collection and consensus tests due to them not passing properly on CI .PHONY: ci-integration-test -ci-integration-test: common-tests execution-tests # collection-tests # consensus-tests # verification-tests +ci-integration-test: common-tests execution-tests verification-tests # collection-tests # consensus-tests .PHONY: collection-tests collection-tests: diff --git a/integration/tests/common/receipt_state.go b/integration/tests/common/receipt_state.go index 3d1b8985eba..3579201269f 100644 --- a/integration/tests/common/receipt_state.go +++ b/integration/tests/common/receipt_state.go @@ -61,7 +61,8 @@ func (rs *ReceiptState) WaitForReceiptFrom(t *testing.T, blockID, executorID flo // WaitUntilFinalizedStateCommitmentChanged waits until a different state commitment for a finalized block is received // compared to the latest one from any execution node and returns the corresponding block and execution receipt -func WaitUntilFinalizedStateCommitmentChanged(t *testing.T, bs *BlockState, rs *ReceiptState) (*messages.BlockProposal, +func WaitUntilFinalizedStateCommitmentChanged(t *testing.T, bs *BlockState, rs *ReceiptState, + qualifiers ...func(receipt flow.ExecutionReceipt) bool) (*messages.BlockProposal, *flow.ExecutionReceipt) { // get the state commitment for the highest finalized block @@ -96,6 +97,13 @@ func WaitUntilFinalizedStateCommitmentChanged(t *testing.T, bs *BlockState, rs * currentHeight++ return false } + + for _, qualifier := range qualifiers { + if !qualifier(*r2) { + return false + } + } + return true }, receiptStateTimeout, 100*time.Millisecond, fmt.Sprintf("did not receive an execution receipt with a different state commitment from %x within %v seconds,"+ @@ -106,3 +114,10 @@ func WaitUntilFinalizedStateCommitmentChanged(t *testing.T, bs *BlockState, rs * return b2, r2 } + +// WithMinimumChunks creates a qualifier that returns true if receipt has the specified minimum number of chunks. +func WithMinimumChunks(chunkNum int) func(flow.ExecutionReceipt) bool { + return func(receipt flow.ExecutionReceipt) bool { + return len(receipt.ExecutionResult.Chunks) >= chunkNum + } +} diff --git a/integration/tests/verification/result_approval_test.go b/integration/tests/verification/result_approval_test.go index ec506786163..2566c8d16c2 100644 --- a/integration/tests/verification/result_approval_test.go +++ b/integration/tests/verification/result_approval_test.go @@ -22,7 +22,7 @@ func (r *ResultApprovalTestSuite) TestVerificationNodeHappyPath() { r.T().Logf("blockA generated, height: %v ID: %v", blockA.Header.Height, blockA.Header.ID()) // waits for execution receipt for blockA from execution node, called receiptA - receiptA := r.ReceiptState.WaitForReceiptFrom(r.T(), blockA.Header.ID(), r.exeID) + receiptA := r.ReceiptState.WaitForReceiptFrom(r.T(), blockA.Header.ID(), r.exe1ID) resultID := receiptA.ExecutionResult.ID() r.T().Logf("receipt for blockA generated: result ID: %x", resultID) diff --git a/integration/tests/verification/sealing_happypath_test.go b/integration/tests/verification/sealing_happypath_test.go new file mode 100644 index 00000000000..f277705e823 --- /dev/null +++ b/integration/tests/verification/sealing_happypath_test.go @@ -0,0 +1,63 @@ +package verification + +import ( + "context" + "testing" + + sdk "github.com/onflow/flow-go-sdk" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/onflow/flow-go/integration/tests/common" +) + +func TestSealingHappyPathTest(t *testing.T) { + suite.Run(t, new(TestSealingHappyPathTestSuite)) +} + +type TestSealingHappyPathTestSuite struct { + Suite +} + +// TestSealingAndVerificationHappyPath evaluates the health of the happy path of verification and sealing. It +// deploys a transaction into the testnet hence causing an execution result with more than +// one chunk, assigns all chunks to the same single verification node in this testnet, and then verifies whether verification node +// generates a result approval for all chunks of that execution result. +// It also enables sealing based on result approvals and verifies whether the block of that specific multi-chunk execution result is sealed +// affected by the emitted result approvals. +func (s *TestSealingHappyPathTestSuite) TestSealingAndVerificationHappyPath() { + // captures one finalized block called blockA, just to make sure that the finalization progresses. + blockA := s.BlockState.WaitForFirstFinalized(s.T()) + s.T().Logf("blockA generated, height: %v ID: %v", blockA.Header.Height, blockA.Header.ID()) + + // sends a transaction + err := s.AccessClient().DeployContract(context.Background(), sdk.Identifier(s.net.Root().ID()), common.CounterContract) + require.NoError(s.T(), err, "could not deploy counter") + + // waits until for a different state commitment for a finalized block, call that block blockB, + // which has more than one chunk on its execution result. + blockB, _ := common.WaitUntilFinalizedStateCommitmentChanged(s.T(), s.BlockState, &s.ReceiptState, common.WithMinimumChunks(2)) + s.T().Logf("got blockB height %v ID %v", blockB.Header.Height, blockB.Header.ID()) + + // waits for the execution receipt of blockB from both execution nodes, and makes sure that there is no execution fork. + receiptB1 := s.ReceiptState.WaitForReceiptFrom(s.T(), blockB.Header.ID(), s.exe1ID) + s.T().Logf("receipt for blockB generated by execution node-1: %x result ID: %x", s.exe1ID, receiptB1.ExecutionResult.ID()) + receiptB2 := s.ReceiptState.WaitForReceiptFrom(s.T(), blockB.Header.ID(), s.exe2ID) + s.T().Logf("receipt for blockB generated by execution node-2: %x result ID: %x", s.exe2ID, receiptB2.ExecutionResult.ID()) + + require.Equal(s.T(), receiptB1.ExecutionResult.ID(), receiptB2.ExecutionResult.ID(), "execution fork happened at blockB") + resultB := receiptB1.ExecutionResult + resultBId := resultB.ID() + // re-evaluates that resultB has more than one chunk. + require.Greater(s.T(), len(resultB.Chunks), 1) + s.T().Logf("receipt for blockB generated: result ID: %x with %d chunks", resultBId, len(resultB.Chunks)) + + // wait for a result approval from verification node for the chunks of resultB. + for i := 0; i < len(resultB.Chunks); i++ { + s.ApprovalState.WaitForResultApproval(s.T(), s.verID, resultBId, uint64(i)) + s.T().Logf("result approval generated for blockB: result ID: %x chunk index: %d", resultBId, i) + } + + // waits until blockB is sealed by consensus nodes after result approvals for all of its chunks emitted. + s.BlockState.WaitForSealed(s.T(), blockB.Header.Height) +} diff --git a/integration/tests/verification/suite.go b/integration/tests/verification/suite.go index e4d4e3c0ee5..8c030941327 100644 --- a/integration/tests/verification/suite.go +++ b/integration/tests/verification/suite.go @@ -25,8 +25,9 @@ type Suite struct { nodeConfigs []testnet.NodeConfig // used to keep configuration of nodes in testnet nodeIDs []flow.Identifier // used to keep identifier of nodes in testnet ghostID flow.Identifier // represents id of ghost node - exeID flow.Identifier // represents id of execution node - verID flow.Identifier // represents id of verification node + exe1ID flow.Identifier + exe2ID flow.Identifier + verID flow.Identifier // represents id of verification node } // Ghost returns a client to interact with the Ghost node on testnet. @@ -75,6 +76,8 @@ func (s *Suite) SetupTest() { testnet.WithID(nodeID), testnet.WithLogLevel(zerolog.FatalLevel), testnet.WithAdditionalFlag("--hotstuff-timeout=12s"), + testnet.WithAdditionalFlag("--required-verification-seal-approvals=1"), + testnet.WithAdditionalFlag("--required-construction-seal-approvals=1"), testnet.WithAdditionalFlag(blockRateFlag), ) s.nodeConfigs = append(s.nodeConfigs, nodeConfig) @@ -87,13 +90,19 @@ func (s *Suite) SetupTest() { testnet.WithLogLevel(zerolog.InfoLevel)) s.nodeConfigs = append(s.nodeConfigs, verConfig) - // generates one execution nodes - s.exeID = unittest.IdentifierFixture() + // generates two execution nodes + s.exe1ID = unittest.IdentifierFixture() exe1Config := testnet.NewNodeConfig(flow.RoleExecution, - testnet.WithID(s.exeID), + testnet.WithID(s.exe1ID), testnet.WithLogLevel(zerolog.FatalLevel)) s.nodeConfigs = append(s.nodeConfigs, exe1Config) + s.exe2ID = unittest.IdentifierFixture() + exe2Config := testnet.NewNodeConfig(flow.RoleExecution, + testnet.WithID(s.exe2ID), + testnet.WithLogLevel(zerolog.FatalLevel)) + s.nodeConfigs = append(s.nodeConfigs, exe2Config) + // generates two collection node coll1Config := testnet.NewNodeConfig(flow.RoleCollection, testnet.WithLogLevel(zerolog.FatalLevel), diff --git a/integration/tests/verification/system_chunk_test.go b/integration/tests/verification/system_chunk_test.go index 6a823470adc..1702aa68bb3 100644 --- a/integration/tests/verification/system_chunk_test.go +++ b/integration/tests/verification/system_chunk_test.go @@ -27,12 +27,12 @@ func (st *SystemChunkTestSuite) TestSystemChunkIDsShouldBeDifferent() { st.T().Logf("blockB generated, height: %v ID: %v", blockB.Header.Height, blockB.Header.ID()) // waits for execution receipt for blockA from execution node, called receiptA. - receiptA := st.ReceiptState.WaitForReceiptFrom(st.T(), blockA.Header.ID(), st.exeID) + receiptA := st.ReceiptState.WaitForReceiptFrom(st.T(), blockA.Header.ID(), st.exe1ID) resultAId := receiptA.ExecutionResult.ID() st.T().Logf("receipt for blockA generated: result ID: %x", resultAId) // waits for execution receipt for blockB from execution node, called receiptB. - receiptB := st.ReceiptState.WaitForReceiptFrom(st.T(), blockB.Header.ID(), st.exeID) + receiptB := st.ReceiptState.WaitForReceiptFrom(st.T(), blockB.Header.ID(), st.exe1ID) resultBId := receiptB.ExecutionResult.ID() st.T().Logf("receipt for blockB generated: result ID: %x", resultBId) From caeb04dc530a7f7ffc4cd5b12d926645702c8735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Wed, 14 Jul 2021 20:14:00 -0400 Subject: [PATCH 6/7] Add a Makefile target that checks the relic submodule version Check it matches the script crypto/build_dependency.sh Add that check to CI - avoids accidental relic updates, - avoids forgetting to update the script when updating the submodule --- Makefile | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 04aad5e9c93..a04b88ae145 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,10 @@ K8S_YAMLS_LOCATION_STAGING=./k8s/staging export CONTAINER_REGISTRY := gcr.io/flow-container-registry export DOCKER_BUILDKIT := 1 +# relic versions in script and submodule +export LOCAL_VERSION := $(shell git submodule status | egrep '\s[0-9a-f]' | cut -c 2-9) +export SCRIPT_VERSION := $(shell egrep 'relic_version="[0-9a-f]{8}"' ./crypto/build_dependency.sh| cut -c 16-23) + .PHONY: crypto/relic crypto/relic: rm -rf crypto/relic @@ -38,6 +42,15 @@ crypto/relic: crypto/relic/build: crypto/relic ./crypto/relic_build.sh +.PHONY: crypto/relic/check +crypto/relic/check: +ifeq ($(SCRIPT_VERSION), $(LOCAL_VERSION)) + @echo "Relic submodule version matches script, good!" +else + $(error Mismatch between relic submodule commit and the version in ./crypto/build_dependency.sh) +endif + + crypto/relic/update: git submodule update --recursive @@ -134,7 +147,7 @@ generate-mocks: # this ensures there is no unused dependency being added by accident .PHONY: tidy -tidy: +tidy: crypto/relic/check go mod tidy cd integration; go mod tidy cd crypto; go mod tidy From ce3b61bad2c71c0871c40f078d9547a1b02f179e Mon Sep 17 00:00:00 2001 From: Yura Date: Mon, 19 Jul 2021 09:41:35 +0300 Subject: [PATCH 7/7] [Consensus] Enforcing block timestamps (#948) * Refactored builder to accept block timestamp as utility structure * Extracted interface from block timestamp. Updated tests. Injected block timestamp into hotstuff validator. * Extracted BlockTimestamp into separate module. Added tests * Godoc. Updated tests * Added noop block timestamp. Linted * Apply suggestions from code review Co-authored-by: Alexander Hentschel * Updated naming. Applied suggestions from PR * More fixes from PR comments * Moving block timer checks to compliaence layer * Fixed tests. Linted. Fixed compilation * Apply comments from PR review. Adjusted tests Co-authored-by: Alexander Hentschel --- cmd/access/main.go | 5 +- cmd/collection/main.go | 2 + cmd/consensus/main.go | 11 +- cmd/execution/main.go | 2 + cmd/verification/main.go | 2 + consensus/hotstuff/model/errors.go | 1 - consensus/hotstuff/validator/validator.go | 6 +- consensus/integration/nodes_test.go | 7 +- engine/testutil/nodes.go | 7 +- module/builder/collection/builder_test.go | 3 +- module/builder/consensus/builder.go | 24 +-- module/builder/consensus/builder_test.go | 1 - module/builder/consensus/config.go | 15 +- module/mocks/network.go | 63 +++--- state/cluster/badger/mutator_test.go | 3 +- state/protocol/badger/mutator.go | 37 ++-- state/protocol/badger/mutator_test.go | 45 +++- state/protocol/blocktimer.go | 16 ++ state/protocol/blocktimer/blocktimer.go | 77 +++++++ state/protocol/blocktimer/blocktimer_test.go | 90 ++++++++ state/protocol/blocktimer/noop.go | 19 ++ state/protocol/errors.go | 23 +++ state/protocol/mock/block_timer.go | 42 ++++ state/protocol/util/testing.go | 24 ++- storage/mocks/storage.go | 207 +++++++++---------- 25 files changed, 539 insertions(+), 193 deletions(-) create mode 100644 state/protocol/blocktimer.go create mode 100644 state/protocol/blocktimer/blocktimer.go create mode 100644 state/protocol/blocktimer/blocktimer_test.go create mode 100644 state/protocol/blocktimer/noop.go create mode 100644 state/protocol/mock/block_timer.go diff --git a/cmd/access/main.go b/cmd/access/main.go index 942e7796ca7..04a54478a50 100644 --- a/cmd/access/main.go +++ b/cmd/access/main.go @@ -37,6 +37,7 @@ import ( "github.com/onflow/flow-go/module/synchronization" "github.com/onflow/flow-go/state/protocol" badgerState "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/blocktimer" storage "github.com/onflow/flow-go/storage/badger" grpcutils "github.com/onflow/flow-go/utils/grpc" ) @@ -119,6 +120,7 @@ func main() { node.Storage.Payloads, node.Tracer, node.ProtocolEvents, + blocktimer.DefaultBlockTimer, ) return err }). @@ -282,7 +284,8 @@ func main() { // creates a consensus follower with ingestEngine as the notifier // so that it gets notified upon each new finalized block - followerCore, err := consensus.NewFollower(node.Logger, committee, node.Storage.Headers, final, verifier, finalizationDistributor, node.RootBlock.Header, node.RootQC, finalized, pending) + followerCore, err := consensus.NewFollower(node.Logger, committee, node.Storage.Headers, final, verifier, + finalizationDistributor, node.RootBlock.Header, node.RootQC, finalized, pending) if err != nil { return nil, fmt.Errorf("could not initialize follower core: %w", err) } diff --git a/cmd/collection/main.go b/cmd/collection/main.go index 96505656a31..287c27edbf5 100644 --- a/cmd/collection/main.go +++ b/cmd/collection/main.go @@ -39,6 +39,7 @@ import ( "github.com/onflow/flow-go/module/synchronization" "github.com/onflow/flow-go/state/protocol" badgerState "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/blocktimer" "github.com/onflow/flow-go/state/protocol/events/gadgets" storagekv "github.com/onflow/flow-go/storage/badger" ) @@ -139,6 +140,7 @@ func main() { node.Storage.Payloads, node.Tracer, node.ProtocolEvents, + blocktimer.DefaultBlockTimer, ) return err }). diff --git a/cmd/consensus/main.go b/cmd/consensus/main.go index 426df9edca8..ed54bca123b 100644 --- a/cmd/consensus/main.go +++ b/cmd/consensus/main.go @@ -48,6 +48,7 @@ import ( "github.com/onflow/flow-go/module/validation" "github.com/onflow/flow-go/state/protocol" badgerState "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/blocktimer" bstorage "github.com/onflow/flow-go/storage/badger" "github.com/onflow/flow-go/utils/io" ) @@ -91,6 +92,7 @@ func main() { receiptValidator module.ReceiptValidator chunkAssigner *chmodule.ChunkAssigner finalizationDistributor *pubsub.FinalizationDistributor + blockTimer protocol.BlockTimer ) cmd.FlowNode(flow.RoleConsensus.String()). @@ -167,12 +169,18 @@ func main() { return fmt.Errorf("could not instantiate seal validator: %w", err) } + blockTimer, err = blocktimer.NewBlockTimer(minInterval, maxInterval) + if err != nil { + return err + } + mutableState, err = badgerState.NewFullConsensusState( state, node.Storage.Index, node.Storage.Payloads, node.Tracer, node.ProtocolEvents, + blockTimer, receiptValidator, sealValidator) return err @@ -384,8 +392,7 @@ func main() { consensusMempools.NewIncorporatedResultSeals(seals, node.Storage.Receipts), receipts, node.Tracer, - builder.WithMinInterval(minInterval), - builder.WithMaxInterval(maxInterval), + builder.WithBlockTimer(blockTimer), builder.WithMaxSealCount(maxSealPerBlock), builder.WithMaxGuaranteeCount(maxGuaranteePerBlock), ) diff --git a/cmd/execution/main.go b/cmd/execution/main.go index fc75c73ac49..c16fcde16fd 100644 --- a/cmd/execution/main.go +++ b/cmd/execution/main.go @@ -47,6 +47,7 @@ import ( chainsync "github.com/onflow/flow-go/module/synchronization" "github.com/onflow/flow-go/state/protocol" badgerState "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/blocktimer" storage "github.com/onflow/flow-go/storage/badger" ) @@ -137,6 +138,7 @@ func main() { node.Storage.Payloads, node.Tracer, node.ProtocolEvents, + blocktimer.DefaultBlockTimer, ) return err }). diff --git a/cmd/verification/main.go b/cmd/verification/main.go index 5d9380efba3..ef04b75c8fe 100644 --- a/cmd/verification/main.go +++ b/cmd/verification/main.go @@ -35,6 +35,7 @@ import ( "github.com/onflow/flow-go/module/synchronization" "github.com/onflow/flow-go/state/protocol" badgerState "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/blocktimer" storage "github.com/onflow/flow-go/storage/badger" ) @@ -102,6 +103,7 @@ func main() { node.Storage.Payloads, node.Tracer, node.ProtocolEvents, + blocktimer.DefaultBlockTimer, ) return err }). diff --git a/consensus/hotstuff/model/errors.go b/consensus/hotstuff/model/errors.go index e3ebf46b4c1..38f54f82527 100644 --- a/consensus/hotstuff/model/errors.go +++ b/consensus/hotstuff/model/errors.go @@ -3,7 +3,6 @@ package model import ( "errors" "fmt" - "github.com/onflow/flow-go/model/flow" ) diff --git a/consensus/hotstuff/validator/validator.go b/consensus/hotstuff/validator/validator.go index 1192fef8d9f..10599398744 100644 --- a/consensus/hotstuff/validator/validator.go +++ b/consensus/hotstuff/validator/validator.go @@ -19,7 +19,11 @@ type Validator struct { } // New creates a new Validator instance -func New(committee hotstuff.Committee, forks hotstuff.ForksReader, verifier hotstuff.Verifier) *Validator { +func New( + committee hotstuff.Committee, + forks hotstuff.ForksReader, + verifier hotstuff.Verifier, +) *Validator { return &Validator{ committee: committee, forks: forks, diff --git a/consensus/integration/nodes_test.go b/consensus/integration/nodes_test.go index 4618a830a0b..bf1d11a7ce8 100644 --- a/consensus/integration/nodes_test.go +++ b/consensus/integration/nodes_test.go @@ -33,6 +33,7 @@ import ( "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/state/protocol" bprotocol "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/blocktimer" "github.com/onflow/flow-go/state/protocol/events" "github.com/onflow/flow-go/state/protocol/inmem" "github.com/onflow/flow-go/state/protocol/util" @@ -131,7 +132,11 @@ func createNode( state, err := bprotocol.Bootstrap(metrics, db, headersDB, sealsDB, resultsDB, blocksDB, setupsDB, commitsDB, statusesDB, rootSnapshot) require.NoError(t, err) - fullState, err := bprotocol.NewFullConsensusState(state, indexDB, payloadsDB, tracer, consumer, util.MockReceiptValidator(), util.MockSealValidator(sealsDB)) + blockTimer, err := blocktimer.NewBlockTimer(1*time.Millisecond, 90*time.Second) + require.NoError(t, err) + + fullState, err := bprotocol.NewFullConsensusState(state, indexDB, payloadsDB, tracer, consumer, + blockTimer, util.MockReceiptValidator(), util.MockSealValidator(sealsDB)) require.NoError(t, err) localID := identity.ID() diff --git a/engine/testutil/nodes.go b/engine/testutil/nodes.go index 5fb312b3ed6..e160a037767 100644 --- a/engine/testutil/nodes.go +++ b/engine/testutil/nodes.go @@ -64,6 +64,7 @@ import ( "github.com/onflow/flow-go/network/stub" "github.com/onflow/flow-go/state/protocol" badgerstate "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/blocktimer" "github.com/onflow/flow-go/state/protocol/events" "github.com/onflow/flow-go/state/protocol/util" storage "github.com/onflow/flow-go/storage/badger" @@ -164,7 +165,8 @@ func CompleteStateFixture(t testing.TB, metric *metrics.NoopCollector, tracer mo state, err := badgerstate.Bootstrap(metric, db, s.Headers, s.Seals, s.Results, s.Blocks, s.Setups, s.EpochCommits, s.Statuses, rootSnapshot) require.NoError(t, err) - mutableState, err := badgerstate.NewFullConsensusState(state, s.Index, s.Payloads, tracer, consumer, util.MockReceiptValidator(), util.MockSealValidator(s.Seals)) + mutableState, err := badgerstate.NewFullConsensusState(state, s.Index, s.Payloads, tracer, consumer, + util.MockBlockTimer(), util.MockReceiptValidator(), util.MockSealValidator(s.Seals)) require.NoError(t, err) return &testmock.StateFixture{ @@ -365,7 +367,8 @@ func ExecutionNode(t *testing.T, hub *stub.Hub, identity *flow.Identity, identit protoState, ok := node.State.(*badgerstate.MutableState) require.True(t, ok) - followerState, err := badgerstate.NewFollowerState(protoState.State, node.Index, node.Payloads, node.Tracer, node.ProtocolEvents) + followerState, err := badgerstate.NewFollowerState(protoState.State, node.Index, node.Payloads, node.Tracer, + node.ProtocolEvents, blocktimer.DefaultBlockTimer) require.NoError(t, err) pendingBlocks := buffer.NewPendingBlocks() // for following main chain consensus diff --git a/module/builder/collection/builder_test.go b/module/builder/collection/builder_test.go index 86faee66a5e..717cc5c21bd 100644 --- a/module/builder/collection/builder_test.go +++ b/module/builder/collection/builder_test.go @@ -23,6 +23,7 @@ import ( pbadger "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/events" "github.com/onflow/flow-go/state/protocol/inmem" + "github.com/onflow/flow-go/state/protocol/util" storage "github.com/onflow/flow-go/storage/badger" "github.com/onflow/flow-go/storage/badger/procedure" sutil "github.com/onflow/flow-go/storage/util" @@ -97,7 +98,7 @@ func (suite *BuilderSuite) SetupTest() { state, err := pbadger.Bootstrap(metrics, suite.db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(suite.T(), err) - suite.protoState, err = pbadger.NewFollowerState(state, index, conPayloads, tracer, consumer) + suite.protoState, err = pbadger.NewFollowerState(state, index, conPayloads, tracer, consumer, util.MockBlockTimer()) require.NoError(suite.T(), err) // add some transactions to transaction pool diff --git a/module/builder/consensus/builder.go b/module/builder/consensus/builder.go index e5660af6cce..810556ddcf8 100644 --- a/module/builder/consensus/builder.go +++ b/module/builder/consensus/builder.go @@ -15,6 +15,7 @@ import ( "github.com/onflow/flow-go/module/trace" "github.com/onflow/flow-go/state/fork" "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/state/protocol/blocktimer" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/storage/badger/operation" ) @@ -56,10 +57,14 @@ func NewBuilder( options ...func(*Config), ) (*Builder, error) { + blockTimer, err := blocktimer.NewBlockTimer(500*time.Millisecond, 10*time.Second) + if err != nil { + return nil, fmt.Errorf("could not create default block timer: %w", err) + } + // initialize default config cfg := Config{ - minInterval: 500 * time.Millisecond, - maxInterval: 10 * time.Second, + blockTimer: blockTimer, maxSealCount: 100, maxGuaranteeCount: 100, maxReceiptCount: 200, @@ -88,7 +93,7 @@ func NewBuilder( cfg: cfg, } - err := b.repopulateExecutionTree() + err = b.repopulateExecutionTree() if err != nil { return nil, fmt.Errorf("could not repopulate execution tree: %w", err) } @@ -607,18 +612,7 @@ func (b *Builder) createProposal(parentID flow.Identifier, return nil, fmt.Errorf("could not retrieve parent: %w", err) } - // calculate the timestamp and cutoffs - timestamp := time.Now().UTC() - from := parent.Timestamp.Add(b.cfg.minInterval) - to := parent.Timestamp.Add(b.cfg.maxInterval) - - // adjust timestamp if outside of cutoffs - if timestamp.Before(from) { - timestamp = from - } - if timestamp.After(to) { - timestamp = to - } + timestamp := b.cfg.blockTimer.Build(parent.Timestamp) // construct default block on top of the provided parent header := &flow.Header{ diff --git a/module/builder/consensus/builder_test.go b/module/builder/consensus/builder_test.go index 4a5e3ef8f63..f98ff6584aa 100644 --- a/module/builder/consensus/builder_test.go +++ b/module/builder/consensus/builder_test.go @@ -425,7 +425,6 @@ func (bs *BuilderSuite) SetupTest() { require.NoError(bs.T(), err) bs.build.cfg.expiry = 11 - } func (bs *BuilderSuite) TearDownTest() { diff --git a/module/builder/consensus/config.go b/module/builder/consensus/config.go index 0e1a730a878..8c1df13b213 100644 --- a/module/builder/consensus/config.go +++ b/module/builder/consensus/config.go @@ -3,12 +3,11 @@ package consensus import ( - "time" + "github.com/onflow/flow-go/state/protocol" ) type Config struct { - minInterval time.Duration - maxInterval time.Duration + blockTimer protocol.BlockTimer // the max number of seals to be included in a block proposal maxSealCount uint maxGuaranteeCount uint @@ -16,15 +15,9 @@ type Config struct { expiry uint } -func WithMinInterval(minInterval time.Duration) func(*Config) { +func WithBlockTimer(timer protocol.BlockTimer) func(*Config) { return func(cfg *Config) { - cfg.minInterval = minInterval - } -} - -func WithMaxInterval(maxInterval time.Duration) func(*Config) { - return func(cfg *Config) { - cfg.maxInterval = maxInterval + cfg.blockTimer = timer } } diff --git a/module/mocks/network.go b/module/mocks/network.go index b680ae44c89..38f65a552ce 100644 --- a/module/mocks/network.go +++ b/module/mocks/network.go @@ -5,39 +5,38 @@ package mocks import ( - reflect "reflect" - gomock "github.com/golang/mock/gomock" crypto "github.com/onflow/flow-go/crypto" hash "github.com/onflow/flow-go/crypto/hash" flow "github.com/onflow/flow-go/model/flow" network "github.com/onflow/flow-go/network" + reflect "reflect" ) -// MockNetwork is a mock of Network interface. +// MockNetwork is a mock of Network interface type MockNetwork struct { ctrl *gomock.Controller recorder *MockNetworkMockRecorder } -// MockNetworkMockRecorder is the mock recorder for MockNetwork. +// MockNetworkMockRecorder is the mock recorder for MockNetwork type MockNetworkMockRecorder struct { mock *MockNetwork } -// NewMockNetwork creates a new mock instance. +// NewMockNetwork creates a new mock instance func NewMockNetwork(ctrl *gomock.Controller) *MockNetwork { mock := &MockNetwork{ctrl: ctrl} mock.recorder = &MockNetworkMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockNetwork) EXPECT() *MockNetworkMockRecorder { return m.recorder } -// Register mocks base method. +// Register mocks base method func (m *MockNetwork) Register(arg0 network.Channel, arg1 network.Engine) (network.Conduit, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Register", arg0, arg1) @@ -46,36 +45,36 @@ func (m *MockNetwork) Register(arg0 network.Channel, arg1 network.Engine) (netwo return ret0, ret1 } -// Register indicates an expected call of Register. +// Register indicates an expected call of Register func (mr *MockNetworkMockRecorder) Register(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockNetwork)(nil).Register), arg0, arg1) } -// MockLocal is a mock of Local interface. +// MockLocal is a mock of Local interface type MockLocal struct { ctrl *gomock.Controller recorder *MockLocalMockRecorder } -// MockLocalMockRecorder is the mock recorder for MockLocal. +// MockLocalMockRecorder is the mock recorder for MockLocal type MockLocalMockRecorder struct { mock *MockLocal } -// NewMockLocal creates a new mock instance. +// NewMockLocal creates a new mock instance func NewMockLocal(ctrl *gomock.Controller) *MockLocal { mock := &MockLocal{ctrl: ctrl} mock.recorder = &MockLocalMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockLocal) EXPECT() *MockLocalMockRecorder { return m.recorder } -// Address mocks base method. +// Address mocks base method func (m *MockLocal) Address() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Address") @@ -83,13 +82,13 @@ func (m *MockLocal) Address() string { return ret0 } -// Address indicates an expected call of Address. +// Address indicates an expected call of Address func (mr *MockLocalMockRecorder) Address() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Address", reflect.TypeOf((*MockLocal)(nil).Address)) } -// NodeID mocks base method. +// NodeID mocks base method func (m *MockLocal) NodeID() flow.Identifier { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NodeID") @@ -97,13 +96,13 @@ func (m *MockLocal) NodeID() flow.Identifier { return ret0 } -// NodeID indicates an expected call of NodeID. +// NodeID indicates an expected call of NodeID func (mr *MockLocalMockRecorder) NodeID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeID", reflect.TypeOf((*MockLocal)(nil).NodeID)) } -// NotMeFilter mocks base method. +// NotMeFilter mocks base method func (m *MockLocal) NotMeFilter() flow.IdentityFilter { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NotMeFilter") @@ -111,13 +110,13 @@ func (m *MockLocal) NotMeFilter() flow.IdentityFilter { return ret0 } -// NotMeFilter indicates an expected call of NotMeFilter. +// NotMeFilter indicates an expected call of NotMeFilter func (mr *MockLocalMockRecorder) NotMeFilter() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotMeFilter", reflect.TypeOf((*MockLocal)(nil).NotMeFilter)) } -// Sign mocks base method. +// Sign mocks base method func (m *MockLocal) Sign(arg0 []byte, arg1 hash.Hasher) (crypto.Signature, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Sign", arg0, arg1) @@ -126,13 +125,13 @@ func (m *MockLocal) Sign(arg0 []byte, arg1 hash.Hasher) (crypto.Signature, error return ret0, ret1 } -// Sign indicates an expected call of Sign. +// Sign indicates an expected call of Sign func (mr *MockLocalMockRecorder) Sign(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sign", reflect.TypeOf((*MockLocal)(nil).Sign), arg0, arg1) } -// SignFunc mocks base method. +// SignFunc mocks base method func (m *MockLocal) SignFunc(arg0 []byte, arg1 hash.Hasher, arg2 func(crypto.PrivateKey, []byte, hash.Hasher) (crypto.Signature, error)) (crypto.Signature, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SignFunc", arg0, arg1, arg2) @@ -141,66 +140,66 @@ func (m *MockLocal) SignFunc(arg0 []byte, arg1 hash.Hasher, arg2 func(crypto.Pri return ret0, ret1 } -// SignFunc indicates an expected call of SignFunc. +// SignFunc indicates an expected call of SignFunc func (mr *MockLocalMockRecorder) SignFunc(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignFunc", reflect.TypeOf((*MockLocal)(nil).SignFunc), arg0, arg1, arg2) } -// MockRequester is a mock of Requester interface. +// MockRequester is a mock of Requester interface type MockRequester struct { ctrl *gomock.Controller recorder *MockRequesterMockRecorder } -// MockRequesterMockRecorder is the mock recorder for MockRequester. +// MockRequesterMockRecorder is the mock recorder for MockRequester type MockRequesterMockRecorder struct { mock *MockRequester } -// NewMockRequester creates a new mock instance. +// NewMockRequester creates a new mock instance func NewMockRequester(ctrl *gomock.Controller) *MockRequester { mock := &MockRequester{ctrl: ctrl} mock.recorder = &MockRequesterMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockRequester) EXPECT() *MockRequesterMockRecorder { return m.recorder } -// EntityByID mocks base method. +// EntityByID mocks base method func (m *MockRequester) EntityByID(arg0 flow.Identifier, arg1 flow.IdentityFilter) { m.ctrl.T.Helper() m.ctrl.Call(m, "EntityByID", arg0, arg1) } -// EntityByID indicates an expected call of EntityByID. +// EntityByID indicates an expected call of EntityByID func (mr *MockRequesterMockRecorder) EntityByID(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EntityByID", reflect.TypeOf((*MockRequester)(nil).EntityByID), arg0, arg1) } -// Force mocks base method. +// Force mocks base method func (m *MockRequester) Force() { m.ctrl.T.Helper() m.ctrl.Call(m, "Force") } -// Force indicates an expected call of Force. +// Force indicates an expected call of Force func (mr *MockRequesterMockRecorder) Force() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Force", reflect.TypeOf((*MockRequester)(nil).Force)) } -// Query mocks base method. +// Query mocks base method func (m *MockRequester) Query(arg0 flow.Identifier, arg1 flow.IdentityFilter) { m.ctrl.T.Helper() m.ctrl.Call(m, "Query", arg0, arg1) } -// Query indicates an expected call of Query. +// Query indicates an expected call of Query func (mr *MockRequesterMockRecorder) Query(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockRequester)(nil).Query), arg0, arg1) diff --git a/state/cluster/badger/mutator_test.go b/state/cluster/badger/mutator_test.go index 9abd3f157dd..728760b1ef7 100644 --- a/state/cluster/badger/mutator_test.go +++ b/state/cluster/badger/mutator_test.go @@ -21,6 +21,7 @@ import ( pbadger "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/events" "github.com/onflow/flow-go/state/protocol/inmem" + protocolutil "github.com/onflow/flow-go/state/protocol/util" storage "github.com/onflow/flow-go/storage/badger" "github.com/onflow/flow-go/storage/badger/operation" "github.com/onflow/flow-go/storage/badger/procedure" @@ -85,7 +86,7 @@ func (suite *MutatorSuite) SetupTest() { state, err := pbadger.Bootstrap(metrics, suite.db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(suite.T(), err) - suite.protoState, err = pbadger.NewFollowerState(state, index, conPayloads, tracer, consumer) + suite.protoState, err = pbadger.NewFollowerState(state, index, conPayloads, tracer, consumer, protocolutil.MockBlockTimer()) require.NoError(suite.T(), err) } diff --git a/state/protocol/badger/mutator.go b/state/protocol/badger/mutator.go index c0e6f89124c..c4a992007b0 100644 --- a/state/protocol/badger/mutator.go +++ b/state/protocol/badger/mutator.go @@ -33,11 +33,12 @@ import ( type FollowerState struct { *State - index storage.Index - payloads storage.Payloads - tracer module.Tracer - consumer protocol.Consumer - cfg Config + index storage.Index + payloads storage.Payloads + tracer module.Tracer + consumer protocol.Consumer + blockTimer protocol.BlockTimer + cfg Config } // MutableState implements a mutable protocol state. When extending the @@ -56,14 +57,16 @@ func NewFollowerState( payloads storage.Payloads, tracer module.Tracer, consumer protocol.Consumer, + blockTimer protocol.BlockTimer, ) (*FollowerState, error) { followerState := &FollowerState{ - State: state, - index: index, - payloads: payloads, - tracer: tracer, - consumer: consumer, - cfg: DefaultConfig(), + State: state, + index: index, + payloads: payloads, + tracer: tracer, + consumer: consumer, + blockTimer: blockTimer, + cfg: DefaultConfig(), } return followerState, nil } @@ -78,10 +81,11 @@ func NewFullConsensusState( payloads storage.Payloads, tracer module.Tracer, consumer protocol.Consumer, + blockTimer protocol.BlockTimer, receiptValidator module.ReceiptValidator, sealValidator module.SealValidator, ) (*MutableState, error) { - followerState, err := NewFollowerState(state, index, payloads, tracer, consumer) + followerState, err := NewFollowerState(state, index, payloads, tracer, consumer, blockTimer) if err != nil { return nil, fmt.Errorf("initialization of Mutable Follower State failed: %w", err) } @@ -196,6 +200,15 @@ func (m *FollowerState) headerExtend(candidate *flow.Block) error { header.Height, parent.Height) } + // check validity of block timestamp using parent's timestamp + err = m.blockTimer.Validate(parent.Timestamp, candidate.Header.Timestamp) + if err != nil { + if protocol.IsInvalidBlockTimestampError(err) { + return state.NewInvalidExtensionErrorf("candidate contains invalid timestamp: %w", err) + } + return fmt.Errorf("validating block's time stamp failed with unexpected error: %w", err) + } + // THIRD: Once we have established the block is valid within itself, and the // block is valid in relation to its parent, we can check whether it is // valid in the context of the entire state. For this, the block needs to diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index 9ee087e62d3..b636254355c 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -23,6 +23,7 @@ import ( mockmodule "github.com/onflow/flow-go/module/mock" "github.com/onflow/flow-go/module/trace" st "github.com/onflow/flow-go/state" + realprotocol "github.com/onflow/flow-go/state/protocol" protocol "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/events" "github.com/onflow/flow-go/state/protocol/inmem" @@ -98,7 +99,7 @@ func TestExtendValid(t *testing.T) { state, err := protocol.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) - fullState, err := protocol.NewFullConsensusState(state, index, payloads, tracer, consumer, + fullState, err := protocol.NewFullConsensusState(state, index, payloads, tracer, consumer, util.MockBlockTimer(), util.MockReceiptValidator(), util.MockSealValidator(seals)) require.NoError(t, err) @@ -468,7 +469,8 @@ func TestExtendEpochTransitionValid(t *testing.T) { require.NoError(t, err) receiptValidator := util.MockReceiptValidator() sealValidator := util.MockSealValidator(seals) - state, err := protocol.NewFullConsensusState(protoState, index, payloads, tracer, consumer, receiptValidator, sealValidator) + state, err := protocol.NewFullConsensusState(protoState, index, payloads, tracer, consumer, + util.MockBlockTimer(), receiptValidator, sealValidator) require.NoError(t, err) head, err := rootSnapshot.Head() @@ -1204,7 +1206,7 @@ func TestExtendInvalidSealsInBlock(t *testing.T) { Times(3) fullState, err := protocol.NewFullConsensusState(state, index, payloads, tracer, consumer, - util.MockReceiptValidator(), sealValidator) + util.MockBlockTimer(), util.MockReceiptValidator(), sealValidator) require.NoError(t, err) err = fullState.Extend(&block1) @@ -1494,3 +1496,40 @@ func TestCacheAtomicity(t *testing.T) { wg.Wait() }) } + +// TestHeaderInvalidTimestamp tests that extending header with invalid timestamp results in sentinel error +func TestHeaderInvalidTimestamp(t *testing.T) { + unittest.RunWithBadgerDB(t, func(db *badger.DB) { + metrics := metrics.NewNoopCollector() + tracer := trace.NewNoopTracer() + headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := storeutil.StorageLayer(t, db) + + // create a event consumer to test epoch transition events + distributor := events.NewDistributor() + consumer := new(mockprotocol.Consumer) + distributor.AddConsumer(consumer) + + block, result, seal := unittest.BootstrapFixture(participants) + qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(block.ID())) + rootSnapshot, err := inmem.SnapshotFromBootstrapState(block, result, seal, qc) + require.NoError(t, err) + + state, err := protocol.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) + require.NoError(t, err) + + blockTimer := &mockprotocol.BlockTimer{} + blockTimer.On("Validate", mock.Anything, mock.Anything).Return(realprotocol.NewInvalidBlockTimestamp("")) + + fullState, err := protocol.NewFullConsensusState(state, index, payloads, tracer, consumer, blockTimer, + util.MockReceiptValidator(), util.MockSealValidator(seals)) + require.NoError(t, err) + + extend := unittest.BlockWithParentFixture(block.Header) + extend.Payload.Guarantees = nil + extend.Header.PayloadHash = extend.Payload.Hash() + + err = fullState.Extend(&extend) + assert.Error(t, err, "a proposal with invalid timestamp has to be rejected") + assert.True(t, st.IsInvalidExtensionError(err), "if timestamp is invalid it should return invalid block error") + }) +} diff --git a/state/protocol/blocktimer.go b/state/protocol/blocktimer.go new file mode 100644 index 00000000000..50f75c9bc83 --- /dev/null +++ b/state/protocol/blocktimer.go @@ -0,0 +1,16 @@ +package protocol + +import ( + "time" +) + +// BlockTimer constructs and validates block timestamps. +type BlockTimer interface { + // Build generates a timestamp based on definition of valid timestamp. + Build(parentTimestamp time.Time) time.Time + // Validate checks validity of a block's time stamp. + // Error returns + // * `model.InvalidBlockTimestampError` if time stamp is invalid. + // * all other errors are unexpected and potentially symptoms of internal implementation bugs or state corruption (fatal). + Validate(parentTimestamp, currentTimestamp time.Time) error +} diff --git a/state/protocol/blocktimer/blocktimer.go b/state/protocol/blocktimer/blocktimer.go new file mode 100644 index 00000000000..5764eacfebd --- /dev/null +++ b/state/protocol/blocktimer/blocktimer.go @@ -0,0 +1,77 @@ +package blocktimer + +import ( + "fmt" + "time" + + "github.com/onflow/flow-go/state/protocol" +) + +// Is a functor that generates a current timestamp, usually it's just time.Now(). +// Used to make testing easier +type timestampGenerator = func() time.Time + +// BlockTimestamp is a helper structure that performs building and validation of valid +// timestamp for blocks that are generated by block builder and checked by hotstuff event loop. +// Let τ be the time stamp of the parent block and t be the current clock time of the proposer that is building the child block +// An honest proposer sets the Timestamp of its proposal according to the following rule: +// if t is within the interval [τ + minInterval, τ + maxInterval], then the proposer sets Timestamp := t +// otherwise, the proposer chooses the time stamp from the interval that is closest to its current time t, i.e. +// if t < τ + minInterval, the proposer sets Timestamp := τ + minInterval +// if τ + maxInterval < t, the proposer sets Timestamp := τ + maxInterval +type BlockTimestamp struct { + minInterval time.Duration + maxInterval time.Duration + generator timestampGenerator +} + +var DefaultBlockTimer = NewNoopBlockTimer() + +// NewBlockTimer creates new block timer with specific intervals and time.Now as generator +func NewBlockTimer(minInterval, maxInterval time.Duration) (*BlockTimestamp, error) { + if minInterval >= maxInterval { + return nil, fmt.Errorf("invariant minInterval < maxInterval is not satisfied, %d >= %d", minInterval, maxInterval) + } + if minInterval <= 0 { + return nil, fmt.Errorf("invariant minInterval > 0 it not satisifed") + } + + return &BlockTimestamp{ + minInterval: minInterval, + maxInterval: maxInterval, + generator: func() time.Time { return time.Now().UTC() }, + }, nil +} + +// Build generates a timestamp based on definition of valid timestamp. +func (b BlockTimestamp) Build(parentTimestamp time.Time) time.Time { + // calculate the timestamp and cutoffs + timestamp := b.generator() + from := parentTimestamp.Add(b.minInterval) + to := parentTimestamp.Add(b.maxInterval) + + // adjust timestamp if outside of cutoffs + if timestamp.Before(from) { + timestamp = from + } + if timestamp.After(to) { + timestamp = to + } + + return timestamp +} + +// Validate accepts parent and current timestamps and checks if current timestamp satisfies +// definition of valid timestamp. +// Timestamp is valid if: Timestamp ∈ [τ + minInterval, τ + maxInterval] +// Returns: +// * model.ErrInvalidBlockTimestamp - timestamp is invalid +// * nil - success +func (b BlockTimestamp) Validate(parentTimestamp, currentTimestamp time.Time) error { + from := parentTimestamp.Add(b.minInterval) + to := parentTimestamp.Add(b.maxInterval) + if currentTimestamp.Before(from) || currentTimestamp.After(to) { + return protocol.NewInvalidBlockTimestamp("timestamp %v is not within interval [%v; %v]", currentTimestamp, from, to) + } + return nil +} diff --git a/state/protocol/blocktimer/blocktimer_test.go b/state/protocol/blocktimer/blocktimer_test.go new file mode 100644 index 00000000000..1ac8f1fd5b6 --- /dev/null +++ b/state/protocol/blocktimer/blocktimer_test.go @@ -0,0 +1,90 @@ +package blocktimer + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/state/protocol" +) + +// TestBlockTimestamp_Validate tests that validation accepts valid time and rejects invalid +func TestBlockTimestamp_Validate(t *testing.T) { + t.Parallel() + builder, err := NewBlockTimer(10*time.Millisecond, 1*time.Second) + require.NoError(t, err) + t.Run("parentTime + minInterval + 1", func(t *testing.T) { + parentTime := time.Now().UTC() + blockTime := parentTime.Add(builder.minInterval + time.Millisecond) + require.NoError(t, builder.Validate(parentTime, blockTime)) + }) + t.Run("parentTime + minInterval", func(t *testing.T) { + parentTime := time.Now().UTC() + blockTime := parentTime.Add(builder.minInterval) + require.NoError(t, builder.Validate(parentTime, blockTime)) + }) + t.Run("parentTime + minInterval - 1", func(t *testing.T) { + parentTime := time.Now().UTC() + blockTime := parentTime.Add(builder.minInterval - time.Millisecond) + err := builder.Validate(parentTime, blockTime) + require.Error(t, err) + require.True(t, protocol.IsInvalidBlockTimestampError(err)) + }) + t.Run("parentTime + maxInterval - 1", func(t *testing.T) { + parentTime := time.Now().UTC() + blockTime := parentTime.Add(builder.maxInterval - time.Millisecond) + require.NoError(t, builder.Validate(parentTime, blockTime)) + }) + t.Run("parentTime + maxInterval", func(t *testing.T) { + parentTime := time.Now().UTC() + blockTime := parentTime.Add(builder.maxInterval) + require.NoError(t, builder.Validate(parentTime, blockTime)) + }) + t.Run("parentTime + maxInterval + 1", func(t *testing.T) { + parentTime := time.Now().UTC() + blockTime := parentTime.Add(builder.maxInterval + time.Millisecond) + err := builder.Validate(parentTime, blockTime) + require.Error(t, err) + require.True(t, protocol.IsInvalidBlockTimestampError(err)) + }) +} + +// TestBlockTimestamp_Build tests that builder correctly generates new block time +func TestBlockTimestamp_Build(t *testing.T) { + t.Parallel() + minInterval := 100 * time.Millisecond + maxInterval := 10 * time.Second + deltas := []time.Duration{0, minInterval, maxInterval} + + // this test tries to cover next scenarious in generic way: + // now = parent - 1 + // now = parent + // now = parent + 1 + // now = parent + minInterval - 1 + // now = parent + minInterval + // now = parent + minInterval + 1 + // now = parent + maxInterval - 1 + // now = parent + maxInterval + // now = parent + maxInterval + 1 + for _, durationDelta := range deltas { + duration := durationDelta + t.Run(fmt.Sprintf("duration-delta-%d", durationDelta), func(t *testing.T) { + builder, err := NewBlockTimer(minInterval, maxInterval) + require.NoError(t, err) + + parentTime := time.Now().UTC() + + // now = parentTime + delta + {-1, 0, +1} + for i := -1; i <= 1; i++ { + builder.generator = func() time.Time { + return parentTime.Add(duration + time.Millisecond*time.Duration(i)) + } + + blockTime := builder.Build(parentTime) + require.NoError(t, builder.Validate(parentTime, blockTime)) + } + }) + } +} diff --git a/state/protocol/blocktimer/noop.go b/state/protocol/blocktimer/noop.go new file mode 100644 index 00000000000..0d76f2fa50e --- /dev/null +++ b/state/protocol/blocktimer/noop.go @@ -0,0 +1,19 @@ +package blocktimer + +import "time" + +// NoopBlockTimer implements an always valid behavior for BlockTimestamp interface. +// Can be used by nodes that don't perform validation of block timestamps. +type NoopBlockTimer struct{} + +func NewNoopBlockTimer() *NoopBlockTimer { + return &NoopBlockTimer{} +} + +func (n NoopBlockTimer) Build(time.Time) time.Time { + return time.Now().UTC() +} + +func (n NoopBlockTimer) Validate(time.Time, time.Time) error { + return nil +} diff --git a/state/protocol/errors.go b/state/protocol/errors.go index b4508228b13..707b85d372a 100644 --- a/state/protocol/errors.go +++ b/state/protocol/errors.go @@ -34,3 +34,26 @@ func IsIdentityNotFound(err error) bool { var errIdentityNotFound IdentityNotFoundError return errors.As(err, &errIdentityNotFound) } + +type InvalidBlockTimestampError struct { + err error +} + +func (e InvalidBlockTimestampError) Unwrap() error { + return e.err +} + +func (e InvalidBlockTimestampError) Error() string { + return e.err.Error() +} + +func IsInvalidBlockTimestampError(err error) bool { + var errInvalidTimestampError InvalidBlockTimestampError + return errors.As(err, &errInvalidTimestampError) +} + +func NewInvalidBlockTimestamp(msg string, args ...interface{}) error { + return InvalidBlockTimestampError{ + err: fmt.Errorf(msg, args...), + } +} diff --git a/state/protocol/mock/block_timer.go b/state/protocol/mock/block_timer.go new file mode 100644 index 00000000000..4ba3418fdef --- /dev/null +++ b/state/protocol/mock/block_timer.go @@ -0,0 +1,42 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +package mock + +import ( + mock "github.com/stretchr/testify/mock" + + time "time" +) + +// BlockTimer is an autogenerated mock type for the BlockTimer type +type BlockTimer struct { + mock.Mock +} + +// Build provides a mock function with given fields: parentTimestamp +func (_m *BlockTimer) Build(parentTimestamp time.Time) time.Time { + ret := _m.Called(parentTimestamp) + + var r0 time.Time + if rf, ok := ret.Get(0).(func(time.Time) time.Time); ok { + r0 = rf(parentTimestamp) + } else { + r0 = ret.Get(0).(time.Time) + } + + return r0 +} + +// Validate provides a mock function with given fields: parentTimestamp, currentTimestamp +func (_m *BlockTimer) Validate(parentTimestamp time.Time, currentTimestamp time.Time) error { + ret := _m.Called(parentTimestamp, currentTimestamp) + + var r0 error + if rf, ok := ret.Get(0).(func(time.Time, time.Time) error); ok { + r0 = rf(parentTimestamp, currentTimestamp) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/state/protocol/util/testing.go b/state/protocol/util/testing.go index 3252abf2621..5e6b4ba2538 100644 --- a/state/protocol/util/testing.go +++ b/state/protocol/util/testing.go @@ -15,6 +15,7 @@ import ( "github.com/onflow/flow-go/state/protocol" pbadger "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/events" + mockprotocol "github.com/onflow/flow-go/state/protocol/mock" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/storage/util" "github.com/onflow/flow-go/utils/unittest" @@ -30,6 +31,14 @@ func MockReceiptValidator() module.ReceiptValidator { return validator } +// MockBlockTimer returns BlockTimer that accepts all timestamps +// without performing any checks. +func MockBlockTimer() protocol.BlockTimer { + blockTimer := &mockprotocol.BlockTimer{} + blockTimer.On("Validate", mock.Anything, mock.Anything).Return(nil) + return blockTimer +} + // MockSealValidator returns a SealValidator that accepts // all seals without performing any // integrity checks, returns first seal in block as valid one @@ -73,7 +82,8 @@ func RunWithFullProtocolState(t testing.TB, rootSnapshot protocol.Snapshot, f fu require.NoError(t, err) receiptValidator := MockReceiptValidator() sealValidator := MockSealValidator(seals) - fullState, err := pbadger.NewFullConsensusState(state, index, payloads, tracer, consumer, receiptValidator, sealValidator) + mockTimer := MockBlockTimer() + fullState, err := pbadger.NewFullConsensusState(state, index, payloads, tracer, consumer, mockTimer, receiptValidator, sealValidator) require.NoError(t, err) f(db, fullState) }) @@ -88,7 +98,8 @@ func RunWithFullProtocolStateAndValidator(t testing.TB, rootSnapshot protocol.Sn state, err := pbadger.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) sealValidator := MockSealValidator(seals) - fullState, err := pbadger.NewFullConsensusState(state, index, payloads, tracer, consumer, validator, sealValidator) + mockTimer := MockBlockTimer() + fullState, err := pbadger.NewFullConsensusState(state, index, payloads, tracer, consumer, mockTimer, validator, sealValidator) require.NoError(t, err) f(db, fullState) }) @@ -102,7 +113,8 @@ func RunWithFollowerProtocolState(t testing.TB, rootSnapshot protocol.Snapshot, headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := util.StorageLayer(t, db) state, err := pbadger.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) - followerState, err := pbadger.NewFollowerState(state, index, payloads, tracer, consumer) + mockTimer := MockBlockTimer() + followerState, err := pbadger.NewFollowerState(state, index, payloads, tracer, consumer, mockTimer) require.NoError(t, err) f(db, followerState) }) @@ -117,7 +129,8 @@ func RunWithFullProtocolStateAndConsumer(t testing.TB, rootSnapshot protocol.Sna require.NoError(t, err) receiptValidator := MockReceiptValidator() sealValidator := MockSealValidator(seals) - fullState, err := pbadger.NewFullConsensusState(state, index, payloads, tracer, consumer, receiptValidator, sealValidator) + mockTimer := MockBlockTimer() + fullState, err := pbadger.NewFullConsensusState(state, index, payloads, tracer, consumer, mockTimer, receiptValidator, sealValidator) require.NoError(t, err) f(db, fullState) }) @@ -131,7 +144,8 @@ func RunWithFollowerProtocolStateAndHeaders(t testing.TB, rootSnapshot protocol. headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := util.StorageLayer(t, db) state, err := pbadger.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) - followerState, err := pbadger.NewFollowerState(state, index, payloads, tracer, consumer) + mockTimer := MockBlockTimer() + followerState, err := pbadger.NewFollowerState(state, index, payloads, tracer, consumer, mockTimer) require.NoError(t, err) f(db, followerState, headers, index) }) diff --git a/storage/mocks/storage.go b/storage/mocks/storage.go index 7a829429fc4..2967a3e5cce 100644 --- a/storage/mocks/storage.go +++ b/storage/mocks/storage.go @@ -5,38 +5,37 @@ package mocks import ( - reflect "reflect" - gomock "github.com/golang/mock/gomock" flow "github.com/onflow/flow-go/model/flow" storage "github.com/onflow/flow-go/storage" transaction "github.com/onflow/flow-go/storage/badger/transaction" + reflect "reflect" ) -// MockBlocks is a mock of Blocks interface. +// MockBlocks is a mock of Blocks interface type MockBlocks struct { ctrl *gomock.Controller recorder *MockBlocksMockRecorder } -// MockBlocksMockRecorder is the mock recorder for MockBlocks. +// MockBlocksMockRecorder is the mock recorder for MockBlocks type MockBlocksMockRecorder struct { mock *MockBlocks } -// NewMockBlocks creates a new mock instance. +// NewMockBlocks creates a new mock instance func NewMockBlocks(ctrl *gomock.Controller) *MockBlocks { mock := &MockBlocks{ctrl: ctrl} mock.recorder = &MockBlocksMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockBlocks) EXPECT() *MockBlocksMockRecorder { return m.recorder } -// ByCollectionID mocks base method. +// ByCollectionID mocks base method func (m *MockBlocks) ByCollectionID(arg0 flow.Identifier) (*flow.Block, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByCollectionID", arg0) @@ -45,13 +44,13 @@ func (m *MockBlocks) ByCollectionID(arg0 flow.Identifier) (*flow.Block, error) { return ret0, ret1 } -// ByCollectionID indicates an expected call of ByCollectionID. +// ByCollectionID indicates an expected call of ByCollectionID func (mr *MockBlocksMockRecorder) ByCollectionID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByCollectionID", reflect.TypeOf((*MockBlocks)(nil).ByCollectionID), arg0) } -// ByHeight mocks base method. +// ByHeight mocks base method func (m *MockBlocks) ByHeight(arg0 uint64) (*flow.Block, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByHeight", arg0) @@ -60,13 +59,13 @@ func (m *MockBlocks) ByHeight(arg0 uint64) (*flow.Block, error) { return ret0, ret1 } -// ByHeight indicates an expected call of ByHeight. +// ByHeight indicates an expected call of ByHeight func (mr *MockBlocksMockRecorder) ByHeight(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByHeight", reflect.TypeOf((*MockBlocks)(nil).ByHeight), arg0) } -// ByID mocks base method. +// ByID mocks base method func (m *MockBlocks) ByID(arg0 flow.Identifier) (*flow.Block, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByID", arg0) @@ -75,13 +74,13 @@ func (m *MockBlocks) ByID(arg0 flow.Identifier) (*flow.Block, error) { return ret0, ret1 } -// ByID indicates an expected call of ByID. +// ByID indicates an expected call of ByID func (mr *MockBlocksMockRecorder) ByID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByID", reflect.TypeOf((*MockBlocks)(nil).ByID), arg0) } -// GetLastFullBlockHeight mocks base method. +// GetLastFullBlockHeight mocks base method func (m *MockBlocks) GetLastFullBlockHeight() (uint64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLastFullBlockHeight") @@ -90,13 +89,13 @@ func (m *MockBlocks) GetLastFullBlockHeight() (uint64, error) { return ret0, ret1 } -// GetLastFullBlockHeight indicates an expected call of GetLastFullBlockHeight. +// GetLastFullBlockHeight indicates an expected call of GetLastFullBlockHeight func (mr *MockBlocksMockRecorder) GetLastFullBlockHeight() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastFullBlockHeight", reflect.TypeOf((*MockBlocks)(nil).GetLastFullBlockHeight)) } -// IndexBlockForCollections mocks base method. +// IndexBlockForCollections mocks base method func (m *MockBlocks) IndexBlockForCollections(arg0 flow.Identifier, arg1 []flow.Identifier) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IndexBlockForCollections", arg0, arg1) @@ -104,13 +103,13 @@ func (m *MockBlocks) IndexBlockForCollections(arg0 flow.Identifier, arg1 []flow. return ret0 } -// IndexBlockForCollections indicates an expected call of IndexBlockForCollections. +// IndexBlockForCollections indicates an expected call of IndexBlockForCollections func (mr *MockBlocksMockRecorder) IndexBlockForCollections(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexBlockForCollections", reflect.TypeOf((*MockBlocks)(nil).IndexBlockForCollections), arg0, arg1) } -// Store mocks base method. +// Store mocks base method func (m *MockBlocks) Store(arg0 *flow.Block) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Store", arg0) @@ -118,13 +117,13 @@ func (m *MockBlocks) Store(arg0 *flow.Block) error { return ret0 } -// Store indicates an expected call of Store. +// Store indicates an expected call of Store func (mr *MockBlocksMockRecorder) Store(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Store", reflect.TypeOf((*MockBlocks)(nil).Store), arg0) } -// StoreTx mocks base method. +// StoreTx mocks base method func (m *MockBlocks) StoreTx(arg0 *flow.Block) func(*transaction.Tx) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StoreTx", arg0) @@ -132,13 +131,13 @@ func (m *MockBlocks) StoreTx(arg0 *flow.Block) func(*transaction.Tx) error { return ret0 } -// StoreTx indicates an expected call of StoreTx. +// StoreTx indicates an expected call of StoreTx func (mr *MockBlocksMockRecorder) StoreTx(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreTx", reflect.TypeOf((*MockBlocks)(nil).StoreTx), arg0) } -// UpdateLastFullBlockHeight mocks base method. +// UpdateLastFullBlockHeight mocks base method func (m *MockBlocks) UpdateLastFullBlockHeight(arg0 uint64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateLastFullBlockHeight", arg0) @@ -146,36 +145,36 @@ func (m *MockBlocks) UpdateLastFullBlockHeight(arg0 uint64) error { return ret0 } -// UpdateLastFullBlockHeight indicates an expected call of UpdateLastFullBlockHeight. +// UpdateLastFullBlockHeight indicates an expected call of UpdateLastFullBlockHeight func (mr *MockBlocksMockRecorder) UpdateLastFullBlockHeight(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateLastFullBlockHeight", reflect.TypeOf((*MockBlocks)(nil).UpdateLastFullBlockHeight), arg0) } -// MockHeaders is a mock of Headers interface. +// MockHeaders is a mock of Headers interface type MockHeaders struct { ctrl *gomock.Controller recorder *MockHeadersMockRecorder } -// MockHeadersMockRecorder is the mock recorder for MockHeaders. +// MockHeadersMockRecorder is the mock recorder for MockHeaders type MockHeadersMockRecorder struct { mock *MockHeaders } -// NewMockHeaders creates a new mock instance. +// NewMockHeaders creates a new mock instance func NewMockHeaders(ctrl *gomock.Controller) *MockHeaders { mock := &MockHeaders{ctrl: ctrl} mock.recorder = &MockHeadersMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockHeaders) EXPECT() *MockHeadersMockRecorder { return m.recorder } -// BatchIndexByChunkID mocks base method. +// BatchIndexByChunkID mocks base method func (m *MockHeaders) BatchIndexByChunkID(arg0, arg1 flow.Identifier, arg2 storage.BatchStorage) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BatchIndexByChunkID", arg0, arg1, arg2) @@ -183,13 +182,13 @@ func (m *MockHeaders) BatchIndexByChunkID(arg0, arg1 flow.Identifier, arg2 stora return ret0 } -// BatchIndexByChunkID indicates an expected call of BatchIndexByChunkID. +// BatchIndexByChunkID indicates an expected call of BatchIndexByChunkID func (mr *MockHeadersMockRecorder) BatchIndexByChunkID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchIndexByChunkID", reflect.TypeOf((*MockHeaders)(nil).BatchIndexByChunkID), arg0, arg1, arg2) } -// ByBlockID mocks base method. +// ByBlockID mocks base method func (m *MockHeaders) ByBlockID(arg0 flow.Identifier) (*flow.Header, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByBlockID", arg0) @@ -198,13 +197,13 @@ func (m *MockHeaders) ByBlockID(arg0 flow.Identifier) (*flow.Header, error) { return ret0, ret1 } -// ByBlockID indicates an expected call of ByBlockID. +// ByBlockID indicates an expected call of ByBlockID func (mr *MockHeadersMockRecorder) ByBlockID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByBlockID", reflect.TypeOf((*MockHeaders)(nil).ByBlockID), arg0) } -// ByHeight mocks base method. +// ByHeight mocks base method func (m *MockHeaders) ByHeight(arg0 uint64) (*flow.Header, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByHeight", arg0) @@ -213,13 +212,13 @@ func (m *MockHeaders) ByHeight(arg0 uint64) (*flow.Header, error) { return ret0, ret1 } -// ByHeight indicates an expected call of ByHeight. +// ByHeight indicates an expected call of ByHeight func (mr *MockHeadersMockRecorder) ByHeight(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByHeight", reflect.TypeOf((*MockHeaders)(nil).ByHeight), arg0) } -// ByParentID mocks base method. +// ByParentID mocks base method func (m *MockHeaders) ByParentID(arg0 flow.Identifier) ([]*flow.Header, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByParentID", arg0) @@ -228,13 +227,13 @@ func (m *MockHeaders) ByParentID(arg0 flow.Identifier) ([]*flow.Header, error) { return ret0, ret1 } -// ByParentID indicates an expected call of ByParentID. +// ByParentID indicates an expected call of ByParentID func (mr *MockHeadersMockRecorder) ByParentID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByParentID", reflect.TypeOf((*MockHeaders)(nil).ByParentID), arg0) } -// IDByChunkID mocks base method. +// IDByChunkID mocks base method func (m *MockHeaders) IDByChunkID(arg0 flow.Identifier) (flow.Identifier, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IDByChunkID", arg0) @@ -243,13 +242,13 @@ func (m *MockHeaders) IDByChunkID(arg0 flow.Identifier) (flow.Identifier, error) return ret0, ret1 } -// IDByChunkID indicates an expected call of IDByChunkID. +// IDByChunkID indicates an expected call of IDByChunkID func (mr *MockHeadersMockRecorder) IDByChunkID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IDByChunkID", reflect.TypeOf((*MockHeaders)(nil).IDByChunkID), arg0) } -// IndexByChunkID mocks base method. +// IndexByChunkID mocks base method func (m *MockHeaders) IndexByChunkID(arg0, arg1 flow.Identifier) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IndexByChunkID", arg0, arg1) @@ -257,13 +256,13 @@ func (m *MockHeaders) IndexByChunkID(arg0, arg1 flow.Identifier) error { return ret0 } -// IndexByChunkID indicates an expected call of IndexByChunkID. +// IndexByChunkID indicates an expected call of IndexByChunkID func (mr *MockHeadersMockRecorder) IndexByChunkID(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexByChunkID", reflect.TypeOf((*MockHeaders)(nil).IndexByChunkID), arg0, arg1) } -// Store mocks base method. +// Store mocks base method func (m *MockHeaders) Store(arg0 *flow.Header) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Store", arg0) @@ -271,36 +270,36 @@ func (m *MockHeaders) Store(arg0 *flow.Header) error { return ret0 } -// Store indicates an expected call of Store. +// Store indicates an expected call of Store func (mr *MockHeadersMockRecorder) Store(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Store", reflect.TypeOf((*MockHeaders)(nil).Store), arg0) } -// MockPayloads is a mock of Payloads interface. +// MockPayloads is a mock of Payloads interface type MockPayloads struct { ctrl *gomock.Controller recorder *MockPayloadsMockRecorder } -// MockPayloadsMockRecorder is the mock recorder for MockPayloads. +// MockPayloadsMockRecorder is the mock recorder for MockPayloads type MockPayloadsMockRecorder struct { mock *MockPayloads } -// NewMockPayloads creates a new mock instance. +// NewMockPayloads creates a new mock instance func NewMockPayloads(ctrl *gomock.Controller) *MockPayloads { mock := &MockPayloads{ctrl: ctrl} mock.recorder = &MockPayloadsMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockPayloads) EXPECT() *MockPayloadsMockRecorder { return m.recorder } -// ByBlockID mocks base method. +// ByBlockID mocks base method func (m *MockPayloads) ByBlockID(arg0 flow.Identifier) (*flow.Payload, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByBlockID", arg0) @@ -309,13 +308,13 @@ func (m *MockPayloads) ByBlockID(arg0 flow.Identifier) (*flow.Payload, error) { return ret0, ret1 } -// ByBlockID indicates an expected call of ByBlockID. +// ByBlockID indicates an expected call of ByBlockID func (mr *MockPayloadsMockRecorder) ByBlockID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByBlockID", reflect.TypeOf((*MockPayloads)(nil).ByBlockID), arg0) } -// Store mocks base method. +// Store mocks base method func (m *MockPayloads) Store(arg0 flow.Identifier, arg1 *flow.Payload) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Store", arg0, arg1) @@ -323,36 +322,36 @@ func (m *MockPayloads) Store(arg0 flow.Identifier, arg1 *flow.Payload) error { return ret0 } -// Store indicates an expected call of Store. +// Store indicates an expected call of Store func (mr *MockPayloadsMockRecorder) Store(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Store", reflect.TypeOf((*MockPayloads)(nil).Store), arg0, arg1) } -// MockCollections is a mock of Collections interface. +// MockCollections is a mock of Collections interface type MockCollections struct { ctrl *gomock.Controller recorder *MockCollectionsMockRecorder } -// MockCollectionsMockRecorder is the mock recorder for MockCollections. +// MockCollectionsMockRecorder is the mock recorder for MockCollections type MockCollectionsMockRecorder struct { mock *MockCollections } -// NewMockCollections creates a new mock instance. +// NewMockCollections creates a new mock instance func NewMockCollections(ctrl *gomock.Controller) *MockCollections { mock := &MockCollections{ctrl: ctrl} mock.recorder = &MockCollectionsMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockCollections) EXPECT() *MockCollectionsMockRecorder { return m.recorder } -// ByID mocks base method. +// ByID mocks base method func (m *MockCollections) ByID(arg0 flow.Identifier) (*flow.Collection, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByID", arg0) @@ -361,13 +360,13 @@ func (m *MockCollections) ByID(arg0 flow.Identifier) (*flow.Collection, error) { return ret0, ret1 } -// ByID indicates an expected call of ByID. +// ByID indicates an expected call of ByID func (mr *MockCollectionsMockRecorder) ByID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByID", reflect.TypeOf((*MockCollections)(nil).ByID), arg0) } -// LightByID mocks base method. +// LightByID mocks base method func (m *MockCollections) LightByID(arg0 flow.Identifier) (*flow.LightCollection, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LightByID", arg0) @@ -376,13 +375,13 @@ func (m *MockCollections) LightByID(arg0 flow.Identifier) (*flow.LightCollection return ret0, ret1 } -// LightByID indicates an expected call of LightByID. +// LightByID indicates an expected call of LightByID func (mr *MockCollectionsMockRecorder) LightByID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LightByID", reflect.TypeOf((*MockCollections)(nil).LightByID), arg0) } -// LightByTransactionID mocks base method. +// LightByTransactionID mocks base method func (m *MockCollections) LightByTransactionID(arg0 flow.Identifier) (*flow.LightCollection, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LightByTransactionID", arg0) @@ -391,13 +390,13 @@ func (m *MockCollections) LightByTransactionID(arg0 flow.Identifier) (*flow.Ligh return ret0, ret1 } -// LightByTransactionID indicates an expected call of LightByTransactionID. +// LightByTransactionID indicates an expected call of LightByTransactionID func (mr *MockCollectionsMockRecorder) LightByTransactionID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LightByTransactionID", reflect.TypeOf((*MockCollections)(nil).LightByTransactionID), arg0) } -// Remove mocks base method. +// Remove mocks base method func (m *MockCollections) Remove(arg0 flow.Identifier) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Remove", arg0) @@ -405,13 +404,13 @@ func (m *MockCollections) Remove(arg0 flow.Identifier) error { return ret0 } -// Remove indicates an expected call of Remove. +// Remove indicates an expected call of Remove func (mr *MockCollectionsMockRecorder) Remove(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockCollections)(nil).Remove), arg0) } -// Store mocks base method. +// Store mocks base method func (m *MockCollections) Store(arg0 *flow.Collection) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Store", arg0) @@ -419,13 +418,13 @@ func (m *MockCollections) Store(arg0 *flow.Collection) error { return ret0 } -// Store indicates an expected call of Store. +// Store indicates an expected call of Store func (mr *MockCollectionsMockRecorder) Store(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Store", reflect.TypeOf((*MockCollections)(nil).Store), arg0) } -// StoreLight mocks base method. +// StoreLight mocks base method func (m *MockCollections) StoreLight(arg0 *flow.LightCollection) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StoreLight", arg0) @@ -433,13 +432,13 @@ func (m *MockCollections) StoreLight(arg0 *flow.LightCollection) error { return ret0 } -// StoreLight indicates an expected call of StoreLight. +// StoreLight indicates an expected call of StoreLight func (mr *MockCollectionsMockRecorder) StoreLight(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreLight", reflect.TypeOf((*MockCollections)(nil).StoreLight), arg0) } -// StoreLightAndIndexByTransaction mocks base method. +// StoreLightAndIndexByTransaction mocks base method func (m *MockCollections) StoreLightAndIndexByTransaction(arg0 *flow.LightCollection) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StoreLightAndIndexByTransaction", arg0) @@ -447,36 +446,36 @@ func (m *MockCollections) StoreLightAndIndexByTransaction(arg0 *flow.LightCollec return ret0 } -// StoreLightAndIndexByTransaction indicates an expected call of StoreLightAndIndexByTransaction. +// StoreLightAndIndexByTransaction indicates an expected call of StoreLightAndIndexByTransaction func (mr *MockCollectionsMockRecorder) StoreLightAndIndexByTransaction(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreLightAndIndexByTransaction", reflect.TypeOf((*MockCollections)(nil).StoreLightAndIndexByTransaction), arg0) } -// MockCommits is a mock of Commits interface. +// MockCommits is a mock of Commits interface type MockCommits struct { ctrl *gomock.Controller recorder *MockCommitsMockRecorder } -// MockCommitsMockRecorder is the mock recorder for MockCommits. +// MockCommitsMockRecorder is the mock recorder for MockCommits type MockCommitsMockRecorder struct { mock *MockCommits } -// NewMockCommits creates a new mock instance. +// NewMockCommits creates a new mock instance func NewMockCommits(ctrl *gomock.Controller) *MockCommits { mock := &MockCommits{ctrl: ctrl} mock.recorder = &MockCommitsMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockCommits) EXPECT() *MockCommitsMockRecorder { return m.recorder } -// BatchStore mocks base method. +// BatchStore mocks base method func (m *MockCommits) BatchStore(arg0 flow.Identifier, arg1 flow.StateCommitment, arg2 storage.BatchStorage) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BatchStore", arg0, arg1, arg2) @@ -484,13 +483,13 @@ func (m *MockCommits) BatchStore(arg0 flow.Identifier, arg1 flow.StateCommitment return ret0 } -// BatchStore indicates an expected call of BatchStore. +// BatchStore indicates an expected call of BatchStore func (mr *MockCommitsMockRecorder) BatchStore(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchStore", reflect.TypeOf((*MockCommits)(nil).BatchStore), arg0, arg1, arg2) } -// ByBlockID mocks base method. +// ByBlockID mocks base method func (m *MockCommits) ByBlockID(arg0 flow.Identifier) (flow.StateCommitment, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByBlockID", arg0) @@ -499,13 +498,13 @@ func (m *MockCommits) ByBlockID(arg0 flow.Identifier) (flow.StateCommitment, err return ret0, ret1 } -// ByBlockID indicates an expected call of ByBlockID. +// ByBlockID indicates an expected call of ByBlockID func (mr *MockCommitsMockRecorder) ByBlockID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByBlockID", reflect.TypeOf((*MockCommits)(nil).ByBlockID), arg0) } -// Store mocks base method. +// Store mocks base method func (m *MockCommits) Store(arg0 flow.Identifier, arg1 flow.StateCommitment) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Store", arg0, arg1) @@ -513,36 +512,36 @@ func (m *MockCommits) Store(arg0 flow.Identifier, arg1 flow.StateCommitment) err return ret0 } -// Store indicates an expected call of Store. +// Store indicates an expected call of Store func (mr *MockCommitsMockRecorder) Store(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Store", reflect.TypeOf((*MockCommits)(nil).Store), arg0, arg1) } -// MockEvents is a mock of Events interface. +// MockEvents is a mock of Events interface type MockEvents struct { ctrl *gomock.Controller recorder *MockEventsMockRecorder } -// MockEventsMockRecorder is the mock recorder for MockEvents. +// MockEventsMockRecorder is the mock recorder for MockEvents type MockEventsMockRecorder struct { mock *MockEvents } -// NewMockEvents creates a new mock instance. +// NewMockEvents creates a new mock instance func NewMockEvents(ctrl *gomock.Controller) *MockEvents { mock := &MockEvents{ctrl: ctrl} mock.recorder = &MockEventsMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockEvents) EXPECT() *MockEventsMockRecorder { return m.recorder } -// BatchStore mocks base method. +// BatchStore mocks base method func (m *MockEvents) BatchStore(arg0 flow.Identifier, arg1 []flow.EventsList, arg2 storage.BatchStorage) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BatchStore", arg0, arg1, arg2) @@ -550,13 +549,13 @@ func (m *MockEvents) BatchStore(arg0 flow.Identifier, arg1 []flow.EventsList, ar return ret0 } -// BatchStore indicates an expected call of BatchStore. +// BatchStore indicates an expected call of BatchStore func (mr *MockEventsMockRecorder) BatchStore(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchStore", reflect.TypeOf((*MockEvents)(nil).BatchStore), arg0, arg1, arg2) } -// ByBlockID mocks base method. +// ByBlockID mocks base method func (m *MockEvents) ByBlockID(arg0 flow.Identifier) ([]flow.Event, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByBlockID", arg0) @@ -565,13 +564,13 @@ func (m *MockEvents) ByBlockID(arg0 flow.Identifier) ([]flow.Event, error) { return ret0, ret1 } -// ByBlockID indicates an expected call of ByBlockID. +// ByBlockID indicates an expected call of ByBlockID func (mr *MockEventsMockRecorder) ByBlockID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByBlockID", reflect.TypeOf((*MockEvents)(nil).ByBlockID), arg0) } -// ByBlockIDEventType mocks base method. +// ByBlockIDEventType mocks base method func (m *MockEvents) ByBlockIDEventType(arg0 flow.Identifier, arg1 flow.EventType) ([]flow.Event, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByBlockIDEventType", arg0, arg1) @@ -580,13 +579,13 @@ func (m *MockEvents) ByBlockIDEventType(arg0 flow.Identifier, arg1 flow.EventTyp return ret0, ret1 } -// ByBlockIDEventType indicates an expected call of ByBlockIDEventType. +// ByBlockIDEventType indicates an expected call of ByBlockIDEventType func (mr *MockEventsMockRecorder) ByBlockIDEventType(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByBlockIDEventType", reflect.TypeOf((*MockEvents)(nil).ByBlockIDEventType), arg0, arg1) } -// ByBlockIDTransactionID mocks base method. +// ByBlockIDTransactionID mocks base method func (m *MockEvents) ByBlockIDTransactionID(arg0, arg1 flow.Identifier) ([]flow.Event, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByBlockIDTransactionID", arg0, arg1) @@ -595,36 +594,36 @@ func (m *MockEvents) ByBlockIDTransactionID(arg0, arg1 flow.Identifier) ([]flow. return ret0, ret1 } -// ByBlockIDTransactionID indicates an expected call of ByBlockIDTransactionID. +// ByBlockIDTransactionID indicates an expected call of ByBlockIDTransactionID func (mr *MockEventsMockRecorder) ByBlockIDTransactionID(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByBlockIDTransactionID", reflect.TypeOf((*MockEvents)(nil).ByBlockIDTransactionID), arg0, arg1) } -// MockServiceEvents is a mock of ServiceEvents interface. +// MockServiceEvents is a mock of ServiceEvents interface type MockServiceEvents struct { ctrl *gomock.Controller recorder *MockServiceEventsMockRecorder } -// MockServiceEventsMockRecorder is the mock recorder for MockServiceEvents. +// MockServiceEventsMockRecorder is the mock recorder for MockServiceEvents type MockServiceEventsMockRecorder struct { mock *MockServiceEvents } -// NewMockServiceEvents creates a new mock instance. +// NewMockServiceEvents creates a new mock instance func NewMockServiceEvents(ctrl *gomock.Controller) *MockServiceEvents { mock := &MockServiceEvents{ctrl: ctrl} mock.recorder = &MockServiceEventsMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockServiceEvents) EXPECT() *MockServiceEventsMockRecorder { return m.recorder } -// BatchStore mocks base method. +// BatchStore mocks base method func (m *MockServiceEvents) BatchStore(arg0 flow.Identifier, arg1 []flow.Event, arg2 storage.BatchStorage) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BatchStore", arg0, arg1, arg2) @@ -632,13 +631,13 @@ func (m *MockServiceEvents) BatchStore(arg0 flow.Identifier, arg1 []flow.Event, return ret0 } -// BatchStore indicates an expected call of BatchStore. +// BatchStore indicates an expected call of BatchStore func (mr *MockServiceEventsMockRecorder) BatchStore(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchStore", reflect.TypeOf((*MockServiceEvents)(nil).BatchStore), arg0, arg1, arg2) } -// ByBlockID mocks base method. +// ByBlockID mocks base method func (m *MockServiceEvents) ByBlockID(arg0 flow.Identifier) ([]flow.Event, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByBlockID", arg0) @@ -647,36 +646,36 @@ func (m *MockServiceEvents) ByBlockID(arg0 flow.Identifier) ([]flow.Event, error return ret0, ret1 } -// ByBlockID indicates an expected call of ByBlockID. +// ByBlockID indicates an expected call of ByBlockID func (mr *MockServiceEventsMockRecorder) ByBlockID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByBlockID", reflect.TypeOf((*MockServiceEvents)(nil).ByBlockID), arg0) } -// MockTransactionResults is a mock of TransactionResults interface. +// MockTransactionResults is a mock of TransactionResults interface type MockTransactionResults struct { ctrl *gomock.Controller recorder *MockTransactionResultsMockRecorder } -// MockTransactionResultsMockRecorder is the mock recorder for MockTransactionResults. +// MockTransactionResultsMockRecorder is the mock recorder for MockTransactionResults type MockTransactionResultsMockRecorder struct { mock *MockTransactionResults } -// NewMockTransactionResults creates a new mock instance. +// NewMockTransactionResults creates a new mock instance func NewMockTransactionResults(ctrl *gomock.Controller) *MockTransactionResults { mock := &MockTransactionResults{ctrl: ctrl} mock.recorder = &MockTransactionResultsMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockTransactionResults) EXPECT() *MockTransactionResultsMockRecorder { return m.recorder } -// BatchStore mocks base method. +// BatchStore mocks base method func (m *MockTransactionResults) BatchStore(arg0 flow.Identifier, arg1 []flow.TransactionResult, arg2 storage.BatchStorage) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BatchStore", arg0, arg1, arg2) @@ -684,13 +683,13 @@ func (m *MockTransactionResults) BatchStore(arg0 flow.Identifier, arg1 []flow.Tr return ret0 } -// BatchStore indicates an expected call of BatchStore. +// BatchStore indicates an expected call of BatchStore func (mr *MockTransactionResultsMockRecorder) BatchStore(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchStore", reflect.TypeOf((*MockTransactionResults)(nil).BatchStore), arg0, arg1, arg2) } -// ByBlockIDTransactionID mocks base method. +// ByBlockIDTransactionID mocks base method func (m *MockTransactionResults) ByBlockIDTransactionID(arg0, arg1 flow.Identifier) (*flow.TransactionResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByBlockIDTransactionID", arg0, arg1) @@ -699,7 +698,7 @@ func (m *MockTransactionResults) ByBlockIDTransactionID(arg0, arg1 flow.Identifi return ret0, ret1 } -// ByBlockIDTransactionID indicates an expected call of ByBlockIDTransactionID. +// ByBlockIDTransactionID indicates an expected call of ByBlockIDTransactionID func (mr *MockTransactionResultsMockRecorder) ByBlockIDTransactionID(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByBlockIDTransactionID", reflect.TypeOf((*MockTransactionResults)(nil).ByBlockIDTransactionID), arg0, arg1)