Get ETH balance and tokens balance

前言

最近都在處理區塊鏈相關的問題,其中一個就是怎麼直接透過節點(****.infura.io)來取得 ETH 和其他 ERC-20 的地址餘額(balance)。

JSON RPC

ETH 節點所使用的 request body 是依照 JSON RPC 的方式,也就是我們所使用的 request path 都是相同的,像是主要的 https://mainnet.infura.io ;不同的是,我們可以透過不同的 body 內容來區別不同的需求。

基本的 body 格式

{
    "jsonrpc": "2.0",
    "method": "method name",
    "params": [],
    "id": 1
}

取得 ETH 餘額

取得 ETH 餘額的方式較為單純,節點有提供一個 method 是 eth_getBalance,使用起來如下

{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": ["你的 ETH 地址", "latest"],
    "id": 1
}

其中這邊的 latest 意思是指向最新的那個區塊取資料的意思;
我們便可以得到 response:

{
    "id":1,
    "jsonrpc": "2.0",
    "result": "0x0234c8a3397aab58" // 158972490234375000
}

其中注意到一點是,result 所回應的是你這個地址有幾聰的 hex 字串,所以當我們取得的時候得注意一下。

ERC-20 的餘額

而 ERC-20 的餘額就沒有上述那麼直覺的取得方式了,我們需要透過別的 method 來完成這個動作。
這邊我們只是需要取得餘額,而沒有更新合約的狀態,所以使用 eth_call 這個 method。

{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": [
    {
        "to": "Token 的 contract address",
        "data": "data"
    },
    "latest"
    ],
    "id": 1
}

這邊的重點便是 data 的部分,它的組成為
0x + 8 bits + 64 bits
其中 8 bits 是由 function signature hash 的結果取前 8 bits;
我們這邊所使用的 function 名稱是 balanceOf(address),去做 Keccak-256(SHA-3)hash 可以得到
70a08231b98ef4ca268c9cc3f6b4590e4bfec28280db06bb5d45e689f2a360be
而前 8 bits 便是 70a08231

後 64 bits 則是所要查詢的 ETH 地址,
000000000000000000000000 + 要查詢的地址 40 bits
所以組成起來的 data 欄位就會是:
0x70a08231000000000000000000000000 + ETH 地址
這樣就可以取得相對應的 token 餘額了。

BIP-39 Mnemonic validate

前言

最近處理了一些加密貨幣的問題,其中一個是助記詞的驗證,這邊將會解釋助記詞的生成和驗證方式。

生成助記詞

首先我們這邊所介紹的是助記詞生成方式,先建立一個 128 bytes 的隨機序列,也就是隨機產生 16 個 UInt8 的序列;
主要有分成 128、160、192、224、256 bits(每 32 bits 做為一個區間),而下列會以 128 bits 作為流程解釋。

對隨機序列加密(SHA256)

我們對剛剛所產生的序列做 SHA256 加密,便會得到由 32 個 UInt8 所組成的 Array。

Checksum

用來驗證助記詞是否正確的方式是透過 Checksum 來辨別,而 Checksum 的 size 為序列的長度 / 區間,如我們這邊所提及的 128 / 32 = 4;
意思便是剛剛所加密完的序列,我們取前面 4 bytes 的數值當作 checksum。

產生助記詞

而剛剛的隨機序列(128 bits)加上 4 bits 的 checksum 組成 132 bits,接著我們每 11 bits 作為一個分隔,也就可以得到 132 / 11 = 12 個數字。
而每 11 bits 作為一個分隔的意思也意味著數字的區間落在 0 - 2047 之間,也就是為什麼 12 字的助記詞所支援的單字庫數量為 2048。
接著就到詞庫裏頭撿取相對應 index 的字詞來組成助記詞。

驗證助記詞

驗證的方式就是將上述的流程反過來做,我們先講助記詞轉成詞庫的 index 順序;
接著看最後 4 bits 的內容,也就是這組助記詞的 checksum。
而前 128 bits 以 8 bits 作分隔,接著以 SHA256 加密,判斷前 4 bits 是否和剛剛提及的 checksum 相符。

Change login shells without chsh

前言

最近心血來潮更新了 brew,其中我正在使用的 shells - zsh 也一併更新了,才發現當初在從 bash 轉到 zsh 的時候有指定到版號的部分⋯也就是長得像這樣

/usr/local/bin/zsh-5.6.1

zsh 一更新之後,我的 iterm2 便很合理地打不開,因為找不到 login 的 shells;
當初我是在 bash 裡頭使用 chsh 來變更

chsh -s $(which zsh)

如今打不開 iterm2 該如何更改呢?

使用者與群組

首先先到 使用者與群組

並先解鎖來得到更改的權限

就可以按住 control 並點擊使用者,就會出現 進階選項 可以選擇

