From fabed4da5d919694fb944f62bbad8ccbada00ab4 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Wed, 9 Oct 2024 17:46:23 +0700 Subject: [PATCH 01/25] reorganizing Learn section --- docs/learn/{technology => DISC}/DISC.md | 0 docs/learn/DISC/erasure-coding.md | 41 +++++++ .../{advanced => DISC}/neighbourhoods.md | 0 .../advanced/erasure-cost-calculation.md | 111 ------------------ docs/learn/{technology => }/what-is-swarm.md | 0 docusaurus.config.js | 2 +- sidebars.js | 24 ++-- 7 files changed, 55 insertions(+), 123 deletions(-) rename docs/learn/{technology => DISC}/DISC.md (100%) create mode 100644 docs/learn/DISC/erasure-coding.md rename docs/learn/{advanced => DISC}/neighbourhoods.md (100%) delete mode 100644 docs/learn/advanced/erasure-cost-calculation.md rename docs/learn/{technology => }/what-is-swarm.md (100%) diff --git a/docs/learn/technology/DISC.md b/docs/learn/DISC/DISC.md similarity index 100% rename from docs/learn/technology/DISC.md rename to docs/learn/DISC/DISC.md diff --git a/docs/learn/DISC/erasure-coding.md b/docs/learn/DISC/erasure-coding.md new file mode 100644 index 000000000..8e5adf3c5 --- /dev/null +++ b/docs/learn/DISC/erasure-coding.md @@ -0,0 +1,41 @@ +--- +title: Erasure Coding +id: erasure-coding +--- + +# Erasure Coding on Swarm + + +Erasure coding (also known as erasure code) is a powerful approach to data protection which is an optional feature for Swarm uploads. It is a technique that increases data protection by enabling the recovery of original data even when some encoded chunks are lost or corrupted. When used, it ensures that data on Swarm can always be accessed reliably, even if some nodes or entire neighborhoods go offline. + + +### How It Works +Erasure coding enhances data protection by dividing the source data into "chunks" and adding extra data to allow reconstruction of the original data. + +Specifically, data is divided into **N** chunks, and **K** additional chunks are generated, resulting in **N + K** total chunks. The data is encoded across these chunks such that as long as **N** chunks are intact, the original data can be fully reconstructed. This approach provides a robust method for data recovery in distributed storage networks like Swarm. + +#### Example + +For an 8KB image, if we set **N = 2** and **K = 1**, we create 3 chunks (2 original + 1 redundant). As long as any 2 of these 3 chunks are available, we can reconstruct the original data. By increasing **K** to 4, we can tolerate the loss of up to 4 chunks while still recovering the original data. + +![Erasure Code Example](https://www.ethswarm.org/uploads/erasure-coding-01.png) + +### General Benefits of Erasure Coding + +1. **Cost-Effective**: Erasure coding achieves data protection similar to or better than replication while requiring less storage space. + +2. **Tolerates Data Loss**: Unlike traditional error correction codes, erasure coding can withstand the total loss of specific chunks, making it suitable for scenarios where data integrity is critical. + +## Implications for Swarm + +### Tailored Data Protection for Swarm + +Swarm’s existing architecture, which naturally divides files into chunks and distributes them across a network of nodes, aligns perfectly with the principles of erasure coding. This addition enhances the resilience of data storage, ensuring that data remains accessible even if entire neighborhoods of nodes go offline. + +### Enabling New Use-Cases + +The integration of erasure coding significantly increases the durability of data stored on Swarm. It allows Swarm to cater to enterprise users and scenarios requiring enhanced data protection, serving as insurance against potential outages or attacks. + +## Conclusion + +The development of erasure coding on Swarm is actively progressing. A technical paper outlining Swarm’s specific approach to erasure coding will be released soon. Follow us for updates on these developments. diff --git a/docs/learn/advanced/neighbourhoods.md b/docs/learn/DISC/neighbourhoods.md similarity index 100% rename from docs/learn/advanced/neighbourhoods.md rename to docs/learn/DISC/neighbourhoods.md diff --git a/docs/learn/advanced/erasure-cost-calculation.md b/docs/learn/advanced/erasure-cost-calculation.md deleted file mode 100644 index 033bd8af1..000000000 --- a/docs/learn/advanced/erasure-cost-calculation.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Erasure Cost Calculation -id: erasure-cost-calculation ---- - -### Precise Cost Calculations - -For each redundancy level, there are m + k = 128 chunks, where m are the data chunks (shown in column "Data Chunks") and k are the parity chunks (shown in column "Parities"). If the number of chunks in the data being uploaded are an exact multiple of m, then the percent cost of the upload will simply equal the one shown in the chart above in the "Percent" column for the corresponding redundancy level. - -#### Exact Multiples - -For example, if we are uploading with the Strong redundancy level, and our source data consists of 321 (3 * 107) chunks, then we can simply use the percentage from the "Percent" column for the Strong level - 19.6% (63 parities / 321 data chunks). - -#### With Remainders - -However, generally speaking uploads will not come in exact multiples of m, so we need to adjust our calculations. To do so we need to use the table below which shows the number of parities for sets of chunks starting at a single chunk for each redundancy level up to the maximum number of data chunks for that level. Then we simply sum up the total parities and data chunks for the entire upload and calculate the resulting percentage. - -| Security | Parities | Chunks | Percent | Chunks Encrypted | Percent Encrypted | -|----------|----------|--------|-------------|------------------|-------------------| -| Medium | 2 | 1 | 200% | | | -| Medium | 3 | 2-5 | 150% - 60% | 1-2 | 300% - 150% | -| Medium | 4 | 6-14 | 66.7% - 28.6%| 3-7 | 133.3% - 57.1% | -| Medium | 5 | 15-28 | 33.3% - 17.9%| 7-14 | 71.4% - 35.7% | -| Medium | 6 | 29-46 | 20.7% - 13% | 14-23 | 42.9% - 26.1% | -| Medium | 7 | 47-68 | 14.9% - 10.3%| 23-34 | 30.4% - 20.6% | -| Medium | 8 | 69-94 | 11.6% - 8.5%| 34-47 | 23.5% - 17% | -| Medium | 9 | 95-119 | 9.5% - 7.6% | 47-59 | 19.1% - 15.3% | -| Strong | 4 | 1 | 400% | | | -| Strong | 5 | 2-3 | 250% - 166.7%| 1 | 500% | -| Strong | 6 | 4-6 | 150% - 100% | 2-3 | 300% - 200% | -| Strong | 7 | 7-10 | 100% - 70% | 3-5 | 233.3% - 140% | -| Strong | 8 | 11-15 | 72.7% - 53.3%| 5-7 | 160% - 114.3% | -| Strong | 9 | 16-20 | 56.2% - 45% | 8-10 | 112.5% - 90% | -| Strong | 10 | 21-26 | 47.6% - 38.5%| 10-13 | 100% - 76.9% | -| Strong | 11 | 27-32 | 40.7% - 34.4%| 13-16 | 84.6% - 68.8% | -| Strong | 12 | 33-39 | 36.4% - 30.8%| 16-19 | 75% - 63.2% | -| Strong | 13 | 40-46 | 32.5% - 28.3%| 20-23 | 65% - 56.5% | -| Strong | 14 | 47-53 | 29.8% - 26.4%| 23-26 | 60.9% - 53.8% | -| Strong | 15 | 54-61 | 27.8% - 24.6%| 27-30 | 55.6% - 50% | -| Strong | 16 | 62-69 | 25.8% - 23.2%| 31-34 | 51.6% - 47.1% | -| Strong | 17 | 70-77 | 24.3% - 22.1%| 35-38 | 48.6% - 44.7% | -| Strong | 18 | 78-86 | 23.1% - 20.9%| 39-43 | 46.2% - 41.9% | -| Strong | 19 | 87-95 | 21.8% - 20% | 43-47 | 44.2% - 40.4% | -| Strong | 20 | 96-104 | 20.8% - 19.2%| 48-52 | 41.7% - 38.5% | -| Strong | 21 | 105-107| 20% - 19.6% | 52-53 | 40.4% - 39.6% | -| Insane | 5 | 1 | 500% | | | -| Insane | 6 | 2 | 300% | 1 | 600% | -| Insane | 7 | 3 | 233.3% | 1 | 700% | -| Insane | 8 | 4-5 | 200% - 160% | 2 | 400% | -| Insane | 9 | 6-8 | 150% - 112.5%| 3-4 | 300% - 225% | -| Insane | 10 | 9-10 | 111.1% - 100%| 4-5 | 250% - 200% | -| Insane | 11 | 11-13 | 100% - 84.6%| 5-6 | 220% - 183.3% | -| Insane | 12 | 14-16 | 85.7% - 75% | 7-8 | 171.4% - 150% | -| Insane | 13 | 17-19 | 76.5% - 68.4%| 8-9 | 162.5% - 144.4% | -| Insane | 14 | 20-22 | 70% - 63.6% | 10-11 | 140% - 127.3% | -| Insane | 15 | 23-26 | 65.2% - 57.7%| 11-13 | 136.4% - 115.4% | -| Insane | 16 | 27-29 | 59.3% - 55.2%| 13-14 | 123.1% - 114.3% | -| Insane | 17 | 30-33 | 56.7% - 51.5%| 15-16 | 113.3% - 106.2% | -| Insane | 18 | 34-37 | 52.9% - 48.6%| 17-18 | 105.9% - 100% | -| Insane | 19 | 38-41 | 50% - 46.3% | 19-20 | 100% - 95% | -| Insane | 20 | 42-45 | 47.6% - 44.4%| 21-22 | 95.2% - 90.9% | -| Insane | 21 | 46-50 | 45.7% - 42% | 23-25 | 91.3% - 84% | -| Insane | 22 | 51-54 | 43.1% - 40.7%| 25-27 | 88% - 81.5% | -| Insane | 23 | 55-59 | 41.8% - 39% | 27-29 | 85.2% - 79.3% | -| Insane | 24 | 60-63 | 40% - 38.1% | 30-31 | 80% - 77.4% | -| Insane | 25 | 64-68 | 39.1% - 36.8%| 32-34 | 78.1% - 73.5% | -| Insane | 26 | 69-73 | 37.7% - 35.6%| 34-36 | 76.5% - 72.2% | -| Insane | 27 | 74-77 | 36.5% - 35.1%| 37-38 | 73% - 71.1% | -| Insane | 28 | 78-82 | 35.9% - 34.1%| 39-41 | 71.8% - 68.3% | -| Insane | 29 | 83-87 | 34.9% - 33.3%| 41-43 | 70.7% - 67.4% | -| Insane | 30 | 88-92 | 34.1% - 32.6%| 44-46 | 68.2% - 65.2% | -| Insane | 31 | 93-97 | 33.3% - 32% | 46-48 | 67.4% - 64.6% | -| Paranoid | 19 | 1 | 1900% | | | -| Paranoid | 23 | 2 | 1150% | 1 | 2300% | -| Paranoid | 26 | 3 | 866.7% | 1 | 2600% | -| Paranoid | 29 | 4 | 725% | 2 | 1450% | -| Paranoid | 31 | 5 | 620% | 2 | 1550% | -| Paranoid | 34 | 6 | 566.7% | 3 | 1133.3% | -| Paranoid | 36 | 7 | 514.3% | 3 | 1200% | -| Paranoid | 38 | 8 | 475% | 4 | 950% | -| Paranoid | 40 | 9 | 444.4% | 4 | 1000% | -| Paranoid | 43 | 10 | 430% | 5 | 860% | -| Paranoid | 45 | 11 | 409.1% | 5 | 900% | -| Paranoid | 47 | 12 | 391.7% | 6 | 783.3% | -| Paranoid | 48 | 13 | 369.2% | 6 | 800% | -| Paranoid | 50 | 14 | 357.1% | 7 | 714.3% | -| Paranoid | 52 | 15 | 346.7% | 7 | 742.9% | -| Paranoid | 54 | 16 | 337.5% | 8 | 675% | -| Paranoid | 56 | 17 | 329.4% | 8 | 700% | -| Paranoid | 58 | 18 | 322.2% | 9 | 644.4% | -| Paranoid | 59 | 19 | 310.5% | 9 | 655.6% | -| Paranoid | 61 | 20 | 305% | 10 | 610% | -| Paranoid | 63 | 21 | 300% | 10 | 630% | -| Paranoid | 65 | 22 | 295.5% | 11 | 590.9% | -| Paranoid | 66 | 23 | 287% | 11 | 600% | -| Paranoid | 68 | 24 | 283.3% | 12 | 566.7% | -| Paranoid | 70 | 25 | 280% | 12 | 583.3% | -| Paranoid | 71 | 26 | 273.1% | 13 | 546.2% | -| Paranoid | 73 | 27 | 270.4% | 13 | 561.5% | -| Paranoid | 75 | 28 | 267.9% | 14 | 535.7% | -| Paranoid | 76 | 29 | 262.1% | 14 | 542.9% | -| Paranoid | 78 | 30 | 260% | 15 | 520% | -| Paranoid | 80 | 31 | 258.1% | 15 | 533.3% | -| Paranoid | 81 | 32 | 253.1% | 16 | 506.2% | -| Paranoid | 83 | 33 | 251.5% | 16 | 518.8% | -| Paranoid | 84 | 34 | 247.1% | 17 | 494.1% | -| Paranoid | 86 | 35 | 245.7% | 17 | 505.9% | -| Paranoid | 87 | 36 | 241.7% | 18 | 483.3% | -| Paranoid | 89 | 37 | 240.5% | 18 | 494.4% | - -Let's take our previous example and adjust it slightly. Instead of a source data consisting of 321 (3 * 107) chunks, we will add 19 chunks for a total of 340 chunks. Looking at our chart, we can see that at the Strong level for 19 data chunks we need 9 parity chunks. From this we can calculate the final percentage price: 72 / 340 = 21.17%. \ No newline at end of file diff --git a/docs/learn/technology/what-is-swarm.md b/docs/learn/what-is-swarm.md similarity index 100% rename from docs/learn/technology/what-is-swarm.md rename to docs/learn/what-is-swarm.md diff --git a/docusaurus.config.js b/docusaurus.config.js index f98def80a..fd2688128 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -125,7 +125,7 @@ module.exports = { label: 'Introduction', }, { - to: 'docs/learn/technology/what-is-swarm', + to: 'docs/learn/what-is-swarm', label: 'Technology', }, { diff --git a/sidebars.js b/sidebars.js index 855ec2283..ee64a1a95 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1,12 +1,23 @@ module.exports = { learn: [ 'learn/introduction', + 'learn/what-is-swarm', + + { + type: 'category', + label: 'DISC Storage', + items: [ + 'learn/DISC/disc', + 'learn/DISC/erasure-cost-calculation', + 'learn/DISC/neighbourhoods', + + ], + collapsed: false + }, { type: 'category', label: 'Technology', items: [ - 'learn/technology/what-is-swarm', - 'learn/technology/disc', 'learn/technology/incentives', 'learn/technology/pss', 'learn/technology/act', @@ -38,15 +49,6 @@ module.exports = { ], collapsed: false }, - { - type: 'category', - label: 'Advanced', - items: [ - 'learn/advanced/erasure-cost-calculation', - 'learn/advanced/neighbourhoods', - ], - collapsed: false - }, 'learn/tokens', 'learn/glossary', 'learn/faq', From 2a9ec9bc5362008e7891a887986b391f9132cc48 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Tue, 15 Oct 2024 02:44:35 +0700 Subject: [PATCH 02/25] learn section, erasure code sections revisions --- .../access-the-swarm/erasure-coding.md | 23 ++-- docs/learn/DISC/erasure-coding.md | 127 +++++++++++++++-- docs/learn/DISC/neighbourhoods.md | 9 +- sidebars.js | 2 +- src/components/parities.js | 11 +- src/components/paritiesEncrypted.js | 128 +++++++++--------- 6 files changed, 208 insertions(+), 92 deletions(-) diff --git a/docs/develop/access-the-swarm/erasure-coding.md b/docs/develop/access-the-swarm/erasure-coding.md index 737b49249..44c230e21 100644 --- a/docs/develop/access-the-swarm/erasure-coding.md +++ b/docs/develop/access-the-swarm/erasure-coding.md @@ -5,7 +5,7 @@ id: erasure-coding import RedundancyCalc from '@site/src/components/RedundancyCalc.js'; -Erasure coding is a powerful method for safeguarding data, offering robust protection against partial data loss. This technique involves dividing the original data into multiple fragments and generating extra parity fragments to introduce redundancy. A key advantage of erasure coding is its ability to recover the complete original data even if some fragments are lost. Additionally, it offers the flexibility to customize the level of data loss protection, making it a versatile and reliable choice for preserving data integrity on Swarm. For a more in depth dive into erasure coding on Swarm, see the [erasure coding paper](https://papers.ethswarm.org/p/erasure/) from the Swarm research team. +[Erasure coding](/docs/learn/DISC/erasure-coding) is a powerful method for safeguarding data, offering robust protection against partial data loss. This technique involves dividing the original data into multiple fragments and generating extra parity fragments to introduce redundancy. A key advantage of erasure coding is its ability to recover the complete original data even if some fragments are lost. Additionally, it offers the flexibility to customize the level of data loss protection, making it a versatile and reliable choice for preserving data integrity on Swarm. For a more in depth dive into erasure coding on Swarm, see the [erasure coding paper](https://papers.ethswarm.org/p/erasure/) from the Swarm research team. ## Uploading With Erasure Coding @@ -27,15 +27,14 @@ To upload data to Swarm using erasure coding, the `swarm-redundancy-level: Swarm address: `da7e5cc3ed9a46b6e7491d3bf738535d98112641380cbed2e9ddfe4cf4fc01c4` -Neighbourhoods are formed by nodes which share a certain number of leading bits in their addresses. The number of leading bits is variable and can increase as more data enters the network, this number is referred to as the [storage depth](/docs/learn/glossary#2b-storage-depth) (the term "radius" is sometimes used as an alternative to depth, so you may see "storage radius" in some documentation). Nodes in a neighbourhood are responsible for storing chunks which share the same leading bits with the nodes in the neighbourhood up to the storage depth. +Neighbourhoods are formed by nodes which share a certain number of leading bits in their addresses. The number of leading bits is variable and can increase as more data enters the network, this number is referred to as the [storage depth](/docs/learn/glossary#2b-storage-depth). Nodes in a neighbourhood are responsible for storing chunks which share the same leading bits with the nodes in the neighbourhood up to the storage depth. ### Example Neighbourhood diff --git a/sidebars.js b/sidebars.js index ee64a1a95..7c706389d 100644 --- a/sidebars.js +++ b/sidebars.js @@ -8,7 +8,7 @@ module.exports = { label: 'DISC Storage', items: [ 'learn/DISC/disc', - 'learn/DISC/erasure-cost-calculation', + 'learn/DISC/erasure-coding', 'learn/DISC/neighbourhoods', ], diff --git a/src/components/parities.js b/src/components/parities.js index 603c59fc5..1f2701329 100644 --- a/src/components/parities.js +++ b/src/components/parities.js @@ -5,10 +5,10 @@ let parities = [ 2, // For chunk 1 3, 3, 3, 3, // For chunks 2-5 4, 4, 4, 4, 4, 4, 4, 4, 4, // For chunks 6-14 - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // For chunks 15-28 + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // For chunks 15-28 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // For chunks 29-46 - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // For chunks 47-68 - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // For chunks 69-94 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // For chunks 47-68 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // For chunks 69-94 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 // For chunks 95-119 ], // Strong @@ -28,7 +28,7 @@ let parities = [ 16, 16, 16, 16, 16, 16, 16, 16, // For chunks 62-69 17, 17, 17, 17, 17, 17, 17, 17, // For chunks 70-77 18, 18, 18, 18, 18, 18, 18, 18, 18, // For chunks 78-86 - 19, 19, 19, 19, 19, 19, 19, 19, // For chunks 87-95 + 19, 19, 19, 19, 19, 19, 19, 19, 19, // For chunks 87-95 20, 20, 20, 20, 20, 20, 20, 20, 20, // For chunks 96-104 21, 21, 21, // For chunks 105-107 ], @@ -100,7 +100,8 @@ let parities = [ 84, // For chunk 34 86, // For chunk 35 87, // For chunk 36 - 89 // For chunk 37 + 89, // For chunk 37 + 90 // For chunk 38 ] ] diff --git a/src/components/paritiesEncrypted.js b/src/components/paritiesEncrypted.js index 7b1a2237d..d5e822deb 100644 --- a/src/components/paritiesEncrypted.js +++ b/src/components/paritiesEncrypted.js @@ -2,7 +2,7 @@ let parities = [ // Medium [ - 3, 3, // For chunks 1-2 + 3, 3, // For chunks 1-2 4, 4, 4, 4, 4, // For chunks 3-7 5, 5, 5, 5, 5, 5, 5, // For chunks 8-14 6, 6, 6, 6, 6, 6, 6, 6, 6, // For chunks 15-23 @@ -12,76 +12,74 @@ let parities = [ ], // Strong [ - 5, - 6, 6, - 7, 7, - 8, 8, - 9, 9, 9, - 10, 10, 10, 10, - 11, 11, 11, - 12, 12, 12, - 13, 13, 13, 13, - 14, 14, 14, - 15, 15, 15, 15, - 16, 16, 16, 16, - 17, 17, 17, 17, - 18, 18, 18, 18, 18, - 19, 19, 19, 19, - 20, 20, 20, 20, 20, - 21 + 5, // For chunk 1 + 6, 6, // For chunks 2-3 + 7, 7, // For chunks 4-5 + 8, 8, // For chunks 6-7 + 9, 9, 9, // For chunks 8-10 + 10, 10, 10, // For chunks 11-13 + 11, 11, 11, // For chunks 14-16 + 12, 12, 12, // For chunks 17-19 + 13, 13, 13, 13, // For chunks 20-23 + 14, 14, 14, // For chunks 24-26 + 15, 15, 15, 15, // For chunks 27-30 + 16, 16, 16, 16, // For chunks 31-34 + 17, 17, 17, 17, // For chunks 35-38 + 18, 18, 18, 18, 18, // For chunks 39-43 + 19, 19, 19, 19, // For chunks 44-47 + 20, 20, 20, 20, 20, // For chunks 48-52 + 21 // For chunk 53 ], // Insane [ - 6, - 8, - 9, 9, - 10, - 11, - 12, 12, - 13, 14, - 14, - 15, 15, - 16, - 17, 17, - 18, 18, - 19, 19, - 20, 20, - 21, 21, 21, - 22, 22, - 23, 23, - 24, 24, - 25, 25, 25, - 26, 26, - 27, 27, - 28, 28, 28, - 29, 29, - 30, 30, 30, - 31, 31 + 6, // For chunk 1 + 8, // For chunk 2 + 9, 9, // For chunks 3-4 + 10, // For chunk 5 + 11, // For chunk 6 + 12, 12, // For chunks 7-8 + 13, // For chunk 9 + 14, 14, // For chunks 10-11 + 15, 15, // For chunks 12-13 + 16, // For chunk 14 + 17, 17, // For chunks 15-16 + 18, 18, // For chunks 17-18 + 19, 19, // For chunks 19-20 + 20, 20, // For chunks 21-22 + 21, 21, 21, // For chunks 23-25 + 22, 22, // For chunks 26-27 + 23, 23, // For chunks 28-29 + 24, 24, // For chunks 30-31 + 25, 25, 25, // For chunks 32-34 + 26, 26, // For chunks 35-36 + 27, 27, // For chunks 37-38 + 28, 28, 28, // For chunks 39-41 + 29, 29, // For chunks 42-43 + 30, 30, 30, // For chunks 44-46 + 31, 31 // For chunks 47-48 ], // Paranoid [ - 23, - 29, - 34, - 38, - 43, - 47, - 50, - 54, - 58, - 61, - 65, - 68, - 71, - 75, - 78, - 81, - 84, - 87 - ] - - - + 23, // For chunk 1 + 29, // For chunk 2 + 34, // For chunk 3 + 38, // For chunk 4 + 43, // For chunk 5 + 47, // For chunk 6 + 50, // For chunk 7 + 54, // For chunk 8 + 58, // For chunk 9 + 61, // For chunk 10 + 65, // For chunk 11 + 68, // For chunk 12 + 71, // For chunk 13 + 75, // For chunk 14 + 78, // For chunk 15 + 81, // For chunk 16 + 84, // For chunk 17 + 87, // For chunk 18 + 90 // For chunk 19 + ] ] export default parities From b5a39cceac2d6c6c8e8fef78aa23ff82b88b8922 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Wed, 16 Oct 2024 02:46:10 +0700 Subject: [PATCH 03/25] erasure coding page edits, added kademlia page --- docs/learn/DISC/erasure-coding.md | 37 +++++++++++-------------------- docs/learn/DISC/kademlia.md | 33 +++++++++++++++++++++++++++ sidebars.js | 2 +- 3 files changed, 47 insertions(+), 25 deletions(-) create mode 100644 docs/learn/DISC/kademlia.md diff --git a/docs/learn/DISC/erasure-coding.md b/docs/learn/DISC/erasure-coding.md index 74c9248d1..0efa52f8e 100644 --- a/docs/learn/DISC/erasure-coding.md +++ b/docs/learn/DISC/erasure-coding.md @@ -3,42 +3,20 @@ title: Erasure Coding id: erasure-coding --- -# Erasure Coding on Swarm - Erasure coding (also known as erasure code) is an efficient and flexible approach to data protection which is an optional feature for Swarm uploads. It is a technique that increases data protection by enabling the recovery of original data even when some encoded chunks are lost or corrupted. When used, it ensures that data on Swarm can always be accessed reliably, even if some nodes or entire neighborhoods go offline. -### How It Works +## How It Works Erasure coding enhances data protection by dividing the source data into "chunks" and adding additional redundant chunks. Specifically, data is divided into **m** chunks, and **k** additional chunks are generated, resulting in **m + k** total chunks. The data is encoded across these chunks such that as long as **m** chunks are intact, the original data can be fully reconstructed. Chunks are then distributed across the network as with a standard upload. This approach provides a robust method for data recovery in distributed storage networks like Swarm. -### Adjustability - -Erasure coding parameters can be adjusted in order to increase the strength of data protection, with a corresponding - - -#### Example +### Example For an 8KB image, if we set **m = 2** and **k = 1**, we create 3 chunks (2 original + 1 redundant). As long as any 2 of these 3 chunks are available, we can reconstruct the original data. By increasing **k** to 4, we can tolerate the loss of up to 4 chunks while still recovering the original data. ![Erasure Code Example](https://www.ethswarm.org/uploads/erasure-coding-01.png) -### General Benefits of Erasure Coding - -1. **Cost-Effective**: Erasure coding achieves data protection similar to or better than replication while requiring less storage space. - -2. **Tolerates Data Loss**: Unlike traditional error correction codes, erasure coding can withstand the total loss of specific chunks, making it suitable for scenarios where data integrity is critical. - -## Implications for Swarm - -### Tailored Data Protection for Swarm - -Swarm’s existing architecture, which naturally divides files into chunks and distributes them across a network of nodes, aligns perfectly with the principles of erasure coding. This addition enhances the resilience of data storage, ensuring that data remains accessible even if entire neighborhoods of nodes go offline. - -### Enabling New Use-Cases - -The integration of erasure coding significantly increases the durability of data stored on Swarm. It allows Swarm to cater to enterprise users and scenarios requiring enhanced data protection, serving as insurance against potential outages or attacks. ## Usage @@ -46,6 +24,17 @@ For usage instructions, see the [erasure coding page in the "Develop" section](/ ## Cost Calculation +In Swarm's implementation of erasure coding, there are four levels of protection: Medium, Strong, Insane, and Paranoid. Each level adds additional parity chunks for a corresponding increase in data protection (and also cost). + +The table below shows the number of parities and data chunks for each level, as well as the percent increase in cost vs a non-erasure coded upload. + +| Redundancy | Parities | Data Chunks | Percent | Chunks Encrypted | Percent Encrypted | +|----------|----------|--------|---------|------------------|-------------------| +| Medium | 9 | 119 | *7.6%* | 59 | 15% | +| Strong | 21 | 107 | *19.6%* | 53 | 40% | +| Insane | 31 | 97 | 32% | 48 | 65% | +| Paranoid | 90 | 38 | 240.5% | 18 | 494% | + For each redundancy level, there are **m + k** = 128 chunks, where **m** are the data chunks (shown in column "Data Chunks") and **k** are the parity chunks (shown in column "Parities"). If the number of chunks in the data being uploaded are an exact multiple of **m**, then the percent cost of the upload will simply equal the one shown in the chart above in the "Percent" column for the corresponding redundancy level. #### Exact Multiples diff --git a/docs/learn/DISC/kademlia.md b/docs/learn/DISC/kademlia.md new file mode 100644 index 000000000..d77d0bca0 --- /dev/null +++ b/docs/learn/DISC/kademlia.md @@ -0,0 +1,33 @@ +--- +title: Kademlia +id: kademlia +--- + +Kademlia is a distributed hash table (DHT) algorithm used for decentralized peer-to-peer networks. It enables efficient storage and retrieval of data by using an overlay network which enforces a specific connectivity pattern. Kademlia is based on a binary tree structure that provides a way for nodes in the network to organize themselves and locate data or other nodes efficiently without relying on a centralized authority. + +Swarm employs a modified version of Kademlia to manage the routing of data between nodes in the network. + +Key features of Kademlia include: + +1. **XOR Distance Metric**: Kademlia uses a unique distance metric based on the XOR (exclusive OR) between any addresses. This allows nodes to calculate "distance" from each other. Lookups are made by recursively querying nodes that are progressively closer to the target. Swarm also introduces the related concept of proximity order (PO), which is a measure of relatedness of any two addresses on a discrete scale. + +2. **Routing Table**: Each node in a Kademlia network maintains a routing table containing information about other nodes, organized by the XOR distance between node IDs. In Swarm, these nodes are divided into PO buckets, each containing a list of nodes at different PO levels. + +3. **Efficient Lookup**: Efficient Lookup: To retrieve a specific chunk, a node uses Kademlia's lookup process to find and fetch the chunk from a node in the neighborhood where it is stored. + +:::info +Kademlia comes in two flavors, iterative and forwarding. In iterative Kademlia, the requesting node directly queries each node it contacts for nodes that are progressively closer to the target until the node with the requested chunk is found. The chunk is then sent directly from the storer node to the node which initiated the request. + +In contrast, Swarm makes use of forwarding Kademlia. Here each node forwards the query to the next closest node in the network, and this process continues until a node with the requested chunk is found. Once the chunk is found, it is sent back along the same chain of nodes rather than sent directly to the initiator of the request. + +The main advantage of forwarding Kademlia is that it maintains the anonymity of the node which initiated the request. +::: + +4. **Fault Tolerance**: Kademlia is resilient to node failures. Because nodes are regularly refreshed through lookups and interactions, the network remains functional even when individual nodes leave or fail. + +5. **Scalability**: Kademlia's design allows it to scale to large networks, as each node only needs to keep track of a small subset of the total nodes in the network. The required set of connected peers grows logarithmically with the number of nodes, making it efficient even in large networks. + + +:::info +For a more in depth understanding of Swarm's specific implementation of Kademlia, refer to the second chapter of [The Book of Swarm](https://www.ethswarm.org/the-book-of-swarm-2.pdf). +::: \ No newline at end of file diff --git a/sidebars.js b/sidebars.js index 7c706389d..64a057636 100644 --- a/sidebars.js +++ b/sidebars.js @@ -10,7 +10,7 @@ module.exports = { 'learn/DISC/disc', 'learn/DISC/erasure-coding', 'learn/DISC/neighbourhoods', - + 'learn/DISC/kademlia', ], collapsed: false }, From b71e53e72ed145edd063d478d7c92fa722b055f7 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Wed, 16 Oct 2024 02:48:57 +0700 Subject: [PATCH 04/25] Remove repetitive language in PO glossary description --- docs/learn/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/learn/glossary.md b/docs/learn/glossary.md index e8083ce42..80dc2281a 100644 --- a/docs/learn/glossary.md +++ b/docs/learn/glossary.md @@ -88,7 +88,7 @@ When data is uploaded to Swarm, it is broken down into 4kb sized pieces which ar ## Proximity Order (PO) -Proximity Order is a concept defined in The Book of Swarm and is closely related to Kademlia distance. Proximity order is defined as the number of shared prefix bits of any two addresses. It is found by performing the XOR bitwise operation on the two addresses and counting how many leading 0 there are before the first 1. In other words, PO is equal to the number of shared binary prefix bits. +Proximity Order is a concept defined in The Book of Swarm and is closely related to Kademlia distance. Proximity order is defined as the number of shared prefix bits of any two addresses. It is found by performing the XOR bitwise operation on the two addresses and counting how many leading 0 there are before the first 1. Taking the previous example used in the Kademlia distance definition: From 842d4beb5b520f38456b824ec80e47a1ce50b50c Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Thu, 17 Oct 2024 16:20:50 +0700 Subject: [PATCH 05/25] edits to learn section --- docs/learn/DISC/DISC.md | 3 +- docs/learn/DISC/kademlia.md | 52 +++++++++++++++++++++++-------- docs/learn/DISC/neighbourhoods.md | 7 +++-- docs/learn/glossary.md | 17 ++++++---- sidebars.js | 4 +-- 5 files changed, 57 insertions(+), 26 deletions(-) diff --git a/docs/learn/DISC/DISC.md b/docs/learn/DISC/DISC.md index 09d4d8db7..d7e357ffe 100644 --- a/docs/learn/DISC/DISC.md +++ b/docs/learn/DISC/DISC.md @@ -7,7 +7,7 @@ DISC (Distributed Immutable Storage of Chunks) is a storage solution developed b ### Kademlia Topology and Routing -Each node within Swarm connects to a certain number of its peers. When a chunk is first inserted into the network, the uploading node will send it to the peer which is closest (as measured by [proximity order](/docs/learn/glossary#proximity-order-po), which is based on a measure of [Kademlia distance](/docs/learn/glossary#kademlia-distance)) to the destination of the chunk, and then the recipient node will then forward the chunk on to its peer which is closest to the destination of the chunk, and so on until the chunk arrives at its destination. +[Kademlia](/docs/learn/DISC/kademlia) is a distributed hash table (DHT) widely used in peer-to-peer networks such as Ethereum and Bittorent. It serves as the routing and topology foundation for communication between nodes in the Swarm netowrk. It organizes nodes based on their overlay addresses and ensures that messages are relayed efficiently, even in a dynamic, decentralized environment. One of the advantages of using Kademlia as a model for network topology is that both the number of forwarding "hops" required to route a chunk to its destination and the number of peer connections required to maintain Kademlia topology are logarithmic to the size of the network (a minimum of two connections is required in order to maintain Kademlia topology in case of network churn - nodes dropping in and out of the network). This makes Swarm a highly scalable system which is efficient even at very large scales. @@ -30,4 +30,3 @@ For single-owner chunks on the other hand, the address is calculated as the hash When a file is first uploaded to Swarm, it gets broken down by the uploading Bee node chunks which are then distributed amongst other Bee nodes in the Swarm network. Chunks get distributed to the target neighborhood by the ***push-sync*** protocol. Once a chunk reaches its destination, it will then be duplicated and synced to other nodes in order to achieve data redundancy through the ***pull-sync*** protocol. The pull-sync protocol operates continuously as nodes enter or exit the network – ensuring that data redundancy is always maintained. When a client node requests a file for download, its request gets forwarded by the ***retrieval-protocol*** to all the nodes storing the relevant chunks, and then those chunks get returned to the requesting node and the file gets reconstructed from its constituent chunks. - diff --git a/docs/learn/DISC/kademlia.md b/docs/learn/DISC/kademlia.md index d77d0bca0..38c2871cf 100644 --- a/docs/learn/DISC/kademlia.md +++ b/docs/learn/DISC/kademlia.md @@ -3,31 +3,57 @@ title: Kademlia id: kademlia --- -Kademlia is a distributed hash table (DHT) algorithm used for decentralized peer-to-peer networks. It enables efficient storage and retrieval of data by using an overlay network which enforces a specific connectivity pattern. Kademlia is based on a binary tree structure that provides a way for nodes in the network to organize themselves and locate data or other nodes efficiently without relying on a centralized authority. -Swarm employs a modified version of Kademlia to manage the routing of data between nodes in the network. +Kademlia is a distributed hash table (DHT) algorithm used in peer-to-peer networks to efficiently store and retrieve data without relying on centralized servers. It organizes nodes into an overlay network that ensures efficient routing using a binary tree structure. + +## Kademlia Core Concepts Key features of Kademlia include: -1. **XOR Distance Metric**: Kademlia uses a unique distance metric based on the XOR (exclusive OR) between any addresses. This allows nodes to calculate "distance" from each other. Lookups are made by recursively querying nodes that are progressively closer to the target. Swarm also introduces the related concept of proximity order (PO), which is a measure of relatedness of any two addresses on a discrete scale. +### **XOR Distance Metric**: +Kademlia uses a unique distance metric based on the XOR (exclusive OR) between any addresses. This allows nodes to calculate "distance" from each other. Lookups are made by recursively querying nodes that are progressively closer to the target. + +### **Routing Table**: +Each node in a Kademlia network maintains a routing table containing information about other nodes, organized by the XOR distance between node IDs. + +### **Efficient Lookups**: + +To retrieve a specific chunk, a node uses Kademlia's lookup process to find and fetch the chunk from a node in the neighborhood where it is stored. The number of hops required for a chunk to be retrieved is logarithmic to the number of nodes in the network, meaning lookups remain efficient even as the network grows larger and larger. + + +### **Fault Tolerance**: +Kademlia is resilient to node failures. Because nodes are regularly refreshed through lookups and interactions, the network remains functional even when individual nodes leave or fail. -2. **Routing Table**: Each node in a Kademlia network maintains a routing table containing information about other nodes, organized by the XOR distance between node IDs. In Swarm, these nodes are divided into PO buckets, each containing a list of nodes at different PO levels. +### **Scalability**: +Kademlia's design allows it to scale to large networks, as each node only needs to keep track of a small subset of the total nodes in the network. The required set of connected peers grows logarithmically with the number of nodes, making it efficient even in large networks. -3. **Efficient Lookup**: Efficient Lookup: To retrieve a specific chunk, a node uses Kademlia's lookup process to find and fetch the chunk from a node in the neighborhood where it is stored. :::info +For a more in depth understanding of Swarm's specific implementation of Kademlia, refer to the second chapter of [The Book of Swarm](https://www.ethswarm.org/the-book-of-swarm-2.pdf). +::: + +## Kademlia in Swarm + +As mentioned above, Swarm's version of Kademlia differs from commonly used implementations of Kademlia in several important ways: + +### Proximity Order & Neighborhoods + +Swarm introduces the concept of [proximity order (PO)](/docs/learn/glossary#proximity-order-po) as a discrete measure of node relatedness between two addresses. In contrast with Kademlia distance which is an exact measure of relatedness, PO is a more general measure of the relatedness between two addresses based on their number of shared leading bits. + + + +In Swarm's version of Kademlia, nodes are grouped into neighborhoods of nodes based on PO (ie., all nodes in a neighborhood share the same leading binary prefix bits). Each neighborhood of nodes is responsible for storing the same set of chunks. + +Neighborhoods are important for ensuring data redundancy, and they also play a role in the incentives system which guarantees nodes are rewarded for contributing resources to the network. + +### Forwarding Kademlia + Kademlia comes in two flavors, iterative and forwarding. In iterative Kademlia, the requesting node directly queries each node it contacts for nodes that are progressively closer to the target until the node with the requested chunk is found. The chunk is then sent directly from the storer node to the node which initiated the request. In contrast, Swarm makes use of forwarding Kademlia. Here each node forwards the query to the next closest node in the network, and this process continues until a node with the requested chunk is found. Once the chunk is found, it is sent back along the same chain of nodes rather than sent directly to the initiator of the request. The main advantage of forwarding Kademlia is that it maintains the anonymity of the node which initiated the request. -::: - -4. **Fault Tolerance**: Kademlia is resilient to node failures. Because nodes are regularly refreshed through lookups and interactions, the network remains functional even when individual nodes leave or fail. - -5. **Scalability**: Kademlia's design allows it to scale to large networks, as each node only needs to keep track of a small subset of the total nodes in the network. The required set of connected peers grows logarithmically with the number of nodes, making it efficient even in large networks. +### Incentives -:::info -For a more in depth understanding of Swarm's specific implementation of Kademlia, refer to the second chapter of [The Book of Swarm](https://www.ethswarm.org/the-book-of-swarm-2.pdf). -::: \ No newline at end of file +Swarm introduces an incentives layer on top of its Kademlia implementation in order to reward nodes for continuing to provide resources to the network. \ No newline at end of file diff --git a/docs/learn/DISC/neighbourhoods.md b/docs/learn/DISC/neighbourhoods.md index f7a7d5326..2752fbca7 100644 --- a/docs/learn/DISC/neighbourhoods.md +++ b/docs/learn/DISC/neighbourhoods.md @@ -3,7 +3,8 @@ title: Neighbourhoods id: neighbourhoods --- -A neighbourhood is a group of nodes which are responsible for storing the same chunks of data. The nodes which make up a neighbourhood and the chunks which they are responsible for storing are determined by the node and chunk addresses. Node and chunk addresses follow the same format of a 256 bit hexadecimal number. +In Swarm, a neighbourhood refers to an area of responsibility within the network, where nodes in proximity to one another share the task of storing and maintaining data chunks. It is defined by the [proximity order (PO)](/docs/learn/glossary#proximity-order-po) of nodes' addresses. Nodes within a neighbourhood replicate data chunks to ensure that if one node goes offline, other nodes in the neighbourhood can still retrieve and serve the content. + :::info To see neighborhood populations and the current storage depth / storage radius navigate to the ["Neighborhoods" page of Swarmscan.io](https://swarmscan.io/neighborhoods). @@ -13,9 +14,9 @@ To see neighborhood populations and the current storage depth / storage radius n The terms "depth" and "radius" are often used interchangeably when discussing neighborhoods. Both refer to number of shared leading bits of node and chunk addresses used to determine the nodes and chunks which fall into which neighborhoods. ::: -> Swarm address: `da7e5cc3ed9a46b6e7491d3bf738535d98112641380cbed2e9ddfe4cf4fc01c4` +## Neighborhood Formation -Neighbourhoods are formed by nodes which share a certain number of leading bits in their addresses. The number of leading bits is variable and can increase as more data enters the network, this number is referred to as the [storage depth](/docs/learn/glossary#2b-storage-depth). Nodes in a neighbourhood are responsible for storing chunks which share the same leading bits with the nodes in the neighbourhood up to the storage depth. +A Swarm neighborhood is determined by the [proximity order (PO)](/docs/learn/glossary#proximity-order-po) of node addresses, which is calculated based on the number of leading bits shared between the addresses of nodes in the network. Nodes in the same neighborhood share the same prefix of their addresses, and the neighborhood expands or contracts depending on the availability of nearby nodes. As a result, each node is responsible for interacting with other nodes within its neighborhood to store and replicate data chunks, ensuring data availability and redundancy. The neighborhood depth dynamically adjusts as peers join or leave the network, maintaining a healthy distribution of storage responsibility across nodes. ### Example Neighbourhood diff --git a/docs/learn/glossary.md b/docs/learn/glossary.md index 80dc2281a..582cf69b3 100644 --- a/docs/learn/glossary.md +++ b/docs/learn/glossary.md @@ -66,12 +66,17 @@ Kademlia is a distributed hash table (DHT) which is commonly used in distributed ## Kademlia distance -Within Kademlia, nodes have numeric ids with the same length and format taken from the same namespace as the keys of the key/value pairs. Kademlia distance between node ids and keys is calculated through the XOR bitwise operation done over any ids or keys. - +Kademlia introduces an XOR based distance metric to define the relatedness of two addresses. In Kademlia nodes have numeric ids with the same length and format taken from the same namespace as the keys of the key/value pairs. Kademlia distance between node ids and keys is calculated through the XOR bitwise operation done over any ids or keys. Note: For a Kademlia DHT, any standardized numerical format can be used for ids. However, within Swarm, ids are derived from a Keccak256 digest and are represented as 256 bit hexadecimal numbers. They are referred to as addresses or hashes. -Example: We have a Kademlia DHT consisting of only ten nodes with ids of 1 - 10. We want to find the distance between node 4 and 7. In order to do that, we perform the XOR bitwise operation: +Swarm hash: + +> eada6722670c6de6da7d0470167bf14f6e4dc1b98476da94a7330041adec26a3 + +In the examples which follow, we use short binary numbers to increase example clarity rather than the actual Swarm hash format. + +Example: We have a Kademlia DHT consisting of only ten nodes with ids of 1 - 10. We want to find the distance between node 4 and 7. In order to do that, we perform the XOR bitwise operation: 4 | 0100 7 | 0111 @@ -80,15 +85,15 @@ Example: We have a Kademlia DHT consisting of only ten nodes with ids of 1 - 1 And we find that the distance between the two nodes is 3. - - ## Chunk When data is uploaded to Swarm, it is broken down into 4kb sized pieces which are each assigned an address in the same format as node’s overlay addresses. Chunk addresses are formed by taking the BMT hash of the chunk content along with an 8 byte measure of the number of the chunk’s child chunks, the `span`. The BMT hashing algorithm is based on the Keccac256 hashing algorithm, so it produces an address with the same format as that for the node overlay addresses. ## Proximity Order (PO) -Proximity Order is a concept defined in The Book of Swarm and is closely related to Kademlia distance. Proximity order is defined as the number of shared prefix bits of any two addresses. It is found by performing the XOR bitwise operation on the two addresses and counting how many leading 0 there are before the first 1. +Proximity Order is a concept defined in The Book of Swarm and is closely related to Kademlia distance. In contrast to distance which is an exact measure of the relatedness of two nodes, PO is a discrete measure relatedness between two nodes. By "discrete", we mean that PO is a general measure of relatedness rather than an exact measure of relatedness like the XOR distance metric of Kademlia. + +Proximity order is defined as the number of shared prefix bits of any two addresses. It is found by performing the XOR bitwise operation on the two addresses and counting how many leading 0 there are before the first 1. Taking the previous example used in the Kademlia distance definition: diff --git a/sidebars.js b/sidebars.js index 64a057636..a3943fc19 100644 --- a/sidebars.js +++ b/sidebars.js @@ -8,9 +8,9 @@ module.exports = { label: 'DISC Storage', items: [ 'learn/DISC/disc', - 'learn/DISC/erasure-coding', - 'learn/DISC/neighbourhoods', 'learn/DISC/kademlia', + 'learn/DISC/neighbourhoods', + 'learn/DISC/erasure-coding', ], collapsed: false }, From b598d119cdf9cdb90f4de8bbfabd0ea2c8c26581 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Wed, 23 Oct 2024 02:38:44 +0700 Subject: [PATCH 06/25] reorganizing Learn section --- .../access-the-swarm/erasure-coding.md | 41 +-- docs/learn/DISC/erasure-coding.md | 43 +++- docs/learn/DISC/kademlia.md | 6 +- docs/learn/incentives/chequebook.md | 19 ++ docs/learn/incentives/incentives.md | 61 +++++ docs/learn/incentives/postage-stamp.md | 233 ++++++++++++++++++ docs/learn/incentives/price-oracle.md | 11 + 7 files changed, 372 insertions(+), 42 deletions(-) create mode 100644 docs/learn/incentives/chequebook.md create mode 100644 docs/learn/incentives/incentives.md create mode 100644 docs/learn/incentives/postage-stamp.md create mode 100644 docs/learn/incentives/price-oracle.md diff --git a/docs/develop/access-the-swarm/erasure-coding.md b/docs/develop/access-the-swarm/erasure-coding.md index 44c230e21..1c094b8e5 100644 --- a/docs/develop/access-the-swarm/erasure-coding.md +++ b/docs/develop/access-the-swarm/erasure-coding.md @@ -24,44 +24,25 @@ To upload data to Swarm using erasure coding, the `swarm-redundancy-level: +For more details of erasure coding costs, see [here](/docs/learn/DISC/erasure-coding). + ## Downloading Erasure Encoded Data For a downloader, the process for downloading a file which has been erasure encoded does not require any changes from the [normal download process](/docs/develop/access-the-swarm/upload-and-download). There are several options for adjusting the default behaviour for erasure encoded downloads, however there is no need to adjust them. @@ -117,5 +98,5 @@ However, as noted above, it is recommended to not adjust the default settings fo 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 ``` -This means that there is no need for you to inform downloaders that a file you have uploaded uses erasure coding, as even with the default download behaviour reconstruction of the source file will be attempted if any chunks are missing. +This means that there is no need to inform downloaders that a file uses erasure coding, as even with the default download behaviour reconstruction of the source file will be attempted if any chunks are missing. diff --git a/docs/learn/DISC/erasure-coding.md b/docs/learn/DISC/erasure-coding.md index 0efa52f8e..bf4433150 100644 --- a/docs/learn/DISC/erasure-coding.md +++ b/docs/learn/DISC/erasure-coding.md @@ -3,7 +3,7 @@ title: Erasure Coding id: erasure-coding --- -Erasure coding (also known as erasure code) is an efficient and flexible approach to data protection which is an optional feature for Swarm uploads. It is a technique that increases data protection by enabling the recovery of original data even when some encoded chunks are lost or corrupted. When used, it ensures that data on Swarm can always be accessed reliably, even if some nodes or entire neighborhoods go offline. +Erasure coding (also known as erasure code) is an efficient and flexible approach to data protection which is an optional feature for Swarm uploads. It is a technique that increases data protection by enabling the recovery of original data even when some encoded chunks are lost or corrupted. When used, it ensures that data on Swarm can always be accessed reliably, even if some nodes or entire neighborhoods go offline. Refer to the [official erasure coding paper](https://papers.ethswarm.org/p/erasure/) for more in depth details. ## How It Works @@ -17,6 +17,19 @@ For an 8KB image, if we set **m = 2** and **k = 1**, we create 3 chunks (2 origi ![Erasure Code Example](https://www.ethswarm.org/uploads/erasure-coding-01.png) +### Levels of Protection + +In Swarm's implementation of erasure coding, there are four named levels of protection, Medium, Strong, Insane, and Paranoid. For each level, the **m** and **k** values have been adjusted in order to meet a certain level of data protection: + +***Table A:*** +| Redundancy Level Value | Level Name | Chunk Loss Tolerance | +| ---------------- | --------- | ----------------------------------- | +| 1 | Medium | 1% | +| 2 | Strong | 5% | +| 3 | Insane | 10% | +| 4 | Paranoid | 50% | + +The "Chunk Loss Tolerance" column corresponds to the exact level of data protection for each level, and is the maximum percent of erasure coded chunks which can be lost before the original data is no longer retrievable (with 99.9999% statistical certainty). Note that this guarantee of retrievability is for the data from each 128 chunk segment, and therefore does not correspond to retrievability of a whole file. The retrievability failure rate for any individual file depends on the size of the file, and increases with the size of the file. For a detailed explanation of how to calculate the retrievability of any sized file refer to [section 3 in the erasure coding paper](https://papers.ethswarm.org/erasure-coding.pdf). ## Usage @@ -28,6 +41,7 @@ In Swarm's implementation of erasure coding, there are four levels of protection The table below shows the number of parities and data chunks for each level, as well as the percent increase in cost vs a non-erasure coded upload. +***Table B:*** | Redundancy | Parities | Data Chunks | Percent | Chunks Encrypted | Percent Encrypted | |----------|----------|--------|---------|------------------|-------------------| | Medium | 9 | 119 | *7.6%* | 59 | 15% | @@ -35,16 +49,14 @@ The table below shows the number of parities and data chunks for each level, as | Insane | 31 | 97 | 32% | 48 | 65% | | Paranoid | 90 | 38 | 240.5% | 18 | 494% | -For each redundancy level, there are **m + k** = 128 chunks, where **m** are the data chunks (shown in column "Data Chunks") and **k** are the parity chunks (shown in column "Parities"). If the number of chunks in the data being uploaded are an exact multiple of **m**, then the percent cost of the upload will simply equal the one shown in the chart above in the "Percent" column for the corresponding redundancy level. - -#### Exact Multiples +For each redundancy level, there are **m + k** = 128 chunks, where **m** are the data chunks (shown in column "Data Chunks") and **k** are the parity chunks (shown in column "Parities"). The "Percent" and "Percent Encrypted" columns show percent of "parity overhead" cost increase from using erasure coding for normal and encrypted uploads respectively. -For example, if we are uploading with the Strong redundancy level, and our source data consists of 321 (3 * 107) chunks, then we can simply use the percentage from the "Percent" column for the Strong level - 19.6% (63 parities / 321 data chunks). -#### With Remainders +### Cost Calculation for Smaller Uploads -However, generally speaking uploads will not come in exact multiples of **m**, so we need to adjust our calculations. To do so we need to use the table below which shows the number of parities for sets of chunks starting at a single chunk for each redundancy level up to the maximum number of data chunks for that level. Then we simply sum up the total parities and data chunks for the entire upload and calculate the resulting percentage. +To find the percent increase in cost for uploads of less than 128 chunks, refer to the table below: +***Table C:*** | Security | Parities | Chunks | Percent | Chunks Encrypted | Percent Encrypted | |----------|----------|--------|-------------|------------------|-------------------| | Medium | 2 | 1 | 200% | | | @@ -138,4 +150,19 @@ However, generally speaking uploads will not come in exact multiples of **m**, s | Paranoid | 87 | 36 | 241.7% | 18 | 483.3% | | Paranoid | 89 | 37 | 240.5% | 18 | 494.4% | -Let's take our previous example and adjust it slightly. Instead of a source data consisting of 321 (3 * 107) chunks, we will add 19 chunks for a total of 340 chunks. Looking at our chart, we can see that at the Strong level for 19 data chunks we need 9 parity chunks. From this we can calculate the final percentage price: 72 / 340 = 21.17%. \ No newline at end of file +### Example Cost Calculation + +For each redundancy level, there are m + k = 128 chunks, where m are the data chunks (shown in column "Data Chunks") and k are the parity chunks. If the number of chunks in the data being uploaded are an exact multiple of m, then the percent cost of the upload will simply equal the one shown in table B from the section above in the "Percent" column for the corresponding redundancy level. + +#### Exact Multiples + +For example, if we are uploading with the Strong redundancy level, and our source data consists of 321 (3 * 107) chunks, then we can simply use the percentage from the "Percent" column for the Strong level - 19.6% (63 parities / 321 data chunks). + +#### With Remainders + +However, generally speaking uploads will not come in exact multiples of m, so we need to adjust our calculations. To do so we need to use table C from the section above which shows the number of parities for sets of chunks starting at a single chunk for each redundancy level up to the maximum number of data chunks for that level. Then we simply sum up the total parities and data chunks for the entire upload and calculate the resulting percentage. + +Let's say for example we have a source file of 340 chunks which we want to upload with the Strong level of protection. Referring to table B, we see for the Strong level there are 21 parity chunks for each 107 data chunks. 340 / 107 = ~3.177, meaning our upload will have three full sets of 128 chunks where m = 107 and k = 21. The remainder can be calculated from the modulus of 340 % 107 = 19 + + +Looking at our chart, we can see that at the Strong level for 19 data chunks we need 9 parity chunks. From this we can calculate the final percentage price: 72 / 340 = 21.17%. \ No newline at end of file diff --git a/docs/learn/DISC/kademlia.md b/docs/learn/DISC/kademlia.md index 38c2871cf..b00bba01c 100644 --- a/docs/learn/DISC/kademlia.md +++ b/docs/learn/DISC/kademlia.md @@ -40,8 +40,6 @@ As mentioned above, Swarm's version of Kademlia differs from commonly used imple Swarm introduces the concept of [proximity order (PO)](/docs/learn/glossary#proximity-order-po) as a discrete measure of node relatedness between two addresses. In contrast with Kademlia distance which is an exact measure of relatedness, PO is a more general measure of the relatedness between two addresses based on their number of shared leading bits. - - In Swarm's version of Kademlia, nodes are grouped into neighborhoods of nodes based on PO (ie., all nodes in a neighborhood share the same leading binary prefix bits). Each neighborhood of nodes is responsible for storing the same set of chunks. Neighborhoods are important for ensuring data redundancy, and they also play a role in the incentives system which guarantees nodes are rewarded for contributing resources to the network. @@ -54,6 +52,6 @@ In contrast, Swarm makes use of forwarding Kademlia. Here each node forwards the The main advantage of forwarding Kademlia is that it maintains the anonymity of the node which initiated the request. -### Incentives +### Storage Incentives -Swarm introduces an incentives layer on top of its Kademlia implementation in order to reward nodes for continuing to provide resources to the network. \ No newline at end of file +Swarm introduces a storage incentives layer on top of its Kademlia implementation in order to reward nodes for continuing to provide resources to the network. Neighborhoods play a key role in the storage incentives mechanism. Storage incentives take the role of a "game" in which nodes play to win a reward for storing the correct data. Each round in the game, one neighborhood is chosen to play, and all nodes within the same neighborhood participate as a group. The nodes each compare the data they are storing with each other to make sure they are all storing the data they are responsible for, and one node is chosen to win from among the group. You can read more about how storage incentives work in the dedicated page for storage incentives. \ No newline at end of file diff --git a/docs/learn/incentives/chequebook.md b/docs/learn/incentives/chequebook.md new file mode 100644 index 000000000..4fc69d47b --- /dev/null +++ b/docs/learn/incentives/chequebook.md @@ -0,0 +1,19 @@ +--- +title: Chequebook +id: chequebook +--- + +The [chequebook contract](https://github.com/ethersphere/swap-swear-and-swindle/blob/master/contracts/ERC20SimpleSwap.sol) is a smart contract used in the [Swarm Accounting Protocol (SWAP)](/docs/learn/technology/incentives) to manage cheques that are sent between nodes on the network. The contract is responsible for keeping track of the balances of each node and ensuring that cheques are valid and can be cashed out correctly. + +When a node sends a cheque to another node, it includes a signed message that specifies the amount of xBZZ tokens being transferred and the recipient's address. The chequebook contract receives this message and verifies that it is valid by checking the signature and ensuring that the sender has enough funds to cover the transfer. + +:::caution +Settlement of cheques is not enforced by smart contract. +::: + +If the cheque is valid, the contract updates the balances of both nodes accordingly. The recipient can then cash out their xBZZ tokens by sending a transaction to the blockchain that invokes a function in the chequebook contract. This function transfers the specified amount of xBZZ tokens from the sender's account to the recipient's account. + +The chequebook contract also includes some additional features to prevent abuse. For example, it can impose limits on how much debt a node can accumulate before requiring payment. + +The chequebook contract plays an important role in ensuring that SWAP operates smoothly and fairly by providing a secure and reliable way for nodes to exchange value on the network. + diff --git a/docs/learn/incentives/incentives.md b/docs/learn/incentives/incentives.md new file mode 100644 index 000000000..756864b56 --- /dev/null +++ b/docs/learn/incentives/incentives.md @@ -0,0 +1,61 @@ +--- +title: Incentives +id: incentives +--- + +One of the key challenges in a decentralised data network is incentivising users to store data and provide bandwidth. Swarm addresses this challenge with two incentives systems, one which rewards nodes for sharing their storage space and another which rewards them for sharing bandwidth. + + +## Storage Incentives + +Swarm's storage incentives protocol is defined in depth in the [Future Proof Storage](https://www.ethswarm.org/swarm-storage-incentives.pdf) paper published by the Swarm team. + +Swarm's storage incentives are based on [postage stamps](/docs/learn/technology/contracts/postage-stamp), which serve as verifiable proof of payment associated with chunks witnessed by their owner's signature. Postage stamps signal chunks' relative importance by ascribing them with xBZZ quantity which storer nodes can use when selecting which chunks to retain and which to evict from their reserve when their reserve capacity is exceeded. + +The amount of xBZZ required for a postage stamp depends on the amount of data being stored and the duration for which it will be stored. The longer a chunk is stored, the more xBZZ is required for the postage stamp. This ensures that users are incentivised to store data for longer periods, which helps ensure that data remains available in the network. + +Storer nodes can use the xBZZ associated with postage stamps when selecting which chunks to retain and serve or garbage collect during capacity shortage. This means that popular content will be widely distributed across the network, reducing retrieval latency. + + +### Storage Incentives Details + + When someone wants to upload data to Swarm, they do so by buying [postage stamp batches](/docs/learn/technology/contracts/postage-stamp) with xBZZ. The xBZZ is collected and later redistributed to storage provider nodes to pay for their services. Which node earns the reward is determined by playing a "game". Every 152 Gnosis Chain blocks the game is played, and one node will win the accumulated xBZZ. + +The game has 3 phases, `commit`, `reveal`, and `claim`. In the `reveal` phase of a previous game, an "anchor" overlay address is randomly generated and used to determine the neighborhood for the current round. Only nodes within that neighborhood (meaning they have a certain number of [shared leading bits](/docs/learn/glossary#proximity-order-po) with the neighborhood address) may participate and have a chance to win. + +In the `commit` phase, nodes issue an on-chain transaction including an encrypted hash of the data they are storing (the unencrypted hash is known as the "reserve commitment") along with the [depth](/docs/learn/glossary#2-area-of-responsibility-related-depths) for which they are reporting. This serves as an attestation of the data they are storing without revealing any other information. + +In the `reveal` phase, each node reveals the decryption key for their encrypted hashes thereby publishing the hash. One of the nodes is chosen as the honest node, and from among the honest nodes, one node is chosen as the winner. The winner is chosen at random among the honest nodes, but it is weighted in proportion to each node's stake density. Stake density is calculated as so: + +$$ +\text{stake density} = \text{stake(xBZZ)} \times {2}^\text{storage depth} +$$ + + +### Penalties + +During the `reveal` phase if a nodes' revealed hash does not match the honest nodes' hash, that node will be temporarily frozen and will not be able to participate in a number of upcoming rounds. Currently the freeze period is defined in the [redistribution smart contract](https://github.com/ethersphere/storage-incentives/blob/master/src/Redistribution.sol#L536C1-L536C100) as: + + +$$ +152 \times 2^\text{storage radius} \text{ blocks (at 5s per block)} +$$ + +So for example at a storage radius of 10: + +$$ +152 \times 2^{10} \text{ blocks (at 5s per block)} ≈ \text{ 9 days} +$$ + + +## Bandwidth Incentives (SWAP) + +The Swarm Accounting Protocol (SWAP) is a protocol used in the Swarm network to manage the exchange of resources between nodes. SWAP ensures that node operators collaborate in routing messages and data while protecting the network against frivolous use of bandwidth. + +SWAP works by tracking the relative consumption of bandwidth between nodes. As nodes relay requests and responses, they keep track of their bandwidth usage with each of their peers. Within bounds, peers engage in a service-for-service exchange, where they provide resources to each other based on their relative usage. + +However, once a limit is reached, the party in debt can either wait until their liabilities are amortized over time or can pay by sending cheques that cash out in xBZZ on the blockchain. [Chequebook](/docs/learn/technology/contracts/chequebook/) contracts are used to manage these cheques and ensure that they are valid and can be cashed out correctly. + +SWAP uses built-in incentives to optimize the allocation of bandwidth and storage resources and render Swarm economically self-sustaining. Swarm nodes track their relative bandwidth contribution on each peer connection, and excess debt due to unequal consumption can be settled in xBZZ. Publishers in Swarm must spend xBZZ to purchase the right to write data to Swarm and prepay some rent for long-term storage. + +The SWAP protocol also includes some additional features to prevent abuse or fraud. For example, it can impose limits on how much debt a node can accumulate before requiring payment or require nodes to provide collateral before sending cheques. diff --git a/docs/learn/incentives/postage-stamp.md b/docs/learn/incentives/postage-stamp.md new file mode 100644 index 000000000..e94b356d7 --- /dev/null +++ b/docs/learn/incentives/postage-stamp.md @@ -0,0 +1,233 @@ +--- +title: Postage Stamp +id: postage-stamp +--- + +The [postage stamp contract](https://github.com/ethersphere/storage-incentives/blob/master/src/PostageStamp.sol) is one component in the suite of smart contract orchestrating Swarm's [storage incentives](/docs/learn/technology/incentives) which make up the foundation of Swarm's self-sustaining economic system. + +When a node uploads data to Swarm, it 'attaches' postage stamps to each [chunk](/docs/learn/technology/disc) of data. Postage stamps are purchased in batches rather than one by one. The value assigned to a stamp indicates how much it is worth to persist the associated data on Swarm, which nodes use to prioritize which chunks to remove from their reserve first. + +The value of a postage stamp decreases over time as if storage rent was regularly deducted from the batch balance. We say that a stamp expires when the batch it is issued from has insufficient balance. A chunk with an expired stamp can not be used in the proof of entitlement storer nodes need to submit in order to get compensated for their contributed storage space, therefore such expired chunks are evicted from nodes' reserves and put into the cache where their continued persistence depends on their popularity. + +## Batch Buckets + +Postage stamps are issued in batches with a certain number of storage slots partitioned into $$2^{bucketDepth}$$ equally sized address space buckets. Each bucket is responsible for storing chunks that fall within a certain range of the address space. When uploaded, files are split into 4kb chunks, each chunk is assigned a unique address, and each chunk is then assigned to the bucket in which its address falls. Falling into the same range means a match on `n` leading bits of the chunk and bucket. This restriction is necessary to ensure (incentivise) uniform utilisation of the address space and is fair since the distribution of content addresses are uniform as well. Uniformity depth is the number of leading bits determining bucket membership (also called `bucket depth`). The uniformity depth is set to 16, so there are a total of $$2^{16} = 65,536$$ buckets. + +### Bucket Size + +Each bucket has a certain number of slots which can be "filled" by chunks (In other words, for each bucket, a certain number of chunks can be stamped). Once all the slots of a bucket are filled, the entire postage batch will be fully utilised and can no longer be used to upload additional data. + +Together with `batch depth`, `bucket depth`determines how many chunks are allowed in each bucket. The number of chunks allowed in each bucket is calculated like so: + +$$ +2^{(batchDepth - bucketDepth)} +$$ + +So with a batch depth of 24 and a bucket depth of 16: + +$$ +2^{(24 - 16)} = 2^{8} = 256 \text{ chunks/bucket} +$$ + +:::info +Note that due how buckets fill as described above, a batch can become fully utilised before its theoretical maximum volume has been reached. See [batch utilisation section below](/docs/learn/technology/contracts/postage-stamp#batch-utilisation) for more information. +::: + +## Batch Depth and Batch Amount + +Each batch of stamps has two key parameters, `batch depth` and `amount`, which are recorded on Gnosis Chain at issuance. Note that these "depths" do not refer to the depth terms used to describe topology which are outlined [here in the glossary](/docs/learn/glossary#depth-types). + +### Batch Depth + +:::caution +The minimum value for `depth` is 17, however higher depths are recommended for most use cases due to the [mechanics of stamp batch utilisation](#batch-utilisation). See [the depths utilisation table](#effective-utilisation-table) to help decide which depth is best for your use case. +::: + +`Batch depth` determines how much data can be stored by a batch. The number of chunks which can be stored (stamped) by a batch is equal to $$2^{batchDepth}$$. + +For a batch with a `batch depth` of 24, a maximum of $$2^{24} = 16,777,216$$ chunks can be stamped. + +Since we know that one chunk can store 4 kb of data, we can calculate the theoretical maximum amount of data which can be stored by a batch from the `batch depth`. + +$$ +\text{Theoretical maximum batch volume} = 2^{batchDepth} \times \text{4 kb} +$$ + +However, due to the way postage stamp batches are utilised, batches will become fully utilised before stamping the theoretical maximum number of chunks. Therefore when deciding which batch depth to use, it is important to consider the effective amount of data that can be stored by a batch, and not the theoretical maximum. The effective rate of utilisation increases along with the batch depth. See [section on stamp batch utilisation below](/docs/learn/technology/contracts/postage-stamp#batch-utilisation) for more information. + +### Batch Amount (& Batch Cost) + +The `amount` parameter is the quantity of xBZZ in PLUR $$(1 \times 10^{16}PLUR = 1 \text{ xBZZ})$$ that is assigned per chunk in the batch. The total number of xBZZ that will be paid for the batch is calculated from this figure and the `batch depth` like so: + +$$2^{batchDepth} \times {amount}$$ + +The paid xBZZ forms the `balance` of the batch. This `balance` is then slowly depleted as time ticks on and blocks are mined on Gnosis Chain. + +For example, with a `batch depth` of 24 and an `amount` of 1000000000 PLUR: + +$$ +2^{24} \times 1000000000 = 16777216000000000 \text{ PLUR} = 1.6777216 \text{ xBZZ} +$$ + +### Calculating `amount` needed for desired TTL + +The desired `amount` can be easily estimated based on the current postage stamp price and the desired amount of storage time in seconds with the given Gnosis block time of 5 seconds and the stamp price. For the example below we assume a stamp price of 24000 PLUR / chunk / block: + +:::info +The postage stamp price is dynamically determined according to a network utilisation signal. You can view the current storage price at [Swarmscan.io](https://swarmscan.io/). +::: + +$$ +(\text{stamp price} \div \text{block time in seconds}) \times \text{storage time in seconds} +$$ + +There are 1036800 seconds in 12 days, so the `amount` value required to store for 12 days can be calculated: + +$$ +(\text{24000} \div \text{5}) \times \text{1036800} = 4976640000 +$$ + +So we can use 4976640000 as our `amount` value in order for our postage batch to store data for 12 days. + + + +## Batch Utilisation + +### Immutable Batches + +Utilisation of an immutable batch is computed using a hash map of size $$2^{bucketDepth}$$ which is $$2^{16}$$ for all batches, so 65536 total entries. For the keys of the key-value pairs of the hash map, the keys are 16 digit binary numbers from 0 to 65535, and the value is a counter. + +![](/img/batches_01.png) + +As chunks are uploaded to Swarm, each chunk is assigned to a bucket based the first 16 binary digits of the [chunk's hash](/docs/learn/technology/disc#chunks). The chunk will be assigned to whichever bucket's key matches the first 16 bits of its hash, and that bucket's counter will be incremented by 1. + +The batch is deemed "full" when ANY of these counters reach a certain max value. The max value is computed from the batch depth as such: $$2^{(batchDepth-bucketDepth)}$$. For example with batch depth of 24, the max value is $$2^{(24-16)}$$ or 256. A bucket can be thought of as have a number of "slots" equal to this maximum value, and every time the bucket's counter is incremented, one of its slots gets filled. + +In the diagram below, the batch depth is 18, so there are $$2^{(18-16)}$$ or 4 slots for each bucket. The utilisation of a batch is simply the highest number of filled slots out of all 65536 entries or "buckets". In this batch, none of the slots in any of the buckets have yet been filled with 4 chunks, so the batch is not yet fully utilised. The most filled slots out of all buckets is 2, so the stamp batch's utilisation is 2 out of 4. + +![](/img/batches_02.png) + +As more chunks get uploaded and stamped, the bucket slots will begin to fill. As soon as the slots for any SINGLE bucket get filled, the entire batch is considered 100% utilised and can no longer be used to upload additional chunks. + +![](/img/batches_03.png) + + +### Mutable Batches + +Mutable batches use the same hash map structure as immutable batches, however its utilisation works very differently. In contrast with immutable batches, mutable batches are never considered fully utilised. Rather, at the point where an immutable batch would be considered fully utilised, a mutable batch can continue to stamp chunks. However, if any chunk's address lands in a bucket whose slots are already filled, rather than the batch becoming fully utilised, that bucket's counter gets reset, and the new chunk will replace the oldest chunk in that bucket. + +![](/img/batches_04.png) + +Therefore rather than speaking of the number of slots as determining the utilisation of a batch as with immutable batches, we can think of the slots as defining a limit to the amount of data which can be uploaded before old data starts to get overwritten. + +### Which Type of Batch to Use + +Immutable batches are suitable for long term storage of data or for data which otherwise does not need to be changed and should never be overwritten, such as records archival, legal documents, family photos, etc. + +Mutable batches are great for data which needs to be frequently updated and does not require a guarantee of immutability. For example, a blog, personal or company websites, ephemeral messaging app, etc. + +The default batch type when unspecified is immutable. This can be modified through the Bee api by setting the `immutable` header with the [`\stamps POST` endpoint](https://docs.ethswarm.org/api/#tag/Transaction/paths/~1transactions~1%7BtxHash%7D/post) to `false`. + +### Re-uploading + +There are several nuances to how the re-uploading of previously uploaded data to Swarm affect stamp batch utilisation. For single chunks, the behaviour is relatively straightforward, however with files that must get split into multiple chunks, the behaviour is less straightforward. + +#### Single chunks + +When a chunk which has previously been uploaded to Swarm is re-uploaded from the same node while the initial postage batch it was stamped by is still valid, no additional stamp will be utilised from the batch. However if the chunk comes from a different node than the original node, then a stamp WILL be utilised, and as long as at least one of the batches the chunk was stamped by is still valid, the chunk will be retained by storer nodes in its neighbourhood. + +#### Files + +When an identical file is re-uploaded then the stamp utilisation behaviour will be the same as with single chunks described in the section above. However, if part of the file has been modified and then re-uploaded, stamp utilisation behaviour will be different. This is due to how the chunking process works when a file is uploaded to Swarm. When uploaded to Swarm, files are split into 4kb sized chunks (2^12 bytes), and each chunk is assigned an address which is based on the content of the chunk. If even a single bit within the chunk is modified, then the address of the chunk will also be modified. + +When a file which was previously uploaded with a single bit flipped is again split into chunks by a node before being uploaded to Swarm, then only the chunk with the flipped bit will have an updated address and require the utilisation of another stamp. The content of all the other chunks will remain the same, and therefore will not require new stamps to be utilised. + +However, if rather than flipping a single bit we add some data to our file, this could cause changes in the content of every chunk of the file, meaning that every single chunk must be re-stamped. We can use a simplified example of why this is the case to more easily understand the stamp utilisation behaviour. Let us substitute a message containing letters of the alphabet rather than binary data. + +Our initial message consists of 16 letters: + + +> abcdefghijklmnop + +When initially uploaded, it will be split into four chunks of four letters each: + +> abcdefghijklmnop => abcd | efgh | ijkl | mnop + +Let us look at what happens when a single letter is changed (here we change a to z): + +> abcdefghijklmnop => zbcd | efgh | ijkl | mnop + +In this case, only the first chunk is affected, all the other chunks retain the same content. + +> Now let is examine the case where a new letter is added rather than simply modifying an already existing one. Here we add the number 1 at the start of the message: + +> 1abcdefghijklmnop => 1abc | defg | hijk | lmno | p + +As you can see, by adding a single new letter at the start of the message, all the letters are shifted to the right by a single position, which a has caused EVERY chunk in the message to be modified rather than just a single chunk. + +#### Affect on Batch Utilisation + +The implications of this behaviour are that even a small change to the data of a file may cause every single chunk from the file to be changed, meaning that new stamps must be utilised for every chunk from that file. In practice, this could lead to high costs in data which is frequently changed, since for even a small change, every chunk from the file must be re-stamped. + + +### Implications for Swarm Users + +Due to the nature of batch utilisation described above, batches are often fully utilised before reaching their theoretical maximum storage amount. However as the batch depth increases, the chance of a postage batch becoming fully utilised early decreases. At batch depth 24, there is a 0.1% chance that a batch will be fully utilised/start replacing old chunks before reaching 64.33% of its theoretical maximum. + +Let's look at an example to make it clearer. Using the method of calculating the theoretical maximum storage amount [outlined above](/docs/learn/technology/contracts/postage-stamp#batch-depth), we can see that for a batch depth of 24, the theoretical maximum amount which can be stored is 68.72 gb: + +$$ +2^{24+12} = \text{68,719,476,736 bytes} = \text{68.72 gb} +$$ + +Therefore we should use 64.33% the effective rate of usage for the stamp batch: + +$$ +\text{68.72 gb} \times{0.6433} = \text{44.21 gb } +$$ + +:::info +The details of how the effective rates of utilisation are calculated will be published soon. +::: + +### Effective Utilisation Table + + +When a user buys a batch of stamps they may make the naive assumption that they will be able to upload data equal to the sum total size of the maximum capacity of the batch. However, in practice this assumption is incorrect, so it is essential that Swarm users understand the relationship between batch depth and the theoretical and effective volumes of a batch. + +The provided table shows the effective volume for each batch depth from 20 to 41 (note that currently the minimum stamp batch depth is 17, however 22 is the first depth with an effective volume above zero). The utilisation rate is the rate of utilisation of the theoretical max volume that a stamp batch can reach with a 0.1% failure rate (that is, there is only a one-in-a-thousand chance that the difference between the actual effectively utilised volume and effective volume shown in the table is greater than 0.1%). The "effective volume" figure shows the actual amount of data which can be stored at the effective rate. The effective volume figure is the one which should be used as the de-facto maximum amount of data that a batch can store before becoming either fully utilised (for immutable batches), or start overwriting older chunks (mutable batches). + + +| Batch Depth | Utilisation Rate | Theoretical Max Volume | Effective Volume | +|-------------|------------------|-------------------------|-------------------| +| 20 | 0.00% | 4.29 GB | 0.00 B | +| 21 | 0.00% | 8.59 GB | 0.00 B | +| 22 | 28.67% | 17.18 GB | 4.93 GB | +| 23 | 49.56% | 34.36 GB | 17.03 GB | +| 24 | 64.33% | 68.72 GB | 44.21 GB | +| 25 | 74.78% | 137.44 GB | 102.78 GB | +| 26 | 82.17% | 274.88 GB | 225.86 GB | +| 27 | 87.39% | 549.76 GB | 480.43 GB | +| 28 | 91.08% | 1.10 TB | 1.00 TB | +| 29 | 93.69% | 2.20 TB | 2.06 TB | +| 30 | 95.54% | 4.40 TB | 4.20 TB | +| 31 | 96.85% | 8.80 TB | 8.52 TB | +| 32 | 97.77% | 17.59 TB | 17.20 TB | +| 33 | 98.42% | 35.18 TB | 34.63 TB | +| 34 | 98.89% | 70.37 TB | 69.58 TB | +| 35 | 99.21% | 140.74 TB | 139.63 TB | +| 36 | 99.44% | 281.47 TB | 279.91 TB | +| 37 | 99.61% | 562.95 TB | 560.73 TB | +| 38 | 99.72% | 1.13 PB | 1.12 PB | +| 39 | 99.80% | 2.25 PB | 2.25 PB | +| 40 | 99.86% | 4.50 PB | 4.50 PB | +| 41 | 99.90% | 9.01 PB | 9.00 PB | + + +:::info +This table is based on preliminary calculations and may be subject to change. +::: + +Nodes' storage is actually defined as a number of chunks with a size of 4kb (2^12 bytes) each, but in fact some [SOC](/docs/learn/technology/disc#content-addressed-chunks-and-single-owner-chunks) chunks can be a few bytes longer, and some chunks can be smaller, so the conversion is not precise. Furthermore, due to the way Swarm represents files in a Merkle tree, the intermediate chunks are additional overhead which must also be accounted for. + +Additionally, when a node stores chunks it uses additional indexes — therefore the disk space a maximally filled reserve would demand cannot be calculated with perfect accuracy. diff --git a/docs/learn/incentives/price-oracle.md b/docs/learn/incentives/price-oracle.md new file mode 100644 index 000000000..869e9f0a4 --- /dev/null +++ b/docs/learn/incentives/price-oracle.md @@ -0,0 +1,11 @@ +--- +title: Price Oracle +id: price-oracle +--- + +The job of the [Oracle contract](https://github.com/ethersphere/price-oracle) is to set the price of Postage Stamps. The oracle contract uses data from the [Postage Stamp contract](/docs/learn/technology/contracts/postage-stamp) in order to set the appropriate price for Postage Stamps. The data in the Postage Stamp contract is used to calculate a "utilisation signal". This signal is an indicator of how much the Swarm network’s data storage capacity is being utilized. Specifically, the signal is a measure of data redundancy on the network. Redundancy is a measure of how many copies of each piece of data can be stored by the network. The protocol targets a fourfold level of data redundancy as a safe minimum. + +For example, if there is an increase in postage stamps being purchased while the number of nodes remains constant, the data redundancy level will begin to fall as data storers’ available space begins to become reserved. If too many postage stamps are purchased without an equivalent increase in storage providers, the redundancy level may fall below four. In this case, the oracle will increase the price of postage stamps so that it becomes more expensive to store data on Swarm. The higher cost of storage will then lead to less postage stamps being purchased, and will push the redundancy level back up towards four. + +Conversely, if the amount of Stamps being purchased decreases while the number of storage provider nodes remains constant, the redundancy level will increase as there are fewer chunks of data to be distributed amongst the same number of nodes. In this case, the oracle will decrease the Postage Stamp price in order to promote more data storers to store their data on Swarm. The lower cost of storage will then lead to more Postage Stamps being purchased and push the redundancy level back down towards four. + From 547559e9b122b2c3a41b0049b471fc2389b2670c Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Wed, 23 Oct 2024 05:40:25 +0700 Subject: [PATCH 07/25] finished reorganizing Learn section --- docs/learn/DISC/DISC.md | 4 +- docs/learn/DISC/erasure-coding.md | 4 +- docs/learn/DISC/kademlia.md | 32 ++- docs/learn/DISC/neighbourhoods.md | 4 +- docs/learn/ecosystem.md | 28 +++ docs/learn/ecosystem/awesome.md | 125 ---------- docs/learn/ecosystem/community.md | 24 -- docs/learn/ecosystem/grants-bounties.md | 11 - docs/learn/ecosystem/swarm-foundation.md | 12 - .../{ecosystem => }/fair-data-society.md | 0 .../access-control.md | 2 +- docs/learn/{technology => features}/pss.md | 0 docs/learn/incentives/bandwidth-incentives.md | 24 ++ docs/learn/incentives/chequebook.md | 19 -- docs/learn/incentives/incentives.md | 61 ----- docs/learn/incentives/overview.md | 32 +++ .../{postage-stamp.md => postage-stamps.md} | 10 +- docs/learn/incentives/price-oracle.md | 2 +- docs/learn/incentives/redistribution-game.md | 41 +++ docs/learn/introduction.md | 13 +- .../overview.md => smart-contracts.md} | 5 +- docs/learn/technology/contracts/chequebook.md | 19 -- .../technology/contracts/postage-stamp.md | 233 ------------------ .../technology/contracts/price-oracle.md | 11 - docs/learn/technology/incentives.md | 61 ----- sidebars.js | 36 +-- 26 files changed, 182 insertions(+), 631 deletions(-) create mode 100644 docs/learn/ecosystem.md delete mode 100644 docs/learn/ecosystem/awesome.md delete mode 100644 docs/learn/ecosystem/community.md delete mode 100644 docs/learn/ecosystem/grants-bounties.md delete mode 100644 docs/learn/ecosystem/swarm-foundation.md rename docs/learn/{ecosystem => }/fair-data-society.md (100%) rename docs/learn/{technology => features}/access-control.md (99%) rename docs/learn/{technology => features}/pss.md (100%) create mode 100644 docs/learn/incentives/bandwidth-incentives.md delete mode 100644 docs/learn/incentives/chequebook.md delete mode 100644 docs/learn/incentives/incentives.md create mode 100644 docs/learn/incentives/overview.md rename docs/learn/incentives/{postage-stamp.md => postage-stamps.md} (94%) create mode 100644 docs/learn/incentives/redistribution-game.md rename docs/learn/{technology/contracts/overview.md => smart-contracts.md} (97%) delete mode 100644 docs/learn/technology/contracts/chequebook.md delete mode 100644 docs/learn/technology/contracts/postage-stamp.md delete mode 100644 docs/learn/technology/contracts/price-oracle.md delete mode 100644 docs/learn/technology/incentives.md diff --git a/docs/learn/DISC/DISC.md b/docs/learn/DISC/DISC.md index d7e357ffe..442b74287 100644 --- a/docs/learn/DISC/DISC.md +++ b/docs/learn/DISC/DISC.md @@ -3,11 +3,11 @@ title: DISC id: disc --- -DISC (Distributed Immutable Storage of Chunks) is a storage solution developed by Swarm based on a modified implementation of a [Kademlia DHT](/docs/learn/glossary#kademlia ) which has been specialized for data storage. Swarm's implementation of a DHT differs significantly in that it stores the content in the DHT directly, rather than just storing a list of seeders who are able to serve the content. This approach allows for much faster and more efficient retrieval of data. +DISC (Distributed Immutable Storage of Chunks) is a storage solution developed by Swarm based on a modified implementation of a [Kademlia DHT](/docs/learn/DISC/kademlia) which has been specialized for data storage. Swarm's implementation of a DHT differs significantly in that it stores the content in the DHT directly, rather than just storing a list of seeders who are able to serve the content. This approach allows for much faster and more efficient retrieval of data. ### Kademlia Topology and Routing -[Kademlia](/docs/learn/DISC/kademlia) is a distributed hash table (DHT) widely used in peer-to-peer networks such as Ethereum and Bittorent. It serves as the routing and topology foundation for communication between nodes in the Swarm netowrk. It organizes nodes based on their overlay addresses and ensures that messages are relayed efficiently, even in a dynamic, decentralized environment. +Kademlia is a distributed hash table (DHT) widely used in peer-to-peer networks such as Ethereum and Bittorent. It serves as the routing and topology foundation for communication between nodes in the Swarm netowrk. It organizes nodes based on their overlay addresses and ensures that messages are relayed efficiently, even in a dynamic, decentralized environment. One of the advantages of using Kademlia as a model for network topology is that both the number of forwarding "hops" required to route a chunk to its destination and the number of peer connections required to maintain Kademlia topology are logarithmic to the size of the network (a minimum of two connections is required in order to maintain Kademlia topology in case of network churn - nodes dropping in and out of the network). This makes Swarm a highly scalable system which is efficient even at very large scales. diff --git a/docs/learn/DISC/erasure-coding.md b/docs/learn/DISC/erasure-coding.md index bf4433150..80e53366d 100644 --- a/docs/learn/DISC/erasure-coding.md +++ b/docs/learn/DISC/erasure-coding.md @@ -29,7 +29,9 @@ In Swarm's implementation of erasure coding, there are four named levels of prot | 3 | Insane | 10% | | 4 | Paranoid | 50% | -The "Chunk Loss Tolerance" column corresponds to the exact level of data protection for each level, and is the maximum percent of erasure coded chunks which can be lost before the original data is no longer retrievable (with 99.9999% statistical certainty). Note that this guarantee of retrievability is for the data from each 128 chunk segment, and therefore does not correspond to retrievability of a whole file. The retrievability failure rate for any individual file depends on the size of the file, and increases with the size of the file. For a detailed explanation of how to calculate the retrievability of any sized file refer to [section 3 in the erasure coding paper](https://papers.ethswarm.org/erasure-coding.pdf). +The "Redundancy Level" is a numeric value for each level of protection, the "Level Name" is the official name for each level, and the "Chunk Loss Tolerance" column corresponds to the exact level of data protection for each level. For each redundancy level, the original data is retrievable with >=99.9999% statistical certainty given a percent chunk loss equal or less than the percent shown in the "Chunk Loss Tolerance" column. + +Note that this guarantee of retrievability is for each 128 chunk segment, and therefore does not correspond to retrievability of a whole file. The retrievability failure rate for any individual file depends on the size of the file, and increases with the size of the file. For a detailed explanation of how to calculate the retrievability of any sized file refer to [section 3 in the erasure coding paper](https://papers.ethswarm.org/erasure-coding.pdf). ## Usage diff --git a/docs/learn/DISC/kademlia.md b/docs/learn/DISC/kademlia.md index b00bba01c..e06047a5b 100644 --- a/docs/learn/DISC/kademlia.md +++ b/docs/learn/DISC/kademlia.md @@ -6,31 +6,29 @@ id: kademlia Kademlia is a distributed hash table (DHT) algorithm used in peer-to-peer networks to efficiently store and retrieve data without relying on centralized servers. It organizes nodes into an overlay network that ensures efficient routing using a binary tree structure. -## Kademlia Core Concepts +## Kademlia Key Concepts -Key features of Kademlia include: +### **XOR Distance Metric** +Kademlia uses a distance metric based on the XOR (exclusive OR) between any addresses. This allows nodes to calculate "distance" from each other. Lookups are made by recursively querying nodes that are progressively closer to the target. -### **XOR Distance Metric**: -Kademlia uses a unique distance metric based on the XOR (exclusive OR) between any addresses. This allows nodes to calculate "distance" from each other. Lookups are made by recursively querying nodes that are progressively closer to the target. - -### **Routing Table**: +### **Routing Table** Each node in a Kademlia network maintains a routing table containing information about other nodes, organized by the XOR distance between node IDs. -### **Efficient Lookups**: +## Kademlia Advantages + +### **Efficient Lookups** To retrieve a specific chunk, a node uses Kademlia's lookup process to find and fetch the chunk from a node in the neighborhood where it is stored. The number of hops required for a chunk to be retrieved is logarithmic to the number of nodes in the network, meaning lookups remain efficient even as the network grows larger and larger. -### **Fault Tolerance**: -Kademlia is resilient to node failures. Because nodes are regularly refreshed through lookups and interactions, the network remains functional even when individual nodes leave or fail. +### **Fault Tolerance** -### **Scalability**: -Kademlia's design allows it to scale to large networks, as each node only needs to keep track of a small subset of the total nodes in the network. The required set of connected peers grows logarithmically with the number of nodes, making it efficient even in large networks. +Because nodes' peer lists are regularly refreshed through lookups and interactions, and because redundant copies of data are replicated within the network, the network remains functional even when individual nodes leave or fail. +### **Scalability** + +Kademlia's design allows it to scale to large networks, as each node only needs to keep track of a small subset of the total nodes in the network. The required set of connected peers grows logarithmically with the number of nodes, making it efficient even in large networks. -:::info -For a more in depth understanding of Swarm's specific implementation of Kademlia, refer to the second chapter of [The Book of Swarm](https://www.ethswarm.org/the-book-of-swarm-2.pdf). -::: ## Kademlia in Swarm @@ -38,9 +36,9 @@ As mentioned above, Swarm's version of Kademlia differs from commonly used imple ### Proximity Order & Neighborhoods -Swarm introduces the concept of [proximity order (PO)](/docs/learn/glossary#proximity-order-po) as a discrete measure of node relatedness between two addresses. In contrast with Kademlia distance which is an exact measure of relatedness, PO is a more general measure of the relatedness between two addresses based on their number of shared leading bits. +Swarm introduces the concept of [proximity order (PO)](/docs/learn/glossary#proximity-order-po) as a discrete measure of node relatedness between two addresses. In contrast with Kademlia distance which is an exact measure of relatedness, PO is used to measure the relatedness between two addresses on a discrete scale based on the number of shared leading bits. Since this metric ignores all the bits after the shared leading bits, it is not an exact measure of distance between any two addresses. -In Swarm's version of Kademlia, nodes are grouped into neighborhoods of nodes based on PO (ie., all nodes in a neighborhood share the same leading binary prefix bits). Each neighborhood of nodes is responsible for storing the same set of chunks. +In Swarm's version of Kademlia, nodes are grouped into [neighborhoods](/docs/learn/DISC/neighbourhoods) of nodes based on PO (ie., neighborhood are composed of nodes which all share the same leading binary prefix bits). Each neighborhood of nodes is responsible for storing the same set of chunks. Neighborhoods are important for ensuring data redundancy, and they also play a role in the incentives system which guarantees nodes are rewarded for contributing resources to the network. @@ -52,6 +50,6 @@ In contrast, Swarm makes use of forwarding Kademlia. Here each node forwards the The main advantage of forwarding Kademlia is that it maintains the anonymity of the node which initiated the request. -### Storage Incentives +### Neighborhood Based Storage Incentives Swarm introduces a storage incentives layer on top of its Kademlia implementation in order to reward nodes for continuing to provide resources to the network. Neighborhoods play a key role in the storage incentives mechanism. Storage incentives take the role of a "game" in which nodes play to win a reward for storing the correct data. Each round in the game, one neighborhood is chosen to play, and all nodes within the same neighborhood participate as a group. The nodes each compare the data they are storing with each other to make sure they are all storing the data they are responsible for, and one node is chosen to win from among the group. You can read more about how storage incentives work in the dedicated page for storage incentives. \ No newline at end of file diff --git a/docs/learn/DISC/neighbourhoods.md b/docs/learn/DISC/neighbourhoods.md index 2752fbca7..85528beb4 100644 --- a/docs/learn/DISC/neighbourhoods.md +++ b/docs/learn/DISC/neighbourhoods.md @@ -16,7 +16,7 @@ The terms "depth" and "radius" are often used interchangeably when discussing ne ## Neighborhood Formation -A Swarm neighborhood is determined by the [proximity order (PO)](/docs/learn/glossary#proximity-order-po) of node addresses, which is calculated based on the number of leading bits shared between the addresses of nodes in the network. Nodes in the same neighborhood share the same prefix of their addresses, and the neighborhood expands or contracts depending on the availability of nearby nodes. As a result, each node is responsible for interacting with other nodes within its neighborhood to store and replicate data chunks, ensuring data availability and redundancy. The neighborhood depth dynamically adjusts as peers join or leave the network, maintaining a healthy distribution of storage responsibility across nodes. +A Swarm neighborhood is determined by the proximity order (PO) of node addresses, which is calculated based on the number of leading bits shared between the addresses of nodes in the network. Nodes in the same neighborhood share the same prefix of their addresses, and the neighborhood expands or contracts depending on the availability of nearby nodes. As a result, each node is responsible for interacting with other nodes within its neighborhood to store and replicate data chunks, ensuring data availability and redundancy. The neighborhood depth dynamically adjusts as peers join or leave the network, maintaining a healthy distribution of storage responsibility across nodes. ### Example Neighbourhood @@ -103,6 +103,6 @@ Each of our two example chunks will also be split amongst the two new neighbourh |da69 (chunk)| 1101101001101001| -### Doubling Implications for Node Operators +#### Doubling Implications for Node Operators One of the implications of doubling for node operators is that the reward chances for a node depends in part on how many other nodes are in its neighbourhood. If it is in a neighbourhood with fewer nodes, its chances of winning rewards are greater. Therefore node operators should make certain to place their nodes into less populated neighbourhoods, and also should look ahead to neighbourhoods at the next depth after a doubling. For more details about how to adjust node placement, see [the section on setting a target neighbourhood](/docs/bee/installation/install#set-target-neighborhood-optional) in the installation guide. \ No newline at end of file diff --git a/docs/learn/ecosystem.md b/docs/learn/ecosystem.md new file mode 100644 index 000000000..5ac5fc889 --- /dev/null +++ b/docs/learn/ecosystem.md @@ -0,0 +1,28 @@ +--- +title: Ecosystem +id: ecosystem +--- + + +## Official Links + +[Twitter](https://twitter.com/ethswarm) +[Discord server](https://discord.gg/wdghaQsGq5) +[Reddit](https://www.reddit.com/r/ethswarm/) +[GitHub](https://github.com/ethersphere) +[Blog](https://blog.ethswarm.org) +[Homepage](https://www.ethswarm.org/) + +## Awesome Swarm + +An [awesome list](https://awesome.re) on anything awesome related to the Swarm platform. 🐝 🐝 🐝 + +To see the most up to date list or submit an addition to it, make sure to check the [awesome-swarm repo](https://github.com/ethersphere/awesome-swarm). + +## Grants and Bounties + +Swarm grants support many interesting projects that are already building their products on top of Swarm. Swarm bounties extend the ecosystem with tooling and infrastructure. + +If you have an idea for a project which uses Swarm's technology we welcome you to [apply for a grant](https://grants.ethswarm.org). + +Learn more about grants for building on Swarm at the [EthSwarm homepage](https://www.ethswarm.org/grants). \ No newline at end of file diff --git a/docs/learn/ecosystem/awesome.md b/docs/learn/ecosystem/awesome.md deleted file mode 100644 index 496e7a4db..000000000 --- a/docs/learn/ecosystem/awesome.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -title: Awesome Swarm -id: awesome ---- - -# Awesome Swarm - -An [awesome list](https://awesome.re) on anything awesome related to the Swarm platform. 🐝 🐝 🐝 - -To see the most up to date list or submit an addition to it, make sure to check the [awesome-swarm repo](https://github.com/ethersphere/awesome-swarm). - -We love your contribution, pull requests are most welcome! - -:::caution -Some software on this list is community created and has not been reviewed for functionality or security and comes with no guarantees. -::: - -### Services - -[Bee](https://github.com/ethersphere/bee) - Also referred to as the _node_ or the _client_, this service allows you to join the Swarm network - -### Libraries - -[Bee-JS](https://github.com/ethersphere/bee-js) - A high-level Javascript library to interact with Bee through its REST API - -[Mantaray-JS](https://github.com/ethersphere/mantaray-js) - A low-level Swarm manifest manipulation library - -[Sepatree](https://github.com/dr-chesster/sepatree) - The SepaTree data structure abstracted on Swarm - -[BeeJeez](https://github.com/beejeez/beejeez) - Javascript implementation of the handshake protocol and others based on libp2p - -### CI/CD - -[Beekeeper](https://github.com/ethersphere/beekeeper) - Orchestrate and test Bee clusters through Kubernetes - -[Bee Factory](https://github.com/ethersphere/bee-factory) - Sets up a Dockerized stack of Bee nodes including Ganache blockchain - -[Bee Factory VPS](https://github.com/Cafe137/bee-factory-vps) - Provides an automatized way to set up Bee Factory on a fresh Ubuntu VPS - -[Beeload Action](https://github.com/ethersphere/beeload-action) - GitHub Actions workflow for uploading data to the Swarm network - -### UI - -[Bee Dashboard](https://github.com/ethersphere/bee-dashboard) - React project to troubleshoot and interact with your Bee node - -[Gateway](https://github.com/ethersphere/gateway) - Gateway to the Swarm project, for uploading, downloading and sharing assets on the network - -[Pastebee](https://github.com/1up-digital/pastebee) - Pastebin, but on Swarm and with unstoppable publishing - -[Chess UI](https://github.com/dr-chesster/chess-ui) - Play, store and share Chess games on Swarm - -### Tools - -[Swarm CLI](https://github.com/ethersphere/swarm-cli) - Do everything on Swarm with the power of the terminal - -[Swarm Extension](https://github.com/ethersphere/swarm-extension) - Official extension that adds Swarm support and injects Bee library to the browser - -[Pastebee CLI](https://github.com/AuHau/pastebee-cli) - Upload to Pastebee via the CLI and share the Swarm hash - -[Swarm CID Converter](https://github.com/agazso/swarm-cid-converter) - Convert Swarm hashes or links to CID and vice versa. - -[Bee-AFS](https://github.com/aloknerurkar/bee-afs) - FUSE filesystem for Bee - -[Nextcloud Swarm Plugin](https://github.com/MetaProvide/nextcloud-swarm-plugin) - Plugin for bridging Nextcloud and Swarm. - -### Smart Contracts - -[Swap, Swear and Swindle](https://github.com/ethersphere/swap-swear-and-swindle) - Protocols for peer-to-peer accounting - -[Storage Incentives](https://github.com/ethersphere/storage-incentives) - Smart contracts providing the basis for Swarm's storage incentivization model - -### Documentation - -[The Book of Swarm](https://www.ethswarm.org/the-book-of-swarm-2.pdf) - Storage and communication infrastructure for self-sovereign digital society back-end stack for the decentralised web - -[Bee Docs](https://github.com/ethersphere/bee-docs) - Documentation for the Swarm Bee Client. View at [docs.ethswarm.org](https://docs.ethswarm.org/docs/). - -[Bee-JS Docs](https://github.com/ethersphere/bee-js-docs) - Documentation for the Swarm Bee-js javascript library. View at [bee-js.ethswarm.org](https://bee-js.ethswarm.org/docs/). - -### Community / Ecosystem - -[Fair data society](https://fairdatasociety.org/) - Ecosystem initiative for ethical Web3 - -[FairOS](https://github.com/fairDataSociety/fairOS-dfs) - Distributed file system, key-value store and nosql store on Swarm (for developers) - -[Fair Data Protocol roadmap enabling data interoperability](https://github.com/fairDataSociety/FIPs/blob/master/text/0001-fdp-roadmap.md) - Develop your dapp on Swarm fast and in an interoperable way - -[FDP play](https://github.com/fairDataSociety/fdp-play) - CLI tool to spin up local development FDP environment and Bee cluster with Docker - -[Blossom browser extension](https://github.com/fairDataSociety/blossom) - Browser Extension based on Fair Data Protocol that acts as a web3 framework for dApps and a Fair Data Society account manager for end-users - -[Fairdrive](https://fairdrive.fairdatasociety.org/) - Decentralised and unstoppable "Dropbox" for end-users and developers using Fair Data Protocol - -[Fairdrive code](https://github.com/fairDataSociety/fairdrive-theapp) - Code for decentralised and unstoppable "Dropbox" for end-users and developers using Fair Data Protocol - -[Fairdrop](https://fairdrop.xyz) - Decentralised file sharing - -[Galileo](https://app.galileo.fairdatasociety.org/) - Open Street Maps on Swarm - -[Dracula](https://app.dracula.fairdatasociety.org/) - Hackmd-like markdown editor that works with Swarm - -[SwarmScan](https://swarmscan.resenje.org/) - Get network insights - -[Etherna.io](https://etherna.io/) - Decentralised Youtube on Swarm - -[Social Archive](https://socialarchive.info/) - Archive your social media - -[Swapchat 2.0](https://swapchat.bzz.link) - Decentralised, ephemeral, peer-to-peer, encrypted chat - -[Hacker Manifesto](https://bah5acgza3gsduiek2cykkbj27jd7ug2vpzi3exbsfd76mjujqijcjvmo4mia.bzz.link) - The Hacker Manifesto on Swarm with a community funded postage stamp - -[SwarmNFT library](https://github.com/igar1991/SwarmNFT) - JavaScript library for creating NFTs on Ethereum-compatible blockchains and storing content on Swarm - -[videoNFT](https://github.com/pabloVoorvaart/videoNFT/) - NFT live streaming with Swarm (winner of EthBerlin3 2022 Freedom to Transact Track) - -[DeBoot](https://github.com/awmacpherson/deboot) - DeBoot is a project to research and implement approaches to bootloading OS images from a decentralized storage network such as Swarm or IPFS. - -[Swarm DAppNode Package](https://github.com/rndlabs/dappnodepackage-swarm) - Swarm DAppNode package for Swarm Mainnet with multi-platform (x86_64 and arm64) support. Testnet DAppNode packages can be found [here](https://github.com/rndlabs/dappnodepackage-swarm-testnet). - -### Miscellaneous - -[Swarm Bot](https://github.com/ethersphere/swarm-bot) - Discord bot handling commands related to Swarm and its community - - - diff --git a/docs/learn/ecosystem/community.md b/docs/learn/ecosystem/community.md deleted file mode 100644 index 067f0a37b..000000000 --- a/docs/learn/ecosystem/community.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Community -id: community ---- - -Swarm is all about community. We want to make sure you have the best -possible experience as you join and enjoy being a part of the new -Internet. - -Swarm has a vibrant community of wonderful individuals who are eager -to answer any questions and graciously welcome you to our swarm. - -Join us for peace, love, unity, respect, community, assistance and lots of highly obscure chat about cryptography. - -Thanks for being here with us, making a difference, we appreciate you! 🧡 - -## Links - -[Twitter](https://twitter.com/ethswarm) -[Discord server](https://discord.gg/wdghaQsGq5) -[Reddit](https://www.reddit.com/r/ethswarm/) -[GitHub](https://github.com/ethersphere) -[Blog](https://blog.ethswarm.org) -[Homepage](https://www.ethswarm.org/) \ No newline at end of file diff --git a/docs/learn/ecosystem/grants-bounties.md b/docs/learn/ecosystem/grants-bounties.md deleted file mode 100644 index f8cc1aa96..000000000 --- a/docs/learn/ecosystem/grants-bounties.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Grants and Bounties -id: grants-bounties ---- - - -Swarm grants support many interesting projects that are already building their products on top of Swarm. Swarm bounties extend the ecosystem with tooling and infrastructure. - -If you have an idea for a project which uses Swarm's technology we welcome you to [apply for a grant](https://grants.ethswarm.org). - -Learn more about grants for building on Swarm at the [EthSwarm homepage](https://www.ethswarm.org/grants). \ No newline at end of file diff --git a/docs/learn/ecosystem/swarm-foundation.md b/docs/learn/ecosystem/swarm-foundation.md deleted file mode 100644 index e7dd63021..000000000 --- a/docs/learn/ecosystem/swarm-foundation.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Foundation -id: swarm-foundation ---- - -## Swarm Foundation - -The goal of [Swarm Foundation](https://www.ethswarm.org/foundation) is to support and contribute to the creation of technology whose code will be open to all and will allow the storage and exchange of data in a decentralised manner. To this end, the foundation intends in particular to promote and support a community and a sustainable and independent ecosystem for the development of free open source software (FLOSS), allowing for digital services coordinated by crypto-economic incentives that process, distribute and store data. - -Its mission is to empower digital freedom by promoting the development and maintenance of the Swarm network, the base layer of the emerging fair data economy, and the community surrounding this network. - -It does so by supporting many different initiatives, either through financial grants or other types of support, all of which is assessed on a case-by-case basis. \ No newline at end of file diff --git a/docs/learn/ecosystem/fair-data-society.md b/docs/learn/fair-data-society.md similarity index 100% rename from docs/learn/ecosystem/fair-data-society.md rename to docs/learn/fair-data-society.md diff --git a/docs/learn/technology/access-control.md b/docs/learn/features/access-control.md similarity index 99% rename from docs/learn/technology/access-control.md rename to docs/learn/features/access-control.md index 20e96eb86..39c7976f3 100644 --- a/docs/learn/technology/access-control.md +++ b/docs/learn/features/access-control.md @@ -1,6 +1,6 @@ --- title: Access Control -id: act +id: access-control --- The Access Control Trie (ACT) implements the operation of encryption at the chunk level, with the presence of a decryption/encryption key being the only distinction between accessing private and public data. diff --git a/docs/learn/technology/pss.md b/docs/learn/features/pss.md similarity index 100% rename from docs/learn/technology/pss.md rename to docs/learn/features/pss.md diff --git a/docs/learn/incentives/bandwidth-incentives.md b/docs/learn/incentives/bandwidth-incentives.md new file mode 100644 index 000000000..807c1ef3f --- /dev/null +++ b/docs/learn/incentives/bandwidth-incentives.md @@ -0,0 +1,24 @@ +--- +title: Bandwidth Incentives +id: bandwidth-incentives +--- + +The Swarm Accounting Protocol (SWAP) is a protocol used to manage the exchange of bandwidth resources between nodes. SWAP ensures that node operators collaborate in routing messages and data while protecting the network against frivolous use of bandwidth. The protocol combines off-chain peer-to-peer based accounting with on-chain settlement through the chequebook contract. + +As nodes relay requests and responses, they keep track of their bandwidth usage with each of their peers. Peers engage in a service-for-service exchange, where they provide resources to each other based on their relative usage. + +Once a node's relative debt with one of their peers crosses a certain threshold, the party in debt can either send a xBZZ payment in the form of a "cheque" (an off chain commitment to pay their debt), or can continue to provide bandwidth services in kind until their debt is paid off. Each node can set their own threshold for the level of relative debt they accept. Freeloader nodes which do not pay their debts are at risk of being blacklisted by other nodes. + +## Chequebook Contract + +The [chequebook contract](https://github.com/ethersphere/swap-swear-and-swindle/blob/master/contracts/ERC20SimpleSwap.sol) is a smart contract used in the SWAP protocol to manage cheques that are sent between nodes on the network. It acts as a wallet which nodes can fund with xBZZ which can be used to issue payments when presented with a valid cheque. The contract is also responsible for ensuring that cheques are valid and are cashed out correctly. + +When a node sends a cheque to one of its peers, it includes a signed message that specifies the amount of xBZZ tokens being transferred and the recipient's address. The chequebook contract receives this message and verifies that it is valid by checking the signature and ensuring that the sender has enough funds to cover the transfer. + +If the cheque is valid, the contract updates the balances of both nodes accordingly. The recipient can then cash out their xBZZ tokens by sending a transaction to the blockchain that invokes a function in the chequebook contract. This function transfers the specified amount of xBZZ tokens from the sender's account to the recipient's account. + +## Opportunistic Caching + +When a node serves a chunk, the chunk is saved in the nodes' cache. Popular chunks which are frequently requested are kept in the cache so that they can be served again without the need to re-download the chunks from the network. This allows nodes to maximise their earnings by retaining popular chunks. This mechanism also contributes to Swarm's scalability, as popular chunks are always readily available for download as a result of opportunistic caching. + +To learn in more detail about how bandwidth incentives work, refer to sections 3.1 and 3.2 from [The Book of Swarm](https://papers.ethswarm.org/p/book-of-swarm/). \ No newline at end of file diff --git a/docs/learn/incentives/chequebook.md b/docs/learn/incentives/chequebook.md deleted file mode 100644 index 4fc69d47b..000000000 --- a/docs/learn/incentives/chequebook.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: Chequebook -id: chequebook ---- - -The [chequebook contract](https://github.com/ethersphere/swap-swear-and-swindle/blob/master/contracts/ERC20SimpleSwap.sol) is a smart contract used in the [Swarm Accounting Protocol (SWAP)](/docs/learn/technology/incentives) to manage cheques that are sent between nodes on the network. The contract is responsible for keeping track of the balances of each node and ensuring that cheques are valid and can be cashed out correctly. - -When a node sends a cheque to another node, it includes a signed message that specifies the amount of xBZZ tokens being transferred and the recipient's address. The chequebook contract receives this message and verifies that it is valid by checking the signature and ensuring that the sender has enough funds to cover the transfer. - -:::caution -Settlement of cheques is not enforced by smart contract. -::: - -If the cheque is valid, the contract updates the balances of both nodes accordingly. The recipient can then cash out their xBZZ tokens by sending a transaction to the blockchain that invokes a function in the chequebook contract. This function transfers the specified amount of xBZZ tokens from the sender's account to the recipient's account. - -The chequebook contract also includes some additional features to prevent abuse. For example, it can impose limits on how much debt a node can accumulate before requiring payment. - -The chequebook contract plays an important role in ensuring that SWAP operates smoothly and fairly by providing a secure and reliable way for nodes to exchange value on the network. - diff --git a/docs/learn/incentives/incentives.md b/docs/learn/incentives/incentives.md deleted file mode 100644 index 756864b56..000000000 --- a/docs/learn/incentives/incentives.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Incentives -id: incentives ---- - -One of the key challenges in a decentralised data network is incentivising users to store data and provide bandwidth. Swarm addresses this challenge with two incentives systems, one which rewards nodes for sharing their storage space and another which rewards them for sharing bandwidth. - - -## Storage Incentives - -Swarm's storage incentives protocol is defined in depth in the [Future Proof Storage](https://www.ethswarm.org/swarm-storage-incentives.pdf) paper published by the Swarm team. - -Swarm's storage incentives are based on [postage stamps](/docs/learn/technology/contracts/postage-stamp), which serve as verifiable proof of payment associated with chunks witnessed by their owner's signature. Postage stamps signal chunks' relative importance by ascribing them with xBZZ quantity which storer nodes can use when selecting which chunks to retain and which to evict from their reserve when their reserve capacity is exceeded. - -The amount of xBZZ required for a postage stamp depends on the amount of data being stored and the duration for which it will be stored. The longer a chunk is stored, the more xBZZ is required for the postage stamp. This ensures that users are incentivised to store data for longer periods, which helps ensure that data remains available in the network. - -Storer nodes can use the xBZZ associated with postage stamps when selecting which chunks to retain and serve or garbage collect during capacity shortage. This means that popular content will be widely distributed across the network, reducing retrieval latency. - - -### Storage Incentives Details - - When someone wants to upload data to Swarm, they do so by buying [postage stamp batches](/docs/learn/technology/contracts/postage-stamp) with xBZZ. The xBZZ is collected and later redistributed to storage provider nodes to pay for their services. Which node earns the reward is determined by playing a "game". Every 152 Gnosis Chain blocks the game is played, and one node will win the accumulated xBZZ. - -The game has 3 phases, `commit`, `reveal`, and `claim`. In the `reveal` phase of a previous game, an "anchor" overlay address is randomly generated and used to determine the neighborhood for the current round. Only nodes within that neighborhood (meaning they have a certain number of [shared leading bits](/docs/learn/glossary#proximity-order-po) with the neighborhood address) may participate and have a chance to win. - -In the `commit` phase, nodes issue an on-chain transaction including an encrypted hash of the data they are storing (the unencrypted hash is known as the "reserve commitment") along with the [depth](/docs/learn/glossary#2-area-of-responsibility-related-depths) for which they are reporting. This serves as an attestation of the data they are storing without revealing any other information. - -In the `reveal` phase, each node reveals the decryption key for their encrypted hashes thereby publishing the hash. One of the nodes is chosen as the honest node, and from among the honest nodes, one node is chosen as the winner. The winner is chosen at random among the honest nodes, but it is weighted in proportion to each node's stake density. Stake density is calculated as so: - -$$ -\text{stake density} = \text{stake(xBZZ)} \times {2}^\text{storage depth} -$$ - - -### Penalties - -During the `reveal` phase if a nodes' revealed hash does not match the honest nodes' hash, that node will be temporarily frozen and will not be able to participate in a number of upcoming rounds. Currently the freeze period is defined in the [redistribution smart contract](https://github.com/ethersphere/storage-incentives/blob/master/src/Redistribution.sol#L536C1-L536C100) as: - - -$$ -152 \times 2^\text{storage radius} \text{ blocks (at 5s per block)} -$$ - -So for example at a storage radius of 10: - -$$ -152 \times 2^{10} \text{ blocks (at 5s per block)} ≈ \text{ 9 days} -$$ - - -## Bandwidth Incentives (SWAP) - -The Swarm Accounting Protocol (SWAP) is a protocol used in the Swarm network to manage the exchange of resources between nodes. SWAP ensures that node operators collaborate in routing messages and data while protecting the network against frivolous use of bandwidth. - -SWAP works by tracking the relative consumption of bandwidth between nodes. As nodes relay requests and responses, they keep track of their bandwidth usage with each of their peers. Within bounds, peers engage in a service-for-service exchange, where they provide resources to each other based on their relative usage. - -However, once a limit is reached, the party in debt can either wait until their liabilities are amortized over time or can pay by sending cheques that cash out in xBZZ on the blockchain. [Chequebook](/docs/learn/technology/contracts/chequebook/) contracts are used to manage these cheques and ensure that they are valid and can be cashed out correctly. - -SWAP uses built-in incentives to optimize the allocation of bandwidth and storage resources and render Swarm economically self-sustaining. Swarm nodes track their relative bandwidth contribution on each peer connection, and excess debt due to unequal consumption can be settled in xBZZ. Publishers in Swarm must spend xBZZ to purchase the right to write data to Swarm and prepay some rent for long-term storage. - -The SWAP protocol also includes some additional features to prevent abuse or fraud. For example, it can impose limits on how much debt a node can accumulate before requiring payment or require nodes to provide collateral before sending cheques. diff --git a/docs/learn/incentives/overview.md b/docs/learn/incentives/overview.md new file mode 100644 index 000000000..618484ac3 --- /dev/null +++ b/docs/learn/incentives/overview.md @@ -0,0 +1,32 @@ +--- +title: Incentives Overview +id: overview +--- + +One of the key challenges in a decentralised data network is incentivizing users to store data and provide bandwidth. Swarm addresses this challenge with two incentives systems, one which rewards nodes for sharing their storage space and another which rewards them for sharing bandwidth. The incentives system consists of multiple elements which work together to build a self sustaining economic system where nodes are rewarded for honestly providing their resources to the network. + +:::info +Swarm's storage incentives protocols are defined in depth in the [Future Proof Storage](https://www.ethswarm.org/swarm-storage-incentives.pdf) paper published by the Swarm team, and are also discussed in [The Book of Swarm](https://papers.ethswarm.org/p/book-of-swarm/). +::: + +## Storage Incentives + +Storage incentives are used to reward node operators for providing their disk space to the network and storing the data they are responsible for storing over time. The storage incentives system is composed of three smart contracts which work together to enact a self regulating economic system. The postage stamp contract manages payments for uploading data, the redistribution contract manages the redistribution of those payments to storer nodes, and the price oracle contract uses data from the redistribution contract to set the price for postage stamps in the postage stamp contract. + +### Postage Stamps + +Postage stamps are used to pre-purchase the right to upload data on storm, much in the same way that real life postage stamps are used to pre-pay for use of the postal service. Postage stamps are purchased in batches rather than one by one, and are consumed when uploading data to Swarm. Postage stamp batches are purchased using xBZZ through the [postage stamp smart contract](https://gnosisscan.io/address/0x45a1502382541Cd610CC9068e88727426b696293#code). the xBZZ used to pay for postage stamp batches serve as the funds which are redistributed as storage incentives in the redistribution game. The price of postage stamps is set by the price oracle. Read more [here](/docs/learn/incentives/postage-stamps). + +### Redistribution Game + +The redistribution game is used to redistribute the xBZZ paid into the postage stamp contract to full staking nodes which contribute their disk space to the network. The game is designed in such a way that the most profitable way to participate is to honestly store all the data for which a node is responsible. The game's rules are determined by the [redistribution smart contract](https://gnosisscan.io/address/0xFfF73fd14537277B3F3807e1AB0F85E17c0ABea5#code). The results of the game also supply the utilization signal which is used by the price oracle to set the price for postage stamps. Read more [here](/docs/learn/incentives/postage-stamps). + +### Price Oracle + +The price oracle contract uses a utilization signal derived from the redistribution contract to set the price for postage stamps through the postage stamp contract. The utilization signal is based on a measure of data redundancy in the network. The postage stamp price is increased or decreased in order to maintain a healthy degree of redundancy. Read more [here](/docs/learn/incentives/price-oracle). + + +## Bandwidth Incentives + +In addition to storing data over time, nodes must also serve the data they store and must also relay data and messages to other nodes in the network. +Bandwidth incentives are used to reward nodes for relaying data across the network, both by serving up the data they store themselves and by serving as an intermediary relayer of data between other peers. The Swarm Accounting Protocol (SWAP) defines how bandwidth incentives work. At the core of SWAP is the concept of cheques along with the chequebook contract. Read more [here](/docs/learn/incentives/bandwidth-incentives). \ No newline at end of file diff --git a/docs/learn/incentives/postage-stamp.md b/docs/learn/incentives/postage-stamps.md similarity index 94% rename from docs/learn/incentives/postage-stamp.md rename to docs/learn/incentives/postage-stamps.md index e94b356d7..3d2919398 100644 --- a/docs/learn/incentives/postage-stamp.md +++ b/docs/learn/incentives/postage-stamps.md @@ -1,13 +1,15 @@ --- title: Postage Stamp -id: postage-stamp +id: postage-stamps --- -The [postage stamp contract](https://github.com/ethersphere/storage-incentives/blob/master/src/PostageStamp.sol) is one component in the suite of smart contract orchestrating Swarm's [storage incentives](/docs/learn/technology/incentives) which make up the foundation of Swarm's self-sustaining economic system. +Postage stamps are used to pay for storing data on Swarm. They are purchased in batches which represent the pre-paid right to store data on Swarm in the same way that real-life postage stamps represent the pre-paid right to send mail through the postal service. -When a node uploads data to Swarm, it 'attaches' postage stamps to each [chunk](/docs/learn/technology/disc) of data. Postage stamps are purchased in batches rather than one by one. The value assigned to a stamp indicates how much it is worth to persist the associated data on Swarm, which nodes use to prioritize which chunks to remove from their reserve first. +When a node uploads data to Swarm, it 'attaches' postage stamps to each [chunk](/docs/learn/DISC/DISC) of data. The value assigned to a stamp indicates how much it is worth to persist the associated data on Swarm, which nodes use to prioritize which chunks to remove from their reserve first. -The value of a postage stamp decreases over time as if storage rent was regularly deducted from the batch balance. We say that a stamp expires when the batch it is issued from has insufficient balance. A chunk with an expired stamp can not be used in the proof of entitlement storer nodes need to submit in order to get compensated for their contributed storage space, therefore such expired chunks are evicted from nodes' reserves and put into the cache where their continued persistence depends on their popularity. +The value of a postage stamp decreases over time as if storage rent was regularly deducted from the batch balance. A stamp expires when the batch it is issued from has insufficient balance. A chunk with an expired stamp can not be used in the proof storer nodes need to submit in order to get compensated for their contributed storage space in the redistribution game, therefore such expired chunks are evicted from nodes' reserves and put into the cache where their continued persistence depends on their popularity. + +Postage stamp prices are dynamically set based on a utilization signal supplied by the price oracle smart contract. Prices will automatically increase or decrease according to the level of utilization. ## Batch Buckets diff --git a/docs/learn/incentives/price-oracle.md b/docs/learn/incentives/price-oracle.md index 869e9f0a4..c020c7a27 100644 --- a/docs/learn/incentives/price-oracle.md +++ b/docs/learn/incentives/price-oracle.md @@ -3,7 +3,7 @@ title: Price Oracle id: price-oracle --- -The job of the [Oracle contract](https://github.com/ethersphere/price-oracle) is to set the price of Postage Stamps. The oracle contract uses data from the [Postage Stamp contract](/docs/learn/technology/contracts/postage-stamp) in order to set the appropriate price for Postage Stamps. The data in the Postage Stamp contract is used to calculate a "utilisation signal". This signal is an indicator of how much the Swarm network’s data storage capacity is being utilized. Specifically, the signal is a measure of data redundancy on the network. Redundancy is a measure of how many copies of each piece of data can be stored by the network. The protocol targets a fourfold level of data redundancy as a safe minimum. +The job of the [oracle contract](https://github.com/ethersphere/storage-incentives/blob/master/src/PriceOracle.sol) is to set the price of postage stamps. The oracle contract uses data from the [redistribution contract](https://github.com/ethersphere/storage-incentives/blob/master/src/Redistribution.sol) in order to set the appropriate price for postage stamps through the [postage stamp contract](https://github.com/ethersphere/storage-incentives/blob/master/src/PostageStamp.sol). The data from the redistribution contract is used to calculate a "utilisation signal". This signal is an indicator of how much the Swarm network’s data storage capacity is being utilized. Specifically, the signal is a measure of data redundancy on the network. Redundancy is a measure of how many copies of each piece of data can be stored by the network. The protocol targets a fourfold level of data redundancy as a safe minimum. For example, if there is an increase in postage stamps being purchased while the number of nodes remains constant, the data redundancy level will begin to fall as data storers’ available space begins to become reserved. If too many postage stamps are purchased without an equivalent increase in storage providers, the redundancy level may fall below four. In this case, the oracle will increase the price of postage stamps so that it becomes more expensive to store data on Swarm. The higher cost of storage will then lead to less postage stamps being purchased, and will push the redundancy level back up towards four. diff --git a/docs/learn/incentives/redistribution-game.md b/docs/learn/incentives/redistribution-game.md new file mode 100644 index 000000000..8aa01dc58 --- /dev/null +++ b/docs/learn/incentives/redistribution-game.md @@ -0,0 +1,41 @@ +--- +title: Redistribution Game +id: redistribution-game +--- + + +## Redistribution Game + +The redistribution game is used to redistribute the xBZZ which is accumulated by the [postage stamp](/docs/learn/incentives/postage-stamps) contract when postage stamp batches are purchased in order to pay for uploading data to Swarm. The reward paid out in the redistribution game serve as incentive for nodes to continue providing their disk space to the network. The game is designed in such a way that the optimal strategy for participants is to honestly store the data for which they are responsible. + +### Redistribution Game Details + +When someone wants to upload data to Swarm, they do so by buying postage stamp batches with xBZZ. The xBZZ is collected and later paid out to storage provider nodes as a part of the redistribution game. Every 152 Gnosis Chain blocks a single [neighborhood](/docs/learn/DISC/neighbourhoods) is selected to play the redistribution game. For each round of the game, one node from the selected neighborhood will have the chance to win a reward which is paid out from the accumulated xBZZ. + +The game has 3 phases, `commit`, `reveal`, and `claim`. In the `reveal` phase of a previous game, an "anchor" address is randomly generated and used to determine the neighborhood for the current round. + +In the `commit` phase, nodes issue an on-chain transaction including an encrypted hash of the data they are storing (the unencrypted hash is known as the "reserve commitment") along with the [depth](/docs/learn/glossary#2-area-of-responsibility-related-depths) for which they are reporting. This serves as an attestation of the data they are storing without revealing any other information. + +In the `reveal` phase, each node reveals the decryption key for their encrypted hashes thereby publishing the hash. One of the nodes is chosen as the honest node, and from among the honest nodes, one node is chosen as the winner. The winner is chosen at random among the honest nodes, but it is weighted in proportion to each node's stake density. Stake density is calculated as so: + +$$ +\text{stake density} = \text{stake(xBZZ)} \times {2}^\text{storage depth} +$$ + + +### Penalties + +During the `reveal` phase if a nodes' revealed hash does not match the honest nodes' hash, that node will be temporarily frozen and will not be able to participate in a number of upcoming rounds. Currently the freeze period is defined in the [redistribution smart contract](https://github.com/ethersphere/storage-incentives/blob/master/src/Redistribution.sol#L536C1-L536C100) as: + + +$$ +152 \times 2^\text{storage radius} \text{ blocks (at 5s per block)} +$$ + +So for example at a storage radius of 10: + +$$ +152 \times 2^{10} \text{ blocks (at 5s per block)} ≈ \text{ 9 days} +$$ + + diff --git a/docs/learn/introduction.md b/docs/learn/introduction.md index 8c89b6334..78007d226 100644 --- a/docs/learn/introduction.md +++ b/docs/learn/introduction.md @@ -3,7 +3,18 @@ title: Introduction id: introduction --- -### Welcome! Swarm is a peer-to-peer network of Bee nodes that collectively provide censorship resistant decentralised storage and communication services. Swarm's mission is to shape the future towards a self-sovereign global society and permissionless open markets by providing scalable base-layer data storage infrastructure for the decentralised internet. Its incentive system is enforced through smart contracts on the Gnosis Chain blockchain and powered by the xBZZ token, making it economically self-sustaining. +## Swarm Foundation + +The goal of [Swarm Foundation](https://www.ethswarm.org/foundation) is to support and contribute to the creation of technology whose code will be open to all and will allow the storage and exchange of data in a decentralised manner. To this end, the foundation intends in particular to promote and support a community and a sustainable and independent ecosystem for the development of free open source software (FLOSS), allowing for digital services coordinated by crypto-economic incentives that process, distribute and store data. + +Its mission is to empower digital freedom by promoting the development and maintenance of the Swarm network, the base layer of the emerging fair data economy, and the community surrounding this network. + +It does so by supporting many different initiatives, either through financial grants or other types of support, all of which is assessed on a case-by-case basis. + +## Bee Client + +Bee is a Swarm client implemented in Go and is the basic building block for the Swarm network. Bee nodes collectively work together to form a private, decentralized, and self sustaining network for permissionless publishing and data storage. + diff --git a/docs/learn/technology/contracts/overview.md b/docs/learn/smart-contracts.md similarity index 97% rename from docs/learn/technology/contracts/overview.md rename to docs/learn/smart-contracts.md index a8f9c3597..c66d0d6af 100644 --- a/docs/learn/technology/contracts/overview.md +++ b/docs/learn/smart-contracts.md @@ -1,6 +1,6 @@ --- -title: Overview -id: overview +title: Smart Contracts +id: smart-contracts --- ### Token Contracts @@ -12,6 +12,7 @@ id: overview |BZZ Bonding Curve| Ethereum|[`0x4F32Ab778e85C4aD0CEad54f8f82F5Ee74d46904`](https://etherscan.io/address/0x4F32Ab778e85C4aD0CEad54f8f82F5Ee74d46904)|[`0x4F32Ab778e85C4aD0CEad54f8f82F5Ee74d46904`](https://etherscan.io/address/0x4F32Ab778e85C4aD0CEad54f8f82F5Ee74d46904)| ### Storage Incentives Contracts + You can find the Solidity source code for each contract in the [storage incentives Github repo](https://github.com/ethersphere/storage-incentives ). diff --git a/docs/learn/technology/contracts/chequebook.md b/docs/learn/technology/contracts/chequebook.md deleted file mode 100644 index 4fc69d47b..000000000 --- a/docs/learn/technology/contracts/chequebook.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: Chequebook -id: chequebook ---- - -The [chequebook contract](https://github.com/ethersphere/swap-swear-and-swindle/blob/master/contracts/ERC20SimpleSwap.sol) is a smart contract used in the [Swarm Accounting Protocol (SWAP)](/docs/learn/technology/incentives) to manage cheques that are sent between nodes on the network. The contract is responsible for keeping track of the balances of each node and ensuring that cheques are valid and can be cashed out correctly. - -When a node sends a cheque to another node, it includes a signed message that specifies the amount of xBZZ tokens being transferred and the recipient's address. The chequebook contract receives this message and verifies that it is valid by checking the signature and ensuring that the sender has enough funds to cover the transfer. - -:::caution -Settlement of cheques is not enforced by smart contract. -::: - -If the cheque is valid, the contract updates the balances of both nodes accordingly. The recipient can then cash out their xBZZ tokens by sending a transaction to the blockchain that invokes a function in the chequebook contract. This function transfers the specified amount of xBZZ tokens from the sender's account to the recipient's account. - -The chequebook contract also includes some additional features to prevent abuse. For example, it can impose limits on how much debt a node can accumulate before requiring payment. - -The chequebook contract plays an important role in ensuring that SWAP operates smoothly and fairly by providing a secure and reliable way for nodes to exchange value on the network. - diff --git a/docs/learn/technology/contracts/postage-stamp.md b/docs/learn/technology/contracts/postage-stamp.md deleted file mode 100644 index e94b356d7..000000000 --- a/docs/learn/technology/contracts/postage-stamp.md +++ /dev/null @@ -1,233 +0,0 @@ ---- -title: Postage Stamp -id: postage-stamp ---- - -The [postage stamp contract](https://github.com/ethersphere/storage-incentives/blob/master/src/PostageStamp.sol) is one component in the suite of smart contract orchestrating Swarm's [storage incentives](/docs/learn/technology/incentives) which make up the foundation of Swarm's self-sustaining economic system. - -When a node uploads data to Swarm, it 'attaches' postage stamps to each [chunk](/docs/learn/technology/disc) of data. Postage stamps are purchased in batches rather than one by one. The value assigned to a stamp indicates how much it is worth to persist the associated data on Swarm, which nodes use to prioritize which chunks to remove from their reserve first. - -The value of a postage stamp decreases over time as if storage rent was regularly deducted from the batch balance. We say that a stamp expires when the batch it is issued from has insufficient balance. A chunk with an expired stamp can not be used in the proof of entitlement storer nodes need to submit in order to get compensated for their contributed storage space, therefore such expired chunks are evicted from nodes' reserves and put into the cache where their continued persistence depends on their popularity. - -## Batch Buckets - -Postage stamps are issued in batches with a certain number of storage slots partitioned into $$2^{bucketDepth}$$ equally sized address space buckets. Each bucket is responsible for storing chunks that fall within a certain range of the address space. When uploaded, files are split into 4kb chunks, each chunk is assigned a unique address, and each chunk is then assigned to the bucket in which its address falls. Falling into the same range means a match on `n` leading bits of the chunk and bucket. This restriction is necessary to ensure (incentivise) uniform utilisation of the address space and is fair since the distribution of content addresses are uniform as well. Uniformity depth is the number of leading bits determining bucket membership (also called `bucket depth`). The uniformity depth is set to 16, so there are a total of $$2^{16} = 65,536$$ buckets. - -### Bucket Size - -Each bucket has a certain number of slots which can be "filled" by chunks (In other words, for each bucket, a certain number of chunks can be stamped). Once all the slots of a bucket are filled, the entire postage batch will be fully utilised and can no longer be used to upload additional data. - -Together with `batch depth`, `bucket depth`determines how many chunks are allowed in each bucket. The number of chunks allowed in each bucket is calculated like so: - -$$ -2^{(batchDepth - bucketDepth)} -$$ - -So with a batch depth of 24 and a bucket depth of 16: - -$$ -2^{(24 - 16)} = 2^{8} = 256 \text{ chunks/bucket} -$$ - -:::info -Note that due how buckets fill as described above, a batch can become fully utilised before its theoretical maximum volume has been reached. See [batch utilisation section below](/docs/learn/technology/contracts/postage-stamp#batch-utilisation) for more information. -::: - -## Batch Depth and Batch Amount - -Each batch of stamps has two key parameters, `batch depth` and `amount`, which are recorded on Gnosis Chain at issuance. Note that these "depths" do not refer to the depth terms used to describe topology which are outlined [here in the glossary](/docs/learn/glossary#depth-types). - -### Batch Depth - -:::caution -The minimum value for `depth` is 17, however higher depths are recommended for most use cases due to the [mechanics of stamp batch utilisation](#batch-utilisation). See [the depths utilisation table](#effective-utilisation-table) to help decide which depth is best for your use case. -::: - -`Batch depth` determines how much data can be stored by a batch. The number of chunks which can be stored (stamped) by a batch is equal to $$2^{batchDepth}$$. - -For a batch with a `batch depth` of 24, a maximum of $$2^{24} = 16,777,216$$ chunks can be stamped. - -Since we know that one chunk can store 4 kb of data, we can calculate the theoretical maximum amount of data which can be stored by a batch from the `batch depth`. - -$$ -\text{Theoretical maximum batch volume} = 2^{batchDepth} \times \text{4 kb} -$$ - -However, due to the way postage stamp batches are utilised, batches will become fully utilised before stamping the theoretical maximum number of chunks. Therefore when deciding which batch depth to use, it is important to consider the effective amount of data that can be stored by a batch, and not the theoretical maximum. The effective rate of utilisation increases along with the batch depth. See [section on stamp batch utilisation below](/docs/learn/technology/contracts/postage-stamp#batch-utilisation) for more information. - -### Batch Amount (& Batch Cost) - -The `amount` parameter is the quantity of xBZZ in PLUR $$(1 \times 10^{16}PLUR = 1 \text{ xBZZ})$$ that is assigned per chunk in the batch. The total number of xBZZ that will be paid for the batch is calculated from this figure and the `batch depth` like so: - -$$2^{batchDepth} \times {amount}$$ - -The paid xBZZ forms the `balance` of the batch. This `balance` is then slowly depleted as time ticks on and blocks are mined on Gnosis Chain. - -For example, with a `batch depth` of 24 and an `amount` of 1000000000 PLUR: - -$$ -2^{24} \times 1000000000 = 16777216000000000 \text{ PLUR} = 1.6777216 \text{ xBZZ} -$$ - -### Calculating `amount` needed for desired TTL - -The desired `amount` can be easily estimated based on the current postage stamp price and the desired amount of storage time in seconds with the given Gnosis block time of 5 seconds and the stamp price. For the example below we assume a stamp price of 24000 PLUR / chunk / block: - -:::info -The postage stamp price is dynamically determined according to a network utilisation signal. You can view the current storage price at [Swarmscan.io](https://swarmscan.io/). -::: - -$$ -(\text{stamp price} \div \text{block time in seconds}) \times \text{storage time in seconds} -$$ - -There are 1036800 seconds in 12 days, so the `amount` value required to store for 12 days can be calculated: - -$$ -(\text{24000} \div \text{5}) \times \text{1036800} = 4976640000 -$$ - -So we can use 4976640000 as our `amount` value in order for our postage batch to store data for 12 days. - - - -## Batch Utilisation - -### Immutable Batches - -Utilisation of an immutable batch is computed using a hash map of size $$2^{bucketDepth}$$ which is $$2^{16}$$ for all batches, so 65536 total entries. For the keys of the key-value pairs of the hash map, the keys are 16 digit binary numbers from 0 to 65535, and the value is a counter. - -![](/img/batches_01.png) - -As chunks are uploaded to Swarm, each chunk is assigned to a bucket based the first 16 binary digits of the [chunk's hash](/docs/learn/technology/disc#chunks). The chunk will be assigned to whichever bucket's key matches the first 16 bits of its hash, and that bucket's counter will be incremented by 1. - -The batch is deemed "full" when ANY of these counters reach a certain max value. The max value is computed from the batch depth as such: $$2^{(batchDepth-bucketDepth)}$$. For example with batch depth of 24, the max value is $$2^{(24-16)}$$ or 256. A bucket can be thought of as have a number of "slots" equal to this maximum value, and every time the bucket's counter is incremented, one of its slots gets filled. - -In the diagram below, the batch depth is 18, so there are $$2^{(18-16)}$$ or 4 slots for each bucket. The utilisation of a batch is simply the highest number of filled slots out of all 65536 entries or "buckets". In this batch, none of the slots in any of the buckets have yet been filled with 4 chunks, so the batch is not yet fully utilised. The most filled slots out of all buckets is 2, so the stamp batch's utilisation is 2 out of 4. - -![](/img/batches_02.png) - -As more chunks get uploaded and stamped, the bucket slots will begin to fill. As soon as the slots for any SINGLE bucket get filled, the entire batch is considered 100% utilised and can no longer be used to upload additional chunks. - -![](/img/batches_03.png) - - -### Mutable Batches - -Mutable batches use the same hash map structure as immutable batches, however its utilisation works very differently. In contrast with immutable batches, mutable batches are never considered fully utilised. Rather, at the point where an immutable batch would be considered fully utilised, a mutable batch can continue to stamp chunks. However, if any chunk's address lands in a bucket whose slots are already filled, rather than the batch becoming fully utilised, that bucket's counter gets reset, and the new chunk will replace the oldest chunk in that bucket. - -![](/img/batches_04.png) - -Therefore rather than speaking of the number of slots as determining the utilisation of a batch as with immutable batches, we can think of the slots as defining a limit to the amount of data which can be uploaded before old data starts to get overwritten. - -### Which Type of Batch to Use - -Immutable batches are suitable for long term storage of data or for data which otherwise does not need to be changed and should never be overwritten, such as records archival, legal documents, family photos, etc. - -Mutable batches are great for data which needs to be frequently updated and does not require a guarantee of immutability. For example, a blog, personal or company websites, ephemeral messaging app, etc. - -The default batch type when unspecified is immutable. This can be modified through the Bee api by setting the `immutable` header with the [`\stamps POST` endpoint](https://docs.ethswarm.org/api/#tag/Transaction/paths/~1transactions~1%7BtxHash%7D/post) to `false`. - -### Re-uploading - -There are several nuances to how the re-uploading of previously uploaded data to Swarm affect stamp batch utilisation. For single chunks, the behaviour is relatively straightforward, however with files that must get split into multiple chunks, the behaviour is less straightforward. - -#### Single chunks - -When a chunk which has previously been uploaded to Swarm is re-uploaded from the same node while the initial postage batch it was stamped by is still valid, no additional stamp will be utilised from the batch. However if the chunk comes from a different node than the original node, then a stamp WILL be utilised, and as long as at least one of the batches the chunk was stamped by is still valid, the chunk will be retained by storer nodes in its neighbourhood. - -#### Files - -When an identical file is re-uploaded then the stamp utilisation behaviour will be the same as with single chunks described in the section above. However, if part of the file has been modified and then re-uploaded, stamp utilisation behaviour will be different. This is due to how the chunking process works when a file is uploaded to Swarm. When uploaded to Swarm, files are split into 4kb sized chunks (2^12 bytes), and each chunk is assigned an address which is based on the content of the chunk. If even a single bit within the chunk is modified, then the address of the chunk will also be modified. - -When a file which was previously uploaded with a single bit flipped is again split into chunks by a node before being uploaded to Swarm, then only the chunk with the flipped bit will have an updated address and require the utilisation of another stamp. The content of all the other chunks will remain the same, and therefore will not require new stamps to be utilised. - -However, if rather than flipping a single bit we add some data to our file, this could cause changes in the content of every chunk of the file, meaning that every single chunk must be re-stamped. We can use a simplified example of why this is the case to more easily understand the stamp utilisation behaviour. Let us substitute a message containing letters of the alphabet rather than binary data. - -Our initial message consists of 16 letters: - - -> abcdefghijklmnop - -When initially uploaded, it will be split into four chunks of four letters each: - -> abcdefghijklmnop => abcd | efgh | ijkl | mnop - -Let us look at what happens when a single letter is changed (here we change a to z): - -> abcdefghijklmnop => zbcd | efgh | ijkl | mnop - -In this case, only the first chunk is affected, all the other chunks retain the same content. - -> Now let is examine the case where a new letter is added rather than simply modifying an already existing one. Here we add the number 1 at the start of the message: - -> 1abcdefghijklmnop => 1abc | defg | hijk | lmno | p - -As you can see, by adding a single new letter at the start of the message, all the letters are shifted to the right by a single position, which a has caused EVERY chunk in the message to be modified rather than just a single chunk. - -#### Affect on Batch Utilisation - -The implications of this behaviour are that even a small change to the data of a file may cause every single chunk from the file to be changed, meaning that new stamps must be utilised for every chunk from that file. In practice, this could lead to high costs in data which is frequently changed, since for even a small change, every chunk from the file must be re-stamped. - - -### Implications for Swarm Users - -Due to the nature of batch utilisation described above, batches are often fully utilised before reaching their theoretical maximum storage amount. However as the batch depth increases, the chance of a postage batch becoming fully utilised early decreases. At batch depth 24, there is a 0.1% chance that a batch will be fully utilised/start replacing old chunks before reaching 64.33% of its theoretical maximum. - -Let's look at an example to make it clearer. Using the method of calculating the theoretical maximum storage amount [outlined above](/docs/learn/technology/contracts/postage-stamp#batch-depth), we can see that for a batch depth of 24, the theoretical maximum amount which can be stored is 68.72 gb: - -$$ -2^{24+12} = \text{68,719,476,736 bytes} = \text{68.72 gb} -$$ - -Therefore we should use 64.33% the effective rate of usage for the stamp batch: - -$$ -\text{68.72 gb} \times{0.6433} = \text{44.21 gb } -$$ - -:::info -The details of how the effective rates of utilisation are calculated will be published soon. -::: - -### Effective Utilisation Table - - -When a user buys a batch of stamps they may make the naive assumption that they will be able to upload data equal to the sum total size of the maximum capacity of the batch. However, in practice this assumption is incorrect, so it is essential that Swarm users understand the relationship between batch depth and the theoretical and effective volumes of a batch. - -The provided table shows the effective volume for each batch depth from 20 to 41 (note that currently the minimum stamp batch depth is 17, however 22 is the first depth with an effective volume above zero). The utilisation rate is the rate of utilisation of the theoretical max volume that a stamp batch can reach with a 0.1% failure rate (that is, there is only a one-in-a-thousand chance that the difference between the actual effectively utilised volume and effective volume shown in the table is greater than 0.1%). The "effective volume" figure shows the actual amount of data which can be stored at the effective rate. The effective volume figure is the one which should be used as the de-facto maximum amount of data that a batch can store before becoming either fully utilised (for immutable batches), or start overwriting older chunks (mutable batches). - - -| Batch Depth | Utilisation Rate | Theoretical Max Volume | Effective Volume | -|-------------|------------------|-------------------------|-------------------| -| 20 | 0.00% | 4.29 GB | 0.00 B | -| 21 | 0.00% | 8.59 GB | 0.00 B | -| 22 | 28.67% | 17.18 GB | 4.93 GB | -| 23 | 49.56% | 34.36 GB | 17.03 GB | -| 24 | 64.33% | 68.72 GB | 44.21 GB | -| 25 | 74.78% | 137.44 GB | 102.78 GB | -| 26 | 82.17% | 274.88 GB | 225.86 GB | -| 27 | 87.39% | 549.76 GB | 480.43 GB | -| 28 | 91.08% | 1.10 TB | 1.00 TB | -| 29 | 93.69% | 2.20 TB | 2.06 TB | -| 30 | 95.54% | 4.40 TB | 4.20 TB | -| 31 | 96.85% | 8.80 TB | 8.52 TB | -| 32 | 97.77% | 17.59 TB | 17.20 TB | -| 33 | 98.42% | 35.18 TB | 34.63 TB | -| 34 | 98.89% | 70.37 TB | 69.58 TB | -| 35 | 99.21% | 140.74 TB | 139.63 TB | -| 36 | 99.44% | 281.47 TB | 279.91 TB | -| 37 | 99.61% | 562.95 TB | 560.73 TB | -| 38 | 99.72% | 1.13 PB | 1.12 PB | -| 39 | 99.80% | 2.25 PB | 2.25 PB | -| 40 | 99.86% | 4.50 PB | 4.50 PB | -| 41 | 99.90% | 9.01 PB | 9.00 PB | - - -:::info -This table is based on preliminary calculations and may be subject to change. -::: - -Nodes' storage is actually defined as a number of chunks with a size of 4kb (2^12 bytes) each, but in fact some [SOC](/docs/learn/technology/disc#content-addressed-chunks-and-single-owner-chunks) chunks can be a few bytes longer, and some chunks can be smaller, so the conversion is not precise. Furthermore, due to the way Swarm represents files in a Merkle tree, the intermediate chunks are additional overhead which must also be accounted for. - -Additionally, when a node stores chunks it uses additional indexes — therefore the disk space a maximally filled reserve would demand cannot be calculated with perfect accuracy. diff --git a/docs/learn/technology/contracts/price-oracle.md b/docs/learn/technology/contracts/price-oracle.md deleted file mode 100644 index 869e9f0a4..000000000 --- a/docs/learn/technology/contracts/price-oracle.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Price Oracle -id: price-oracle ---- - -The job of the [Oracle contract](https://github.com/ethersphere/price-oracle) is to set the price of Postage Stamps. The oracle contract uses data from the [Postage Stamp contract](/docs/learn/technology/contracts/postage-stamp) in order to set the appropriate price for Postage Stamps. The data in the Postage Stamp contract is used to calculate a "utilisation signal". This signal is an indicator of how much the Swarm network’s data storage capacity is being utilized. Specifically, the signal is a measure of data redundancy on the network. Redundancy is a measure of how many copies of each piece of data can be stored by the network. The protocol targets a fourfold level of data redundancy as a safe minimum. - -For example, if there is an increase in postage stamps being purchased while the number of nodes remains constant, the data redundancy level will begin to fall as data storers’ available space begins to become reserved. If too many postage stamps are purchased without an equivalent increase in storage providers, the redundancy level may fall below four. In this case, the oracle will increase the price of postage stamps so that it becomes more expensive to store data on Swarm. The higher cost of storage will then lead to less postage stamps being purchased, and will push the redundancy level back up towards four. - -Conversely, if the amount of Stamps being purchased decreases while the number of storage provider nodes remains constant, the redundancy level will increase as there are fewer chunks of data to be distributed amongst the same number of nodes. In this case, the oracle will decrease the Postage Stamp price in order to promote more data storers to store their data on Swarm. The lower cost of storage will then lead to more Postage Stamps being purchased and push the redundancy level back down towards four. - diff --git a/docs/learn/technology/incentives.md b/docs/learn/technology/incentives.md deleted file mode 100644 index 756864b56..000000000 --- a/docs/learn/technology/incentives.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Incentives -id: incentives ---- - -One of the key challenges in a decentralised data network is incentivising users to store data and provide bandwidth. Swarm addresses this challenge with two incentives systems, one which rewards nodes for sharing their storage space and another which rewards them for sharing bandwidth. - - -## Storage Incentives - -Swarm's storage incentives protocol is defined in depth in the [Future Proof Storage](https://www.ethswarm.org/swarm-storage-incentives.pdf) paper published by the Swarm team. - -Swarm's storage incentives are based on [postage stamps](/docs/learn/technology/contracts/postage-stamp), which serve as verifiable proof of payment associated with chunks witnessed by their owner's signature. Postage stamps signal chunks' relative importance by ascribing them with xBZZ quantity which storer nodes can use when selecting which chunks to retain and which to evict from their reserve when their reserve capacity is exceeded. - -The amount of xBZZ required for a postage stamp depends on the amount of data being stored and the duration for which it will be stored. The longer a chunk is stored, the more xBZZ is required for the postage stamp. This ensures that users are incentivised to store data for longer periods, which helps ensure that data remains available in the network. - -Storer nodes can use the xBZZ associated with postage stamps when selecting which chunks to retain and serve or garbage collect during capacity shortage. This means that popular content will be widely distributed across the network, reducing retrieval latency. - - -### Storage Incentives Details - - When someone wants to upload data to Swarm, they do so by buying [postage stamp batches](/docs/learn/technology/contracts/postage-stamp) with xBZZ. The xBZZ is collected and later redistributed to storage provider nodes to pay for their services. Which node earns the reward is determined by playing a "game". Every 152 Gnosis Chain blocks the game is played, and one node will win the accumulated xBZZ. - -The game has 3 phases, `commit`, `reveal`, and `claim`. In the `reveal` phase of a previous game, an "anchor" overlay address is randomly generated and used to determine the neighborhood for the current round. Only nodes within that neighborhood (meaning they have a certain number of [shared leading bits](/docs/learn/glossary#proximity-order-po) with the neighborhood address) may participate and have a chance to win. - -In the `commit` phase, nodes issue an on-chain transaction including an encrypted hash of the data they are storing (the unencrypted hash is known as the "reserve commitment") along with the [depth](/docs/learn/glossary#2-area-of-responsibility-related-depths) for which they are reporting. This serves as an attestation of the data they are storing without revealing any other information. - -In the `reveal` phase, each node reveals the decryption key for their encrypted hashes thereby publishing the hash. One of the nodes is chosen as the honest node, and from among the honest nodes, one node is chosen as the winner. The winner is chosen at random among the honest nodes, but it is weighted in proportion to each node's stake density. Stake density is calculated as so: - -$$ -\text{stake density} = \text{stake(xBZZ)} \times {2}^\text{storage depth} -$$ - - -### Penalties - -During the `reveal` phase if a nodes' revealed hash does not match the honest nodes' hash, that node will be temporarily frozen and will not be able to participate in a number of upcoming rounds. Currently the freeze period is defined in the [redistribution smart contract](https://github.com/ethersphere/storage-incentives/blob/master/src/Redistribution.sol#L536C1-L536C100) as: - - -$$ -152 \times 2^\text{storage radius} \text{ blocks (at 5s per block)} -$$ - -So for example at a storage radius of 10: - -$$ -152 \times 2^{10} \text{ blocks (at 5s per block)} ≈ \text{ 9 days} -$$ - - -## Bandwidth Incentives (SWAP) - -The Swarm Accounting Protocol (SWAP) is a protocol used in the Swarm network to manage the exchange of resources between nodes. SWAP ensures that node operators collaborate in routing messages and data while protecting the network against frivolous use of bandwidth. - -SWAP works by tracking the relative consumption of bandwidth between nodes. As nodes relay requests and responses, they keep track of their bandwidth usage with each of their peers. Within bounds, peers engage in a service-for-service exchange, where they provide resources to each other based on their relative usage. - -However, once a limit is reached, the party in debt can either wait until their liabilities are amortized over time or can pay by sending cheques that cash out in xBZZ on the blockchain. [Chequebook](/docs/learn/technology/contracts/chequebook/) contracts are used to manage these cheques and ensure that they are valid and can be cashed out correctly. - -SWAP uses built-in incentives to optimize the allocation of bandwidth and storage resources and render Swarm economically self-sustaining. Swarm nodes track their relative bandwidth contribution on each peer connection, and excess debt due to unequal consumption can be settled in xBZZ. Publishers in Swarm must spend xBZZ to purchase the right to write data to Swarm and prepay some rent for long-term storage. - -The SWAP protocol also includes some additional features to prevent abuse or fraud. For example, it can impose limits on how much debt a node can accumulate before requiring payment or require nodes to provide collateral before sending cheques. diff --git a/sidebars.js b/sidebars.js index a3943fc19..40625bf80 100644 --- a/sidebars.js +++ b/sidebars.js @@ -2,7 +2,6 @@ module.exports = { learn: [ 'learn/introduction', 'learn/what-is-swarm', - { type: 'category', label: 'DISC Storage', @@ -16,40 +15,29 @@ module.exports = { }, { type: 'category', - label: 'Technology', + label: 'Incentives', items: [ - 'learn/technology/incentives', - 'learn/technology/pss', - 'learn/technology/act', - { - type: 'category', - label: 'Contracts', - items: [ - - 'learn/technology/contracts/overview', - 'learn/technology/contracts/chequebook', - 'learn/technology/contracts/postage-stamp', - 'learn/technology/contracts/price-oracle', - - ], - collapsed: false - }, + 'learn/incentives/overview', + 'learn/incentives/redistribution-game', + 'learn/incentives/postage-stamps', + 'learn/incentives/bandwidth-incentives', + 'learn/incentives/price-oracle', ], collapsed: false }, { type: 'category', - label: 'Ecosystem', + label: 'Features', items: [ - 'learn/ecosystem/swarm-foundation', - 'learn/ecosystem/fair-data-society', - 'learn/ecosystem/community', - 'learn/ecosystem/grants-bounties', - 'learn/ecosystem/awesome', + 'learn/features/pss', + 'learn/features/access-control', ], collapsed: false }, + 'learn/smart-contracts', 'learn/tokens', + 'learn/ecosystem', + 'learn/fair-data-society', 'learn/glossary', 'learn/faq', ], From b3a7ddd4d71733ae26d65af954cfd0385a0335a8 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Thu, 24 Oct 2024 20:43:21 +0700 Subject: [PATCH 08/25] fix broken links --- docs/bee/installation/fund-your-node.md | 2 +- docs/bee/installation/install.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/bee/installation/fund-your-node.md b/docs/bee/installation/fund-your-node.md index 6f5a1d7cf..4a4488112 100644 --- a/docs/bee/installation/fund-your-node.md +++ b/docs/bee/installation/fund-your-node.md @@ -43,7 +43,7 @@ zero. If you want to get your Bee node up and running as easily as possible, then you can set its -[`--swap-initial-deposit`](/docs/bee/working-with-bee/configuration#--swap-initial-deposit) +[`--swap-initial-deposit`](/docs/bee/working-with-bee/configuration) value to zero. This means that your node's chequebook will not get funded with xBZZ, meaning that other nodes will only serve it within the free tier bandwidth threshold. diff --git a/docs/bee/installation/install.md b/docs/bee/installation/install.md index e89f91f16..70c577368 100644 --- a/docs/bee/installation/install.md +++ b/docs/bee/installation/install.md @@ -282,7 +282,7 @@ sudo vi /usr/local/etc/swarm-bee/bee.yaml ### Config for `bee start` -When running your node using `bee start` you can set options using either command line flags, environment variables, or a YAML configuration file. See the configuration section for [more information on setting options for running a node with `bee start`](/docs/bee/working-with-bee/configuration#configuration-for-bee-start). +When running your node using `bee start` you can set options using either command line flags, environment variables, or a YAML configuration file. See the configuration section for [more information on setting options for running a node with `bee start`](/docs/bee/working-with-bee/configuration). No default YAML configuration file is generated to be used with the `bee start` command, so it must be generated and placed in the default config directory if you wish to use it to set your node's options. You can view the default configuration including the default config directory for your system with the `bee printconfig` command. From 548b7142af799da43eeb42db687bf952dfcdeb22 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Thu, 24 Oct 2024 21:00:03 +0700 Subject: [PATCH 09/25] fix inconsistent spelling --- docs/bee/working-with-bee/backups.md | 2 +- docs/bee/working-with-bee/staking.md | 4 +- docs/develop/access-the-swarm/pinning.md | 2 +- docs/develop/contribute/protocols.md | 34 ++++++++--------- .../develop/tools-and-features/chunk-types.md | 2 +- docs/develop/tools-and-features/pss.md | 8 ++-- docs/learn/DISC/kademlia.md | 2 +- .../{neighbourhoods.md => neighborhoods.md} | 38 +++++++++---------- docs/learn/glossary.md | 2 +- docs/learn/incentives/postage-stamps.md | 6 +-- docs/learn/incentives/redistribution-game.md | 2 +- sidebars.js | 2 +- 12 files changed, 52 insertions(+), 52 deletions(-) rename docs/learn/DISC/{neighbourhoods.md => neighborhoods.md} (54%) diff --git a/docs/bee/working-with-bee/backups.md b/docs/bee/working-with-bee/backups.md index 17ffc9e62..8a3487ba6 100644 --- a/docs/bee/working-with-bee/backups.md +++ b/docs/bee/working-with-bee/backups.md @@ -88,7 +88,7 @@ To use `swarm.key` to manage the Gnosis account for a node through Metamask or o ### Statestore and Localstore. -The `statestore` retains data related to its operation, and the `localstore` contains chunks locally which are frequently requested, pinned in the node, or are in the node's neighbourhood of responsibility. +The `statestore` retains data related to its operation, and the `localstore` contains chunks locally which are frequently requested, pinned in the node, or are in the node's neighborhood of responsibility. :::info As the data in `statestore` and `localstore` continually changes during normal operation of a node, when taking a backup the node should first be stopped and not re-connected to the Swarm network until restoring from the backup (otherwise the `statestore` and `localstore` files will get out of sync with the network). It is possible to restore using out of sync `statestore` and `localstore` files, however it may lead to data loss or unexpected behavior related to chunk uploads, postage stamps, and more. diff --git a/docs/bee/working-with-bee/staking.md b/docs/bee/working-with-bee/staking.md index 721899c7c..52626c16c 100644 --- a/docs/bee/working-with-bee/staking.md +++ b/docs/bee/working-with-bee/staking.md @@ -3,13 +3,13 @@ title: Staking id: staking --- -In order to participate in the redistribution of xBZZ from uploaders to storers, storers must first deposit a non-refundable xBZZ stake with a smart contract. Then, they are going to be chosen for payout with a probability proportional to their stake in their neighbourhood, as long as they can log storing the part of the content that they are supposed to be storing according to protocol rules. +In order to participate in the redistribution of xBZZ from uploaders to storers, storers must first deposit a non-refundable xBZZ stake with a smart contract. Then, they are going to be chosen for payout with a probability proportional to their stake in their neighborhood, as long as they can log storing the part of the content that they are supposed to be storing according to protocol rules. In order to participate in redistribution, storers need to do the following: - Join the network and download all the data that the protocol assigns to them. They can only participate if they are fully synchronised with the network. - Deposit a stake with the staking contract. There is a minimum staking requirement, presently 10 xBZZ. It can change in the future. -- Stay online and fully synced, so that when a redistribution round comes, their node can check whether their neighbourhood (nodes that are assigned the same content to store) has been selected and if so, they can perform a certain calculation (a random sampling) on their content and submit the result to the redistribution contract. This happens in two phases (commit and reveal), so that the nodes cannot know the results of others’ calculations when committing to their own. +- Stay online and fully synced, so that when a redistribution round comes, their node can check whether their neighborhood (nodes that are assigned the same content to store) has been selected and if so, they can perform a certain calculation (a random sampling) on their content and submit the result to the redistribution contract. This happens in two phases (commit and reveal), so that the nodes cannot know the results of others’ calculations when committing to their own. - Round length is estimated around 15 minutes (152 blocks to be precise), though it can be extended. Amongst the nodes that agree with the correct result, one is chosen — with a probability in proportion to their stake — as the winner. The winner must execute an on-chain transaction claiming their reward, which is the entire pot of storage rent paid since the previous round, or even more, if the previous pot has not been claimed at that time. diff --git a/docs/develop/access-the-swarm/pinning.md b/docs/develop/access-the-swarm/pinning.md index 457954c38..ce7d4f77b 100644 --- a/docs/develop/access-the-swarm/pinning.md +++ b/docs/develop/access-the-swarm/pinning.md @@ -3,7 +3,7 @@ title: Pinning id: pinning --- -Each Bee node is configured to reserve a certain amount of memory on your computer's hard drive to store and serve chunks within their _neighbourhood of responsibility_ for other nodes in the Swarm network. Once this alloted space has been filled, each Bee node deletes older chunks to make way for newer ones as they are uploaded by the network. +Each Bee node is configured to reserve a certain amount of memory on your computer's hard drive to store and serve chunks within their _neighborhood of responsibility_ for other nodes in the Swarm network. Once this alloted space has been filled, each Bee node deletes older chunks to make way for newer ones as they are uploaded by the network. Each time a chunk is accessed, it is moved back to the end of the deletion queue, so that regularly accessed content stays alive in the network and is not deleted by a node's garbage collection routine. diff --git a/docs/develop/contribute/protocols.md b/docs/develop/contribute/protocols.md index 47d45eb59..bb398841d 100644 --- a/docs/develop/contribute/protocols.md +++ b/docs/develop/contribute/protocols.md @@ -84,7 +84,7 @@ Conversely the downstream should be informed when the upstream is no longer inte Swarm involves a direct storage scheme of fixed size where chunks are stored on nodes with address corresponding to the chunk address. -The syncing protocols act in such a way that they reach those neighbourhoods whenever a request is initiated. +The syncing protocols act in such a way that they reach those neighborhoods whenever a request is initiated. Such a route is sure to exist as a result of the Kademlia topology of keep-alive connections between peers. @@ -215,9 +215,9 @@ Pushsync protocol is responsible for ensuring delivery of the chunk to its presc It works in a similar way to the Retrieval protocol in the sense that the chunk is being passed to a peer whose address is closest to the chunk address and a custody receipt is received in response. -Then the same process is repeated until the chunk eventually reaches the storer node located in a certain "neighbourhood". +Then the same process is repeated until the chunk eventually reaches the storer node located in a certain "neighborhood". -Since the Pushsync protocol is a "mirror" version of the Retrieval protocol - it ensures that a successfully uploaded chunk is retrievable from the same "neighbourhood" by the virtue of the fact that nodes in a neighbourhood are connected to each other. +Since the Pushsync protocol is a "mirror" version of the Retrieval protocol - it ensures that a successfully uploaded chunk is retrievable from the same "neighborhood" by the virtue of the fact that nodes in a neighborhood are connected to each other. ### Multiplexing @@ -225,29 +225,29 @@ Multiplexing is a recommended node strategy for the push sync protocol that invo #### Context -The current implementation of the push sync protocol aims to push a chunk to the closest node in the neighbourhood which is then supposed to give out a receipt. +The current implementation of the push sync protocol aims to push a chunk to the closest node in the neighborhood which is then supposed to give out a receipt. This is motivated by the retrieval protocol that aims to find the chunk at this closest node. -When the closest node hands out a receipt, this node also replicates the chunk to 3 peers in the neighbourhood which are further away from the chunk than him. +When the closest node hands out a receipt, this node also replicates the chunk to 3 peers in the neighborhood which are further away from the chunk than him. -This replication takes place to ensure that the chunk is not lost when the closest node shuts down before the chunk is not pull-sync'ed and to speed up the spreading of the chunk in the neighbourhood, in advance of pull sync. +This replication takes place to ensure that the chunk is not lost when the closest node shuts down before the chunk is not pull-sync'ed and to speed up the spreading of the chunk in the neighborhood, in advance of pull sync. #### Problem Treating the closest node as a single target of push sync is fragile. If this peer has a badly-performing blockchain backend, slow or incomplete connectivity or is malicious, it may not spread the chunk and/or does not respond with a receipt. -In this case, currently, the originator must retry the entire push-sync operation many times before the other peers within neighbourhood recognise the improper behaviour. +In this case, currently, the originator must retry the entire push-sync operation many times before the other peers within neighborhood recognise the improper behaviour. -In-neighbourhood retries are ideally avoided because such retries might cause the downstream timeouts to expire. +In-neighborhood retries are ideally avoided because such retries might cause the downstream timeouts to expire. -In case of incomplete connectivity, the push sync protocol can end at a different branch of the neighbourhood than the retrieval protocol--causing the chunk not to be retrievable. +In case of incomplete connectivity, the push sync protocol can end at a different branch of the neighborhood than the retrieval protocol--causing the chunk not to be retrievable. It should be noted that the pull sync protocol (may?) remedies this problem with a small time-delay. -#### Multiplexing: early replication within neighbourhood +#### Multiplexing: early replication within neighborhood -The first node in the push sync forward chain that falls within the neighbourhood acts as *multiplexer*, i.e., it forwards the request to a number of closest nodes and responds with a self-signed receipt. +The first node in the push sync forward chain that falls within the neighborhood acts as *multiplexer*, i.e., it forwards the request to a number of closest nodes and responds with a self-signed receipt. Thus in achieving retrievability and security via early replication, we do not critically rely on the closest node to be available any more. @@ -257,15 +257,15 @@ We define the different roles peers have as part of the push sync forwarding cha - originator -- creator of the request - forwarder -- closer to the chunk than the originator, further away away than the 1-before node. -- multiplexer -- first node in the forward chain who is in the neighbourhood +- multiplexer -- first node in the forward chain who is in the neighborhood - closest nodes -- according to the downstream node (usually the multiplexer), within the `n` closest nodes to the chunks (not including self) we describe the envisioned flow of push sync by describing the intended behaviour strategy of the various roles. 1. originator sends chunk to a peer closer to the chunk. -2. forwarder(s) forwards chunk that ends up with a node already within the neighbourhood that acts as multiplexer +2. forwarder(s) forwards chunk that ends up with a node already within the neighborhood that acts as multiplexer 3. The multiplexer concurrently sends the chunk to 3 closest nodes attaching a multiplexing-list as part of the protocol message. At the same time they respond to their upstream peer with a self-signed receipt (unless the multiplexer is itself the originator). -4. Non-multiplexing closest nodes, i.e., nodes in the neighbourhood that receive the pushsync message from a not-closest neighbour with a multiplexing list included, validate whether, based on their view, the multiplexing list covers all 3 closest nodes (potentially including the peer and/or the upstream peer themselves). If not, the node forwards the chunk to the peers left out. These peers are also added to the multiplexing list received from upstream and the extended list is attached with the chunk pushed. +4. Non-multiplexing closest nodes, i.e., nodes in the neighborhood that receive the pushsync message from a not-closest neighbour with a multiplexing list included, validate whether, based on their view, the multiplexing list covers all 3 closest nodes (potentially including the peer and/or the upstream peer themselves). If not, the node forwards the chunk to the peers left out. These peers are also added to the multiplexing list received from upstream and the extended list is attached with the chunk pushed. If the multiplexer node does not know a closest peer *p* but several of its chosen closest nodes do, then that node *p* will receive the same pushsynced chunk multiple times @@ -303,11 +303,11 @@ While the other described protocols are request scoped, Pullsync is a subscripti It's worth mentioning that the chunks that are being synchronized between nodes always travel alongside their corresponding postage stamps. -Pullsync's role is to help synchronization of the chunks between neighbourhood nodes. It bootstraps new nodes by filling up their storage with the chunks in range of their storage radius and also ensures eventual consistency - by making sure that the chunks will gradually migrate to their storer nodes. +Pullsync's role is to help synchronization of the chunks between neighborhood nodes. It bootstraps new nodes by filling up their storage with the chunks in range of their storage radius and also ensures eventual consistency - by making sure that the chunks will gradually migrate to their storer nodes. There are two kinds of syncing: -- historical syncing: catching up with content that arrived to relevant neighbourhood before this session started (after an outage or for completely new nodes). +- historical syncing: catching up with content that arrived to relevant neighborhood before this session started (after an outage or for completely new nodes). - live syncing: fetching the chunks that are received after the session has started. The chunks are served in batches (ordered by timestamp) and they cover contiguous ranges. @@ -383,7 +383,7 @@ When choosing a peer in relation to a given address - in addition to the distanc - the historical performance of the given peer, both in terms of latencies and past occurrences of protocol misalignments. - the accounting aspect, peers with whom we have higher credit will be preferred. -- we should also prioritise those downstream peers that managed to produce responses in a previously computed amount of time (that would take into consideration the average time needed for a hop multiplied by the expected number of hops needed to reach a target neighbourhood). +- we should also prioritise those downstream peers that managed to produce responses in a previously computed amount of time (that would take into consideration the average time needed for a hop multiplied by the expected number of hops needed to reach a target neighborhood). Kademila should be indexing peers by their proximity order and peers rating in order to prioritize peers based on their expected performance. diff --git a/docs/develop/tools-and-features/chunk-types.md b/docs/develop/tools-and-features/chunk-types.md index fb5eef2b6..91830667a 100644 --- a/docs/develop/tools-and-features/chunk-types.md +++ b/docs/develop/tools-and-features/chunk-types.md @@ -17,7 +17,7 @@ To be able trust your data, you must run your own Bee node that automatically ve ## Trojan Chunks -Trojan chunks are a special version of content addressed chunks that have been 'mined' so that their natural home is in a particular area of the Swarm. If the destination node is in the right neighbourhood, it will be able to receive and decrypt the message. See [PSS](/docs/develop/tools-and-features/pss) for more information, or check out the [bee-js](https://bee-js.ethswarm.org/docs/api/classes/Bee/#psssend) bindings. +Trojan chunks are a special version of content addressed chunks that have been 'mined' so that their natural home is in a particular area of the Swarm. If the destination node is in the right neighborhood, it will be able to receive and decrypt the message. See [PSS](/docs/develop/tools-and-features/pss) for more information, or check out the [bee-js](https://bee-js.ethswarm.org/docs/api/classes/Bee/#psssend) bindings. ## Single Owner Chunks diff --git a/docs/develop/tools-and-features/pss.md b/docs/develop/tools-and-features/pss.md index 669daa201..3e0be67d8 100644 --- a/docs/develop/tools-and-features/pss.md +++ b/docs/develop/tools-and-features/pss.md @@ -5,7 +5,7 @@ id: pss Out of the ashes of Ethereum's vision for a leak-proof decentralised anonymous messaging system - Whisper - comes PSS (or BZZ, whispered! 🤫). Swarm provides the ability to send messages that appear to be normal Swarm traffic, but are in fact messages that may be received and decrypted to reveal their content only by the specific nodes they were intended to be received by. -PSS provides a pub-sub facility that can be used for a variety of tasks. Nodes are able to listen to messages received for a specific topic in their nearest neighbourhood and create messages destined for another neighbourhood which are sent over the network using Swarm's usual data dissemination protocols. +PSS provides a pub-sub facility that can be used for a variety of tasks. Nodes are able to listen to messages received for a specific topic in their nearest neighborhood and create messages destined for another neighborhood which are sent over the network using Swarm's usual data dissemination protocols. ### Subscribe and Receive Messages @@ -17,7 +17,7 @@ Here we subscribe to the topic `test-topic` websocat ws://localhost:1633/pss/subscribe/test-topic ``` -Our node is now watching for new messages received in its nearest neighbourhood. +Our node is now watching for new messages received in its nearest neighborhood. :::info Because a message is disguised as a normal chunk in Swarm, you will receive the message upon syncing the chunk, even if your node is not online at the moment when the message was send to you. @@ -29,7 +29,7 @@ Messages can be sent simply by sending a `POST` request to the PSS API endpoint. When sending messages, we must specify a 'target' prefix of the recipient's Swarm address, a partial address representing their -neighbourhood. Currently the length of this prefix is recommended to +neighborhood. Currently the length of this prefix is recommended to be two bytes, which will work well until the network has grown to a size of ca. 20-50K nodes. We must also provide the public key, so that Bee can encrypt the message in such a way that it may only be read by @@ -194,7 +194,7 @@ curl \ The PSS API endpoint will now create a PSS message for its recipient in the form of a 'Trojan Chunk' and send this into the network so that -it may be pushed to the correct neighbourhood. Once it is received by +it may be pushed to the correct neighborhood. Once it is received by its recipient it will be decrypted and determined to be a message with the topic we are listening for. Our second node will decrypt the data and we'll see a message pop up in our `websocat` console! diff --git a/docs/learn/DISC/kademlia.md b/docs/learn/DISC/kademlia.md index e06047a5b..010f2c73b 100644 --- a/docs/learn/DISC/kademlia.md +++ b/docs/learn/DISC/kademlia.md @@ -38,7 +38,7 @@ As mentioned above, Swarm's version of Kademlia differs from commonly used imple Swarm introduces the concept of [proximity order (PO)](/docs/learn/glossary#proximity-order-po) as a discrete measure of node relatedness between two addresses. In contrast with Kademlia distance which is an exact measure of relatedness, PO is used to measure the relatedness between two addresses on a discrete scale based on the number of shared leading bits. Since this metric ignores all the bits after the shared leading bits, it is not an exact measure of distance between any two addresses. -In Swarm's version of Kademlia, nodes are grouped into [neighborhoods](/docs/learn/DISC/neighbourhoods) of nodes based on PO (ie., neighborhood are composed of nodes which all share the same leading binary prefix bits). Each neighborhood of nodes is responsible for storing the same set of chunks. +In Swarm's version of Kademlia, nodes are grouped into [neighborhoods](/docs/learn/DISC/neighborhoods) of nodes based on PO (ie., neighborhood are composed of nodes which all share the same leading binary prefix bits). Each neighborhood of nodes is responsible for storing the same set of chunks. Neighborhoods are important for ensuring data redundancy, and they also play a role in the incentives system which guarantees nodes are rewarded for contributing resources to the network. diff --git a/docs/learn/DISC/neighbourhoods.md b/docs/learn/DISC/neighborhoods.md similarity index 54% rename from docs/learn/DISC/neighbourhoods.md rename to docs/learn/DISC/neighborhoods.md index 85528beb4..c5dd95120 100644 --- a/docs/learn/DISC/neighbourhoods.md +++ b/docs/learn/DISC/neighborhoods.md @@ -1,9 +1,9 @@ --- -title: Neighbourhoods -id: neighbourhoods +title: neighborhoods +id: neighborhoods --- -In Swarm, a neighbourhood refers to an area of responsibility within the network, where nodes in proximity to one another share the task of storing and maintaining data chunks. It is defined by the [proximity order (PO)](/docs/learn/glossary#proximity-order-po) of nodes' addresses. Nodes within a neighbourhood replicate data chunks to ensure that if one node goes offline, other nodes in the neighbourhood can still retrieve and serve the content. +In Swarm, a neighborhood refers to an area of responsibility within the network, where nodes in proximity to one another share the task of storing and maintaining data chunks. It is defined by the [proximity order (PO)](/docs/learn/glossary#proximity-order-po) of nodes' addresses. Nodes within a neighborhood replicate data chunks to ensure that if one node goes offline, other nodes in the neighborhood can still retrieve and serve the content. :::info @@ -18,9 +18,9 @@ The terms "depth" and "radius" are often used interchangeably when discussing ne A Swarm neighborhood is determined by the proximity order (PO) of node addresses, which is calculated based on the number of leading bits shared between the addresses of nodes in the network. Nodes in the same neighborhood share the same prefix of their addresses, and the neighborhood expands or contracts depending on the availability of nearby nodes. As a result, each node is responsible for interacting with other nodes within its neighborhood to store and replicate data chunks, ensuring data availability and redundancy. The neighborhood depth dynamically adjusts as peers join or leave the network, maintaining a healthy distribution of storage responsibility across nodes. -### Example Neighbourhood +### Example neighborhood -Let's take a closer look at an example. Below is a neighbourhood of six nodes at depth 10. Each node is identified by its Swarm address, which is a 256 bit hexadecimal number derived from the node's Gnosis Chain address, the Swarm network id, and a random nonce. +Let's take a closer look at an example. Below is a neighborhood of six nodes at depth 10. Each node is identified by its Swarm address, which is a 256 bit hexadecimal number derived from the node's Gnosis Chain address, the Swarm network id, and a random nonce. > da4cb0d125bba638def55c0061b00d7c01ed4033fa193d6e53a67183c5488d73 > da5d39a5508fadf66c8665d5e51617f0e9e5fd501e429c38471b861f104c1504 @@ -29,7 +29,7 @@ Let's take a closer look at an example. Below is a neighbourhood of six nodes at > da7b6439c8d3803286b773a56c4b9a38776b5cd0beb8fd628b6007df235cf35c > da7fd412b79358f84b7928d2f6b7ccdaf165a21313608e16edd317a5355ba250 -Since we are only concerned with the leading binary bits close to the neighbourhood depth, for the rest of this example we will abbreviate the addresses to the first four prefixed hexadecimal digits only. Below are listed the hex prefixes and their binary representation, with the first ten leading bits underlined: +Since we are only concerned with the leading binary bits close to the neighborhood depth, for the rest of this example we will abbreviate the addresses to the first four prefixed hexadecimal digits only. Below are listed the hex prefixes and their binary representation, with the first ten leading bits underlined: | Hex prefix | Binary Bits | |------------|-----------------| @@ -40,14 +40,14 @@ Since we are only concerned with the leading binary bits close to the neighbourh | da7b | 1101101001111011| | da7f | 1101101001111111| -### Chunk Neighbourhood Assignment +### Chunk neighborhood Assignment -Chunks are assigned to neighbourhoods based on their addresses, which are in the same 256 bit format as node addresses. Here are two example chunks which fall within our example neighbourhood: +Chunks are assigned to neighborhoods based on their addresses, which are in the same 256 bit format as node addresses. Here are two example chunks which fall within our example neighborhood: > Chunk A address: `da49a42926015cd1e2bc552147c567b1ca13e8d4302c9e6026e79a24de328b65` > Chunk B address: `da696a3dfb0f7f952872eb33e0e2a1435c61f111ff361e64203b5348cc06dc8a` -As the address of the chunk shown above shares the same ten leading binary bits as the nodes in our example neighbourhood, it falls into that neighbourhood's [area of responsibility](/docs/learn/glossary#2-area-of-responsibility-related-depths), and all the nodes in that neighbourhood are required to store that chunk: +As the address of the chunk shown above shares the same ten leading binary bits as the nodes in our example neighborhood, it falls into that neighborhood's [area of responsibility](/docs/learn/glossary#2-area-of-responsibility-related-depths), and all the nodes in that neighborhood are required to store that chunk: > da49 --> 1101101001001001 > da69 --> 1101101001101001 @@ -55,25 +55,25 @@ As the address of the chunk shown above shares the same ten leading binary bits *As with the example for nodes, we've abbreviated the chunk addresses to their leading four hexadecimal digits only and converted them to binary digits.* -### Neighbourhood Doubling +### neighborhood Doubling -As more and more chunks are assigned to neighbourhoods, the chunk reserves of the nodes in that neighbourhood will begin to fill up. Once the nodes' reserves in a neighbourhood become full and can no longer store additional chunks, that neighbourhood will split, with each half of the neighbourhood taking responsibility for half of the chunks. This event is referred to as a "doubling", as it results in double the number of neighbourhoods. The split is done by increasing the storage depth by one, so that the number of shared leading bits is increased by one. This results in a binary splitting of the neighbourhood and associated chunks into two new neighbourhoods and respective groups of chunks. +As more and more chunks are assigned to neighborhoods, the chunk reserves of the nodes in that neighborhood will begin to fill up. Once the nodes' reserves in a neighborhood become full and can no longer store additional chunks, that neighborhood will split, with each half of the neighborhood taking responsibility for half of the chunks. This event is referred to as a "doubling", as it results in double the number of neighborhoods. The split is done by increasing the storage depth by one, so that the number of shared leading bits is increased by one. This results in a binary splitting of the neighborhood and associated chunks into two new neighborhoods and respective groups of chunks. :::info -Note that when chunks begin to expire and new chunks are not uploaded to Swarm, it is possible for node's reserves to empty out, once they fall below a certain threshold, a "halving" will occur in which the storage depth will be decreased by one and two neighbourhoods will merge to make a new one so that they are responsible for a wider set of chunks. +Note that when chunks begin to expire and new chunks are not uploaded to Swarm, it is possible for node's reserves to empty out, once they fall below a certain threshold, a "halving" will occur in which the storage depth will be decreased by one and two neighborhoods will merge to make a new one so that they are responsible for a wider set of chunks. ::: -Using our previous example neighbourhood, during a doubling, the storage depth would increase from 10 to 11, and the neighbourhood would be split based on the 11th leading bit. +Using our previous example neighborhood, during a doubling, the storage depth would increase from 10 to 11, and the neighborhood would be split based on the 11th leading bit. -**Neighbourhood A:** +**neighborhood A:** | Hex prefix | Binary Bits | |------------|-----------------| | da4c | 1101101001001100| | da5d | 1101101001011101| -**Neighbourhood B:** +**neighborhood B:** | Hex prefix | Binary Bits | |------------|-----------------| | da76 | 1101101001110110| @@ -82,10 +82,10 @@ Using our previous example neighbourhood, during a doubling, the storage depth w | da7f | 1101101001111111| -Each of our two example chunks will also be split amongst the two new neighbourhoods based on their 11th leading bit: +Each of our two example chunks will also be split amongst the two new neighborhoods based on their 11th leading bit: -**Neighbourhood A:** +**neighborhood A:** | Hex prefix | Binary Bits | |------------|-----------------| | da4c | 1101101001001100| @@ -93,7 +93,7 @@ Each of our two example chunks will also be split amongst the two new neighbourh |da49 (chunk)| 1101101001001001| -**Neighbourhood B:** +**neighborhood B:** | Hex prefix | Binary Bits | |------------|-----------------| | da76 | 1101101001110110| @@ -105,4 +105,4 @@ Each of our two example chunks will also be split amongst the two new neighbourh #### Doubling Implications for Node Operators -One of the implications of doubling for node operators is that the reward chances for a node depends in part on how many other nodes are in its neighbourhood. If it is in a neighbourhood with fewer nodes, its chances of winning rewards are greater. Therefore node operators should make certain to place their nodes into less populated neighbourhoods, and also should look ahead to neighbourhoods at the next depth after a doubling. For more details about how to adjust node placement, see [the section on setting a target neighbourhood](/docs/bee/installation/install#set-target-neighborhood-optional) in the installation guide. \ No newline at end of file +One of the implications of doubling for node operators is that the reward chances for a node depends in part on how many other nodes are in its neighborhood. If it is in a neighborhood with fewer nodes, its chances of winning rewards are greater. Therefore node operators should make certain to place their nodes into less populated neighborhoods, and also should look ahead to neighborhoods at the next depth after a doubling. For more details about how to adjust node placement, see [the section on setting a target neighborhood](/docs/bee/installation/install#set-target-neighborhood-optional) in the installation guide. \ No newline at end of file diff --git a/docs/learn/glossary.md b/docs/learn/glossary.md index 9f9c1c4f6..086d28e7d 100644 --- a/docs/learn/glossary.md +++ b/docs/learn/glossary.md @@ -123,7 +123,7 @@ There are three fundamental categories of depth: ### 1. Topology related depth -This depth is defined in relation to the connection topology of a single node as the subject in relation to all the other nodes it is connected to. It is referred to using several different terms which all refer to the same concept (Connectivity depth / Kademlia depth / neighbourhood depth / physical depth) +This depth is defined in relation to the connection topology of a single node as the subject in relation to all the other nodes it is connected to. It is referred to using several different terms which all refer to the same concept (Connectivity depth / Kademlia depth / neighborhood depth / physical depth) Connectivity depth refers to the saturation level of the node’s topology - the level to which the topology of a node’s connections has Kademlia connectivity. Defined as one level deeper than the deepest fully saturated level. A PO is defined as saturated if it has at least the minimum required level of connected nodes, which is set at 8 nodes in the current implementation of Swarm. diff --git a/docs/learn/incentives/postage-stamps.md b/docs/learn/incentives/postage-stamps.md index 3d2919398..ade3ce5ba 100644 --- a/docs/learn/incentives/postage-stamps.md +++ b/docs/learn/incentives/postage-stamps.md @@ -13,11 +13,11 @@ Postage stamp prices are dynamically set based on a utilization signal supplied ## Batch Buckets -Postage stamps are issued in batches with a certain number of storage slots partitioned into $$2^{bucketDepth}$$ equally sized address space buckets. Each bucket is responsible for storing chunks that fall within a certain range of the address space. When uploaded, files are split into 4kb chunks, each chunk is assigned a unique address, and each chunk is then assigned to the bucket in which its address falls. Falling into the same range means a match on `n` leading bits of the chunk and bucket. This restriction is necessary to ensure (incentivise) uniform utilisation of the address space and is fair since the distribution of content addresses are uniform as well. Uniformity depth is the number of leading bits determining bucket membership (also called `bucket depth`). The uniformity depth is set to 16, so there are a total of $$2^{16} = 65,536$$ buckets. +Postage stamps are issued in batches with a certain number of storage slots partitioned into $$2^{bucketDepth}$$ equally sized address space buckets (bucket depth has a fixed value of 16). Each bucket is responsible for storing chunks that fall within a certain range of the address space. When uploaded, files are split into 4kb chunks, each chunk is assigned a unique address, and each chunk is then assigned to the bucket in which its address falls. ### Bucket Size -Each bucket has a certain number of slots which can be "filled" by chunks (In other words, for each bucket, a certain number of chunks can be stamped). Once all the slots of a bucket are filled, the entire postage batch will be fully utilised and can no longer be used to upload additional data. +Each bucket has a certain number of slots which can be "filled" by chunks (In other words, for each bucket, a certain number of chunks can be stamped). Once all the slots of any single bucket from the batch are filled, the entire postage batch will become fully utilised and can no longer be used to upload additional data. Together with `batch depth`, `bucket depth`determines how many chunks are allowed in each bucket. The number of chunks allowed in each bucket is calculated like so: @@ -136,7 +136,7 @@ There are several nuances to how the re-uploading of previously uploaded data to #### Single chunks -When a chunk which has previously been uploaded to Swarm is re-uploaded from the same node while the initial postage batch it was stamped by is still valid, no additional stamp will be utilised from the batch. However if the chunk comes from a different node than the original node, then a stamp WILL be utilised, and as long as at least one of the batches the chunk was stamped by is still valid, the chunk will be retained by storer nodes in its neighbourhood. +When a chunk which has previously been uploaded to Swarm is re-uploaded from the same node while the initial postage batch it was stamped by is still valid, no additional stamp will be utilised from the batch. However if the chunk comes from a different node than the original node, then a stamp WILL be utilised, and as long as at least one of the batches the chunk was stamped by is still valid, the chunk will be retained by storer nodes in its neighborhood. #### Files diff --git a/docs/learn/incentives/redistribution-game.md b/docs/learn/incentives/redistribution-game.md index 8aa01dc58..7970815ea 100644 --- a/docs/learn/incentives/redistribution-game.md +++ b/docs/learn/incentives/redistribution-game.md @@ -10,7 +10,7 @@ The redistribution game is used to redistribute the xBZZ which is accumulated by ### Redistribution Game Details -When someone wants to upload data to Swarm, they do so by buying postage stamp batches with xBZZ. The xBZZ is collected and later paid out to storage provider nodes as a part of the redistribution game. Every 152 Gnosis Chain blocks a single [neighborhood](/docs/learn/DISC/neighbourhoods) is selected to play the redistribution game. For each round of the game, one node from the selected neighborhood will have the chance to win a reward which is paid out from the accumulated xBZZ. +When someone wants to upload data to Swarm, they do so by buying postage stamp batches with xBZZ. The xBZZ is collected and later paid out to storage provider nodes as a part of the redistribution game. Every 152 Gnosis Chain blocks a single [neighborhood](/docs/learn/DISC/neighborhoods) is selected to play the redistribution game. For each round of the game, one node from the selected neighborhood will have the chance to win a reward which is paid out from the accumulated xBZZ. The game has 3 phases, `commit`, `reveal`, and `claim`. In the `reveal` phase of a previous game, an "anchor" address is randomly generated and used to determine the neighborhood for the current round. diff --git a/sidebars.js b/sidebars.js index 40625bf80..740ce4f50 100644 --- a/sidebars.js +++ b/sidebars.js @@ -8,7 +8,7 @@ module.exports = { items: [ 'learn/DISC/disc', 'learn/DISC/kademlia', - 'learn/DISC/neighbourhoods', + 'learn/DISC/neighborhood', 'learn/DISC/erasure-coding', ], collapsed: false From 97ff6cff795346728eb78f9e1f9caeed1009678e Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Thu, 24 Oct 2024 21:33:24 +0700 Subject: [PATCH 10/25] typo --- sidebars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sidebars.js b/sidebars.js index 740ce4f50..898ef489f 100644 --- a/sidebars.js +++ b/sidebars.js @@ -8,7 +8,7 @@ module.exports = { items: [ 'learn/DISC/disc', 'learn/DISC/kademlia', - 'learn/DISC/neighborhood', + 'learn/DISC/neighborhoods', 'learn/DISC/erasure-coding', ], collapsed: false From 9542748a182d3e926a6ae3e6149947bb996fdabc Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Fri, 25 Oct 2024 05:20:04 +0700 Subject: [PATCH 11/25] fix broken links --- docs/bee/installation/hive.md | 2 +- docs/bee/installation/install.md | 2 +- docs/bee/working-with-bee/bee-api.md | 7 +---- docs/bee/working-with-bee/staking.md | 10 +++--- docs/desktop/postage-stamps.md | 2 +- .../access-the-swarm/buy-a-stamp-batch.md | 16 +++++----- docs/develop/access-the-swarm/syncing.md | 2 +- .../access-the-swarm/upload-and-download.md | 7 ++--- .../tools-and-features/access-control.md | 2 +- docs/learn/DISC/DISC.md | 7 +++-- docs/learn/DISC/neighborhoods.md | 2 +- docs/learn/faq.md | 2 +- docs/learn/glossary.md | 2 +- docs/learn/incentives/postage-stamps.md | 14 ++++----- docs/learn/what-is-swarm.md | 2 +- docusaurus.config.js | 31 ++++++++++++++++--- src/components/AmountAndDepthCalc.js | 4 +-- 17 files changed, 65 insertions(+), 49 deletions(-) diff --git a/docs/bee/installation/hive.md b/docs/bee/installation/hive.md index 1e6aed6c5..7c005ce68 100644 --- a/docs/bee/installation/hive.md +++ b/docs/bee/installation/hive.md @@ -40,4 +40,4 @@ Configure your nodes as desired, but ensure that the values `api-addr`, `data-di ### Monitoring -See the monitoring section on how to access Bee's internal metrics! Share your community creations (like [swarmMonitor](https://github.com/doristeo/SwarmMonitoring) - thanks doristeo!) in the [#node-operators](https://discord.gg/X3ph5yGRFU) channel of our Discord server so we can add you to our list of all things that are [awesome](/docs/learn/ecosystem/awesome) and Swarm. 🧡 +See the monitoring section on how to access Bee's internal metrics! Share your community creations (like [swarmMonitor](https://github.com/doristeo/SwarmMonitoring) - thanks doristeo!) in the [#node-operators](https://discord.gg/X3ph5yGRFU) channel of our Discord server so we can add you to our list of all things that are [awesome](https://github.com/ethersphere/awesome-swarm) and Swarm. 🧡 diff --git a/docs/bee/installation/install.md b/docs/bee/installation/install.md index 70c577368..71f4c493f 100644 --- a/docs/bee/installation/install.md +++ b/docs/bee/installation/install.md @@ -526,7 +526,7 @@ resolver-options: ["https://mainnet.infura.io/v3/<>"] ### Set Target Neighborhood (Optional) -In older versions of Bee, [neighborhood](/docs/learn/technology/disc#neighborhoods) assignment was random by default. However, we can maximize a node's chances of winning xBZZ and also strengthen the resiliency of the network by strategically assigning neighborhoods to new nodes (see the [staking section](/docs/bee/working-with-bee/staking) for more details). +In older versions of Bee, [neighborhood](/docs/learn/DISC/neighborhoods) assignment was random by default. However, we can maximize a node's chances of winning xBZZ and also strengthen the resiliency of the network by strategically assigning neighborhoods to new nodes (see the [staking section](/docs/bee/working-with-bee/staking) for more details). Therefore the default Bee configuration now includes the `neighborhood-suggester` option which is set by default to to use the Swarmscan neighborhood suggester (`https://api.swarmscan.io/v1/network/neighborhoods/suggestion`). An alternative suggester URL could be used as long as it returns a JSON file in the same format `{"neighborhood":"101000110101"}`, however only the Swarmscan suggester is officially recommended. diff --git a/docs/bee/working-with-bee/bee-api.md b/docs/bee/working-with-bee/bee-api.md index c039fd5c4..c94930005 100644 --- a/docs/bee/working-with-bee/bee-api.md +++ b/docs/bee/working-with-bee/bee-api.md @@ -458,7 +458,7 @@ From the results we can see that we have a healthy neighborhood size when compar ### `/rchash` -Calling the /rchash endpoint will make your node generate a reserve commitment hash (the hash used in the [redistribution game](/docs/learn/technology/incentives#storage-incentives-details)), and will report the amount of time it took to generate the hash. This is useful for getting a performance benchmark to ensure that your node's hardware is sufficient. +Calling the /rchash endpoint will make your node generate a reserve commitment hash (the hash used in the [redistribution game](/docs/learn/incentives/redistribution-game)), and will report the amount of time it took to generate the hash. This is useful for getting a performance benchmark to ensure that your node's hardware is sufficient. ```bash sudo curl -sX GET http://localhost:1633/rchash/10/aaaa/aaaa | jq @@ -503,8 +503,3 @@ If the `Time` value is much longer than 6 minutes then it likely means that the * `"version"` - The version of your Bee node. You can find latest version by checking the [Bee github repo](https://github.com/ethersphere/bee). * `"apiVersion"` -## Debug API Removal Notice - -:::info -The Debug API endpoints have been merged into the Bee API in the Bee version 2.2.0 release, and will be fully removed in the 2.2.0 release. The [Debug API reference docs](/debug-api/) are still available until the 2.2.0 release for your reference. -::: \ No newline at end of file diff --git a/docs/bee/working-with-bee/staking.md b/docs/bee/working-with-bee/staking.md index 52626c16c..5ab6ed2c9 100644 --- a/docs/bee/working-with-bee/staking.md +++ b/docs/bee/working-with-bee/staking.md @@ -62,7 +62,7 @@ curl -X GET http://localhost:1633/redistributionstate | jq * `"hasSufficientFunds": ` - Shows whether the node has enough xDAI balance to submit at least five storage incentives redistribution related transactions. If `false` the node will not be permitted to participate in next round. * `"isFrozen": ` - Shows node frozen status. * `"isFullySynced": ` - Shows whether node's localstore has completed full historical syncing with all connected peers. -* `"phase": ` - Current phase of [redistribution game](/docs/learn/technology/incentives#storage-incentives-details) (`commit`, `reveal`, or `claim`). +* `"phase": ` - Current phase of [redistribution game](/docs/learn/incentives/redistribution-game) (`commit`, `reveal`, or `claim`). * `"round": ` - Current round of redistribution game. The round number is determined by dividing the current Gnosis Chain block height by the number of blocks in one round. One round takes 152 blocks, so using the "block" output from the example above we can confirm that the round number is 176319 (block 26800488 / 152 blocks = round 176319). * `"lastWonRound": ` - Number of round last won by this node. * `"lastPlayedRound": ` - Number of the last round where node's neighborhood was selected to participate in redistribution game. @@ -111,11 +111,11 @@ curl -X DELETE http://localhost:1633/stake/withdrawable ## Maximize rewards -There are two main factors which determine the chances for a staking node to win a reward — neighborhood selection and stake density. Both of these should be considered together before starting up a Bee node for the first time. See the [incentives page](/docs/learn/technology/incentives/) for more context. +There are two main factors which determine the chances for a staking node to win a reward — neighborhood selection and stake density. Both of these should be considered together before starting up a Bee node for the first time. See the [incentives page](/docs/learn/incentives/redistribution-game) for more context. ### Neighborhood selection -By default when running a Bee node for the first time an overlay address will be generated and used to assign the node to a random [neighborhood](/docs/learn/technology/disc#neighborhoods). However, by using the `target-neighborhood` config option, a specific neighborhood can be selected in which to generate the node's overlay address. This is an excellent tool for maximizing reward chances as generally speaking running in a less populated neighborhood will increase the chances of winning a reward. See the [config section](/docs/bee/installation/install#set-target-neighborhood-optional) on the installation page for more information on how to set a target neighborhood. +By default when running a Bee node for the first time an overlay address will be generated and used to assign the node to a random [neighborhood](/docs/learn/DISC/neighborhoods). However, by using the `target-neighborhood` config option, a specific neighborhood can be selected in which to generate the node's overlay address. This is an excellent tool for maximizing reward chances as generally speaking running in a less populated neighborhood will increase the chances of winning a reward. See the [config section](/docs/bee/installation/install#set-target-neighborhood-optional) on the installation page for more information on how to set a target neighborhood. ### Stake density @@ -126,7 +126,7 @@ $$ \text{stake density} = \text{staked xBZZ} \times {2}^\text{storageDepth} $$ -*To learn more about stake density and the mechanics of the incentives system, see the [incentives page](/docs/learn/technology/incentives/).* +*To learn more about stake density and the mechanics of the incentives system, see the [incentives page](/docs/learn/incentives/redistribution-game).* Stake density determines the weighted chances of nodes within a neighborhood of winning rewards. The chance of winning within a neighborhood corresponds to stake density. Stake density can be increased by depositing more xBZZ as stake (note that stake withdrawals are not currently possible, so any staked xBZZ is not currently recoverable). @@ -198,7 +198,7 @@ In this section we cover several commonly seen issues encountered for staking no ### Frozen node -A node will be frozen when the reserve commitment hash it submits in its [`commit` transaction](/docs/learn/technology/incentives#storage-incentives-details) does not match the correct hash. The reserve commitment hash is used as proof that a node is storing the chunks it is responsible for. It will not be able to play in the redistribution game during the freezing period. See the [penalties](/docs/learn/technology/incentives#penalties) section for more information. +A node will be frozen when the reserve commitment hash it submits in its [`commit` transaction](/docs/learn/incentives/redistribution-game) does not match the correct hash. The reserve commitment hash is used as proof that a node is storing the chunks it is responsible for. It will not be able to play in the redistribution game during the freezing period. See the [penalties](/docs/learn/incentives/redistribution-game) section for more information. #### Check frozen status diff --git a/docs/desktop/postage-stamps.md b/docs/desktop/postage-stamps.md index a7965a89b..e7d1e634d 100644 --- a/docs/desktop/postage-stamps.md +++ b/docs/desktop/postage-stamps.md @@ -23,7 +23,7 @@ And then clicking the ***Buy New Postage Stamp*** button: ### Depth and Amount -Batch [depth and amount](/docs/learn/technology/contracts/postage-stamp) are the two required parameters which must be set when purchasing a postage stamp batch. Depth determines how many chunks can be stamped with a batch while amount determines how much xBZZ is assigned per chunk. +Batch [depth and amount](/docs/learn/incentives/postage-stamps) are the two required parameters which must be set when purchasing a postage stamp batch. Depth determines how many chunks can be stamped with a batch while amount determines how much xBZZ is assigned per chunk. ![](/img/stamps3.png) diff --git a/docs/develop/access-the-swarm/buy-a-stamp-batch.md b/docs/develop/access-the-swarm/buy-a-stamp-batch.md index 828d8adb7..b074fc512 100644 --- a/docs/develop/access-the-swarm/buy-a-stamp-batch.md +++ b/docs/develop/access-the-swarm/buy-a-stamp-batch.md @@ -10,9 +10,9 @@ import TabItem from '@theme/TabItem'; -A postage batch is required to upload data to Swarm. Postage stamp batches represent _right to write_ data on Swarm's [DISC (Distributed Immutable Store of Chunks)](/docs/learn/technology/disc). The parameters which control the duration and quantity of data that can be stored by a postage batch are `depth` and `amount`, with `depth` determining data volume that can be uploaded by the batch and `amount` determining storage duration of data uploaded with the batch. +A postage batch is required to upload data to Swarm. Postage stamp batches represent _right to write_ data on Swarm's [DISC (Distributed Immutable Store of Chunks)](/docs/learn/DISC/). The parameters which control the duration and quantity of data that can be stored by a postage batch are `depth` and `amount`, with `depth` determining data volume that can be uploaded by the batch and `amount` determining storage duration of data uploaded with the batch. -For a deeper understanding of how `depth` and `amount` parameters determine the data volume and storage duration of a postage batch, see the [postage stamp contract page](/docs/learn/technology/contracts/postage-stamp). +For a deeper understanding of how `depth` and `amount` parameters determine the data volume and storage duration of a postage batch, see the [postage stamp page](/docs/learn/incentives/postage-stamps/). ## Fund your node's wallet. @@ -94,12 +94,12 @@ When purchasing a batch of stamps there are several parameters and options which ### Choosing `depth` :::caution -The minimum value for `depth` is 17, however a higher depth value is recommended for most use cases due to the [mechanics of stamp batch utilisation](/docs/learn/technology/contracts/postage-stamp#batch-utilisation). See [the depths utilisation table](/docs/learn/technology/contracts/postage-stamp#effective-utilisation-table) to help decide which depth is best for your use case. +The minimum value for `depth` is 17, however a higher depth value is recommended for most use cases due to the [mechanics of stamp batch utilisation](/docs/learn/incentives/postage-stamps/#batch-utilisation). See [the depths utilisation table](/docs/learn/incentives/postage-stamps/#effective-utilisation-table) to help decide which depth is best for your use case. ::: One notable aspect of batch utilisation is that the entire batch is considered fully utilised as soon as any one of its buckets are filled. This means that the actual amount of chunks storable by a batch is less than the nominal maximum amount. -See the [postage stamp contract page](/docs/learn/technology/contracts/postage-stamp#batch-utilisation) for a more complete explanation of how batch utilisation works and a [table](/docs/learn/technology/contracts/postage-stamp#effective-utilisation-table) with the specific amounts of data which can be safely uploaded for each `depth` value. +See the [postage stamp page](/docs/learn/incentives/postage-stamps) for a more complete explanation of how batch utilisation works and a [table](/docs/learn/incentives/postage-stamps#effective-utilisation-table) with the specific amounts of data which can be safely uploaded for each `depth` value. ### Choosing `amount` @@ -107,18 +107,18 @@ See the [postage stamp contract page](/docs/learn/technology/contracts/postage-s The minimum `amount` value for purchasing stamps is required to be at least enough to pay for 24 hours of storage. To find this value multiply the lastPrice value from the postage stamp contract times 17280 (the number of blocks in 24 hours). You can also use the [calculator](#calculators) below. This requirement is in place in order to prevent spamming the network. ::: -The `amount` parameter determines how much xBZZ is assigned per chunk for a postage stamp batch. You can use the calculators below to find the appropriate `amount` value for your target duration of storage and can also preview the price. For more information see the [postage stamp batch contract page](/docs/learn/technology/contracts/postage-stamp#batch-amount--batch-cost) where a more complete description is included. +The `amount` parameter determines how much xBZZ is assigned per chunk for a postage stamp batch. You can use the calculators below to find the appropriate `amount` value for your target duration of storage and can also preview the price. For more information see the [postage stamp](/docs/learn/incentives/postage-stamps#batch-depth-and-batch-amount) page where a more complete description is included. ### Mutable or Immutable? -Depending on the use case, uploaders may desire to use mutable or immutable batches. The fundamental difference between immutable and mutable batches is that immutable batches become unusable once their capacity is filled, while for mutable batches, once their capacity is filled, they may continue to be used, however older chunks of data will be overwritten with the newer once over capacity. The default batch type is immutable. In order to set the batch type to mutable, the `immutable` header should be set to `false`. See [this section on postage stamp batch utilisation](/docs/learn/technology/contracts/postage-stamp#which-type-of-batch-to-use) to learn more about mutable vs immutable batches, and about which type may be right for your use case. +Depending on the use case, uploaders may desire to use mutable or immutable batches. The fundamental difference between immutable and mutable batches is that immutable batches become unusable once their capacity is filled, while for mutable batches, once their capacity is filled, they may continue to be used, however older chunks of data will be overwritten with the newer once over capacity. The default batch type is immutable. In order to set the batch type to mutable, the `immutable` header should be set to `false`. See [this section on postage stamp batch utilisation](/docs/learn/incentives/postage-stamps#which-type-of-batch-to-use) to learn more about mutable vs immutable batches, and about which type may be right for your use case. ## Calculators The following postage batch calculators allow you to conveniently find the depth and amount values for a given storage duration and storage volume, or to find the storage duration and storage volume for a given depth and amount. The results will display the cost in xBZZ for the postage batch. The current pricing information is sourced from the Swarmscan API and will vary over time. :::info -The 'effective volume' is the volume of data that can safely stored for each storage depth. The 'theoretical max volume' is significantly lower than the effective volume at lower depths and the two values trend towards the same value at higher depths. The lowest depth with an effective volume above zero is 22, with an effective depth of 4.93 GB. Lower depth values can be used for smaller uploads but do not come with the same storage guarantees. [Learn more here](/docs/learn/technology/contracts/postage-stamp#effective-utilisation-table). +The 'effective volume' is the volume of data that can safely stored for each storage depth. The 'theoretical max volume' is significantly lower than the effective volume at lower depths and the two values trend towards the same value at higher depths. The lowest depth with an effective volume above zero is 22, with an effective depth of 4.93 GB. Lower depth values can be used for smaller uploads but do not come with the same storage guarantees. [Learn more here](/docs/learn/incentives/postage-stamps#effective-utilisation-table). ::: ### Depth & Amount to Time & Volume Calculator @@ -127,7 +127,7 @@ The 'effective volume' is the volume of data that can safely stored for each sto ### Time & Volume to Depth & Amount Calculator -The recommended depth in this calculator's results is the lowest depth value whose [effective volume](/docs/learn/technology/contracts/postage-stamp#effective-utilisation-table) is greater than the entered volume. +The recommended depth in this calculator's results is the lowest depth value whose [effective volume](/docs/learn/incentives/postage-stamps#effective-utilisation-table) is greater than the entered volume. diff --git a/docs/develop/access-the-swarm/syncing.md b/docs/develop/access-the-swarm/syncing.md index 32e2270a5..7b96661ec 100644 --- a/docs/develop/access-the-swarm/syncing.md +++ b/docs/develop/access-the-swarm/syncing.md @@ -55,5 +55,5 @@ curl http://localhost:1633/tags/5 | jq The response contains all the information that you need to follow the status of your file as it is synced with the network. :::info -The numbers that the `tags` endpoint returns under `total`, `processed` and `synced` are denominated in [*chunks*](/docs/learn/technology/disc#chunks), i.e. Swarm's 4kb data units. +The numbers that the `tags` endpoint returns under `total`, `processed` and `synced` are denominated in [*chunks*](/docs/learn/DISC/#chunks), i.e. Swarm's 4kb data units. ::: diff --git a/docs/develop/access-the-swarm/upload-and-download.md b/docs/develop/access-the-swarm/upload-and-download.md index f7b575d29..3c3e78bd2 100644 --- a/docs/develop/access-the-swarm/upload-and-download.md +++ b/docs/develop/access-the-swarm/upload-and-download.md @@ -11,12 +11,11 @@ When you upload your files to the Swarm, they are split into 4kb _chunks_ and then distributed to nodes in the network that are responsible for storing and serving these parts of your content. To learn more about how Swarm's decentralized storage solution works, -check out the ["Learn" section](/docs/learn/technology/what-is-swarm). +check out the ["Learn" section](/docs/learn/what-is-swarm). In order for you to be able to upload any data to the network, -you must first purchase [postage stamps](/docs/learn/technology/contracts/postage-stamp) -and then use those stamps to upload your data. Keep on reading -below to learn how. +you must first purchase [postage stamps](/docs/learn/incentives/postage-stamps) +and then use those stamps to upload your data. Keep on reading below to learn how. ## Uploads and Download Endpoints Overview diff --git a/docs/develop/tools-and-features/access-control.md b/docs/develop/tools-and-features/access-control.md index 17b838c0d..2fa5d0d72 100644 --- a/docs/develop/tools-and-features/access-control.md +++ b/docs/develop/tools-and-features/access-control.md @@ -4,7 +4,7 @@ id: act --- :::info -This is guide contains a detailed explanation of how to use the ACT feature, but does not cover its higher level concepts. To better understand how ACT works and why to use it, read [the ACT page in the "Learn" section](/docs/learn/technology/act). +This is guide contains a detailed explanation of how to use the ACT feature, but does not cover its higher level concepts. To better understand how ACT works and why to use it, read [the ACT page in the "Learn" section](/docs/learn/features/access-control). ::: diff --git a/docs/learn/DISC/DISC.md b/docs/learn/DISC/DISC.md index 442b74287..cb05ad02c 100644 --- a/docs/learn/DISC/DISC.md +++ b/docs/learn/DISC/DISC.md @@ -7,20 +7,21 @@ DISC (Distributed Immutable Storage of Chunks) is a storage solution developed b ### Kademlia Topology and Routing -Kademlia is a distributed hash table (DHT) widely used in peer-to-peer networks such as Ethereum and Bittorent. It serves as the routing and topology foundation for communication between nodes in the Swarm netowrk. It organizes nodes based on their overlay addresses and ensures that messages are relayed efficiently, even in a dynamic, decentralized environment. +[Kademlia](/docs/learn/DISC/kademlia) is a distributed hash table (DHT) widely used in peer-to-peer networks such as Ethereum and Bittorent. It serves as the routing and topology foundation for communication between nodes in the Swarm netowrk. It organizes nodes based on their overlay addresses and ensures that messages are relayed efficiently, even in a dynamic, decentralized environment. One of the advantages of using Kademlia as a model for network topology is that both the number of forwarding "hops" required to route a chunk to its destination and the number of peer connections required to maintain Kademlia topology are logarithmic to the size of the network (a minimum of two connections is required in order to maintain Kademlia topology in case of network churn - nodes dropping in and out of the network). This makes Swarm a highly scalable system which is efficient even at very large scales. ### Neighborhoods -Neighborhoods are groups of nodes which are responsible for sharing the same chunks. The chunks which each neighborhood is responsible for storing are defined by the proximity order of the nodes and the chunks. In other words, each node is responsible for storing chunks with which their overlay addresses share a certain number of prefix bits, and together with other nodes which share the same prefix bits, make up neighborhoods which share the responsibility for storing the same chunks. +[Neighborhoods](/docs/learn/DISC/neighborhoods) are groups of nodes which are responsible for sharing the same chunks. The chunks which each neighborhood is responsible for storing are defined by the proximity order of the nodes and the chunks. In other words, each node is responsible for storing chunks with which their overlay addresses share a certain number of prefix bits, and together with other nodes which share the same prefix bits, make up neighborhoods which share the responsibility for storing the same chunks. +Neighborhoods play a key role in providing data redundancy for chunks stored on Swarm since each node in a neighborhood will keep copies of the same chunks. The optional [erasure coding](/docs/learn/DISC/erasure-coding) feature can also be enabled for added redundancy and greater data protection. ### Chunks In the DISC model, chunks are the canonical unit of data. When a file is uploaded to Swarm, it gets broken down into 4kb pieces with attached metadata. The pieces then get distributed amongst nodes in the Swarm network based on their [overlay addresses](/docs/learn/glossary#overlay). There are two fundamental chunk types: content-addressed chunks and single-owner chunks. -### Content-Addressed Chunks and Single-Owner Chunks +#### Content-Addressed Chunks and Single-Owner Chunks Content-addressed chunks are chunks whose address is based on the hash digest of their data. Using a hash as the chunk address makes it possible to verify the integrity of chunk data. Swarm uses the BMT hash function based on a binary Merkle tree over small segments of the chunk data. A content-addressed chunk has an at most 4KB payload, and its address is calculated as the hash of the span (chunk metadata) and the Binary Merkle Tree hash of the payload. diff --git a/docs/learn/DISC/neighborhoods.md b/docs/learn/DISC/neighborhoods.md index c5dd95120..cedaf0d6e 100644 --- a/docs/learn/DISC/neighborhoods.md +++ b/docs/learn/DISC/neighborhoods.md @@ -1,5 +1,5 @@ --- -title: neighborhoods +title: Neighborhoods id: neighborhoods --- diff --git a/docs/learn/faq.md b/docs/learn/faq.md index a4b9975f9..5afdca38f 100644 --- a/docs/learn/faq.md +++ b/docs/learn/faq.md @@ -55,7 +55,7 @@ There are many ways to acquire BZZ tokens, either on custodial centralised excha ### What is the BZZ token address? -See [this page](/docs/learn/technology/contracts/overview) for a list of relevant token addresses. +See [this page](/docs/learn/smart-contracts) for a list of relevant token addresses. ### What is the BZZ token supply? diff --git a/docs/learn/glossary.md b/docs/learn/glossary.md index 086d28e7d..850ccf9ee 100644 --- a/docs/learn/glossary.md +++ b/docs/learn/glossary.md @@ -36,7 +36,7 @@ Overlay addresses are a Keccak256 hash of a node’s Gnosis Chain address and th ## Neighborhood -Neighborhoods are nodes which are grouped together based on their overlay addresses and are responsible for storing the same chunks of data. The chunks which each neighborhood are responsible for storing are defined by the proximity order of the nodes and the chunks. See [DISC](/docs/learn/technology/disc#neighborhoods) for more details. +[Neighborhoods](/docs/learn/DISC/neighborhoods) are nodes which are grouped together based on their overlay addresses and are responsible for storing the same chunks of data. The chunks which each neighborhood are responsible for storing are defined by the proximity order of the nodes and the chunks. ## Underlay diff --git a/docs/learn/incentives/postage-stamps.md b/docs/learn/incentives/postage-stamps.md index ade3ce5ba..eebb68e01 100644 --- a/docs/learn/incentives/postage-stamps.md +++ b/docs/learn/incentives/postage-stamps.md @@ -1,11 +1,11 @@ --- -title: Postage Stamp +title: Postage Stamps id: postage-stamps --- Postage stamps are used to pay for storing data on Swarm. They are purchased in batches which represent the pre-paid right to store data on Swarm in the same way that real-life postage stamps represent the pre-paid right to send mail through the postal service. -When a node uploads data to Swarm, it 'attaches' postage stamps to each [chunk](/docs/learn/DISC/DISC) of data. The value assigned to a stamp indicates how much it is worth to persist the associated data on Swarm, which nodes use to prioritize which chunks to remove from their reserve first. +When a node uploads data to Swarm, it 'attaches' postage stamps to each [chunk](/docs/learn/DISC/) of data. The value assigned to a stamp indicates how much it is worth to persist the associated data on Swarm, which nodes use to prioritize which chunks to remove from their reserve first. The value of a postage stamp decreases over time as if storage rent was regularly deducted from the batch balance. A stamp expires when the batch it is issued from has insufficient balance. A chunk with an expired stamp can not be used in the proof storer nodes need to submit in order to get compensated for their contributed storage space in the redistribution game, therefore such expired chunks are evicted from nodes' reserves and put into the cache where their continued persistence depends on their popularity. @@ -32,7 +32,7 @@ $$ $$ :::info -Note that due how buckets fill as described above, a batch can become fully utilised before its theoretical maximum volume has been reached. See [batch utilisation section below](/docs/learn/technology/contracts/postage-stamp#batch-utilisation) for more information. +Note that due how buckets fill as described above, a batch can become fully utilised before its theoretical maximum volume has been reached. See [batch utilisation section below](/docs/learn/incentives/postage-stamps#batch-utilisation) for more information. ::: ## Batch Depth and Batch Amount @@ -55,7 +55,7 @@ $$ \text{Theoretical maximum batch volume} = 2^{batchDepth} \times \text{4 kb} $$ -However, due to the way postage stamp batches are utilised, batches will become fully utilised before stamping the theoretical maximum number of chunks. Therefore when deciding which batch depth to use, it is important to consider the effective amount of data that can be stored by a batch, and not the theoretical maximum. The effective rate of utilisation increases along with the batch depth. See [section on stamp batch utilisation below](/docs/learn/technology/contracts/postage-stamp#batch-utilisation) for more information. +However, due to the way postage stamp batches are utilised, batches will become fully utilised before stamping the theoretical maximum number of chunks. Therefore when deciding which batch depth to use, it is important to consider the effective amount of data that can be stored by a batch, and not the theoretical maximum. The effective rate of utilisation increases along with the batch depth. See [section on stamp batch utilisation below](/docs/learn/incentives/postage-stamps#batch-utilisation) for more information. ### Batch Amount (& Batch Cost) @@ -101,7 +101,7 @@ Utilisation of an immutable batch is computed using a hash map of size $$2^{buck ![](/img/batches_01.png) -As chunks are uploaded to Swarm, each chunk is assigned to a bucket based the first 16 binary digits of the [chunk's hash](/docs/learn/technology/disc#chunks). The chunk will be assigned to whichever bucket's key matches the first 16 bits of its hash, and that bucket's counter will be incremented by 1. +As chunks are uploaded to Swarm, each chunk is assigned to a bucket based the first 16 binary digits of the [chunk's hash](/docs/learn/DISC/#chunks). The chunk will be assigned to whichever bucket's key matches the first 16 bits of its hash, and that bucket's counter will be incremented by 1. The batch is deemed "full" when ANY of these counters reach a certain max value. The max value is computed from the batch depth as such: $$2^{(batchDepth-bucketDepth)}$$. For example with batch depth of 24, the max value is $$2^{(24-16)}$$ or 256. A bucket can be thought of as have a number of "slots" equal to this maximum value, and every time the bucket's counter is incremented, one of its slots gets filled. @@ -176,7 +176,7 @@ The implications of this behaviour are that even a small change to the data of a Due to the nature of batch utilisation described above, batches are often fully utilised before reaching their theoretical maximum storage amount. However as the batch depth increases, the chance of a postage batch becoming fully utilised early decreases. At batch depth 24, there is a 0.1% chance that a batch will be fully utilised/start replacing old chunks before reaching 64.33% of its theoretical maximum. -Let's look at an example to make it clearer. Using the method of calculating the theoretical maximum storage amount [outlined above](/docs/learn/technology/contracts/postage-stamp#batch-depth), we can see that for a batch depth of 24, the theoretical maximum amount which can be stored is 68.72 gb: +Let's look at an example to make it clearer. Using the method of calculating the theoretical maximum storage amount [outlined above](/docs/learn/incentives/postage-stamps#batch-depth), we can see that for a batch depth of 24, the theoretical maximum amount which can be stored is 68.72 gb: $$ 2^{24+12} = \text{68,719,476,736 bytes} = \text{68.72 gb} @@ -230,6 +230,6 @@ The provided table shows the effective volume for each batch depth from 20 to 41 This table is based on preliminary calculations and may be subject to change. ::: -Nodes' storage is actually defined as a number of chunks with a size of 4kb (2^12 bytes) each, but in fact some [SOC](/docs/learn/technology/disc#content-addressed-chunks-and-single-owner-chunks) chunks can be a few bytes longer, and some chunks can be smaller, so the conversion is not precise. Furthermore, due to the way Swarm represents files in a Merkle tree, the intermediate chunks are additional overhead which must also be accounted for. +Nodes' storage is actually defined as a number of chunks with a size of 4kb (2^12 bytes) each, but in fact some [SOC](/docs/learn/DISC/#chunks) chunks can be a few bytes longer, and some chunks can be smaller, so the conversion is not precise. Furthermore, due to the way Swarm represents files in a Merkle tree, the intermediate chunks are additional overhead which must also be accounted for. Additionally, when a node stores chunks it uses additional indexes — therefore the disk space a maximally filled reserve would demand cannot be calculated with perfect accuracy. diff --git a/docs/learn/what-is-swarm.md b/docs/learn/what-is-swarm.md index ff911e524..786bb8731 100644 --- a/docs/learn/what-is-swarm.md +++ b/docs/learn/what-is-swarm.md @@ -47,7 +47,7 @@ As the [libp2p](https://libp2p.io/) library meets all these requirements it has ### 2. Overlay Network -The second part of Swarm is an overlay network with protocols powering the [Distributed Immutable Store of Chunks (DISC)](/docs/learn/technology/disc/). This layer is responsible for storing and retrieving data in a decentralised and secure manner. +The second part of Swarm is an overlay network with protocols powering the [Distributed Immutable Store of Chunks (DISC)](/docs/learn/DISC/). This layer is responsible for storing and retrieving data in a decentralised and secure manner. Swarm's overlay network is built on top of the underlay transport layer and uses [Kademlia](/docs/learn/glossary#kademlia) overlay routing to enable efficient and scalable communication between nodes. Kademlia is a distributed hash table (DHT) algorithm that allows nodes to locate each other in the network based on their unique identifier or hash. diff --git a/docusaurus.config.js b/docusaurus.config.js index fd2688128..63616f254 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -90,7 +90,7 @@ module.exports = { ], ], onBrokenLinks: 'warn', - onBrokenMarkdownLinks: 'throw', + onBrokenMarkdownLinks: 'warn', onDuplicateRoutes: 'throw', favicon: 'img/favicon.ico', organizationName: 'Swarm', // Usually your GitHub org/user name. @@ -125,17 +125,38 @@ module.exports = { label: 'Introduction', }, { - to: 'docs/learn/what-is-swarm', - label: 'Technology', + to: '/docs/learn/what-is-swarm', + label: 'What is Swarm?', }, { - to: 'docs/learn/ecosystem/swarm-foundation', - label: 'Ecosystem', + to: '/docs/learn/DISC/', + label: 'DISC Storage', + }, + { + to: '/docs/learn/incentives/overview', + label: 'Incentives', + }, + { + to: '/docs/learn/features/pss', + label: 'Features', + }, + + { + to: '/docs/learn/smart-contracts', + label: 'Smart Contracts', }, { to: 'docs/learn/tokens', label: 'Tokens', }, + { + to: '/docs/learn/ecosystem', + label: 'Ecosystem', + }, + { + to: '/docs/learn/fair-data-society', + label: 'Fair Data Society', + }, { to: 'docs/learn/glossary', label: 'Glossary', diff --git a/src/components/AmountAndDepthCalc.js b/src/components/AmountAndDepthCalc.js index 3688ffce9..41868f2a8 100644 --- a/src/components/AmountAndDepthCalc.js +++ b/src/components/AmountAndDepthCalc.js @@ -269,11 +269,11 @@ function FetchPriceComponent() { Suggested Safe Depth - {`${depth} (for an `}effective volume{` of ${depthToVolume[depth]})`} + {`${depth} (for an `}effective volume{` of ${depthToVolume[depth]})`} Suggested Minimum Depth - {minimumDepth} (see batch utilisation - may require dilution) + {minimumDepth} (see batch utilisation - may require dilution) Batch Cost for Safe Depth From f78cbe6f51e82c4bb45655dfe2accb7a7f729fa8 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Fri, 25 Oct 2024 13:58:25 +0700 Subject: [PATCH 12/25] change page title --- docs/learn/incentives/bandwidth-incentives.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/learn/incentives/bandwidth-incentives.md b/docs/learn/incentives/bandwidth-incentives.md index 807c1ef3f..3406f0532 100644 --- a/docs/learn/incentives/bandwidth-incentives.md +++ b/docs/learn/incentives/bandwidth-incentives.md @@ -1,5 +1,5 @@ --- -title: Bandwidth Incentives +title: Bandwidth Incentives (SWAP) id: bandwidth-incentives --- From 0658d8ff85f452ddc59bcca27bb16dac616fa0df Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Fri, 25 Oct 2024 14:03:41 +0700 Subject: [PATCH 13/25] change ecosystem to community --- docs/learn/ecosystem.md | 4 ++-- docusaurus.config.js | 4 ++-- sidebars.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/learn/ecosystem.md b/docs/learn/ecosystem.md index 5ac5fc889..f27aa0b55 100644 --- a/docs/learn/ecosystem.md +++ b/docs/learn/ecosystem.md @@ -1,6 +1,6 @@ --- -title: Ecosystem -id: ecosystem +title: Community +id: community --- diff --git a/docusaurus.config.js b/docusaurus.config.js index 63616f254..284a10350 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -150,8 +150,8 @@ module.exports = { label: 'Tokens', }, { - to: '/docs/learn/ecosystem', - label: 'Ecosystem', + to: '/docs/learn/community', + label: 'Community', }, { to: '/docs/learn/fair-data-society', diff --git a/sidebars.js b/sidebars.js index 898ef489f..0f6f83753 100644 --- a/sidebars.js +++ b/sidebars.js @@ -36,7 +36,7 @@ module.exports = { }, 'learn/smart-contracts', 'learn/tokens', - 'learn/ecosystem', + 'learn/community', 'learn/fair-data-society', 'learn/glossary', 'learn/faq', From 092842d6602833387d2fdf30f11f6acfa5df9d49 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Fri, 25 Oct 2024 14:17:36 +0700 Subject: [PATCH 14/25] change links --- docs/learn/introduction.md | 8 +++++--- docs/learn/what-is-swarm.md | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/learn/introduction.md b/docs/learn/introduction.md index 78007d226..08fd4bc95 100644 --- a/docs/learn/introduction.md +++ b/docs/learn/introduction.md @@ -6,6 +6,11 @@ id: introduction Swarm is a peer-to-peer network of Bee nodes that collectively provide censorship resistant decentralised storage and communication services. Swarm's mission is to shape the future towards a self-sovereign global society and permissionless open markets by providing scalable base-layer data storage infrastructure for the decentralised internet. Its incentive system is enforced through smart contracts on the Gnosis Chain blockchain and powered by the xBZZ token, making it economically self-sustaining. +## Bee Client + +Bee is a Swarm client implemented in Go and is the basic building block for the Swarm network. Bee nodes collectively work together to form a private, decentralized, and self sustaining network for permissionless publishing and data storage. + + ## Swarm Foundation The goal of [Swarm Foundation](https://www.ethswarm.org/foundation) is to support and contribute to the creation of technology whose code will be open to all and will allow the storage and exchange of data in a decentralised manner. To this end, the foundation intends in particular to promote and support a community and a sustainable and independent ecosystem for the development of free open source software (FLOSS), allowing for digital services coordinated by crypto-economic incentives that process, distribute and store data. @@ -14,7 +19,4 @@ Its mission is to empower digital freedom by promoting the development and maint It does so by supporting many different initiatives, either through financial grants or other types of support, all of which is assessed on a case-by-case basis. -## Bee Client - -Bee is a Swarm client implemented in Go and is the basic building block for the Swarm network. Bee nodes collectively work together to form a private, decentralized, and self sustaining network for permissionless publishing and data storage. diff --git a/docs/learn/what-is-swarm.md b/docs/learn/what-is-swarm.md index 786bb8731..5ba78b44c 100644 --- a/docs/learn/what-is-swarm.md +++ b/docs/learn/what-is-swarm.md @@ -49,7 +49,7 @@ As the [libp2p](https://libp2p.io/) library meets all these requirements it has The second part of Swarm is an overlay network with protocols powering the [Distributed Immutable Store of Chunks (DISC)](/docs/learn/DISC/). This layer is responsible for storing and retrieving data in a decentralised and secure manner. -Swarm's overlay network is built on top of the underlay transport layer and uses [Kademlia](/docs/learn/glossary#kademlia) overlay routing to enable efficient and scalable communication between nodes. Kademlia is a distributed hash table (DHT) algorithm that allows nodes to locate each other in the network based on their unique identifier or hash. +Swarm's overlay network is built on top of the underlay transport layer and uses [Kademlia](/docs/learn/DISC/kademlia) overlay routing to enable efficient and scalable communication between nodes. Kademlia is a distributed hash table (DHT) algorithm that allows nodes to locate each other in the network based on their unique identifier or hash. Swarm's DISC is an implementation of a Kademlia DHT optimized for storage. While the use of DHTs in distributed data storage protocols is common, for many implementations DHTs are used only for indexing of specific file locations. Swarm's DISC distinguishes itself from other implementations by instead breaking files into chunks and storing the chunks themselves directly within a Kademlia DHT. From 0cd8c64dc60c07847f66abba880679edd610e8c5 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Fri, 25 Oct 2024 14:24:51 +0700 Subject: [PATCH 15/25] fix capitalization --- docs/learn/DISC/neighborhoods.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/learn/DISC/neighborhoods.md b/docs/learn/DISC/neighborhoods.md index cedaf0d6e..57c146056 100644 --- a/docs/learn/DISC/neighborhoods.md +++ b/docs/learn/DISC/neighborhoods.md @@ -55,7 +55,7 @@ As the address of the chunk shown above shares the same ten leading binary bits *As with the example for nodes, we've abbreviated the chunk addresses to their leading four hexadecimal digits only and converted them to binary digits.* -### neighborhood Doubling +### Neighborhood Doubling As more and more chunks are assigned to neighborhoods, the chunk reserves of the nodes in that neighborhood will begin to fill up. Once the nodes' reserves in a neighborhood become full and can no longer store additional chunks, that neighborhood will split, with each half of the neighborhood taking responsibility for half of the chunks. This event is referred to as a "doubling", as it results in double the number of neighborhoods. The split is done by increasing the storage depth by one, so that the number of shared leading bits is increased by one. This results in a binary splitting of the neighborhood and associated chunks into two new neighborhoods and respective groups of chunks. From f208ee2048057f4480b2dc0e4e3330e08f1515a9 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Mon, 28 Oct 2024 14:27:21 +0700 Subject: [PATCH 16/25] Reorganize and rename sections, add new 'references' top level section --- docs/bee/installation/fund-your-node.md | 8 +- docs/bee/installation/install.md | 2 +- docs/bee/working-with-bee/bee-api.md | 2 +- docs/bee/working-with-bee/staking.md | 12 +-- docs/{learn => concepts}/DISC/DISC.md | 10 +-- .../DISC/erasure-coding.md | 0 docs/{learn => concepts}/DISC/kademlia.md | 4 +- .../{learn => concepts}/DISC/neighborhoods.md | 4 +- .../incentives/bandwidth-incentives.md | 0 .../incentives/overview.md | 8 +- .../incentives/postage-stamps.md | 14 ++-- .../incentives/price-oracle.md | 0 .../incentives/redistribution-game.md | 6 +- docs/{learn => concepts}/introduction.md | 0 .../protocols}/access-control.md | 0 .../features => concepts/protocols}/pss.md | 0 docs/{learn => concepts}/what-is-swarm.md | 4 +- docs/desktop/postage-stamps.md | 2 +- .../access-the-swarm/buy-a-stamp-batch.md | 18 ++--- .../access-the-swarm/erasure-coding.md | 6 +- docs/develop/access-the-swarm/syncing.md | 2 +- .../access-the-swarm/upload-and-download.md | 4 +- .../tools-and-features/access-control.md | 2 +- .../ecosystem.md => references/community.md} | 0 .../fair-data-society.md | 0 docs/{learn => references}/faq.md | 2 +- docs/{learn => references}/glossary.md | 2 +- docs/{learn => references}/smart-contracts.md | 0 docs/{learn => references}/tokens.md | 0 docusaurus.config.js | 76 ++++++++++--------- sidebars.js | 48 ++++++------ src/components/AmountAndDepthCalc.js | 4 +- src/pages/index.js | 2 +- 33 files changed, 126 insertions(+), 116 deletions(-) rename docs/{learn => concepts}/DISC/DISC.md (66%) rename docs/{learn => concepts}/DISC/erasure-coding.md (100%) rename docs/{learn => concepts}/DISC/kademlia.md (83%) rename docs/{learn => concepts}/DISC/neighborhoods.md (93%) rename docs/{learn => concepts}/incentives/bandwidth-incentives.md (100%) rename docs/{learn => concepts}/incentives/overview.md (93%) rename docs/{learn => concepts}/incentives/postage-stamps.md (93%) rename docs/{learn => concepts}/incentives/price-oracle.md (100%) rename docs/{learn => concepts}/incentives/redistribution-game.md (68%) rename docs/{learn => concepts}/introduction.md (100%) rename docs/{learn/features => concepts/protocols}/access-control.md (100%) rename docs/{learn/features => concepts/protocols}/pss.md (100%) rename docs/{learn => concepts}/what-is-swarm.md (93%) rename docs/{learn/ecosystem.md => references/community.md} (100%) rename docs/{learn => references}/fair-data-society.md (100%) rename docs/{learn => references}/faq.md (98%) rename docs/{learn => references}/glossary.md (97%) rename docs/{learn => references}/smart-contracts.md (100%) rename docs/{learn => references}/tokens.md (100%) diff --git a/docs/bee/installation/fund-your-node.md b/docs/bee/installation/fund-your-node.md index 4a4488112..094c8b491 100644 --- a/docs/bee/installation/fund-your-node.md +++ b/docs/bee/installation/fund-your-node.md @@ -6,14 +6,14 @@ id: fund-your-node In order to start your Bee node on the _mainnet_, its Ethereum wallet must be funded with: -- 1 [xBZZ](/docs/learn/glossary#xbzz-token), for traffic +- 1 [xBZZ](/docs/references/glossary#xbzz-token), for traffic accounting (this is optional, [see below](#basic-deployment)) -- some [xDAI](/docs/learn/glossary#xdai-token), to pay the gas fees of +- some [xDAI](/docs/references/glossary#xdai-token), to pay the gas fees of a couple of transactions on the [Gnosis - Chain](/docs/learn/glossary#gnosis-chain). + Chain](/docs/references/glossary#gnosis-chain). -Take note that xBZZ is the [bridged](/docs/learn/glossary#bridged-tokens) version of BZZ from Ethereum to the Gnosis Chain. +Take note that xBZZ is the [bridged](/docs/references/glossary#bridged-tokens) version of BZZ from Ethereum to the Gnosis Chain. ### A node's wallet diff --git a/docs/bee/installation/install.md b/docs/bee/installation/install.md index 71f4c493f..a1c480c11 100644 --- a/docs/bee/installation/install.md +++ b/docs/bee/installation/install.md @@ -526,7 +526,7 @@ resolver-options: ["https://mainnet.infura.io/v3/<>"] ### Set Target Neighborhood (Optional) -In older versions of Bee, [neighborhood](/docs/learn/DISC/neighborhoods) assignment was random by default. However, we can maximize a node's chances of winning xBZZ and also strengthen the resiliency of the network by strategically assigning neighborhoods to new nodes (see the [staking section](/docs/bee/working-with-bee/staking) for more details). +In older versions of Bee, [neighborhood](/docs/concepts/DISC/neighborhoods) assignment was random by default. However, we can maximize a node's chances of winning xBZZ and also strengthen the resiliency of the network by strategically assigning neighborhoods to new nodes (see the [staking section](/docs/bee/working-with-bee/staking) for more details). Therefore the default Bee configuration now includes the `neighborhood-suggester` option which is set by default to to use the Swarmscan neighborhood suggester (`https://api.swarmscan.io/v1/network/neighborhoods/suggestion`). An alternative suggester URL could be used as long as it returns a JSON file in the same format `{"neighborhood":"101000110101"}`, however only the Swarmscan suggester is officially recommended. diff --git a/docs/bee/working-with-bee/bee-api.md b/docs/bee/working-with-bee/bee-api.md index c94930005..cbb3ce02d 100644 --- a/docs/bee/working-with-bee/bee-api.md +++ b/docs/bee/working-with-bee/bee-api.md @@ -458,7 +458,7 @@ From the results we can see that we have a healthy neighborhood size when compar ### `/rchash` -Calling the /rchash endpoint will make your node generate a reserve commitment hash (the hash used in the [redistribution game](/docs/learn/incentives/redistribution-game)), and will report the amount of time it took to generate the hash. This is useful for getting a performance benchmark to ensure that your node's hardware is sufficient. +Calling the /rchash endpoint will make your node generate a reserve commitment hash (the hash used in the [redistribution game](/docs/concepts/incentives/redistribution-game)), and will report the amount of time it took to generate the hash. This is useful for getting a performance benchmark to ensure that your node's hardware is sufficient. ```bash sudo curl -sX GET http://localhost:1633/rchash/10/aaaa/aaaa | jq diff --git a/docs/bee/working-with-bee/staking.md b/docs/bee/working-with-bee/staking.md index 5ab6ed2c9..39e35b087 100644 --- a/docs/bee/working-with-bee/staking.md +++ b/docs/bee/working-with-bee/staking.md @@ -62,13 +62,13 @@ curl -X GET http://localhost:1633/redistributionstate | jq * `"hasSufficientFunds": ` - Shows whether the node has enough xDAI balance to submit at least five storage incentives redistribution related transactions. If `false` the node will not be permitted to participate in next round. * `"isFrozen": ` - Shows node frozen status. * `"isFullySynced": ` - Shows whether node's localstore has completed full historical syncing with all connected peers. -* `"phase": ` - Current phase of [redistribution game](/docs/learn/incentives/redistribution-game) (`commit`, `reveal`, or `claim`). +* `"phase": ` - Current phase of [redistribution game](/docs/concepts/incentives/redistribution-game) (`commit`, `reveal`, or `claim`). * `"round": ` - Current round of redistribution game. The round number is determined by dividing the current Gnosis Chain block height by the number of blocks in one round. One round takes 152 blocks, so using the "block" output from the example above we can confirm that the round number is 176319 (block 26800488 / 152 blocks = round 176319). * `"lastWonRound": ` - Number of round last won by this node. * `"lastPlayedRound": ` - Number of the last round where node's neighborhood was selected to participate in redistribution game. * `"lastFrozenRound": ` The number the round when node was last frozen. * `"block": ` - Gnosis block of the current redistribution game. -* `"reward": ` - Record of total reward received in [PLUR](/docs/learn/glossary#plur). +* `"reward": ` - Record of total reward received in [PLUR](/docs/references/glossary#plur). * `"fees": ` - Record of total spent in 1E-18 xDAI on all redistribution related transactions. @@ -111,11 +111,11 @@ curl -X DELETE http://localhost:1633/stake/withdrawable ## Maximize rewards -There are two main factors which determine the chances for a staking node to win a reward — neighborhood selection and stake density. Both of these should be considered together before starting up a Bee node for the first time. See the [incentives page](/docs/learn/incentives/redistribution-game) for more context. +There are two main factors which determine the chances for a staking node to win a reward — neighborhood selection and stake density. Both of these should be considered together before starting up a Bee node for the first time. See the [incentives page](/docs/concepts/incentives/redistribution-game) for more context. ### Neighborhood selection -By default when running a Bee node for the first time an overlay address will be generated and used to assign the node to a random [neighborhood](/docs/learn/DISC/neighborhoods). However, by using the `target-neighborhood` config option, a specific neighborhood can be selected in which to generate the node's overlay address. This is an excellent tool for maximizing reward chances as generally speaking running in a less populated neighborhood will increase the chances of winning a reward. See the [config section](/docs/bee/installation/install#set-target-neighborhood-optional) on the installation page for more information on how to set a target neighborhood. +By default when running a Bee node for the first time an overlay address will be generated and used to assign the node to a random [neighborhood](/docs/concepts/DISC/neighborhoods). However, by using the `target-neighborhood` config option, a specific neighborhood can be selected in which to generate the node's overlay address. This is an excellent tool for maximizing reward chances as generally speaking running in a less populated neighborhood will increase the chances of winning a reward. See the [config section](/docs/bee/installation/install#set-target-neighborhood-optional) on the installation page for more information on how to set a target neighborhood. ### Stake density @@ -126,7 +126,7 @@ $$ \text{stake density} = \text{staked xBZZ} \times {2}^\text{storageDepth} $$ -*To learn more about stake density and the mechanics of the incentives system, see the [incentives page](/docs/learn/incentives/redistribution-game).* +*To learn more about stake density and the mechanics of the incentives system, see the [incentives page](/docs/concepts/incentives/redistribution-game).* Stake density determines the weighted chances of nodes within a neighborhood of winning rewards. The chance of winning within a neighborhood corresponds to stake density. Stake density can be increased by depositing more xBZZ as stake (note that stake withdrawals are not currently possible, so any staked xBZZ is not currently recoverable). @@ -198,7 +198,7 @@ In this section we cover several commonly seen issues encountered for staking no ### Frozen node -A node will be frozen when the reserve commitment hash it submits in its [`commit` transaction](/docs/learn/incentives/redistribution-game) does not match the correct hash. The reserve commitment hash is used as proof that a node is storing the chunks it is responsible for. It will not be able to play in the redistribution game during the freezing period. See the [penalties](/docs/learn/incentives/redistribution-game) section for more information. +A node will be frozen when the reserve commitment hash it submits in its [`commit` transaction](/docs/concepts/incentives/redistribution-game) does not match the correct hash. The reserve commitment hash is used as proof that a node is storing the chunks it is responsible for. It will not be able to play in the redistribution game during the freezing period. See the [penalties](/docs/concepts/incentives/redistribution-game) section for more information. #### Check frozen status diff --git a/docs/learn/DISC/DISC.md b/docs/concepts/DISC/DISC.md similarity index 66% rename from docs/learn/DISC/DISC.md rename to docs/concepts/DISC/DISC.md index cb05ad02c..8deea8da8 100644 --- a/docs/learn/DISC/DISC.md +++ b/docs/concepts/DISC/DISC.md @@ -3,23 +3,23 @@ title: DISC id: disc --- -DISC (Distributed Immutable Storage of Chunks) is a storage solution developed by Swarm based on a modified implementation of a [Kademlia DHT](/docs/learn/DISC/kademlia) which has been specialized for data storage. Swarm's implementation of a DHT differs significantly in that it stores the content in the DHT directly, rather than just storing a list of seeders who are able to serve the content. This approach allows for much faster and more efficient retrieval of data. +DISC (Distributed Immutable Storage of Chunks) is a storage solution developed by Swarm based on a modified implementation of a [Kademlia DHT](/docs/concepts/DISC/kademlia) which has been specialized for data storage. Swarm's implementation of a DHT differs significantly in that it stores the content in the DHT directly, rather than just storing a list of seeders who are able to serve the content. This approach allows for much faster and more efficient retrieval of data. ### Kademlia Topology and Routing -[Kademlia](/docs/learn/DISC/kademlia) is a distributed hash table (DHT) widely used in peer-to-peer networks such as Ethereum and Bittorent. It serves as the routing and topology foundation for communication between nodes in the Swarm netowrk. It organizes nodes based on their overlay addresses and ensures that messages are relayed efficiently, even in a dynamic, decentralized environment. +[Kademlia](/docs/concepts/DISC/kademlia) is a distributed hash table (DHT) widely used in peer-to-peer networks such as Ethereum and Bittorent. It serves as the routing and topology foundation for communication between nodes in the Swarm netowrk. It organizes nodes based on their overlay addresses and ensures that messages are relayed efficiently, even in a dynamic, decentralized environment. One of the advantages of using Kademlia as a model for network topology is that both the number of forwarding "hops" required to route a chunk to its destination and the number of peer connections required to maintain Kademlia topology are logarithmic to the size of the network (a minimum of two connections is required in order to maintain Kademlia topology in case of network churn - nodes dropping in and out of the network). This makes Swarm a highly scalable system which is efficient even at very large scales. ### Neighborhoods -[Neighborhoods](/docs/learn/DISC/neighborhoods) are groups of nodes which are responsible for sharing the same chunks. The chunks which each neighborhood is responsible for storing are defined by the proximity order of the nodes and the chunks. In other words, each node is responsible for storing chunks with which their overlay addresses share a certain number of prefix bits, and together with other nodes which share the same prefix bits, make up neighborhoods which share the responsibility for storing the same chunks. +[Neighborhoods](/docs/concepts/DISC/neighborhoods) are groups of nodes which are responsible for sharing the same chunks. The chunks which each neighborhood is responsible for storing are defined by the proximity order of the nodes and the chunks. In other words, each node is responsible for storing chunks with which their overlay addresses share a certain number of prefix bits, and together with other nodes which share the same prefix bits, make up neighborhoods which share the responsibility for storing the same chunks. -Neighborhoods play a key role in providing data redundancy for chunks stored on Swarm since each node in a neighborhood will keep copies of the same chunks. The optional [erasure coding](/docs/learn/DISC/erasure-coding) feature can also be enabled for added redundancy and greater data protection. +Neighborhoods play a key role in providing data redundancy for chunks stored on Swarm since each node in a neighborhood will keep copies of the same chunks. The optional [erasure coding](/docs/concepts/DISC/erasure-coding) feature can also be enabled for added redundancy and greater data protection. ### Chunks -In the DISC model, chunks are the canonical unit of data. When a file is uploaded to Swarm, it gets broken down into 4kb pieces with attached metadata. The pieces then get distributed amongst nodes in the Swarm network based on their [overlay addresses](/docs/learn/glossary#overlay). There are two fundamental chunk types: content-addressed chunks and single-owner chunks. +In the DISC model, chunks are the canonical unit of data. When a file is uploaded to Swarm, it gets broken down into 4kb pieces with attached metadata. The pieces then get distributed amongst nodes in the Swarm network based on their [overlay addresses](/docs/references/glossary#overlay). There are two fundamental chunk types: content-addressed chunks and single-owner chunks. #### Content-Addressed Chunks and Single-Owner Chunks diff --git a/docs/learn/DISC/erasure-coding.md b/docs/concepts/DISC/erasure-coding.md similarity index 100% rename from docs/learn/DISC/erasure-coding.md rename to docs/concepts/DISC/erasure-coding.md diff --git a/docs/learn/DISC/kademlia.md b/docs/concepts/DISC/kademlia.md similarity index 83% rename from docs/learn/DISC/kademlia.md rename to docs/concepts/DISC/kademlia.md index 010f2c73b..00dc98869 100644 --- a/docs/learn/DISC/kademlia.md +++ b/docs/concepts/DISC/kademlia.md @@ -36,9 +36,9 @@ As mentioned above, Swarm's version of Kademlia differs from commonly used imple ### Proximity Order & Neighborhoods -Swarm introduces the concept of [proximity order (PO)](/docs/learn/glossary#proximity-order-po) as a discrete measure of node relatedness between two addresses. In contrast with Kademlia distance which is an exact measure of relatedness, PO is used to measure the relatedness between two addresses on a discrete scale based on the number of shared leading bits. Since this metric ignores all the bits after the shared leading bits, it is not an exact measure of distance between any two addresses. +Swarm introduces the concept of [proximity order (PO)](/docs/references/glossary#proximity-order-po) as a discrete measure of node relatedness between two addresses. In contrast with Kademlia distance which is an exact measure of relatedness, PO is used to measure the relatedness between two addresses on a discrete scale based on the number of shared leading bits. Since this metric ignores all the bits after the shared leading bits, it is not an exact measure of distance between any two addresses. -In Swarm's version of Kademlia, nodes are grouped into [neighborhoods](/docs/learn/DISC/neighborhoods) of nodes based on PO (ie., neighborhood are composed of nodes which all share the same leading binary prefix bits). Each neighborhood of nodes is responsible for storing the same set of chunks. +In Swarm's version of Kademlia, nodes are grouped into [neighborhoods](/docs/concepts/DISC/neighborhoods) of nodes based on PO (ie., neighborhood are composed of nodes which all share the same leading binary prefix bits). Each neighborhood of nodes is responsible for storing the same set of chunks. Neighborhoods are important for ensuring data redundancy, and they also play a role in the incentives system which guarantees nodes are rewarded for contributing resources to the network. diff --git a/docs/learn/DISC/neighborhoods.md b/docs/concepts/DISC/neighborhoods.md similarity index 93% rename from docs/learn/DISC/neighborhoods.md rename to docs/concepts/DISC/neighborhoods.md index 57c146056..b25eff709 100644 --- a/docs/learn/DISC/neighborhoods.md +++ b/docs/concepts/DISC/neighborhoods.md @@ -3,7 +3,7 @@ title: Neighborhoods id: neighborhoods --- -In Swarm, a neighborhood refers to an area of responsibility within the network, where nodes in proximity to one another share the task of storing and maintaining data chunks. It is defined by the [proximity order (PO)](/docs/learn/glossary#proximity-order-po) of nodes' addresses. Nodes within a neighborhood replicate data chunks to ensure that if one node goes offline, other nodes in the neighborhood can still retrieve and serve the content. +In Swarm, a neighborhood refers to an area of responsibility within the network, where nodes in proximity to one another share the task of storing and maintaining data chunks. It is defined by the [proximity order (PO)](/docs/references/glossary#proximity-order-po) of nodes' addresses. Nodes within a neighborhood replicate data chunks to ensure that if one node goes offline, other nodes in the neighborhood can still retrieve and serve the content. :::info @@ -47,7 +47,7 @@ Chunks are assigned to neighborhoods based on their addresses, which are in the > Chunk A address: `da49a42926015cd1e2bc552147c567b1ca13e8d4302c9e6026e79a24de328b65` > Chunk B address: `da696a3dfb0f7f952872eb33e0e2a1435c61f111ff361e64203b5348cc06dc8a` -As the address of the chunk shown above shares the same ten leading binary bits as the nodes in our example neighborhood, it falls into that neighborhood's [area of responsibility](/docs/learn/glossary#2-area-of-responsibility-related-depths), and all the nodes in that neighborhood are required to store that chunk: +As the address of the chunk shown above shares the same ten leading binary bits as the nodes in our example neighborhood, it falls into that neighborhood's [area of responsibility](/docs/references/glossary#2-area-of-responsibility-related-depths), and all the nodes in that neighborhood are required to store that chunk: > da49 --> 1101101001001001 > da69 --> 1101101001101001 diff --git a/docs/learn/incentives/bandwidth-incentives.md b/docs/concepts/incentives/bandwidth-incentives.md similarity index 100% rename from docs/learn/incentives/bandwidth-incentives.md rename to docs/concepts/incentives/bandwidth-incentives.md diff --git a/docs/learn/incentives/overview.md b/docs/concepts/incentives/overview.md similarity index 93% rename from docs/learn/incentives/overview.md rename to docs/concepts/incentives/overview.md index 618484ac3..b62385a32 100644 --- a/docs/learn/incentives/overview.md +++ b/docs/concepts/incentives/overview.md @@ -15,18 +15,18 @@ Storage incentives are used to reward node operators for providing their disk sp ### Postage Stamps -Postage stamps are used to pre-purchase the right to upload data on storm, much in the same way that real life postage stamps are used to pre-pay for use of the postal service. Postage stamps are purchased in batches rather than one by one, and are consumed when uploading data to Swarm. Postage stamp batches are purchased using xBZZ through the [postage stamp smart contract](https://gnosisscan.io/address/0x45a1502382541Cd610CC9068e88727426b696293#code). the xBZZ used to pay for postage stamp batches serve as the funds which are redistributed as storage incentives in the redistribution game. The price of postage stamps is set by the price oracle. Read more [here](/docs/learn/incentives/postage-stamps). +Postage stamps are used to pre-purchase the right to upload data on storm, much in the same way that real life postage stamps are used to pre-pay for use of the postal service. Postage stamps are purchased in batches rather than one by one, and are consumed when uploading data to Swarm. Postage stamp batches are purchased using xBZZ through the [postage stamp smart contract](https://gnosisscan.io/address/0x45a1502382541Cd610CC9068e88727426b696293#code). the xBZZ used to pay for postage stamp batches serve as the funds which are redistributed as storage incentives in the redistribution game. The price of postage stamps is set by the price oracle. Read more [here](/docs/concepts/incentives/postage-stamps). ### Redistribution Game -The redistribution game is used to redistribute the xBZZ paid into the postage stamp contract to full staking nodes which contribute their disk space to the network. The game is designed in such a way that the most profitable way to participate is to honestly store all the data for which a node is responsible. The game's rules are determined by the [redistribution smart contract](https://gnosisscan.io/address/0xFfF73fd14537277B3F3807e1AB0F85E17c0ABea5#code). The results of the game also supply the utilization signal which is used by the price oracle to set the price for postage stamps. Read more [here](/docs/learn/incentives/postage-stamps). +The redistribution game is used to redistribute the xBZZ paid into the postage stamp contract to full staking nodes which contribute their disk space to the network. The game is designed in such a way that the most profitable way to participate is to honestly store all the data for which a node is responsible. The game's rules are determined by the [redistribution smart contract](https://gnosisscan.io/address/0xFfF73fd14537277B3F3807e1AB0F85E17c0ABea5#code). The results of the game also supply the utilization signal which is used by the price oracle to set the price for postage stamps. Read more [here](/docs/concepts/incentives/postage-stamps). ### Price Oracle -The price oracle contract uses a utilization signal derived from the redistribution contract to set the price for postage stamps through the postage stamp contract. The utilization signal is based on a measure of data redundancy in the network. The postage stamp price is increased or decreased in order to maintain a healthy degree of redundancy. Read more [here](/docs/learn/incentives/price-oracle). +The price oracle contract uses a utilization signal derived from the redistribution contract to set the price for postage stamps through the postage stamp contract. The utilization signal is based on a measure of data redundancy in the network. The postage stamp price is increased or decreased in order to maintain a healthy degree of redundancy. Read more [here](/docs/concepts/incentives/price-oracle). ## Bandwidth Incentives In addition to storing data over time, nodes must also serve the data they store and must also relay data and messages to other nodes in the network. -Bandwidth incentives are used to reward nodes for relaying data across the network, both by serving up the data they store themselves and by serving as an intermediary relayer of data between other peers. The Swarm Accounting Protocol (SWAP) defines how bandwidth incentives work. At the core of SWAP is the concept of cheques along with the chequebook contract. Read more [here](/docs/learn/incentives/bandwidth-incentives). \ No newline at end of file +Bandwidth incentives are used to reward nodes for relaying data across the network, both by serving up the data they store themselves and by serving as an intermediary relayer of data between other peers. The Swarm Accounting Protocol (SWAP) defines how bandwidth incentives work. At the core of SWAP is the concept of cheques along with the chequebook contract. Read more [here](/docs/concepts/incentives/bandwidth-incentives). \ No newline at end of file diff --git a/docs/learn/incentives/postage-stamps.md b/docs/concepts/incentives/postage-stamps.md similarity index 93% rename from docs/learn/incentives/postage-stamps.md rename to docs/concepts/incentives/postage-stamps.md index eebb68e01..f1fc1cce5 100644 --- a/docs/learn/incentives/postage-stamps.md +++ b/docs/concepts/incentives/postage-stamps.md @@ -5,7 +5,7 @@ id: postage-stamps Postage stamps are used to pay for storing data on Swarm. They are purchased in batches which represent the pre-paid right to store data on Swarm in the same way that real-life postage stamps represent the pre-paid right to send mail through the postal service. -When a node uploads data to Swarm, it 'attaches' postage stamps to each [chunk](/docs/learn/DISC/) of data. The value assigned to a stamp indicates how much it is worth to persist the associated data on Swarm, which nodes use to prioritize which chunks to remove from their reserve first. +When a node uploads data to Swarm, it 'attaches' postage stamps to each [chunk](/docs/concepts/DISC/) of data. The value assigned to a stamp indicates how much it is worth to persist the associated data on Swarm, which nodes use to prioritize which chunks to remove from their reserve first. The value of a postage stamp decreases over time as if storage rent was regularly deducted from the batch balance. A stamp expires when the batch it is issued from has insufficient balance. A chunk with an expired stamp can not be used in the proof storer nodes need to submit in order to get compensated for their contributed storage space in the redistribution game, therefore such expired chunks are evicted from nodes' reserves and put into the cache where their continued persistence depends on their popularity. @@ -32,12 +32,12 @@ $$ $$ :::info -Note that due how buckets fill as described above, a batch can become fully utilised before its theoretical maximum volume has been reached. See [batch utilisation section below](/docs/learn/incentives/postage-stamps#batch-utilisation) for more information. +Note that due how buckets fill as described above, a batch can become fully utilised before its theoretical maximum volume has been reached. See [batch utilisation section below](/docs/concepts/incentives/postage-stamps#batch-utilisation) for more information. ::: ## Batch Depth and Batch Amount -Each batch of stamps has two key parameters, `batch depth` and `amount`, which are recorded on Gnosis Chain at issuance. Note that these "depths" do not refer to the depth terms used to describe topology which are outlined [here in the glossary](/docs/learn/glossary#depth-types). +Each batch of stamps has two key parameters, `batch depth` and `amount`, which are recorded on Gnosis Chain at issuance. Note that these "depths" do not refer to the depth terms used to describe topology which are outlined [here in the glossary](/docs/references/glossary#depth-types). ### Batch Depth @@ -55,7 +55,7 @@ $$ \text{Theoretical maximum batch volume} = 2^{batchDepth} \times \text{4 kb} $$ -However, due to the way postage stamp batches are utilised, batches will become fully utilised before stamping the theoretical maximum number of chunks. Therefore when deciding which batch depth to use, it is important to consider the effective amount of data that can be stored by a batch, and not the theoretical maximum. The effective rate of utilisation increases along with the batch depth. See [section on stamp batch utilisation below](/docs/learn/incentives/postage-stamps#batch-utilisation) for more information. +However, due to the way postage stamp batches are utilised, batches will become fully utilised before stamping the theoretical maximum number of chunks. Therefore when deciding which batch depth to use, it is important to consider the effective amount of data that can be stored by a batch, and not the theoretical maximum. The effective rate of utilisation increases along with the batch depth. See [section on stamp batch utilisation below](/docs/concepts/incentives/postage-stamps#batch-utilisation) for more information. ### Batch Amount (& Batch Cost) @@ -101,7 +101,7 @@ Utilisation of an immutable batch is computed using a hash map of size $$2^{buck ![](/img/batches_01.png) -As chunks are uploaded to Swarm, each chunk is assigned to a bucket based the first 16 binary digits of the [chunk's hash](/docs/learn/DISC/#chunks). The chunk will be assigned to whichever bucket's key matches the first 16 bits of its hash, and that bucket's counter will be incremented by 1. +As chunks are uploaded to Swarm, each chunk is assigned to a bucket based the first 16 binary digits of the [chunk's hash](/docs/concepts/DISC/#chunks). The chunk will be assigned to whichever bucket's key matches the first 16 bits of its hash, and that bucket's counter will be incremented by 1. The batch is deemed "full" when ANY of these counters reach a certain max value. The max value is computed from the batch depth as such: $$2^{(batchDepth-bucketDepth)}$$. For example with batch depth of 24, the max value is $$2^{(24-16)}$$ or 256. A bucket can be thought of as have a number of "slots" equal to this maximum value, and every time the bucket's counter is incremented, one of its slots gets filled. @@ -176,7 +176,7 @@ The implications of this behaviour are that even a small change to the data of a Due to the nature of batch utilisation described above, batches are often fully utilised before reaching their theoretical maximum storage amount. However as the batch depth increases, the chance of a postage batch becoming fully utilised early decreases. At batch depth 24, there is a 0.1% chance that a batch will be fully utilised/start replacing old chunks before reaching 64.33% of its theoretical maximum. -Let's look at an example to make it clearer. Using the method of calculating the theoretical maximum storage amount [outlined above](/docs/learn/incentives/postage-stamps#batch-depth), we can see that for a batch depth of 24, the theoretical maximum amount which can be stored is 68.72 gb: +Let's look at an example to make it clearer. Using the method of calculating the theoretical maximum storage amount [outlined above](/docs/concepts/incentives/postage-stamps#batch-depth), we can see that for a batch depth of 24, the theoretical maximum amount which can be stored is 68.72 gb: $$ 2^{24+12} = \text{68,719,476,736 bytes} = \text{68.72 gb} @@ -230,6 +230,6 @@ The provided table shows the effective volume for each batch depth from 20 to 41 This table is based on preliminary calculations and may be subject to change. ::: -Nodes' storage is actually defined as a number of chunks with a size of 4kb (2^12 bytes) each, but in fact some [SOC](/docs/learn/DISC/#chunks) chunks can be a few bytes longer, and some chunks can be smaller, so the conversion is not precise. Furthermore, due to the way Swarm represents files in a Merkle tree, the intermediate chunks are additional overhead which must also be accounted for. +Nodes' storage is actually defined as a number of chunks with a size of 4kb (2^12 bytes) each, but in fact some [SOC](/docs/concepts/DISC/#chunks) chunks can be a few bytes longer, and some chunks can be smaller, so the conversion is not precise. Furthermore, due to the way Swarm represents files in a Merkle tree, the intermediate chunks are additional overhead which must also be accounted for. Additionally, when a node stores chunks it uses additional indexes — therefore the disk space a maximally filled reserve would demand cannot be calculated with perfect accuracy. diff --git a/docs/learn/incentives/price-oracle.md b/docs/concepts/incentives/price-oracle.md similarity index 100% rename from docs/learn/incentives/price-oracle.md rename to docs/concepts/incentives/price-oracle.md diff --git a/docs/learn/incentives/redistribution-game.md b/docs/concepts/incentives/redistribution-game.md similarity index 68% rename from docs/learn/incentives/redistribution-game.md rename to docs/concepts/incentives/redistribution-game.md index 7970815ea..75dae81e7 100644 --- a/docs/learn/incentives/redistribution-game.md +++ b/docs/concepts/incentives/redistribution-game.md @@ -6,15 +6,15 @@ id: redistribution-game ## Redistribution Game -The redistribution game is used to redistribute the xBZZ which is accumulated by the [postage stamp](/docs/learn/incentives/postage-stamps) contract when postage stamp batches are purchased in order to pay for uploading data to Swarm. The reward paid out in the redistribution game serve as incentive for nodes to continue providing their disk space to the network. The game is designed in such a way that the optimal strategy for participants is to honestly store the data for which they are responsible. +The redistribution game is used to redistribute the xBZZ which is accumulated by the [postage stamp](/docs/concepts/incentives/postage-stamps) contract when postage stamp batches are purchased in order to pay for uploading data to Swarm. The reward paid out in the redistribution game serve as incentive for nodes to continue providing their disk space to the network. The game is designed in such a way that the optimal strategy for participants is to honestly store the data for which they are responsible. ### Redistribution Game Details -When someone wants to upload data to Swarm, they do so by buying postage stamp batches with xBZZ. The xBZZ is collected and later paid out to storage provider nodes as a part of the redistribution game. Every 152 Gnosis Chain blocks a single [neighborhood](/docs/learn/DISC/neighborhoods) is selected to play the redistribution game. For each round of the game, one node from the selected neighborhood will have the chance to win a reward which is paid out from the accumulated xBZZ. +When someone wants to upload data to Swarm, they do so by buying postage stamp batches with xBZZ. The xBZZ is collected and later paid out to storage provider nodes as a part of the redistribution game. Every 152 Gnosis Chain blocks a single [neighborhood](/docs/concepts/DISC/neighborhoods) is selected to play the redistribution game. For each round of the game, one node from the selected neighborhood will have the chance to win a reward which is paid out from the accumulated xBZZ. The game has 3 phases, `commit`, `reveal`, and `claim`. In the `reveal` phase of a previous game, an "anchor" address is randomly generated and used to determine the neighborhood for the current round. -In the `commit` phase, nodes issue an on-chain transaction including an encrypted hash of the data they are storing (the unencrypted hash is known as the "reserve commitment") along with the [depth](/docs/learn/glossary#2-area-of-responsibility-related-depths) for which they are reporting. This serves as an attestation of the data they are storing without revealing any other information. +In the `commit` phase, nodes issue an on-chain transaction including an encrypted hash of the data they are storing (the unencrypted hash is known as the "reserve commitment") along with the [depth](/docs/references/glossary#2-area-of-responsibility-related-depths) for which they are reporting. This serves as an attestation of the data they are storing without revealing any other information. In the `reveal` phase, each node reveals the decryption key for their encrypted hashes thereby publishing the hash. One of the nodes is chosen as the honest node, and from among the honest nodes, one node is chosen as the winner. The winner is chosen at random among the honest nodes, but it is weighted in proportion to each node's stake density. Stake density is calculated as so: diff --git a/docs/learn/introduction.md b/docs/concepts/introduction.md similarity index 100% rename from docs/learn/introduction.md rename to docs/concepts/introduction.md diff --git a/docs/learn/features/access-control.md b/docs/concepts/protocols/access-control.md similarity index 100% rename from docs/learn/features/access-control.md rename to docs/concepts/protocols/access-control.md diff --git a/docs/learn/features/pss.md b/docs/concepts/protocols/pss.md similarity index 100% rename from docs/learn/features/pss.md rename to docs/concepts/protocols/pss.md diff --git a/docs/learn/what-is-swarm.md b/docs/concepts/what-is-swarm.md similarity index 93% rename from docs/learn/what-is-swarm.md rename to docs/concepts/what-is-swarm.md index 5ba78b44c..3646830f5 100644 --- a/docs/learn/what-is-swarm.md +++ b/docs/concepts/what-is-swarm.md @@ -47,9 +47,9 @@ As the [libp2p](https://libp2p.io/) library meets all these requirements it has ### 2. Overlay Network -The second part of Swarm is an overlay network with protocols powering the [Distributed Immutable Store of Chunks (DISC)](/docs/learn/DISC/). This layer is responsible for storing and retrieving data in a decentralised and secure manner. +The second part of Swarm is an overlay network with protocols powering the [Distributed Immutable Store of Chunks (DISC)](/docs/concepts/DISC/). This layer is responsible for storing and retrieving data in a decentralised and secure manner. -Swarm's overlay network is built on top of the underlay transport layer and uses [Kademlia](/docs/learn/DISC/kademlia) overlay routing to enable efficient and scalable communication between nodes. Kademlia is a distributed hash table (DHT) algorithm that allows nodes to locate each other in the network based on their unique identifier or hash. +Swarm's overlay network is built on top of the underlay transport layer and uses [Kademlia](/docs/concepts/DISC/kademlia) overlay routing to enable efficient and scalable communication between nodes. Kademlia is a distributed hash table (DHT) algorithm that allows nodes to locate each other in the network based on their unique identifier or hash. Swarm's DISC is an implementation of a Kademlia DHT optimized for storage. While the use of DHTs in distributed data storage protocols is common, for many implementations DHTs are used only for indexing of specific file locations. Swarm's DISC distinguishes itself from other implementations by instead breaking files into chunks and storing the chunks themselves directly within a Kademlia DHT. diff --git a/docs/desktop/postage-stamps.md b/docs/desktop/postage-stamps.md index e7d1e634d..d8dd7e748 100644 --- a/docs/desktop/postage-stamps.md +++ b/docs/desktop/postage-stamps.md @@ -23,7 +23,7 @@ And then clicking the ***Buy New Postage Stamp*** button: ### Depth and Amount -Batch [depth and amount](/docs/learn/incentives/postage-stamps) are the two required parameters which must be set when purchasing a postage stamp batch. Depth determines how many chunks can be stamped with a batch while amount determines how much xBZZ is assigned per chunk. +Batch [depth and amount](/docs/concepts/incentives/postage-stamps) are the two required parameters which must be set when purchasing a postage stamp batch. Depth determines how many chunks can be stamped with a batch while amount determines how much xBZZ is assigned per chunk. ![](/img/stamps3.png) diff --git a/docs/develop/access-the-swarm/buy-a-stamp-batch.md b/docs/develop/access-the-swarm/buy-a-stamp-batch.md index b074fc512..5118b9512 100644 --- a/docs/develop/access-the-swarm/buy-a-stamp-batch.md +++ b/docs/develop/access-the-swarm/buy-a-stamp-batch.md @@ -10,9 +10,9 @@ import TabItem from '@theme/TabItem'; -A postage batch is required to upload data to Swarm. Postage stamp batches represent _right to write_ data on Swarm's [DISC (Distributed Immutable Store of Chunks)](/docs/learn/DISC/). The parameters which control the duration and quantity of data that can be stored by a postage batch are `depth` and `amount`, with `depth` determining data volume that can be uploaded by the batch and `amount` determining storage duration of data uploaded with the batch. +A postage batch is required to upload data to Swarm. Postage stamp batches represent _right to write_ data on Swarm's [DISC (Distributed Immutable Store of Chunks)](/docs/concepts/DISC/). The parameters which control the duration and quantity of data that can be stored by a postage batch are `depth` and `amount`, with `depth` determining data volume that can be uploaded by the batch and `amount` determining storage duration of data uploaded with the batch. -For a deeper understanding of how `depth` and `amount` parameters determine the data volume and storage duration of a postage batch, see the [postage stamp page](/docs/learn/incentives/postage-stamps/). +For a deeper understanding of how `depth` and `amount` parameters determine the data volume and storage duration of a postage batch, see the [postage stamp page](/docs/concepts/incentives/postage-stamps/). ## Fund your node's wallet. @@ -94,12 +94,12 @@ When purchasing a batch of stamps there are several parameters and options which ### Choosing `depth` :::caution -The minimum value for `depth` is 17, however a higher depth value is recommended for most use cases due to the [mechanics of stamp batch utilisation](/docs/learn/incentives/postage-stamps/#batch-utilisation). See [the depths utilisation table](/docs/learn/incentives/postage-stamps/#effective-utilisation-table) to help decide which depth is best for your use case. +The minimum value for `depth` is 17, however a higher depth value is recommended for most use cases due to the [mechanics of stamp batch utilisation](/docs/concepts/incentives/postage-stamps/#batch-utilisation). See [the depths utilisation table](/docs/concepts/incentives/postage-stamps/#effective-utilisation-table) to help decide which depth is best for your use case. ::: One notable aspect of batch utilisation is that the entire batch is considered fully utilised as soon as any one of its buckets are filled. This means that the actual amount of chunks storable by a batch is less than the nominal maximum amount. -See the [postage stamp page](/docs/learn/incentives/postage-stamps) for a more complete explanation of how batch utilisation works and a [table](/docs/learn/incentives/postage-stamps#effective-utilisation-table) with the specific amounts of data which can be safely uploaded for each `depth` value. +See the [postage stamp page](/docs/concepts/incentives/postage-stamps) for a more complete explanation of how batch utilisation works and a [table](/docs/concepts/incentives/postage-stamps#effective-utilisation-table) with the specific amounts of data which can be safely uploaded for each `depth` value. ### Choosing `amount` @@ -107,18 +107,18 @@ See the [postage stamp page](/docs/learn/incentives/postage-stamps) for a more c The minimum `amount` value for purchasing stamps is required to be at least enough to pay for 24 hours of storage. To find this value multiply the lastPrice value from the postage stamp contract times 17280 (the number of blocks in 24 hours). You can also use the [calculator](#calculators) below. This requirement is in place in order to prevent spamming the network. ::: -The `amount` parameter determines how much xBZZ is assigned per chunk for a postage stamp batch. You can use the calculators below to find the appropriate `amount` value for your target duration of storage and can also preview the price. For more information see the [postage stamp](/docs/learn/incentives/postage-stamps#batch-depth-and-batch-amount) page where a more complete description is included. +The `amount` parameter determines how much xBZZ is assigned per chunk for a postage stamp batch. You can use the calculators below to find the appropriate `amount` value for your target duration of storage and can also preview the price. For more information see the [postage stamp](/docs/concepts/incentives/postage-stamps#batch-depth-and-batch-amount) page where a more complete description is included. ### Mutable or Immutable? -Depending on the use case, uploaders may desire to use mutable or immutable batches. The fundamental difference between immutable and mutable batches is that immutable batches become unusable once their capacity is filled, while for mutable batches, once their capacity is filled, they may continue to be used, however older chunks of data will be overwritten with the newer once over capacity. The default batch type is immutable. In order to set the batch type to mutable, the `immutable` header should be set to `false`. See [this section on postage stamp batch utilisation](/docs/learn/incentives/postage-stamps#which-type-of-batch-to-use) to learn more about mutable vs immutable batches, and about which type may be right for your use case. +Depending on the use case, uploaders may desire to use mutable or immutable batches. The fundamental difference between immutable and mutable batches is that immutable batches become unusable once their capacity is filled, while for mutable batches, once their capacity is filled, they may continue to be used, however older chunks of data will be overwritten with the newer once over capacity. The default batch type is immutable. In order to set the batch type to mutable, the `immutable` header should be set to `false`. See [this section on postage stamp batch utilisation](/docs/concepts/incentives/postage-stamps#which-type-of-batch-to-use) to learn more about mutable vs immutable batches, and about which type may be right for your use case. ## Calculators The following postage batch calculators allow you to conveniently find the depth and amount values for a given storage duration and storage volume, or to find the storage duration and storage volume for a given depth and amount. The results will display the cost in xBZZ for the postage batch. The current pricing information is sourced from the Swarmscan API and will vary over time. :::info -The 'effective volume' is the volume of data that can safely stored for each storage depth. The 'theoretical max volume' is significantly lower than the effective volume at lower depths and the two values trend towards the same value at higher depths. The lowest depth with an effective volume above zero is 22, with an effective depth of 4.93 GB. Lower depth values can be used for smaller uploads but do not come with the same storage guarantees. [Learn more here](/docs/learn/incentives/postage-stamps#effective-utilisation-table). +The 'effective volume' is the volume of data that can safely stored for each storage depth. The 'theoretical max volume' is significantly lower than the effective volume at lower depths and the two values trend towards the same value at higher depths. The lowest depth with an effective volume above zero is 22, with an effective depth of 4.93 GB. Lower depth values can be used for smaller uploads but do not come with the same storage guarantees. [Learn more here](/docs/concepts/incentives/postage-stamps#effective-utilisation-table). ::: ### Depth & Amount to Time & Volume Calculator @@ -127,7 +127,7 @@ The 'effective volume' is the volume of data that can safely stored for each sto ### Time & Volume to Depth & Amount Calculator -The recommended depth in this calculator's results is the lowest depth value whose [effective volume](/docs/learn/incentives/postage-stamps#effective-utilisation-table) is greater than the entered volume. +The recommended depth in this calculator's results is the lowest depth value whose [effective volume](/docs/concepts/incentives/postage-stamps#effective-utilisation-table) is greater than the entered volume. @@ -205,7 +205,7 @@ It is not possible to reupload unencrypted content which was stamped using an ex At present, TTL is a primitive calculation based on the current storage price and the assumption that storage price will remain static in the future. As more data is uploaded into Swarm, the price of storage will begin to increase. For data that it is important to keep alive, make sure your batches have plenty of time to live! ::: -In order to make sure your *batch* has sufficient *remaining balance* to be stored and served by nodes in its [*area of responsibility*](/docs/learn/glossary#2-area-of-responsibility-related-depths), you must regularly check on its _time to live_ and act accordingly. The *time to live* is the number of seconds before the chunks will be considered for garbage collection by nodes in the network. +In order to make sure your *batch* has sufficient *remaining balance* to be stored and served by nodes in its [*area of responsibility*](/docs/references/glossary#2-area-of-responsibility-related-depths), you must regularly check on its _time to live_ and act accordingly. The *time to live* is the number of seconds before the chunks will be considered for garbage collection by nodes in the network. The remaining *time to live* in seconds is shown in the API in the returned json object as the value for `batchTTL`, and with Swarm CLI you will see the formatted TTL as the `TTL` value. diff --git a/docs/develop/access-the-swarm/erasure-coding.md b/docs/develop/access-the-swarm/erasure-coding.md index 1c094b8e5..631a3d014 100644 --- a/docs/develop/access-the-swarm/erasure-coding.md +++ b/docs/develop/access-the-swarm/erasure-coding.md @@ -5,7 +5,7 @@ id: erasure-coding import RedundancyCalc from '@site/src/components/RedundancyCalc.js'; -[Erasure coding](/docs/learn/DISC/erasure-coding) is a powerful method for safeguarding data, offering robust protection against partial data loss. This technique involves dividing the original data into multiple fragments and generating extra parity fragments to introduce redundancy. A key advantage of erasure coding is its ability to recover the complete original data even if some fragments are lost. Additionally, it offers the flexibility to customize the level of data loss protection, making it a versatile and reliable choice for preserving data integrity on Swarm. For a more in depth dive into erasure coding on Swarm, see the [erasure coding paper](https://papers.ethswarm.org/p/erasure/) from the Swarm research team. +[Erasure coding](/docs/concepts/DISC/erasure-coding) is a powerful method for safeguarding data, offering robust protection against partial data loss. This technique involves dividing the original data into multiple fragments and generating extra parity fragments to introduce redundancy. A key advantage of erasure coding is its ability to recover the complete original data even if some fragments are lost. Additionally, it offers the flexibility to customize the level of data loss protection, making it a versatile and reliable choice for preserving data integrity on Swarm. For a more in depth dive into erasure coding on Swarm, see the [erasure coding paper](https://papers.ethswarm.org/p/erasure/) from the Swarm research team. ## Uploading With Erasure Coding @@ -33,7 +33,7 @@ The accepted values for the `swarm-redundancy-level` header range from the defau | 3 | Insane | | 4 | Paranoid | -For more details about each level of protection refer to the [erasure coding page](/docs/learn/DISC/erasure-coding) in the learn section and refer to the [erasure coding paper](https://papers.ethswarm.org/p/erasure/) for an even deeper dive. +For more details about each level of protection refer to the [erasure coding page](/docs/concepts/DISC/erasure-coding) in the learn section and refer to the [erasure coding paper](https://papers.ethswarm.org/p/erasure/) for an even deeper dive. ## Cost Calculator Widget @@ -41,7 +41,7 @@ This calculator takes as input an amount of data and an erasure coding redundanc -For more details of erasure coding costs, see [here](/docs/learn/DISC/erasure-coding). +For more details of erasure coding costs, see [here](/docs/concepts/DISC/erasure-coding). ## Downloading Erasure Encoded Data diff --git a/docs/develop/access-the-swarm/syncing.md b/docs/develop/access-the-swarm/syncing.md index 7b96661ec..63dc0cac9 100644 --- a/docs/develop/access-the-swarm/syncing.md +++ b/docs/develop/access-the-swarm/syncing.md @@ -55,5 +55,5 @@ curl http://localhost:1633/tags/5 | jq The response contains all the information that you need to follow the status of your file as it is synced with the network. :::info -The numbers that the `tags` endpoint returns under `total`, `processed` and `synced` are denominated in [*chunks*](/docs/learn/DISC/#chunks), i.e. Swarm's 4kb data units. +The numbers that the `tags` endpoint returns under `total`, `processed` and `synced` are denominated in [*chunks*](/docs/concepts/DISC/#chunks), i.e. Swarm's 4kb data units. ::: diff --git a/docs/develop/access-the-swarm/upload-and-download.md b/docs/develop/access-the-swarm/upload-and-download.md index 3c3e78bd2..bd94765b0 100644 --- a/docs/develop/access-the-swarm/upload-and-download.md +++ b/docs/develop/access-the-swarm/upload-and-download.md @@ -11,10 +11,10 @@ When you upload your files to the Swarm, they are split into 4kb _chunks_ and then distributed to nodes in the network that are responsible for storing and serving these parts of your content. To learn more about how Swarm's decentralized storage solution works, -check out the ["Learn" section](/docs/learn/what-is-swarm). +check out the ["Learn" section](/docs/concepts/what-is-swarm). In order for you to be able to upload any data to the network, -you must first purchase [postage stamps](/docs/learn/incentives/postage-stamps) +you must first purchase [postage stamps](/docs/concepts/incentives/postage-stamps) and then use those stamps to upload your data. Keep on reading below to learn how. ## Uploads and Download Endpoints Overview diff --git a/docs/develop/tools-and-features/access-control.md b/docs/develop/tools-and-features/access-control.md index 2fa5d0d72..d161d6a56 100644 --- a/docs/develop/tools-and-features/access-control.md +++ b/docs/develop/tools-and-features/access-control.md @@ -4,7 +4,7 @@ id: act --- :::info -This is guide contains a detailed explanation of how to use the ACT feature, but does not cover its higher level concepts. To better understand how ACT works and why to use it, read [the ACT page in the "Learn" section](/docs/learn/features/access-control). +This is guide contains a detailed explanation of how to use the ACT feature, but does not cover its higher level concepts. To better understand how ACT works and why to use it, read [the ACT page in the "Learn" section](/docs/concepts/protocols/access-control). ::: diff --git a/docs/learn/ecosystem.md b/docs/references/community.md similarity index 100% rename from docs/learn/ecosystem.md rename to docs/references/community.md diff --git a/docs/learn/fair-data-society.md b/docs/references/fair-data-society.md similarity index 100% rename from docs/learn/fair-data-society.md rename to docs/references/fair-data-society.md diff --git a/docs/learn/faq.md b/docs/references/faq.md similarity index 98% rename from docs/learn/faq.md rename to docs/references/faq.md index 5afdca38f..ef07eabb9 100644 --- a/docs/learn/faq.md +++ b/docs/references/faq.md @@ -55,7 +55,7 @@ There are many ways to acquire BZZ tokens, either on custodial centralised excha ### What is the BZZ token address? -See [this page](/docs/learn/smart-contracts) for a list of relevant token addresses. +See [this page](/docs/references/smart-contracts) for a list of relevant token addresses. ### What is the BZZ token supply? diff --git a/docs/learn/glossary.md b/docs/references/glossary.md similarity index 97% rename from docs/learn/glossary.md rename to docs/references/glossary.md index 850ccf9ee..21f8a39ab 100644 --- a/docs/learn/glossary.md +++ b/docs/references/glossary.md @@ -36,7 +36,7 @@ Overlay addresses are a Keccak256 hash of a node’s Gnosis Chain address and th ## Neighborhood -[Neighborhoods](/docs/learn/DISC/neighborhoods) are nodes which are grouped together based on their overlay addresses and are responsible for storing the same chunks of data. The chunks which each neighborhood are responsible for storing are defined by the proximity order of the nodes and the chunks. +[Neighborhoods](/docs/concepts/DISC/neighborhoods) are nodes which are grouped together based on their overlay addresses and are responsible for storing the same chunks of data. The chunks which each neighborhood are responsible for storing are defined by the proximity order of the nodes and the chunks. ## Underlay diff --git a/docs/learn/smart-contracts.md b/docs/references/smart-contracts.md similarity index 100% rename from docs/learn/smart-contracts.md rename to docs/references/smart-contracts.md diff --git a/docs/learn/tokens.md b/docs/references/tokens.md similarity index 100% rename from docs/learn/tokens.md rename to docs/references/tokens.md diff --git a/docusaurus.config.js b/docusaurus.config.js index 284a10350..b2d3dc1c4 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -115,62 +115,39 @@ module.exports = { items: [ { type: 'dropdown', - activeBasePath: 'docs/learn', - label: 'Learn', + activeBasePath: 'docs/concepts', + label: 'Concepts', position: 'left', className: 'inter', items: [ { - to: 'docs/learn/introduction', + to: 'docs/concepts/introduction', label: 'Introduction', }, { - to: '/docs/learn/what-is-swarm', + to: '/docs/concepts/what-is-swarm', label: 'What is Swarm?', }, { - to: '/docs/learn/DISC/', + to: '/docs/concepts/DISC/', label: 'DISC Storage', }, { - to: '/docs/learn/incentives/overview', + to: '/docs/concepts/incentives/overview', label: 'Incentives', }, { - to: '/docs/learn/features/pss', - label: 'Features', + to: '/docs/concepts/protocols/pss', + label: 'Protocols', }, - { - to: '/docs/learn/smart-contracts', - label: 'Smart Contracts', - }, - { - to: 'docs/learn/tokens', - label: 'Tokens', - }, - { - to: '/docs/learn/community', - label: 'Community', - }, - { - to: '/docs/learn/fair-data-society', - label: 'Fair Data Society', - }, - { - to: 'docs/learn/glossary', - label: 'Glossary', - }, - { - to: 'docs/learn/faq', - label: 'FAQ ', - } + ] }, { type: 'dropdown', activeBasePath: 'docs/desktop', - label: 'Desktop', + label: 'Desktop App', position: 'left', items: [ { @@ -214,7 +191,7 @@ module.exports = { { type: 'dropdown', activeBasePath: 'docs/bee', - label: 'Bee', + label: 'Bee Client', position: 'left', items: [ { @@ -268,10 +245,39 @@ module.exports = { // position: 'left', // }, // {to: 'blog', label: 'Blog', position: 'left'}, + { + type: 'dropdown', + activeBasePath: 'docs/references', + label: 'References', + position: 'left', + items: [ + { + to: 'docs/references/smart-contracts', + label: 'Smart Contracts' + }, + { + to: 'docs/references/tokens', + label: 'Tokens' + }, + { + to: '/docs/references/community', + label: 'Community', + }, + { + to: 'docs/references/glossary', + label: 'Glossary' + }, + { + to: 'docs/references/faq', + label: 'FAQ' + }, + ] + }, + { to: '/api/', activeBasePath: '/api/', - label: 'API Reference', + label: 'API Specification', position: 'left', }, { diff --git a/sidebars.js b/sidebars.js index 0f6f83753..6c5060195 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1,15 +1,15 @@ module.exports = { - learn: [ - 'learn/introduction', - 'learn/what-is-swarm', + concepts: [ + 'concepts/introduction', + 'concepts/what-is-swarm', { type: 'category', label: 'DISC Storage', items: [ - 'learn/DISC/disc', - 'learn/DISC/kademlia', - 'learn/DISC/neighborhoods', - 'learn/DISC/erasure-coding', + 'concepts/DISC/disc', + 'concepts/DISC/kademlia', + 'concepts/DISC/neighborhoods', + 'concepts/DISC/erasure-coding', ], collapsed: false }, @@ -17,29 +17,25 @@ module.exports = { type: 'category', label: 'Incentives', items: [ - 'learn/incentives/overview', - 'learn/incentives/redistribution-game', - 'learn/incentives/postage-stamps', - 'learn/incentives/bandwidth-incentives', - 'learn/incentives/price-oracle', + 'concepts/incentives/overview', + 'concepts/incentives/redistribution-game', + 'concepts/incentives/postage-stamps', + 'concepts/incentives/bandwidth-incentives', + 'concepts/incentives/price-oracle', ], collapsed: false }, { type: 'category', - label: 'Features', + label: 'Protocols', items: [ - 'learn/features/pss', - 'learn/features/access-control', + 'concepts/protocols/pss', + 'concepts/protocols/access-control', ], collapsed: false }, - 'learn/smart-contracts', - 'learn/tokens', - 'learn/community', - 'learn/fair-data-society', - 'learn/glossary', - 'learn/faq', + + ], desktop: [ 'desktop/introduction', @@ -140,5 +136,13 @@ module.exports = { collapsed: false }, - ] + ], + References: [ + 'references/smart-contracts', + 'references/tokens', + 'references/glossary', + 'references/community', + 'references/fair-data-society', + 'references/faq' + ], }; diff --git a/src/components/AmountAndDepthCalc.js b/src/components/AmountAndDepthCalc.js index 41868f2a8..45b4a8ced 100644 --- a/src/components/AmountAndDepthCalc.js +++ b/src/components/AmountAndDepthCalc.js @@ -269,11 +269,11 @@ function FetchPriceComponent() { Suggested Safe Depth - {`${depth} (for an `}effective volume{` of ${depthToVolume[depth]})`} + {`${depth} (for an `}effective volume{` of ${depthToVolume[depth]})`} Suggested Minimum Depth - {minimumDepth} (see batch utilisation - may require dilution) + {minimumDepth} (see batch utilisation - may require dilution) Batch Cost for Safe Depth diff --git a/src/pages/index.js b/src/pages/index.js index 8b77eb60c..ceff8bcc6 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -28,7 +28,7 @@ function Home() {

Official documentation of the decentralised data storage and distribution protocol built to power the next generation of censorship-resistant, unstoppable, serverless dapps.

- +
From 64e7507a7c2516cd533addbc07abf5603a890de4 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Tue, 29 Oct 2024 13:21:01 +0700 Subject: [PATCH 17/25] add to description of Bee client in Introduction --- docs/concepts/introduction.md | 4 +- docusaurus.config.js | 101 +++++++++++++++------------------- 2 files changed, 46 insertions(+), 59 deletions(-) diff --git a/docs/concepts/introduction.md b/docs/concepts/introduction.md index 08fd4bc95..e4cefd9cc 100644 --- a/docs/concepts/introduction.md +++ b/docs/concepts/introduction.md @@ -8,12 +8,12 @@ Swarm is a peer-to-peer network of Bee nodes that collectively provide censorshi ## Bee Client -Bee is a Swarm client implemented in Go and is the basic building block for the Swarm network. Bee nodes collectively work together to form a private, decentralized, and self sustaining network for permissionless publishing and data storage. +Bee is a Swarm client implemented in Go and is the basic building block for the Swarm network. Bee nodes collectively work together to form a private, decentralized, and self sustaining network for permissionless publishing and data storage. You can learn more about how Bee clients work by reading about the [concepts and protocols](/docs/concepts/what-is-swarm/) which underpin the Swarm network. To get hands on experience working with Swarm, you can start by learning how to [install and operate a Bee node](/docs/bee/installation/quick-start). ## Swarm Foundation -The goal of [Swarm Foundation](https://www.ethswarm.org/foundation) is to support and contribute to the creation of technology whose code will be open to all and will allow the storage and exchange of data in a decentralised manner. To this end, the foundation intends in particular to promote and support a community and a sustainable and independent ecosystem for the development of free open source software (FLOSS), allowing for digital services coordinated by crypto-economic incentives that process, distribute and store data. +The goal of [Swarm Foundation](https://www.ethswarm.org/foundation) is to foster the creation of technology whose code will be open to all and will allow the storage and exchange of data in a decentralised manner. To this end, the foundation intends in particular to promote and support a community and a sustainable and independent ecosystem for the development of free open source software (FLOSS), allowing for digital services coordinated by crypto-economic incentives that process, distribute and store data. Its mission is to empower digital freedom by promoting the development and maintenance of the Swarm network, the base layer of the emerging fair data economy, and the community surrounding this network. diff --git a/docusaurus.config.js b/docusaurus.config.js index b2d3dc1c4..363f871c8 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -144,50 +144,6 @@ module.exports = { ] }, - { - type: 'dropdown', - activeBasePath: 'docs/desktop', - label: 'Desktop App', - position: 'left', - items: [ - { - to: 'docs/desktop/introduction', - label: 'Introduction' - }, - { - to: 'docs/desktop/install', - label: 'Install' - }, - { - to: 'docs/desktop/configuration', - label: 'Configuration' - }, - { - to: 'docs/desktop/access-content', - label: 'Access Content' - }, - { - to: 'docs/desktop/postage-stamps', - label: 'Postage Stamps' - }, - { - to: 'docs/desktop/upload-content', - label: 'Upload Content' - }, - { - to: 'docs/desktop/backup-restore', - label: 'Backup and Restore' - }, - { - to: 'docs/desktop/publish-a-website', - label: 'Publish a Static Website' - }, - { - to: 'docs/desktop/start-a-blog', - label: 'Start a Blog' - } - ] - }, { type: 'dropdown', activeBasePath: 'docs/bee', @@ -232,19 +188,50 @@ module.exports = { }, ] }, - // { - // to: 'docs/bee/installation/quick-start', - // activeBasePath: 'docs', - // label: 'Installation', - // position: 'left', - // }, - // { - // to: 'docs/getting-started/start-your-node', - // activeBasePath: 'docs', - // label: 'Start Your Node', - // position: 'left', - // }, - // {to: 'blog', label: 'Blog', position: 'left'}, + { + type: 'dropdown', + activeBasePath: 'docs/desktop', + label: 'Desktop App', + position: 'left', + items: [ + { + to: 'docs/desktop/introduction', + label: 'Introduction' + }, + { + to: 'docs/desktop/install', + label: 'Install' + }, + { + to: 'docs/desktop/configuration', + label: 'Configuration' + }, + { + to: 'docs/desktop/access-content', + label: 'Access Content' + }, + { + to: 'docs/desktop/postage-stamps', + label: 'Postage Stamps' + }, + { + to: 'docs/desktop/upload-content', + label: 'Upload Content' + }, + { + to: 'docs/desktop/backup-restore', + label: 'Backup and Restore' + }, + { + to: 'docs/desktop/publish-a-website', + label: 'Publish a Static Website' + }, + { + to: 'docs/desktop/start-a-blog', + label: 'Start a Blog' + } + ] + }, { type: 'dropdown', activeBasePath: 'docs/references', From 7e92137eec3f2e4d322cff2fe1ab279dbbcff871 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Tue, 29 Oct 2024 17:19:48 +0700 Subject: [PATCH 18/25] add missing FDS link to dropdown menu --- docusaurus.config.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docusaurus.config.js b/docusaurus.config.js index 363f871c8..e6f8e0292 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -254,6 +254,10 @@ module.exports = { to: 'docs/references/glossary', label: 'Glossary' }, + { + to: 'docs/references/fair-data-society', + label: 'Fair Data Society' + }, { to: 'docs/references/faq', label: 'FAQ' From 4395cb07b17d224d606d56f37a2f93c9dca858bb Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Thu, 7 Nov 2024 15:09:41 +0700 Subject: [PATCH 19/25] add figures, revise neighborhoods description, move PSS and ACT to top level sidebar --- docs/concepts/DISC/DISC.md | 11 ++++ docs/concepts/DISC/kademlia.md | 10 ++++ docs/concepts/DISC/neighborhoods.md | 24 ++++++--- .../{protocols => }/access-control.md | 0 docs/concepts/{protocols => }/pss.md | 0 .../{what-is-swarm.md => what-is-swarm.mdx} | 47 ++++++++---------- .../access-the-swarm/upload-and-download.md | 2 +- .../tools-and-features/access-control.md | 2 +- docusaurus.config.js | 9 ++-- sidebars.js | 12 ++--- src/css/custom.css | 12 +++++ static/img/bos_fig_1_1.jpg | Bin 0 -> 127262 bytes static/img/bos_fig_2_3.jpg | Bin 0 -> 166273 bytes static/img/bos_fig_2_7.jpg | Bin 0 -> 18726 bytes 14 files changed, 81 insertions(+), 48 deletions(-) rename docs/concepts/{protocols => }/access-control.md (100%) rename docs/concepts/{protocols => }/pss.md (100%) rename docs/concepts/{what-is-swarm.md => what-is-swarm.mdx} (56%) create mode 100644 static/img/bos_fig_1_1.jpg create mode 100644 static/img/bos_fig_2_3.jpg create mode 100644 static/img/bos_fig_2_7.jpg diff --git a/docs/concepts/DISC/DISC.md b/docs/concepts/DISC/DISC.md index 8deea8da8..ddf81e10b 100644 --- a/docs/concepts/DISC/DISC.md +++ b/docs/concepts/DISC/DISC.md @@ -3,6 +3,9 @@ title: DISC id: disc --- +import bos_fig_2_7 from '/static/img/bos_fig_2_7.jpg'; + + DISC (Distributed Immutable Storage of Chunks) is a storage solution developed by Swarm based on a modified implementation of a [Kademlia DHT](/docs/concepts/DISC/kademlia) which has been specialized for data storage. Swarm's implementation of a DHT differs significantly in that it stores the content in the DHT directly, rather than just storing a list of seeders who are able to serve the content. This approach allows for much faster and more efficient retrieval of data. ### Kademlia Topology and Routing @@ -25,6 +28,14 @@ In the DISC model, chunks are the canonical unit of data. When a file is uploade Content-addressed chunks are chunks whose address is based on the hash digest of their data. Using a hash as the chunk address makes it possible to verify the integrity of chunk data. Swarm uses the BMT hash function based on a binary Merkle tree over small segments of the chunk data. A content-addressed chunk has an at most 4KB payload, and its address is calculated as the hash of the span (chunk metadata) and the Binary Merkle Tree hash of the payload. + + + For single-owner chunks on the other hand, the address is calculated as the hash of a unique id and the owner's overlay address. The content consists of an arbitrary data payload along with required headers. Unlike a content-addressed chunk, the contents of a single-owner chunk may be updated while the address remains unchanged. Single owner chunks form the basis for feeds, which are data structures that allow for mutable content with a static address. ### Push-Sync, Pull-Sync, and Retrieval Protocols diff --git a/docs/concepts/DISC/kademlia.md b/docs/concepts/DISC/kademlia.md index 00dc98869..04b26eb9a 100644 --- a/docs/concepts/DISC/kademlia.md +++ b/docs/concepts/DISC/kademlia.md @@ -3,6 +3,8 @@ title: Kademlia id: kademlia --- +import bos_fig_2_3 from '/static/img/bos_fig_2_3.jpg'; + Kademlia is a distributed hash table (DHT) algorithm used in peer-to-peer networks to efficiently store and retrieve data without relying on centralized servers. It organizes nodes into an overlay network that ensures efficient routing using a binary tree structure. @@ -50,6 +52,14 @@ In contrast, Swarm makes use of forwarding Kademlia. Here each node forwards the The main advantage of forwarding Kademlia is that it maintains the anonymity of the node which initiated the request. + + + ### Neighborhood Based Storage Incentives Swarm introduces a storage incentives layer on top of its Kademlia implementation in order to reward nodes for continuing to provide resources to the network. Neighborhoods play a key role in the storage incentives mechanism. Storage incentives take the role of a "game" in which nodes play to win a reward for storing the correct data. Each round in the game, one neighborhood is chosen to play, and all nodes within the same neighborhood participate as a group. The nodes each compare the data they are storing with each other to make sure they are all storing the data they are responsible for, and one node is chosen to win from among the group. You can read more about how storage incentives work in the dedicated page for storage incentives. \ No newline at end of file diff --git a/docs/concepts/DISC/neighborhoods.md b/docs/concepts/DISC/neighborhoods.md index b25eff709..a9c33a921 100644 --- a/docs/concepts/DISC/neighborhoods.md +++ b/docs/concepts/DISC/neighborhoods.md @@ -3,22 +3,30 @@ title: Neighborhoods id: neighborhoods --- -In Swarm, a neighborhood refers to an area of responsibility within the network, where nodes in proximity to one another share the task of storing and maintaining data chunks. It is defined by the [proximity order (PO)](/docs/references/glossary#proximity-order-po) of nodes' addresses. Nodes within a neighborhood replicate data chunks to ensure that if one node goes offline, other nodes in the neighborhood can still retrieve and serve the content. - +In Swarm, a neighborhood refers to an area of responsibility within the network, where nodes in proximity to one another share the task of storing and maintaining data chunks. Nodes within a neighborhood replicate chunks to ensure that if one node goes offline, other nodes in the neighborhood can still retrieve and serve the content. :::info -To see neighborhood populations and the current storage depth / storage radius navigate to the ["Neighborhoods" page of Swarmscan.io](https://swarmscan.io/neighborhoods). +To see current neighborhood populations and the current storage depth / storage radius navigate to the ["Neighborhoods" page of Swarmscan.io](https://swarmscan.io/neighborhoods). ::: :::info The terms "depth" and "radius" are often used interchangeably when discussing neighborhoods. Both refer to number of shared leading bits of node and chunk addresses used to determine the nodes and chunks which fall into which neighborhoods. ::: -## Neighborhood Formation +## Key Concepts + +### Proximity Order (PO) +The PO is a measure how close a node is to a particular chunk of data or other node. It is defined as the number of shared leading bits between two addresses. Nodes with equal or greater proximity order value relative to a chunk have equal responsibility for storing and maintaining it. Proximity order plays a role in how neighborhoods are defined, as a node’s neighborhood extends up to its storage depth, covering all nodes within that proximity​. + +### Storage Depth + +Storage depth represents the effective reach or area up measured in proximity order to which a node must synchronize and store chunks within its neighborhood. It is the proximity order of chunks for which a node is responsible for storing. + +### Neighborhood -A Swarm neighborhood is determined by the proximity order (PO) of node addresses, which is calculated based on the number of leading bits shared between the addresses of nodes in the network. Nodes in the same neighborhood share the same prefix of their addresses, and the neighborhood expands or contracts depending on the availability of nearby nodes. As a result, each node is responsible for interacting with other nodes within its neighborhood to store and replicate data chunks, ensuring data availability and redundancy. The neighborhood depth dynamically adjusts as peers join or leave the network, maintaining a healthy distribution of storage responsibility across nodes. +A neighborhood is a set of nodes in close proximity to each other based on their Kademlia proximity order (PO). Each node in the network has a neighborhood determined by its storage depth, which defines the radius or boundary of responsibility for storing chunks. Each node in a neighborhood is responsible for interacting with other nodes within its neighborhood to store and replicate data chunks, ensuring data availability and redundancy. -### Example neighborhood +## Example neighborhood Let's take a closer look at an example. Below is a neighborhood of six nodes at depth 10. Each node is identified by its Swarm address, which is a 256 bit hexadecimal number derived from the node's Gnosis Chain address, the Swarm network id, and a random nonce. @@ -40,9 +48,9 @@ Since we are only concerned with the leading binary bits close to the neighborho | da7b | 1101101001111011| | da7f | 1101101001111111| -### Chunk neighborhood Assignment +### Area of Responsibility -Chunks are assigned to neighborhoods based on their addresses, which are in the same 256 bit format as node addresses. Here are two example chunks which fall within our example neighborhood: +Storer nodes are responsible for storing chunks with addresses whose leading btis match their own up to the storage depth. Here are two example chunks which fall within our example neighborhood: > Chunk A address: `da49a42926015cd1e2bc552147c567b1ca13e8d4302c9e6026e79a24de328b65` > Chunk B address: `da696a3dfb0f7f952872eb33e0e2a1435c61f111ff361e64203b5348cc06dc8a` diff --git a/docs/concepts/protocols/access-control.md b/docs/concepts/access-control.md similarity index 100% rename from docs/concepts/protocols/access-control.md rename to docs/concepts/access-control.md diff --git a/docs/concepts/protocols/pss.md b/docs/concepts/pss.md similarity index 100% rename from docs/concepts/protocols/pss.md rename to docs/concepts/pss.md diff --git a/docs/concepts/what-is-swarm.md b/docs/concepts/what-is-swarm.mdx similarity index 56% rename from docs/concepts/what-is-swarm.md rename to docs/concepts/what-is-swarm.mdx index 3646830f5..7c026785c 100644 --- a/docs/concepts/what-is-swarm.md +++ b/docs/concepts/what-is-swarm.mdx @@ -3,44 +3,37 @@ title: What is Swarm? id: what-is-swarm --- +import bos_fig_1_1 from '/static/img/bos_fig_1_1.jpg'; + # What is Swarm? -The complete vision of Swarm is described in detail in [The Book of Swarm](https://www.ethswarm.org/the-book-of-swarm-2.pdf) written by Swarm founder Viktor Tron, with further high level details described in the [whitepaper](https://papers.ethswarm.org/p/whitepaper/). More in depth low level implementation details can be found in the [Swarm Specification paper](https://papers.ethswarm.org/p/swarm-specification/). To stay up to date with all the latest research and technical papers from Swarm, make sure to bookmark the [Papers section](https://papers.ethswarm.org/) of the Ethswarm homepage. +The complete vision of Swarm is described in detail in [The Book of Swarm](https://papers.ethswarm.org/p/book-of-swarm/) written by Swarm founder Viktor Tron, with further high level details described in the [whitepaper](https://papers.ethswarm.org/p/whitepaper/). More in depth low level implementation details can be found in the [Swarm Specification paper](https://papers.ethswarm.org/p/swarm-specification/). The latest research and technical papers from Swarm can be found on the ["Papers" section](https://papers.ethswarm.org/) of the Ethswarm homepage. Swarm is peer-to-peer network of nodes which work together to provide decentralised storage and communication infrastructure. Swarm can be divided into four main parts: -1. Underlay Network - A peer-to-peer network protocol to serve as underlay transport. -2. Overlay Network - An overlay network with protocols powering a distributed immutable storage of chunks (fixed size data blocks). +1. Underlay Network - A peer-to-peer network protocol to serve as underlay transport. Swarm's underlay network is built with [libp2p](https://libp2p.io/). +2. Overlay Network - An overlay network with protocols powering a distributed immutable storage of chunks (fixed size data blocks). 3. Data Access Layer - A component providing high-level data access and defining APIs for base-layer features. 4. Application Layer - An application layer defining standards and outlining best practices for more elaborate use cases. + + + + +Of these four main parts, parts 2 and 3 form the core of Swarm. ### 1. Underlay Network The first part of Swarm is a peer-to-peer network protocol that serves as the underlay transport. The underlay transport layer is responsible for establishing connections between nodes in the network and routing data between them. It provides a low-level communication channel that enables nodes to communicate with each other directly, without relying on any centralised infrastructure. -Swarm is designed to be agnostic of the particular underlay transport used, as long as it satisfies certain requirements. - -1. Addressing – Nodes are identified by their underlay address. -2. Dialling – Nodes can initiate a direct connection to a peer by dialing them on -their underlay address. -3. Listening – Nodes can listen to other peers dialing them and can accept incoming -connections. Nodes that do not accept incoming connections are called light -nodes. -4. Live connection – A node connection establishes a channel of communication which -is kept alive until explicit disconnection, so that the existence of a connection -means the remote peer is online and accepting messages. -5. Channel security – The channel provides identity verification and implements -encrypted and authenticated transport resisting man in the middle attacks. -6. Protocol multiplexing – The underlay network service can accommodate several -protocols running on the same connection. -7. Delivery guarantees – Protocol messages have guaranteed delivery, i.e. delivery -failures due to network problems result in direct error response. Order of delivery -of messages within each protocol is guaranteed. -8. Serialisation – The protocol message construction supports arbitrary data structure -serialisation conventions. +Swarm is designed to be agnostic of the particular underlay transport used, as long as it satisfies certain requirements described in The Book of Swarm. As the [libp2p](https://libp2p.io/) library meets all these requirements it has been used to build the Swarm underlay network. @@ -51,17 +44,19 @@ The second part of Swarm is an overlay network with protocols powering the [Dist Swarm's overlay network is built on top of the underlay transport layer and uses [Kademlia](/docs/concepts/DISC/kademlia) overlay routing to enable efficient and scalable communication between nodes. Kademlia is a distributed hash table (DHT) algorithm that allows nodes to locate each other in the network based on their unique identifier or hash. -Swarm's DISC is an implementation of a Kademlia DHT optimized for storage. While the use of DHTs in distributed data storage protocols is common, for many implementations DHTs are used only for indexing of specific file locations. Swarm's DISC distinguishes itself from other implementations by instead breaking files into chunks and storing the chunks themselves directly within a Kademlia DHT. +Swarm's DISC is an implementation of a Kademlia DHT optimized for storage. While the use of DHTs in distributed data storage protocols is common, for many implementations DHTs are used only for indexing file references. Swarm's DISC distinguishes itself from other implementations by instead breaking files into chunks and storing the chunks themselves directly within the DHT. Each chunk has a fixed size of 4kb and is distributed across the network using the DISC model. Each chunk has a unique address taken from the same namespace as the network node addresses that allows it to be located and retrieved by other nodes in the network. -Swarm's distributed immutable storage provides several benefits, including data redundancy, tamper-proofing, and fault tolerance. Because data is stored across multiple nodes in the network, it can be retrieved even if some nodes fail or go offline. +Swarm's distributed immutable storage provides several benefits, including data redundancy, tamper-proofing, and fault tolerance. Because data is stored across multiple nodes in the network, it can be retrieved even if some nodes fail or go offline. + +Built on top of the overlay network is also an [incentives layer](/docs/concepts/incentives/overview) which guarantees that node operators which share their resources with the network are fairly rewarded for their services. ### 3. Data Access Layer The third part of Swarm is a component that provides high-level data access and defines APIs for base-layer features. This layer is responsible for providing an easy-to-use interface for developers to interact with Swarm's underlying storage and communication infrastructure. -Swarm's high-level data access component provides APIs that allow developers to perform various operations on the network, including [uploading and downloading data](/docs/develop/access-the-swarm/upload-and-download) and searching for content. These APIs are designed to be simple and intuitive, making it easy for developers to build decentralised applications on top of Swarm. +Swarm's high-level data access component provides [APIs that allow developers to perform various operations](/api/) on the network, including [uploading and downloading data](/docs/develop/access-the-swarm/upload-and-download) and searching for content. These APIs are designed to be simple and intuitive, making it easy for developers to build decentralised applications on top of Swarm. ### 4. Application Layer diff --git a/docs/develop/access-the-swarm/upload-and-download.md b/docs/develop/access-the-swarm/upload-and-download.md index bd94765b0..b8dd91f1d 100644 --- a/docs/develop/access-the-swarm/upload-and-download.md +++ b/docs/develop/access-the-swarm/upload-and-download.md @@ -11,7 +11,7 @@ When you upload your files to the Swarm, they are split into 4kb _chunks_ and then distributed to nodes in the network that are responsible for storing and serving these parts of your content. To learn more about how Swarm's decentralized storage solution works, -check out the ["Learn" section](/docs/concepts/what-is-swarm). +check out the ["Concepts" section](/docs/concepts/what-is-swarm). In order for you to be able to upload any data to the network, you must first purchase [postage stamps](/docs/concepts/incentives/postage-stamps) diff --git a/docs/develop/tools-and-features/access-control.md b/docs/develop/tools-and-features/access-control.md index d161d6a56..c8e18fe63 100644 --- a/docs/develop/tools-and-features/access-control.md +++ b/docs/develop/tools-and-features/access-control.md @@ -4,7 +4,7 @@ id: act --- :::info -This is guide contains a detailed explanation of how to use the ACT feature, but does not cover its higher level concepts. To better understand how ACT works and why to use it, read [the ACT page in the "Learn" section](/docs/concepts/protocols/access-control). +This is guide contains a detailed explanation of how to use the ACT feature, but does not cover its higher level concepts. To better understand how ACT works and why to use it, read [the ACT page in the "Concepts" section](/docs/concepts/access-control). ::: diff --git a/docusaurus.config.js b/docusaurus.config.js index fd4a5b0a0..6700449e8 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -136,10 +136,13 @@ module.exports = { label: 'Incentives', }, { - to: '/docs/concepts/protocols/pss', - label: 'Protocols', + to: '/docs/concepts/pss', + label: 'PSS', + }, + { + to: '/docs/concepts/access-control', + label: 'Access Control', }, - ] }, diff --git a/sidebars.js b/sidebars.js index 6c5060195..7b7ac3eeb 100644 --- a/sidebars.js +++ b/sidebars.js @@ -22,18 +22,12 @@ module.exports = { 'concepts/incentives/postage-stamps', 'concepts/incentives/bandwidth-incentives', 'concepts/incentives/price-oracle', + ], collapsed: false }, - { - type: 'category', - label: 'Protocols', - items: [ - 'concepts/protocols/pss', - 'concepts/protocols/access-control', - ], - collapsed: false - }, + 'concepts/pss', + 'concepts/access-control', ], diff --git a/src/css/custom.css b/src/css/custom.css index d4255e3e7..cfab3421f 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -300,3 +300,15 @@ body[data-rh] .redocusaurus { display: block; } +.responsive-image { + width: 90%; + display: block; + margin: auto; +} + +/* Apply 50% width for screens larger than 768px (tablet and desktop) */ +@media (min-width: 768px) { + .responsive-image { + width: 60%; + } +} \ No newline at end of file diff --git a/static/img/bos_fig_1_1.jpg b/static/img/bos_fig_1_1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e43e5cdced57fe1f72bf6c2bca032a26342ea6c8 GIT binary patch literal 127262 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Ð4&exIQ+Ie`Ab%XN~-q-+vLvLQ>WMi9c7; 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 literal 0 HcmV?d00001 diff --git a/static/img/bos_fig_2_3.jpg b/static/img/bos_fig_2_3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b048a3d14c810d4836db677b1c2a0278e77aaf4c GIT binary patch literal 166273 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

TDk*jCb(yk*Ux+i`rfD6_X)OwvQh#zrYrKsyx zIqHvGE`ZxgR@AQq5f}!a0HeUg@xrquYSQHh`zAfT?H{Uqxqk@WKS+Aqm7lybPi7( zjy2kt%vbK2EC6Ikq!zHcj?6@hP~0TrEyj)Zax@wA4Ef*ANR9=!LKbgVgoPAj@?#}h z<>A$(F=DB!PDMHX0CP}+C$^9wI@os)kk3=OMuG`#Byty7QN63bwrys3BPnx;y~Opb z7iss_Dq~IRxWIXzUUbqJ;dOexZb?@W{>?90NeO90;}Hfm!No`@wKw+ z9j3PhdA)cc#SxZ#PX5S_RvT}^-A%pF zBk0F6uy{&_M{Xj2o2*q5XweoCd*cXZ2y>(bC>^GMfIz4k1v}#eoWu?$5&d%$B5978 zi+McdFe|36V9Ld2Z{@HW(~3FLmHN15y%8R)ewACvU~*IZUaUY(vY9SfWhTs<2T z&xJ!6wFs?+@q&$pyhptd3(Gi=m1ZiGuo7;Bag=ifC?W;@GbdrdI%lmIa+p={&Jd~z z>?C0!Vzco-063CP_SO&yE4hgW`&SD^sZ(2Y&1|#l`K$2V)i=VO2(+8MA{GR+-8qKbrKCWCn~pUi%%?K z?G4g?*BV%G<8uX3CvZ>IfN``mPrP^`4*AOjaN>;u&idi>z{aq%pc-ty0kD-aPNwnb z3B7b;hQ|hEvMh-wOGK)n7Hs`Nv~wK+s-cyJ<qEy)cAlN`7`YWJc+)Ns%YSQhM@ z8iS=8*64#61`cL^%lqI;v1-dBrE9IZcLtHBW=a1`9dziS#q9rGleF7vRHV}tCM7nW z3@{4+ml_uKVZ=b>LU7;}R%$`4fay-vR_U)S%BnDW{~uc9cRp7U;;eH;jinMPl8n=jc+$meM4{}i`J+IPMti+*V#MDtJ)va77|DWS_?AOo&;%{q9id|1 z<3&-=m6hf5AK6Z=Ocsml>I7e%$I^$N4&poECr(hk>|gfAaKau?MlQMC%nFKt2#uu3 z#5Ck{y%8L=1CdetjM=5&nqd(-x9*W1n%7AVK0dKf5#VbkLn?o+d1UcXVeL^yzcwf9 zc1eTktW$nqieJH6pHV}wUu-Nj@Kd4TDg!hk1A7MC*`3Ct^g@RgxsD&-okEx{+Ne2g z`GbU{$cOLtcdCo7IBj7K*pUJ?p_63NMv0fs|FT=)_Kv!I;wDiisWt;GDkNqk-8 z*i4mF7C0sG`PapSQCy~n4e)8Eop`x4v4=XYU$MSgeWxgSr4)ztsULg3nw=Y8lC(ob z{_oQJ_pg5^-spp=Zxq=KzVL4!%Jb_bX71``_6<0IYU5kpDGQucG=7d*yzNc;uM`QQ z^@z%T?;gSZ#S#`vWc-Bg_BFcg?9tv5;^l3t2h?;STcWSS{8^j@28ICy{VR1HCSZSv z+zO5=P*2?#L@}}U_I#f3u*6f^mhb@W{_3t3bScg9M<_ORma!}voGP; z72-T8J74hg4vVzdtD+ysOE1d{!Ka9;rF;$Y=Q#Zi3=0J*-<6JtH~-$1F==4h__4#n zg}SDykXFU+4#{kQqqLi1@JJ!+VnL*tCl^ZPl4__)E&in|!Ib7>%;NS&VIw#Njbj^U zO!8Lw3;xJFxWM&}S2^Vdt=>_Je4wThL~3!=lUTF{^#5WS<-+#<{C*kLWAj1b6$I>_ z5*}8qu!_eb`9X<9!HgD>k@!VZ;80eGO*RS%0s}9gCi@nvd)O#k`{g9y?KOV{DD6IfrB$!MqP+t70>D zZ!S>GIW$Ghy@F30WP4wMRHPudx3}WEuQrabBgS}ppYF1TM758yg%Un%_w@{sQzF)0 zqBsW@SuGz~IPOq;x84Pa+wJ%{`2%U#qD6Pr25XN?sRN;)Z-?{_PM6_>!k|lqvBCtK zK{i{BDW$)5WVtp2&VOu{a7@W=usA`C$+N_4JV1S==Jl>KQ*bQufUlCfUAc-HOZlnh zOHhk|gVTlTG}eDZUq?qN2X|h`!gB0Xu##(^Q2B3{yj)66Y{+VWFr_Qm4Xum&y|ND6 z?#Y(r02!I$LFIE<9xNNy>HZs3IJawR=|2DmbwCJ-l^=RNQKlTU?c5lPULRpvwdB%N z*nKj0bo17uAREar*dQi+Z>XTYr8dI2i4Vu;^ItGWTJcYNb6n`3PZv~xH58Yg2R$XE zDeU~WSU6{gAt@N0qAiJVTHK;-diLo>mjk0~J!GUQn0z^DOx2wj@ZD#LC@LMOOUV-H zt*9{fX2X?d<@`{4gTm6cnZ}1S9hblj z<|WY0(qpJ;2i>etTp^j?1P5%wTE7X-fwe!A%Fv*!_WXUdml+x4c8sr@$6Nh5Oq0sbe&iA{j50hz_g^rBB8 z3A0o{HOgO53SmdfUu5N-*>bqjc{Rbo{tQ~8p^7KJ6bW;FCKOI$2D~qvtGJWgZzC`x zDet;HCU$_l!$EK~Q=SGd2>FMKZ*?d}(CzY;_F&i^8aBqE;`F+U>2Ef#r5ffk zi(C5>mIss)S@GT&6yJy+cIwcu*9z7UB%{+FlmAZ^EvAtueSqhef$Jubv?Tp<{QqAv zJ95junyb>LE6lFo-+w3(>_=AG^9fU#g&*)RPxeFOQClS2W5hpUc{cbs z)#U4LXuKVIpyzomOGAP)05z*8Z}u@VH|mnx6yae9XW9vnXR=$=myO#^17xc5RV6R9 z`A4!E%u#);lFD+vk4DF*{rEg%^amF&VcZ-Ta**D7`czH<1uTtYPq#ry7J1s|ukP*b z3I2GMQNjluQE;a9bQcx1Z##>zqw57~1kfE)N>NJ{oPng2HwTqa`b^&KeIEk|lpxVb zE95u|mp4!5!ayR=E&5gv<1{lYdeATbsL|i;2XSQQ^1$_zd4zf%A2=y2&uS*Kg`h0Xo;S_K)g98>2keFrK zFL7KmZOL<} z@zii^I_hL3p?O-sh^o+e$r?F|-#mDK9y)6ln^m(I!EzC#5FpGfmC$2!KF!6W;fSO8 zF5U;SBQKg2FJ$e5c4tjhd{HJ8C>%_}J`7sCLm~u%OO4$1WyT5Hr5#^IMWZ6Sq-&lH zQ24$g&pY>QKK4HVW&y5180$K}th;v#zT7-PU%~-LzZh^rVKGZazbwamvSg{ee%Vm6 zEKAq?RZh-#7?`8<*`$Z17$f+(ynxN?O9-MkoQTL|0c&KZ3}=pVHEg-G3&FTT(;Js; zZChvFPiGdcS6@*P{_@kYvoDGeCXfQ>xl1bZ$lko#BFbOy6y3weqh;*7VJC_;A&9nA z>)97)Vf{xw{(a+A2fC0z;lC5Ai%~O+g zp@K^10`cbq5p!%L->Q2~B`#yBls_1_eAGER+h8{#?H6r_%~SzPn|S9ChVu0(Cuzpp zHiV|&!k1C5T?+XoZ(p!5L4KGkK~RP`_`PQi2zIR)pU$wEJA0=h5Q$z+SQ?g|?2T#5 zVBk?D(m5=9zaLn%ZTS26&R~NbJ7wjYN zY&{1--W?^T!)0XI+VqVY12Yp@fs7y$jN+$qus7tO(#+Z!{&Oj^9F*Czo1i%SIElktkv!fBhbtcunc2L(ivl6mS@;7Aw~8?xlyM#yIJKro zU;yi1wC~4Ys#RIGW-`0eDq%Q&)Mfe9(cs8^jrR1QLtU$(82S~rTDQ>MQsvU2xXkpU z?zSI?h@r-=ISmlD(9SgG9j7CFLKoSz&hHHp4_8%UfvS)dROQ#Q!!M$68quNgAdI{X zrHc4kGy|>3i|n~<6c8j4V6?RPeSij}O6tr4EJ}kEBN#Nbs>uUxV$L=}0Ki8TDLYjq z%_%&^obv}QgW3jBLY-Bvc`Y_D(Q`j#|H@42P?<~gZBM<{?e3_ z<8rmpaOJO~GIH<1=Ovo&Ak^OsKA6|#RbGSqHs>x0#k@cYJ2DNj#rQ7+L0(PK zVZtdUy_EB#e8ZK^X_Y^zp2~RxJgzXqgYs{iJYn?0KhZ<8aMA8ZsfvYUNv`xO?pmG5 zv~X3wBA_QS*oMURB_rqzc_D-EDH?-B+kC_$%wPA}R@zbC3t&kwgL+3U5cdptK}ruW zE8Yz**ic6<=jTBhD9*foe(x7T(WD>nI8XKh{V~}j>*=+)M``Di+N@|ZO3sHxcVd8V zPkeFV`EtS>sv9xjx{o4J)H-qJk~P9xaAQWzV)#%#S@SYSl4r$iYx8-r%;F@V+E^yn z0%XkISFlg&Z8-uLjN&m9i{AiFH6wqr1UEbNcbFV+BLyGo8VAxR5p*$nD4(ch{Qhn? zF!^D6)yJM?OY*4If_yL&`T6G=MBG)F-|9_9V>RgGMoqTFK2$rc-(>%u%SQ@&m>X|ypdsK z!&}PaZ4Kk1%AUt(*&}h}q24mc8kJ$@%$i8COenK?zqm*?5U~X_!KALi^FRO84{Q&q z^iAuG0dMepWCPepcM`1EQbFwpFM5w5ytNbML&Tt5!HI#_pj<&lo&Y@^kzWeBq+E^r zPoAqmx&!2#3Pj)!az#x7KmbL3iTqaou?9pT(HZa6PM{`j;10t_l! zAIi4>yVFG@gC{b~Ow!3Rj=_|+^vC^%Rz_~de`Zv22fbfI;6!dwTzpZ^Ph~U+^+$-{ z6y0PW0emoJDyHxEae|`*8VdOXEd5go`TZC4W*8Mn2>t;^uzNtL?A@Y?f+IpZ>d3yv zU27Z!!`g;!00GxX1PtDK-7r%_la}Kq%3{N7YB9LQpc<)|7uq$No1_~$pkgq3mAvg@K2OD_=O$X zWK{iXuLy>l-|f7#QkU}&Am=F=QZ5bN;oT_gNJC|%QK<;WIzT#_)|tan?D=uLcO>lU zp!B^rAP(1sHW!j7cM|vkrm^rBZHTzwsno38`*JT?JVp2!7>PIzW2wk$y@5hMvQtWh zT^k%GgiqaV9P4iwPtbw^#Ps*S%Yee@N12^%9+Zt4RP=*3Ol^(8dz0)TzI*Oyu6GcK zc&c*zc=n;F8K(T5wmL+z|5QDS{I)u1K3?FV|NMszb_; z+Y7CR92GowIK|{5NG^xVudL!5Q4y26!^>FSjpYd^Csj!7A2eJ)s3U*uZi}d_oe<0| z!%vGUE5Xg;>)jW-;nC~N>j4T1um9cttl0xI^ctzc9G7dbENyJL&K7j>#pgDs;Q`-j ziA51WAo&l##W|Bw(y-{!Jf`ir;!`yWQ_Y{``C5~)7brGZ-_7uu0g|S0#IKVzh&ho~ zkU@uepcJYwD15>4R1lxqzWMYT;4e#B!>ZoF3aJBRYqUXU!?ZTFM#$B7G&)We9{ruB z9prlXi_MUm|0Bz9Ks1ytluY=o`n78I9I_GkQZ`(_5)^n&J|8nnnT6vYVyw;TFJP%p;ZjIBw8&=xlY9+*SkTsz7VH!$xa~f0UzOg(uJRu?*$VgG-V8eNZ2n_4 zAn=@jq(%#2h*qR}hy%|!$v<7Hr3giCjV@Jj@D1IY@%d!?MY}${=KW=3tTMw;iI%Rh zuC}Muj#(FLrk8iek+AE3f5l|Ql+O)vLiifBa-_q5Dw@S*V_XL&mGXsZ7HQbQhTS>=bunun}gd%?`0s-7Cwn%QPb> z)Q6l~P0#`@^;CDkm-^*Z;F+d9{ocKWfMD88?z~FejjNh9?MAUcftIQcs@qE9r1} z-yEr~YvjShNAQ|x(k4xIKfkssS?s}X^2yL(js%Nop9_W{$SinlCHR;yIPKmf)Lwq< zUut$a|1CF58lSeo{|l>ol?BfIm>;YGg)3Q>?A|w2Otk~GEhI%6{Ku|8)Qg-si#}?- zkGh0>NM*49Ik#6yW$gO-S~C(2^{s-{H`mMhN6-qX$2oFw`Jc;;;iEDaoKmfxm6WezJLO0|-;r$`b9_7@D) zqGxI~nlL?T;!M7N_*h^Sy0m%_ETRh5QY{z;#Q zPPl?znR@GQ5jSI9aFBohs=JC@^2XQaYFP*2PQ&ru=@BVpER~sy%k7K#H0vt2+5 z|L~wg)8z&h?@US={lgzTS73+ z8_mn1l70ykaIW3KJ^sTeBi#QxM1g4mwap(LNu{jeV3v1lu*QYC=Ai__<1TF0T+&hNWK>OZ8qsg$s35?SGYCy7u(0V zkYYE=Lyp_sDr21W>~hTJyiziFv!#b;GXNA!5u*e*Rvm*k-dB)kX{p2i@q2cP*FoCY zuv1%6*&oHRQyotWCV~{-w>MOr&F&ImG3?%_{GVyyYFNM>k}C2kn!GELymM9adNdhq zM!^J;HE1wJ^w#WLbp+N_2DZl&B7Ul^*Sp>I;1jBRJ8cDC*IGAaHZlH9KB|v$o_tHd z(#ZWiUd>s>N7DVS$et(sTIk(KZ6z1nm917N0q(FW)bZK3|* zO)iYo(siPvMYk%(KVr8QdA?coROv^GX_*fyTb#}lEdLuo;nS&i zl(c%U2`w4Q;jx0>4oU^^4=h9OvXl=&NC#9ZE~J%+@cbmu&sp-0g< z#*v($m5^K%W|i^-bk4@c5R&LfnqELv+sv!dxA%y??xmUVNwKw&SqmUrpgw}^(Ac%` zr9B(;DVSmvlFfGNd{7ZO@VOe${@Mw)k#H=*OF6)Qiz3)lpy zibRGUzkC*|8IP~%XX!?w?{&D2&PENsZy4Bv@yf`w)lt~BKMZ(4(lisTj5RtG2KjZk zH*q{|{A&8H1b~KzTEgod^G5^7nd=fdTg`rUssGe&J+ucgj<%E|(JKo_voWf-G1}OF zmQ%ifk&>M|BEv9x1HM;Pd8Ap_VlDMEV5*9wAvgAX`3lB`DF#F#y5M2R1=nhzcYBD6 zFm=At$h#Va95i*x};H>{$6xgRr8nQ)Aj zwO(3;W@?XcZ}Zpw-G}$6=u-9TBr139-Myv=R<}Bjy--X8dCzB>f-OcOT>lQt@}skp zfTR*k4}pt4560Mkoatkh>bUM}tYL^3UMUHAO%iF%s2EheJ7=H_0&+3Ku5J3Y9uj0X zqhM11voAIS8I0VhYow$l$MTcieq^_Qr6(3NuQCT&fTwm>g-A<9o?}{j0OdtHLE~0l z+QHF^nFHQ>X=vF$%qgRF%05U4+{_^t+fAlTqWae&!CzBT{qk^$erhJpFGshCa12sk zEAmwo>K+yy*Ylokv%LVrjG8-*KH?A8KwS(-Dn`Vt;7czyUolv~+MRni+y4Cuhoum< z57_wt(a!=CIJ1uB^B31XCQxg!dfdwmoI((|AOT20p#^QwV5cntGG8L<3Xco&Y9r5+ zsgD6!9JMtpIX`t!5pZJ<=)dYkcJ*s~F5PmbnE{jF&w>d+v+U9%oBdOv>_E4}U3osd ztMLkpps*gWNZX^@iT%It?rkLGBlN`+JPWfR*xdB3lv;nQQ`_ZZR4!bL%xr;-n1nE~ z*jKa9Aii$P_t2wZV5$dKHHT@lPnKW@(P0%zl*&6njS^@3D{jZqdpC_``9nYah#fge z_RM3;JXd}V5{(%*@X3ggrQb_f9~03S$jsRNMkzN@1zXV@($QKtzl6bciELS6a!Gp4 zXMx_r+={7SPD7TK-VvmE6>6Qs$al0te_r~rTU*39pJ5p`I%`#!q8`j6R$7#8LQmwTKat zl4eG7K>2eODoNm9wv-d(rRmcW4tK|kOhq0CQ;_^k6tCCSzT<+3_owO)Ik!)1;{gTG zLFOs!iZ6HYz&I<}6S|qPA;UFkt>y&&`Q^u1`b^`<=6!f*AF%hM_8s@(54+VuG0gZ0 zxA(1PM?{k^QbHjYv~Z|`Yr(t%r@IdNq+a$k>H&glAuTAX28q+1Kr?(IaS|#pvd=6* zL@jlGred1t@1EJA)CY&nSdZxl#Z#rjhy7v2j&+;;&_p(P5XH=Cl7SjW4>vc&OWeU| zuE!5hJLxjb5oa8(tv-f3s%J5zjCZ?U{i|DuLnxI%Fk-P#n2lS%t~HB@VKG85TYGC) zjeZwZTmjutD~lMQf&q#x_FO8;z#6`_ad*JbsZ7iM&-LAx@7QqVi`z05aRe{5?J;04 z@#oxXqO>w_9K*aBVONDWXGP%((zl!u1DQVxlZH|x zqnBNsKa2QSCM7g&7prEum_bdZ!VVloS+H}-uqWer^r?jM1-A!9vLe4)Cov<%y*@sE z>F`vMX+DG{ZIHFaLDm8VdsB9u)j9YZ&6L^Ha6o+L*ka!|y)eUIsPxIr{z`;l$>QOk zAt5_gxAPFzis?;}JSOYq`pKte|C2nIrr(68*N)KY=u*nQI_i{WkaU=aw;BVQ%lUV)mgqigeJtRC9Q$^TgDL zW=K`*dNwFv#NvNw(0$Oz7xJv~cvu)WPy!s8{|BJWBY&^*zlnPD2j~J03M0;lcS56d z^WwPTz`jM%*;T>O#pD0V7hT1q+$cP9r#PSaJKdxeKV`)Fnz7h^xd&>rz?WlH*QZD0 znWq6YCh+UK7cK&AKoutOKyt8P6lJa6aJ&5QR}2>kPu#*IFmsGCG$rUXZpMp9u(Am@ zi}-%DbjpKGXl~f8=KQru3-J~sp9!Sl<8c%x{TKI6TUF?U@sFT98;>(7S%S9R%^Z5G zUv^_i75q|w414sk!xapdl=hp)zgu@+K3$lTwb}S4$xuEY8P*~0zNk^WdA)y^HDate zCiW0wprw*=Hq9xr!B_Mh8)j?jIvxLp`VV8P6p8gAkewhtv+Bnbfx2tgwjSb`TxBsW4 zhwWqpCJ-h>Z~0hO5-&atexNn#JKZ;&(?H$?T($zL+8)TcoXFpv*m^%2Ek|tnu!<9a zn^BU}j8PHU5a95{&0Z5XL%e=*FPLl|+jaJ-$+g?DAwt||tReLgml?Bcm71=Oi;=JT z)MSmNmGELYE*4)D;S9aEfz~$*e!q@}T_8W@4DVHSmr+O3r5Oa}iLfZeQJF>`Q4mO^ z@=PUa$%65bl`87=r~*H4)|SRET=wCpQ_TJWP(+Bf?8$N*kI+j;-#Qh3*NJ(&HP8N4 z?q>r9yw|6i%dA@jyKaCnet^x4k(fvDw%8@`MTNM#hf)yoN40^@p4au^ifOM6dw(Nz zqnyQK-6SLRsHw%s((-BlJS$tkX~g7L0-kaxOT>&4i0jr+X5}Wp3atzmG1(@N6&*~a zYm7QP&p4yAy)hvo_LM~&nNGBeC6Z=~)=CyxPC#P3WmcyFs`Z!z0*Kwz2RzhR4RvtSE|Q* z*!h2UsXSKEM--}_XC@UIK%x;O)2?LaC*U3?!3gB0{&u}U$>7zXbFMHvVGvR&f{DrW z!WD`b-@XLSm2g*ceMW2-ma!nq3dnX`AiGxhYUr-MQ@0D~v>I;wskUd@<@xIEWtKMc8-r0@NN{jVFkC#F+=&>Z6t;Z>KfWPblM*7jo`W2jb` zZ`+&SA^RI`J;r)T+aGDP#!eE^XOKwOiaYw!<@Z{Dj48BE<0k96I<1C7Y^pQsqLhBs zr|LgTx{{*oN)W0jGshnCskYcB81F@^FeQBenRV-c=7tr>i;$bY#@xV({ew)Xi}OyIA;1 znVFX)FWPTRm=$ZM6y?t#CS+yz^g)mqtrkvDaL!lv(>Jt-o#e(|9J@4|opkd-N5S&& zezJLQUNN^NXfFPPcEkCv1}CTTN3-hP{onF>nwy8;KgZ}rHG7d2cQRv>Igu7yf=~D> ziY@5V%${onl9Cz(S`b`JWV_4z!9Y%1bj?{{OtvLv-qci-jd`Z znB-BD%A_l&x7&#Tvng<8@2cUolMUHKrP6C6)0>Q_}P zHM-E}%q$L$&lEN5T;Z&kD6&7}!(6ryc@bhusCPAtCMa2=Z-#~T{1(ZUZhz^tg<4T` zG)T~8?mUP`gQRDS_j1m#|2l+O9?&>MeaL0v@Jn-YifG|#DV~!3k*fzM|5RSRTocMX07pTr$3D)Jq0_ElNy9gPoh+>~Px0)&yL7mqvwiZ7w{GG9= z!2|2(ddU~~yPQN`={A*aL|4*ZmgLeg-@@#D61AqQ9O!8Buoz5x2|aLWuS}cTma}k6 zV{5s^`kbuiAQK!sGefDZoHJU4nI7tUMzJuOe~h3T3!yx`^Xsvg3vH^Y5Fug2Z*{ zZHd@j@H#}|ZOQi%=SLiaa?8?TD-@L=#D)aznZgIRgvUw=<=O0#RFCBMfo^*p B5 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

+ {content} +
+ ); +} From 8136625cd567114d7eb5b9315a61c0de3cf1fbac Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Thu, 7 Nov 2024 16:25:45 +0700 Subject: [PATCH 21/25] Add new title for awesome swarm page --- docs/references/awesome-list.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references/awesome-list.mdx b/docs/references/awesome-list.mdx index 3239b4461..83f25739d 100644 --- a/docs/references/awesome-list.mdx +++ b/docs/references/awesome-list.mdx @@ -1,5 +1,5 @@ --- -title: Awesome List +title: Awesome Swam id: awesome-list --- From 2a89a625645d68d4d243452a73e255b59ac3f617 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Thu, 7 Nov 2024 16:29:07 +0700 Subject: [PATCH 22/25] add link to contribute for awesome swarm --- docs/references/awesome-list.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/references/awesome-list.mdx b/docs/references/awesome-list.mdx index 83f25739d..ec456e87e 100644 --- a/docs/references/awesome-list.mdx +++ b/docs/references/awesome-list.mdx @@ -5,4 +5,6 @@ id: awesome-list import AwesomeList from '@site/src/components/AwesomeList'; +*Contribute to the Awesome Swarm list on [GitHub](https://github.com/ethersphere/awesome-swarm)* + From ecf36917820b69f4e08108f0eb87660e68301ecd Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Thu, 7 Nov 2024 16:33:02 +0700 Subject: [PATCH 23/25] add loading indicator and error message for awesome swarm component --- src/components/AwesomeList.js | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/components/AwesomeList.js b/src/components/AwesomeList.js index 76a1adabc..cc626deb5 100644 --- a/src/components/AwesomeList.js +++ b/src/components/AwesomeList.js @@ -3,13 +3,36 @@ import Markdown from 'react-markdown'; const AwesomeList = () => { const [content, setContent] = useState(''); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); useEffect(() => { fetch('https://raw.githubusercontent.com/ethersphere/awesome-swarm/refs/heads/master/README.md') - .then((res) => res.text()) - .then((text) => setContent(text)); + .then((res) => { + if (!res.ok) { + throw new Error('Network response was not ok'); + } + return res.text(); + }) + .then((text) => { + setContent(text); + setLoading(false); + }) + .catch((err) => { + console.error('Error fetching Awesome Swarm list:', err); + setError(true); + setLoading(false); + }); }, []); + if (loading) { + return

Loading Awesome Swarm list...

; + } + + if (error) { + return

Failed to retrieve Awesome Swarm list contents

; + } + return {content}; }; From d193c3d6984d09388da94b3cf66251ee27d69a1f Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Thu, 7 Nov 2024 16:39:42 +0700 Subject: [PATCH 24/25] add period --- docs/references/awesome-list.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references/awesome-list.mdx b/docs/references/awesome-list.mdx index ec456e87e..788f74151 100644 --- a/docs/references/awesome-list.mdx +++ b/docs/references/awesome-list.mdx @@ -5,6 +5,6 @@ id: awesome-list import AwesomeList from '@site/src/components/AwesomeList'; -*Contribute to the Awesome Swarm list on [GitHub](https://github.com/ethersphere/awesome-swarm)* +*Contribute to the Awesome Swarm list on [GitHub](https://github.com/ethersphere/awesome-swarm).* From 38e297ae83570d13d06f6a353b284107f328f4c2 Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Thu, 7 Nov 2024 19:33:02 +0700 Subject: [PATCH 25/25] add glossary terms and neighborhood terms --- docs/concepts/DISC/neighborhoods.md | 19 ++++++++++++++++--- docs/references/glossary.md | 8 ++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/concepts/DISC/neighborhoods.md b/docs/concepts/DISC/neighborhoods.md index a9c33a921..53250df98 100644 --- a/docs/concepts/DISC/neighborhoods.md +++ b/docs/concepts/DISC/neighborhoods.md @@ -16,15 +16,28 @@ The terms "depth" and "radius" are often used interchangeably when discussing ne ## Key Concepts ### Proximity Order (PO) -The PO is a measure how close a node is to a particular chunk of data or other node. It is defined as the number of shared leading bits between two addresses. Nodes with equal or greater proximity order value relative to a chunk have equal responsibility for storing and maintaining it. Proximity order plays a role in how neighborhoods are defined, as a node’s neighborhood extends up to its storage depth, covering all nodes within that proximity​. +The PO is a measure how close a node is to a particular chunk of data or other node. It is defined as the number of shared leading bits between two addresses. Proximity order plays a role in how neighborhoods are defined, as a node’s neighborhood extends up to its storage depth, covering all nodes within that proximity​. + +### Reserve Depth + +The reserve depth is the shallowest PO at which neighborhoods are able to store all of the chunks which have been paid for through [postage stamp batch](/docs/concepts/incentives/overview#postage-stamps) purchases. ### Storage Depth -Storage depth represents the effective reach or area up measured in proximity order to which a node must synchronize and store chunks within its neighborhood. It is the proximity order of chunks for which a node is responsible for storing. +Storage depth is the shallowest PO at which neighborhoods are able to store all the chunks which have been *uploaded*. If 100% of all all chunks which have been paid for have been stamped and uploaded to the network, then storage depth will equal reserve depth. However, it is common that stamp batches are not always fully utilized, meaning that it is possible for the storage depth to be shallower than the reserve depth. + +Storage depth is the proximity order of chunks for which a node must synchronize and store chunks, and it is determined by nodes' reserve sizes in combination with the amount of chunks actually uploaded. + +### Neighborhood Depth + +Neigborhood depth for a node is the highest (deepest) PO *`d`* where the node has at least 3 peers which share the same *`d`* number of leading binary prefix bits in their addresses. + ### Neighborhood -A neighborhood is a set of nodes in close proximity to each other based on their Kademlia proximity order (PO). Each node in the network has a neighborhood determined by its storage depth, which defines the radius or boundary of responsibility for storing chunks. Each node in a neighborhood is responsible for interacting with other nodes within its neighborhood to store and replicate data chunks, ensuring data availability and redundancy. +A neighborhood is a set of nodes in close proximity to each other based on their proximity order (PO). Each node in the neighborhood defined by storage depth is responsible for interacting with other nodes within that neighborhood to store and replicate data chunks, ensuring data availability and redundancy. + + ## Example neighborhood diff --git a/docs/references/glossary.md b/docs/references/glossary.md index 21f8a39ab..b09a51f10 100644 --- a/docs/references/glossary.md +++ b/docs/references/glossary.md @@ -38,6 +38,14 @@ Overlay addresses are a Keccak256 hash of a node’s Gnosis Chain address and th [Neighborhoods](/docs/concepts/DISC/neighborhoods) are nodes which are grouped together based on their overlay addresses and are responsible for storing the same chunks of data. The chunks which each neighborhood are responsible for storing are defined by the proximity order of the nodes and the chunks. +## Sister Neighborhood + +A sister neighborhood is composed of nodes in the other half of an old neighborhood after a neighborhood split. + +## Parent Neighborhood + +A parent neighborhood is the neighborhood one proximity order shallower than the two sister neighborhoods it contains. For example, given a neighborhood at depth 5 of 01011 and its sister neighborhood of 01010, their parent neighborhood is 0101 at depth 4. + ## Underlay An underlay network is the low level network on which an overlay network is built. It allows nodes to find each other, communicate, and transfer data. Swarm's underlay network is a p2p network built with [libp2p](https://libp2p.io/). Nodes are assigned underlay addresses which in contrast to their overlay addresses are not permanent and may change over time.

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 z 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 Udzt=u%zu3U*Z<);0~2Te7aH|G+yDRo literal 0 HcmV?d00001 From 64872f87435596eebde6df12233c87850a58147a Mon Sep 17 00:00:00 2001 From: NoahMaizels Date: Thu, 7 Nov 2024 16:19:05 +0700 Subject: [PATCH 20/25] add global variables for commonly updated values like incentives contracts, component for dynamically rendering awesome swarm from github, revised descriptions for neighborhood terms --- docs/concepts/DISC/{DISC.md => DISC.mdx} | 0 .../DISC/{kademlia.md => kademlia.mdx} | 1 - .../incentives/{overview.md => overview.mdx} | 8 +++-- docs/references/awesome-list.mdx | 8 +++++ docs/references/community.md | 8 ++++- ...smart-contracts.md => smart-contracts.mdx} | 13 +++---- docusaurus.config.js | 5 +++ package-lock.json | 35 +++++++++++++++++++ package.json | 1 + sidebars.js | 3 +- src/components/AwesomeList.js | 16 +++++++++ src/config/globalVariables.js | 7 ++++ src/pages/awesome-list.js | 18 ++++++++++ 13 files changed, 112 insertions(+), 11 deletions(-) rename docs/concepts/DISC/{DISC.md => DISC.mdx} (100%) rename docs/concepts/DISC/{kademlia.md => kademlia.mdx} (99%) rename docs/concepts/incentives/{overview.md => overview.mdx} (78%) create mode 100644 docs/references/awesome-list.mdx rename docs/references/{smart-contracts.md => smart-contracts.mdx} (55%) create mode 100644 src/components/AwesomeList.js create mode 100644 src/config/globalVariables.js create mode 100644 src/pages/awesome-list.js diff --git a/docs/concepts/DISC/DISC.md b/docs/concepts/DISC/DISC.mdx similarity index 100% rename from docs/concepts/DISC/DISC.md rename to docs/concepts/DISC/DISC.mdx diff --git a/docs/concepts/DISC/kademlia.md b/docs/concepts/DISC/kademlia.mdx similarity index 99% rename from docs/concepts/DISC/kademlia.md rename to docs/concepts/DISC/kademlia.mdx index 04b26eb9a..602555e47 100644 --- a/docs/concepts/DISC/kademlia.md +++ b/docs/concepts/DISC/kademlia.mdx @@ -5,7 +5,6 @@ id: kademlia import bos_fig_2_3 from '/static/img/bos_fig_2_3.jpg'; - Kademlia is a distributed hash table (DHT) algorithm used in peer-to-peer networks to efficiently store and retrieve data without relying on centralized servers. It organizes nodes into an overlay network that ensures efficient routing using a binary tree structure. ## Kademlia Key Concepts diff --git a/docs/concepts/incentives/overview.md b/docs/concepts/incentives/overview.mdx similarity index 78% rename from docs/concepts/incentives/overview.md rename to docs/concepts/incentives/overview.mdx index b62385a32..6619cc1ec 100644 --- a/docs/concepts/incentives/overview.md +++ b/docs/concepts/incentives/overview.mdx @@ -3,6 +3,9 @@ title: Incentives Overview id: overview --- +import { globalVariables } from '/src/config/globalVariables'; + + One of the key challenges in a decentralised data network is incentivizing users to store data and provide bandwidth. Swarm addresses this challenge with two incentives systems, one which rewards nodes for sharing their storage space and another which rewards them for sharing bandwidth. The incentives system consists of multiple elements which work together to build a self sustaining economic system where nodes are rewarded for honestly providing their resources to the network. :::info @@ -13,13 +16,14 @@ Swarm's storage incentives protocols are defined in depth in the [Future Proof S Storage incentives are used to reward node operators for providing their disk space to the network and storing the data they are responsible for storing over time. The storage incentives system is composed of three smart contracts which work together to enact a self regulating economic system. The postage stamp contract manages payments for uploading data, the redistribution contract manages the redistribution of those payments to storer nodes, and the price oracle contract uses data from the redistribution contract to set the price for postage stamps in the postage stamp contract. + ### Postage Stamps -Postage stamps are used to pre-purchase the right to upload data on storm, much in the same way that real life postage stamps are used to pre-pay for use of the postal service. Postage stamps are purchased in batches rather than one by one, and are consumed when uploading data to Swarm. Postage stamp batches are purchased using xBZZ through the [postage stamp smart contract](https://gnosisscan.io/address/0x45a1502382541Cd610CC9068e88727426b696293#code). the xBZZ used to pay for postage stamp batches serve as the funds which are redistributed as storage incentives in the redistribution game. The price of postage stamps is set by the price oracle. Read more [here](/docs/concepts/incentives/postage-stamps). +Postage stamps are used to pre-purchase the right to upload data on storm, much in the same way that real life postage stamps are used to pre-pay for use of the postal service. Postage stamps are purchased in batches rather than one by one, and are consumed when uploading data to Swarm. Postage stamp batches are purchased using xBZZ through the postage stamp smart contract . the xBZZ used to pay for postage stamp batches serve as the funds which are redistributed as storage incentives in the redistribution game. The price of postage stamps is set by the price oracle. Read more [here](/docs/concepts/incentives/postage-stamps). ### Redistribution Game -The redistribution game is used to redistribute the xBZZ paid into the postage stamp contract to full staking nodes which contribute their disk space to the network. The game is designed in such a way that the most profitable way to participate is to honestly store all the data for which a node is responsible. The game's rules are determined by the [redistribution smart contract](https://gnosisscan.io/address/0xFfF73fd14537277B3F3807e1AB0F85E17c0ABea5#code). The results of the game also supply the utilization signal which is used by the price oracle to set the price for postage stamps. Read more [here](/docs/concepts/incentives/postage-stamps). +The redistribution game is used to redistribute the xBZZ paid into the postage stamp contract to full staking nodes which contribute their disk space to the network. The game is designed in such a way that the most profitable way to participate is to honestly store all the data for which a node is responsible. The game's rules are determined by the redistribution smart contract. The results of the game also supply the utilization signal which is used by the price oracle to set the price for postage stamps. Read more [here](/docs/concepts/incentives/postage-stamps). ### Price Oracle diff --git a/docs/references/awesome-list.mdx b/docs/references/awesome-list.mdx new file mode 100644 index 000000000..3239b4461 --- /dev/null +++ b/docs/references/awesome-list.mdx @@ -0,0 +1,8 @@ +--- +title: Awesome List +id: awesome-list +--- + +import AwesomeList from '@site/src/components/AwesomeList'; + + diff --git a/docs/references/community.md b/docs/references/community.md index f27aa0b55..62af0e878 100644 --- a/docs/references/community.md +++ b/docs/references/community.md @@ -25,4 +25,10 @@ Swarm grants support many interesting projects that are already building their p If you have an idea for a project which uses Swarm's technology we welcome you to [apply for a grant](https://grants.ethswarm.org). -Learn more about grants for building on Swarm at the [EthSwarm homepage](https://www.ethswarm.org/grants). \ No newline at end of file +Learn more about grants for building on Swarm at the [EthSwarm homepage](https://www.ethswarm.org/grants). + +## Fellowships + +[Swarm fellows](https://www.ethswarm.org/fellowships) work on items identified as needs for the Swarm network to evolve and grow but are not part of core Swarm development. Fellows are expected to pursue the goals supported by the fellowship in the long term as part of their career path. A fellowship helps them achieve results to a certain degree, but afterwards, the project should be sustainable and able to continue on its own. + +Current Swarm fellows include both [Datafund](https://datafund.io/) and [Solar Punk](https://solarpunk.buzz/). \ No newline at end of file diff --git a/docs/references/smart-contracts.md b/docs/references/smart-contracts.mdx similarity index 55% rename from docs/references/smart-contracts.md rename to docs/references/smart-contracts.mdx index c66d0d6af..4e7fdefd7 100644 --- a/docs/references/smart-contracts.md +++ b/docs/references/smart-contracts.mdx @@ -2,6 +2,7 @@ title: Smart Contracts id: smart-contracts --- +import { globalVariables } from '/src/config/globalVariables'; ### Token Contracts |Contract|Blockchain | Address | @@ -18,9 +19,9 @@ You can find the Solidity source code for each contract in the [storage incentiv For a history of smart contract addresses, see the [storage incentives ABI repo history](https://github.com/ethersphere/go-storage-incentives-abi/commits/master/abi/abi_mainnet.go). -|Contract|Blockchain | Address | -| ---------------------- | ------------------------------- |--------- | -|Postage Stamp|Gnosis Chain|[0x45a1502382541Cd610CC9068e88727426b696293](https://gnosisscan.io/address/0x45a1502382541Cd610CC9068e88727426b696293#code)| -|Staking|Gnosis Chain|[0xBe212EA1A4978a64e8f7636Ae18305C38CA092Bd](https://gnosisscan.io/address/0xBe212EA1A4978a64e8f7636Ae18305C38CA092Bd#code)| -|Redistribution|Gnosis Chain|[0xFfF73fd14537277B3F3807e1AB0F85E17c0ABea5](https://gnosisscan.io/address/0xFfF73fd14537277B3F3807e1AB0F85E17c0ABea5#code)| -|Price Oracle|Gnosis Chain|[0x86DE783Bf23Bc13DaeF5A55ec531C198da8f10cF](https://gnosisscan.io/address/0x86DE783Bf23Bc13DaeF5A55ec531C198da8f10cF#code)| \ No newline at end of file +| Contract | Blockchain | Address | +| --------------- | ------------ | -------------------------------------------------------------------------------------------------------------- | +| Postage Stamp | Gnosis Chain | {globalVariables.postageStampContract} | +| Staking | Gnosis Chain | {globalVariables.stakingContract} | +| Redistribution | Gnosis Chain | {globalVariables.redistributionContract} | +| Price Oracle | Gnosis Chain | {globalVariables.priceOracleContract} | diff --git a/docusaurus.config.js b/docusaurus.config.js index 6700449e8..5fdf6cb0a 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -260,10 +260,15 @@ module.exports = { to: 'docs/references/fair-data-society', label: 'Fair Data Society' }, + { + to: 'docs/references/awesome-list', + label: 'Awesome List' + }, { to: 'docs/references/faq', label: 'FAQ' }, + ] }, diff --git a/package-lock.json b/package-lock.json index 7403da866..9d3880c9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "plugin-image-zoom": "github:flexanalytics/plugin-image-zoom", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-markdown": "^9.0.1", "redocusaurus": "^2.0.0", "rehype-katex": "^7.0.0", "remark-math": "^6.0.0" @@ -11345,6 +11346,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/html-void-elements": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", @@ -17870,6 +17880,31 @@ "webpack": ">=4.41.1 || 5.x" } }, + "node_modules/react-markdown": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", + "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-remark": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/react-remark/-/react-remark-2.1.0.tgz", diff --git a/package.json b/package.json index 15bc45ac0..3c83caf1e 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "plugin-image-zoom": "github:flexanalytics/plugin-image-zoom", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-markdown": "^9.0.1", "redocusaurus": "^2.0.0", "rehype-katex": "^7.0.0", "remark-math": "^6.0.0" diff --git a/sidebars.js b/sidebars.js index 7b7ac3eeb..ab63a1432 100644 --- a/sidebars.js +++ b/sidebars.js @@ -137,6 +137,7 @@ module.exports = { 'references/glossary', 'references/community', 'references/fair-data-society', - 'references/faq' + 'references/faq', + 'references/awesome-list', ], }; diff --git a/src/components/AwesomeList.js b/src/components/AwesomeList.js new file mode 100644 index 000000000..76a1adabc --- /dev/null +++ b/src/components/AwesomeList.js @@ -0,0 +1,16 @@ +import React, { useEffect, useState } from 'react'; +import Markdown from 'react-markdown'; + +const AwesomeList = () => { + const [content, setContent] = useState(''); + + useEffect(() => { + fetch('https://raw.githubusercontent.com/ethersphere/awesome-swarm/refs/heads/master/README.md') + .then((res) => res.text()) + .then((text) => setContent(text)); + }, []); + + return {content}; +}; + +export default AwesomeList; diff --git a/src/config/globalVariables.js b/src/config/globalVariables.js new file mode 100644 index 000000000..e5b0cc691 --- /dev/null +++ b/src/config/globalVariables.js @@ -0,0 +1,7 @@ +export const globalVariables = { + exampleVariable: 'Hello, World!', + postageStampContract: '0x45a1502382541Cd610CC9068e88727426b696293', + stakingContract: '0xBe212EA1A4978a64e8f7636Ae18305C38CA092Bd', + redistributionContract: '0xFfF73fd14537277B3F3807e1AB0F85E17c0ABea5', + priceOracleContract: '0x86DE783Bf23Bc13DaeF5A55ec531C198da8f10cF' + }; diff --git a/src/pages/awesome-list.js b/src/pages/awesome-list.js new file mode 100644 index 000000000..52cca04dc --- /dev/null +++ b/src/pages/awesome-list.js @@ -0,0 +1,18 @@ +import React, { useEffect, useState } from 'react'; +import Markdown from 'react-markdown'; + +export default function AwesomeList() { + const [content, setContent] = useState(''); + + useEffect(() => { + fetch('https://raw.githubusercontent.com/ethersphere/awesome-swarm/refs/heads/master/README.md') + .then((res) => res.text()) + .then((text) => setContent(text)); + }, []); + + return ( +