当前位置: 首页 > wzjs >正文

网站开发小程序开发百度高级搜索网址

网站开发小程序开发,百度高级搜索网址,美女 妹子 by wordpress,免费自己做网站软件部分内容与前文互补。 文章目录 一个简单的智能合约子货币(Subcurrency)示例区块链基础交易区块预编译合约 一个简单的智能合约 我们从一个基础示例开始,该示例用于设置变量的值,并允许其他合约访问它。 // SPDX-License-Identi…

部分内容与前文互补。

在这里插入图片描述

文章目录

    • 一个简单的智能合约
    • 子货币(Subcurrency)示例
    • 区块链基础
      • 交易
      • 区块
      • 预编译合约

一个简单的智能合约

我们从一个基础示例开始,该示例用于设置变量的值,并允许其他合约访问它。

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;contract SimpleStorage {uint storedData;function set(uint x) public {storedData = x;}function get() public view returns (uint) {return storedData;}
}

代码的第一行表明,该源代码采用 GPL 3.0 许可证进行授权。在以源代码公开为默认规则的环境中,使用机器可读的许可证标识符是非常重要的。

接下来一行 pragma solidity >=0.4.16 <0.9.0; 指定了该合约适用于 Solidity 0.4.16 及以上版本,但不包括 0.9.0。这是为了确保合约不会在未来的破坏性更新(Breaking Changes)中出现兼容性问题。Pragma 语句是编译器的指令,类似于 C/C++ 语言中的 pragma once,用于指定源代码的编译方式。

在 Solidity 语言中,合约(contract) 本质上是一个代码(函数)和数据(状态)的集合,它们驻留在以太坊区块链上的特定地址处。

contract SimpleStorage {uint storedData;function set(uint x) public {storedData = x;}function get() public view returns (uint) {return storedData;}
}

在合约 SimpleStorage 中,uint storedData; 声明了一个状态变量 storedData,其类型为 uint(无符号整数,默认为 256 位)。你可以把它看作数据库中的一个单一存储槽位,可以通过调用合约中的函数来查询和修改它。在这个示例中,合约提供了 set 和 get 两个函数,分别用于修改和获取 storedData 的值。

在 Solidity 中,访问当前合约的成员变量(如 storedData),通常无需使用 this. 前缀,直接使用变量名即可。这不仅仅是代码风格的问题,而是影响访问方式的关键区别(后续会详细讲解)。

这个合约本身功能还比较简单,但得益于以太坊的基础架构,它允许任何人存储一个数值,并让全球范围内的任何人访问。理论上,没有任何方法可以阻止你发布这个数值。但需要注意,任何人都可以再次调用 set 方法,修改存储的值,并覆盖之前的数据。不过,之前存储的数据仍然会保留在区块链的历史记录中。

后续会介绍如何实现访问权限控制,以便只有你自己才能修改这个值。

警告:使用 Unicode 文本时需要小心,因为一些看起来相似甚至完全相同的字符,可能具有不同的代码点(Code Point),因此它们的字节编码可能不同,从而引发安全或兼容性问题。
注意:所有标识符(包括合约名、函数名和变量名)都必须使用 ASCII 字符集。不过,你仍然可以在 string 类型的变量中存储 UTF-8 编码的数据。

子货币(Subcurrency)示例

以下合约实现了最简单形式的加密货币。该合约仅允许其创建者铸造新币。任何人都可以在没有用户名和密码的情况下相互转账,所需的只是一个以太坊密钥对。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.26;// 该合约只能通过 IR 编译
contract Coin {// 关键字 "public" 使变量可被其他合约访问// 相当于所有人可见创建者的合约地址address public minter;mapping(address => uint) public balances;// 事件允许客户端对你声明的特定合约更改做出反应event Sent(address from, address to, uint amount);// 构造函数代码仅在合约创建时运行constructor() {minter = msg.sender;}// 向指定地址铸造一定数量的新币// 仅合约创建者可以调用// 相当于只有合约创建者可以向别人发送新币function mint(address receiver, uint amount) public {require(msg.sender == minter);balances[receiver] += amount;}// 错误(Errors)允许提供有关操作失败原因的信息// 这些信息会返回给调用该函数的用户error InsufficientBalance(uint requested, uint available);// 发送一定数量的现有币// 任何人都可以调用,将代币发送至指定地址function send(address receiver, uint amount) public {// 发送的数量必须小于等于自己拥有的数量require(amount <= balances[msg.sender], InsufficientBalance(amount, balances[msg.sender]));// 发送者减少balances[msg.sender] -= amount;// 接收者增加balances[receiver] += amount;emit Sent(msg.sender, receiver, amount);}
}

代码 address public minter; 声明了一个 address 类型的状态变量。

address 类型是一个 160 位的值,不允许执行任何算术运算。它适用于存储合约地址,或者存储外部账户(EOA)公钥哈希的一部分。

关键字 public 会自动生成一个函数,使外部可以访问当前合约的状态变量。如果没有 public,其他合约将无法访问该变量。

