1.以太坊基础知识 智能合约: Smart Contracts 是以太坊最重要的特性之一,智能合约让以太坊有更多的可能性,让链不仅仅是链,还可以做很多的应用,智能合约是一段存储在以太坊区块链上的代码,可以自动执行协议的条款。例如,可以创建一个智能合约来管理众筹活动,当众筹达到目标时,合约会自动将资金转给项目方。
单位换算 1 Ether(ETH)=1 x 10^3 Finney = 1 x 10^6 Szabo = 1 x 10^9 Gwei = 1 x 10^12 Wei
以太坊虚拟机(EVM) Ethereum Virtual Machine EVM 是以太坊的核心组件,EVM是一个图灵完备的虚拟机,他可以执行用以太坊脚本语言(Solidity)编写的任意代码。EVM使得智能合约的执行变得可能。也就是说你可以自己的代码放到EVM上去执行,
Dapps(去中心化应用) Decentralized Applications Dapps 是基于以太坊区块链和智能合约构建的应用程序,与传统应用不同,Dapps没有中央服务,数据和应用逻辑是分布式的,这使得他们更加透明和安全。
ERC标准 ERC标准是以太坊的技术规范,用于创建代币和其他合约,最著名的是ERC-20标准,他定义了一种通用的接口,使得代码可以在不同的DApp之间互操作。ERC 提案仅仅是以太坊社区的一些建议和规范,并没有强制执行的机制。开发者可以选择是否遵循这些标准,但遵循 ERC 标准通常会提高智能合约和代币的互操作性,并使其更易于与其他以太坊应用和工具进行集成。大家遵守这个标准之后,更便于代币的流通,
ERC-721是另一种标准,用于创建非同质化代币(NFT)这些代码具有独特性和不可互换性
ETH1.0和ETH2.0
ETH1.0
共识机制:工作量证明(proof of work PoW):ETH1.0使用的是PoW共识机制,旷工通过解决复杂的数学问题来验证交易并添加区块,添加区块的旷工可以获得代币奖励,解决数学难题需要耗费巨大的GPU去运算,这个就是传统意义上的“挖矿”,这个过程能耗非常高,效率很低,且容易造成不平衡,就是拥有更多的显卡的人可以挖的更多的区块,
性能:吞吐量: ETH1.0每秒只能处理大约15-30笔交易,存在拓展性瓶颈,尤其是在网络负载(交易量大)的高峰期,交易确认时间和手续费会显著增加。
安全性:矿池集中化:PoW机制导致矿池的集中化问题,少数大型矿池控制了大部分的算力,有显卡的人就有算力,就可以挖掉区块。可能带来中心化风险
智能合约和DApps:ETH1.0支持智能合约和去中心化应用(DApps),比如去中心化金融(DeFi)、游戏和供应量管理
ETH-2.0
共识机制: 权益证明(Proof of Stake, PoS):ETH2.0 使用PoS共识机制,验证者通过质押ETH来获得验证区块的机会。PoS大幅减少能耗,提高了效率和安全性。比如说全网有10个验证者,每个人质押不同数量的代币来获得出块的机会
,最后出块的人会奖励代币的
性能:
分片链(Sharding chains ):ETH2.0引入了分片技术,通过将区块链分成多个并行链(分片 shard),每个分片处理不同的交易和智能合约,从而显著提高了网络的吞吐量和处理能力
信标链(Beacon Chain):ETH2.0的核心链,负责协调分片和验证者活动,确保整个网络的同步和共识,信标链负责验证和维护网络的共识,并引入了验证者角色。信标链通过随机性机制来选择验证者参与区块的提议和验证。这种随机性确保了公平性和安全性,并防止潜在的攻击行为。信标链还引入了轮次(Epoch)的概念,将时间划分为较长的周期,以便进行验证者的轮换和共识机制的调整
安全性:去中心化和抗攻击性:PoS机制下,恶意攻击者需要持有大量的ETH,成本高昂,使得网络更安全,此外,前面说的信标链通过随机性机制来选择验证者参与区块提议,有随机性,进一步分散风险。
智能合约和DApps:ETH2.0保留了对智能合约的DApps的支持,同事通过更高的吞吐量和更低的交易费用,提升了用户体验和应用性能。
过渡过程:合并(The Merge):ETH2.0并不是从零开始的新区块链,而是对ETH1.0的升级而来,合并将ETH1.0的现有链和ETH2.0的PoS链结合,实现无缝过渡。
主要改进和优点
能耗:ETH2.0通过 PoS 机制大幅减少了能源消耗,相比 PoW 机制更环保(降交易费了)
扩展性:分片技术和信标链的引入显著提高了网络的扩展性和吞吐量(大哥带小弟)
去中心化:ETH2.0通过随机选择验证者和经济激励机制,增强了去中心化程度和安全性(随机验证者,叫你们挖挖挖矿,挖不到了吧)
经济模型:ETH2.0引入了新的经济模型,通过质押和奖励机制,进一步激励网络参与者,提高网络的稳定性和安全性(验证者和抵押、奖励和惩罚、利息和通胀、质押和解之一,压币有收益,做恶有惩罚)
1.3 ETH2.0中的Epoch,Slot,Block和Block状态
ETH2.0 按照epoch 出块 (一个epoch一个纪元)
每一个epoch有32个slot (slot 时隙)
每一个slot 可以承载1个块
Slot(时隙)
定义:Slot 是以太坊2.0中最基本的时间单位,每个slot 都有一个指定的时间长度。在每个slot 中,可能会有一个区块被提议并添加到链中。
时间长度:一个slot 的长度为12秒。这意味着没12秒会有一个新的slot。
功能:在每个slot 中,网络中的验证者将有机会提议一个新的区块。这些提议者是通过权益证明(PoS)随机选择的。
Epoch(纪元)
定义:Epoch 是由多个连续的slot 组成的更长时间段。在Eth2.0中,Epoch 用于管理和组织验证者的活动。
组成:一个Epoch 由 32个 slot 组成。
时间长度:由于一个slot 是12秒,一个Epoch 的总长度就是12 * 32=384秒(6.4分钟)
功能:Epoch 是用来实现共识机制的一部分。在每个Epoch 结束时,网络会进行状态和共识的检查和调整,包括对验证者的奖励和惩罚。
Block(区块)
定义:Block 区块一个是包含交易和其他所有相关数据的记录单元。所有的数据都会包在这个块中,在ETH2.0中,每个 slot 可能会有一个区块被提议,只是提议,但不能保证每个 slot 都有区块的。
内容:一个区块包含区块头
、交易列表
、状态根哈希
、签名
等数据。
创建过程:在每个slot 开始
时,网络会随机选出一个验证者
来提议
区块。该验证者将创建一个包含新交易和其他必要信息的区块,并广播到全网络上。
Safe (安全) “Safe”
状态指的是一个区块已经被多数验证者接受和认可 ,并且他很可能 会成为最终的区块,但还没有达到 完全最终确定的状态。
条件:一个区块在被认为是“safe”时,意味着它已经收到了足够多的验证者投票(attestations),通常超过了一个特定的阈值,但还没有达到最终确定的标准。
安全性:在“safe”状态下,区块的存在是相对安全的,不太可能被回滚或者被另一个不同的区块链分支所替代,也就是说不太可能提议了另一个区块。
作用:这个状态用来提高网络对区块的信任度,即是在它还没有被完全最终确定之前。它帮助节点和用户判断哪些区块在短期内是可信的,一个中间状态
Finalized(最终确定)
“Finalized”状态指的是一个区块已经被永久地添加到区块链中,并且不可能被回滚或替代。这是区块链中最强的确认状态。
条件:一个区块被认为是“finalized”时,必须通过了严格的共识验证,通常需要超过2/3的验证者投票同意。具体来说两个联系的epoch被最终确定时,意味着在这两个epoch之间的所有区块都被最终确定。
安全性:一旦区块达到“finalized”的状态,它就不可逆转,保证了区块链的最终一致性和数据的永久性。这种状态防治了分叉和双花攻击的可能性。
作用:最终确定的区块为用户和应用提供了最高级别的交易安全性和网络信任度。
1.4 以太坊钱包确认位
最终确认位之前的交易会被并入系统
2.离线地址生成 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package mainimport ( "encoding/hex" "fmt" "github.com/ethereum/go-ethereum/crypto" "github.com/tyler-smith/go-bip32" "github.com/tyler-smith/go-bip39" )func main () { fmt.Println("开始" ) entropy, _ := bip39.NewEntropy(128 ) fmt.Println("entroy:" , entropy) mnemonic, _ := bip39.NewMnemonic(entropy) fmt.Println("mnemonic:" , mnemonic) seed := bip39.NewSeed(mnemonic, "" ) fmt.Println("seed" , seed) masterKey, _ := bip32.NewMasterKey(seed) fmt.Println("masterKey" , masterKey) key, _ := masterKey.NewChildKey(bip32.FirstHardenedChild + 44 ) key, _ = key.NewChildKey(bip32.FirstHardenedChild + uint32 (60 )) key, _ = key.NewChildKey(bip32.FirstHardenedChild + uint32 (0 )) key, _ = key.NewChildKey(uint32 (0 )) key, _ = key.NewChildKey(uint32 (0 )) ethPrivateKey := hex.EncodeToString(key.Key) fmt.Println("privateKey" , ethPrivateKey) ethPublicKey := hex.EncodeToString(key.PublicKey().Key) fmt.Println("ethPublicKey" , ethPublicKey) compressPubKey, _ := crypto.DecompressPubkey(key.PublicKey().Key) fmt.Println("compressPubKey" , compressPubKey) ethAddre := crypto.PubkeyToAddress(*compressPubKey).Hex() fmt.Println("ethAddre" , ethAddre) }
用python写一遍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from bip32utils import BIP32Keyfrom bip32utils import BIP32_HARDENfrom bip39 import Mnemonicfrom ethereum.utils import privtoaddrdef eth_wallet (): entropy = Mnemonic().generate_entropy(128 ) mnemonic = Mnemonic().to_mnemonic(entropy) seed = Mnemonic().to_seed(mnemonic) bip32_key = BIP32Key.fromEntropy(seed) path = "m/44'/60'/0'/0/0" key = bip32_key.ChildKey( BIP32_HARDEN + 44 ).ChildKey( BIP32_HARDEN + 60 ).ChildKey( BIP32_HARDEN + 0 ).ChildKey( 0 ).ChildKey( 0 ) eth_private_key = key.WalletImportFormat() eth_address = privtoaddr(key.PrivateKey().to_string()).hex () print ("助记词:" , mnemonic) print ("私钥:" , eth_private_key) print ("地址:" , eth_address) eth_wallet()
3.离线交易签名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 package mainimport ( "context" "fmt" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "log" "math/big" )var ( PrivateKeyStr = "c535faxxxxxxxxxxxxxxxxxxxxxxxxx" fromAddrStr = "0xa3856a939A623EdBde8f908037d3F33FceBC5408" toAddrStr = "0x38B59D6D4ef6A4991926Cf04c7c2092a0E86140F" ethUrl = "https://eth-sepolia.g.alchemy.com/v2/fzgfj4QuLlNEyn2LrLZsseBAClGdMnyP" )func main () { privateKey, _ := crypto.HexToECDSA(PrivateKeyStr) toAddr := common.HexToAddress(toAddrStr) client, err := ethclient.Dial(ethUrl) if err != nil { log.Fatal(err) } fromAddr := common.HexToAddress(fromAddrStr) balance, err := client.BalanceAt(context.Background(), fromAddr, nil ) if err != nil { log.Fatal(err) } fmt.Printf("Account balance: %d\n" , balance) block, err := client.BlockByNumber(context.Background(), nil ) if err != nil { log.Fatal(err) } fmt.Printf("Latest block: %d\n" , block.Number().Uint64()) nonce, err := client.PendingNonceAt(context.Background(), fromAddr) if err != nil { log.Fatal(err) } fmt.Printf("nonce: %d\n" , nonce) gasPriceNow, err := client.SuggestGasPrice(context.Background()) if err != nil { log.Fatalf("Failed to retrieve gas price: %v" , err) } fmt.Printf("gasPriceNow: %d\n" , gasPriceNow) gasPrice := big.NewInt(2000000000 ) amount := big.NewInt(1100000000000000 ) gasLimit := uint64 (21208 ) data := []byte ("Hello, World!" ) tx := types.NewTx(&types.LegacyTx{ Nonce: nonce, To: &toAddr, Value: amount, Gas: gasLimit, GasPrice: gasPrice, Data: data, }) chainID, err := client.NetworkID(context.Background()) if err != nil { log.Fatal(err) } signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey) if err != nil { log.Fatal(err) } err = client.SendTransaction(context.Background(), signedTx) if err != nil { log.Fatal(err) } fmt.Printf("转账交易已发送,交易哈希\n:%s\n" , signedTx.Hash().Hex()) }
4.以太坊 RPC 接口 这里在做的就是扫链,钱包都要扫链
4.1.检查网络的 RPC 接口是否可以 先调接口看下链是不是处于活跃的状态,如果是才能继续下去
4.2.获取最新块高 调eth block number获取到最新的快高是多 比如说当前是第101个块
4.3.根据块高获取块里面的信息 从我之前上一次扫到的块(比如说第100)到我这一次扫到的块(第101)之间,扫到交易就下一步
4.4.根据交易 Hash 获取交易详情 扫到交易之后就要去解析交易。然后判断from 和to 的地址是什么地址,来判断是转出还是转入是归集还是冷热互转
4.5.获取交易状态 解析交易就是获取交易状态是成功的吗,fail 还是 success
4.6. 获取签名需要的参数 Nonce 4.7. 获取签名需要的参数 Gas
这里其实不要用你获取到的当前gasprice,你可以调高一点,更容易被链上处理
4.8. 发送交易到区块链网络
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 package mainimport ( "context" "fmt" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "log" "math/big" )var ( PrivateKeyStr = "c535facd6873ca2b3718e3ede4f626ae126c99b4a26b4354da704d8dc78b43c1" fromAddrStr = "0xa3856a939A623EdBde8f908037d3F33FceBC5408" toAddrStr = "0x38B59D6D4ef6A4991926Cf04c7c2092a0E86140F" ethUrl = "https://eth-sepolia.g.alchemy.com/v2/fzgfj4QuLlNEyn2LrLZsseBAClGdMnyP" )func main () { privateKey, _ := crypto.HexToECDSA(PrivateKeyStr) toAddr := common.HexToAddress(toAddrStr) client, err := ethclient.Dial(ethUrl) if err != nil { log.Fatal(err) } fromAddr := common.HexToAddress(fromAddrStr) balance, err := client.BalanceAt(context.Background(), fromAddr, nil ) if err != nil { log.Fatal(err) } fmt.Printf("Account balance: %d\n" , balance) block, err := client.BlockByNumber(context.Background(), nil ) if err != nil { log.Fatal(err) } fmt.Printf("Latest block: %d\n" , block.Number().Uint64()) nonce, err := client.PendingNonceAt(context.Background(), fromAddr) if err != nil { log.Fatal(err) } fmt.Printf("nonce: %d\n" , nonce) gasPriceNow, err := client.SuggestGasPrice(context.Background()) if err != nil { log.Fatalf("Failed to retrieve gas price: %v" , err) } fmt.Printf("gasPriceNow: %d\n" , gasPriceNow) gasPrice := big.NewInt(2000000000 ) amount := big.NewInt(1100000000000000 ) gasLimit := uint64 (21208 ) data := []byte ("Hello, World!" ) tx := types.NewTx(&types.LegacyTx{ Nonce: nonce, To: &toAddr, Value: amount, Gas: gasLimit, GasPrice: gasPrice, Data: data, }) chainID, err := client.NetworkID(context.Background()) if err != nil { log.Fatal(err) } signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey) if err != nil { log.Fatal(err) } err = client.SendTransaction(context.Background(), signedTx) if err != nil { log.Fatal(err) } fmt.Printf("转账交易已发送,交易哈希\n:%s\n" , signedTx.Hash().Hex()) }
Python代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 from web3 import Web3import json private_key = "c535facd68xxxxxxxxxxx26ae126c99bda704d8dc78b43c1" from_address = "0xa3856a9xxxxxxxxe8f90835408" to_address = "0x38B59D6Dxxxxxxxx6Cf04c786140F" eth_url = "https://eth-sepolia.g.alchemy.com/v2/fzgfj4QuLlNEyn2LrLZsseBAClGdMnyP" web3 = Web3(Web3.HTTPProvider(eth_url))if not web3.is_connected(): print ("Failed to connect to Ethereum node." ) exit() balance = web3.eth.get_balance(from_address)print (f"Account balance: {web3.from_wei(balance, 'ether' )} ETH" ) block = web3.eth.get_block('latest' )print (f"Latest block: {block.number} " ) nonce = web3.eth.get_transaction_count(from_address)print (f"Nonce: {nonce} " ) gas_price = web3.eth.gas_priceprint (f"Gas price: {web3.from_wei(gas_price, 'gwei' )} Gwei" ) transaction = { 'to' : to_address, 'value' : 1100000000000000 , 'gas' : 21320 , 'gasPrice' : 2000000000 , 'nonce' : nonce, 'data' : web3.to_hex(b"Hello, World! python" ), 'chainId' : web3.eth.chain_id } signed_tx = web3.eth.account.sign_transaction(transaction, private_key)print (f"Transaction hash: {signed_tx.hash .hex ()} " ) tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction)print (f"Transaction sent, hash: {tx_hash.hex ()} " )
5.中心化钱包 5.1 离线地址生成