前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >开发者如何使用 Ceramic 开发 DApp

开发者如何使用 Ceramic 开发 DApp

作者头像
Tiny熊
发布2022-11-07 10:08:15
1.2K0
发布2022-11-07 10:08:15
举报
文章被收录于专栏:深入浅出区块链技术

译文出自:登链翻译计划[1] 译者:翻译小组[2] 校对:Tiny 熊[3]

这个是一份适合初学者的开发者指南,我们将为你提供将Ceramic Network[4]集成到你的 Web 3 DApps[5]所需的所有工具和知识。

如果你喜欢看视频可以点击Ceramic 版本 V2.0.0 On Youtube[6]

Ceramic 网络是一个去中心化的数据网络,旨在为 Web 3 应用程序带来可组合的数据。Ceramic 可以处理很多类型的数据,但在本指南中,我们可以把 Ceramic 当作一个去中心化的 NOSQL 文档数据库。

本指南的目的是让你跟着一起实践,因为本文中会有图表和代码示例。

开发技能要求

除了这个书面指南,我还提供了一个GitHub[7]仓库,其中包含我参考的所有代码。

如果你喜欢视频指南,而不是书面指南,你可以在Ceramic Youtube Channel[8]上观看视频演练。

在你开始之前,需要你已经具备了下面列出一般 web 开发技能。

本指南中使用的技能有

  • 基础 JavaScript[9]
  • 了解客户端 JS 与服务器端 JS[10]之间的基本差异
  • JavaScript 包管理
  • 对Webpack[11]有基本了解。

可选的技能

  • Git[12]
  • 版本控制(如 GitHub、GitLab、BitBucket)。

必要的工具(在继续之前需要安装)

  • 文本编辑器(如 VS Code, Sublime, vim)。
  • NodeJS[13] v16 或更高版本
  • NPM[14] v8 或更高版本或Yarn[15]

明确关键术语

在开始之前,我将介绍一些将在本指南中使用的关键术语。

去中心化的身份[16]

通常被称为DID[17]

一个DID[18]是一个独特的身份标识符,包含关于你的元数据。诸如你的公钥[19],一些验证信息,以及被你允许访问哪些服务点和其他一些东西。

简单地说,DID[20]被用来作为 Ceramic 账户的身份标识。

使用此功能的依赖库是dids

DID 解析器[21]

DID 解析器接受一个 DID 作为输入并返回一个DID 文档[22]

这个解析过程将 DID 从一般的东西变成一个文件,准确地描述一个身份以及该身份允许执行的方法和能力。

简单地说,解析者将一个 DID 与它能够执行的行动结合起来。

使用此功能的依赖库是:

  • key-did-resolver
  • @glazed/did-datastore

以太坊 Providers[23]

如果你想让你的应用程序能够访问区块链,你需要使用一个提供者。

本指南将连接到以太坊区块链,因此使用了一个以太坊提供者。

提供者是用来代替自己运行区块链节点的。提供者有两个主要任务:

  1. 告诉你的应用程序要连接到什么区块链。
  2. 连接之后,就可以运行查询、以及发送修改区块链状态的签名交易。

Metamask 是最流行的区块链提供者之一,它是将用于将我们的应用程序连接到以太坊区块链

简单地说,提供者认证用户在区块链上执行操作。

使用此功能的依赖库是:

  • key-did-provider-ed25519
  • @glazed/did-session
  • @ceramicnetwork/blockchain-utils-link

**数据的流类型(StreamTypes[24] **)

当我说到数据流的时候,我不是在说从消费的角度来看的流数据[25]。流是 Ceramic 对其数据结构的称呼。请随意阅读更多关于流[26]的信息。

StreamType[27]只是一个流的可能数据结构之一。在本指南中,我们将间接地使用 TileDocument流类型[28],你可以把它看作是一个JSON Object[29]。这些 StreamTypes 是处理与数据有关的所有事情,它们在Ceramic nodes[30]上运行。

简单地说,StreamTypes 定义了数据结构和数据的状态被允许改变的方式。

使用此功能的依赖库是:

  • @glazed/did-datastore

数据模型[31]

数据模型通常用于表示一个应用程序的功能:比如笔记、用户资料、博客文章,甚至是社交图谱。

数据模型是可组合数据的核心。一个应用程序使用多个数据模型是很常见的,而一个数据模型在多个应用程序中使用也是很常见的!

这样做的可组合性也使开发者的体验更好。在 Ceramic 上构建一个应用程序看起来就像浏览一个数据模型的市场,将它们插入你的应用程序,并自动获得网络上存储在这些模型中的所有数据。

简单地说,数据模型是在一个应用程序中实现数据可组合性的东西。

构建应用程序

你将建立一个简单的网络应用,对 Ceramic 网络上的数据进行简单的读写操作。为了使这个应用程序正常工作,它需要按照以下罗列的顺序完成步骤:

  1. 使用一个以太坊 Provider 来验证区块链。
  2. 一旦通过认证,获取一个 DID,以便与 Ceramic 一起使用。
  3. 使用一个 Ceramic 实例,用提供的 DID 来读写一个TileDocument流。

我在关键术语[32]一节中提到了一些依赖关系,但在你进一步了解之前,还有一些其他的依赖关系需要了解:

Ceramic 客户端[33]

这是一个 Web 客户端,它允许你的应用程序连接到作为网络一部分的Ceramic 节点[34]

用于此功能的依赖是:

  • @ceramicnetwork/http-client

Webpack[35]

