分散数据库:WeavedB
#web3 #database #区块链

这是我一系列分散数据库的第二部分,在这里我查看Web3是否提供了比缓慢且昂贵的区块链提供的。

在上一篇文章I looked into ComposeDB中,在陶瓷网络上运行的IPFS驱动图数据库;这次,我选择了Weavedb。因此,如果您喜欢耐心制度的基础架构,并且想知道可以存储数据的位置,请阅读!

什么是WeavedB?

WeaveDB nosql数据库与Firestore类似。它的主要数据结构是可嵌套的JSON文档,该文档存储在集合中,但也可以保存其自己的子收集,使其非常灵活。

WeavedB是一个智能合约数据库,这意味着整个数据库代码是在ARWEAVE上实现的。

通过区块链网络地址处理权限。对于数据库管理,支持的网络地址是EVM,DFINITY,INTMAX和ARWEAVE。常规用户还可以使用镜头协议和WebAuthn。

数据库所有者添加了他们的密钥,并在创建时成为数据库管理员。创建数据库后,所有者可以根据文档中的键添加其他密钥或定义集合的访问控件。

游览:Arweave基础知识

对于不认识arweave的人,这里是快速介绍。

什么是Arweave?

arweave有点像一个区块链;区别在于它已针对永久存储进行了优化。它使用称为BlockWeave的数据结构,该数据结构提供的存储空间比区块链更便宜。网络中的“矿工”互相询问块中的随机数据,并且只有在证明存储数据的情况下才获得奖励(矿物令)。这激励矿工尽可能多地持有街区。

ARWEAVE上使用的智能合同规范称为SmartWeave。与以太坊的智能合约不同,智能编织者不是由矿工/节点执行的,而是由客户执行(即浏览器,node.js)。

什么是SmartWeave?

SmartWeave合同只是JavaScript或WebAssembly,并且是ARWEAVE上存储为交易(TX)的初始状态JSON对象。实际合同是纯缩减/折叠功能,该功能采用初始状态并将输入应用于其计算当前状态。输入也存储在ARWEAVE上。客户端加载所有TXS并在本地计算最新状态,然后才能发送新的输入TX。

这是伪代码中的插图:

// code in contract TX
function handler(state, { input }) {
  if(input.function === "inc") return {value: state.value++}
  if(input.function === "dec") return {value: state.value--}
}

// code in initial state TX
{"value": 0}

// code in action TXs
{"input": {"function": "inc"}}
{"input": {"function": "dec"}}
{"input": {"function": "inc"}}

// client code
const contractCode = await getContract("<CONTRACT_TX_ID>")
eval(contractCode) // creates a handler function

const initialState = await getState("<INITIAL_STATE_TX_ID>")
let state = JSON.parse(initialState)

const actions = await getActions("<CONTRACT_TX_ID>")

state = actions.reduce(handler, state) // state is now {value: 1}

此体系结构消除了从网络节点进行任意计算的需求,并允许每个客户端独立扩展性能。

您可以将SmartWeave合同视为CQRS systems,其中Arweave是事件商店,状态是投影。

共识如何工作?

在伪代码示例中,每个人都可以在合同中添加新的输入;唯一的有效性标准是正确的格式。

如果您将区块链地址或公共密钥添加到初始状态,则合同可以在应用之前使用您的私钥签署每个输入。

攻击者仍然可以在ARWEAVE中添加无效的输入,但是您的合同会在评估时过滤掉。

存在哪些SmartWeave的实现?

在撰写本文时,我发现了SmartWeave规范的两个实现。

第一个是Arweave团队的the reference implementation。它是相当基础的,但直接与ARWEAVE网络合作,没有其他服务,但也非常慢。

第二个是Warp Contracts,它在参考实施方面具有许多生活质量的改进。它使用公司托管的服务,该公司维持经纱合同和其他第三方(例如Bundlr和StreamR),但表现良好。

什么是智能合约数据库?

由于汽油价格不关心智能环境合同,您可以用JavaScript或WebAssembly编写合同,因此该平台比其他智能合同规格更灵活。

人们正在使用SmartWeave规范创建各种软件,例如,将其代码和初始状态存储在ARWEAVE上并在客户端上执行的数据库。每次客户端撰写数据时,TX都会发送到ARWEAVE,以便其他客户可以阅读TX并更新其状态。