最後就可以在 登入 shell 那欄更改即可!

AWS EC2 with WordPress

前言

Vultr

這個網站原本是在 Vultr 上開一台機器,每個月大概的費用為 USD $6 - 7 元之間,並利用 Certbot 自動更新 SSL 憑證。但後來發現不太會維護那台機器,時常因為記憶體造成資料庫錯誤,每隔兩三天就得去手動重新啟動來解決問題,乾脆就搬家到 WordPress.com 的服務。

WordPress.com

WordPress.com 的好處就是全部幫你處理好好的,然後我使用的是個人版的方案;NT$ 130 / 月,便有提供轉址的功能和一年份免費的網域。不過由於我自己是向 PChome 買 Archie.tw,所以主要是為了轉過去而付費。

但使用服務和自己開一台機器的差異性就在於彈性,像是安裝外掛便被放在商務版的功能之中,而費用為 NT$ 799 / 月。

這樣一用也用了一段時間,直到近期註冊了 AWS 的服務,便決定搬移到 AWS 上。

AWS

在 AWS 上新註冊的用戶可以擁有一年的免費方案,而這個網站目前使用到幾個 AWS 服務分別為

  • EC2
  • Load Balancer
  • Certificate Manager
  • Route 53

這邊來介紹我的流程:

Key Pairs

首先我們先到 EC2 的頁面選擇 NETWORK & SECURITY ➡️ Key Pairs 來建立一組 Key pair,用於之後連接到 EC2 instance

EC2

透過 AWS Markeyplace 開一台新的 instance,使用 WordPress Certified by Bitnami 搭配 t2.micro 的免費方案;而之後若有收費的時候,約為 USD$ 0.012 / 小時,也就是一個月 USD$ 8.64 / 月。
一路照著步驟走,若你本身沒有其他服務要規劃的話,後續的動作都不需要做任何調整,就可以開好一台裡頭已經安裝好 WordPress 的 EC2 了。

這時候你就可以使用那台 EC2 的 Public IP 來開啟你的 WordPress 了!

使用者

在網址的最後加上 /admin 可以開啟管理後台,預設的使用者名稱為 user,而密碼的部分可以透過 EC2 Actions ➡️ Instance Settings ➡️ Get System Log 來查看,裡頭會有標註出密碼的欄位。

綁定個人網域

我們需要透過 Route 53 來處理個人網域的綁定動作,並使用 Load Balancer 和 Certicate Manager 來處理 HTTPS 的連線。

首先先到 Route 53 的 Hosted zones 建立一個 Hosted Zone 備著,其中你會需要將 NS 帶入到購買網域的管理頁面去設定,並進行 Certificate Manager 建立 SSL certificate。

建立憑證的時候,會需要將 CHAME 帶入到 Route 53 裡頭,再來建立一個 Load Balancer 來取得 DNS name,這時候再填入到 Route 53 裡頭便完成了 AWS 這部分的建構。

最後還需要使用最前面所建立的 Key pair 連線到 instance 上,來開啟 HTTPS 的功能。

Bitnami WordPress 詳細的 SSL 圖文教學

⚠️ 幾個需要注意的地方

  • Key Pairs 需要建立在 EC2 instance 之前,且弄丟了你就無法連回去那台 instance 了
  • Route 53、Certificate Manager CHAME 那些設定是需要一些時間轉移的
  • Route 53 和 Load Balancer 是需要收費的功能

整體費用的部分我一個月收再來更新這網站一共得支出多少在 AWS 上,處理完這個之後就可以來補上前陣子的 iOS 心得了!

POSTMAN – Pre-request script

POSTMAN 除了可以發送 request 到 server 之外,也有提供 pre-request script 的功能,讓我們可以撰寫 script 來進行一連串的測試。
舉個例子,我們有三支 API,邏輯為 名稱(Request body)【Response body】:

  • 登入(帳號、密碼)【Token】
  • 取得使用者的消費紀錄列表(Token)【Array<Receipt ID>】
  • 取得單筆消費紀錄(Receipt ID)【Receipt】

在沒有撰寫 pre-request 之前若要取得 Receipt 的話,我們得要先呼叫 登入,接著手動複製 Token 到 取得使用者的消費紀錄列表 的 request body 裡頭,然後再複製⋯⋯(你們應該懂)
這邊就來教教如何先寫好 script,之後只要按最後你想得到的那支 API 就行了!

let account = 'account'
let password = 'password'
let index = 0
pm.sendRequest({
    url: 'https://Archie.tw/login?account=' + account + '&password=' + password,
    method: 'get'
}, function(err, response) {
    let token = response.json().token;
    pm.sendRequest({
        url: 'https://Archie.tw/getReceiptList?token=' + token,
        method: 'get'
    }, function(err, response) {
        pm.environment.set("receiptIdentifier", response.receiptIdentifier);
    });
});