你将编写的 JavaScript 使用 Node 包,使其成为服务器端的代码。然而,Web 浏览器则需要客户端的代码。

Webpack 是一个很好的模块,它将把你将要编写的服务器端 JavaScript 转换成浏览器可以理解的客户端 JavaScript。

为了达到这个目的,我们需要一些依赖库。

用于此功能的依赖库是:

  • webpack
  • webpack-cli
  • buffer

构建前端

我将引导你通过使用简单的 HTML 和 CSS 来构建这个应用程序前端的主要步骤

  1. 让我们开始为这个项目创建一个新的目录。这个过程会根据你的操作系统而有所不同,所以选择最适合你环境的方案。 Windows
代码语言:javascript
复制
md getting-started-with-ceramic

MacOS/Linux

代码语言:javascript
复制
mkdir getting-started-with-ceramic
  1. 现在,在该目录下创建一个名为index.html的文件。index.html文件应该包含以下内容:
代码语言:javascript
复制
<!DOCTYPE html>
   <html lang="en">

   <head>
       <meta charset="UTF-8">
       <meta http-equiv="X-UA-Compatible" content="IE=edge">
       <meta name="viewport" content="width=device-width, initial-scale=1.0">
       <link rel="stylesheet" href="style.css">
       <link rel="shortcut icon" href="/favicon.ico">

       <title>Getting Started</title>
   </head>

   <body>
       <!-- create header with connect button -->
       <header class="SiteHeader">
           <div class="HeaderContainer">
               <h1 id="pageTitle">Getting Start With Ceramic</h1>
           </div>
           <div class="HeaderContainer">
               <button id="walletBtn"></button>
           </div>

       </header>
       <div class="MainCont">
           <div class="DataBlocks">
               <div class="DataBlock">
                   <div id="basicProfile">
                       <div class="BodyContainer">
                           <h2>Basic Profile</h2>
                           <p>Read from Ceramic Datamodel</p>
                           <br>
                           <p class="ProfileData" id="profileName"></p>
                           <p class="ProfileData" id="profileGender"></p>
                           <p class="ProfileData" id="profileCountry"></p>
                       </div>
                   </div>
               </div>
           </div>
           <div class="ProfileForm">
               <div class="BodyContainer">
                   <h2>Update Basic Profile on Ceramic</h2>
                   <br>
                   <form id="profileForm">
                       <div class="formfield">
                           <label class="formLabel" for="name">Name:</label>
                           <input class="forminput" type="text" id="name" placeholder="John Doe">
                       </div>
                       <div class="formfield">
                           <label class="formLabel" for="country">Country:</label>
                           <input class="forminput" type="text" id="country" placeholder="USA">
                       </div>
                       <div class="formfield">
                           <label class="formLabel" for="gender">Gender:</label>
                           <select class="forminput" id="gender">
                               <option value="female">Female</option>
                               <option value="male">Male</option>
                               <option value="non-binary">Non-Binary</option>
                               <option value="other">Other</option>
                           </select>
                       </div>
                       <div class="formfield">
                           <input class="forminput" type="submit" id="submitBtn" value="Submit">
                       </div>
                   </form>
               </div>
           </div>
       </div>

       <!-- <button id="setBasicProf">Set Profile</button>
       <button id="getBasicProf">Get Profile</button> -->
       <script src="dist/bundle.js" type="module"></script>
   </body>

   </html>
  1. 接下来,在getting-started-with-ceramic目录下创建一个名为style.css的文件。这个文件应该包含以下内容:
代码语言:javascript
复制
* {
       margin: 0;
       padding: 0;
   }

   .SiteHeader {
       display: flex;
       justify-content: space-between;
       padding: 10px;
       background-color: orange;
   }

   .HeaderContainer {
       display: flex;
       align-items: center;
   }


   .MainCont {
       display: flex;
       justify-content: space-around;
       padding: 10px;
   }

   .DataBlock {

       margin-bottom: 10px;

   }

   .BodyContainer {
       background-color: lightsalmon;
       border: 1px solid black;
       border-radius: 30px;
       padding: 20px;
       min-width: 250px;
   }

   .ProfileForm {
       min-width: 400px;
   }



   .formfield {
       display: flex;
       justify-content: space-between;
       margin-bottom: 10px;
   }

   .forminput {
       min-width: 150px;

   }

   #submitBtn {
       display: block;
       margin: auto;
       width: auto;
   }

   .ProfileData {
       font-weight: bold;
   }

现在,如果你在浏览器中打开index.html文件,或使用LiveShare[36]这样的工具,你应该看到这样的内容:

添加 JavaScript 和 Ceramic

现在应用程序还没有实际功能,它没有内置的逻辑,它只是一个有一些内容和一些样式的静态页面。

在这一步,我将向你展示如何使用提供者、解析器和 Ceramic 将这个应用程序从一个静态网站转变为一个 web 3 dapp!

  1. 首先,使用NPM[37]或Yarn[38]初始化一个新的NodeJS[39]项目。 NPM
代码语言:javascript
复制
npm init -y

Yarn

代码语言:javascript
复制
yarn init -y
  1. 接下来,安装上述的依赖项: NPM 开发中依赖项
代码语言:javascript
复制
npm install -D buffer dids key-did-provider-ed25519 key-did-resolver webpack webpack-cli

常规依赖项

代码语言:javascript
复制
npm install @ceramicnetwork/blockchain-utils-linking @ceramicnetwork/http-client @glazed/did-datastore @glazed/did-session

Yarn

开发依赖项

