
搜索引擎Typesense的使用
注: 使用语言 python一:Typesense介绍Typesense将数据保存在磁盘当中,建立的索引保存内存中 Typesense是一个开源的、有容错能力的搜索引擎,针对实时(通常低于 50 毫秒)搜索即键入体验和开发人员生产力进行了优化。 Typesense做了一个对于其他搜索引擎的对比。(文档版,表格版) 索引数据速度以及资源占用: 对于220万份食谱(一份食谱相当于下文中提到的一个document)在 Typesense 中进行索引时占用了大约 900MB 的 RAM(内存)花了 3.6 分钟索引所有 220 万条记录在具有 4 个 vCPU 的服务器上,Typesense 每秒能够处理104 个并发搜索查询,平均搜索处理时间为11毫秒。RAM(内存)方面:如果数据量为 X MB大小,则需要占用2X-3XRAM(2-3倍数据量大小的占用) 如需深入了解可以查阅官方文档二:Typesense的用法1:使用typesense有两种方法使用自带的云服务,配置运行简单(收费)在本地安装typesense,自己维护配置(本文使用这种方法)2:安装启动typesense(1):下载...

Mastodon 和 Nostr:两种不同的社交产品,一样的去中心化愿景
概述:本文将分析和讲解Mastodon和Nostr这两个社交媒体平台,重点关注它们的产品应用和技术层面,以了解它们如何实现去中心化社交,并探讨它们在这方面的优势。我们将深入了解它们的架构设计和实现思路,并比较它们在用户体验、隐私保护、安全性等方面的差异。通过本文的分析和总结,读者将更好地了解这两个平台,以及它们在去中心化社交方面的贡献和发展。MastodonMastodon(长毛象)成立于2016年,是由Eugen Rochko创建的一个开源的微博客(microblog)平台,旨在为用户提供去中心化、隐私保护的社交体验。首先对涉及到的一些名词进行简单解释:联邦(federation):联邦是去中心化的一种形式。在联邦中,不是所有人共同使用一个中心服务,而是使用多个不限人数的服务器。ActivityPub:Mastodon使用一种标准化的、开放的协议来实现站点之间的互动,这种协议叫做ActivityPub。任何通过ActivityPub实现互联的软件都可以与Mastodon无缝通信,就像Mastodon站点之间的通信一样。实例(instance):每个人都可以在自己服务器上配置运行...