编译器生成的代码等效于以下函数(暂时忽略 external 和 view 关键字):

function minter() external view returns (address) { return minter; 
}

下一行代码:

mapping(address => uint) public balances;

这行代码同样定义了一个 public 状态变量,但它的类型比 address 更复杂。mapping 是 Solidity 提供的一种映射类型,它将地址映射到 uint(无符号整数),即每个地址对应一个余额。

mapping 的特性:

  • mapping 类似于哈希表,所有可能的键在初始化时就已经存在,并默认映射到 0(即字节表示全为零)。

  • 无法获取 mapping 的所有键或所有值,因此如果你需要跟踪存储在 mapping 中的数据,最好自己维护一个列表,或者使用更合适的数据结构。

使用 mapping 是因为它提供了一种高效且简洁的方式来关联每个地址与其余额,且适应了区块链中分布式账本的特点。

由于 balances 变量是 public,编译器会自动生成以下 getter 函数:

function balances(address account) external view returns (uint) {return balances[account];
}

这个函数可以用于查询某个账户的余额,例如:

uint myBalance = contract.balances(myAddress);

这样,你就可以直接在外部访问某个地址的 balance,而无需手动编写 getter 方法。

这一行代码:

event Sent(address from, address to, uint amount);

声明了一个 事件(event),它在 send 函数的最后一行被触发(emit)。像 Web 应用程序这样的以太坊客户端可以监听这些事件,而不会产生太多成本。

当事件被触发后,监听器会立即收到 from、to 和 amount 这三个参数,从而能够跟踪交易。

刚才提到的以太坊客户端使用以下 JavaScript 代码(web3.js)监听 Sent 事件,并调用 balances 函数来更新用户界面:

Coin.Sent().watch({}, '', function(error, result) {if (!error) {console.log("Coin transfer: " + result.args.amount +" coins were sent from " + result.args.from +" to " + result.args.to + ".");console.log("Balances now:\n" +"Sender: " + Coin.balances.call(result.args.from) +"Receiver: " + Coin.balances.call(result.args.to));}
});

构造函数是一种特殊的函数,在合约创建时执行,且无法在之后被调用。

在这个合约中,构造函数会永久存储创建合约的人的地址:

constructor() {minter = msg.sender;
}

其中,msg 是 Solidity 提供的全局变量,它包含了一些区块链相关的属性,比如msg.sender为当前调用该函数的外部账户(EOA)或合约地址。

这个合约有两个主要的用户调用函数:

  • mint —— 铸造新币

  • send —— 发送已存在的币

mint(铸造新币)

function mint(address receiver, uint amount) public {require(msg.sender == minter);balances[receiver] += amount;
}

只有合约的创建者(minter)可以调用 mint,因为:

require(msg.sender == minter);

如果 msg.sender 不是 minter,则交易会被回滚(revert)。

balances[receiver] += amount; 为接收者账户增加一定数量的新币。

注意: 虽然 minter 可以无限制铸造代币,但如果 balances[receiver] + amount 超过 uint 类型的最大值 2的256次方 - 1,就会导致溢出(overflow)。然而,Solidity 默认启用了 Checked arithmetic(溢出检查),所以如果溢出发生,交易会自动回滚。

send(发送币)

function send(address receiver, uint amount) public {require(amount <= balances[msg.sender], InsufficientBalance(amount, balances[msg.sender]));balances[msg.sender] -= amount;balances[receiver] += amount;emit Sent(msg.sender, receiver, amount);
}

任何人(已经拥有币的人)都可以调用 send,将币发送给其他人。

如果 msg.sender 的余额不足:

require(amount <= balances[msg.sender], InsufficientBalance(amount, balances[msg.sender]));

交易会回滚(revert),并返回 InsufficientBalance 错误,错误信息会提供给调用者,以便前端应用或区块浏览器能够显示失败的具体原因。

Solidity 允许在交易失败时提供更多的错误信息,以便前端应用可以更容易地调试或做出反应。

错误信息通过 revert 语句触发:

error InsufficientBalance(uint requested, uint available);

当 require 失败时,它会返回 InsufficientBalance,并提供请求的金额 requested 和可用余额 available。

注意,在这个例子中,所有的代币操作(如铸造、转账)都在合约内部完成,余额和交易信息是局部的,仅存储在合约的 balances 映射中。

普通区块链浏览器(如 Etherscan)只能显示以太坊全局账户余额,你不会在普通的区块浏览器中看到余额变化。

解决方案:监听 Sent 事件,并创建自己的区块链浏览器来跟踪交易记录和余额变化,但你查询合约地址(通过合约内部的查询函数),而不是代币持有人的地址。

区块链基础

区块链作为一个概念对于程序员来说并不难理解。大多数复杂性(如哈希、椭圆曲线加密、对等网络等)只是为了为平台提供一组特定的功能和承诺。一旦你接受了这些特性作为前提,你就不必担心底层技术——就像你不需要知道亚马逊的 AWS 是如何在内部工作的。

