---
# System prepended metadata

title: 面試補充

---


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