代码语言:javascript
复制
yarn add -D buffer dids key-did-provider-ed25519 key-did-resolver webpack webpack-cli

常规依赖项

代码语言:javascript
复制
yarn add @ceramicnetwork/blockchain-utils-linking @ceramicnetwork/http-client @glazed/did-datastore @glazed/did-session
  1. 现在,在getting-started-with-ceramic目录下创建一个名为main.js的文件。
  2. 首先,将需要的 depndencies 导入该文件:
代码语言:javascript
复制
//main.js

   import { CeramicClient } from '@ceramicnetwork/http-client'
   import { EthereumAuthProvider } from '@ceramicnetwork/blockchain-utils-linking'
   import { DIDDataStore } from '@glazed/did-datastore'
   import { DIDSession } from '@glazed/did-session'

你是否注意到,有些软件包来自@ceramicnetwork,有些来自@glazed? 来自@ceramicnetwork 的包是 Ceramic 核心协议的一部分。它们帮助应用程序连接到 Ceramic 节点。 来自@glazed 的软件包不是核心 Ceramic 协议的一部分,它们被称为 中间件,为开发者提供一些额外的功能和便利。

  1. 在依赖库导入之后,你应该设置一系列的 DOM 元素选择器(selctors)。这不仅使我们的代码在编写时更容易阅读,而且在更大的应用程序中,这种技术可以增加性能优势。在main.js中添加以下内容:
代码语言:javascript
复制
import { CeramicClient } from '@ceramicnetwork/http-client'
   import { EthereumAuthProvider } from '@ceramicnetwork/blockchain-utils-linking'
   import { DIDDataStore } from '@glazed/did-datastore'
   import { DIDSession } from '@glazed/did-session'

   const profileForm = document.getElementById('profileForm')
   const walletBtn = document.getElementById('walletBtn')
   const profileName = document.getElementById('profileName')
   const profileGender = document.getElementById('profileGender')
   const profileCountry = document.getElementById('profileCountry')
   const submitBtn = document.getElementById('submitBtn')
  1. 使用刚刚导入的CeramiClient,通过在main.js文件中添加以下代码,创建一个新的 Ceramic 客户端实例:
代码语言:javascript
复制
//main.js

   import { CeramicClient } from '@ceramicnetwork/http-client'
   import { EthereumAuthProvider } from '@ceramicnetwork/blockchain-utils-linking'
   import { DIDDataStore } from '@glazed/did-datastore'
   import { DIDSession } from '@glazed/did-session'

   const profileForm = document.getElementById('profileForm')
   const walletBtn = document.getElementById('walletBtn')
   const profileName = document.getElementById('profileName')
   const profileGender = document.getElementById('profileGender')
   const profileCountry = document.getElementById('profileCountry')
   const submitBtn = document.getElementById('submitBtn')

   const ceramic = new CeramicClient("https://ceramic-clay.3boxlabs.com")

目前有 4 种可能的网络来供 Ceramic HTTP 客户端连接。你可以点击每个链接来了解更多关于网络的信息。

  • 主网[40]
  • Clay Testnet[41] (推荐使用,目前正被我们的应用程序使用)
  • Dev Unstable[42]
  • 本地网络[43]
  1. 接下来,创建一个名为aliases的变量,它将保存BasicProfile数据模型的引用:
代码语言:javascript
复制
//main.js

   import { CeramicClient } from '@ceramicnetwork/http-client'
   import { EthereumAuthProvider } from '@ceramicnetwork/blockchain-utils-linking'
   import { DIDDataStore } from '@glazed/did-datastore'
   import { DIDSession } from '@glazed/did-session'

   const profileForm = document.getElementById('profileForm')
   const walletBtn = document.getElementById('walletBtn')
   const profileName = document.getElementById('profileName')
   const profileGender = document.getElementById('profileGender')
   const profileCountry = document.getElementById('profileCountry')
   const submitBtn = document.getElementById('submitBtn')

   const ceramic = new CeramicClient("https://ceramic-clay.3boxlabs.com")

   const aliases = {
       schemas: {
           basicProfile: 'ceramic://k3y52l7qbv1frxt706gqfzmq6cbqdkptzk8uudaryhlkf6ly9vx21hqu4r6k1jqio',

       },
       definitions: {
           BasicProfile: 'kjzl6cwe1jw145cjbeko9kil8g9bxszjhyde21ob8epxuxkaon1izyqsu8wgcic',
       },
       tiles: {},
   }

数据模型的各个部分schemas:定义数据模型的 JSON schema definitions: 将一个用户友好的模型名称和描述链接到一个特定的 schema。 tiles: 在 schema 内参数集的各条数据记录。

  1. DIDDataStore允许应用程序从 Ceramic 写入和读取数据。DIDDataStore是基于数据模型的。在main.js中添加以下代码,以配置DIDDataStore,它使用到上面定义的aliasesceramic instance
代码语言:javascript
复制
//main.js

   import { CeramicClient } from '@ceramicnetwork/http-client'
   import { EthereumAuthProvider } from '@ceramicnetwork/blockchain-utils-linking'
   import { DIDDataStore } from '@glazed/did-datastore'
   import { DIDSession } from '@glazed/did-session'

   const profileForm = document.getElementById('profileForm')
   const walletBtn = document.getElementById('walletBtn')
   const profileName = document.getElementById('profileName')
   const profileGender = document.getElementById('profileGender')
   const profileCountry = document.getElementById('profileCountry')
   const submitBtn = document.getElementById('submitBtn')

   const ceramic = new CeramicClient("https://ceramic-clay.3boxlabs.com")

   const aliases = {
       schemas: {
           basicProfile: 'ceramic://k3y52l7qbv1frxt706gqfzmq6cbqdkptzk8uudaryhlkf6ly9vx21hqu4r6k1jqio',

       },
       definitions: {
           BasicProfile: 'kjzl6cwe1jw145cjbeko9kil8g9bxszjhyde21ob8epxuxkaon1izyqsu8wgcic',
       },
       tiles: {},
   }

   const datastore = new DIDDataStore({ ceramic, model: aliases })

