From 0de12b7ddafe86fda2c13716a3e2014dcc562fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Tr=C3=B3n?= Date: Thu, 28 Sep 2023 13:35:41 +0200 Subject: [PATCH 01/17] feat(storageincentives): storage incentives phase 4 (#4345) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Viktor Levente Tóth Co-authored-by: nugaon <50576770+nugaon@users.noreply.github.com> Co-authored-by: istae <14264581+istae@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 +- pkg/api/api.go | 5 + pkg/api/api_test.go | 5 +- pkg/api/rchash.go | 119 +++++++++- pkg/api/router.go | 12 +- pkg/bmt/bmt.go | 13 ++ pkg/bmt/proof.go | 35 ++- pkg/bmt/proof_test.go | 30 ++- pkg/bmt/trhasher.go | 25 --- pkg/config/chain.go | 6 +- pkg/storageincentives/agent.go | 48 +++- pkg/storageincentives/agent_test.go | 4 +- pkg/storageincentives/export_test.go | 5 +- pkg/storageincentives/proof.go | 206 ++++++++++++++++-- pkg/storageincentives/proof_test.go | 143 +++++++++++- .../redistribution/inclusionproof.go | 105 +++++++++ .../redistribution/redistribution.go | 10 +- .../redistribution/redistribution_test.go | 50 ++++- pkg/storageincentives/redistributionstate.go | 7 +- pkg/storageincentives/soc_mine_test.go | 201 +++++++++++++++++ .../testdata/inclusion-proofs.json | 126 +++++++++++ pkg/storer/sample.go | 73 +++++-- pkg/swarm/hasher.go | 10 +- pkg/swarm/hasher_test.go | 2 +- 25 files changed, 1103 insertions(+), 143 deletions(-) delete mode 100644 pkg/bmt/trhasher.go create mode 100644 pkg/storageincentives/redistribution/inclusionproof.go create mode 100644 pkg/storageincentives/soc_mine_test.go create mode 100644 pkg/storageincentives/testdata/inclusion-proofs.json diff --git a/go.mod b/go.mod index e37ec7d1f66..fe4a9bb110d 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/ethereum/go-ethereum v1.13.4 github.com/ethersphere/go-price-oracle-abi v0.1.0 - github.com/ethersphere/go-storage-incentives-abi v0.5.0 + github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc3 github.com/ethersphere/go-sw3-abi v0.4.0 github.com/ethersphere/langos v1.0.0 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index 3610f81909b..1ce0ac6ae81 100644 --- a/go.sum +++ b/go.sum @@ -242,8 +242,8 @@ github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8 github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= github.com/ethersphere/go-price-oracle-abi v0.1.0 h1:yg/hK8nETNvk+GEBASlbakMFv/CVp7HXiycrHw1pRV8= github.com/ethersphere/go-price-oracle-abi v0.1.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= -github.com/ethersphere/go-storage-incentives-abi v0.5.0 h1:dd01OZmPraCjOIiSX5FsCfFFwUR2b9PuTO/LDcYxS+s= -github.com/ethersphere/go-storage-incentives-abi v0.5.0/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc3 h1:tXux2FnhuU6DbrY+Z4nVQMGp63JkJPq7pKb5Xi2Sjxo= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc3/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g= github.com/ethersphere/go-sw3-abi v0.4.0/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU= github.com/ethersphere/langos v1.0.0 h1:NBtNKzXTTRSue95uOlzPN4py7Aofs0xWPzyj4AI1Vcc= diff --git a/pkg/api/api.go b/pkg/api/api.go index 3ecea7ad000..49331689a0f 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -10,6 +10,7 @@ import ( "context" "crypto/ecdsa" "encoding/base64" + "encoding/hex" "encoding/json" "errors" "fmt" @@ -286,6 +287,10 @@ func New( buf, err := base64.URLEncoding.DecodeString(v) return string(buf), err }, + "decHex": func(v string) (string, error) { + buf, err := hex.DecodeString(v) + return string(buf), err + }, } s.validate = validator.New() s.validate.RegisterTagNameFunc(func(fld reflect.StructField) string { diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go index 8655853474f..21e535f10ac 100644 --- a/pkg/api/api_test.go +++ b/pkg/api/api_test.go @@ -57,6 +57,7 @@ import ( "github.com/ethersphere/bee/pkg/storage/inmemstore" testingc "github.com/ethersphere/bee/pkg/storage/testing" "github.com/ethersphere/bee/pkg/storageincentives" + "github.com/ethersphere/bee/pkg/storageincentives/redistribution" "github.com/ethersphere/bee/pkg/storageincentives/staking" mock2 "github.com/ethersphere/bee/pkg/storageincentives/staking/mock" mockstorer "github.com/ethersphere/bee/pkg/storer/mock" @@ -775,14 +776,14 @@ func (m *mockContract) IsWinner(context.Context) (bool, error) { return false, nil } -func (m *mockContract) Claim(context.Context) (common.Hash, error) { +func (m *mockContract) Claim(context.Context, redistribution.ChunkInclusionProofs) (common.Hash, error) { m.mtx.Lock() defer m.mtx.Unlock() m.callsList = append(m.callsList, claimCall) return common.Hash{}, nil } -func (m *mockContract) Commit(context.Context, []byte, *big.Int) (common.Hash, error) { +func (m *mockContract) Commit(context.Context, []byte, uint32) (common.Hash, error) { m.mtx.Lock() defer m.mtx.Unlock() m.callsList = append(m.callsList, commitCall) diff --git a/pkg/api/rchash.go b/pkg/api/rchash.go index c37f642da2b..ad7a25f9992 100644 --- a/pkg/api/rchash.go +++ b/pkg/api/rchash.go @@ -6,40 +6,135 @@ package api import ( "encoding/hex" "net/http" + "strconv" + "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethersphere/bee/pkg/jsonhttp" - "github.com/ethersphere/bee/pkg/storageincentives" + "github.com/ethersphere/bee/pkg/storageincentives/redistribution" + "github.com/ethersphere/bee/pkg/swarm" "github.com/gorilla/mux" ) -type RCHashResponse storageincentives.SampleWithProofs +type RCHashResponse struct { + Hash swarm.Address `json:"hash"` + Proofs ChunkInclusionProofs `json:"proofs"` + Duration time.Duration `json:"duration"` +} + +type ChunkInclusionProofs struct { + A ChunkInclusionProof `json:"proof1"` + B ChunkInclusionProof `json:"proof2"` + C ChunkInclusionProof `json:"proofLast"` +} + +// ChunkInclusionProof structure must exactly match +// corresponding structure (of the same name) in Redistribution.sol smart contract. +// github.com/ethersphere/storage-incentives/blob/ph_f2/src/Redistribution.sol +// github.com/ethersphere/storage-incentives/blob/master/src/Redistribution.sol (when merged to master) +type ChunkInclusionProof struct { + ProofSegments []string `json:"proofSegments"` + ProveSegment string `json:"proveSegment"` + ProofSegments2 []string `json:"proofSegments2"` + ProveSegment2 string `json:"proveSegment2"` + ChunkSpan uint64 `json:"chunkSpan"` + ProofSegments3 []string `json:"proofSegments3"` + PostageProof PostageProof `json:"postageProof"` + SocProof []SOCProof `json:"socProof"` +} + +// SOCProof structure must exactly match +// corresponding structure (of the same name) in Redistribution.sol smart contract. +type PostageProof struct { + Signature string `json:"signature"` + PostageId string `json:"postageId"` + Index string `json:"index"` + TimeStamp string `json:"timeStamp"` +} + +// SOCProof structure must exactly match +// corresponding structure (of the same name) in Redistribution.sol smart contract. +type SOCProof struct { + Signer string `json:"signer"` + Signature string `json:"signature"` + Identifier string `json:"identifier"` + ChunkAddr string `json:"chunkAddr"` +} + +func renderChunkInclusionProofs(proofs redistribution.ChunkInclusionProofs) ChunkInclusionProofs { + return ChunkInclusionProofs{ + A: renderChunkInclusionProof(proofs.A), + B: renderChunkInclusionProof(proofs.B), + C: renderChunkInclusionProof(proofs.C), + } +} + +func renderChunkInclusionProof(proof redistribution.ChunkInclusionProof) ChunkInclusionProof { + var socProof []SOCProof + if len(proof.SocProof) == 1 { + socProof = []SOCProof{{ + Signer: hex.EncodeToString(proof.SocProof[0].Signer.Bytes()), + Signature: hex.EncodeToString(proof.SocProof[0].Signature[:]), + Identifier: hex.EncodeToString(proof.SocProof[0].Identifier.Bytes()), + ChunkAddr: hex.EncodeToString(proof.SocProof[0].ChunkAddr.Bytes()), + }} + } + + return ChunkInclusionProof{ + ProveSegment: hex.EncodeToString(proof.ProveSegment.Bytes()), + ProofSegments: renderCommonHash(proof.ProofSegments), + ProveSegment2: hex.EncodeToString(proof.ProveSegment2.Bytes()), + ProofSegments2: renderCommonHash(proof.ProofSegments2), + ProofSegments3: renderCommonHash(proof.ProofSegments3), + ChunkSpan: proof.ChunkSpan, + PostageProof: PostageProof{ + Signature: hex.EncodeToString(proof.PostageProof.Signature[:]), + PostageId: hex.EncodeToString(proof.PostageProof.PostageId[:]), + Index: strconv.FormatUint(proof.PostageProof.Index, 16), + TimeStamp: strconv.FormatUint(proof.PostageProof.TimeStamp, 16), + }, + SocProof: socProof, + } +} + +func renderCommonHash(proofSegments []common.Hash) []string { + output := make([]string, len(proofSegments)) + for i, s := range proofSegments { + output[i] = hex.EncodeToString(s.Bytes()) + } + return output +} // This API is kept for testing the sampler. As a result, no documentation or tests are added here. func (s *Service) rchash(w http.ResponseWriter, r *http.Request) { logger := s.logger.WithName("get_rchash").Build() paths := struct { - Depth *uint8 `map:"depth" validate:"required"` - Anchor1 string `map:"anchor1" validate:"required"` + Depth uint8 `map:"depth"` + Anchor1 string `map:"anchor1,decHex" validate:"required"` + Anchor2 string `map:"anchor2,decHex" validate:"required"` }{} if response := s.mapStructure(mux.Vars(r), &paths); response != nil { response("invalid path params", logger, w) return } - anchor1, err := hex.DecodeString(paths.Anchor1) - if err != nil { - logger.Error(err, "invalid hex params") - jsonhttp.InternalServerError(w, "invalid hex params") - return - } + anchor1 := []byte(paths.Anchor1) - resp, err := s.redistributionAgent.SampleWithProofs(r.Context(), anchor1, *paths.Depth) + anchor2 := []byte(paths.Anchor2) + + swp, err := s.redistributionAgent.SampleWithProofs(r.Context(), anchor1, anchor2, paths.Depth) if err != nil { logger.Error(err, "failed making sample with proofs") jsonhttp.InternalServerError(w, "failed making sample with proofs") return } - jsonhttp.OK(w, RCHashResponse(resp)) + resp := RCHashResponse{ + Hash: swp.Hash, + Duration: swp.Duration, + Proofs: renderChunkInclusionProofs(swp.Proofs), + } + + jsonhttp.OK(w, resp) } diff --git a/pkg/api/router.go b/pkg/api/router.go index aba674e2379..1ce1bb94ae0 100644 --- a/pkg/api/router.go +++ b/pkg/api/router.go @@ -339,12 +339,6 @@ func (s *Service) mountAPI() { web.FinalHandlerFunc(s.healthHandler), )) - handle("/rchash/{depth}/{anchor1}", web.ChainHandlers( - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.rchash), - }), - )) - if s.Restricted { handle("/auth", jsonhttp.MethodHandler{ "POST": web.ChainHandlers( @@ -601,4 +595,10 @@ func (s *Service) mountBusinessDebug(restricted bool) { web.FinalHandlerFunc(s.statusGetPeersHandler), ), }) + + handle("/rchash/{depth}/{anchor1}/{anchor2}", web.ChainHandlers( + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.rchash), + }), + )) } diff --git a/pkg/bmt/bmt.go b/pkg/bmt/bmt.go index e13aa3ec1cb..f314e012776 100644 --- a/pkg/bmt/bmt.go +++ b/pkg/bmt/bmt.go @@ -40,6 +40,19 @@ type Hasher struct { span []byte // The span of the data subsumed under the chunk } +// NewHasher gives back an instance of a Hasher struct +func NewHasher(hasherFact func() hash.Hash) *Hasher { + conf := NewConf(hasherFact, swarm.BmtBranches, 32) + + return &Hasher{ + Conf: conf, + result: make(chan []byte), + errc: make(chan error, 1), + span: make([]byte, SpanSize), + bmt: newTree(conf.segmentSize, conf.maxSize, conf.depth, conf.hasher), + } +} + // Capacity returns the maximum amount of bytes that will be processed by this hasher implementation. // since BMT assumes a balanced binary tree, capacity it is always a power of 2 func (h *Hasher) Capacity() int { diff --git a/pkg/bmt/proof.go b/pkg/bmt/proof.go index b9a958db9ab..cc59fd33766 100644 --- a/pkg/bmt/proof.go +++ b/pkg/bmt/proof.go @@ -17,6 +17,17 @@ type Proof struct { Index int } +// Hash overrides base hash function of Hasher to fill buffer with zeros until chunk length +func (p Prover) Hash(b []byte) ([]byte, error) { + for i := p.size; i < p.maxSize; i += len(zerosection) { + _, err := p.Hasher.Write(zerosection) + if err != nil { + return nil, err + } + } + return p.Hasher.Hash(b) +} + // Proof returns the inclusion proof of the i-th data segment func (p Prover) Proof(i int) Proof { index := i @@ -36,26 +47,38 @@ func (p Prover) Proof(i int) Proof { secsize := 2 * p.segmentSize offset := i * secsize section := p.bmt.buffer[offset : offset+secsize] - return Proof{section, sisters, p.span, index} + segment, firstSegmentSister := section[:p.segmentSize], section[p.segmentSize:] + if index%2 != 0 { + segment, firstSegmentSister = firstSegmentSister, segment + } + sisters = append([][]byte{firstSegmentSister}, sisters...) + return Proof{segment, sisters, p.span, index} } // Verify returns the bmt hash obtained from the proof which can then be checked against // the BMT hash of the chunk func (p Prover) Verify(i int, proof Proof) (root []byte, err error) { + var section []byte + if i%2 == 0 { + section = append(append(section, proof.ProveSegment...), proof.ProofSegments[0]...) + } else { + section = append(append(section, proof.ProofSegments[0]...), proof.ProveSegment...) + } i = i / 2 n := p.bmt.leaves[i] + hasher := p.hasher() isLeft := n.isLeft - root, err = doHash(n.hasher, proof.ProveSegment) + root, err = doHash(hasher, section) if err != nil { return nil, err } n = n.parent - for _, sister := range proof.ProofSegments { + for _, sister := range proof.ProofSegments[1:] { if isLeft { - root, err = doHash(n.hasher, root, sister) + root, err = doHash(hasher, root, sister) } else { - root, err = doHash(n.hasher, sister, root) + root, err = doHash(hasher, sister, root) } if err != nil { return nil, err @@ -63,7 +86,7 @@ func (p Prover) Verify(i int, proof Proof) (root []byte, err error) { isLeft = n.isLeft n = n.parent } - return sha3hash(proof.Span, root) + return doHash(hasher, proof.Span, root) } func (n *node) getSister(isLeft bool) []byte { diff --git a/pkg/bmt/proof_test.go b/pkg/bmt/proof_test.go index 337b1bf3420..1b7f6d3b3dd 100644 --- a/pkg/bmt/proof_test.go +++ b/pkg/bmt/proof_test.go @@ -20,7 +20,8 @@ func TestProofCorrectness(t *testing.T) { t.Parallel() testData := []byte("hello world") - testData = append(testData, make([]byte, 4096-len(testData))...) + testDataPadded := make([]byte, swarm.ChunkSize) + copy(testDataPadded, testData) verifySegments := func(t *testing.T, exp []string, found [][]byte) { t.Helper() @@ -57,8 +58,8 @@ func TestProofCorrectness(t *testing.T) { if err != nil { t.Fatal(err) } - - rh, err := hh.Hash(nil) + pr := bmt.Prover{hh} + rh, err := pr.Hash(nil) if err != nil { t.Fatal(err) } @@ -66,9 +67,10 @@ func TestProofCorrectness(t *testing.T) { t.Run("proof for left most", func(t *testing.T) { t.Parallel() - proof := bmt.Prover{hh}.Proof(0) + proof := pr.Proof(0) expSegmentStrings := []string{ + "0000000000000000000000000000000000000000000000000000000000000000", "ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", "b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", "21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", @@ -79,7 +81,7 @@ func TestProofCorrectness(t *testing.T) { verifySegments(t, expSegmentStrings, proof.ProofSegments) - if !bytes.Equal(proof.ProveSegment, testData[:2*hh.Size()]) { + if !bytes.Equal(proof.ProveSegment, testDataPadded[:hh.Size()]) { t.Fatal("section incorrect") } @@ -91,9 +93,10 @@ func TestProofCorrectness(t *testing.T) { t.Run("proof for right most", func(t *testing.T) { t.Parallel() - proof := bmt.Prover{hh}.Proof(127) + proof := pr.Proof(127) expSegmentStrings := []string{ + "0000000000000000000000000000000000000000000000000000000000000000", "ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", "b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", "21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", @@ -104,7 +107,7 @@ func TestProofCorrectness(t *testing.T) { verifySegments(t, expSegmentStrings, proof.ProofSegments) - if !bytes.Equal(proof.ProveSegment, testData[126*hh.Size():]) { + if !bytes.Equal(proof.ProveSegment, testDataPadded[127*hh.Size():]) { t.Fatal("section incorrect") } @@ -116,9 +119,10 @@ func TestProofCorrectness(t *testing.T) { t.Run("proof for middle", func(t *testing.T) { t.Parallel() - proof := bmt.Prover{hh}.Proof(64) + proof := pr.Proof(64) expSegmentStrings := []string{ + "0000000000000000000000000000000000000000000000000000000000000000", "ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", "b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", "21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", @@ -129,7 +133,7 @@ func TestProofCorrectness(t *testing.T) { verifySegments(t, expSegmentStrings, proof.ProofSegments) - if !bytes.Equal(proof.ProveSegment, testData[64*hh.Size():66*hh.Size()]) { + if !bytes.Equal(proof.ProveSegment, testDataPadded[64*hh.Size():65*hh.Size()]) { t.Fatal("section incorrect") } @@ -142,6 +146,7 @@ func TestProofCorrectness(t *testing.T) { t.Parallel() segmentStrings := []string{ + "0000000000000000000000000000000000000000000000000000000000000000", "ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", "b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", "21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", @@ -159,9 +164,9 @@ func TestProofCorrectness(t *testing.T) { segments = append(segments, decoded) } - segment := testData[64*hh.Size() : 66*hh.Size()] + segment := testDataPadded[64*hh.Size() : 65*hh.Size()] - rootHash, err := bmt.Prover{hh}.Verify(64, bmt.Proof{ + rootHash, err := pr.Verify(64, bmt.Proof{ ProveSegment: segment, ProofSegments: segments, Span: bmt.LengthToSpan(4096), @@ -200,6 +205,7 @@ func TestProof(t *testing.T) { } rh, err := hh.Hash(nil) + pr := bmt.Prover{hh} if err != nil { t.Fatal(err) } @@ -209,7 +215,7 @@ func TestProof(t *testing.T) { t.Run(fmt.Sprintf("segmentIndex %d", i), func(t *testing.T) { t.Parallel() - proof := bmt.Prover{hh}.Proof(i) + proof := pr.Proof(i) h := pool.Get() defer pool.Put(h) diff --git a/pkg/bmt/trhasher.go b/pkg/bmt/trhasher.go deleted file mode 100644 index 00df6664b85..00000000000 --- a/pkg/bmt/trhasher.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2023 The Swarm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package bmt - -import ( - "hash" - - "github.com/ethersphere/bee/pkg/swarm" -) - -func NewTrHasher(prefix []byte) *Hasher { - capacity := 32 - hasherFact := func() hash.Hash { return swarm.NewTrHasher(prefix) } - conf := NewConf(hasherFact, swarm.BmtBranches, capacity) - - return &Hasher{ - Conf: conf, - result: make(chan []byte), - errc: make(chan error, 1), - span: make([]byte, SpanSize), - bmt: newTree(conf.segmentSize, conf.maxSize, conf.depth, conf.hasher), - } -} diff --git a/pkg/config/chain.go b/pkg/config/chain.go index 2fd2e6f8fe7..4d7fd19e419 100644 --- a/pkg/config/chain.go +++ b/pkg/config/chain.go @@ -42,7 +42,7 @@ var ( SwarmTokenSymbol: "gBZZ", StakingAddress: common.HexToAddress(abi.TestnetStakingAddress), - PostageStampAddress: common.HexToAddress(abi.TestnetPostageStampStampAddress), + PostageStampAddress: common.HexToAddress(abi.TestnetPostageStampAddress), RedistributionAddress: common.HexToAddress(abi.TestnetRedistributionAddress), SwapPriceOracleAddress: common.HexToAddress("0x0c9de531dcb38b758fe8a2c163444a5e54ee0db2"), CurrentFactoryAddress: common.HexToAddress("0x73c412512E1cA0be3b89b77aB3466dA6A1B9d273"), @@ -51,7 +51,7 @@ var ( }, StakingABI: abi.TestnetStakingABI, - PostageStampABI: abi.TestnetPostageStampStampABI, + PostageStampABI: abi.TestnetPostageStampABI, RedistributionABI: abi.TestnetRedistributionABI, } @@ -84,7 +84,7 @@ func GetByChainID(chainID int64) (ChainConfig, bool) { NativeTokenSymbol: Testnet.NativeTokenSymbol, SwarmTokenSymbol: Testnet.SwarmTokenSymbol, StakingABI: abi.TestnetStakingABI, - PostageStampABI: abi.TestnetPostageStampStampABI, + PostageStampABI: abi.TestnetPostageStampABI, RedistributionABI: abi.TestnetRedistributionABI, }, false } diff --git a/pkg/storageincentives/agent.go b/pkg/storageincentives/agent.go index 3a6ba36074a..b856625025e 100644 --- a/pkg/storageincentives/agent.go +++ b/pkg/storageincentives/agent.go @@ -353,7 +353,22 @@ func (a *Agent) handleClaim(ctx context.Context, round uint64) error { a.logger.Info("could not set balance", "err", err) } - txHash, err := a.contract.Claim(ctx) + sampleData, exists := a.state.SampleData(round - 1) + if !exists { + return fmt.Errorf("sample not found") + } + + anchor2, err := a.contract.ReserveSalt(ctx) + if err != nil { + a.logger.Info("failed getting anchor after second reveal", "err", err) + } + + proofs, err := makeInclusionProofs(sampleData.ReserveSampleItems, sampleData.Anchor1, anchor2) + if err != nil { + return fmt.Errorf("making inclusion proofs: %w", err) + } + + txHash, err := a.contract.Claim(ctx, proofs) if err != nil { a.metrics.ErrClaim.Inc() return fmt.Errorf("claiming win: %w", err) @@ -418,10 +433,12 @@ func (a *Agent) handleSample(ctx context.Context, round uint64) (bool, error) { if err != nil { return false, err } + dur := time.Since(now) + a.metrics.SampleDuration.Set(dur.Seconds()) a.logger.Info("produced sample", "hash", sample.ReserveSampleHash, "radius", sample.StorageRadius, "round", round) - a.state.SetSampleData(round, sample, time.Since(now)) + a.state.SetSampleData(round, sample, dur) return true, nil } @@ -437,12 +454,10 @@ func (a *Agent) makeSample(ctx context.Context, storageRadius uint8) (SampleData return SampleData{}, err } - t := time.Now() rSample, err := a.store.ReserveSample(ctx, salt, storageRadius, uint64(timeLimiter), a.minBatchBalance()) if err != nil { return SampleData{}, err } - a.metrics.SampleDuration.Set(time.Since(t).Seconds()) sampleHash, err := sampleHash(rSample.Items) if err != nil { @@ -450,8 +465,10 @@ func (a *Agent) makeSample(ctx context.Context, storageRadius uint8) (SampleData } sample := SampleData{ - ReserveSampleHash: sampleHash, - StorageRadius: storageRadius, + Anchor1: salt, + ReserveSampleItems: rSample.Items, + ReserveSampleHash: sampleHash, + StorageRadius: storageRadius, } return sample, nil @@ -493,7 +510,7 @@ func (a *Agent) commit(ctx context.Context, sample SampleData, round uint64) err return err } - txHash, err := a.contract.Commit(ctx, obfuscatedHash, big.NewInt(int64(round))) + txHash, err := a.contract.Commit(ctx, obfuscatedHash, uint32(round)) if err != nil { a.metrics.ErrCommit.Inc() return err @@ -538,14 +555,16 @@ func (a *Agent) Status() (*Status, error) { } type SampleWithProofs struct { - Items []storer.SampleItem - Hash swarm.Address - Duration time.Duration + Hash swarm.Address `json:"hash"` + Proofs redistribution.ChunkInclusionProofs `json:"proofs"` + Duration time.Duration `json:"duration"` } +// SampleWithProofs is called only by rchash API func (a *Agent) SampleWithProofs( ctx context.Context, anchor1 []byte, + anchor2 []byte, storageRadius uint8, ) (SampleWithProofs, error) { sampleStartTime := time.Now() @@ -562,12 +581,17 @@ func (a *Agent) SampleWithProofs( hash, err := sampleHash(rSample.Items) if err != nil { - return SampleWithProofs{}, fmt.Errorf("sample hash: %w:", err) + return SampleWithProofs{}, fmt.Errorf("sample hash: %w", err) + } + + proofs, err := makeInclusionProofs(rSample.Items, anchor1, anchor2) + if err != nil { + return SampleWithProofs{}, fmt.Errorf("make proofs: %w", err) } return SampleWithProofs{ - Items: rSample.Items, Hash: hash, + Proofs: proofs, Duration: time.Since(sampleStartTime), }, nil } diff --git a/pkg/storageincentives/agent_test.go b/pkg/storageincentives/agent_test.go index 5d7ca5ffe90..70ec3193975 100644 --- a/pkg/storageincentives/agent_test.go +++ b/pkg/storageincentives/agent_test.go @@ -276,14 +276,14 @@ func (m *mockContract) IsWinner(context.Context) (bool, error) { return false, nil } -func (m *mockContract) Claim(context.Context) (common.Hash, error) { +func (m *mockContract) Claim(context.Context, redistribution.ChunkInclusionProofs) (common.Hash, error) { m.mtx.Lock() defer m.mtx.Unlock() m.callsList = append(m.callsList, claimCall) return common.Hash{}, nil } -func (m *mockContract) Commit(context.Context, []byte, *big.Int) (common.Hash, error) { +func (m *mockContract) Commit(context.Context, []byte, uint32) (common.Hash, error) { m.mtx.Lock() defer m.mtx.Unlock() m.callsList = append(m.callsList, commitCall) diff --git a/pkg/storageincentives/export_test.go b/pkg/storageincentives/export_test.go index 8c26fc1761c..f617501ba29 100644 --- a/pkg/storageincentives/export_test.go +++ b/pkg/storageincentives/export_test.go @@ -5,6 +5,7 @@ package storageincentives var ( - NewEvents = newEvents - SampleChunk = sampleChunk + NewEvents = newEvents + SampleChunk = sampleChunk + MakeInclusionProofs = makeInclusionProofs ) diff --git a/pkg/storageincentives/proof.go b/pkg/storageincentives/proof.go index 6415dd51af6..ccc04cd935b 100644 --- a/pkg/storageincentives/proof.go +++ b/pkg/storageincentives/proof.go @@ -5,12 +5,196 @@ package storageincentives import ( + "errors" + "fmt" + "hash" + "math/big" + + "github.com/ethersphere/bee/pkg/bmt" "github.com/ethersphere/bee/pkg/bmtpool" "github.com/ethersphere/bee/pkg/cac" + "github.com/ethersphere/bee/pkg/soc" + "github.com/ethersphere/bee/pkg/storageincentives/redistribution" storer "github.com/ethersphere/bee/pkg/storer" "github.com/ethersphere/bee/pkg/swarm" ) +var errProofCreation = errors.New("reserve commitment hasher: failure in proof creation") + +// spanOffset returns the byte index of chunkdata where the spansize starts +func spanOffset(sampleItem storer.SampleItem) uint8 { + ch := swarm.NewChunk(sampleItem.ChunkAddress, sampleItem.ChunkData) + if soc.Valid(ch) { + return swarm.HashSize + swarm.SocSignatureSize + } + + return 0 +} + +// makeInclusionProofs creates transaction data for claim method. +// In the document this logic, result data, is also called Proof of entitlement (POE). +func makeInclusionProofs( + reserveSampleItems []storer.SampleItem, + anchor1 []byte, + anchor2 []byte, +) (redistribution.ChunkInclusionProofs, error) { + if len(reserveSampleItems) != storer.SampleSize { + return redistribution.ChunkInclusionProofs{}, fmt.Errorf("reserve sample items should have %d elements", storer.SampleSize) + } + if len(anchor1) == 0 { + return redistribution.ChunkInclusionProofs{}, errors.New("anchor1 is not set") + } + if len(anchor2) == 0 { + return redistribution.ChunkInclusionProofs{}, errors.New("anchor2 is not set") + } + + require3 := storer.SampleSize - 1 + require1 := new(big.Int).Mod(new(big.Int).SetBytes(anchor2), big.NewInt(int64(require3))).Uint64() + require2 := new(big.Int).Mod(new(big.Int).SetBytes(anchor2), big.NewInt(int64(require3-1))).Uint64() + if require2 >= require1 { + require2++ + } + + prefixHasherFactory := func() hash.Hash { + return swarm.NewPrefixHasher(anchor1) + } + prefixHasherPool := bmt.NewPool(bmt.NewConf(prefixHasherFactory, swarm.BmtBranches, 8)) + + // Sample chunk proofs + rccontent := bmt.Prover{Hasher: bmtpool.Get()} + rccontent.SetHeaderInt64(swarm.HashSize * storer.SampleSize * 2) + rsc, err := sampleChunk(reserveSampleItems) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + rscData := rsc.Data() + _, err = rccontent.Write(rscData[swarm.SpanSize:]) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + _, err = rccontent.Hash(nil) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + proof1p1 := rccontent.Proof(int(require1) * 2) + proof2p1 := rccontent.Proof(int(require2) * 2) + proofLastp1 := rccontent.Proof(require3 * 2) + bmtpool.Put(rccontent.Hasher) + + // Witness1 proofs + segmentIndex := int(new(big.Int).Mod(new(big.Int).SetBytes(anchor2), big.NewInt(int64(128))).Uint64()) + // OG chunk proof + chunk1Content := bmt.Prover{Hasher: bmtpool.Get()} + chunk1Offset := spanOffset(reserveSampleItems[require1]) + chunk1Content.SetHeader(reserveSampleItems[require1].ChunkData[chunk1Offset : chunk1Offset+swarm.SpanSize]) + chunk1ContentPayload := reserveSampleItems[require1].ChunkData[chunk1Offset+swarm.SpanSize:] + _, err = chunk1Content.Write(chunk1ContentPayload) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + _, err = chunk1Content.Hash(nil) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + proof1p2 := chunk1Content.Proof(segmentIndex) + // TR chunk proof + chunk1TrContent := bmt.Prover{Hasher: prefixHasherPool.Get()} + chunk1TrContent.SetHeader(reserveSampleItems[require1].ChunkData[chunk1Offset : chunk1Offset+swarm.SpanSize]) + _, err = chunk1TrContent.Write(chunk1ContentPayload) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + _, err = chunk1TrContent.Hash(nil) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + proof1p3 := chunk1TrContent.Proof(segmentIndex) + // cleanup + bmtpool.Put(chunk1Content.Hasher) + prefixHasherPool.Put(chunk1TrContent.Hasher) + + // Witness2 proofs + // OG Chunk proof + chunk2Offset := spanOffset(reserveSampleItems[require2]) + chunk2Content := bmt.Prover{Hasher: bmtpool.Get()} + chunk2ContentPayload := reserveSampleItems[require2].ChunkData[chunk2Offset+swarm.SpanSize:] + chunk2Content.SetHeader(reserveSampleItems[require2].ChunkData[chunk2Offset : chunk2Offset+swarm.SpanSize]) + _, err = chunk2Content.Write(chunk2ContentPayload) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + _, err = chunk2Content.Hash(nil) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + proof2p2 := chunk2Content.Proof(segmentIndex) + // TR Chunk proof + chunk2TrContent := bmt.Prover{Hasher: prefixHasherPool.Get()} + chunk2TrContent.SetHeader(reserveSampleItems[require2].ChunkData[chunk2Offset : chunk2Offset+swarm.SpanSize]) + _, err = chunk2TrContent.Write(chunk2ContentPayload) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + _, err = chunk2TrContent.Hash(nil) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + proof2p3 := chunk2TrContent.Proof(segmentIndex) + // cleanup + bmtpool.Put(chunk2Content.Hasher) + prefixHasherPool.Put(chunk2TrContent.Hasher) + + // Witness3 proofs + // OG Chunk proof + chunkLastOffset := spanOffset(reserveSampleItems[require3]) + chunkLastContent := bmt.Prover{Hasher: bmtpool.Get()} + chunkLastContent.SetHeader(reserveSampleItems[require3].ChunkData[chunkLastOffset : chunkLastOffset+swarm.SpanSize]) + chunkLastContentPayload := reserveSampleItems[require3].ChunkData[chunkLastOffset+swarm.SpanSize:] + _, err = chunkLastContent.Write(chunkLastContentPayload) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + _, err = chunkLastContent.Hash(nil) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + proofLastp2 := chunkLastContent.Proof(segmentIndex) + // TR Chunk Proof + chunkLastTrContent := bmt.Prover{Hasher: prefixHasherPool.Get()} + chunkLastTrContent.SetHeader(reserveSampleItems[require3].ChunkData[chunkLastOffset : chunkLastOffset+swarm.SpanSize]) + _, err = chunkLastTrContent.Write(chunkLastContentPayload) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + _, err = chunkLastTrContent.Hash(nil) + if err != nil { + return redistribution.ChunkInclusionProofs{}, errProofCreation + } + proofLastp3 := chunkLastTrContent.Proof(segmentIndex) + // cleanup + bmtpool.Put(chunkLastContent.Hasher) + prefixHasherPool.Put(chunkLastTrContent.Hasher) + + // map to output and add SOC related data if it is necessary + A, err := redistribution.NewChunkInclusionProof(proof1p1, proof1p2, proof1p3, reserveSampleItems[require1]) + if err != nil { + return redistribution.ChunkInclusionProofs{}, err + } + B, err := redistribution.NewChunkInclusionProof(proof2p1, proof2p2, proof2p3, reserveSampleItems[require2]) + if err != nil { + return redistribution.ChunkInclusionProofs{}, err + } + C, err := redistribution.NewChunkInclusionProof(proofLastp1, proofLastp2, proofLastp3, reserveSampleItems[require3]) + if err != nil { + return redistribution.ChunkInclusionProofs{}, err + } + return redistribution.ChunkInclusionProofs{ + A: A, + B: B, + C: C, + }, nil +} + func sampleChunk(items []storer.SampleItem) (swarm.Chunk, error) { contentSize := len(items) * 2 * swarm.HashSize @@ -27,23 +211,9 @@ func sampleChunk(items []storer.SampleItem) (swarm.Chunk, error) { } func sampleHash(items []storer.SampleItem) (swarm.Address, error) { - hasher := bmtpool.Get() - defer bmtpool.Put(hasher) - - for _, s := range items { - _, err := hasher.Write(s.TransformedAddress.Bytes()) - if err != nil { - return swarm.ZeroAddress, err - } + ch, err := sampleChunk(items) + if err != nil { + return swarm.ZeroAddress, err } - hash := hasher.Sum(nil) - - return swarm.NewAddress(hash), nil - - // PH4_Logic: - // ch, err := sampleChunk(items) - // if err != nil { - // return swarm.ZeroAddress, err - // } - // return ch.Address(), nil + return ch.Address(), nil } diff --git a/pkg/storageincentives/proof_test.go b/pkg/storageincentives/proof_test.go index 36350c2af2c..9ee3745e1af 100644 --- a/pkg/storageincentives/proof_test.go +++ b/pkg/storageincentives/proof_test.go @@ -6,20 +6,157 @@ package storageincentives_test import ( "bytes" + _ "embed" + "encoding/json" + "fmt" + "math/big" "testing" + "github.com/ethersphere/bee/pkg/cac" + "github.com/ethersphere/bee/pkg/crypto" + "github.com/ethersphere/bee/pkg/postage" + postagetesting "github.com/ethersphere/bee/pkg/postage/testing" + "github.com/ethersphere/bee/pkg/soc" "github.com/ethersphere/bee/pkg/storageincentives" + "github.com/ethersphere/bee/pkg/storageincentives/redistribution" storer "github.com/ethersphere/bee/pkg/storer" "github.com/ethersphere/bee/pkg/swarm" + "github.com/ethersphere/bee/pkg/util/testutil" + "github.com/google/go-cmp/cmp" ) +// Test asserts valid case for MakeInclusionProofs. +func TestMakeInclusionProofs(t *testing.T) { + t.Parallel() + + anchor := testutil.RandBytes(t, 1) + sample := storer.RandSample(t, anchor) + + _, err := storageincentives.MakeInclusionProofs(sample.Items, anchor, anchor) + if err != nil { + t.Fatal(err) + } +} + +//go:embed testdata/inclusion-proofs.json +var testData []byte + +// Test asserts that MakeInclusionProofs will generate the same +// output for given sample. +func TestMakeInclusionProofsRegression(t *testing.T) { + t.Parallel() + + const sampleSize = 16 + + keyRaw := `00000000000000000000000000000000` + privKey, err := crypto.DecodeSecp256k1PrivateKey([]byte(keyRaw)) + if err != nil { + t.Fatal(err) + } + signer := crypto.NewDefaultSigner(privKey) + + stampID, _ := crypto.LegacyKeccak256([]byte("The Inverted Jenny")) + index := []byte{0, 0, 0, 0, 0, 8, 3, 3} + timestamp := []byte{0, 0, 0, 0, 0, 3, 3, 8} + stamper := func(addr swarm.Address) *postage.Stamp { + sig := postagetesting.MustNewValidSignature(signer, addr, stampID, index, timestamp) + return postage.NewStamp(stampID, index, timestamp, sig) + } + + anchor1 := big.NewInt(100).Bytes() + anchor2 := big.NewInt(30).Bytes() // this anchor will pick chunks 3, 6, 15 + + // generate chunks that will be used as sample + sampleChunks := make([]swarm.Chunk, 0, sampleSize) + for i := 0; i < sampleSize; i++ { + ch, err := cac.New([]byte(fmt.Sprintf("Unstoppable data! Chunk #%d", i+1))) + if err != nil { + t.Fatal(err) + } + + if i%2 == 0 { + id, err := crypto.LegacyKeccak256([]byte(fmt.Sprintf("ID #%d", i+1))) + if err != nil { + t.Fatal(err) + } + + socCh, err := soc.New(id, ch).Sign(signer) + if err != nil { + t.Fatal(err) + } + + ch = socCh + } + + ch = ch.WithStamp(stamper(ch.Address())) + + sampleChunks = append(sampleChunks, ch) + } + + // make sample from chunks + sample, err := storer.MakeSampleUsingChunks(sampleChunks, anchor1) + if err != nil { + t.Fatal(err) + } + + // assert that sample chunk hash/address does not change + sch, err := storageincentives.SampleChunk(sample.Items) + if err != nil { + t.Fatal(err) + } + if want := swarm.MustParseHexAddress("193bbea3dd0656d813c2c1e27b821f141286bbe6ab0dbf8e26fc7dd491e8f921"); !sch.Address().Equal(want) { + t.Fatalf("expecting sample chunk address %v, got %v", want, sch.Address()) + } + + // assert that inclusion proofs values does not change + proofs, err := storageincentives.MakeInclusionProofs(sample.Items, anchor1, anchor2) + if err != nil { + t.Fatal(err) + } + + var expectedProofs redistribution.ChunkInclusionProofs + + err = json.Unmarshal(testData, &expectedProofs) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(proofs, expectedProofs); diff != "" { + t.Fatalf("unexpected inclusion proofs (-want +have):\n%s", diff) + } +} + +// Test asserts cases when MakeInclusionProofs should return error. +func TestMakeInclusionProofsExpectedError(t *testing.T) { + t.Parallel() + + t.Run("invalid sample length", func(t *testing.T) { + anchor := testutil.RandBytes(t, 8) + sample := storer.RandSample(t, anchor) + + _, err := storageincentives.MakeInclusionProofs(sample.Items[:1], anchor, anchor) + if err == nil { + t.Fatal("expecting error") + } + }) + + t.Run("empty anchor", func(t *testing.T) { + sample := storer.RandSample(t, []byte{}) + + _, err := storageincentives.MakeInclusionProofs(sample.Items[:1], []byte{}, []byte{}) + if err == nil { + t.Fatal("expecting error") + } + }) +} + // Tests asserts that creating sample chunk is valid for all lengths [1-MaxSampleSize] func TestSampleChunk(t *testing.T) { t.Parallel() sample := storer.RandSample(t, nil) - for i := 1; i < len(sample.Items); i++ { + for i := 0; i < len(sample.Items); i++ { items := sample.Items[:i] chunk, err := storageincentives.SampleChunk(items) @@ -40,6 +177,10 @@ func TestSampleChunk(t *testing.T) { } pos += swarm.HashSize } + + if !chunk.Address().IsValidNonEmpty() { + t.Error("address shouldn't be empty") + } } } diff --git a/pkg/storageincentives/redistribution/inclusionproof.go b/pkg/storageincentives/redistribution/inclusionproof.go new file mode 100644 index 00000000000..b786d1d3001 --- /dev/null +++ b/pkg/storageincentives/redistribution/inclusionproof.go @@ -0,0 +1,105 @@ +// Copyright 2023 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package redistribution + +import ( + "encoding/binary" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethersphere/bee/pkg/bmt" + "github.com/ethersphere/bee/pkg/soc" + "github.com/ethersphere/bee/pkg/storer" + "github.com/ethersphere/bee/pkg/swarm" +) + +type ChunkInclusionProofs struct { + A ChunkInclusionProof `json:"proof1"` + B ChunkInclusionProof `json:"proof2"` + C ChunkInclusionProof `json:"proofLast"` +} + +// ChunkInclusionProof structure must exactly match +// corresponding structure (of the same name) in Redistribution.sol smart contract. +// github.com/ethersphere/storage-incentives/blob/ph_f2/src/Redistribution.sol +// github.com/ethersphere/storage-incentives/blob/master/src/Redistribution.sol (when merged to master) +type ChunkInclusionProof struct { + ProofSegments []common.Hash `json:"proofSegments"` + ProveSegment common.Hash `json:"proveSegment"` + ProofSegments2 []common.Hash `json:"proofSegments2"` + ProveSegment2 common.Hash `json:"proveSegment2"` + ChunkSpan uint64 `json:"chunkSpan"` + ProofSegments3 []common.Hash `json:"proofSegments3"` + PostageProof PostageProof `json:"postageProof"` + SocProof []SOCProof `json:"socProof"` +} + +// SOCProof structure must exactly match +// corresponding structure (of the same name) in Redistribution.sol smart contract. +type PostageProof struct { + Signature []byte `json:"signature"` + PostageId common.Hash `json:"postageId"` + Index uint64 `json:"index"` + TimeStamp uint64 `json:"timeStamp"` +} + +// SOCProof structure must exactly match +// corresponding structure (of the same name) in Redistribution.sol smart contract. +type SOCProof struct { + Signer common.Address `json:"signer"` + Signature []byte `json:"signature"` + Identifier common.Hash `json:"identifier"` + ChunkAddr common.Hash `json:"chunkAddr"` +} + +// NewChunkInclusionProof transforms arguments to ChunkInclusionProof object +func NewChunkInclusionProof(proofp1, proofp2, proofp3 bmt.Proof, sampleItem storer.SampleItem) (ChunkInclusionProof, error) { + socProof, err := makeSOCProof(sampleItem) + if err != nil { + return ChunkInclusionProof{}, err + } + + return ChunkInclusionProof{ + ProofSegments: toCommonHash(proofp1.ProofSegments), + ProveSegment: common.BytesToHash(proofp1.ProveSegment), + ProofSegments2: toCommonHash(proofp2.ProofSegments), + ProveSegment2: common.BytesToHash(proofp2.ProveSegment), + ChunkSpan: binary.LittleEndian.Uint64(proofp2.Span[:swarm.SpanSize]), // should be uint64 on the other size; copied from pkg/api/bytes.go + ProofSegments3: toCommonHash(proofp3.ProofSegments), + PostageProof: PostageProof{ + Signature: sampleItem.Stamp.Sig(), + PostageId: common.BytesToHash(sampleItem.Stamp.BatchID()), + Index: binary.BigEndian.Uint64(sampleItem.Stamp.Index()), + TimeStamp: binary.BigEndian.Uint64(sampleItem.Stamp.Timestamp()), + }, + SocProof: socProof, + }, nil +} + +func toCommonHash(hashes [][]byte) []common.Hash { + output := make([]common.Hash, len(hashes)) + for i, s := range hashes { + output[i] = common.BytesToHash(s) + } + return output +} + +func makeSOCProof(sampleItem storer.SampleItem) ([]SOCProof, error) { + ch := swarm.NewChunk(sampleItem.ChunkAddress, sampleItem.ChunkData) + if !soc.Valid(ch) { + return []SOCProof{}, nil + } + + socCh, err := soc.FromChunk(ch) + if err != nil { + return []SOCProof{}, err + } + + return []SOCProof{{ + Signer: common.BytesToAddress(socCh.OwnerAddress()), + Signature: socCh.Signature(), + Identifier: common.BytesToHash(socCh.ID()), + ChunkAddr: common.BytesToHash(socCh.WrappedChunk().Address().Bytes()), + }}, nil +} diff --git a/pkg/storageincentives/redistribution/redistribution.go b/pkg/storageincentives/redistribution/redistribution.go index d7187726b42..2c163f5a76c 100644 --- a/pkg/storageincentives/redistribution/redistribution.go +++ b/pkg/storageincentives/redistribution/redistribution.go @@ -23,8 +23,8 @@ type Contract interface { ReserveSalt(context.Context) ([]byte, error) IsPlaying(context.Context, uint8) (bool, error) IsWinner(context.Context) (bool, error) - Claim(context.Context) (common.Hash, error) - Commit(context.Context, []byte, *big.Int) (common.Hash, error) + Claim(context.Context, ChunkInclusionProofs) (common.Hash, error) + Commit(context.Context, []byte, uint32) (common.Hash, error) Reveal(context.Context, uint8, []byte, []byte) (common.Hash, error) } @@ -92,8 +92,8 @@ func (c *contract) IsWinner(ctx context.Context) (isWinner bool, err error) { } // Claim sends a transaction to blockchain if a win is claimed. -func (c *contract) Claim(ctx context.Context) (common.Hash, error) { - callData, err := c.incentivesContractABI.Pack("claim") +func (c *contract) Claim(ctx context.Context, proofs ChunkInclusionProofs) (common.Hash, error) { + callData, err := c.incentivesContractABI.Pack("claim", proofs.A, proofs.B, proofs.C) if err != nil { return common.Hash{}, err } @@ -115,7 +115,7 @@ func (c *contract) Claim(ctx context.Context) (common.Hash, error) { } // Commit submits the obfusHash hash by sending a transaction to the blockchain. -func (c *contract) Commit(ctx context.Context, obfusHash []byte, round *big.Int) (common.Hash, error) { +func (c *contract) Commit(ctx context.Context, obfusHash []byte, round uint32) (common.Hash, error) { callData, err := c.incentivesContractABI.Pack("commit", common.BytesToHash(obfusHash), common.BytesToHash(c.overlay.Bytes()), round) if err != nil { return common.Hash{}, err diff --git a/pkg/storageincentives/redistribution/redistribution_test.go b/pkg/storageincentives/redistribution/redistribution_test.go index d47790d3d4b..943d3013ba4 100644 --- a/pkg/storageincentives/redistribution/redistribution_test.go +++ b/pkg/storageincentives/redistribution/redistribution_test.go @@ -7,6 +7,7 @@ package redistribution_test import ( "bytes" "context" + "encoding/binary" "errors" "fmt" "math/big" @@ -27,6 +28,36 @@ import ( var redistributionContractABI = abiutil.MustParseABI(chaincfg.Testnet.RedistributionABI) +func randChunkInclusionProof(t *testing.T) redistribution.ChunkInclusionProof { + t.Helper() + + return redistribution.ChunkInclusionProof{ + ProofSegments: []common.Hash{common.BytesToHash(testutil.RandBytes(t, 32))}, + ProveSegment: common.BytesToHash(testutil.RandBytes(t, 32)), + ProofSegments2: []common.Hash{common.BytesToHash(testutil.RandBytes(t, 32))}, + ProveSegment2: common.BytesToHash(testutil.RandBytes(t, 32)), + ProofSegments3: []common.Hash{common.BytesToHash(testutil.RandBytes(t, 32))}, + PostageProof: redistribution.PostageProof{ + Signature: testutil.RandBytes(t, 65), + PostageId: common.BytesToHash(testutil.RandBytes(t, 32)), + Index: binary.BigEndian.Uint64(testutil.RandBytes(t, 8)), + TimeStamp: binary.BigEndian.Uint64(testutil.RandBytes(t, 8)), + }, + ChunkSpan: 1, + SocProof: []redistribution.SOCProof{}, + } +} + +func randChunkInclusionProofs(t *testing.T) redistribution.ChunkInclusionProofs { + t.Helper() + + return redistribution.ChunkInclusionProofs{ + A: randChunkInclusionProof(t), + B: randChunkInclusionProof(t), + C: randChunkInclusionProof(t), + } +} + func TestRedistribution(t *testing.T) { t.Parallel() @@ -153,7 +184,9 @@ func TestRedistribution(t *testing.T) { t.Run("Claim", func(t *testing.T) { t.Parallel() - expectedCallData, err := redistributionContractABI.Pack("claim") + proofs := randChunkInclusionProofs(t) + + expectedCallData, err := redistributionContractABI.Pack("claim", proofs.A, proofs.B, proofs.C) if err != nil { t.Fatal(err) } @@ -183,7 +216,7 @@ func TestRedistribution(t *testing.T) { redistributionContractABI, ) - _, err = contract.Claim(ctx) + _, err = contract.Claim(ctx, proofs) if err != nil { t.Fatal(err) } @@ -192,7 +225,8 @@ func TestRedistribution(t *testing.T) { t.Run("Claim with tx reverted", func(t *testing.T) { t.Parallel() - expectedCallData, err := redistributionContractABI.Pack("claim") + proofs := randChunkInclusionProofs(t) + expectedCallData, err := redistributionContractABI.Pack("claim", proofs.A, proofs.B, proofs.C) if err != nil { t.Fatal(err) } @@ -222,7 +256,7 @@ func TestRedistribution(t *testing.T) { redistributionContractABI, ) - _, err = contract.Claim(ctx) + _, err = contract.Claim(ctx, proofs) if !errors.Is(err, transaction.ErrTransactionReverted) { t.Fatal(err) } @@ -233,7 +267,7 @@ func TestRedistribution(t *testing.T) { var obfus [32]byte testobfus := common.Hex2Bytes("hash") copy(obfus[:], testobfus) - expectedCallData, err := redistributionContractABI.Pack("commit", obfus, common.BytesToHash(owner.Bytes()), big.NewInt(0)) + expectedCallData, err := redistributionContractABI.Pack("commit", obfus, common.BytesToHash(owner.Bytes()), uint32(0)) if err != nil { t.Fatal(err) } @@ -263,7 +297,7 @@ func TestRedistribution(t *testing.T) { redistributionContractABI, ) - _, err = contract.Commit(ctx, testobfus, big.NewInt(0)) + _, err = contract.Commit(ctx, testobfus, uint32(0)) if err != nil { t.Fatal(err) } @@ -368,7 +402,7 @@ func TestRedistribution(t *testing.T) { t.Run("invalid call data", func(t *testing.T) { t.Parallel() - expectedCallData, err := redistributionContractABI.Pack("commit", common.BytesToHash(common.Hex2Bytes("some hash")), common.BytesToHash(common.Hex2Bytes("some address")), big.NewInt(0)) + expectedCallData, err := redistributionContractABI.Pack("commit", common.BytesToHash(common.Hex2Bytes("some hash")), common.BytesToHash(common.Hex2Bytes("some address")), uint32(0)) if err != nil { t.Fatal(err) } @@ -390,7 +424,7 @@ func TestRedistribution(t *testing.T) { redistributionContractABI, ) - _, err = contract.Commit(ctx, common.Hex2Bytes("hash"), big.NewInt(0)) + _, err = contract.Commit(ctx, common.Hex2Bytes("hash"), uint32(0)) if err == nil { t.Fatal("expected error") } diff --git a/pkg/storageincentives/redistributionstate.go b/pkg/storageincentives/redistributionstate.go index b61c7a5cbd2..b1f4b60dd03 100644 --- a/pkg/storageincentives/redistributionstate.go +++ b/pkg/storageincentives/redistributionstate.go @@ -15,6 +15,7 @@ import ( "github.com/ethersphere/bee/pkg/log" "github.com/ethersphere/bee/pkg/settlement/swap/erc20" "github.com/ethersphere/bee/pkg/storage" + storer "github.com/ethersphere/bee/pkg/storer" "github.com/ethersphere/bee/pkg/swarm" "github.com/ethersphere/bee/pkg/transaction" ) @@ -63,8 +64,10 @@ type RoundData struct { } type SampleData struct { - ReserveSampleHash swarm.Address - StorageRadius uint8 + Anchor1 []byte + ReserveSampleItems []storer.SampleItem + ReserveSampleHash swarm.Address + StorageRadius uint8 } func NewStatus() *Status { diff --git a/pkg/storageincentives/soc_mine_test.go b/pkg/storageincentives/soc_mine_test.go new file mode 100644 index 00000000000..d5b93241b21 --- /dev/null +++ b/pkg/storageincentives/soc_mine_test.go @@ -0,0 +1,201 @@ +// Copyright 2023 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package storageincentives_test + +import ( + "context" + "encoding/binary" + "encoding/hex" + "fmt" + "hash" + "math/big" + "os" + "sync" + "testing" + + "github.com/ethersphere/bee/pkg/bmt" + "github.com/ethersphere/bee/pkg/cac" + "github.com/ethersphere/bee/pkg/crypto" + "github.com/ethersphere/bee/pkg/soc" + "github.com/ethersphere/bee/pkg/swarm" + "golang.org/x/sync/errgroup" +) + +// TestSocMine dumps a sample made out SOCs to upload for storage incestives + +// dump chunks + +// go test -v ./pkg/storageincentives/ -run TestSocMine -count 1 > socs.txt + +// to generate uploads using the input +// cat socs.txt | tail 19 | head 16 | perl -pne 's/([a-f0-9]+)\t([a-f0-9]+)\t([a-f0-9]+)\t([a-f0-9]+)/echo -n $4 | xxd -r -p | curl -X POST \"http:\/\/localhost:1633\/soc\/$1\/$2?sig=$3\" -H \"accept: application\/json, text\/plain, \/\" -H \"content-type: application\/octet-stream\" -H \"swarm-postage-batch-id: 14b26beca257e763609143c6b04c2c487f01a051798c535c2f542ce75a97c05f\" --data-binary \@-/' +func TestSocMine(t *testing.T) { + t.Parallel() + // the anchor used in neighbourhood selection and reserve salt for sampling + prefix, err := hex.DecodeString("3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff") + if err != nil { + t.Fatal(err) + } + // the transformed address hasher factory function + prefixhasher := func() hash.Hash { return swarm.NewPrefixHasher(prefix) } + trHasher := func() hash.Hash { return bmt.NewHasher(prefixhasher) } + // the bignum cast of the maximum sample value (upper bound on transformed addresses as a 256-bit article) + // this constant is for a minimum reserve size of 2 million chunks with sample size of 16 + // = 1.284401 * 10^71 = 1284401 + 66 0-s + mstring := "1284401" + for i := 0; i < 66; i++ { + mstring = mstring + "0" + } + n, ok := new(big.Int).SetString(mstring, 10) + if !ok { + t.Fatalf("SetString: error setting to '%s'", mstring) + } + // the filter function on the SOC address + // meant to make sure we pass check for proof of retrievability for + // a node of overlay 0x65xxx with a reserve depth of 1, i.e., + // SOC address must start with zero bit + filterSOCAddr := func(a swarm.Address) bool { + return a.Bytes()[0]&0x80 != 0x00 + } + // the filter function on the transformed address using the density estimation constant + filterTrAddr := func(a swarm.Address) (bool, error) { + m := new(big.Int).SetBytes(a.Bytes()) + return m.Cmp(n) < 0, nil + } + // setup the signer with a private key from a fixture + data, err := hex.DecodeString("634fb5a872396d9693e5c9f9d7233cfa93f395c093371017ff44aa9ae6564cdd") + if err != nil { + t.Fatal(err) + } + privKey, err := crypto.DecodeSecp256k1PrivateKey(data) + if err != nil { + t.Fatal(err) + } + signer := crypto.NewDefaultSigner(privKey) + + sampleSize := 16 + // for sanity check: given a filterSOCAddr requiring a 0 leading bit (chance of 1/2) + // we expect an overall rough 4 million chunks to be mined to create this sample + // for 8 workers that is half a million round on average per worker + err = makeChunks(t, signer, sampleSize, filterSOCAddr, filterTrAddr, trHasher) + if err != nil { + t.Fatal(err) + } +} + +func makeChunks(t *testing.T, signer crypto.Signer, sampleSize int, filterSOCAddr func(swarm.Address) bool, filterTrAddr func(swarm.Address) (bool, error), trHasher func() hash.Hash) error { + t.Helper() + + // set owner address from signer + ethAddress, err := signer.EthereumAddress() + if err != nil { + return err + } + ownerAddressBytes := ethAddress.Bytes() + + // use the same wrapped chunk for all mined SOCs + payload := []byte("foo") + ch, err := cac.New(payload) + if err != nil { + return err + } + + var done bool // to signal sampleSize number of chunks found + sampleC := make(chan *soc.SOC, 1) // channel to push resuls on + sample := make([]*soc.SOC, sampleSize) // to collect the sample + ctx, cancel := context.WithCancel(context.Background()) + eg, ectx := errgroup.WithContext(ctx) + // the main loop terminating after sampleSize SOCs have been generated + eg.Go(func() error { + defer cancel() + for i := 0; i < sampleSize; i++ { + select { + case sample[i] = <-sampleC: + case <-ectx.Done(): + return ectx.Err() + } + } + done = true + return nil + }) + + // loop to start mining workers + count := 8 // number of parallel workers + wg := sync.WaitGroup{} + for i := 0; i < count; i++ { + i := i + wg.Add(1) + eg.Go(func() (err error) { + offset := i * 4 + found := 0 + for seed := uint32(1); ; seed++ { + select { + case <-ectx.Done(): + defer wg.Done() + t.Logf("LOG quit worker: %d, rounds: %d, found: %d\n", i, seed, found) + return ectx.Err() + default: + } + id := make([]byte, 32) + binary.BigEndian.PutUint32(id[offset:], seed) + s := soc.New(id, ch) + addr, err := soc.CreateAddress(id, ownerAddressBytes) + if err != nil { + return err + } + // continue if mined SOC addr is not good + if !filterSOCAddr(addr) { + continue + } + hasher := trHasher() + data := s.WrappedChunk().Data() + hasher.(*bmt.Hasher).SetHeader(data[:8]) + _, err = hasher.Write(data[8:]) + if err != nil { + return err + } + trAddr := hasher.Sum(nil) + // hashing the transformed wrapped chunk address with the SOC address + // to arrive at a unique transformed SOC address despite identical payloads + trSocAddr, err := soc.CreateAddress(addr.Bytes(), trAddr) + if err != nil { + return err + } + ok, err := filterTrAddr(trSocAddr) + if err != nil { + return err + } + if ok { + select { + case sampleC <- s: + found++ + t.Logf("LOG worker: %d, rounds: %d, found: %d, id:%x\n", i, seed, found, id) + case <-ectx.Done(): + defer wg.Done() + t.Logf("LOG quit worker: %d, rounds: %d, found: %d\n", i, seed, found) + return ectx.Err() + } + } + } + }) + } + if err := eg.Wait(); !done && err != nil { + return err + } + wg.Wait() + for _, s := range sample { + + // signs the chunk + sch, err := s.Sign(signer) + if err != nil { + return err + } + data := sch.Data() + id, sig, payload := data[:32], data[32:97], data[97:] + fmt.Fprintf(os.Stdout, "%x\t%x\t%x\t%x\n", ownerAddressBytes, id, sig, payload) + + } + return nil +} diff --git a/pkg/storageincentives/testdata/inclusion-proofs.json b/pkg/storageincentives/testdata/inclusion-proofs.json new file mode 100644 index 00000000000..fe79666096e --- /dev/null +++ b/pkg/storageincentives/testdata/inclusion-proofs.json @@ -0,0 +1,126 @@ +{ + "proof1": { + "proofSegments": [ + "0x0875605dea48e812c9685ffba220a2b848bdbafdb95e02d087ba4a32925ea34f", + "0xf873df729270d5f4064286f3f018385a07cb4228734d8aca794299fee6e3e3e5", + "0x1fa8767fe303fe7487f5d58e4d72e5e170cf135f58a91b4fe19e4b19e5b67b5a", + "0x0f64ed713e25291e2c5a0561f584fa78c55a399e31919903d215dd622bcfd0ec", + "0x34dac0c73538614801c1ad16e272ef57f0b96a972073d15418f38daf9eb401c0", + "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d", + "0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968" + ], + "proveSegment": "0x7133885ac59dca7b97773acb740e978d41a4af45bd563067c8a3d863578488f1", + "proofSegments2": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", + "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", + "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", + "0x2047b070a295f8d517121d9ac9b3d5f9a944bac6cfab72dd5a7c625ab4558b0a", + "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d", + "0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968" + ], + "proveSegment2": "0x0000000000000000000000000000000000000000000000000000000000000000", + "chunkSpan": 26, + "proofSegments3": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xa7f526447b68535121d36909a7585c9610d4fe6d4115540464c70499b0d7136d", + "0x066dd7ce6f4f1c97e78ff1c271916db25cb06128c92f8c8520807a0fa2ba93ff", + "0xdf43c86b00db2156e769e8a8df1f08dc89ab5661c6fbaa9563f96fb9c051fc63", + "0x7327aecc9178bab420bb6fe482e07b65af69775b55666ec1ac8ab3da5bcec6dc", + "0xb68323ecaad1185a5e078f41c94c59d0b6dda5d57e109866e64d44acb8702846", + "0x478adfa93a7bb904d0aa86ff0d559f43aa915ee7865592e717b72a24452181cb" + ], + "postageProof": { + "signature": "p8jRioJ504AxaevPTlp9vdTf/vpZHqrY0c6qY2p5Otl15/exCHvOpBdlJbAALt3grL/aINvS37vnd8qziWj9xhs=", + "postageId": "0x4c8efc14c8e3cee608174f995d7afe155897bf643a31226e4f1363bc97686aef", + "index": 525059, + "timeStamp": 197384 + }, + "socProof": [ + { + "signer": "0x827b44d53df2854057713b25cdd653eb70fe36c4", + "signature": "TpV2lJM45MI/RwO/gTZyVquFmzKTT+9Nsu5Gp2v2vjVOlqxii4eEst4Lvq5ZdUaXgxktbRcFSF/Krdje3ebiqhs=", + "identifier": "0x6223cfdd75a40440ccd32d0b11b24f08562ec63b1ea3b8cb1a59dfc3e3c33595", + "chunkAddr": "0xf32442586d93d8c002372ed41fa2ea1f281f38311c161d535c3665de5d9bfd92" + } + ] + }, + "proof2": { + "proofSegments": [ + "0x463aeb4ca5f000064c082e56eba387004265d2f47bf1226ef2d86cb163bcca3a", + "0x829af58b2a2f1c6c156baa196f03be4df510a96419f2dd54c456d3da30166312", + "0xdee4815ec42efa507b79cf4eb1f272e07be1b526cbd48137a287d9e5b2b2808a", + "0x0f64ed713e25291e2c5a0561f584fa78c55a399e31919903d215dd622bcfd0ec", + "0x34dac0c73538614801c1ad16e272ef57f0b96a972073d15418f38daf9eb401c0", + "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d", + "0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968" + ], + "proveSegment": "0x535e6df58a122a8f5e6c851c19b3e042f4cd1b5c5a8c499581c9f6d4e3509182", + "proofSegments2": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", + "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", + "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", + "0x46f43b515833749217540ac60c79e0c6a54c73f3500850b5869b31d5c89d101f", + "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d", + "0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968" + ], + "proveSegment2": "0x0000000000000000000000000000000000000000000000000000000000000000", + "chunkSpan": 26, + "proofSegments3": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xa7f526447b68535121d36909a7585c9610d4fe6d4115540464c70499b0d7136d", + "0x066dd7ce6f4f1c97e78ff1c271916db25cb06128c92f8c8520807a0fa2ba93ff", + "0xdf43c86b00db2156e769e8a8df1f08dc89ab5661c6fbaa9563f96fb9c051fc63", + "0x4284c510d7d64c9e052c73bddadb1fca522fd26caf2ebf007faad50a9a0f09fa", + "0xb68323ecaad1185a5e078f41c94c59d0b6dda5d57e109866e64d44acb8702846", + "0x478adfa93a7bb904d0aa86ff0d559f43aa915ee7865592e717b72a24452181cb" + ], + "postageProof": { + "signature": "sCdPzaWeiq/+6AMCGXGnZKAXziwPQcjOtu796oBwVvYhqY/qtevzO7YGXknAUPQT7IhAsAj8Ik2ILOUkTOPgFxw=", + "postageId": "0x4c8efc14c8e3cee608174f995d7afe155897bf643a31226e4f1363bc97686aef", + "index": 525059, + "timeStamp": 197384 + }, + "socProof": [] + }, + "proofLast": { + "proofSegments": [ + "0xfee18543782df46a86f85456e62dc973a4c84369b6b1cd4f93e57fe247f9730e", + "0x23a0858ee2b8b4cb0ba66d3533f468d6b583a6b77df0cc78fc6df64dc735a917", + "0xb6bffa54dec44ad57349f9aef6cb65a1f8807f15447462ec519751220e5a5bc3", + "0x553aae9948fc13c33d8b353cf5694ecadc7c40c8316ce09cbd4d864dbb94f026", + "0xaf7db874a9b5addf602b3e899194480a32afec6d6cd4ec0fadf9e065db739dd5", + "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d", + "0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968" + ], + "proveSegment": "0x5ba2c8b912fad4aeb4a11a960946d07b9f66bc40ac54d87224914d75f5aeea5f", + "proofSegments2": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", + "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", + "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", + "0x7f575db255ef42dcaeb7658df9f33fe5a1aad5d41af51a72a381acea29d98a12", + "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d", + "0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968" + ], + "proveSegment2": "0x0000000000000000000000000000000000000000000000000000000000000000", + "chunkSpan": 27, + "proofSegments3": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xa7f526447b68535121d36909a7585c9610d4fe6d4115540464c70499b0d7136d", + "0x066dd7ce6f4f1c97e78ff1c271916db25cb06128c92f8c8520807a0fa2ba93ff", + "0xdf43c86b00db2156e769e8a8df1f08dc89ab5661c6fbaa9563f96fb9c051fc63", + "0x7683427ba0ef1fbebf97f2fc36859df88ead8123369fe38d7b767b7a7eda5294", + "0xb68323ecaad1185a5e078f41c94c59d0b6dda5d57e109866e64d44acb8702846", + "0x478adfa93a7bb904d0aa86ff0d559f43aa915ee7865592e717b72a24452181cb" + ], + "postageProof": { + "signature": "Z0fFjOhhNIbGlvW7c5PJxZCUNxlpw6Ur+vdRksYF9K18cMbnH90yDiDQBeQulO4yECwjTrRl9PX9nbYPytA1axw=", + "postageId": "0x4c8efc14c8e3cee608174f995d7afe155897bf643a31226e4f1363bc97686aef", + "index": 525059, + "timeStamp": 197384 + }, + "socProof": [] + } +} \ No newline at end of file diff --git a/pkg/storer/sample.go b/pkg/storer/sample.go index 690e06affc3..7a37fb62e99 100644 --- a/pkg/storer/sample.go +++ b/pkg/storer/sample.go @@ -7,9 +7,9 @@ package storer import ( "bytes" "context" - "crypto/hmac" "encoding/binary" "fmt" + "hash" "math/big" "sort" "sync" @@ -26,7 +26,7 @@ import ( "golang.org/x/sync/errgroup" ) -const SampleSize = 8 +const SampleSize = 16 type SampleItem struct { TransformedAddress swarm.Address @@ -40,18 +40,37 @@ type Sample struct { Items []SampleItem } +// RandSample returns Sample with random values. func RandSample(t *testing.T, anchor []byte) Sample { t.Helper() - hasher := bmt.NewTrHasher(anchor) - - items := make([]SampleItem, SampleSize) + chunks := make([]swarm.Chunk, SampleSize) for i := 0; i < SampleSize; i++ { ch := chunk.GenerateTestRandomChunk() + if i%3 == 0 { + ch = chunk.GenerateTestRandomSoChunk(t, ch) + } + chunks[i] = ch + } + + sample, err := MakeSampleUsingChunks(chunks, anchor) + if err != nil { + t.Fatal(err) + } - tr, err := transformedAddress(hasher, ch, swarm.ChunkTypeContentAddressed) + return sample +} + +// MakeSampleUsingChunks returns Sample constructed using supplied chunks. +func MakeSampleUsingChunks(chunks []swarm.Chunk, anchor []byte) (Sample, error) { + prefixHasherFactory := func() hash.Hash { + return swarm.NewPrefixHasher(anchor) + } + items := make([]SampleItem, len(chunks)) + for i, ch := range chunks { + tr, err := transformedAddress(bmt.NewHasher(prefixHasherFactory), ch, swarm.ChunkTypeContentAddressed) if err != nil { - t.Fatal(err) + return Sample{}, err } items[i] = SampleItem{ @@ -66,7 +85,7 @@ func RandSample(t *testing.T, anchor []byte) Sample { return items[i].TransformedAddress.Compare(items[j].TransformedAddress) == -1 }) - return Sample{Items: items} + return Sample{Items: items}, nil } func newStamp(s swarm.Stamp) *postage.Stamp { @@ -135,19 +154,25 @@ func (db *DB) ReserveSample( // Phase 2: Get the chunk data and calculate transformed hash sampleItemChan := make(chan SampleItem, 64) + prefixHasherFactory := func() hash.Hash { + return swarm.NewPrefixHasher(anchor) + } + const workers = 6 + for i := 0; i < workers; i++ { g.Go(func() error { wstat := SampleStats{} + hasher := bmt.NewHasher(prefixHasherFactory) defer func() { addStats(wstat) }() - hmacr := hmac.New(swarm.NewHasher, anchor) for chItem := range chunkC { // exclude chunks who's batches balance are below minimum if _, found := excludedBatchIDs[string(chItem.BatchID)]; found { wstat.BelowBalanceIgnored++ + continue } @@ -169,16 +194,12 @@ func (db *DB) ReserveSample( wstat.ChunkLoadDuration += time.Since(chunkLoadStart) - hmacrStart := time.Now() - - hmacr.Reset() - _, err = hmacr.Write(chunk.Data()) + taddrStart := time.Now() + taddr, err := transformedAddress(hasher, chunk, chItem.Type) if err != nil { return err } - taddr := swarm.NewAddress(hmacr.Sum(nil)) - - wstat.HmacrDuration += time.Since(hmacrStart) + wstat.TaddrDuration += time.Since(taddrStart) select { case sampleItemChan <- SampleItem{ @@ -222,6 +243,15 @@ func (db *DB) ReserveSample( } } + contains := func(addr swarm.Address) int { + for index, item := range sampleItems { + if item.ChunkAddress.Compare(addr) == 0 { + return index + } + } + return -1 + } + // Phase 3: Assemble the sample. Here we need to assemble only the first SampleSize // no of items from the results of the 2nd phase. // In this step stamps are loaded and validated only if chunk will be added to sample. @@ -259,6 +289,13 @@ func (db *DB) ReserveSample( stats.ValidStampDuration += time.Since(start) item.Stamp = stamp + + // ensuring to pass the check order function of redistribution contract + if index := contains(item.TransformedAddress); index != -1 { + // replace the chunk at index + sampleItems[index] = item + continue + } insert(item) stats.SampleInserts++ } @@ -359,7 +396,7 @@ type SampleStats struct { NewIgnored int64 InvalidStamp int64 BelowBalanceIgnored int64 - HmacrDuration time.Duration + TaddrDuration time.Duration ValidStampDuration time.Duration BatchesBelowValueDuration time.Duration RogueChunk int64 @@ -376,7 +413,7 @@ func (s *SampleStats) add(other SampleStats) { s.NewIgnored += other.NewIgnored s.InvalidStamp += other.InvalidStamp s.BelowBalanceIgnored += other.BelowBalanceIgnored - s.HmacrDuration += other.HmacrDuration + s.TaddrDuration += other.TaddrDuration s.ValidStampDuration += other.ValidStampDuration s.BatchesBelowValueDuration += other.BatchesBelowValueDuration s.RogueChunk += other.RogueChunk diff --git a/pkg/swarm/hasher.go b/pkg/swarm/hasher.go index 485b61ab398..b9823bb50a1 100644 --- a/pkg/swarm/hasher.go +++ b/pkg/swarm/hasher.go @@ -15,15 +15,15 @@ func NewHasher() hash.Hash { return sha3.NewLegacyKeccak256() } -type trHasher struct { +type PrefixHasher struct { hash.Hash prefix []byte } -// NewTrHasher returns new hasher which is Keccak-256 hasher +// NewPrefixHasher returns new hasher which is Keccak-256 hasher // with prefix value added as initial data. -func NewTrHasher(prefix []byte) hash.Hash { - h := &trHasher{ +func NewPrefixHasher(prefix []byte) hash.Hash { + h := &PrefixHasher{ Hash: NewHasher(), prefix: prefix, } @@ -32,7 +32,7 @@ func NewTrHasher(prefix []byte) hash.Hash { return h } -func (h *trHasher) Reset() { +func (h *PrefixHasher) Reset() { h.Hash.Reset() _, _ = h.Write(h.prefix) } diff --git a/pkg/swarm/hasher_test.go b/pkg/swarm/hasher_test.go index bfee0a78e98..3811e09605a 100644 --- a/pkg/swarm/hasher_test.go +++ b/pkg/swarm/hasher_test.go @@ -66,7 +66,7 @@ func TestNewTrHasher(t *testing.T) { // Run tests cases against TrHasher for _, tc := range tests { - h := swarm.NewTrHasher(tc.prefix) + h := swarm.NewPrefixHasher(tc.prefix) _, err := h.Write(tc.plaintext) if err != nil { From 4faae92298d73d3c95f938d0e75614ea9f1311c3 Mon Sep 17 00:00:00 2001 From: mrekucci Date: Fri, 29 Sep 2023 17:58:44 +0400 Subject: [PATCH 02/17] fix: include the reason when the redistribution contract call fails (#4358) --- pkg/postage/postagecontract/contract.go | 15 ++++++--------- pkg/storageincentives/agent.go | 2 +- .../redistribution/redistribution.go | 6 ++++-- pkg/transaction/mock/transaction.go | 4 ++++ pkg/transaction/transaction.go | 18 ++++++++++++++++++ 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/pkg/postage/postagecontract/contract.go b/pkg/postage/postagecontract/contract.go index 94c3dc99061..666037c39d6 100644 --- a/pkg/postage/postagecontract/contract.go +++ b/pkg/postage/postagecontract/contract.go @@ -178,7 +178,7 @@ func (c *postageContract) sendApproveTransaction(ctx context.Context, amount *bi return receipt, nil } -func (c *postageContract) sendTransaction(ctx context.Context, callData []byte, desc string) (*types.Receipt, error) { +func (c *postageContract) sendTransaction(ctx context.Context, callData []byte, desc string) (receipt *types.Receipt, err error) { request := &transaction.TxRequest{ To: &c.postageStampContractAddress, Data: callData, @@ -187,25 +187,22 @@ func (c *postageContract) sendTransaction(ctx context.Context, callData []byte, Value: big.NewInt(0), Description: desc, } + defer func() { + err = c.transactionService.UnwrapRevertReason(ctx, request, err) + }() txHash, err := c.transactionService.Send(ctx, request, transaction.DefaultTipBoostPercent) if err != nil { return nil, err } - receipt, err := c.transactionService.WaitForReceipt(ctx, txHash) + receipt, err = c.transactionService.WaitForReceipt(ctx, txHash) if err != nil { return nil, err } if receipt.Status == 0 { - err := transaction.ErrTransactionReverted - if res, cErr := c.transactionService.Call(ctx, request); cErr == nil { - if reason, uErr := abi.UnpackRevert(res); uErr == nil { - err = fmt.Errorf("%w: reason: %s", err, reason) - } - } - return nil, err + return nil, transaction.ErrTransactionReverted } return receipt, nil diff --git a/pkg/storageincentives/agent.go b/pkg/storageincentives/agent.go index b856625025e..16e7770c859 100644 --- a/pkg/storageincentives/agent.go +++ b/pkg/storageincentives/agent.go @@ -24,7 +24,7 @@ import ( "github.com/ethersphere/bee/pkg/storage" "github.com/ethersphere/bee/pkg/storageincentives/redistribution" "github.com/ethersphere/bee/pkg/storageincentives/staking" - storer "github.com/ethersphere/bee/pkg/storer" + "github.com/ethersphere/bee/pkg/storer" "github.com/ethersphere/bee/pkg/swarm" "github.com/ethersphere/bee/pkg/transaction" ) diff --git a/pkg/storageincentives/redistribution/redistribution.go b/pkg/storageincentives/redistribution/redistribution.go index 2c163f5a76c..ce0692da943 100644 --- a/pkg/storageincentives/redistribution/redistribution.go +++ b/pkg/storageincentives/redistribution/redistribution.go @@ -180,8 +180,10 @@ func (c *contract) ReserveSalt(ctx context.Context) ([]byte, error) { return salt[:], nil } -func (c *contract) sendAndWait(ctx context.Context, request *transaction.TxRequest, boostPercent int) (common.Hash, error) { - txHash, err := c.txService.Send(ctx, request, boostPercent) +func (c *contract) sendAndWait(ctx context.Context, request *transaction.TxRequest, boostPercent int) (txHash common.Hash, err error) { + defer func() { err = c.txService.UnwrapRevertReason(ctx, request, err) }() + + txHash, err = c.txService.Send(ctx, request, boostPercent) if err != nil { return txHash, err } diff --git a/pkg/transaction/mock/transaction.go b/pkg/transaction/mock/transaction.go index f9a557f00be..6110dfeea1d 100644 --- a/pkg/transaction/mock/transaction.go +++ b/pkg/transaction/mock/transaction.go @@ -97,6 +97,10 @@ func (m *transactionServiceMock) TransactionFee(ctx context.Context, txHash comm return big.NewInt(0), nil } +func (m *transactionServiceMock) UnwrapRevertReason(_ context.Context, _ *transaction.TxRequest, err error) error { + return err +} + // Option is the option passed to the mock Chequebook service type Option interface { apply(*transactionServiceMock) diff --git a/pkg/transaction/transaction.go b/pkg/transaction/transaction.go index f000965992d..906ca295ebe 100644 --- a/pkg/transaction/transaction.go +++ b/pkg/transaction/transaction.go @@ -14,6 +14,7 @@ import ( "time" "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethersphere/bee/pkg/crypto" @@ -94,6 +95,9 @@ type Service interface { CancelTransaction(ctx context.Context, originalTxHash common.Hash) (common.Hash, error) // TransactionFee retrieves the transaction fee TransactionFee(ctx context.Context, txHash common.Hash) (*big.Int, error) + // UnwrapRevertReason tries to unwrap the revert reason if the given error is not nil. + // The original error is wrapped in case the revert reason exists. + UnwrapRevertReason(ctx context.Context, req *TxRequest, err error) error } type transactionService struct { @@ -584,3 +588,17 @@ func (t *transactionService) TransactionFee(ctx context.Context, txHash common.H } return trx.Cost(), nil } + +func (t *transactionService) UnwrapRevertReason(ctx context.Context, req *TxRequest, err error) error { + if err == nil { + return nil + } + + if res, cErr := t.Call(ctx, req); cErr == nil { + if reason, uErr := abi.UnpackRevert(res); uErr == nil { + err = fmt.Errorf("%w: reason: %s", err, reason) + } + } + + return err +} From 9553605c285bf048f9ba9d5d28f7a888d79eb2cc Mon Sep 17 00:00:00 2001 From: istae <14264581+istae@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:01:02 +0300 Subject: [PATCH 03/17] fix(transaction): swallow estimate gas error (#4366) --- pkg/storageincentives/agent.go | 10 +-- pkg/transaction/transaction.go | 3 +- pkg/transaction/transaction_test.go | 121 ++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 6 deletions(-) diff --git a/pkg/storageincentives/agent.go b/pkg/storageincentives/agent.go index 16e7770c859..51a62393a8c 100644 --- a/pkg/storageincentives/agent.go +++ b/pkg/storageincentives/agent.go @@ -131,7 +131,7 @@ func (a *Agent) start(blockTime time.Duration, blocksPerRound, blocksPerPhase ui phaseEvents := newEvents() defer phaseEvents.Close() - logPhaseResult := func(phase PhaseType, round uint64, err error) { + logErr := func(phase PhaseType, round uint64, err error) { if err != nil { a.logger.Error(err, "phase failed", "phase", phase, "round", round) } @@ -142,13 +142,13 @@ func (a *Agent) start(blockTime time.Duration, blocksPerRound, blocksPerPhase ui round, _ := a.state.currentRoundAndPhase() err := a.handleCommit(ctx, round) - logPhaseResult(commit, round, err) + logErr(commit, round, err) }) phaseEvents.On(reveal, func(ctx context.Context) { phaseEvents.Cancel(commit, sample) round, _ := a.state.currentRoundAndPhase() - logPhaseResult(reveal, round, a.handleReveal(ctx, round)) + logErr(reveal, round, a.handleReveal(ctx, round)) }) phaseEvents.On(claim, func(ctx context.Context) { @@ -156,13 +156,13 @@ func (a *Agent) start(blockTime time.Duration, blocksPerRound, blocksPerPhase ui phaseEvents.Publish(sample) round, _ := a.state.currentRoundAndPhase() - logPhaseResult(claim, round, a.handleClaim(ctx, round)) + logErr(claim, round, a.handleClaim(ctx, round)) }) phaseEvents.On(sample, func(ctx context.Context) { round, _ := a.state.currentRoundAndPhase() isPhasePlayed, err := a.handleSample(ctx, round) - logPhaseResult(sample, round, err) + logErr(sample, round, err) // Sample handled could potentially take long time, therefore it could overlap with commit // phase of next round. When that case happens commit event needs to be triggered once more diff --git a/pkg/transaction/transaction.go b/pkg/transaction/transaction.go index 906ca295ebe..c9e39c73b13 100644 --- a/pkg/transaction/transaction.go +++ b/pkg/transaction/transaction.go @@ -282,7 +282,8 @@ func (t *transactionService) prepareTransaction(ctx context.Context, request *Tx Data: request.Data, }) if err != nil { - return nil, err + t.logger.Debug("estimage gas failed", "error", err) + gasLimit = request.MinEstimatedGasLimit } gasLimit += gasLimit / 4 // add 25% on top diff --git a/pkg/transaction/transaction_test.go b/pkg/transaction/transaction_test.go index fc2e03da7ca..40672188f19 100644 --- a/pkg/transaction/transaction_test.go +++ b/pkg/transaction/transaction_test.go @@ -7,6 +7,7 @@ package transaction_test import ( "bytes" "context" + "errors" "fmt" "math/big" "testing" @@ -213,6 +214,126 @@ func TestTransactionSend(t *testing.T) { } }) + t.Run("send with estimate error", func(t *testing.T) { + t.Parallel() + + signedTx := types.NewTx(&types.DynamicFeeTx{ + ChainID: chainID, + Nonce: nonce, + To: &recipient, + Value: value, + Gas: estimatedGasLimit, + GasFeeCap: defaultGasFee, + GasTipCap: suggestedGasTip, + Data: txData, + }) + request := &transaction.TxRequest{ + To: &recipient, + Data: txData, + Value: value, + MinEstimatedGasLimit: estimatedGasLimit, + } + store := storemock.NewStateStore() + err := store.Put(nonceKey(sender), nonce) + if err != nil { + t.Fatal(err) + } + + transactionService, err := transaction.NewService(logger, + backendmock.New( + backendmock.WithSendTransactionFunc(func(ctx context.Context, tx *types.Transaction) error { + if tx != signedTx { + t.Fatal("not sending signed transaction") + } + return nil + }), + backendmock.WithEstimateGasFunc(func(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) { + return 0, errors.New("estimate failure") + }), + backendmock.WithSuggestGasPriceFunc(func(ctx context.Context) (*big.Int, error) { + return suggestedGasPrice, nil + }), + backendmock.WithPendingNonceAtFunc(func(ctx context.Context, account common.Address) (uint64, error) { + return nonce - 1, nil + }), + backendmock.WithSuggestGasTipCapFunc(func(ctx context.Context) (*big.Int, error) { + return suggestedGasTip, nil + }), + ), + signerMockForTransaction(t, signedTx, sender, chainID), + store, + chainID, + monitormock.New( + monitormock.WithWatchTransactionFunc(func(txHash common.Hash, nonce uint64) (<-chan types.Receipt, <-chan error, error) { + return nil, nil, nil + }), + ), + ) + if err != nil { + t.Fatal(err) + } + testutil.CleanupCloser(t, transactionService) + + txHash, err := transactionService.Send(context.Background(), request, 0) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(txHash.Bytes(), signedTx.Hash().Bytes()) { + t.Fatal("returning wrong transaction hash") + } + + var storedNonce uint64 + err = store.Get(nonceKey(sender), &storedNonce) + if err != nil { + t.Fatal(err) + } + if storedNonce != nonce+1 { + t.Fatalf("nonce not stored correctly: want %d, got %d", nonce+1, storedNonce) + } + + storedTransaction, err := transactionService.StoredTransaction(txHash) + if err != nil { + t.Fatal(err) + } + + if storedTransaction.To == nil || *storedTransaction.To != recipient { + t.Fatalf("got wrong recipient in stored transaction. wanted %x, got %x", recipient, storedTransaction.To) + } + + if !bytes.Equal(storedTransaction.Data, request.Data) { + t.Fatalf("got wrong data in stored transaction. wanted %x, got %x", request.Data, storedTransaction.Data) + } + + if storedTransaction.Description != request.Description { + t.Fatalf("got wrong description in stored transaction. wanted %x, got %x", request.Description, storedTransaction.Description) + } + + if storedTransaction.GasLimit != estimatedGasLimit { + t.Fatalf("got wrong gas limit in stored transaction. wanted %d, got %d", estimatedGasLimit, storedTransaction.GasLimit) + } + + if defaultGasFee.Cmp(storedTransaction.GasPrice) != 0 { + t.Fatalf("got wrong gas price in stored transaction. wanted %d, got %d", defaultGasFee, storedTransaction.GasPrice) + } + + if storedTransaction.Nonce != nonce { + t.Fatalf("got wrong nonce in stored transaction. wanted %d, got %d", nonce, storedTransaction.Nonce) + } + + pending, err := transactionService.PendingTransactions() + if err != nil { + t.Fatal(err) + } + if len(pending) != 1 { + t.Fatalf("expected one pending transaction, got %d", len(pending)) + } + + if pending[0] != txHash { + t.Fatalf("got wrong pending transaction. wanted %x, got %x", txHash, pending[0]) + } + }) + t.Run("sendWithBoost", func(t *testing.T) { t.Parallel() From 5b80ad1fea0e09c116d78a4e0a7c624b74692ca9 Mon Sep 17 00:00:00 2001 From: istae <14264581+istae@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:56:32 +0300 Subject: [PATCH 04/17] fix(postage): added postage stamps json marshaling (#4369) --- pkg/postage/stamp.go | 30 ++++++++++++++++++++++++++++++ pkg/postage/stamp_test.go | 19 +++++++++++++++++++ pkg/storer/sample.go | 4 ++-- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/pkg/postage/stamp.go b/pkg/postage/stamp.go index 7d65f4eee5b..4fff65797ad 100644 --- a/pkg/postage/stamp.go +++ b/pkg/postage/stamp.go @@ -6,6 +6,7 @@ package postage import ( "bytes" + "encoding/json" "errors" "fmt" @@ -118,6 +119,35 @@ func (s *Stamp) UnmarshalBinary(buf []byte) error { return nil } +type stampJson struct { + BatchID []byte `json:"batchID"` + Index []byte `json:"index"` + Timestamp []byte `json:"timestamp"` + Sig []byte `json:"sig"` +} + +func (s *Stamp) MarshalJSON() ([]byte, error) { + return json.Marshal(&stampJson{ + s.batchID, + s.index, + s.timestamp, + s.sig, + }) +} + +func (a *Stamp) UnmarshalJSON(b []byte) error { + v := &stampJson{} + err := json.Unmarshal(b, v) + if err != nil { + return err + } + a.batchID = v.BatchID + a.index = v.Index + a.timestamp = v.Timestamp + a.sig = v.Sig + return nil +} + // ToSignDigest creates a digest to represent the stamp which is to be signed by the owner. func ToSignDigest(addr, batchId, index, timestamp []byte) ([]byte, error) { h := swarm.NewHasher() diff --git a/pkg/postage/stamp_test.go b/pkg/postage/stamp_test.go index f791d8e7fe6..ab98a197349 100644 --- a/pkg/postage/stamp_test.go +++ b/pkg/postage/stamp_test.go @@ -6,6 +6,7 @@ package postage_test import ( "bytes" + "encoding/json" "math/big" "testing" @@ -31,6 +32,24 @@ func TestStampMarshalling(t *testing.T) { compareStamps(t, sExp, s) } +// TestStampMarshalling tests the idempotence of binary marshal/unmarshals for Stamps. +func TestStampJsonMarshalling(t *testing.T) { + sExp := postagetesting.MustNewStamp() + + b, err := json.Marshal(sExp) + if err != nil { + t.Fatal(err) + } + + s := postage.NewStamp(nil, nil, nil, nil) + err = json.Unmarshal(b, s) + if err != nil { + t.Fatal(err) + } + + compareStamps(t, sExp, s) +} + func compareStamps(t *testing.T, s1, s2 *postage.Stamp) { t.Helper() diff --git a/pkg/storer/sample.go b/pkg/storer/sample.go index 7a37fb62e99..196bb4d26a9 100644 --- a/pkg/storer/sample.go +++ b/pkg/storer/sample.go @@ -32,7 +32,7 @@ type SampleItem struct { TransformedAddress swarm.Address ChunkAddress swarm.Address ChunkData []byte - Stamp swarm.Stamp + Stamp *postage.Stamp } type Sample struct { @@ -288,7 +288,7 @@ func (db *DB) ReserveSample( stats.ValidStampDuration += time.Since(start) - item.Stamp = stamp + item.Stamp = postage.NewStamp(stamp.BatchID(), stamp.Index(), stamp.Timestamp(), stamp.Sig()) // ensuring to pass the check order function of redistribution contract if index := contains(item.TransformedAddress); index != -1 { From 600830e439d65236738c13999d7a50654bcf2743 Mon Sep 17 00:00:00 2001 From: istae <14264581+istae@users.noreply.github.com> Date: Tue, 3 Oct 2023 12:11:19 +0300 Subject: [PATCH 05/17] chore: update si (#4370) --- go.mod | 2 +- go.sum | 4 ++-- pkg/pusher/pusher.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index fe4a9bb110d..aefd3bbb69a 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/ethereum/go-ethereum v1.13.4 github.com/ethersphere/go-price-oracle-abi v0.1.0 - github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc3 + github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc5 github.com/ethersphere/go-sw3-abi v0.4.0 github.com/ethersphere/langos v1.0.0 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index 1ce0ac6ae81..1aec66e35ed 100644 --- a/go.sum +++ b/go.sum @@ -242,8 +242,8 @@ github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8 github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= github.com/ethersphere/go-price-oracle-abi v0.1.0 h1:yg/hK8nETNvk+GEBASlbakMFv/CVp7HXiycrHw1pRV8= github.com/ethersphere/go-price-oracle-abi v0.1.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc3 h1:tXux2FnhuU6DbrY+Z4nVQMGp63JkJPq7pKb5Xi2Sjxo= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc3/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc5 h1:JaiGMFoycByFkaCv6KFg79tfRlBJ5zY01dLWPnf48v0= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc5/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g= github.com/ethersphere/go-sw3-abi v0.4.0/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU= github.com/ethersphere/langos v1.0.0 h1:NBtNKzXTTRSue95uOlzPN4py7Aofs0xWPzyj4AI1Vcc= diff --git a/pkg/pusher/pusher.go b/pkg/pusher/pusher.go index c4b85620add..5e38137979f 100644 --- a/pkg/pusher/pusher.go +++ b/pkg/pusher/pusher.go @@ -296,7 +296,7 @@ func (s *Service) pushDeferred(ctx context.Context, logger log.Logger, op *Op) ( } case err == nil: if err := s.checkReceipt(receipt, loggerV1); err != nil { - loggerV1.Error(err, "pusher: failed checking receipt") + loggerV1.Error(err, "pusher: failed checking receipt", "chunk_address", op.Chunk.Address()) return true, err } if err := s.storer.Report(ctx, op.Chunk, storage.ChunkSynced); err != nil { @@ -350,7 +350,7 @@ func (s *Service) pushDirect(ctx context.Context, logger log.Logger, op *Op) err case err == nil: err = s.checkReceipt(receipt, loggerV1) if err != nil { - loggerV1.Error(err, "pusher: failed checking receipt") + loggerV1.Error(err, "pusher: failed checking receipt", "chunk_address", op.Chunk.Address()) } default: loggerV1.Error(err, "pusher: failed PushChunkToClosest") From f1a68293c7f8bf6fa6e815315fc818c39da33fc2 Mon Sep 17 00:00:00 2001 From: mrekucci Date: Tue, 3 Oct 2023 13:13:26 +0400 Subject: [PATCH 06/17] feat: unwrap smart contract custom abi errors (#4367) --- pkg/node/node.go | 20 +++--------- pkg/postage/postagecontract/contract.go | 27 +++++++++++++--- .../redistribution/redistribution.go | 9 +++++- pkg/storageincentives/staking/contract.go | 32 +++++++++++++++---- pkg/transaction/mock/transaction.go | 2 +- pkg/transaction/transaction.go | 25 +++++++++++---- 6 files changed, 79 insertions(+), 36 deletions(-) diff --git a/pkg/node/node.go b/pkg/node/node.go index ad96a94ac88..01e6d8be9eb 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -19,12 +19,10 @@ import ( "net/http" "path/filepath" "runtime" - "strings" "sync" "sync/atomic" "time" - "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethersphere/bee/pkg/accounting" "github.com/ethersphere/bee/pkg/addressbook" @@ -71,6 +69,7 @@ import ( "github.com/ethersphere/bee/pkg/topology/lightnode" "github.com/ethersphere/bee/pkg/tracing" "github.com/ethersphere/bee/pkg/transaction" + "github.com/ethersphere/bee/pkg/util/abiutil" "github.com/ethersphere/bee/pkg/util/ioutil" "github.com/ethersphere/bee/pkg/util/nbhdutil" "github.com/ethersphere/bee/pkg/util/syncutil" @@ -683,10 +682,7 @@ func NewBee( return nil, errors.New("no known postage stamp addresses for this network") } - postageStampContractABI, err := abi.JSON(strings.NewReader(chainCfg.PostageStampABI)) - if err != nil { - return nil, fmt.Errorf("unable to parse postage stamp ABI: %w", err) - } + postageStampContractABI := abiutil.MustParseABI(chainCfg.PostageStampABI) bzzTokenAddress, err := postagecontract.LookupERC20Address(ctx, transactionService, postageStampContractAddress, postageStampContractABI, chainEnabled) if err != nil { @@ -1012,11 +1008,7 @@ func NewBee( stakingContractAddress = common.HexToAddress(o.StakingContractAddress) } - stakingContractABI, err := abi.JSON(strings.NewReader(chainCfg.StakingABI)) - if err != nil { - return nil, fmt.Errorf("unable to parse staking ABI: %w", err) - } - stakingContract := staking.New(swarmAddress, overlayEthAddress, stakingContractAddress, stakingContractABI, bzzTokenAddress, transactionService, common.BytesToHash(nonce)) + stakingContract := staking.New(swarmAddress, overlayEthAddress, stakingContractAddress, abiutil.MustParseABI(chainCfg.StakingABI), bzzTokenAddress, transactionService, common.BytesToHash(nonce)) var ( pullerService *puller.Puller @@ -1039,16 +1031,12 @@ func NewBee( } redistributionContractAddress = common.HexToAddress(o.RedistributionContractAddress) } - redistributionContractABI, err := abi.JSON(strings.NewReader(chainCfg.RedistributionABI)) - if err != nil { - return nil, fmt.Errorf("unable to parse redistribution ABI: %w", err) - } isFullySynced := func() bool { return localStore.ReserveSize() >= reserveTreshold && pullerService.SyncRate() == 0 } - redistributionContract := redistribution.New(swarmAddress, logger, transactionService, redistributionContractAddress, redistributionContractABI) + redistributionContract := redistribution.New(swarmAddress, logger, transactionService, redistributionContractAddress, abiutil.MustParseABI(chainCfg.RedistributionABI)) agent, err = storageincentives.New( swarmAddress, overlayEthAddress, diff --git a/pkg/postage/postagecontract/contract.go b/pkg/postage/postagecontract/contract.go index 666037c39d6..234cc4afdd8 100644 --- a/pkg/postage/postagecontract/contract.go +++ b/pkg/postage/postagecontract/contract.go @@ -148,25 +148,36 @@ func (c *postageContract) expireLimitedBatches(ctx context.Context, count *big.I return nil } -func (c *postageContract) sendApproveTransaction(ctx context.Context, amount *big.Int) (*types.Receipt, error) { +func (c *postageContract) sendApproveTransaction(ctx context.Context, amount *big.Int) (receipt *types.Receipt, err error) { callData, err := erc20ABI.Pack("approve", c.postageStampContractAddress, amount) if err != nil { return nil, err } - txHash, err := c.transactionService.Send(ctx, &transaction.TxRequest{ + request := &transaction.TxRequest{ To: &c.bzzTokenAddress, Data: callData, GasPrice: sctx.GetGasPrice(ctx), GasLimit: 65000, Value: big.NewInt(0), Description: approveDescription, - }, transaction.DefaultTipBoostPercent) + } + + defer func() { + err = c.transactionService.UnwrapABIError( + ctx, + request, + err, + c.postageStampContractABI.Errors, + ) + }() + + txHash, err := c.transactionService.Send(ctx, request, transaction.DefaultTipBoostPercent) if err != nil { return nil, err } - receipt, err := c.transactionService.WaitForReceipt(ctx, txHash) + receipt, err = c.transactionService.WaitForReceipt(ctx, txHash) if err != nil { return nil, err } @@ -187,8 +198,14 @@ func (c *postageContract) sendTransaction(ctx context.Context, callData []byte, Value: big.NewInt(0), Description: desc, } + defer func() { - err = c.transactionService.UnwrapRevertReason(ctx, request, err) + err = c.transactionService.UnwrapABIError( + ctx, + request, + err, + c.postageStampContractABI.Errors, + ) }() txHash, err := c.transactionService.Send(ctx, request, transaction.DefaultTipBoostPercent) diff --git a/pkg/storageincentives/redistribution/redistribution.go b/pkg/storageincentives/redistribution/redistribution.go index ce0692da943..8b83537cf90 100644 --- a/pkg/storageincentives/redistribution/redistribution.go +++ b/pkg/storageincentives/redistribution/redistribution.go @@ -181,7 +181,14 @@ func (c *contract) ReserveSalt(ctx context.Context) ([]byte, error) { } func (c *contract) sendAndWait(ctx context.Context, request *transaction.TxRequest, boostPercent int) (txHash common.Hash, err error) { - defer func() { err = c.txService.UnwrapRevertReason(ctx, request, err) }() + defer func() { + err = c.txService.UnwrapABIError( + ctx, + request, + err, + c.incentivesContractABI.Errors, + ) + }() txHash, err = c.txService.Send(ctx, request, boostPercent) if err != nil { diff --git a/pkg/storageincentives/staking/contract.go b/pkg/storageincentives/staking/contract.go index aa05267c197..014b1a67adc 100644 --- a/pkg/storageincentives/staking/contract.go +++ b/pkg/storageincentives/staking/contract.go @@ -77,25 +77,36 @@ func New( } } -func (c *contract) sendApproveTransaction(ctx context.Context, amount *big.Int) (*types.Receipt, error) { +func (c *contract) sendApproveTransaction(ctx context.Context, amount *big.Int) (receipt *types.Receipt, err error) { callData, err := erc20ABI.Pack("approve", c.stakingContractAddress, amount) if err != nil { return nil, err } - txHash, err := c.transactionService.Send(ctx, &transaction.TxRequest{ + request := &transaction.TxRequest{ To: &c.bzzTokenAddress, Data: callData, GasPrice: sctx.GetGasPrice(ctx), GasLimit: 65000, Value: big.NewInt(0), Description: approveDescription, - }, 0) + } + + defer func() { + err = c.transactionService.UnwrapABIError( + ctx, + request, + err, + c.stakingContractABI.Errors, + ) + }() + + txHash, err := c.transactionService.Send(ctx, request, 0) if err != nil { return nil, err } - receipt, err := c.transactionService.WaitForReceipt(ctx, txHash) + receipt, err = c.transactionService.WaitForReceipt(ctx, txHash) if err != nil { return nil, err } @@ -107,7 +118,7 @@ func (c *contract) sendApproveTransaction(ctx context.Context, amount *big.Int) return receipt, nil } -func (c *contract) sendTransaction(ctx context.Context, callData []byte, desc string) (*types.Receipt, error) { +func (c *contract) sendTransaction(ctx context.Context, callData []byte, desc string) (receipt *types.Receipt, err error) { request := &transaction.TxRequest{ To: &c.stakingContractAddress, Data: callData, @@ -117,12 +128,21 @@ func (c *contract) sendTransaction(ctx context.Context, callData []byte, desc st Description: desc, } + defer func() { + err = c.transactionService.UnwrapABIError( + ctx, + request, + err, + c.stakingContractABI.Errors, + ) + }() + txHash, err := c.transactionService.Send(ctx, request, transaction.DefaultTipBoostPercent) if err != nil { return nil, err } - receipt, err := c.transactionService.WaitForReceipt(ctx, txHash) + receipt, err = c.transactionService.WaitForReceipt(ctx, txHash) if err != nil { return nil, err } diff --git a/pkg/transaction/mock/transaction.go b/pkg/transaction/mock/transaction.go index 6110dfeea1d..7478daa63e6 100644 --- a/pkg/transaction/mock/transaction.go +++ b/pkg/transaction/mock/transaction.go @@ -97,7 +97,7 @@ func (m *transactionServiceMock) TransactionFee(ctx context.Context, txHash comm return big.NewInt(0), nil } -func (m *transactionServiceMock) UnwrapRevertReason(_ context.Context, _ *transaction.TxRequest, err error) error { +func (m *transactionServiceMock) UnwrapABIError(_ context.Context, _ *transaction.TxRequest, err error, _ map[string]abi.Error) error { return err } diff --git a/pkg/transaction/transaction.go b/pkg/transaction/transaction.go index c9e39c73b13..f1bdb1f2950 100644 --- a/pkg/transaction/transaction.go +++ b/pkg/transaction/transaction.go @@ -5,6 +5,7 @@ package transaction import ( + "bytes" "errors" "fmt" "io" @@ -95,9 +96,9 @@ type Service interface { CancelTransaction(ctx context.Context, originalTxHash common.Hash) (common.Hash, error) // TransactionFee retrieves the transaction fee TransactionFee(ctx context.Context, txHash common.Hash) (*big.Int, error) - // UnwrapRevertReason tries to unwrap the revert reason if the given error is not nil. - // The original error is wrapped in case the revert reason exists. - UnwrapRevertReason(ctx context.Context, req *TxRequest, err error) error + // UnwrapABIError tries to unwrap the ABI error if the given error is not nil. + // The original error is wrapped together with the ABI error if it exists. + UnwrapABIError(ctx context.Context, req *TxRequest, err error, abiErrors map[string]abi.Error) error } type transactionService struct { @@ -590,14 +591,24 @@ func (t *transactionService) TransactionFee(ctx context.Context, txHash common.H return trx.Cost(), nil } -func (t *transactionService) UnwrapRevertReason(ctx context.Context, req *TxRequest, err error) error { +func (t *transactionService) UnwrapABIError(ctx context.Context, req *TxRequest, err error, abiErrors map[string]abi.Error) error { if err == nil { return nil } - if res, cErr := t.Call(ctx, req); cErr == nil { - if reason, uErr := abi.UnpackRevert(res); uErr == nil { - err = fmt.Errorf("%w: reason: %s", err, reason) + res, cErr := t.Call(ctx, req) + if cErr != nil { + return err + } + + if reason, uErr := abi.UnpackRevert(res); uErr == nil { + return fmt.Errorf("%w: %s", err, reason) + } + + for _, abiError := range abiErrors { + if bytes.Equal(res[:4], abiError.ID[:4]) { + //abiError.Unpack(res[4:]) + return fmt.Errorf("%w: %s", err, abiError) } } From 230973c86dc1902060e27d3ec1e82ae9fb35c433 Mon Sep 17 00:00:00 2001 From: istae <14264581+istae@users.noreply.github.com> Date: Thu, 5 Oct 2023 13:35:12 +0300 Subject: [PATCH 07/17] fix: updated contract abi version and docker setup versions --- .github/workflows/beekeeper.yml | 2 +- go.mod | 2 +- go.sum | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/beekeeper.yml b/.github/workflows/beekeeper.yml index dd7bd660523..b0386766f73 100644 --- a/.github/workflows/beekeeper.yml +++ b/.github/workflows/beekeeper.yml @@ -11,7 +11,7 @@ env: K3S_VERSION: "v1.22.17+k3s1" REPLICA: 3 RUN_TYPE: "PR RUN" - SETUP_CONTRACT_IMAGE_TAG: "0.9.9" + SETUP_CONTRACT_IMAGE_TAG: "1.0.3" BEELOCAL_BRANCH: "main" BEEKEEPER_BRANCH: "master" BEEKEEPER_METRICS_ENABLED: false diff --git a/go.mod b/go.mod index aefd3bbb69a..edc13d9caf4 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/ethereum/go-ethereum v1.13.4 github.com/ethersphere/go-price-oracle-abi v0.1.0 - github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc5 + github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc7 github.com/ethersphere/go-sw3-abi v0.4.0 github.com/ethersphere/langos v1.0.0 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index 1aec66e35ed..0cb579bbb41 100644 --- a/go.sum +++ b/go.sum @@ -244,6 +244,8 @@ github.com/ethersphere/go-price-oracle-abi v0.1.0 h1:yg/hK8nETNvk+GEBASlbakMFv/C github.com/ethersphere/go-price-oracle-abi v0.1.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc5 h1:JaiGMFoycByFkaCv6KFg79tfRlBJ5zY01dLWPnf48v0= github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc5/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc7 h1:SPg0WY5rCWt7Y72hAj0o1juLtNi9lqx7b/6fNaNa4H0= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc7/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g= github.com/ethersphere/go-sw3-abi v0.4.0/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU= github.com/ethersphere/langos v1.0.0 h1:NBtNKzXTTRSue95uOlzPN4py7Aofs0xWPzyj4AI1Vcc= From c31866ff91ad213ac10e61a4df9fa638a2dc966f Mon Sep 17 00:00:00 2001 From: mrekucci Date: Thu, 5 Oct 2023 18:37:29 +0400 Subject: [PATCH 08/17] feat: include abi custom error params in the returned error (#4376) --- go.sum | 2 - pkg/transaction/backendmock/backend.go | 12 +++- pkg/transaction/transaction.go | 48 ++++++++++++-- pkg/transaction/transaction_test.go | 92 +++++++++++++++++++++++++- 4 files changed, 144 insertions(+), 10 deletions(-) diff --git a/go.sum b/go.sum index 0cb579bbb41..6dcd24d01e9 100644 --- a/go.sum +++ b/go.sum @@ -242,8 +242,6 @@ github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8 github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= github.com/ethersphere/go-price-oracle-abi v0.1.0 h1:yg/hK8nETNvk+GEBASlbakMFv/CVp7HXiycrHw1pRV8= github.com/ethersphere/go-price-oracle-abi v0.1.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc5 h1:JaiGMFoycByFkaCv6KFg79tfRlBJ5zY01dLWPnf48v0= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc5/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc7 h1:SPg0WY5rCWt7Y72hAj0o1juLtNi9lqx7b/6fNaNa4H0= github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc7/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g= diff --git a/pkg/transaction/backendmock/backend.go b/pkg/transaction/backendmock/backend.go index 8ce38614b6e..7f787fe3d60 100644 --- a/pkg/transaction/backendmock/backend.go +++ b/pkg/transaction/backendmock/backend.go @@ -17,6 +17,7 @@ import ( type backendMock struct { codeAt func(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) + callContract func(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) sendTransaction func(ctx context.Context, tx *types.Transaction) error suggestGasPrice func(ctx context.Context) (*big.Int, error) suggestGasTipCap func(ctx context.Context) (*big.Int, error) @@ -38,7 +39,10 @@ func (m *backendMock) CodeAt(ctx context.Context, contract common.Address, block return nil, errors.New("not implemented") } -func (*backendMock) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { +func (m *backendMock) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + if m.callContract != nil { + return m.callContract(ctx, call, blockNumber) + } return nil, errors.New("not implemented") } @@ -161,6 +165,12 @@ type optionFunc func(*backendMock) func (f optionFunc) apply(r *backendMock) { f(r) } +func WithCallContractFunc(f func(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)) Option { + return optionFunc(func(s *backendMock) { + s.callContract = f + }) +} + func WithCodeAtFunc(f func(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)) Option { return optionFunc(func(s *backendMock) { s.codeAt = f diff --git a/pkg/transaction/transaction.go b/pkg/transaction/transaction.go index f1bdb1f2950..5e0cdfa4162 100644 --- a/pkg/transaction/transaction.go +++ b/pkg/transaction/transaction.go @@ -18,6 +18,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethersphere/bee/pkg/crypto" "github.com/ethersphere/bee/pkg/log" "github.com/ethersphere/bee/pkg/sctx" @@ -596,20 +597,55 @@ func (t *transactionService) UnwrapABIError(ctx context.Context, req *TxRequest, return nil } - res, cErr := t.Call(ctx, req) - if cErr != nil { + _, cErr := t.Call(ctx, req) + if cErr == nil { return err } + err = fmt.Errorf("%w: %s", err, cErr) - if reason, uErr := abi.UnpackRevert(res); uErr == nil { + var derr rpc.DataError + if !errors.As(cErr, &derr) { + return err + } + + res, ok := derr.ErrorData().(string) + if !ok { + return err + } + buf := common.FromHex(res) + + if reason, uErr := abi.UnpackRevert(buf); uErr == nil { return fmt.Errorf("%w: %s", err, reason) } for _, abiError := range abiErrors { - if bytes.Equal(res[:4], abiError.ID[:4]) { - //abiError.Unpack(res[4:]) - return fmt.Errorf("%w: %s", err, abiError) + if !bytes.Equal(buf[:4], abiError.ID[:4]) { + continue + } + + data, uErr := abiError.Unpack(buf) + if uErr != nil { + continue } + + values, ok := data.([]interface{}) + if !ok { + values = make([]interface{}, len(abiError.Inputs)) + for i := range values { + values[i] = "?" + } + } + + params := make([]string, len(abiError.Inputs)) + for i, input := range abiError.Inputs { + if input.Name == "" { + input.Name = fmt.Sprintf("arg%d", i) + } + params[i] = fmt.Sprintf("%s=%v", input.Name, values[i]) + + } + + return fmt.Errorf("%w: %s(%s)", err, abiError.Name, strings.Join(params, ",")) } return err diff --git a/pkg/transaction/transaction_test.go b/pkg/transaction/transaction_test.go index 40672188f19..62250039c56 100644 --- a/pkg/transaction/transaction_test.go +++ b/pkg/transaction/transaction_test.go @@ -10,11 +10,14 @@ import ( "errors" "fmt" "math/big" + "strings" "testing" + "time" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethersphere/bee/pkg/crypto" signermock "github.com/ethersphere/bee/pkg/crypto/mock" "github.com/ethersphere/bee/pkg/log" @@ -23,6 +26,7 @@ import ( "github.com/ethersphere/bee/pkg/transaction" "github.com/ethersphere/bee/pkg/transaction/backendmock" "github.com/ethersphere/bee/pkg/transaction/monitormock" + "github.com/ethersphere/bee/pkg/util/abiutil" "github.com/ethersphere/bee/pkg/util/testutil" ) @@ -239,7 +243,7 @@ func TestTransactionSend(t *testing.T) { t.Fatal(err) } - transactionService, err := transaction.NewService(logger, + transactionService, err := transaction.NewService(logger, sender, backendmock.New( backendmock.WithSendTransactionFunc(func(ctx context.Context, tx *types.Transaction) error { if tx != signedTx { @@ -901,3 +905,89 @@ func TestTransactionCancel(t *testing.T) { } }) } + +// rpcAPIError is a copy of engine.EngineAPIError from go-ethereum pkg. +type rpcAPIError struct { + code int + msg string + err string +} + +func (e *rpcAPIError) ErrorCode() int { return e.code } +func (e *rpcAPIError) Error() string { return e.msg } +func (e *rpcAPIError) ErrorData() interface{} { return e.err } + +var _ rpc.DataError = (*rpcAPIError)(nil) + +func TestTransactionService_UnwrapABIError(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + var ( + sender = common.HexToAddress("0xddff") + recipient = common.HexToAddress("0xbbbddd") + chainID = big.NewInt(5) + nonce = uint64(10) + gasTip = big.NewInt(100) + gasFee = big.NewInt(1100) + txData = common.Hex2Bytes("0xabcdee") + value = big.NewInt(1) + + // This is the ABI of the following contract: https://goerli.etherscan.io/address/0xd29d9e385f19d888557cd609006bb1934cb5d1e2#code + contractABI = abiutil.MustParseABI(`[{"inputs":[{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[],"stateMutability":"nonpayable","type":"function"}]`) + rpcAPIErr = &rpcAPIError{ + code: 3, + msg: "execution reverted", + err: "0xcf4791810000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f", // This is the ABI encoded error form the following failed transaction: https://goerli.etherscan.io/tx/0x74a2577db1c325c41e38977aa1eb32ab03dfa17cc1fa0649e84f3d8c0f0882ee + } + ) + + gasTipCap := new(big.Int).Div(new(big.Int).Mul(big.NewInt(int64(10)+100), gasTip), big.NewInt(100)) + gasFeeCap := new(big.Int).Add(gasFee, gasTipCap) + + signedTx := types.NewTx(&types.DynamicFeeTx{ + ChainID: chainID, + Nonce: nonce, + To: &recipient, + Value: value, + Gas: 21000, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Data: txData, + }) + request := &transaction.TxRequest{ + To: &recipient, + Data: txData, + Value: value, + } + + transactionService, err := transaction.NewService(log.Noop, sender, + backendmock.New( + backendmock.WithCallContractFunc(func(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + return nil, rpcAPIErr + }), + ), + signerMockForTransaction(t, signedTx, recipient, chainID), + storemock.NewStateStore(), + chainID, + monitormock.New(), + ) + if err != nil { + t.Fatal(err) + } + testutil.CleanupCloser(t, transactionService) + + originErr := errors.New("origin error") + wrappedErr := transactionService.UnwrapABIError(ctx, request, originErr, contractABI.Errors) + if !errors.Is(wrappedErr, originErr) { + t.Fatal("origin error not wrapped") + } + if !strings.Contains(wrappedErr.Error(), rpcAPIErr.Error()) { + t.Fatal("wrapped error without rpc api main error") + } + if !strings.Contains(wrappedErr.Error(), "InsufficientBalance(available=0,required=111)") { + t.Fatal("wrapped error without rpc api error data") + } +} From bf5eba5b10d7a142bb09e9c9a22e1ac1a0e0dd65 Mon Sep 17 00:00:00 2001 From: mrekucci Date: Thu, 5 Oct 2023 19:44:37 +0400 Subject: [PATCH 09/17] chore: bump storage-incentives abi to v0.6.0-rc8 (#4381) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index edc13d9caf4..d4b77c5ca7a 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/ethereum/go-ethereum v1.13.4 github.com/ethersphere/go-price-oracle-abi v0.1.0 - github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc7 + github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc8 github.com/ethersphere/go-sw3-abi v0.4.0 github.com/ethersphere/langos v1.0.0 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index 6dcd24d01e9..064ef699381 100644 --- a/go.sum +++ b/go.sum @@ -242,8 +242,8 @@ github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8 github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= github.com/ethersphere/go-price-oracle-abi v0.1.0 h1:yg/hK8nETNvk+GEBASlbakMFv/CVp7HXiycrHw1pRV8= github.com/ethersphere/go-price-oracle-abi v0.1.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc7 h1:SPg0WY5rCWt7Y72hAj0o1juLtNi9lqx7b/6fNaNa4H0= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc7/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc8 h1:+7xFfUGYWhDcXEQyanH6W7CRsNBo9fr/x5hfsmGQ9UY= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc8/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g= github.com/ethersphere/go-sw3-abi v0.4.0/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU= github.com/ethersphere/langos v1.0.0 h1:NBtNKzXTTRSue95uOlzPN4py7Aofs0xWPzyj4AI1Vcc= From 84c21f5db94280afd23ded635d2f86deec85f696 Mon Sep 17 00:00:00 2001 From: mrekucci Date: Mon, 9 Oct 2023 13:45:10 +0400 Subject: [PATCH 10/17] fix: redistribution contract abi type change (#4382) --- .github/workflows/beekeeper.yml | 2 +- pkg/api/api_test.go | 2 +- pkg/storageincentives/agent.go | 2 +- pkg/storageincentives/agent_test.go | 2 +- pkg/storageincentives/redistribution/redistribution.go | 4 ++-- .../redistribution/redistribution_test.go | 8 ++++---- pkg/transaction/transaction.go | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/beekeeper.yml b/.github/workflows/beekeeper.yml index b0386766f73..87aba497249 100644 --- a/.github/workflows/beekeeper.yml +++ b/.github/workflows/beekeeper.yml @@ -11,7 +11,7 @@ env: K3S_VERSION: "v1.22.17+k3s1" REPLICA: 3 RUN_TYPE: "PR RUN" - SETUP_CONTRACT_IMAGE_TAG: "1.0.3" + SETUP_CONTRACT_IMAGE_TAG: "1.0.4" BEELOCAL_BRANCH: "main" BEEKEEPER_BRANCH: "master" BEEKEEPER_METRICS_ENABLED: false diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go index 21e535f10ac..a31bb87ff5d 100644 --- a/pkg/api/api_test.go +++ b/pkg/api/api_test.go @@ -783,7 +783,7 @@ func (m *mockContract) Claim(context.Context, redistribution.ChunkInclusionProof return common.Hash{}, nil } -func (m *mockContract) Commit(context.Context, []byte, uint32) (common.Hash, error) { +func (m *mockContract) Commit(context.Context, []byte, uint64) (common.Hash, error) { m.mtx.Lock() defer m.mtx.Unlock() m.callsList = append(m.callsList, commitCall) diff --git a/pkg/storageincentives/agent.go b/pkg/storageincentives/agent.go index 51a62393a8c..2bd0bf0ffb4 100644 --- a/pkg/storageincentives/agent.go +++ b/pkg/storageincentives/agent.go @@ -510,7 +510,7 @@ func (a *Agent) commit(ctx context.Context, sample SampleData, round uint64) err return err } - txHash, err := a.contract.Commit(ctx, obfuscatedHash, uint32(round)) + txHash, err := a.contract.Commit(ctx, obfuscatedHash, round) if err != nil { a.metrics.ErrCommit.Inc() return err diff --git a/pkg/storageincentives/agent_test.go b/pkg/storageincentives/agent_test.go index 70ec3193975..fe5bc6a937f 100644 --- a/pkg/storageincentives/agent_test.go +++ b/pkg/storageincentives/agent_test.go @@ -283,7 +283,7 @@ func (m *mockContract) Claim(context.Context, redistribution.ChunkInclusionProof return common.Hash{}, nil } -func (m *mockContract) Commit(context.Context, []byte, uint32) (common.Hash, error) { +func (m *mockContract) Commit(context.Context, []byte, uint64) (common.Hash, error) { m.mtx.Lock() defer m.mtx.Unlock() m.callsList = append(m.callsList, commitCall) diff --git a/pkg/storageincentives/redistribution/redistribution.go b/pkg/storageincentives/redistribution/redistribution.go index 8b83537cf90..01ddb9195b0 100644 --- a/pkg/storageincentives/redistribution/redistribution.go +++ b/pkg/storageincentives/redistribution/redistribution.go @@ -24,7 +24,7 @@ type Contract interface { IsPlaying(context.Context, uint8) (bool, error) IsWinner(context.Context) (bool, error) Claim(context.Context, ChunkInclusionProofs) (common.Hash, error) - Commit(context.Context, []byte, uint32) (common.Hash, error) + Commit(context.Context, []byte, uint64) (common.Hash, error) Reveal(context.Context, uint8, []byte, []byte) (common.Hash, error) } @@ -115,7 +115,7 @@ func (c *contract) Claim(ctx context.Context, proofs ChunkInclusionProofs) (comm } // Commit submits the obfusHash hash by sending a transaction to the blockchain. -func (c *contract) Commit(ctx context.Context, obfusHash []byte, round uint32) (common.Hash, error) { +func (c *contract) Commit(ctx context.Context, obfusHash []byte, round uint64) (common.Hash, error) { callData, err := c.incentivesContractABI.Pack("commit", common.BytesToHash(obfusHash), common.BytesToHash(c.overlay.Bytes()), round) if err != nil { return common.Hash{}, err diff --git a/pkg/storageincentives/redistribution/redistribution_test.go b/pkg/storageincentives/redistribution/redistribution_test.go index 943d3013ba4..4a8e84b421b 100644 --- a/pkg/storageincentives/redistribution/redistribution_test.go +++ b/pkg/storageincentives/redistribution/redistribution_test.go @@ -267,7 +267,7 @@ func TestRedistribution(t *testing.T) { var obfus [32]byte testobfus := common.Hex2Bytes("hash") copy(obfus[:], testobfus) - expectedCallData, err := redistributionContractABI.Pack("commit", obfus, common.BytesToHash(owner.Bytes()), uint32(0)) + expectedCallData, err := redistributionContractABI.Pack("commit", obfus, common.BytesToHash(owner.Bytes()), uint64(0)) if err != nil { t.Fatal(err) } @@ -297,7 +297,7 @@ func TestRedistribution(t *testing.T) { redistributionContractABI, ) - _, err = contract.Commit(ctx, testobfus, uint32(0)) + _, err = contract.Commit(ctx, testobfus, 0) if err != nil { t.Fatal(err) } @@ -402,7 +402,7 @@ func TestRedistribution(t *testing.T) { t.Run("invalid call data", func(t *testing.T) { t.Parallel() - expectedCallData, err := redistributionContractABI.Pack("commit", common.BytesToHash(common.Hex2Bytes("some hash")), common.BytesToHash(common.Hex2Bytes("some address")), uint32(0)) + expectedCallData, err := redistributionContractABI.Pack("commit", common.BytesToHash(common.Hex2Bytes("some hash")), common.BytesToHash(common.Hex2Bytes("some address")), uint64(0)) if err != nil { t.Fatal(err) } @@ -424,7 +424,7 @@ func TestRedistribution(t *testing.T) { redistributionContractABI, ) - _, err = contract.Commit(ctx, common.Hex2Bytes("hash"), uint32(0)) + _, err = contract.Commit(ctx, common.Hex2Bytes("hash"), 0) if err == nil { t.Fatal("expected error") } diff --git a/pkg/transaction/transaction.go b/pkg/transaction/transaction.go index 5e0cdfa4162..592af68a566 100644 --- a/pkg/transaction/transaction.go +++ b/pkg/transaction/transaction.go @@ -601,7 +601,7 @@ func (t *transactionService) UnwrapABIError(ctx context.Context, req *TxRequest, if cErr == nil { return err } - err = fmt.Errorf("%w: %s", err, cErr) + err = fmt.Errorf("%w: %s", err, cErr) //nolint:errorlint var derr rpc.DataError if !errors.As(cErr, &derr) { From 8024241035713926cacfca5ea08d67dd7756dce9 Mon Sep 17 00:00:00 2001 From: istae <14264581+istae@users.noreply.github.com> Date: Tue, 10 Oct 2023 16:29:56 +0300 Subject: [PATCH 11/17] chore: upgrade to rc9 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d4b77c5ca7a..3c9a523ab5e 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/ethereum/go-ethereum v1.13.4 github.com/ethersphere/go-price-oracle-abi v0.1.0 - github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc8 + github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc9 github.com/ethersphere/go-sw3-abi v0.4.0 github.com/ethersphere/langos v1.0.0 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index 064ef699381..87bef33f022 100644 --- a/go.sum +++ b/go.sum @@ -242,8 +242,8 @@ github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8 github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= github.com/ethersphere/go-price-oracle-abi v0.1.0 h1:yg/hK8nETNvk+GEBASlbakMFv/CVp7HXiycrHw1pRV8= github.com/ethersphere/go-price-oracle-abi v0.1.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc8 h1:+7xFfUGYWhDcXEQyanH6W7CRsNBo9fr/x5hfsmGQ9UY= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc8/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc9 h1:dq6ESIiV75lfINTy0Q2pXQWWnfv3e+SjRIRLbj59AsY= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc9/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g= github.com/ethersphere/go-sw3-abi v0.4.0/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU= github.com/ethersphere/langos v1.0.0 h1:NBtNKzXTTRSue95uOlzPN4py7Aofs0xWPzyj4AI1Vcc= From 27329027e5626ab5193c63b485b56b8b2e42c26d Mon Sep 17 00:00:00 2001 From: Ivan Vandot Date: Thu, 12 Oct 2023 09:08:20 +0200 Subject: [PATCH 12/17] chore: bump go-storage-incentives-abi --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3c9a523ab5e..6521b943511 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/ethereum/go-ethereum v1.13.4 github.com/ethersphere/go-price-oracle-abi v0.1.0 - github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc9 + github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc10 github.com/ethersphere/go-sw3-abi v0.4.0 github.com/ethersphere/langos v1.0.0 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index 87bef33f022..943736e993e 100644 --- a/go.sum +++ b/go.sum @@ -242,6 +242,8 @@ github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8 github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= github.com/ethersphere/go-price-oracle-abi v0.1.0 h1:yg/hK8nETNvk+GEBASlbakMFv/CVp7HXiycrHw1pRV8= github.com/ethersphere/go-price-oracle-abi v0.1.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc10 h1:r4hp1ffCCmTDILBOfUUMweoMr4OxE+X6E7+p0yYAE/w= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc10/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc9 h1:dq6ESIiV75lfINTy0Q2pXQWWnfv3e+SjRIRLbj59AsY= github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc9/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g= From 41df67c7dabf8f90db41a239824d3a105368d338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Levente=20T=C3=B3th?= Date: Tue, 17 Oct 2023 10:38:22 +0200 Subject: [PATCH 13/17] fix: equal transformed addresses --- pkg/storer/sample.go | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/pkg/storer/sample.go b/pkg/storer/sample.go index 196bb4d26a9..7fd5cc2ad4a 100644 --- a/pkg/storer/sample.go +++ b/pkg/storer/sample.go @@ -233,6 +233,14 @@ func (db *DB) ReserveSample( sampleItems[i] = item added = true break + } else if item.TransformedAddress.Compare(sItem.TransformedAddress) == 0 { // ensuring to pass the check order function of redistribution contract + // replace the chunk at index if the chunk is CAC + ch := swarm.NewChunk(item.ChunkAddress, item.ChunkData) + _, err := soc.FromChunk(ch) + if err != nil { + sampleItems[i] = item + } + return } } if len(sampleItems) > SampleSize { @@ -243,15 +251,6 @@ func (db *DB) ReserveSample( } } - contains := func(addr swarm.Address) int { - for index, item := range sampleItems { - if item.ChunkAddress.Compare(addr) == 0 { - return index - } - } - return -1 - } - // Phase 3: Assemble the sample. Here we need to assemble only the first SampleSize // no of items from the results of the 2nd phase. // In this step stamps are loaded and validated only if chunk will be added to sample. @@ -290,12 +289,6 @@ func (db *DB) ReserveSample( item.Stamp = postage.NewStamp(stamp.BatchID(), stamp.Index(), stamp.Timestamp(), stamp.Sig()) - // ensuring to pass the check order function of redistribution contract - if index := contains(item.TransformedAddress); index != -1 { - // replace the chunk at index - sampleItems[index] = item - continue - } insert(item) stats.SampleInserts++ } From 7e10bb00a50b102adc83458db8d06a138a194775 Mon Sep 17 00:00:00 2001 From: Ivan Vandot Date: Fri, 20 Oct 2023 12:10:10 +0200 Subject: [PATCH 14/17] chore: bump go-storage-incentives-abi to rc12 --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 6521b943511..ffe7350cdcb 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/ethereum/go-ethereum v1.13.4 github.com/ethersphere/go-price-oracle-abi v0.1.0 - github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc10 + github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc12 github.com/ethersphere/go-sw3-abi v0.4.0 github.com/ethersphere/langos v1.0.0 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index 943736e993e..e95ace7fa02 100644 --- a/go.sum +++ b/go.sum @@ -242,10 +242,8 @@ github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8 github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= github.com/ethersphere/go-price-oracle-abi v0.1.0 h1:yg/hK8nETNvk+GEBASlbakMFv/CVp7HXiycrHw1pRV8= github.com/ethersphere/go-price-oracle-abi v0.1.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc10 h1:r4hp1ffCCmTDILBOfUUMweoMr4OxE+X6E7+p0yYAE/w= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc10/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc9 h1:dq6ESIiV75lfINTy0Q2pXQWWnfv3e+SjRIRLbj59AsY= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc9/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc12 h1:9Ax6Oy2C9k/xm6NszGrMfNw1rizOvW1o5qQ32NwEeyg= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc12/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g= github.com/ethersphere/go-sw3-abi v0.4.0/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU= github.com/ethersphere/langos v1.0.0 h1:NBtNKzXTTRSue95uOlzPN4py7Aofs0xWPzyj4AI1Vcc= From 548333fe0164477422ad246c9d2b8fa3ea2965d8 Mon Sep 17 00:00:00 2001 From: Ivan Vandot Date: Mon, 23 Oct 2023 13:57:07 +0200 Subject: [PATCH 15/17] chore: bump go-storage-incentives-abi to rc13 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ffe7350cdcb..29be60d5f23 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/ethereum/go-ethereum v1.13.4 github.com/ethersphere/go-price-oracle-abi v0.1.0 - github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc12 + github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc13 github.com/ethersphere/go-sw3-abi v0.4.0 github.com/ethersphere/langos v1.0.0 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index e95ace7fa02..f0ab78952bb 100644 --- a/go.sum +++ b/go.sum @@ -242,8 +242,8 @@ github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8 github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= github.com/ethersphere/go-price-oracle-abi v0.1.0 h1:yg/hK8nETNvk+GEBASlbakMFv/CVp7HXiycrHw1pRV8= github.com/ethersphere/go-price-oracle-abi v0.1.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc12 h1:9Ax6Oy2C9k/xm6NszGrMfNw1rizOvW1o5qQ32NwEeyg= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc12/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc13 h1:StNVOO88hjxrFsniyRcZ43WJQZBG29kM7L9ZiOKk4zc= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc13/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g= github.com/ethersphere/go-sw3-abi v0.4.0/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU= github.com/ethersphere/langos v1.0.0 h1:NBtNKzXTTRSue95uOlzPN4py7Aofs0xWPzyj4AI1Vcc= From 4e9c6f63e1677e419eb24823675264c827069d13 Mon Sep 17 00:00:00 2001 From: Ivan Vandot Date: Mon, 30 Oct 2023 17:18:03 +0100 Subject: [PATCH 16/17] chore: bump go-storage-incentives-abi to rc15 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 29be60d5f23..6dc8df15078 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/ethereum/go-ethereum v1.13.4 github.com/ethersphere/go-price-oracle-abi v0.1.0 - github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc13 + github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc15 github.com/ethersphere/go-sw3-abi v0.4.0 github.com/ethersphere/langos v1.0.0 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index f0ab78952bb..df8f7739a5f 100644 --- a/go.sum +++ b/go.sum @@ -242,8 +242,8 @@ github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8 github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= github.com/ethersphere/go-price-oracle-abi v0.1.0 h1:yg/hK8nETNvk+GEBASlbakMFv/CVp7HXiycrHw1pRV8= github.com/ethersphere/go-price-oracle-abi v0.1.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc13 h1:StNVOO88hjxrFsniyRcZ43WJQZBG29kM7L9ZiOKk4zc= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc13/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc15 h1:tZ3q1zbxmSwK9qOM74U1X/9RkYBxD/GTX5SdoVkBOWQ= +github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc15/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g= github.com/ethersphere/go-sw3-abi v0.4.0/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU= github.com/ethersphere/langos v1.0.0 h1:NBtNKzXTTRSue95uOlzPN4py7Aofs0xWPzyj4AI1Vcc= From be4f3853bd0abef9ecc78793129df32730d6beee Mon Sep 17 00:00:00 2001 From: mrekucci Date: Tue, 14 Nov 2023 00:11:53 +0800 Subject: [PATCH 17/17] chore: bump storage-incentives abi to v0.6.1-rc2 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6dc8df15078..5ac56bc1b8c 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/ethereum/go-ethereum v1.13.4 github.com/ethersphere/go-price-oracle-abi v0.1.0 - github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc15 + github.com/ethersphere/go-storage-incentives-abi v0.6.1-rc2 github.com/ethersphere/go-sw3-abi v0.4.0 github.com/ethersphere/langos v1.0.0 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index df8f7739a5f..fb7983428c6 100644 --- a/go.sum +++ b/go.sum @@ -242,8 +242,8 @@ github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8 github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= github.com/ethersphere/go-price-oracle-abi v0.1.0 h1:yg/hK8nETNvk+GEBASlbakMFv/CVp7HXiycrHw1pRV8= github.com/ethersphere/go-price-oracle-abi v0.1.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc15 h1:tZ3q1zbxmSwK9qOM74U1X/9RkYBxD/GTX5SdoVkBOWQ= -github.com/ethersphere/go-storage-incentives-abi v0.6.0-rc15/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= +github.com/ethersphere/go-storage-incentives-abi v0.6.1-rc2 h1:XqHSTua/DD/o19SJE1k4GG7kxOSr0IqQJEvi57Pvb2g= +github.com/ethersphere/go-storage-incentives-abi v0.6.1-rc2/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g= github.com/ethersphere/go-sw3-abi v0.4.0/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU= github.com/ethersphere/langos v1.0.0 h1:NBtNKzXTTRSue95uOlzPN4py7Aofs0xWPzyj4AI1Vcc=