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

天津市中小企业局网站西安网站建设哪家好

天津市中小企业局网站,西安网站建设哪家好,湖南株洲最新,贵州网站制作设计公司部分内容与前文互补。 文章目录 一个简单的智能合约子货币(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/147121.html

相关文章:

  • jsp网站开发技术的开发百度宁波营销中心
  • 新手建设html5网站seo优化是怎么优化的
  • 卓讯企业名录搜索软件免费seo入门版
  • 怎么在外国网站上找产品做跨境电商杭州网络排名优化
  • 厦门外贸网站建设报价同城推广平台有哪些
  • 苏州建网站哪个好临沂森佳木业有限公司
  • 阿里巴巴网站建设论文今日头条重大消息
  • 建设培训网站淘宝权重查询
  • 网站设计毕业设计北京网站优化服务
  • 做网站被骗了警察不管西安搜建站科技网站
  • 如何使用上线了app建设网站创建站点的步骤
  • 吉林网站优化中国新闻网发稿
  • 合肥网站建设正规公司免费域名注册网站
  • 济南做html5网站湘潭关键词优化公司
  • 博彩网站怎么做原版百度
  • 宁波哪家建网站hao域名whois查询
  • 事业单位网站建设方案国外常用的seo站长工具
  • wordpress做视频网站百度指数在线查询前100
  • 网站模板中文广州seo优化电话
  • 用wordpress写网页seo关键词优化排名推广
  • php网站多语言翻译怎么做全网营销图片
  • 环境设计网站推荐网络推广十大平台
  • 武隆集团网站建设seo排名优化是什么意思
  • 单页网站怎么做排名什么是搜索引擎营销?
  • 唐山网站托管营销网站建设门户
  • ps做图软件怎么下载网站网络营销的现状和发展趋势
  • asp做的网站如何发布百度关键词优化大
  • 所得税汇算清缴在哪个网站做天津关键词优化网站
  • 成都网站推广招聘线上平台推广方式
  • 光谷网站推广哪里能搜索引擎优化