接著在 Params / Body 那邊就可以使用剛剛所定義的環境變數({{receiptIdentifier}})了!
然後請不要真的打範例的 url,是不會有東西回給你的(真的)。

Info.plist localized

一些需要權限的功能都會需要在 Info.plist 裡頭加上說明,而這個說明要實作多語系則是建立一個 InfoPlist.strings(沒錯,名字就是這樣)
然後對相對應得 Key 填寫翻譯,像是:

NSLocationWhenInUseUsageDescription = "說明填寫";

就可以在要使用位置的說明處顯示多語系的結果了。
至於其他的 Key 可以從CocoaKey 官方文件 以及CoreFundationKey找找。

Git submodule

有些時候,我們會需要幾個檔案和其他專案共用,而 iOS 的專案可以採取 CocoaPods / Carthage,但如果是要和其他語言共用的話該怎麼辦呢?
舉個例子,Server 和 Client 之間傳遞 Status Code,像是 code: 20000、20001、20002 之類的,收到 code 後要再做後續動作。
不過一份 Code 的定義散落在多個平台 / 專案之中,難免會有人雷的時候;不論是 client 記錯或是 server 回錯,而若是有個地方可以共同維護的話,便可以減少這種失誤。
所以就把那些文件(e.g .json)放到 repository 上,然後在你的專案之中:

git submodule add YourDocumentRepository.git

就會在你的專案資料夾中看到 clone 下來的結果,接著再將檔案拖拉至專案之中即可使用。
若要更新 submodule,則下

git submodule update

或是到 clone 下來的資料夾

git pull

像是如果懶得在每一個檔案都 import PodName,就直接弄成 Submodule 的方式來處理也行!
如我自己習慣的一些 Extension 就這麼弄。

Create CocoaPods by yourself

這篇文章會介紹建立自己的 CocoaPods 流程,而我當初是看了 David 的教學文所跑的流程。
首先,先建立一個新的 Pod

pod lib create YourPodName

接著依照自己的 Pod 內容回答問題,便會生成一個新的專案出來。
再來將 Code.Swift 丟到 Pods/Development Pods/YourPodName,也就是它預設 ReplaceMe.Swift 的那個地方。
最後 README.md 和 YourPodName.podspec 寫一寫就完成了 Pod 的準備。
而預設的 REAMME.md 裡頭,有一個 CI Status,你可以移除或者到 Travis.CI 建構;
從 Travis.CI 那邊可以得知如何建構一個 .travis.yml。
都準備好之後,在 GitHub 上開一個 repository 來放置,記得要放上 tag 標記目前的版本。

pod spec lint YourPodName.podspec

最後就送出去就好了!

pod trunk push YourPodName.podspec

如果你沒有註冊過的話,得先註冊一下:

pod trunk register email@domain.com 'Your Name'

而如果你有在 .podspec 裡頭填寫你的 Twitter 的話,就會收到 CocoaPods 貼的文!
如果你搜尋不到你的 Pod 的話,可以清除目前的 cache 就可以順利找到了!

rm -rf ~/Library/Caches/CocoaPods

Socket.IO

這篇文章會介紹有關於 Socket.IO 在 server 端以及 iOS 端的一些實作分享。

Server

首先我們可以先從 Socket.IO 的 Get started 開始,它是以 Node.JS 所編寫的,所以先在資料夾位置執行

npm init

邊可以取得基本的一些檔案和 package.json。
接著安裝 Socket.IO 所需要的 express

npm install --save express@4.15.2

然後新建一個 index.js 的檔案,貼上

var app = require('express')();
var http = require('http').Server(app);
app.get('/', function(req, res){
    res.send('<h1>Hello world</h1>');
});
http.listen(3000, function(){
    console.log('listening on *:3000');
});

這樣待會執行 node index.js 的時候便可以從 http://localhost:3000 來連上這個 server。
確認 server 目前是可以連上後,便開始安裝 Socket.IO 到其中

npm install --save socket.io

並在 index.js 裏頭加上 Socket.IO 的 code:

var io = require('socket.io')(http);
io.on('connection', function(socket){
    console.log('a user connected');
});

這樣便完成監聽 client 連接上 server 的事件了。
'connection' 是 socket.io 所定義的 event,而這個 event 會取得 socket 回來,所以以 function(socket) 的方式去接收並處理後續。
不過這裡的 io 可以想成是一個 server,所以每收到一個 client 的 connection 事件,都會執行 function(socket);而我們若要監聽個別連線的事件,則是使用 socket 來處理:

socket.on('disconnect', function(){
    console.log('user disconnected');
});

也就是說,像是登入、傳送訊息等,和個別使用者相關的動作,我們都是以 socket 來處理。
而除了監聽(on)外,發送(emit)的動作也是如此,像是我想推一段文字給特定的使用者:

socket.emit('new message', '嗨!');

在 client 以及 server 端先彼此定義好 event 名稱,這樣就可以知道要監聽的事件為哪些。
什麼時候該用 io 發送,什麼時候該用 socket 呢?
舉個例子,我們建立一個 Socket.IO 的大聊天室,任何人只要連上這個網址,便等同於加入這個聊天室。

  • 使用者上、下線會顯示提醒
  • 說髒話的人會個別收到「此訊息無法傳送」的訊息
io.on('connection', function(socket) {
    io.emit('new user', '有人加入了這個聊天室!');
    socket.on('add message', function(from, message) {
        if (message.indexOf('馬的') > -1) {
            socket.emit('new message', '此訊息無法傳送');
        } else {
            io.emit('new message', from + ' 說:' + message);
        }
    })
    socket.on('disconnect', function() {
        io.emit('user leave', '有人離開了這個聊天室!');
    });
});

上面便可以得知哪種時候應該要大家都可以接收到、而哪種是只會有個別的使用者接收到。
再來介紹一下 ack,在發送一個 event 時,可以在後面補上一個 ack,而當對方收到的時候,可以透過 ack 來傳遞 data,而非再發送一個新的 event。
有點像是 HTTP request 的概念,發送一個 request(emit),接著對方會回傳 response(ack),不過這並不一定會有,也就是說你發送(emit)了一個 event 過去,雖然有夾帶著 ack,但是對方若沒接收那個 ack 的話也是沒用。
這邊是一個例子,我們從 server 給沒有收過這則廣告的使用者傳送一則過去,並希望使用者真的有收到,若沒收到下次就再傳一次:

// 判斷使用者是否有收到過廣告,若沒有的話執行
socket.emit('new advertise', '恭喜您獲得 $1,000 元折扣!', function(userID) {
    console.log('使用者(' + userID + ')收到廣告了!');
    //去資料庫更新,下次不用再推給這個 userID
)};

其中,function(userID) 便是一個 ack 或者可以說是 callback。
相對的,收到訊息的時候,server 也可以回傳 ack 回去給 client 告知:

socket.on('add message', function(text, ack) {
    console.log('收到: ' + text);
    ack('伺服器收到你的訊息了!請放心!);
});

如此一來,便會在收到訊息之後,以 ack 的方式回傳訊息回去。
而 ack 和 emit 所發送出去有什麼不同?
在 Socket.IO 的 protocol 裏頭有定義:

  • Packet#CONNECT ( 0 )
  • Packet#DISCONNECT ( 1 )
  • Packet#EVENT ( 2)
  • Packet#ACK ( 3 )
  • Packet#ERROR ( 4 )
  • Packet#BINARY_EVENT ( 5 )
  • Packet#BINARY_ACK ( 6 )

所以其實雖然動作類似,但 Socket.IO 可以辨別其中的差異,進而可以在 Socket 之中達到 Request、Response 的概念。

Client

Client 的部分,可以使用 Socket.IO 所提供的 Swift framework,它是基於 Starscream 所開發出來的,如同上述說的有使用到 WebSocket 來連接。
以 Swift 來說:

let manager = SocketManager(socketURL: URL(string: "http://localhost:3000")!, config: [configs])
let socket = manager.defaultSocket

這邊介紹幾個我所使用到的 configs

  • .log(true):開啟 LOG 的功能。
  • .forceWebsockets(true):若沒有使用這個的話,會以 HTTP polling 的方式連接,從 header 來看的話,就會顯示 connection: keep-alive;而使用了 .forceWebsockets(true) 的話,則會使用 WebSocket 來連線,則會顯示 connection: upgrade。
  • .reconnectAttempts(int):重新嘗試連線 n 次,超過就放棄。
  • .connectParams([String: Any]):這邊可以放 token 來做 Authentication。

為什麼不在 header 裏頭加上 Authentication 的欄位?
這邊 Socket.IO 有做解釋,為什麼不建議在 extraHeaders 加東西。
而 on / emit / emitWithAck 這幾個的用法就和 server 的概念一樣,這邊就不再多做解釋,
之後實作有遇到什麼事情再來補充(或是新文章)!

Xcode beta with CocoaPods

Xcode 10 beta 的 Swift 版本為 4.2,而若你目前所使用的 Pods 多數為 Swift 4 的話,該怎麼辦呢?
你可以在 Podfile 裏頭加上全域的參數來規範所有 Pods 的 Swift version:

post_install do |installer|
    installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
            config.build_settings['SWIFT_VERSION'] = '4'
        end
    end
end

如此一番便可以輕鬆地在 Pods 還沒全面支援 Swift 4.2 時就可以使用 Xcode 10 Beta 開發👏