如果你的应用程序需要,你可以通过添加必要的 schemadefinitiontilesaliases变量添加更多的数据模型!

你现在有了启动和运行这个应用程序所需的基本基础。Ceramic 客户端和数据模型的所有配置已经完成。

使用区块链进行认证

接下来的部分将指导你使用以太坊 Provider, Metamask[44],用以太坊区块链来验证用户。

正在使用的认证流程被称为Sign-In With Ethereum[45],以下简称为 SIWE。

请看这篇很棒的文章以了解更多。为什么用以太坊登录是一个游戏规则改变者[46]

让我们把 SIWE 添加到这个应用中吧!

  1. 这个应用程序需要一个异步函数[47],把它命名为authenticateWithEthereum,它使用提供者,然后使用 Resovler,最后把 DID 分配给你之前创建的 Ceramic Client。在main.js中添加这段代码来完成这些任务:
代码语言:javascript
复制
//main.js

   async function authenticateWithEthereum(ethereumProvider) {

       const accounts = await ethereumProvider.request({
       method: 'eth_requestAccounts',
       })

       const authProvider = new EthereumAuthProvider(ethereumProvider, accounts[0])

       const session = new DIDSession({ authProvider })

       const did = await session.authorize()

       ceramic.did = did
   }

DIDSession是它在这个代码片断中为你处理 SIWE 认证流程的。

  1. 在认证流程开始之前,通常有一些逻辑检查需要应用程序来完成。当开发 dapp 时,一个常见的检查是确保提供者是可用的。在我们的案例下,使用Metamask[48]会在浏览器window对象中注入自己作为提供者。它可以通过window.ethereum引用。如果应用程序的最终用户没有安装Metamask[49],或其他提供者,我们的应用程序将无法连接到区块链上。让我们把这些知识应用于一个新的异步函数[50],称为auth。将下面的代码添加到main.js中:
代码语言:javascript
复制
//main.js

   async function authenticateWithEthereum(ethereumProvider) {

       const accounts = await ethereumProvider.request({
       method: 'eth_requestAccounts',
       })

       const authProvider = new EthereumAuthProvider(ethereumProvider, accounts[0])

       const session = new DIDSession({ authProvider })

       const did = await session.authorize()

       ceramic.did = did
   }

   // newly added auth function here:
   async function auth() {
   if (window.ethereum == null) {
       throw new Error('No injected Ethereum provider found')
   }
   await authenticateWithEthereum(window.ethereum)
   }

auth()首先检查window.ethereum是否存在,然后再尝试调用authenticateWithEthereum()。这可以防止应用程序在没有注入提供者的情况下挂起!

完整的main.js文件目前看起来应该是这样的:

代码语言:javascript
复制
//main.js

    // import all dependencies:
    import { CeramicClient } from '@ceramicnetwork/http-client'
    import { EthereumAuthProvider } from '@ceramicnetwork/blockchain-utils-linking'
    import { DIDDataStore } from '@glazed/did-datastore'
    import { DIDSession } from '@glazed/did-session'

    //cache a reference to the DOM Elements
    const profileForm = document.getElementById('profileForm')
    const walletBtn = document.getElementById('walletBtn')
    const profileName = document.getElementById('profileName')
    const profileGender = document.getElementById('profileGender')
    const profileCountry = document.getElementById('profileCountry')
    const submitBtn = document.getElementById('submitBtn')

    // create a new CeramicClient instance:
    const ceramic = new CeramicClient("https://ceramic-clay.3boxlabs.com")

    // reference the data models this application will use:
    const aliases = {
        schemas: {
            basicProfile: 'ceramic://k3y52l7qbv1frxt706gqfzmq6cbqdkptzk8uudaryhlkf6ly9vx21hqu4r6k1jqio',

        },
        definitions: {
            BasicProfile: 'kjzl6cwe1jw145cjbeko9kil8g9bxszjhyde21ob8epxuxkaon1izyqsu8wgcic',
        },
        tiles: {},
    }

    // configure the datastore to use the ceramic instance and data models referenced above:
    const datastore = new DIDDataStore({ ceramic, model: aliases })

    // this function authenticates the user using SIWE
    async function authenticateWithEthereum(ethereumProvider) {

        const accounts = await ethereumProvider.request({
        method: 'eth_requestAccounts',
        })

        const authProvider = new EthereumAuthProvider(ethereumProvider, accounts[0])

        const session = new DIDSession({ authProvider })

        const did = await session.authorize()

        ceramic.did = did
    }

    // check for a provider, then authenticate if the user has one injected:
    async function auth() {
        if (window.ethereum == null) {
        throw new Error('No injected Ethereum provider found')
        }
        await authenticateWithEthereum(window.ethereum)
    }

使用 Ceramic 读取数据

接下来的函数将使用DIDDatastore来从 Ceramic 网络中获取数据。我将称它为getProfileFromCeramic,它也是一个异步函数[51]

函数将在main.js文件中声明。

  1. main.js中添加getProfileFromCeramic函数:
代码语言:javascript
复制
//main.js

   async function getProfileFromCeramic() {
   try {

       //use the DIDDatastore to get profile data from Ceramic
       const profile = await datastore.get('BasicProfile')

       //render profile data to the DOM (not written yet)
       renderProfileData(profile)
   } catch (error) {
       console.error(error)
       }
   }

正如你所看到的,通过调用datastore.get()方法,你可以引用希望读取数据模型的定义

DIDDatastore 使用分配给 Ceramic 客户端的 DID 来进行这个调用。它返回的 profile 对象存储在profile变量中。

  1. 你将需要创建renderProfileData函数来提取这些资料数据并在浏览器窗口中显示。由于这不是一个网页开发的指南,我将不详细介绍这个函数的作用。在你的main.js文件中加入以下内容:
代码语言:javascript
复制
function renderProfileData(data) {
       if (!data) return
       data.name ? profileName.innerHTML = "Name:     " + data.name : profileName.innerHTML = "Name:     "
       data.gender ? profileGender.innerHTML = "Gender:     " + data.gender : profileGender.innerHTML = "Gender:     "
       data.country ? profileCountry.innerHTML = "Country:     " + data.country : profileCountry.innerHTML = "Country:     "
   }

我想指出的是,datadatastore.get()调用返回的profile对象。数据的属性在 "BasicProfile "数据模型中定义。查看Ceramic 数据模型仓库[52]中的数据模型,可以查看完整的属性列表[53]

这就是使用 DIDDataStore从 Ceramic 网络中读取数据的全部内容!

main.js完整代码如下:

代码语言:javascript
复制
//main.js

    // import all dependencies:
    import { CeramicClient } from '@ceramicnetwork/http-client'
    import { EthereumAuthProvider } from '@ceramicnetwork/blockchain-utils-linking'
    import { DIDDataStore } from '@glazed/did-datastore'
    import { DIDSession } from '@glazed/did-session'

    //cache a reference to the DOM Elements
    const profileForm = document.getElementById('profileForm')
    const walletBtn = document.getElementById('walletBtn')
    const profileName = document.getElementById('profileName')
    const profileGender = document.getElementById('profileGender')
    const profileCountry = document.getElementById('profileCountry')
    const submitBtn = document.getElementById('submitBtn')

    // create a new CeramicClient instance:
    const ceramic = new CeramicClient("https://ceramic-clay.3boxlabs.com")

    // reference the data models this application will use:
    const aliases = {
        schemas: {
            basicProfile: 'ceramic://k3y52l7qbv1frxt706gqfzmq6cbqdkptzk8uudaryhlkf6ly9vx21hqu4r6k1jqio',

        },
        definitions: {
            BasicProfile: 'kjzl6cwe1jw145cjbeko9kil8g9bxszjhyde21ob8epxuxkaon1izyqsu8wgcic',
        },
        tiles: {},
    }

    // configure the datastore to use the ceramic instance and data models referenced above:
    const datastore = new DIDDataStore({ ceramic, model: aliases })

    // this function authenticates the user using SIWE
    async function authenticateWithEthereum(ethereumProvider) {

        const accounts = await ethereumProvider.request({
        method: 'eth_requestAccounts',
        })

        const authProvider = new EthereumAuthProvider(ethereumProvider, accounts[0])

        const session = new DIDSession({ authProvider })

        const did = await session.authorize()

        ceramic.did = did
    }

    // check for a provider, then authenticate if the user has one injected:
    async function auth() {
        if (window.ethereum == null) {
        throw new Error('No injected Ethereum provider found')
        }
        await authenticateWithEthereum(window.ethereum)
    }

    //retrieve BasicProfile data from ceramic using the DIDDatastore
    async function getProfileFromCeramic() {
        try {

        //use the DIDDatastore to get profile data from Ceramic
        const profile = await datastore.get('BasicProfile')

        //render profile data to the DOM (not written yet)
        renderProfileData(profile)
        } catch (error) {
        console.error(error)
        }
    }

    //Do some fun web dev stuff to present the BasicProfile in the DOM
    function renderProfileData(data) {
        if (!data) return
        data.name ? profileName.innerHTML = "Name:     " + data.name : profileName.innerHTML = "Name:     "
        data.gender ? profileGender.innerHTML = "Gender:     " + data.gender : profileGender.innerHTML = "Gender:     "
        data.country ? profileCountry.innerHTML = "Country:     " + data.country : profileCountry.innerHTML = "Country:     "
    }

使用 Ceramic 写入数据

接下来要实现的是使用 DIDDatastore向 Ceramic 网络写数据。

  1. 像其他一些已经写好的函数一样,updateProfileOnCeramic函数应该是一个异步函数[54]。在main.js中添加以下内容:
代码语言:javascript
复制
async function updateProfileOnCeramic() {
       try {
       const updatedProfile = getFormProfile()
       submitBtn.value = "Updating..."

       //use the DIDDatastore to merge profile data to Ceramic
       await datastore.merge('BasicProfile', updatedProfile)

       //use the DIDDatastore to get profile data from Ceramic
       const profile = await datastore.get('BasicProfile')

       renderProfileData(profile)

       submitBtn.value = "Submit"
       } catch (error) {
       console.error(error)
       }
   }

在继续前进之前,有两件重要的事情要了解: 首先:DIDDatastore有两个方法允许向数据模型写入:

  • merge():只写已经改变的字段。
  • set():覆盖所有字段,包括那些没有改变的字段。这可能导致数据以不需要的方式被删除。由于这个原因,我们建议使用 merge 而不是 set。