使用 The Graph 获取各大元宇宙项目交易信息
The graph 工作原理 Graph 根据subgraph描述(称为subgraph.graphq)学习什么以及如何索引以太坊数据。子图描述定义了subgraph感兴趣的智能合约,这些合约中要关注的事件,以及如何将事件数据映射到 The Graph 将存储在其数据库中的数据。该流程遵循以下步骤:去中心化应用程序通过智能合约上的交易将数据添加到以太坊。智能合约在处理交易时发出一个或多个事件。Graph Node 不断地扫描以太坊以寻找新的块以及它们可能包含的子图的数据。Graph Node 在这些块中为您的子图查找 Ethereum 事件并运行您提供的映射处理程序。映射是一个 WASM 模块,它创建或更新 Graph Node 存储的数据实体以响应以太坊事件。去中心化应用程序使用节点的GraphQL 端点查询 Graph 节点以获取从区块链索引的数据。Graph 节点反过来将 GraphQL 查询转换为对其底层数据存储的查询,以便利用存储的索引功能获取此数据。去中心化应用程序在丰富的 UI 中为最终用户显示这些数据,他们使用这些数据在以太坊上发布新交易。循环重复 (来自The ...

搜索引擎Typesense的使用
注: 使用语言 python一:Typesense介绍Typesense将数据保存在磁盘当中,建立的索引保存内存中 Typesense是一个开源的、有容错能力的搜索引擎,针对实时(通常低于 50 毫秒)搜索即键入体验和开发人员生产力进行了优化。 Typesense做了一个对于其他搜索引擎的对比。(文档版,表格版) 索引数据速度以及资源占用: 对于220万份食谱(一份食谱相当于下文中提到的一个document)在 Typesense 中进行索引时占用了大约 900MB 的 RAM(内存)花了 3.6 分钟索引所有 220 万条记录在具有 4 个 vCPU 的服务器上,Typesense 每秒能够处理104 个并发搜索查询,平均搜索处理时间为11毫秒。RAM(内存)方面:如果数据量为 X MB大小,则需要占用2X-3XRAM(2-3倍数据量大小的占用) 如需深入了解可以查阅官方文档二:Typesense的用法1:使用typesense有两种方法使用自带的云服务,配置运行简单(收费)在本地安装typesense,自己维护配置(本文使用这种方法)2:安装启动typesense(1):下载...

Mastodon 和 Nostr:两种不同的社交产品,一样的去中心化愿景
概述:本文将分析和讲解Mastodon和Nostr这两个社交媒体平台,重点关注它们的产品应用和技术层面,以了解它们如何实现去中心化社交,并探讨它们在这方面的优势。我们将深入了解它们的架构设计和实现思路,并比较它们在用户体验、隐私保护、安全性等方面的差异。通过本文的分析和总结,读者将更好地了解这两个平台,以及它们在去中心化社交方面的贡献和发展。MastodonMastodon(长毛象)成立于2016年,是由Eugen Rochko创建的一个开源的微博客(microblog)平台,旨在为用户提供去中心化、隐私保护的社交体验。首先对涉及到的一些名词进行简单解释:联邦(federation):联邦是去中心化的一种形式。在联邦中,不是所有人共同使用一个中心服务,而是使用多个不限人数的服务器。ActivityPub:Mastodon使用一种标准化的、开放的协议来实现站点之间的互动,这种协议叫做ActivityPub。任何通过ActivityPub实现互联的软件都可以与Mastodon无缝通信,就像Mastodon站点之间的通信一样。实例(instance):每个人都可以在自己服务器上配置运行...

使用 The Graph 获取各大元宇宙项目交易信息
The graph 工作原理 Graph 根据subgraph描述(称为subgraph.graphq)学习什么以及如何索引以太坊数据。子图描述定义了subgraph感兴趣的智能合约,这些合约中要关注的事件,以及如何将事件数据映射到 The Graph 将存储在其数据库中的数据。该流程遵循以下步骤:去中心化应用程序通过智能合约上的交易将数据添加到以太坊。智能合约在处理交易时发出一个或多个事件。Graph Node 不断地扫描以太坊以寻找新的块以及它们可能包含的子图的数据。Graph Node 在这些块中为您的子图查找 Ethereum 事件并运行您提供的映射处理程序。映射是一个 WASM 模块,它创建或更新 Graph Node 存储的数据实体以响应以太坊事件。去中心化应用程序使用节点的GraphQL 端点查询 Graph 节点以获取从区块链索引的数据。Graph 节点反过来将 GraphQL 查询转换为对其底层数据存储的查询,以便利用存储的索引功能获取此数据。去中心化应用程序在丰富的 UI 中为最终用户显示这些数据,他们使用这些数据在以太坊上发布新交易。循环重复 (来自The ...

Subscribe to Yooma

Subscribe to Yooma
Share Dialog
Share Dialog
<100 subscribers
<100 subscribers
这篇文章讲到什么是EIP-4907,EIP-721,ERC-20
一:介绍
Brownie 是一个基于 Python 的智能合约开发和测试框架。Brownie项目严重依赖web3.py
二:用法
安装
pip install eth-brownie
npm install ganache-cli@latest --global
(ganache-cli 会启动本地的以太坊测试网络便于我们测试,终端输入ganache-cli会看到一些测试账号私钥等信息)

安装成功后创建一个空文件夹(例如名为:brownie_test)
进入文件夹中执行brownie init

成功之后生成以下文件夹
contracts/: 用于存放要部署的智能合约
interfaces/: 用于存放接口,被智能合约调用
scripts/: 用于部署和交互的脚本
tests/: 用于测试项目的脚本
build/:存放项目数据,例如编译合约后的abi和单元测试结果
reports/:用于 GUI 的 JSON 报告文件
接下来编写继承自EIP-721和EIP-4907合约代码
首先将EIP-4907接口代码放到interfaces/
然后我们在contracts/ 创建一个名为rent_test.sol文件写入以下代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "../interfaces/IERC4907.sol";
contract ERC4907 is ERC721, IERC4907 {
struct UserInfo
{
address user; // address of user role
uint64 expires; // unix timestamp, user expires
}
mapping (uint256 => UserInfo) private _users;
constructor()
ERC721("RentNFTTest","RNT"){
}
/// @notice set the user and expires of a NFT
/// @dev The zero address indicates there is no user
/// Throws if `tokenId` is not valid NFT
/// @param user The new user of the NFT
/// @param expires UNIX timestamp, The new user could use the NFT before expires
function setUser(uint256 tokenId, address user, uint64 expires) public override virtual{
// 判断msg.sender是否是该token的owner
require(_isApprovedOrOwner(msg.sender, tokenId), "ERC4907: transfer caller is not owner nor approved");
// 设置 租赁者以及到期时间
UserInfo storage info = _users[tokenId];
info.user = user;
info.expires = expires;
// 更新该token的User
emit UpdateUser(tokenId,user,expires);
}
/// @notice Get the user address of an NFT
/// @dev The zero address indicates that there is no user or the user is expired
/// @param tokenId The NFT to get the user address for
/// @return The user address for this NFT
function userOf(uint256 tokenId) public view override virtual returns(address){
if( uint256(_users[tokenId].expires) >= block.timestamp){
return _users[tokenId].user;
}
return address(0);
}
/// @notice Get the user expires of an NFT
/// @dev The zero value indicates that there is no user
/// @param tokenId The NFT to get the user expires for
/// @return The user expires for this NFT
function userExpires(uint256 tokenId) public view override virtual returns(uint256){
return _users[tokenId].expires;
}
/// @dev See {IERC165-supportsInterface}.
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721) returns (bool) {
return interfaceId == type(IERC4907).interfaceId || super.supportsInterface(interfaceId);
}
function mint(uint256 tokenId) public returns (uint256){
_safeMint(msg.sender, tokenId);
return tokenId;
}
}
配置brownie-config.yaml
终端运行:
brownie pm install OpenZeppelin/openzeppelin-contracts@v4.6.0
创建 brownie-config.yaml(brownie配置文件)

在brownie-config.yaml中写入以下代码
dependencies:
- OpenZeppelin/openzeppelin-contracts@4.6.0
compiler:
solc:
version: null
optimizer:
enabled: true
runs: 200
remappings:
- '@openzeppelin=OpenZeppelin/openzeppelin-contracts@4.6.0'
终端执行 brownie compile 进行编译

该命令会将 contracts 目录下所有的智能合约都进行编译,编译完成后,在 build/contracts和interfaces 中会出现同名的 json 文件,包含bytecode和abi等信息
编写测试合约代码
接下来在 tests/ 目录下创建一个名为 test_rentable.py 的文件(文件名必须符合test_*.py / *_test.py的格式)
在文件中要测试到的方法函数需要以test为前缀,例如下面的test_nft
编写代码,brownie使用pytest来测试
# brownie中并没有ERC4907,这个是我们合约中定义的名字(contract ERC4907 is ERC721, IERC4907...)所以不论运行和测试都要在终端以brownie的命令来做
from brownie import ERC4907, accounts, chain
import brownie
import pytest
from web3.constants import ADDRESS_ZERO
DAY = 24 * 60 * 60
# scope="module"设置该方法在测试的时候只执行一次test_nft()
@pytest.fixture(scope="module")
def test_nft():
global deployer, owner, user
deployer, owner, user = accounts[0:3]
# 可以说是这里对合约中的ERC4907进行实例化,然后调用其中定义的function
testNft = ERC4907.deploy({'from': deployer})
return testNft
def test_mint(test_nft):
# Mint Nft
# 1 为 合约中 mint 接受的tokenId参数, {"from": owner} 中owner 则是msg.sender
test_nft.mint(1, {"from": owner})
print(f'Minted NFT ( TokenId : {1} )')
# 验证owner账户上的nft数量,如果是1个的话证明mint成功
assert test_nft.balanceOf(owner.address) == 1
# 验证 tokenId是1的nft 的owner是否与我们这里定义的owner相同
assert test_nft.ownerOf(1) == owner.address
终端执行 brownie test ,以下代码测试同理

接下来mint成功之后接着上面的代码来测试租赁
def test_renting(test_nft):
# 设置租赁两天
rent_expire_time = chain.time() + 2 * DAY
# 设置 租赁的nft的tokenId, user, 以及租赁时间
# 合约代码中会判断tokenId为1的owner(应该是我们上方mint此nft的地址)地址是否等于msg.sender, 而msg.sender是由{"from": owner.address}传过去,因为只有该nft的owner才有权力租
test_nft.setUser(1, user1.address, rent_expire_time, {"from": owner.address})
# 租出去之后再次查看下nft的owner变没变
assert test_nft.ownerOf(1) == owner1.address
# 验证租该nft用户的地址
assert test_nft.userOf(1) == user1.address
# 验证租nft的到期时间
assert test_nft.userExpires(1) == rent_expire_time
测试下不是owner进行租赁(设置uesr),接着上面代码继续
def test_user_nft_transfer(test_nft):
# 对于user不可以有transfer的权限
# 使用brownie.reverts来捕捉代码中报出的错误。由于{"from": user.address}(合约以msg.sender来接收,判断tokenId=1的owner是否与msg.sender相同),这里safeTransferFrom返回我们捕捉到的错误证明判断成功,而代码会继续向下执行,如果返回的错误和我们捕捉的不一样该代码会测试失败
with brownie.reverts("ERC721: transfer caller is not owner nor approved"):
testNft.safeTransferFrom(owner.address, user.address, 1, {"from": user.address})
测试租赁到期后是否还有user
def test_renting_expired(testNft):
# 假定两天后的时间点
chain.sleep(2 * DAY + 1)
chain.mine(1)
# 来看一下当前的时间是否过了租赁到期时间
assert testNft.userExpires(1) < chain.time()
# 到期后此nft的user为0x0000000000000000000000000000000000000000
assert testNft.userOf(1) == ADDRESS_ZERO
测试成功后结果都为passed
如果需要其他的判断逻辑使合约更严谨可以在代码中加入判断,基于EIP4907简单的实现就是这样。
这篇文章讲到什么是EIP-4907,EIP-721,ERC-20
一:介绍
Brownie 是一个基于 Python 的智能合约开发和测试框架。Brownie项目严重依赖web3.py
二:用法
安装
pip install eth-brownie
npm install ganache-cli@latest --global
(ganache-cli 会启动本地的以太坊测试网络便于我们测试,终端输入ganache-cli会看到一些测试账号私钥等信息)

安装成功后创建一个空文件夹(例如名为:brownie_test)
进入文件夹中执行brownie init

成功之后生成以下文件夹
contracts/: 用于存放要部署的智能合约
interfaces/: 用于存放接口,被智能合约调用
scripts/: 用于部署和交互的脚本
tests/: 用于测试项目的脚本
build/:存放项目数据,例如编译合约后的abi和单元测试结果
reports/:用于 GUI 的 JSON 报告文件
接下来编写继承自EIP-721和EIP-4907合约代码
首先将EIP-4907接口代码放到interfaces/
然后我们在contracts/ 创建一个名为rent_test.sol文件写入以下代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "../interfaces/IERC4907.sol";
contract ERC4907 is ERC721, IERC4907 {
struct UserInfo
{
address user; // address of user role
uint64 expires; // unix timestamp, user expires
}
mapping (uint256 => UserInfo) private _users;
constructor()
ERC721("RentNFTTest","RNT"){
}
/// @notice set the user and expires of a NFT
/// @dev The zero address indicates there is no user
/// Throws if `tokenId` is not valid NFT
/// @param user The new user of the NFT
/// @param expires UNIX timestamp, The new user could use the NFT before expires
function setUser(uint256 tokenId, address user, uint64 expires) public override virtual{
// 判断msg.sender是否是该token的owner
require(_isApprovedOrOwner(msg.sender, tokenId), "ERC4907: transfer caller is not owner nor approved");
// 设置 租赁者以及到期时间
UserInfo storage info = _users[tokenId];
info.user = user;
info.expires = expires;
// 更新该token的User
emit UpdateUser(tokenId,user,expires);
}
/// @notice Get the user address of an NFT
/// @dev The zero address indicates that there is no user or the user is expired
/// @param tokenId The NFT to get the user address for
/// @return The user address for this NFT
function userOf(uint256 tokenId) public view override virtual returns(address){
if( uint256(_users[tokenId].expires) >= block.timestamp){
return _users[tokenId].user;
}
return address(0);
}
/// @notice Get the user expires of an NFT
/// @dev The zero value indicates that there is no user
/// @param tokenId The NFT to get the user expires for
/// @return The user expires for this NFT
function userExpires(uint256 tokenId) public view override virtual returns(uint256){
return _users[tokenId].expires;
}
/// @dev See {IERC165-supportsInterface}.
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721) returns (bool) {
return interfaceId == type(IERC4907).interfaceId || super.supportsInterface(interfaceId);
}
function mint(uint256 tokenId) public returns (uint256){
_safeMint(msg.sender, tokenId);
return tokenId;
}
}
配置brownie-config.yaml
终端运行:
brownie pm install OpenZeppelin/openzeppelin-contracts@v4.6.0
创建 brownie-config.yaml(brownie配置文件)

