【區塊鏈安全 | 第十篇】智能合約概述

部分內容與前文互補。

在這里插入圖片描述

文章目錄

    • 一個簡單的智能合約
    • 子貨幣(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.pswp.cn/web/73912.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/73912.shtml
英文地址,請注明出處:http://en.pswp.cn/web/73912.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

XML標簽格式轉換為YOLO TXT格式

針對的是多邊形&#xff08;<polygon>&#xff09;來描述對象的邊界&#xff0c;而不是傳統的矩形框&#xff08;<bndbox>&#xff09; import xml.etree.ElementTree as ET import os from pathlib import Path# 解析VOC格式的XML文件&#xff0c;提取目標框的標…

大唐杯02 DTM.PX4.016

01 5G關鍵技術概述 回傳壓力大&#xff1a;核心網向基站回傳壓力大 02 5G關鍵技術介紹01

CSS3學習教程,從入門到精通, CSS3 盒子模型的詳細語法知識點及案例代碼(23)

CSS3 盒子模型的詳細語法知識點及案例代碼 CSS3 盒子模型完整指南 一、盒子模型基礎 每個 HTML 元素都被視為一個矩形盒子&#xff0c;由以下部分組成&#xff1a; 內容區 (Content)內邊距 (Padding)邊框 (Border)外邊距 (Margin) 二、語法知識點詳解 1. 盒子的寬和高 sel…

《Linux運維實戰:Ubuntu 22.04修改root用戶默認名并禁止登錄》

總結&#xff1a;整理不易&#xff0c;如果對你有幫助&#xff0c;可否點贊關注一下&#xff1f; 更多詳細內容請參考&#xff1a;Linux運維實戰總結 一、背景信息 由于安全方面的考慮&#xff0c;先要求Ubuntu 22.04系統重的root用戶禁止登錄&#xff0c;并修改用戶名root為ad…

docker-compose自定義網絡,解決docker-compose網段路由沖突

問題排查 先route一波查看一下路由表 容器路由19和堡壘機路由沖突 解決方案 更改docker網段更改docker生成容器的網段 > 基本操作 docker network ls &#xff1a;查看docker網絡列表 docker network inspect <network id/name>&#xff1a;查看某個docker網絡詳情…

前端 - ts - - declare聲明類型

在使用typeScript的項目中 需要聲明屬性類型 單獨的局部屬性 可以直接在當前文件中聲明 全局屬性需要在項目根目錄下新建.d.ts文件 vite會自動識別.d.ts類型文件 在該文件中使用declare聲明類型有三種寫法 1、在某種類型的文件中聲明 2、聲明window上的屬性類型 3、全局聲明…

[Mac]利用Hexo+Github Pages搭建個人博客

由于我這臺Mac基本沒啥環境&#xff0c;因此需要從零開始配置&#xff0c;供各位參考。 注意??&#xff1a;MacBook (M4)使用/bin/zsh作為默認Shell&#xff0c;其對應的配置文件為~/.zshrc 參考文檔&#xff1a; HEXO系列教程 | 使用GitHub部署靜態博客HEXO | 小白向教程 文…

運維面試題(十一)

1.如果一個硬盤 IO 時阻塞了&#xff0c;會發生什么情況&#xff1f; 進程/線程掛起&#xff1a;發起I/O操作的進程或線程會被操作系統置為阻塞狀態&#xff08;等待狀態&#xff09;&#xff0c;直到I/O完成。CPU資源釋放&#xff1a;阻塞的線程會讓出CPU&#xff0c;操作系統…

sql2022 復制 事務級別發布后無法刪除

Cannot execute as the database principal because the principal "dbo" does not exist, this type of principal cannot be impersonated, or you do not have permission. 用SA用戶登錄執行下列語句 USE [xxxxx] GO EXEC dbo.sp_changedbowner loginame Nsa, …

合規+增效 正也科技攜智能營銷產品出席中睿論壇

正也科技作為醫藥數字化領域的標桿企業&#xff0c;受邀參展第二屆中睿醫健產業企業家年會暨第十三屆中睿醫藥新春論壇&#xff0c;本次論壇以“合力啟新程”為主題&#xff0c;吸引了800多位醫藥健康企業的董事長、總經理參與&#xff0c;并通過主論壇、分論壇、路演等形式探討…

ubuntu 安裝 postgresql

在 Ubuntu 系統中安裝 PostgreSQL 的步驟如下&#xff1a; 步驟 1&#xff1a;更新軟件包列表 sudo apt update步驟 2&#xff1a;安裝 PostgreSQL Ubuntu 默認倉庫包含 PostgreSQL&#xff0c;直接安裝&#xff1a; sudo apt install postgresql postgresql-contrib -ypost…

智能巡檢機器人:2025年企業安全運維的“數字哨兵“

文章目錄 一、2025年&#xff0c;為什么企業需要智能巡檢機器人&#xff1f;二、2025年智能巡檢機器人的六大核心價值三、2025行業落地實景1. 電網系統——"巡線鷹"集群作戰2. 化工園區——"防爆衛士"全天候守護3. 數據中心——"冷血偵探"精準運…

K8S學習之基礎五十一:k8s部署jenkins

k8s部署jenkins 創建nfs共享目錄&#xff0c; mkdir -p /data/v2 echo /data/v2 *(rw,no_root_squash) > /etc/exports exportfs -arv創建pv、pvc vi pv.yaml apiVersion: v1 kind: PersistentVolume metadata:name: jenkins-k8s-pv spec:capacity:storage: 1GiaccessMod…

Vue實現的表格多選方案支持跨頁選擇和回顯功能

以下是純Vue實現的表格多選方案&#xff08;不依賴UI庫&#xff09;&#xff0c;支持跨頁選擇和回顯功能&#xff1a; <template><div class"custom-table"><!-- 表格主體 --><table><thead><tr><th><input type"…

Oracle 19C 備份

在 Oracle 19c 中&#xff0c;備份數據庫通常使用 RMAN&#xff08;Recovery Manager&#xff09; 工具&#xff0c;它是 Oracle 提供的官方備份和恢復工具。以下是通過 RMAN 備份 Oracle 19c 數據庫的詳細步驟和命令。 一、RMAN 基本概念 RMAN 是 Oracle 的備份和恢復工具&am…

Elasticsearch:人工智能時代的公共部門數據治理

作者&#xff1a;來自 Elastic Darren Meiss 人工智能&#xff08;AI&#xff09;和生成式人工智能&#xff08;GenAI&#xff09;正在迅速改變公共部門&#xff0c;從理論探討走向實際應用。正確的數據準備、管理和治理將在 GenAI 的成功實施中發揮關鍵作用。 我們最近舉辦了…

AT24Cxx移植第三方庫到裸機中使用

簡介 MCU : STM32F103C8T6 庫: HAL庫裸機開發 EEPROM : AT24C02, 256Byte容量&#xff0c;I2C接口 電路圖 AT24C02 電路圖 電路圖引用 裸機直接讀寫 // 寫入數據到 EEPROM HAL_StatusTypeDef EEPROM_WriteByte(uint16_t MemAddress, uint8_t Data) {// 發送數據uint8_t …

算法刷題記錄——LeetCode篇(1.3) [第21~30題](持續更新)

更新時間&#xff1a;2025-03-29 LeetCode題解專欄&#xff1a;實戰算法解題 (專欄)技術博客總目錄&#xff1a;計算機技術系列目錄頁 優先整理熱門100及面試150&#xff0c;不定期持續更新&#xff0c;歡迎關注&#xff01; 21. 合并兩個有序鏈表 將兩個升序鏈表合并為一個…

常用數據庫

模式的定義于刪除 1.定義模式 CREATE SCHEMA [ <模式名> ] AUTHORIZATION < 用戶名 >;要創建模式&#xff0c;調用該命令的用戶必須擁有數據庫管理員權限&#xff0c;或者獲得了DBA授權 eg:為用戶WANG定義一個模式S-C-SC CREATE SCHEMA "S-C-SC" AUT…

Processor System Reset IP 核 v5.0(vivado)

這個IP的作用&#xff0c;我的理解是&#xff0c;比普通按鍵復位更加高效靈活&#xff0c;可以配置多個復位輸出&#xff0c;可以配置復位周期。 1、輸入信號&#xff1a; 重要的信號有時鐘clk信號&#xff0c;一般連接到系統時鐘&#xff1b;輸入復位信號&#xff0c;一般是外…