<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

使用 cmd 來示範 list 當作 queue ( lpush rpop )

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
```

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)

* **優點**
* 架設簡單
* 一個是讀寫分離,分擔 "master" 的讀寫壓力
* 一個是方便做災難復原
* 資料可靠性提升
* **缺點**
* 不具備自動容錯和復原功能,主機從機的宕機都會導致前端部分讀寫請求失敗,需要等待機器重啟或手動切換前端的IP才能恢復(也就是要人工介入)
* 較難支援線上擴容,在叢集容量達到上限時線上擴容會變得很複雜
* 浪費空間, 同樣的資料儲存在多個slave內
- **哨兵模式** 一個哨兵監控多個Redis 實例, 故障偵測 通知 故障轉移

* **優點**
* 哨兵模式是繼承**主從模式**的,所有主從的優點,哨兵模式都具有。
* 主從可以自動切換,系統更健壯,可用性更高(可以看作自動版的主從複製)。
* **缺點**
* 每台Redis 伺服器都儲存相同的數據,很浪費內存。
* Redis較難支援線上擴容,在叢集容量達到上限時線上擴容會變得很複雜。
- **從集模式** 分散式儲存資料, 同樣的資料分布在不同的redis

* **優點**
* 可以横向擴充, 只需連接集中某幾個節點即可
* 利用CRC16演算法, 將資料平均分散在16384哈希槽,保有資料的分散性
* 公式 HASH_SLOT = CRC16(key) / 16384
* **缺點**
* 維護較困難
* 某些指令因為跨槽問題, 所以不支援
**redis cluster 利用crc16演算法將資料分散在不同實體**

**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架構圖

設計理念就是
* 利用 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

SELECT * FROM orders WHERE user_id = ? AND status = ? AND created_at >= ?
index A : idx_user_id_status_created_at(user_id, status, created_at)
索引的細節

explain的結果

聯合索引的規則是
先將聯合索引 **最左邊** 的開始排序
在第一個欄位的排序基礎下, 在使用第二個欄位進行排序
而且索引不能夠跳著 例如 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操作的相關數據
可以用來分析目前批次寫入的狀態

**linux上的調整**
sudo vim /etc/security/limits.conf
```go=
* soft nofile 10240
* hard nofile 10240
```
# 高併發的mysql
**利用主從分離來達到讀寫分離, INSERT 再寫的連線上, 大量的查詢在讀的連線上**
應用例子是
前台使用 master連線在寫入
後台使用 slave 連現在讀取
**利用 分table表功能**

**適度的分庫和分表**
---
# 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

**獲取 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`