交易

区块链是一个全球共享的事务性数据库。这意味着每个人都可以通过参与网络来读取数据库中的条目。如果你想更改数据库中的内容,你必须创建一个所谓的“交易”,并且这个交易必须被所有其他参与者接受。

“交易”一词意味着你想要进行的更改(假设你同时想更改两个值)要么完全不做,要么完全应用。此外,在你的交易被应用到数据库时,其他交易不能修改它。

例如,假设有一个表格列出了所有账户的余额。如果请求从一个账户转账到另一个账户,数据库的事务性特征确保如果从一个账户扣除金额,这个金额始终会被加到另一个账户上。如果由于某种原因无法将金额添加到目标账户,源账户也不会被修改。

此外,交易总是由发送者(创建者)进行加密签名。这使得保护对数据库特定修改的访问变得简单。举个例子,只有持有账户密钥的人可以从中转移一定的货币。

区块

需要克服的一个主要问题是双重支付攻击:“如果在网络中有两个交易都想清空一个账户,该怎么办?”

解决方案是:只有其中一个交易可以是有效的,通常是先被接受的那个。

问题在于,“先”在对等网络中并不是一个客观的术语。

对此的抽象回答是:你不需要担心。一个全球公认的交易顺序会为你选定,从而解决冲突。这些交易会被打包成一个叫做“区块”的内容,然后被执行并在所有参与节点之间分发。如果两个交易互相矛盾,第二个交易会被拒绝,并不会成为区块的一部分。

这些区块形成了一个线性时间序列,这也是“区块链”这一术语的来源。区块会在定期的间隔时间内添加到链中,尽管这些间隔时间将来可能会发生变化。为了获取最新的信息,建议监控网络,例如通过 Etherscan。

可能会发生区块偶尔被回滚的情况,但仅限于“链顶”部分。这是因为越多的区块添加到某个区块上时,这个区块被回滚的可能性就越小。所以,可能会出现你的交易被回滚甚至从区块链中移除的情况,但等待的时间越长,这种情况发生的可能性就越小。

注意
交易并不能保证会包含在下一个区块或任何特定的未来区块中,因为是否将交易包含在区块中并不是由交易提交者决定的,而是由矿工决定交易被包含在哪个区块中。

如果我们想安排未来的智能合约调用,可以使用智能合约自动化工具(比如定时触发某个操作,或者基于某个事件触发合约的函数调用)或预言机服务。

预编译合约

在以太坊中,智能合约通常用 Solidity 编写,并转换为 EVM 字节码执行。但一些计算(例如椭圆曲线加密、哈希计算)如果用 Solidity 实现,会消耗大量 Gas,甚至无法在区块 Gas 限制内完成。因此,以太坊提供了一组内置的预编译合约。

地址范围 0x01 到 0x0a(包含 0x0a) 属于预编译合约(Precompiled Contracts)。这些合约可以像普通合约一样被调用,但它们的行为(包括 Gas 消耗)并不是由存储在这些地址上的 EVM 代码决定的。这些合约直接在 EVM 层面执行,比普通智能合约运行更高效,并且Gas 消耗更少。

在这里插入图片描述

这些合约特别适用于密码学、哈希计算、零知识证明等高计算量的任务。

http://www.dtcms.com/wzjs/415317.html

相关文章:

  • 网页qq登陆保护杭州seo代理公司
  • 做网站怎么备案互联网营销推广
  • 做淘宝详情页的素材网站seo检测
  • 学校网站建设问卷调查对seo的理解
  • 深圳龙华的学校网站建设微信公众号运营
  • 郑州做网站zztuotian产品市场营销策划书
  • 遂宁网站建设百度链接提交入口
  • 个人域名能做网站吗宁海关键词优化怎么优化
  • 360网站卫士 真实ip网络推广公司运营
  • 自己做网站导航页网站优化公司哪家好
  • 如何做自适应网站网络营销推广策划的步骤
  • 网站推广明细报价表seo营销推广多少钱
  • 游戏开发工作室seo的概念是什么
  • 在线正能量网站地址链接免费搜索引擎排名优化程序
  • 网站介绍怎么写谷歌浏览器手机版下载
  • 联想服务器怎么建设第二个网站网络营销步骤
  • 石家庄网站搭建外包公司排名
  • 河南科技园网站建设页面优化算法
  • 别的网站做相关链接怎么做服务器ip域名解析
  • 做网站作业aso推广
  • 设计手机网站软件网络推广方式方法
  • 做帮助手册的网站杭州线上推广
  • 那个网站可以找人做设计师seo研究中心怎么了
  • 手机上的网页游戏seo的研究对象
  • 企业家网站建设东莞市网站seo内容优化
  • 用vs做网站教程优化师培训
  • 做新房什么网站好重庆seo排名软件
  • 2020给个免费网站好人有好报如何做好网络营销管理
  • java做网站和php做网站6宁波seo外包服务
  • 做网站设计好的公司昆明百度搜索排名优化