其次:在本案例下,从 DIDDatastore 中读取数据并使用renderProfileData()将其渲染到 DOM 中并不是优秀的方式。在这个阶段没有真正的必要从 Ceramic 读取数据。这样做是为了向你展示读和写是多么简单,因为在使用 DIDDatastore 时,每一个都只需要一行代码。

  1. 你可能注意到在上述代码块中对getFormProfile()的调用。这个函数目前并不存在。现在让我们把它添加到main.js中。在main.js中加入以下代码:
代码语言:javascript
复制
function getFormProfile() {

       const name = document.getElementById('name').value
       const country = document.getElementById('country').value
       const gender = document.getElementById('gender').value

       return {
           name,
           country,
           gender
       }
   }

如果你想知道如何想出 namecountrygender这些对象属性的,它们都可以在BasicProfile[55]数据模型中找到。BasicProfile 还有一些额外的属性,在这个项目中没有被引用。你应该在自己的项目中探索这些属性的使用!

你成功了! 这就是你开始使用 Ceramic 所需要的一切。你现在知道的足够多了,足够去创造惊人的 dapp。

不过你还没有完全完成。有一些小东西必须建立起来才能使这个应用程序完全工作。

完善引用

本节以及下一节配置 Webpack[56],与 Ceramic 没有必然联系。这些部分涵盖了一些必要的操作,例如在应用程序的按钮上的操作,以及将服务器端转换成浏览器可以理解的代码。

按钮是如何工作的

应用程序的按钮元素将使用Event Listeners[57]来让它们被点击时执行功能。

以下所有的代码都需要放在main.js中:

  1. 让我们首先创建一个函数,当用户点击 连接钱包按钮时,事件监听器可以调用这个函数:
代码语言:javascript
复制
async function connectWallet(authFunction, callback) {
       try {
       walletBtn.innerHTML = "Connecting..."
       await authFunction()
       await callback()
       walletBtn.innerHTML = "Wallet Connected"

       } catch (error) {
       console.error(error)
       }

   }
  1. 目前,按钮元素没有显示任何innerHTML,所以在继续之前,让我们先解决这个问题。在之前发生在main.js的 DOM 缓存下,添加以下一行:
代码语言:javascript
复制
walletBtn.innerHTML = "Connect Wallet"
  1. 另一个缺少的东西是文本占位符,配置文件数据应该在这里呈现。你可以通过在walletBtn.innerHTML行下添加这段代码来设置该占位符文本:
代码语言:javascript
复制
walletBtn.innerHTML = "Connect Wallet"
   profileName.innerHTML = "Name: "
   profileGender.innerHTML = "Gender: "
   profileCountry.innerHTML = "Country: "
  1. 最后一件事是添加两个事件监听器。一个是 "连接钱包 "按钮,它将调用上面定义的connectWallet函数。另一个将放在作为profileForm元素一部分的按钮上。在main.js中添加这些代码:
代码语言:javascript
复制
walletBtn.addEventListener('click', async () => await connectWallet(auth, getProfileFromCeramic))

   profileForm.addEventListener('submit', async (e) => {
   e.preventDefault()
   await updateProfileOnCeramic()

   })

好了!这就是应用程序需要的所有 JavaScript:

代码语言:javascript
复制
//main.js

    // import all dependencies:
    import { CeramicClient } from '@ceramicnetwork/http-client'
    import { EthereumAuthProvider } from '@ceramicnetwork/blockchain-utils-linking'
    import { DIDDataStore } from '@glazed/did-datastore'
    import { DIDSession } from '@glazed/did-session'

    //cache a reference to the DOM Elements
    const profileForm = document.getElementById('profileForm')
    const walletBtn = document.getElementById('walletBtn')
    const profileName = document.getElementById('profileName')
    const profileGender = document.getElementById('profileGender')
    const profileCountry = document.getElementById('profileCountry')
    const submitBtn = document.getElementById('submitBtn')

    // give the wallet button an initial value to display
    walletBtn.innerHTML = "Connect Wallet"
    // setup placeholder text where profile should render
    walletBtn.innerHTML = "Connect Wallet"
    profileName.innerHTML = "Name: "
    profileGender.innerHTML = "Gender: "
    profileCountry.innerHTML = "Country: "

    // create a new CeramicClient instance:
    const ceramic = new CeramicClient("https://ceramic-clay.3boxlabs.com")

    // reference the data models this application will use:
    const aliases = {
        schemas: {
            basicProfile: 'ceramic://k3y52l7qbv1frxt706gqfzmq6cbqdkptzk8uudaryhlkf6ly9vx21hqu4r6k1jqio',

        },
        definitions: {
            BasicProfile: 'kjzl6cwe1jw145cjbeko9kil8g9bxszjhyde21ob8epxuxkaon1izyqsu8wgcic',
        },
        tiles: {},
    }

    // configure the datastore to use the ceramic instance and data models referenced above:
    const datastore = new DIDDataStore({ ceramic, model: aliases })

    // this function authenticates the user using SIWE
    async function authenticateWithEthereum(ethereumProvider) {

        const accounts = await ethereumProvider.request({
        method: 'eth_requestAccounts',
        })

        const authProvider = new EthereumAuthProvider(ethereumProvider, accounts[0])

        const session = new DIDSession({ authProvider })

        const did = await session.authorize()

        ceramic.did = did
    }

    // check for a provider, then authenticate if the user has one injected:
    async function auth() {
        if (window.ethereum == null) {
        throw new Error('No injected Ethereum provider found')
        }
        await authenticateWithEthereum(window.ethereum)
    }

    //retrieve BasicProfile data from ceramic using the DIDDatastore
    async function getProfileFromCeramic() {
        try {

        //use the DIDDatastore to get profile data from Ceramic
        const profile = await datastore.get('BasicProfile')

        //render profile data to the DOM (not written yet)
        renderProfileData(profile)
        } catch (error) {
        console.error(error)
        }
    }

    //Do some fun web dev stuff to present the BasicProfile in the DOM
    function renderProfileData(data) {
        if (!data) return
        data.name ? profileName.innerHTML = "Name:     " + data.name : profileName.innerHTML = "Name:     "
        data.gender ? profileGender.innerHTML = "Gender:     " + data.gender : profileGender.innerHTML = "Gender:     "
        data.country ? profileCountry.innerHTML = "Country:     " + data.country : profileCountry.innerHTML = "Country:     "
    }

    //this function uses the datastore to write data to the Ceramic Network as well as read data back before populating the changes in the DOM
    async function updateProfileOnCeramic() {
        try {
        const updatedProfile = getFormProfile()
        submitBtn.value = "Updating..."

        //use the DIDDatastore to merge profile data to Ceramic
        await datastore.merge('BasicProfile', updatedProfile)

        //use the DIDDatastore to get profile data from Ceramic
        const profile = await datastore.get('BasicProfile')

        renderProfileData(profile)

        submitBtn.value = "Submit"
        } catch (error) {
        console.error(error)
        }
    }

    // Parse the form and return the values so the BasicProfile can be updated
    function getFormProfile() {

        const name = document.getElementById('name').value
        const country = document.getElementById('country').value
        const gender = document.getElementById('gender').value

        // object needs to conform to the datamodel
        // name -> exists
        // hair-color -> DOES NOT EXIST
        return {
            name,
            country,
            gender
        }
    }


    //a simple utility funciton that will get called from the event listener attached to the connect wallet button
    async function connectWallet(authFunction, callback) {
        try {
        walletBtn.innerHTML = "Connecting..."
        await authFunction()
        await callback()
        walletBtn.innerHTML = "Wallet Connected"

        } catch (error) {
        console.error(error)
        }

    }

    //add both event listeners to that the buttons work when they are clicked
    walletBtn.addEventListener('click', async () => await connectWallet(auth, getProfileFromCeramic))

    profileForm.addEventListener('submit', async (e) => {
    e.preventDefault()
    await updateProfileOnCeramic()

    })

配置 Webpack

下面的部分将为这个应用程序配置Webpack[58]

  1. getting-started-with-ceramic目录下创建一个名为webpack.config.js的新文件,并在其中放置以下内容。
代码语言:javascript
复制
const path = require('path');
   module.exports = {
       entry: './main.js',
       output: {
           path: path.resolve(__dirname, 'dist'),
           filename: 'bundle.js'
       },
       mode: 'development',
       resolve: {
           fallback: { buffer: require.resolve('buffer') }
       }
   }

如果你想知道这段代码的作用,请务必查看Webpack[59]

  1. 接下来,你需要编辑目前存在于根目录下的package.json文件。你将只修改这个文件的scripts。对package.json做如下修改:
代码语言:javascript
复制
"scripts": {
       "test": "echo \"Error: no test specified\" && exit 1",
       "build": "webpack"
   }

为了清楚起见,这里的改动是增加了一个脚本,名为 build,用来调用 webpack。

完整的package.json可以在下面找到:

代码语言:javascript
复制
{
       "name": "getting-started-ceramic",
       "version": "1.0.0",
       "description": "",
       "main": "utils.js",
       "scripts": {
           "test": "echo \"Error: no test specified\" && exit 1",
           "build": "webpack"
       },
       "keywords": [],
       "author": "",
       "license": "ISC",
       "devDependencies": {
           "buffer": "^6.0.3",
           "dids": "^3.1.0",
           "key-did-provider-ed25519": "^2.0.0",
           "key-did-resolver": "^2.0.4",
           "webpack": "^5.72.1",
           "webpack-cli": "^4.9.2"
       },
       "dependencies": {
           "@ceramicnetwork/blockchain-utils-linking": "^2.0.4",
           "@ceramicnetwork/http-client": "^2.0.4",
           "@glazed/did-datastore": "^0.3.1",
           "@glazed/did-session": "^0.0.1"
       }
   }

根据你完成本指南的时间,文件上可能有小的版本差异。这是正常的,没有什么可担心的。

  1. 最后一步是在终端或命令行中运行这个新添加的脚本。运行这个脚本就可以把以前所有的 JavaScript 打包成一个你的浏览器可以解释的版本。不管是什么操作系统,命令都是一样的:

NPM

代码语言:javascript
复制
npm run build

Yarn

代码语言:javascript
复制
yarn run build

恭喜你

恭喜你! 你现在可以在浏览器中重新打开index.html文件,或者通过使用LiveShare[60]

使用你的Metamask[61]钱包,你将能够用 Ethereum 登录[62],从 Ceramic 检索你的BasicProfile,并对该配置文件的一组有限属性进行修改!

如果你从未在Ceramic Network[63]上配置过 BasicProfile,你最初将不会收到数据。你需要使用选择的钱包账户,通过Self.id[64]应用程序或使用本应用程序所包含的表格来创建一个配置文件!


本翻译由 Duet Protocol[65] 赞助支持。

原文:https://blog.ceramic.network/getting-started-with-ceramic/

参考资料