由于它将数据存储在ARWEAVE上,因此每个想编写数据库的人都必须为ARWEAVE TXS付费。这可以是数据库操作员(就像在Web2上完成的一样)或用户。

注意:由Bundlr或Redstone(Warp合同背后的公司)托管的服务目前正在补贴TXS。这意味着小型TX可以免费编写。

如果状态对客户端的增长太大,或者您想为用户补贴TXS付款,则可以在ARWEAVE和客户端之间放置服务器。由于数据是永久和公开存储的,因此每个人都可以验证数据是否有效。

一个有趣的概念,对吗?因此,让我们使用WeavedB构建博客!

建筑学

WeavedB驱动的后端的体系结构看起来像这样:

WeaveDB Architecture

当代码和数据在ARWEAVE上时,直接使用它会很慢;这就是为什么WeavedB SDK使用Warp SDK与Warp网络进行交互的原因。

经纱序器确保订购交互并将其发送到Bundlr网络,该网络又将持续在ARWEAVE上。

Warp Gateway将在ARWEAVE上索引与尚未完成的交互(仅在Bundlr网络上)以进行快速访问。

经线评估器将收集索引交互,并将其发送到计算状态的节点的分散网络。这使浏览器无法在用户的设备上进行所有这些计算。

复制品直接在浏览器或node.js上运行。 Weavedb首先在本地写入更改,并将其作为TXS发送到Arweave。

在Node.js上,WeavedB SDK将自动订阅Warp的通知服务,因此,当其他副本添加了TXS ARWEAVE时,每个本地副本都会收到通知,需要在本地重播。

如果WeavedB在浏览器中运行,则必须通过其他方式进行轮询以获取更新。定期或当用户在页面之间导航时。

总结一下,整个体系结构基于第三方服务。交互存储在ARWEAVE上,并且浏览器加载应用程序时将计算当前状态。

先决条件

功能步骤

我们将从功能步骤开始,因为WeavedB配备了一个Web控制台,该控制台允许我们单击几下创建数据库。

创建一个新数据库

Open the WeaveDB Console in your browser,然后单击“部署weavedb”按钮。

Database Creation Dialog

单击“连接所有者钱包”按钮,然后选择一个帐户/地址。

注意:确保您将开发帐户与访问专用密钥一起使用,因为我们需要将其复制到Node.js代码,该代码将稍后连接到数据库。

这可能需要一点,但是它将为您提供WeavedB Instances列表中的新数据库。

WeaveDB Instances

之后,您可以单击右上角的“使用DB”按钮,然后再次选择相同的地址。现在,我们可以对数据进行建模。

建模数据

首先,我们为个人资料,文章和评论创建集合。

要创建一个集合,您必须在左侧的导航中单击“数据收集”选项卡,然后单击表的“集合”标头旁边的小+符号。

Create Collection

创建三个集合,每个集合都有其中一个集合ID:

  1. profiles
  2. articles
  3. comments

每个可以签署交易的人都可以在这些集合中创建文档。

{
  "allow write": true
}

用以下访问规则将其替换为每个集合:

{
  "allow create": {
    "and": [
      { "!=": [{ "var": "request.auth.signer" }, null] },
      {
        "==": [
          { "var": "request.auth.signer" },
          { "var": "resource.newData.user_address" }
        ]
      }
    ]
  },
  "allow update": {
    "==": [
      { "var": "request.auth.signer" },
      { "var": "resource.data.user_address" }
    ]
  },
  "allow delete": {
    "==": [
      { "var": "request.auth.signer" },
      { "var": "resource.data.user_address" }
    ]
  }
}

首先,我们要求每个想要创建文档以提供其区块链地址的人,而没有匿名用户的写入访问权限。

此外,用户只能创建在user_address字段中包含自己地址的文档。 allow updateallow delete规则使用user_address字段检查用户是否有权修改该文档。

  • resource.data在修改之前包含文档
    • 创建文档以确保用户提供其地址时使用。
  • 修改后,resource.newData包含文档
    • 在更新或删除文档时使用以确保用户仅修改自己的文档。

profiles集合的情况下,应该看起来像这样:

Profiles collection creation dialog

重复此过程的articlescomments集合。

我们设置集合及其访问规则后,让我们更新每个集合的模式。

  • 单击左侧的导航上的“架构”选项卡
  • 在表中选择profiles集合
  • 单击表右上角的编辑符号

