diff --git a/crypto/random.go b/crypto/random.go index 275fb1044f..95622d51e6 100644 --- a/crypto/random.go +++ b/crypto/random.go @@ -16,7 +16,7 @@ func randBytes(numBytes int) []byte { return b } -// This only uses the OS's randomness +// CRandBytes returns requested number of bytes from the OS's randomness. func CRandBytes(numBytes int) []byte { return randBytes(numBytes) } @@ -29,7 +29,7 @@ func CRandHex(numDigits int) string { return hex.EncodeToString(CRandBytes(numDigits / 2)) } -// Returns a crand.Reader. +// CReader returns a crand.Reader. func CReader() io.Reader { return crand.Reader } diff --git a/go.sum b/go.sum index 615e43a484..49fb27c9de 100644 --- a/go.sum +++ b/go.sum @@ -599,13 +599,11 @@ github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lazyledger/go-ipfs v0.8.0-lazypatch h1:8Dkw7Or6d0BmpFYFcxwgqWZ047BPGCsWtG7v9+H0ofk= github.com/lazyledger/go-ipfs v0.8.0-lazypatch/go.mod h1:CE4cJkjUmwW5LwJP26KKEAZ11ZED0DxzSryfv5RMf6E= -github.com/lazyledger/go-leopard v0.0.0-20200604113236-298f93361181 h1:mUeCGuCgjZVadW4CzA2dMBq7p2BqaoCfpnKjxMmSaSE= github.com/lazyledger/go-leopard v0.0.0-20200604113236-298f93361181/go.mod h1:v1o1CRihQ9i7hizx23KK4aR79lxA6VDUIzUCfDva0XQ= github.com/lazyledger/go-leopard v0.0.0-20200724211609-50ec4b3fab41 h1:DTQODNWI71ZtqCT3wQg+RXl4K/zpu+hu5usISUhDq/E= github.com/lazyledger/go-leopard v0.0.0-20200724211609-50ec4b3fab41/go.mod h1:v1o1CRihQ9i7hizx23KK4aR79lxA6VDUIzUCfDva0XQ= @@ -615,7 +613,6 @@ github.com/lazyledger/merkletree v0.0.0-20201214195110-6901c4c3c75f h1:jbyPAH6o6 github.com/lazyledger/merkletree v0.0.0-20201214195110-6901c4c3c75f/go.mod h1:10PA0NlnYtB8HrtwIDQAyTKWp8TEZ0zBZCGlYC/7+QE= github.com/lazyledger/nmt v0.3.1 h1:zP172RR33Es4dhb88GmUr9kBpAkH6Wcl7nQGJ3HQzu4= github.com/lazyledger/nmt v0.3.1/go.mod h1:tY7ypPX26Sbkt6F8EbPl3AT33B5N0BJe4OVPbq849YI= -github.com/lazyledger/rsmt2d v0.1.1-0.20210327010029-ef1d6c54461e h1:3mwa4b4v9puYIFsfRN6TsXMbUagvhLtIzXWHN2GFHB4= github.com/lazyledger/rsmt2d v0.1.1-0.20210327010029-ef1d6c54461e/go.mod h1:ORR2U7THCNr1fpUhwYqZN7QCFJ20iR2uiIWfXKz3KJ4= github.com/lazyledger/rsmt2d v0.1.1-0.20210406153014-e1fd589bdb09 h1:5wpWhlalAm1vNpkR/L1BlWHyn8GT5XNieIZqTlKG9hc= github.com/lazyledger/rsmt2d v0.1.1-0.20210406153014-e1fd589bdb09/go.mod h1:EbB1gGbX51gBNm0hC5lMcbkgEyO3Wj2RYwba9mCUvPA= @@ -1168,7 +1165,6 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spacemonkeygo/errors v0.0.0-20171212215202-9064522e9fd1 h1:xHQewZjohU9/wUsyC99navCjQDNHtTgUOM/J1jAbzfw= github.com/spacemonkeygo/errors v0.0.0-20171212215202-9064522e9fd1/go.mod h1:7NL9UAYQnRM5iKHUCld3tf02fKb5Dft+41+VckASUy0= github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= @@ -1229,7 +1225,6 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/urfave/cli/v2 v2.0.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/vivint/infectious v0.0.0-20190108171102-2455b059135b h1:dLkqBELopfQNhe8S9ucnSf+HhiUCgK/hPIjVG0f9GlY= github.com/vivint/infectious v0.0.0-20190108171102-2455b059135b/go.mod h1:5oyMAv4hrBEKqBwORFsiqIrCNCmL2qcZLQTdJLYeYIc= github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3 h1:zMsHhfK9+Wdl1F7sIKLyx3wrOFofpb3rWFbA4HgcK5k= github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3/go.mod h1:R0Gbuw7ElaGSLOZUSwBm/GgVwMd30jWxBDdAyMOeTuc= @@ -1267,7 +1262,6 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1: github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8 h1:gZfMjx7Jr6N8b7iJO4eUjDsn6xJqoyXg8D+ogdoAfKY= gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8/go.mod h1:ZkMZ0dpQyWwlENaeZVBiQRjhMEZvk6VTXquzl3FOFP8= gitlab.com/NebulousLabs/errors v0.0.0-20200929122200-06c536cf6975 h1:L/ENs/Ar1bFzUeKx6m3XjlmBgIUlykX9dzvp5k9NGxc= gitlab.com/NebulousLabs/errors v0.0.0-20200929122200-06c536cf6975/go.mod h1:ZkMZ0dpQyWwlENaeZVBiQRjhMEZvk6VTXquzl3FOFP8= @@ -1359,6 +1353,7 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1419,7 +1414,6 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -1495,7 +1489,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A= @@ -1561,9 +1554,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.7.0 h1:Hdks0L0hgznZLG9nzXb8vZ0rRvqNvAcgAp84y7Mwkgw= gonum.org/v1/gonum v0.7.0/go.mod h1:L02bwd0sqlsvRv41G7wGWFCsVNZFv/k1xzGIxeANHGM= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -1663,7 +1654,6 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/p2p/ipld/read.go b/p2p/ipld/read.go index 12e7d4bb8f..531b43c2c8 100644 --- a/p2p/ipld/read.go +++ b/p2p/ipld/read.go @@ -10,12 +10,13 @@ import ( "github.com/ipfs/go-cid" coreiface "github.com/ipfs/interface-go-ipfs-core" "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/lazyledger/rsmt2d" + "github.com/lazyledger/lazyledger-core/p2p/ipld/plugin/nodes" "github.com/lazyledger/lazyledger-core/types" - "github.com/lazyledger/rsmt2d" ) -// ////////////////////////////////////// +// /////////////////////////////////////// // Retrieve Block Data // //////////////////////////////////// diff --git a/p2p/ipld/read_test.go b/p2p/ipld/read_test.go index b22e851db1..427fdc15b3 100644 --- a/p2p/ipld/read_test.go +++ b/p2p/ipld/read_test.go @@ -12,18 +12,19 @@ import ( "testing" "time" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" "github.com/ipfs/go-ipfs/core/coreapi" - coremock "github.com/ipfs/go-ipfs/core/mock" format "github.com/ipfs/go-ipld-format" - "github.com/lazyledger/lazyledger-core/p2p/ipld/plugin/nodes" - "github.com/lazyledger/lazyledger-core/types" + iface "github.com/ipfs/interface-go-ipfs-core" "github.com/lazyledger/nmt" "github.com/lazyledger/nmt/namespace" "github.com/lazyledger/rsmt2d" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/lazyledger/lazyledger-core/p2p/ipld/plugin/nodes" + "github.com/lazyledger/lazyledger-core/types" ) var raceDetectorActive = false @@ -98,20 +99,12 @@ func TestGetLeafData(t *testing.T) { leaves [][]byte } - // create a mock node - ipfsNode, err := coremock.NewMockNode() - if err != nil { - t.Error(err) - } + // create the context and batch needed for node collection from the tree + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() // issue a new API object - ipfsAPI, err := coreapi.NewCoreAPI(ipfsNode) - if err != nil { - t.Error(err) - } - - // create the context and batch needed for node collection from the tree - ctx := context.Background() + ipfsAPI := mockedIpfsAPI(t) batch := format.NewBatch(ctx, ipfsAPI.Dag()) // generate random data for the nmt @@ -238,17 +231,8 @@ func TestRetrieveBlockData(t *testing.T) { errStr string } - // create a mock node - ipfsNode, err := coremock.NewMockNode() - if err != nil { - t.Error(err) - } - // issue a new API object - ipfsAPI, err := coreapi.NewCoreAPI(ipfsNode) - if err != nil { - t.Error(err) - } + ipfsAPI := mockedIpfsAPI(t) // the max size of messages that won't get split adjustedMsgSize := types.MsgShareSize - 2 @@ -447,3 +431,18 @@ func generateRandomContiguousShares(count int) types.Txs { } return txs } + +func mockedIpfsAPI(t *testing.T) iface.CoreAPI { + ipfsNode, err := coremock.NewMockNode() + if err != nil { + t.Error(err) + } + + // issue a new API object + ipfsAPI, err := coreapi.NewCoreAPI(ipfsNode) + if err != nil { + t.Error(err) + } + + return ipfsAPI +} diff --git a/p2p/ipld/sample.go b/p2p/ipld/sample.go new file mode 100644 index 0000000000..93c975fa5d --- /dev/null +++ b/p2p/ipld/sample.go @@ -0,0 +1,108 @@ +package ipld + +import ( + crand "crypto/rand" + "math/big" + + "github.com/ipfs/go-cid" + "github.com/lazyledger/nmt/namespace" + + "github.com/lazyledger/lazyledger-core/p2p/ipld/plugin/nodes" + "github.com/lazyledger/lazyledger-core/types" +) + +// Sample is a point in 2D space over square. +type Sample struct { + Row, Col uint32 + + // src defines the source for sampling, either from column(true) or row(false) root + src bool +} + +// SampleSquare randomly picks *num* unique points from arbitrary *width* square +// and returns them as samples. +func SampleSquare(squareWidth uint32, num int) []Sample { + ss := newSquareSampler(squareWidth, num) + ss.sample(num) + return ss.samples() +} + +// Leaf returns leaf info needed for retrieval using data provided with DAHeader. +func (s Sample) Leaf(dah *types.DataAvailabilityHeader) (cid.Cid, uint32, error) { + var ( + leaf uint32 + root namespace.IntervalDigest + ) + + // spread leaves retrieval from both Row and Column roots + if s.src { + root = dah.ColumnRoots[s.Col] + leaf = s.Row + } else { + root = dah.RowsRoots[s.Row] + leaf = s.Col + } + + rootCid, err := nodes.CidFromNamespacedSha256(root.Bytes()) + if err != nil { + return cid.Undef, 0, err + } + + return rootCid, leaf, nil +} + +// Equals check whenever to samples are equal. +func (s Sample) Equals(to Sample) bool { + return s.Row == to.Row && s.Col == to.Col +} + +type squareSampler struct { + squareWidth uint32 + smpls map[Sample]struct{} +} + +func newSquareSampler(squareWidth uint32, expectedSamples int) *squareSampler { + return &squareSampler{ + squareWidth: squareWidth, + smpls: make(map[Sample]struct{}, expectedSamples), + } +} + +func (ss *squareSampler) sample(num int) { + if uint32(num) > ss.squareWidth*ss.squareWidth { + panic("number of samples must be less than square width") + } + + done := 0 + for done < num { + s := Sample{ + Row: randUint32(ss.squareWidth), + Col: randUint32(ss.squareWidth), + src: randUint32(2) == 0, + } + + if _, ok := ss.smpls[s]; ok { + continue + } + + done++ + ss.smpls[s] = struct{}{} + } +} + +func (ss *squareSampler) samples() []Sample { + samples := make([]Sample, 0, len(ss.smpls)) + for s := range ss.smpls { + samples = append(samples, s) + } + return samples +} + +func randUint32(max uint32) uint32 { + n, err := crand.Int(crand.Reader, big.NewInt(int64(max))) + if err != nil { + panic(err) // won't panic as rand.Reader is endless + } + + return uint32(n.Int64()) +} diff --git a/p2p/ipld/sample_test.go b/p2p/ipld/sample_test.go new file mode 100644 index 0000000000..073f47d059 --- /dev/null +++ b/p2p/ipld/sample_test.go @@ -0,0 +1,35 @@ +package ipld + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSampleSquare(t *testing.T) { + tests := []struct { + width uint32 + samples int + }{ + {width: 10, samples: 5}, + {width: 500, samples: 90}, + } + + for _, tt := range tests { + ss := SampleSquare(tt.width, tt.samples) + assert.Len(t, ss, tt.samples) + // check points are within width + for _, s := range ss { + assert.Less(t, s.Row, tt.width) + assert.Less(t, s.Col, tt.width) + } + // checks samples are not equal + for i, s1 := range ss { + for j, s2 := range ss { + if i != j { + assert.False(t, s1.Equals(s2)) + } + } + } + } +} diff --git a/p2p/ipld/validate.go b/p2p/ipld/validate.go new file mode 100644 index 0000000000..26dab1ce9e --- /dev/null +++ b/p2p/ipld/validate.go @@ -0,0 +1,91 @@ +package ipld + +import ( + "context" + "errors" + "time" + + format "github.com/ipfs/go-ipld-format" + coreiface "github.com/ipfs/interface-go-ipfs-core" + "github.com/lazyledger/nmt/namespace" + + "github.com/lazyledger/lazyledger-core/types" +) + +// ValidationTimeout specifies timeout for DA validation during which data have to be found on the network, +// otherwise ErrValidationFailed is thrown. +// TODO: github.com/lazyledger/lazyledger-core/issues/280 +const ValidationTimeout = time.Minute + +// ErrValidationFailed is returned whenever DA validation fails +var ErrValidationFailed = errors.New("validation failed") + +// ValidateAvailability randomly samples the block data that composes a provided +// data availability header. It only returns when all samples have been completed +// successfully. `onLeafValidity` is called on each sampled leaf after +// retrieval. Implements the protocol described in +// https://fc21.ifca.ai/papers/83.pdf. +func ValidateAvailability( + ctx context.Context, + api coreiface.CoreAPI, + dah *types.DataAvailabilityHeader, + numSamples int, + onLeafValidity func(namespace.PrefixedData8), +) error { + // TODO(@Wondertan): Ensure data is fetched within one DAG session + ctx, cancel := context.WithTimeout(ctx, ValidationTimeout) + defer cancel() + + squareWidth := uint32(len(dah.ColumnRoots)) + samples := SampleSquare(squareWidth, numSamples) + + type res struct { + data []byte + err error + } + resCh := make(chan res, len(samples)) + for _, s := range samples { + go func(s Sample) { + root, leaf, err := s.Leaf(dah) + if err != nil { + select { + case resCh <- res{err: err}: + case <-ctx.Done(): + } + return + } + + data, err := GetLeafData(ctx, root, leaf, squareWidth, api) + select { + case resCh <- res{data: data, err: err}: + case <-ctx.Done(): + } + }(s) + } + + for range samples { + select { + case r := <-resCh: + if r.err != nil { + if errors.Is(r.err, format.ErrNotFound) { + return ErrValidationFailed + } + + return r.err + } + + // the fact that we read the data, already gives us Merkle proof, + // thus the data availability is successfully validated :) + onLeafValidity(r.data) + case <-ctx.Done(): + err := ctx.Err() + if err == context.DeadlineExceeded { + return ErrValidationFailed + } + + return err + } + } + + return nil +} diff --git a/p2p/ipld/validate_test.go b/p2p/ipld/validate_test.go new file mode 100644 index 0000000000..8ea0a4066d --- /dev/null +++ b/p2p/ipld/validate_test.go @@ -0,0 +1,45 @@ +package ipld + +import ( + "context" + "testing" + + "github.com/lazyledger/nmt/namespace" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/lazyledger/lazyledger-core/types" +) + +// TODO(@Wondertan): Add test to simulate ErrValidationFailed + +func TestValidateAvailability(t *testing.T) { + const ( + shares = 15 + squareSize = 8 + adjustedMsgSize = types.MsgShareSize - 2 + ) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // issue a new API object + ipfsAPI := mockedIpfsAPI(t) + + blockData := generateRandomBlockData(squareSize*squareSize, adjustedMsgSize) + block := types.Block{ + Data: blockData, + LastCommit: &types.Commit{}, + } + block.Hash() + + err := block.PutBlock(ctx, ipfsAPI.Dag()) + require.NoError(t, err) + + calls := 0 + err = ValidateAvailability(ctx, ipfsAPI, &block.DataAvailabilityHeader, shares, func(data namespace.PrefixedData8) { + calls++ + }) + assert.NoError(t, err) + assert.Equal(t, shares, calls) +}