这是我一系列分散数据库的第二部分,在这里我查看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驱动的后端的体系结构看起来像这样:
当代码和数据在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”按钮。
单击“连接所有者钱包”按钮,然后选择一个帐户/地址。
注意:确保您将开发帐户与访问专用密钥一起使用,因为我们需要将其复制到Node.js代码,该代码将稍后连接到数据库。
这可能需要一点,但是它将为您提供WeavedB Instances列表中的新数据库。
之后,您可以单击右上角的“使用DB”按钮,然后再次选择相同的地址。现在,我们可以对数据进行建模。
建模数据
首先,我们为个人资料,文章和评论创建集合。
要创建一个集合,您必须在左侧的导航中单击“数据收集”选项卡,然后单击表的“集合”标头旁边的小+
符号。
创建三个集合,每个集合都有其中一个集合ID:
profiles
articles
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 update
和allow delete
规则使用user_address
字段检查用户是否有权修改该文档。
-
resource.data
在修改之前包含文档- 创建文档以确保用户提供其地址时使用。
-
修改后,
resource.newData
包含文档- 在更新或删除文档时使用以确保用户仅修改自己的文档。
在profiles
集合的情况下,应该看起来像这样:
重复此过程的articles
和comments
集合。
我们设置集合及其访问规则后,让我们更新每个集合的模式。
- 单击左侧的导航上的“架构”选项卡
- 在表中选择
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_id
,user_address
和name
。它还允许可选字段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_id
,user_address
,title
和content
。
在这种情况下,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软件,因此请勿将其用于生产数据!