集合的默认模式如下:

{
  "type": "object",
  "required": [],
  "properties": {}
}

这意味着,该集合都接受任何文档,无论其形状如何。

为了防止用户添加我们的前端无法理解的文档,请将profiles集合的模式更新为以下JSON:

{
  "type": "object",
  "required": ["profile_id", "user_address", "name"],
  "properties": {
    "profile_id": {
      "type": "string"
    },
    "user_address": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "bio": {
      "type": "string"
    }
  }
}
  • 单击articles Collection
  • 单击表右上角的编辑符号

articles架构更新为以下JSON:

{
  "type": "object",
  "required": ["article_id", "user_address", "title", "content"],
  "properties": {
    "article_id": {
      "type": "string"
    },
    "user_address": {
      "type": "string"
    },
    "title": {
      "type": "string"
    },
    "content": {
      "type": "string"
    }
  }
}
  • 单击comments Collection
  • 单击表右上角的编辑符号

comments架构更新为以下JSON:

{
  "type": "object",
  "required": ["comment_id", "article_id", "user_address", "content", "date"],
  "properties": {
    "comment_id": {
      "type": "string"
    },
    "article_id": {
      "type": "string"
    },
    "user_address": {
      "type": "string"
    },
    "content": {
      "type": "string"
    },
    "date": {
      "type": "number"
    }
  }
}

模式非常简单,使用ID进行关系;建模的另一种方法是将comments直接嵌入articles,因此我们可以一次加载它们。

执行

现在我们已经使用架构和访问控件设置了数据库,我们可以实现读取和编写文档的查询。

创建一个node.js项目

首先,我们必须创建一个新的node.js项目。

$ mkdir weavedb-blog
$ cd weavedb-blog 
$ npm init

安装依赖项

接下来,我们需要WeavedB SDK连接到我们的数据库。要安装它,请使用以下命令:

$ npm i weavedb-sdk weavedb-sdk-node

设置数据库连接

要连接到WeavedB,使用此代码创建一个index.js文件:

const WeaveDB = require("weavedb-sdk-node")

const contractTxId = "<DATABASE_TX_ID>"
const wallet = {
  getAddressString: () => "<ADMIN_ADDRESS>".toLowerCase(),
  getPrivateKey: () => Buffer.from("<ADMIN_PRIVATE_KEY>", "hex"),
}

async function main() {
  const db = new WeaveDB({ contractTxId })
  await db.initializeWithoutWallet()
  db.setDefaultWallet(wallet, "evm")

  process.exit(0)
}

main()

首先,我们需要替换the WeaveDB console中可用的<DATABASE_TX_ID>

在“ WeavedB实例”下选择数据库,然后您可以从右侧的“设置”中复制TX ID。

<ADMIN_ADDRESS><ADMIN_PRIVATE_KEY>是您用来创建数据库的

地址需要较低的情况,私钥需要是缓冲区,但是代码可容纳。

我们必须致电initializeWithoutWallet,因为我们不使用Arweave钱包。我们可以将其直接传递给构造函数。调用setDefaultWallet使我们可以使用EVM地址和私钥作为我们写操作的默认身份。

之后,我们可以从数据库中阅读并写入。请记住,数据库将加载所有TXS并在Node.js进程中重复当前状态。

创建个人资料

我们需要将文档插入profiles集合以创建一个配置文件。

该集合的模式要求每个配置文件都具有profile_iduser_addressname。它还允许可选字段bio描述用户。

要编写具有自定义ID的文档,我们必须使用set方法,并将其传递给具有数据,集合名称以及我们所需ID的对象。创建数据库连接时,此操作将使用我们的默认钱包。如果您想用其他钱包签名,可以在ID参数之后通过它。

const data = {
  profile_id: "p1",
  name: "K",
  bio: "Web enthusiast.",
  user_address: wallet.getAddressString(),
}

const tx = await db.set(
  data,
  "profiles",
  data.profile_id
)

没有user_address的配置文件,它必须是签署相关TX的相同的配置文件,因此我们将其从wallet对象中拿走。

WeavedB允许我们在对象数据中创建没有ID的文档,但是将其放入文档中非常方便,因此我们手动添加了它。