在brownie-config.yaml中写入以下代码
dependencies:
- OpenZeppelin/openzeppelin-contracts@4.6.0
compiler:
solc:
version: null
optimizer:
enabled: true
runs: 200
remappings:
- '@openzeppelin=OpenZeppelin/openzeppelin-contracts@4.6.0'
终端执行 brownie compile 进行编译

该命令会将 contracts 目录下所有的智能合约都进行编译,编译完成后,在 build/contracts和interfaces 中会出现同名的 json 文件,包含bytecode和abi等信息
编写测试合约代码
接下来在 tests/ 目录下创建一个名为 test_rentable.py 的文件(文件名必须符合test_*.py / *_test.py的格式)
在文件中要测试到的方法函数需要以test为前缀,例如下面的test_nft
编写代码,brownie使用pytest来测试
# brownie中并没有ERC4907,这个是我们合约中定义的名字(contract ERC4907 is ERC721, IERC4907...)所以不论运行和测试都要在终端以brownie的命令来做
from brownie import ERC4907, accounts, chain
import brownie
import pytest
from web3.constants import ADDRESS_ZERO
DAY = 24 * 60 * 60
# scope="module"设置该方法在测试的时候只执行一次test_nft()
@pytest.fixture(scope="module")
def test_nft():
global deployer, owner, user
deployer, owner, user = accounts[0:3]
# 可以说是这里对合约中的ERC4907进行实例化,然后调用其中定义的function
testNft = ERC4907.deploy({'from': deployer})
return testNft
def test_mint(test_nft):
# Mint Nft
# 1 为 合约中 mint 接受的tokenId参数, {"from": owner} 中owner 则是msg.sender
test_nft.mint(1, {"from": owner})
print(f'Minted NFT ( TokenId : {1} )')
# 验证owner账户上的nft数量,如果是1个的话证明mint成功
assert test_nft.balanceOf(owner.address) == 1
# 验证 tokenId是1的nft 的owner是否与我们这里定义的owner相同
assert test_nft.ownerOf(1) == owner.address
终端执行 brownie test ,以下代码测试同理

