<style> .markdown-body { max-width: 1280px; } </style> # 面試補充 **覺得在每一份工作都得到讓我成長的養分 希望有機會能夠加入貴公司的團隊 能夠學習到更多新知和精進各種技能** # 自我介紹 - **擁有資深的golang語言開發經驗 (8年)** - **曾參予交易所 和 捕魚和棋牌類 相關遊戲等大型微服務架構設計** - **擅長 棋牌類,SLOT 捕魚機 遊戲開發 ( 熟悉鬥地主AI模型 )** - **擅長開發捕魚遊戲機率 (撰寫核心機率, 反覆大數據測試驗證RTP )** - **有獨立開發 登入Server和 遊戲Server 實務經驗, 可橫向擴充, 且有上市場驗證** [server架構圖](#理想的server架構圖) - **熟系高併發大型微服務系統架構** - **熟悉 mysql redis rabbitmq, kafka mongodb** - **會使用 docker-compose 搭建各種服務** - **將程式專案寫 dockerfile, docker build image 丟到 dockerhub 或自己架設的私有倉庫** - **使用 gin 或 websocket gorilla 來開發微服務** - 交易所 1年 實務經驗 - 會區塊鍊錢包 bitcoin eth(erc20), 也會自架節點 (獲取區塊鍊高度 打包交易上傳到鏈上, 持續監聽區塊確認高度, 完成此交易) - 目前在研習智能合約開發 [發佈時的已部署的合約]( [0xbb6fB7B6Ca2A864776d4eaD564B7Ba98c1AaBABf](https://sepolia.etherscan.io/tx/0x16f80ac1066e84eeeacf826ba81680b819a5ad13fb8b107533824cf51818cbb8) 關於CI/CD 這塊 - 會使用 docker-compose 搭建各類linux服務 - 會針對程式專案寫DockerFile 將 image 上傳到 dockerhub 或 自建私有倉庫 - 會搭建 jenkin 並且 寫 腳本 來 佈署 微服務 - 專案導入docker, 打包docker image 上傳到dockerhub或自建的harbor倉庫 - 基本GCP搭建VM和相關服務設定 # 記憶體狀態管理, channel, goroutine, 及這些東西常用的應用情境. **線上範例** [websocket + channel 應用範例](https://github.com/lion770818/go_websocket_ez) [持續優化的DDD微服務](https://github.com/lion770818/marketplace_server) [erc20錢包](https://github.com/lion770818/webapi_erc20) - 底層框架使用 gorilla - 每個 websocket session 都配置 讀寫通道buffer, buffer大小為100, 來依序接收和依序傳送數據 - 將接收到的訊息塞入 channel(job queue) - 收到 channel 資料後, 解析 封包資料, 依序執行封包的 cmd 命令 - 收到 close_server 封包後, 會透過通道關閉此 websocket連線資源 - 每個 登入的 user, 都配置 100個通道buffer, 用在跟平台錢包溝通(儲幣,提幣,餘額請求) * 主要是在鈊象電子學習了 client繪圖引擎底層 和 tcp協定的 client和server框架 * 在永勝雲端 有了獨自開發一整套 可橫向擴充 game_server 架構的 機會 [理想的server架構圖]([理想的server架構圖](#理想的server架構圖) * 在新禾學習了 GRPC 和 stored procedure, 也學習了其他大師設計的 server 架構 * 在亞太滿譽 學習了 區塊鍊節點架設 區塊鍊錢包API溝通相關 智能合約撰寫和使用, 還有各種 docker-compose 啟動的服務架設 * 自我學習 dockerfile 和 打包image --- **關於map** | 種類 | 說明 | 支援多執行序 | | -------- |:---------------------- | ------------------------------------------------------- | | map | 一般的map | 否, 在goroutine直接引用map, 會引發panic, 需搭配鎖來使用 | | sync.map | 支援goroutine安全的map | 是, 但因為頻繁的鎖, 效能會低落, 適用於讀多寫少的場景。對於寫多的場景, 還是要使用一般map+鎖 | **關於mutex** | 鎖 | 說明 | 效能 | | ------------ |:---------- |:------------------------------------------------------------------------------------------------------------------------------------- | | sync.Mutex | 一般互斥鎖 | 性能較差, 因為 針對 大量變數讀取時的鎖, 會瞬間阻塞一下 | | sync.RWMutex | 讀寫互斥鎖 | 性能較好, 因為 針對 大量變數讀取時的鎖, 彼此讀鎖不會阻塞, 但寫鎖lock時, 讀寫鎖都阻塞等待寫鎖unlock, 可以用在 讀取用戶排行榜或配桌列表 | sync.RWMutex 針對鎖的操作有更細部的控制 針對讀多寫較少的場景, 可以大幅度的提升效能 **讀寫鎖範例** ```go= // 使用讀鎖 加快效能 func (mgr *TableManager) GetTableList() []*Table { mgr.EGMLocker.RLock() defer mgr.EGMLocker.RUnlock() // 讀取配桌列表 (因為是讀鎖, 所以不會阻塞, 可以讓多人同時讀取 配桌列表 return mgr.ListTableMap } // 使用寫鎖, 阻塞其他鎖 func (mgr *TableManager) SetId( userId int, data string) { mgr.EGMLocker.Lock() defer mgr.EGMLocker.Unlock() // 因為是寫鎖, 所以這邊同一時間只有一個 goroutine能夠使用此變數, 其他晚來的會阻塞等待(讀寫都會阻塞等這邊完成) mgr.ListTableMap[userId] = data } ``` 由於 **頻繁鎖還是會造成效能低落, 還有不易維護的問題** 所以如果是有順序的工作, 可以使用 channel 來達成 優點是 - 效能提升 - 容易維護 - 在整個關鍵的地方上鎖, 鎖的數量變少, 不容易造成死鎖 例如 websocket gorilla 就是一個好例子 每個websocket client 都配置 一個 讀取和寫入 chan buffer 來依序 接收或傳送 資料並處理 [細節參考剛寫的 websocket + channel 應用範例](https://github.com/lion770818/go_websocket_ez) ```go= websocket 針對每個 客戶端 session 物件初始化時, 分別建立一個讀寫通道 s.writeChan = make(chan []byte, 1000) s.readChan = make(chan []byte, 1000) // 將要返回給client的資料塞入通道 func (s *Session) Send(data []byte) { s.writeChan <- data } func (s *Session) Run() { for { select { case <-s.exitChan: // 收到離開的訊號 return case sendData := <-s.writeChan: // 通道收到資料後, 寫到 websocket底層送出 err := s.ws.WriteMessage(websocket.TextMessage, sendData) if err != nil { log.Println("websocket send message error", err) } } } } ``` # 對於架構面, 例如 queue, cache, db, 實作層面和適用情境. **redis 常用資料結構** | 資料結構 | 內容 | | -------- |:--------------------------------------------------------------------------------- | | string | 單一的快取結果,不與其他的Key Value 之間有聯繫 ( 當成map結構使用 ) | | hash | 一個Object 包含有許多屬性,而這些屬性都需要單獨儲存 ( 例如儲存登入 user ) | | list | 一個Object 包含很多數據,且這些數據允許重複、要求有順序性 (例如工作queue) | | set | 一個Object 包含很多數據,不要求數據有順序,但是不允許重複 | | zset | 一個Object 包含很多數據,而這些數據本身還包含一個權重值,可以利用這個權重值來排序 | queue 主要用在需要順序執行的工作 例如跟第三方錢包的 提幣/充值/取得餘額, 可以使用下列方式來達成 job queue - 使用 channel buffer 來達成 依序處理錢包 - 使用 rabbitMq 來達成 例如 login_server ---> MQ ----> money_server ----> 第三方wallet - 使用 redis 的 list ![image](https://hackmd.io/_uploads/rJ30Hscmp.png) 使用 cmd 來示範 list 當作 queue ( lpush rpop ) ![image](https://hackmd.io/_uploads/HJJ3Diq7T.png) redis 拿來當緩存cache 是目前主流的方式 除了可以讓每隻server都可以達到無狀態, 方便日後打包成docker image, 推進到k8s上 ```go= # 將登入的user 寫入到 new_user 資料夾內 key = new_user:userId hset new_user:123 data '{ "token": "aaaa", "userId": 123 }' # 設定倒數秒數, 時間到自動銷毀 EXPIRE new_user:123 180 ``` ![image](https://hackmd.io/_uploads/rkPO5jq7T.png) redis 的 pub 和 sub 適合用來 廣播某人中JP的通知, 但因為會漏失, 所以適合廣播不重要的訊息 redis 的分佈式鎖, 可以用來避免 redis 上的資料競爭 常見的舊方式可以使用 setnx + expire 兩個指令來上鎖, 但分兩個指令, 如果中間panic就死鎖 ( 未來官方可能會拋棄相關語法 ) 刪除鎖就 del 新版redis 官方建議方式是使用 set NX EX 指令 假設 ttl=30秒, 用set指令來取代 setnx set key value ex 30 nx set key value ex 30 set key value ex 30 nx = setnx + expire 都有不同效果 **分布式鎖使用範例** key = lock, value = 自產唯一uuid, ttl = 30s - set lock uuid ex 30 nx - 檢查回傳值是否為ok, 如果ok代表可以去存取 **redis共享變數**, 如果是null, 代表有人正在使用共享資源 請稍後在測試 - 存取redis共享變數 - 在30s之前使用完畢的話, 要 delete lock 把鎖給手動釋放 [深度剖析:Redis分散式鎖到底安全嗎?](http://kaito-kidd.com/2021/06/08/is-redis-distributed-lock-really-safe/) #### 高併發 高可用的redis **redis三種模式** - **主從模式** 主(master)掛了 需要手動去切換到從(slave) ![image](https://hackmd.io/_uploads/BJmlxNy9a.png) * **優點** * 架設簡單 * 一個是讀寫分離,分擔 "master" 的讀寫壓力 * 一個是方便做災難復原 * 資料可靠性提升 * **缺點** * 不具備自動容錯和復原功能,主機從機的宕機都會導致前端部分讀寫請求失敗,需要等待機器重啟或手動切換前端的IP才能恢復(也就是要人工介入) * 較難支援線上擴容,在叢集容量達到上限時線上擴容會變得很複雜 * 浪費空間, 同樣的資料儲存在多個slave內 - **哨兵模式** 一個哨兵監控多個Redis 實例, 故障偵測 通知 故障轉移 ![image](https://hackmd.io/_uploads/HJ_ta7J9T.png) * **優點** * 哨兵模式是繼承**主從模式**的,所有主從的優點,哨兵模式都具有。 * 主從可以自動切換,系統更健壯,可用性更高(可以看作自動版的主從複製)。 * **缺點** * 每台Redis 伺服器都儲存相同的數據,很浪費內存。 * Redis較難支援線上擴容,在叢集容量達到上限時線上擴容會變得很複雜。 - **從集模式** 分散式儲存資料, 同樣的資料分布在不同的redis ![image](https://hackmd.io/_uploads/SyewRmy9a.png) * **優點** * 可以横向擴充, 只需連接集中某幾個節點即可 * 利用CRC16演算法, 將資料平均分散在16384哈希槽,保有資料的分散性 * 公式 HASH_SLOT = CRC16(key) / 16384 * **缺點** * 維護較困難 * 某些指令因為跨槽問題, 所以不支援 **redis cluster 利用crc16演算法將資料分散在不同實體** ![image](https://hackmd.io/_uploads/Hk9fjjHrT.png) **redis優化方向** - 開啟慢查詢log - 設定 ttl ( 但不可以過度集中的釋放大量的key ) - 少用複雜或排序的指令, 運算類拉回微服務內 ( set, zadd, keys ) - bigkey ( value 容量別太大 ) - AOF 策略設定錯誤 ( 自己覺得最好是 appendfsync everysec:主執行緒每次寫入操作只寫記憶體就返回,然後由後台執行緒每隔1 秒執行一次刷盤操作(觸發fsync系統呼叫),此方案對效能影響相對較小,但當Redis 宕機時會遺失1 秒的數據) [Redis進階- 效能調優](https://pdai.tech/md/db/nosql-redis/db-redis-x-performance.html) [Redis 數據持久化機制、主從同步原理、常見規範與優化詳解](https://www.readfog.com/a/1688255874946142208) # 自己過往的經驗與知識整理出一些現有痛點的解決方案 在**永勝雲端任職**時 是我第一次獨立製作專案的時機 因為剛到台中的新創公司 我是第一批入職的員工 在了解業務的需求後 也跟老闆提供的技術顧問討論後 製作了一款 可以簡易橫向擴充的捕魚機和SLOT的遊戲架構原型 #### 整個系統我主要撰寫了90%左右的代碼, 剩下的部分是後進同事協助完成 因為當年golang在台中不好招募, 後來只補了兩位golang工程師 [104上的遊戲demo](http://34.80.248.41:888/demo/) #### 理想的server架構圖 ![photo_2024-12-27 14.40.21](https://hackmd.io/_uploads/r1mW2pjBke.jpg) 設計理念就是 * 利用 proxy 來阻擋dot惡意攻擊, dot時, 不斷水平擴充proxy, 可以簡單減輕此區壓力 * proxy 主要來做資料轉拋層, 將不同api請求, 轉交給處理的微服務 * 根據不同需求, 建設不同的微服務 * 利用 redis cluster 來存取運行的快取資料 * 微服務的熱資料都儲存在 redis上, 日後要橫向擴充時會比較好擴充 * 微服務的重要數據儲存在db, 當有用戶來存取時, 先去redis撈取, 如果沒有再去db找, 最後在更新回redis上 * 如果有重要的工作可以提交給 message queue, 來達到 封包資料可靠送到, 資料依序排列處理 的優勢 * 大量的微服務產生的log 可以寫入 mongoDB, 再透過GUI介面來撈取不同等級的log訊息 * 資料庫使用 **讀寫分離** 都對master做寫入, 讀取資料 * 後台報表都對 slave 做讀取 --- **之前壓測過程中發現一些問題** **musql的index有漏加** 利用 explain 來找出那些 sql 語法需要增加 index ![image.png](https://hackmd.io/_uploads/rJ32y157p.png) SELECT * FROM orders WHERE user_id = ? AND status = ? AND created_at >= ? index A : idx_user_id_status_created_at(user_id, status, created_at) 索引的細節 ![image](https://hackmd.io/_uploads/ByJlbha7T.png) explain的結果 ![image](https://hackmd.io/_uploads/rk8Aeh6Q6.png) 聯合索引的規則是 先將聯合索引 **最左邊** 的開始排序 在第一個欄位的排序基礎下, 在使用第二個欄位進行排序 而且索引不能夠跳著 例如 1 3 可以用 explain 來 交叉比對誰的效能較高 key_len決定了索引項目在儲存空間佔用的大小,越小意味著一個磁碟區能夠放置的索引項越多, 從而可以降低B+樹的高度,高度低就意味著查找時所搜尋的路徑越少, 例如一個三層B+樹,從根到葉節點只需2步,而四層就需要3步了, 而搜尋的路徑少就意味著磁碟IO讀取次數少(如果沒有全放內存的話),自然就提高查詢的效率了 **type類型從快到慢:system > const > eq_ref >ref >range > index > ALL** | type | 說明 | | ------ |:------------------------------------------------------------ | | system | 系統表,少量數據,往往不需要進行磁碟IO | | const | 常數連接 | | eq_ref | 主鍵索引(primary key)或非空唯一索引(unique not null)等值掃描 | | ref | 非主鍵非唯一索引等值掃描 | | range | 範圍掃描 | | index | 索引樹掃描 | | ALL | 全表掃描(full table scan) | [explain](https://www.cnblogs.com/bubu99/p/11410194.html) **mysql慢查詢要打開** show variables like 'slow%'; set global slow_query_log = on; ```go= # 開啟慢查詢 slow_query_log = 1 long_query_time = 10 slow_query_log_file =/usr/local/mysql/mysql_slow.log ``` **開啟效能詳情** show variables like '%profiling%'; set profiling = on; show profiles; **mysql的buff要優化 my.cnf** ```go= [mysqld] datadir=C:/Program Files/MariaDB 10.3/data port=3306 # 不可過大(約服務器物理內存大小的80%) #innodb_buffer_pool_size=2020M innodb_buffer_pool_size=5G character-set-server=utf8 max_connections=10000 #column-statistics=0 key_buffer= 4096M bulk_insert_buffer_size = 1G # 查詢緩衝 query_cache_size = 32M # 允許進入查詢緩衝區的最小數據大小 query_cache_limit = 512M # Thread cache的作用在於每次建立新的連線 (Thread) 時,會先看Thread cache 中是否有可用的 Thread,若有則直接取用,若無才重新建立新的連線 thread_cache_size = 8 innodb_flush_log_at_trx_commit=0 innodb_lock_wait_timeout = 100 ###### # 為InnoDB資料表及其索引而保留的RAM記憶體量(默認設置是8MB)。這個參數對速度有著相當大的影響,如果計算機上只運行有MySQL/InnoDB資料庫服務器,就應該把全部記憶體的80%用於這個用途。 #innodb_log_buffer_pool_size = 32M # 開了跑不起來 #innodb_log_file_size = 256M # 事務日誌文件寫操作緩存區的最大長度(默認設置是1MB) innodb_log_buffer_size=16M #innodb_log_files_in_group=3 # 禁止autocommit init_connect='SET autocommit=0' # InnoDB驅動程序能夠同時使用的最大線程個數(默認設置是8)。 #innodb_file_io_threads=8 # InnoDB驅動程序能夠同時使用的最大線程個數(默認設置是8)。 innodb_thread_concurrency=256 # 帶有autoextend屬性的表空間文件每次加大多少兆字節(默認設置是8MB) innodb_autoextend_increment=64M innodb_concurrency_tickets=5000 innodb_old_blocks_time=1000 # innodb_open_files=1000 innodb_stats_on_metadata=0 # innodb_file_per_table=1 #innodb_checksum_algorithm=0 max_connect_errors = 4294967295 #max_allowed_packet = 10M #Net_buffer_length = 32768 query_cache_type = 1 open_files_limit = 1024 read_buffer_size = 131072 # 修改資料插入上限 ( insert 或 update 資料上限 ) 預設值是 16MB max_allowed_packet = 1G thread_stack = 192K thread_cache_size = 8 # This replaces the startup script and checks MyISAM tables if needed # the first time they are touched myisam-recover = BACKUP max_connections = 10000 query_cache_limit = 512M query_cache_size = 32M expire_logs_days = 10 max_binlog_size = 100M [client] port=3306 plugin-dir=C:/Program Files/MariaDB 10.3/lib/plugin ``` 因為使用到 mysql 批次寫入功能 ( 開個 goroutine, 蒐集等待寫入的注單資料, 蒐集約1000筆就整批寫入到sql) **批次寫入sql語法** ```go= INSERT INTO table_name (column_list) VALUES (value_list_1), (value_list_2), ... (value_list_n); ``` 除了基本的buffer條大外, 以下參數需要特別調整 - max_allowed_packet - bulk_insert_buffer_size 大量插入批次緩存 - innodb_flush_log_at_trx_commit - SET autocommit=0 禁止auto commit **利用mysql workbench工具來監測效能** mysql workbench 的 dashborad 面板 裡面有目前sql操作的相關數據 可以用來分析目前批次寫入的狀態 ![image](https://hackmd.io/_uploads/B1T22ljXa.png) **linux上的調整** sudo vim /etc/security/limits.conf ```go= * soft nofile 10240 * hard nofile 10240 ``` # 高併發的mysql **利用主從分離來達到讀寫分離, INSERT 再寫的連線上, 大量的查詢在讀的連線上** 應用例子是 前台使用 master連線在寫入 後台使用 slave 連現在讀取 **利用 分table表功能** ![image](https://hackmd.io/_uploads/HkDPGorra.png) **適度的分庫和分表** --- # clean code - 適當有意義的命名 - 函式一次只做一件事, 且沒有副作用 --- --- # 區塊鏈筆記 [自己整理的區塊鏈知識](https://hackmd.io/2nVt2WcORMS9p152mhZJWw?view) # BTC節點架設 btc 鏈幣種功能 # DOC [RPC API Reference](https://developer.bitcoin.org/reference/rpc/index.html) [比特币 RPC API 目录](http://cw.hubwiz.com/card/c/bitcoin-json-rpc-api/) [Bitcoin Transactions](https://aandds.com/blog/bitcoin-tx.html) [how to send raw transaction BTC using Bitcoin-cli command](https://stackoverflow.com/questions/38493893/heres-how-to-send-raw-transaction-btc-using-bitcoin-cli-command) [create-bitcoin-address-from-ecdsa-publickey](https://github.com/jeffdecola/my-go-examples/blob/a2a1f0255beb/blockchain/create-bitcoin-address-from-ecdsa-publickey/create-bitcoin-address-from-ecdsa-publickey.go) [三种比特币地址格式](https://help.onekey.so/hc/zh-cn/articles/360002057776-%E4%B8%89%E7%A7%8D%E6%AF%94%E7%89%B9%E5%B8%81%E5%9C%B0%E5%9D%80%E6%A0%BC%E5%BC%8F) [第三方付費節點-getblock](https://getblock.io/cn/) **測試鏈** [blockstream testnet](https://blockstream.info/testnet/) # fee 手續費過低 會提幣失敗 fee: 0.000002 fee_ok: 0.00000141 <=== 比他低就容易失敗 fee_fail: 0.00000002 # 一般指令 ## 啟動 / 關閉節點 **啟動, default_port=18332** bitcoind -testnet -conf=/data/btc_data/bitcoin.conf -daemon **關閉** bitcoin-cli -testnet -rpcuser=bitcoinrpc -rpcpassword=btc2018 stop ## 創建錢包 ```go= bitcoin-cli -testnet -conf=/data/btc_data/bitcoin.conf createwallet "testwallet" 如果沒創建錢包, getnewaddress 會得到 get center address err:record not found **重新加載錢包** bitcoin-cli -testnet -rpcuser=bitcoinrpc -rpcpassword=btc2018 -rpcport=18332 loadwallet testwallet bitcoin-cli -testnet -rpcuser=bitcoinrpc -rpcpassword=btc2018 loadwallet testwallet **列出所有錢包** bitcoin-cli -testnet -rpcuser=bitcoinrpc -rpcpassword=btc2018 -rpcport=18332 listwallets ``` # 創建用戶錢包地址 ```go= bitcoin-cli -testnet -rpcuser=bitcoinrpc -rpcpassword=btc2018 getnewaddress ``` 回傳 用戶錢包地址 ```go= tb1q83yazmfte7w3fv64eaqqjv2rmnj3agsy6c3zh4 ``` **導出錢包私鑰** ```go= curl --user bitcoinrpc:btc2018 --data-binary '{"jsonrpc": "1.0", "id": "testwallet", "method": "dumpprivkey", "params": ["tb1q83yazmfte7w3fv64eaqqjv2rmnj3agsy6c3zh4"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ ``` **回傳錢包私鑰** ```go= {"result":"cUz5KrxVm93Uz1t939A9jyegXu2RggbePfUAgMvnyimmnJwSFtX1","error":null,"id":"testwallet"} ``` --- **獲取目前最新高度** ```go= curl --user bitcoinrpc:btc2018 --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getblockcount", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ ``` 結果 result=測試鏈區塊高度 ```go= {"result":2540747,"error":null,"id":"curltest"} ``` **獲取目前區塊資訊** ```go= curl --user bitcoinrpc:btc2018 --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getblockhash", "params": [2428582]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ ``` 回應 區塊的hash ```go= {"result":"00000000000000167198a12e7941e653dd9151fdb941624a386c5ffff699554a","error":null,"id":"curltest"} ``` **獲取目前區塊內的交易明細** ```go= curl --user bitcoinrpc:btc2018 --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getblock", "params": ["00000000000000167198a12e7941e653dd9151fdb941624a386c5ffff699554a"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ ``` 列出整個區塊內包含的交易資訊 [2428582區塊的查詢結果](https://blockstream.info/testnet/block/00000000000000167198a12e7941e653dd9151fdb941624a386c5ffff699554a) **獲取 txhash 的交易明細** ```go= curl --user bitcoinrpc:btc2018 --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "gettransaction", "params": ["bd505fe95841ceec254008a6c86e25cb79a69043855747b4f63bb0a85bcc7b54"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ ``` tx 內 一堆交易的 txId ![image](https://hackmd.io/_uploads/BkkeiRMrp.png) **獲取 txhash 的交易明細(raw data)** ```go= curl --user bitcoinrpc:btc2018 --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getrawtransaction", "params": ["bd505fe95841ceec254008a6c86e25cb79a69043855747b4f63bb0a85bcc7b54"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ ``` 查詢結果 ```go= {"result":"02000000000101e21b02768d9b3081a131db1664dee3e977ed569e88b074ca0191659ed08e137a0000000000feffffff02fefb1100000000001600143da33c22bf1ad527ac92605c1f179a326f8c17e01e150000000000001600142a7421b6ed38c5c3618ac8d33983b425ab0ba3820247304402207fbc7ad987b197be2e51512c0d909401402a2e659f08b91a5e2bf4c2585fba0002205927c61e22d6d5ec12607b1c8c1e813a0d0b90fe0b2c0a362be3be41fd5f4355012103451b939ab892542e0d15cc17f205a4c87255b79f442a154d776701633a090491f10d2500","error":null,"id":"curltest"} ``` [raw data內的資訊](https://blockstream.info/testnet/tx/bd505fe95841ceec254008a6c86e25cb79a69043855747b4f63bb0a85bcc7b54) # 交易指令 1 [Here's how to send raw transaction BTC using Bitcoin-cli command](https://stackoverflow.com/questions/38493893/heres-how-to-send-raw-transaction-btc-using-bitcoin-cli-command) **範例的資料** 來源地址資料 address tb1qytdf0v5mhdw4megdtw8dvv3hfagczy6ju26d3e secret_key cR7zWWke3wEVnCsP9NHAB6uFt8Lmi7DKGceohGXPbJUiTT7hg8zd 目的地址 address tb1q2tl7ze6dxc8p8pqx2jwjazw86n4fyfvxr3rwqu **先到水龍頭去充值** https://bitcoinfaucet.uo1.net/ **導出私鑰** curl --user bitcoinrpc:btc2018 --data-binary '{"jsonrpc": "1.0", "id": "testwallet", "method": "dumpprivkey", "params": ["tb1qytdf0v5mhdw4megdtw8dvv3hfagczy6ju26d3e"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ 結果 {"result":"cR7zWWke3wEVnCsP9NHAB6uFt8Lmi7DKGceohGXPbJUiTT7hg8zd","error":null,"id":"curltest"} **列出所有未花费** curl --user bitcoinrpc:btc2018 --data-binary '{"jsonrpc": "1.0", "id": "testwallet", "method": "listunspent", "params": [0, 9999999, ["tb1qytdf0v5mhdw4megdtw8dvv3hfagczy6ju26d3e"] , true ]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ 結果 {"result":[{"txid":"511671b1ed7ed89fe46ba99de3290b832f2c9da11d0a3972a674ddc2391a2b0c","vout":2,"address":"tb1qytdf0v5mhdw4megdtw8dvv3hfagczy6ju26d3e","label":"","scriptPubKey":"001422da97b29bbb5d5de50d5b8ed632374f51811352","amount":0.00005344,"confirmations":52,"spendable":true,"solvable":true,"desc":"wpkh([0ea36790/0'/0'/71']02f89309da92f327e9e8b547f72b9563deaee2deef9bfc2803991df4dd48d67cdd)#durvzft4","safe":true}],"error":null,"id":"testwallet"} **目前此錢包帳戶餘額** amount = 0.00005344 **創建原始交易** 參數 1 要交易的小錢包 txid, vout 要 跟 listunspent 一樣 參數 2 目的地址和收到的金額, 來源地址和此小錢包最後餘額 bitcoin-cli -rpcuser=bitcoinrpc -rpcpassword=btc2018 -rpcport=18332 createrawtransaction "[{\"txid\":\"511671b1ed7ed89fe46ba99de3290b832f2c9da11d0a3972a674ddc2391a2b0c\",\"vout\":2}]" "[{\"tb1q2tl7ze6dxc8p8pqx2jwjazw86n4fyfvxr3rwqu\":0.00001},{\"tb1qytdf0v5mhdw4megdtw8dvv3hfagczy6ju26d3e\":0.00004144}]" 來源地址原始金額 - 想提幣的金額 - 手續費 = 來源地址最後餘額 0.00005344 - 0.00001 - 0.000002 = 0.00004144 計算第一筆 0.01145407 => 0.00006 + 0.01133891 + 0.00005344 0.01145407 - 0.01145235 結果 拿到 hexstring 02000000010c2b1a39c2dd74a672390a1da19d2c2f830b29e39da96be49fd87eedb17116510200000000ffffffff02e80300000000000016001452ffe1674d360e138406549d2e89c7d4ea922586301000000000000016001422da97b29bbb5d5de50d5b8ed632374f5181135200000000 **用私鑰簽署原始交易** 參數 1 上一個步驟的 hexstring 參數 2 私鑰 bitcoin-cli -rpcuser=bitcoinrpc -rpcpassword=btc2018 -rpcport=18332 signrawtransactionwithkey "02000000010c2b1a39c2dd74a672390a1da19d2c2f830b29e39da96be49fd87eedb17116510200000000ffffffff02e80300000000000016001452ffe1674d360e138406549d2e89c7d4ea922586301000000000000016001422da97b29bbb5d5de50d5b8ed632374f5181135200000000" "[\"cR7zWWke3wEVnCsP9NHAB6uFt8Lmi7DKGceohGXPbJUiTT7hg8zd\"]" 結果 獲得 { "hex": "020000000001010c2b1a39c2dd74a672390a1da19d2c2f830b29e39da96be49fd87eedb17116510200000000ffffffff02e80300000000000016001452ffe1674d360e138406549d2e89c7d4ea922586301000000000000016001422da97b29bbb5d5de50d5b8ed632374f518113520247304402207d86656d7033b7388f1ab233b8b7f70fa21a837264422c772f989ccc4f9b754b02206c73639bbb0bca909e7671d6f1e5f9d335ef1a3e8298108ab41399fff1dd1c60012102f89309da92f327e9e8b547f72b9563deaee2deef9bfc2803991df4dd48d67cdd00000000", "complete": true } **發送交易 (鏈上交易)** 參數 1 signrawtransactionwithkey 步驟的 hex 結果 curl --user bitcoinrpc:btc2018 --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "sendrawtransaction", "params": ["020000000001010c2b1a39c2dd74a672390a1da19d2c2f830b29e39da96be49fd87eedb17116510200000000ffffffff02e80300000000000016001452ffe1674d360e138406549d2e89c7d4ea922586301000000000000016001422da97b29bbb5d5de50d5b8ed632374f518113520247304402207d86656d7033b7388f1ab233b8b7f70fa21a837264422c772f989ccc4f9b754b02206c73639bbb0bca909e7671d6f1e5f9d335ef1a3e8298108ab41399fff1dd1c60012102f89309da92f327e9e8b547f72b9563deaee2deef9bfc2803991df4dd48d67cdd00000000"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ 拿到交易的 txhash {"result":"ec53925fc146a3a77a85d3a4f11cb1c815d6a0707733167ffa595245c848704a","error":null,"id":"curltest"} [此交易的 txhash 可以在區塊鏈上查詢](https://blockstream.info/testnet/tx/ec53925fc146a3a77a85d3a4f11cb1c815d6a0707733167ffa595245c848704a) **獲取原生交易** 參數 1 signrawtransactionwithkey 步驟的 hex 結果 bitcoin-cli -rpcuser=bitcoinrpc -rpcpassword=btc2018 -rpcport=18332 decoderawtransaction 020000000001010c2b1a39c2dd74a672390a1da19d2c2f830b29e39da96be49fd87eedb17116510200000000ffffffff02e80300000000000016001452ffe1674d360e138406549d2e89c7d4ea922586301000000000000016001422da97b29bbb5d5de50d5b8ed632374f518113520247304402207d86656d7033b7388f1ab233b8b7f70fa21a837264422c772f989ccc4f9b754b02206c73639bbb0bca909e7671d6f1e5f9d335ef1a3e8298108ab41399fff1dd1c60012102f89309da92f327e9e8b547f72b9563deaee2deef9bfc2803991df4dd48d67cdd00000000 # 測試帳號 "secret_key":"cTbLF5UtcULPLd8G7CENxSbBQ2dyKsPzjGKEtADoN3ToPfpojLUG", "from_address":"tb1q9f6zrdhd8rzuxcv2erfnnqa5yk4shguzm6mrre", "secret_key":"cQQ3cEz2x7K4t9KPVFRttgWNBSsbEhpFkXLdz4vqm7SnHTFchB44", "from_address":"tb1qjzz6gzq4c4a4fksx2jccgnwc49h8y59n5dam43", curl --user bitcoinrpc:btc2018 --data-binary '{"jsonrpc": "1.0", "id": "testwallet", "method": "listunspent", "params": [0, 9999999, ["tb1q9f6zrdhd8rzuxcv2erfnnqa5yk4shguzm6mrre"] , true ]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ 0.0000444 curl --user bitcoinrpc:btc2018 --data-binary '{"jsonrpc": "1.0", "id": "testwallet", "method": "listunspent", "params": [0, 9999999, ["tb1qqcqpu8e8lrcpet7nx3v6jytqvrcxk0rje362uu"] , true ]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ =================================================================================================== curl --user bitcoinrpc:btc2018 --data-binary '{"jsonrpc": "1.0", "id": "testwallet", "method": "dumpprivkey", "params": ["tb1qp6cw8tr9nturxa3jl9x787kfscf20vuz33jjda"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ {"result":"cQVp5g9hoCjshXvV1yzLe4aPRtBCP9HctYXAWCqFDxZqCQJjhKLx","error":null,"id":"testwallet"} 目前餘額 0.00029009 - 0.00001 - 0.00000685 = 0.00027324 **創建原始交易 2** 參數 2 目的地址和收到的金額, 來源地址和此小錢包最後餘額 bitcoin-cli -rpcuser=bitcoinrpc -rpcpassword=btc2018 -rpcport=18332 createrawtransaction "[]" "[{\"tb1qje93dmw9vpth9yx7llmfm2v86kcy3kel0g2tvl\":0.00001},{\"tb1qytdf0v5mhdw4megdtw8dvv3hfagczy6ju26d3e\":0.00028009}]" **充值裸交易** bitcoin-cli -rpcuser=bitcoinrpc -rpcpassword=btc2018 -rpcport=18332 fundrawtransaction 020000000002e803000000000000160014964b16edc560577290defff69da987d5b048db3f696d00000000000016001422da97b29bbb5d5de50d5b8ed632374f5181135200000000 **用私鑰簽署原始交易** bitcoin-cli -rpcuser=bitcoinrpc -rpcpassword=btc2018 -rpcport=18332 signrawtransactionwithkey "0200000009afa1fecbd03d23238061c1b09547031061dfd05d4046eec5a8d5b5d4470566c80000000000feffffff290b006e34f62959a7714656c8286f9e69a386e65bb409abc49e6e1116866bdd0000000000feffffff04a8700b5f7b52d9bf39c652e84353074a2e0b364f23b8e78c778c498b1409330200000000feffffff3f6c0eac0ba77712ec1fb157e6d59e0505fe1f8eaae0334b61cee017945377160000000000fefffffff34ea431476d637324857bf5c6de79328782a9257904a169d76590a4e26585af0100000000feffffffa870bf0a355e6c36d292a4e4fbbe758527c987d1fd951dc7df0f1a606506ed930000000000feffffff468614705f01b12ccf12c5c1a2c463509382aae5c7067952f1b18481271625c10200000000feffffff2c2816a54b109096ca8846e3fb3967918fd23edf55251197551b98278bc59cb80100000000feffffff2c2816a54b109096ca8846e3fb3967918fd23edf55251197551b98278bc59cb80000000000feffffff02e803000000000000160014964b16edc560577290defff69da987d5b048db3f696d00000000000016001422da97b29bbb5d5de50d5b8ed632374f5181135200000000" "[\"cQVp5g9hoCjshXvV1yzLe4aPRtBCP9HctYXAWCqFDxZqCQJjhKLx\"]" # bitcoin.conf ```go= ###### RPC 配置 ###### testnet=1 # 0-主网 1-测试网 rpcallowip=0.0.0.0/16 # rpc访问白名单 datadir=/data/btc_data # 区块存储位置(硬盘要够用) dbcache=10240 # 所有交易进行索引; 否则只保留钱包地址交易索引记录 rpcuser=bitcoinrpc # RPC 远程连接的认证用户名 rpcpassword=btc2018 # RPC 远程连接的认证用密碼 txindex=1 # daemon=1 # 是否后台运行 server=1 # 是否启动JSON-RPC接口 (0-不启动 1-启动) #rest=1 #rpcbind=0.0.0.0 # rpc接口的监听地址,默认绑定到所有IP mainnet 才要開 #rpcport=8332 # rpc接口的监听端口 mainnet 專用port mainnet 才要開 deprecatedrpc=signrawtransaction walletnotify=sh /root/data/notify_btc/notify.sh %s #通知到账 rpctimeout=30 # rpc客户端超时秒数 maxconnections=200 # 入站/出站最大连接数 ###### 钱包配置 ###### #paytxfee=0.00 # 每次发送比特币时的交易费 #txconfirmtarget=6 # 交易最小确认数 (默认值: 6) ``` # ETH [eth教學](https://goethereumbook.org/zh/transfer-eth/) [智能合約學習中](https://hackmd.io/dcCsefuGT-eRlflYsfv6kA?view) [測試鏈,水龍頭](https://www.grenade.tw/blog/goerli-faucet/) # 搭建 ETH 節點 version: 1.10.26 - [ETH releases](https://geth.ethereum.org/downloads/) - [prysm releases](https://raw.githubusercontent.com/prysmaticlabs/prysm/master/prysm.sh) - [prysm genesis.ssz](https://github.com/eth-clients/eth2-networks/raw/master/shared/prater/genesis.ssz) ## 創建項目目錄 ### 創建並進入 eth 節點目錄 ```shell mkdir -p /data/eth_data && cd /data/eth_data mkdir -p node mkdir -p node_prysm ``` ### 下載可執行文件 ```shell wget https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz tar xvfz geth-linux-amd64-1.11.2-73b01f40.tar.gz ``` ```shell cd /data/eth_data && cp ./geth-linux-amd64-1.11.2-73b01f40/geth ./ ``` ```shell wget https://github.com/eth-clients/eth2-networks/raw/master/shared/prater/genesis.ssz curl https://raw.githubusercontent.com/prysmaticlabs/prysm/master/prysm.sh --output prysm.sh && chmod +x prysm.sh ``` ### 啟動節點 ```shell # 生成 JWT 認證 ./prysm.sh beacon-chain generate-auth-secret ``` ```shell # 啟動 信標節點 vim start_prysm.sh ``` ```shell #!/bin/bash echo "prysm testnet prater" screen -dmS prysm ./prysm.sh beacon-chain \ --execution-endpoint=http://localhost:8551 \ --jwt-secret=./jwt.hex \ --genesis-state=./genesis.ssz \ --suggested-fee-recipient=0x01234567722E6b0000012BFEBf6177F1D2e9758D9 \ --datadir ./node_prysm \ --prater ``` ```shell chmod +x start_prysm.sh ./start_prysm.sh ``` ```shell vim start.sh ``` ```shell #!/bin/bash echo "Starting private ETH testnet" screen -dmS ETH_testnet ./geth \ --goerli \ --datadir ./node \ --syncmode full \ --gcmode archive \ --cache 1024 \ --http \ --http.addr 0.0.0.0 \ --http.port 8545 \ --http.api eth,net,engine,admin \ --authrpc.jwtsecret ./jwt.hex \ --authrpc.addr localhost \ --authrpc.port 8551 \ --authrpc.vhosts localhost \ --allow-insecure-unlock ``` ```shell chmod +x start.sh ./start.sh ``` ## 查看節點數據 ```shell # 信標節點 curl http://localhost:3500/eth/v1alpha1/node/syncing ``` ```shell # geth curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' http://localhost:8545 ./geth attach http://127.0.0.1:8545 eth.syncing eth.blockNumber ``` ## 停止节点 使用 `screen -ls` 获取 session id `screen -X -S {session id} quit`