[1]

登链翻译计划: https://github.com/lbc-team/Pioneer

[2]

翻译小组: https://learnblockchain.cn/people/412

[3]

Tiny 熊: https://learnblockchain.cn/people/15

[4]

Ceramic Network: https://developers.ceramic.network/

[5]

DApps: https://learnblockchain.cn/tags/DApp

[6]

Ceramic版本V2.0.0 On Youtube: https://www.youtube.com/watch?v=rpyokDPnyUs

[7]

GitHub: https://www.github.com/ceramicstudio/guide-getting-started-with-ceramic

[8]

Ceramic Youtube Channel: https://www.youtube.com/channel/UCgCLq5dx7sX-yUrrEbtYqVw

[9]

基础JavaScript: https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics

[10]

客户端JS与服务器端JS: https://computersciencewiki.org/index.php/Client-side_scripting_and_server-side_scripting

[11]

Webpack: https://webpack.js.org/

[12]

Git: https://git-scm.com/

[13]

NodeJS: https://www.nodejs.org/

[14]

NPM: https://www.npmjs.com/

[15]

Yarn: https://yarnpkg.com/

[16]

去中心化的身份: https://www.w3.org/TR/did-core/

[17]

DID: https://www.w3.org/TR/did-core/#dfn-decentralized-identifiers

[18]

DID: https://www.w3.org/TR/did-core/#dfn-decentralized-identifiers

[19]

公钥: https://en.wikipedia.org/wiki/Public-key_cryptography

[20]

DID: https://www.w3.org/TR/did-core/#dfn-decentralized-identifiers

[21]

DID解析器: https://www.w3.org/TR/did-core/#dfn-did-resolvers

[22]

DID文档: https://www.w3.org/TR/did-core/#dfn-did-documents

[23]

以太坊 Providers: https://docs.ethers.io/v4/api-providers.html#providers

[24]

StreamTypes: https://developers.ceramic.network/learn/glossary/#streamtypes

[25]

流数据: https://en.wikipedia.org/wiki/Streaming_data

[26]

流: https://developers.ceramic.network/learn/glossary/#streams

[27]

StreamType: https://developers.ceramic.network/learn/glossary/#streamtypes

[28]

流类型: https://developers.ceramic.network/learn/glossary/#streamtypes

[29]

JSON Object: https://www.json.org/json-en.html

[30]

Ceramic nodes: https://developers.ceramic.network/learn/glossary/#nodes

[31]

数据模型: https://developers.ceramic.network/docs/advanced/standards/data-models/#data-models

[32]

关键术语: #明确关键术语

[33]

Ceramic客户端: https://developers.ceramic.network/reference/core-clients/ceramic-http/

[34]

Ceramic节点: https://developers.ceramic.network/learn/glossary/#nodes

[35]

Webpack: https://webpack.js.org/

[36]

LiveShare: https://visualstudio.microsoft.com/services/live-share/

[37]

NPM: https://www.npmjs.com/

[38]

Yarn: https://yarnpkg.com/

[39]

NodeJS: https://www.nodejs.org/

[40]

主网: https://developers.ceramic.network/learn/networks/#mainnet

[41]

Clay Testnet: https://developers.ceramic.network/learn/networks/#clay-testnet

[42]

Dev Unstable: https://developers.ceramic.network/learn/networks/#dev-unstable

[43]

本地网络: https://developers.ceramic.network/learn/networks/#local

[44]

Metamask: https://metamask.io/

[45]

Sign-In With Ethereum: https://login.xyz/

[46]

为什么用以太坊登录是一个游戏规则改变者: https://blog.spruceid.com/sign-in-with-ethereum-is-a-game-changer-part-1/

[47]

异步函数: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

[48]

Metamask: https://metamask.io/

[49]

Metamask: https://metamask.io/

[50]

异步函数: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

[51]

异步函数: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

[52]

Ceramic 数据模型仓库: https://github.com/ceramicstudio/datamodels

[53]

完整的属性列表: https://github.com/ceramicstudio/datamodels/tree/main/models/identity-profile-basic

[54]

异步函数: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

[55]

BasicProfile: https://github.com/ceramicstudio/datamodels/tree/main/models/identity-profile-basic

[56]

配置Webpack: https://blog.ceramic.network/getting-started-with-ceramic/#configuring-webpack

[57]

Event Listeners: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener

[58]

Webpack: https://webpack.js.org/

[59]

Webpack: https://webpack.js.org/

[60]

LiveShare: https://visualstudio.microsoft.com/services/live-share/

[61]

Metamask: https://metamask.io/

[62]

用Ethereum登录: https://login.xyz/

[63]

Ceramic Network: https://developers.ceramic.network/

[64]

Self.id: https://clay.self.id/

[65]

Duet Protocol: https://duet.finance/?utm_souce=learnblockchain

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 深入浅出区块链技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 开发技能要求
  • 明确关键术语
  • 构建应用程序
  • 构建前端
  • 添加 JavaScript 和 Ceramic
  • 使用区块链进行认证
  • 使用 Ceramic 读取数据
  • 使用 Ceramic 写入数据
  • 完善引用
  • 配置 Webpack
  • 恭喜你
    • 参考资料
    相关产品与服务
    区块链
    云链聚未来,协同无边界。腾讯云区块链作为中国领先的区块链服务平台和技术提供商,致力于构建技术、数据、价值、产业互联互通的区块链基础设施,引领区块链底层技术及行业应用创新,助力传统产业转型升级,推动实体经济与数字经济深度融合。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档