虽然对set的调用是异步的,但这并不意味着当arweave的数据消失时,数据仍然存在。这使得数据在WeavedB的本地复制品中可用,并在稍后将其持续为TX。这可能需要几秒钟,因此,如果操作不是关键的时间,您可以继续在乐观的更新中继续进行本地工作。

要等到数据真正在ARWEAVE上并适用于所有其他副本,我们可以调用tx对象的getResult方法。

const result = await tx.getResult()

注意:如果set方法失败,则tx对象不会有getResult方法,因为数据对象中缺少权限或必需的字段。因此,请确保您在调用它之前遇到任何错误!

创建一篇文章

一篇文章需要article_iduser_addresstitlecontent

在这种情况下,user_address将促进用户个人资料及其文章之间的链接。但是,我们也可以使用其他外键或完全删除关系,并将文章作为嵌套文档添加到配置文件中。独立的文章集合只会使与文章的互动更容易与文章进行互动。例如,当您要列出,排序和过滤主页上的所有文章时。

const data = {
  article_id: "a1",
  title: "What is WeaveDB?",
  content: "WeaveDB is a decentralized database built with SmartWeave contracts on top of Arweave.",
  user_address: wallet.getAddressString(),
}

const tx = await db.set(
  data,
  "articles",
  data.article_id
)

创建评论

要评论一篇文章,我们需要一个文章ID才能知道我们要评论的文章。

在我们的评论架构中,我们还使用date在发表评论时用于时间戳。

const data = {
  comment_id: "c1",
  article_id: "a1",
  user_address: wallet.getAddressString(),
  date: 1687337848805,
  content: "WeaveDB seems interesting. Thanks for the article, I'll check it out!",
}

const tx = await db.set(
  data,
  "comments",
  data.comment_id
)

阅读数据

现在我们创建了一些文档,我们可以使用get命令阅读它们。它需要一个集合和一个可选ID。如果没有通过ID,它将返回一系列文档。

因此,我们可以使用以下代码来创建一个配置文件列表:

const profiles = await db.get("profiles")

const html = `
<ul>
  ${profiles.map(
    ({ profile_id, name }) => `
    <a href="/profiles/${profile_id}">
      <li>${name}</li>
    </a>`
  )}
</ul>`

要在单独的页面上使用用户的文章显示配置文件详细信息,我们可以使用此代码:

const profile = await db.get("profiles", profileId)
const articles = await db.get("articles", ["user_address"], [ "user_address", "==", profile.user_address ])

const html = `
<h1>Profile of ${profile.name}</h1>
<h2>Bio</h2>
<p>${profile.bio}</p>
<h2>Articles</h2>
<ul>
  ${articles.map(
    ({ article_id, title }) => `
    <a href="/articles/${article_id}">
      <li>${title}</li>
    </a>`
  )}
</ul>`

最后,一个显示文章及其所有评论的页面。

const article = await db.get("articles", articleId);
const comments = await db.get(
  "comments",
  ["article_id"],
  ["article_id", "==", article.article_id]
);

const html = `
<h1>${article.title}</h1>
<p>${article.content}</p>
<ul>
  ${comments.map(
    ({ comment_id, content, user_address, date }) => `
      <li>
        "${content}" 
        - by ${user_address} 
        at ${new Date(12345678).toLocaleString()}
      </li>`
  )}
</ul>`

删除评论

要从WeavedB中删除数据,我们可以使用delete命令。

以下代码说明了如何删除评论:

await db.delete("comments", commentId)

注意:delete仅从州删除文件;它不会删除创建文档的TX。如前所述,SmartWeave是一个CQRS系统,状态是投影,而ARWEAVE上的TX是事件。如果某人过去调用块高度的get命令,那么当没有删除时,他们可能会删除文档。此外,他们可以直接浏览数据库的所有TXS以获取数据。

概括

WeavedB是分散数据库的令人兴奋的选择。 ARWEAVE保证所有数据已永久存储,但状态机允许本地数据根据需要增长和收缩。

它带有功能强大的数据建模,查询,访问控制功能,流行的非Arweave钱包的集成以及本介绍文章不涵盖的更多内容。

在其最新版本中,它甚至与LIT和LENS协议集成在一起。它允许您利用文档加密和用户配置文件。

注意:WeavedB仍然是Alpha软件,因此请勿将其用于生产数据!