以太坊账户详解

news/2024/12/24 9:03:14 标签: 以太坊

文章目录

  • 一、账户基本概念
    • 1.1 外部账户
    • 1.2 合约账户
    • 1.3 差异对比
  • 二、帐户创建
    • 2.1 外部账户创建
    • 2.2 合约账户创建
  • 三、账户数据结构
    • 3.1 账户状态
    • 3.2 账户状态结构

对比比特币的 “UTXO” 余额模型,以太坊使用“账户”余额模型。 以太坊丰富了账户内容,除余额外还能自定义存放任意多数据。 并利用账户数据的可维护性,构建智能合约账户。

实际上以太坊是为了实现智能合约而提炼的账户模型。 以账户为单位,安全隔离数据。 账户间信息相互独立,互不干扰。 再配合以太坊虚拟机,让智能合约沙盒运行。

以太坊作为智能合约操作平台,将账户划分为两类:外部账户(EOAs)和合约账户(contract account)。

一、账户基本概念

1.1 外部账户

外部账户(external owned accouts)是由我们通过私钥创建的账户。 是真实世界的金融账户的映射,拥有该账户私钥的任何人都可以控制该账户。 如同银行卡,到ATM机取款时只需要密码输入正确即可交易。 这也是人类与以太坊账本沟通的唯一媒介,因为以太坊中的交易需要签名, 而只能使用拥有私有外部账户签名。

外部账户特点总结:

  1. 拥有以太余额。
  2. 能发送交易,包括转账和执行合约代码。
  3. 被私钥控制。
  4. 没有相关的可执行代码。

1.2 合约账户

合约账户则是含有合约代码的账户。 被外部账户或者合约创建,合约在创建时被自动分配到一个账户地址, 用于存储合约代码以及合约部署或执行过程中产生的存储数据。 合约账户地址是通过SHA3哈希算法产生,而非私钥。 因无私钥,因此无人可以拿合约账户当做外部账户使用。 只能通过外部账户来驱动合约执行合约代码。

合约账户特点总结:

  1. 拥有以太余额。
  2. 有相关的可执行代码(合约代码)。
  3. 合约代码能够被交易或者其他合约消息调用。
  4. 合约代码被执行时可再调用其他合约代码。
  5. 合约代码被执行时可执行复杂运算,可永久地改变合约内部的数据存储。

1.3 差异对比

综上,下面表格列出两类账户差异,合约账户更优于外部账户。 但外部账户是人们和以太坊沟通的唯一媒介,和合约账户相辅相成。

比较项外部账户合约账户
私钥 private Key
余额 balance
代码 code
多重签名
控制方式私钥控制通过外部账户执行合约

上面有列出多重签名,是因为以太坊外部账户只由一个独立私钥创建,无法进行多签。 但合约具有可编程性,可编写符合多重签名的逻辑,实现一个支持多签的账户。

二、帐户创建

2.1 外部账户创建

当你想要创建一个帐户时,大多数库将生成一个随机的私钥。

私钥由 64 个十六进制字符组成,可以用密码加密保存。

例如:

fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036415f

使用椭圆曲线数字签名算法从私钥生成公钥。 通过获取公钥 Keccak-256 哈希的最后 20 个字节并校验码前面添加 0x,可以为帐户获取公共地址。

// PubkeyToAddress 公钥转地址方法
func (p *PublicKey) ToAddress() Address {
	pubBytes := p.FromECDSAPub()
	i := sha3.Keccak256(pubBytes[1:])[12:]
	return BytesToAddress(i)
}

地址具体生成细节可参考: 以太坊地址生成

下面是使用 GETH 的 personal_newAccount 在控制台中创建一个帐户的例子

> personal.newAccount()
Passphrase:  输入账户密码
Repeat passphrase:  再次输入账户密码
"0x0dd26b4f63f9362314b708816a6aa18a04442679"  得到账户地址

> personal.newAccount(123456)
"0x5942bd5c577b569e27dd789b50b7d8824fe399ea"

geth指令详情可查看 geth文档

2.2 合约账户创建