接下来mint成功之后接着上面的代码来测试租赁
def test_renting(test_nft):
# 设置租赁两天
rent_expire_time = chain.time() + 2 * DAY
# 设置 租赁的nft的tokenId, user, 以及租赁时间
# 合约代码中会判断tokenId为1的owner(应该是我们上方mint此nft的地址)地址是否等于msg.sender, 而msg.sender是由{"from": owner.address}传过去,因为只有该nft的owner才有权力租
test_nft.setUser(1, user1.address, rent_expire_time, {"from": owner.address})
# 租出去之后再次查看下nft的owner变没变
assert test_nft.ownerOf(1) == owner1.address
# 验证租该nft用户的地址
assert test_nft.userOf(1) == user1.address
# 验证租nft的到期时间
assert test_nft.userExpires(1) == rent_expire_time
测试下不是owner进行租赁(设置uesr),接着上面代码继续
def test_user_nft_transfer(test_nft):
# 对于user不可以有transfer的权限
# 使用brownie.reverts来捕捉代码中报出的错误。由于{"from": user.address}(合约以msg.sender来接收,判断tokenId=1的owner是否与msg.sender相同),这里safeTransferFrom返回我们捕捉到的错误证明判断成功,而代码会继续向下执行,如果返回的错误和我们捕捉的不一样该代码会测试失败
with brownie.reverts("ERC721: transfer caller is not owner nor approved"):
testNft.safeTransferFrom(owner.address, user.address, 1, {"from": user.address})
测试租赁到期后是否还有user
def test_renting_expired(testNft):
# 假定两天后的时间点
chain.sleep(2 * DAY + 1)
chain.mine(1)
# 来看一下当前的时间是否过了租赁到期时间
assert testNft.userExpires(1) < chain.time()
# 到期后此nft的user为0x0000000000000000000000000000000000000000
assert testNft.userOf(1) == ADDRESS_ZERO
测试成功后结果都为passed
如果需要其他的判断逻辑使合约更严谨可以在代码中加入判断,基于EIP4907简单的实现就是这样。
No activity yet