下面是合约地址生成算法:Keccak256(rlp([sender,nonce])[12:]

// https://github.com/ethereum/go-ethereum/blob/master/crypto/crypto.go

func CreateAddress(b common.Address, nonce uint64) common.Address {
    data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
    return common.BytesToAddress(Keccak256(data)[12:])
}

因为合约由其他账户创建,因此将创建者地址和该交易的随机数进行哈希后截取部分生成。

特别需要注意的是,在EIP1014中提出的另一种生成合约地址的算法。 其目的是为状态通道提供便利,通过确定内容输出稳定的合约地址。 在部署合约前就可以知道确切的合约地址。下面是算法方法:

keccak256( 0xff ++ address ++ salt ++ keccak256(init_code))[12:]

// https://github.com/ethereum/go-ethereum/blob/master/crypto/crypto.go

// CreateAddress2 creates an ethereum address given the address bytes, initial
// contract code hash and a salt.
func CreateAddress2(b common.Address, salt [32]byte, inithash []byte) common.Address {
	return common.BytesToAddress(Keccak256([]byte{0xff}, b.Bytes(), salt[:], inithash)[12:])
}

三、账户数据结构

3.1 账户状态

账户的状态(Acccount State)描述了一个账户当前的情况。以太坊公链时时刻刻跟踪并维护着每一个账户的状态。 一个账户在初次接收或者发出交易后,都会形成初始状态。随着时间的推移,每次针对该账户的交易将不断修改其状态。

注:总结而言,每一个账户在数据结构上具有两个元素:一个公开地址,一个与该地址关联的状态。

在程序逻辑上两类账户的数据结构一致,包含四大元素:

  1. nonce:已执行交易总数,用来标示该账户发出的交易数量;
  2. balance:持币数量,记录用户的以太币余额;
  3. storageRoot hash:存储区的哈希值,指向智能合约账户的存储数据区;
  4. code hash:代码区的哈希值,指向智能合约账户存储的智能合约代码。
//github.com/ethereum/go-ethereum/core/types/account.go

type Account struct {
    Nonce    uint64
    Balance  *big.Int
    Root     common.Hash
    CodeHash []byte
}

以太坊数据以账户为单位组织,账户数据的变更引起账户状态变化。 从而引起以太坊状态变化。

以太坊状态数据:

基于状态机模型,以太坊网络已变成一个依靠矿工维护的去中心化的大型状态机。在任意时刻,只会处于一个状态中,全世界唯一的状态。我们把这个状态机,称之为以太坊世界状态,代表着以太坊网络的全局状态。

世界状态(state)由无数的账户信息组成,每个账户均存在一个唯一的账户信息。账户信息中存储着账户余额、Nonce、合约哈希、账户状态等内容,每个账户信息通过账户地址影射。 从创世状态开始,随着将交易作为输入信息,在预设协议标准(条件)下将世界态推进到下一个新的状态中。

世界状态中存储了哪些内容:

首先,以太坊中有两种级别的状态,一个是顶级的世界状态,另一个是账户级的账户状态。账户状态中存储账户信息:

  1. nonce: 这个值等于由此账户发出的交易数量,或者由这个账户所创建的合约数量(当这个账户有关联代码时)。
  2. balance: 表示这个账户账户余额。
  3. storageRoot: 表示保存了账户存储内容的 MPT 树的根节点的哈希值。
  4. codeHash: 表示账户的 EVM 代码哈希值,当这个地址接收到一个消息调用时,这些代码会被执行; 它和其它字段不同,创建后不可更改。如果 codeHash 为空,则说明该账户是一个简单的外部账户,只存在 nonce 和 balance。

在程序逻辑上两类账户的数据结构一致:

//github.com/ethereum/go-ethereum/core/types/account.go

type Account struct {
    Nonce    uint64
    Balance  *big.Int
    Root     common.Hash
    CodeHash []byte
}

但在数据存储上稍有不同, 因为外部账户无内部存储数据和合约代码,因此外部账户数据中 StateRootHashCodeHash 是一个空默认值。 一旦属于空默认值,则不会存储对应物理数据库中。 在程序逻辑上,存在code则为合约账户。 即 CodeHash 为空值时,账户是一个外部账户,否则是合约账户。

3.2 账户状态结构

下图显示了外部账户与合约账户的账户状态。

外部账户与合约账户的账户状态


http://www.niftyadmin.cn/n/5797601.html

相关文章

【只生一个好 - 单例设计模式(Singleton Pattern)】

单例设计模式 单例设计模式(Singleton Pattern)talk is cheap, show you my code饿汉式懒汉式双重检查锁定静态内部类枚举 总结 单例设计模式(Singleton Pattern) 单例设计模式(Singleton Pattern&#xf…

[spring]处理器

我们可以通过spring来管理我们的类,之后我们可以通过spring的容器来获取我们所需要的Bean类对象。Spring的处理器是Spring对外开发的重要扩展点,它允许我们介入到Bean的整个实例化流程中来,可以动态添加、修改BeanDefinition、动态修改Bean 首…

img向后端获取图片时怎么解决需携带token的问题

1、 问题: 我在vue3的一个项目,需要使用img标签获取后端的图片,但是需要在请求头添加token。尝试发现这种请求不经过axios的拦截请求,且不能以参数的方式携带token(不安全) 2、解决方法: 自定义…

青少年编程与数学 02-004 Go语言Web编程 18课题、日志记录

青少年编程与数学 02-004 Go语言Web编程 18课题、日志记录 一、日志记录(一)目的(二)内容类型(三)日志记录的格式 二、Go Web 日志记录1. 使用标准库 log 包2. 使用 logrus3. 在 Gin 框架中集成 logrus4. 使…

概率论 期末 笔记

第一章 随机事件及其概率 利用“四大公式”求事件概率 习题 推导 全概率公式与贝叶斯公式 伯努利概型求概率 一维随机变量及其分布

使用 pyreqs 快速创建 requirements.txt PyCharm 中 UnicodeDecodeError 问题

目录 什么是 requirements.txt?pyreqs 包分析与解决:应对 JetBrains IDE 中 pipreqs 的 UnicodeDecodeError 问题参考链接 什么是 requirements.txt? 在现代软件开发中,requirements.txt 是用于定义和管理项目依赖的核心文件,主要功能在于…

ADB在浏览器中的革命:ya-webadb项目解析及新手指南

ADB在浏览器中的革命:ya-webadb项目解析及新手指南 ya-webadb ADB in your browser [这里是图片001] 项目地址: https://gitcode.com/gh_mirrors/ya/ya-webadb ya-webadb是一个创新的开源项目,它将Android调试桥(ADB)的功能带入了基于Chromium的浏览器…

vue3和element-plus笔记

对子组件直接使用v-model 子组件内定义如下 const props defineProps({modelValue: {type: String,required: true} }) const emits defineEmits(["update:modelValue"]) 父组件定义如下 <script setup> const deleteId ref(null) </script> <…