https://www.ptt.cc/bbs/MIS/M.1575624725.A.74A.html
: 各位前輩大家好
: 小弟目前被賦予任務如下:
: 評估各家ERP優缺並負責導入相關事宜
: 我司產業為製造業(女鞋代工),產品皆外銷
: 主要需要解決的問題為以下:
: 1.BOM數位化問題
: 2.訂單轉換採購單,備料單重工問題
: 預算目前沒有上限;
: 從未導入過任何ERP系統,截至目前為止皆為人工手KEY各種報表;
: 目前已接觸過鼎新,尚揚,鴻來軟件
: 請問各位前輩還有推薦的ERP廠商嗎?
原提問人已經「面談」過不少軟體商的業務人員,為何還來這裡徵求各界意見?
其實,聽取軟體商簡報後,仍陷入「一頭霧水」狀態的MIS主管,我曾經就是其中一員,彼岸更多!
這個提問觸發我寫下一篇文章《如何選擇ERP》。先預熱一下...
恰巧我曾經任職一家只幫愛迪達代工,18000人的廣東鞋廠,厚顏擔任只出一張嘴「管理」28名部屬的MIS部門最高主管(經理)。
該廠當時:廣東有相距近百公里之遙的2個工廠、臺中辦公室、積極動工中的印度工廠。
除了HR當然有的考勤、薪資業務之外,有員工(收費、補助)餐廳、保險、住宿...等資訊需求。
入職當時,該廠正積極汰換ERP軟體,鄙人立即展開「ERP選型」工作。
接手之前,已經有下列軟體商爭取那門生意:
- SAP的「鞋服solution」
- 金蝶
- 深圳的中X(老闆已改行,偶有聯絡。)
- 高雄的先濬
我的調查方式:製作問卷,列舉技術問題,邀請軟體商答題。
按我的記憶列舉一、二:
1. 軟體能否「同時」處理「品名」和「尺碼」?所謂「處理」,包括:跑MRP、追蹤和紀錄各「品名」和「尺碼」的成本與數量。
2. 軟體能否處理各材料的保稅(出口退稅)功能?
3. 軟體能否線上切換語系?(希望印度廠也能使用)
4. 資訊人員能否設計一份報表,中國廠職員使用簡體字打印、臺中廠職員使用正體字打印、印度職員使用英文打印?
5. 資訊人員每設計一份報表之後,是否必須分別在中國廠的800部電腦安裝、去印度廠安裝、去臺中辦公室安裝?(「安裝」包括遠程分發、自動下載並安裝package)
6. 軟體系統使用何種程式語言設計?(事涉MIS人員接手維護軟體的學習曲線、求才難易度、薪資水準)
7. 軟體使用何種資料庫管理系統?(影響老闆的錢包)
8. 會計月結,是否必須來回嘗試幾次過帳、反過帳、結帳、反結帳,可能失敗、出現「負庫存量」、「負金額」?
9. 軟體能否「隨時」顯示「品名」和「尺碼」的真實(不是「標準」)成本與數量,無須等到下月初的月結?
...
當時,
- 用過Tiptop,確定0分,所以懶得聯絡鼎新業務員。
- 知道SAP是垃圾,故直接忽略SAP,連問卷也不發給那些X子。
- 金蝶不繳交問卷,從此失聯。
- 中X答非所問、回答不完整,但是仍拜訪、打電話,積極爭取訂單。
- 先濬拒絕答題。
我當時最敬佩的軟體商是金蝶,因為他們躲起來,證明是唯一有羞恥心的業務員,他們不願意欺騙、說謊。
老闆無視我的反對,所以先濬拿到訂單。我離職後,老闆支付三分之一合約金(330萬元臺幣)後,先濬從此失聯。
我給原提問人能心安理得、睡好覺的建議:
1、參考我前述問卷內容以及下面的參考資料,製作一份問卷,發給全部軟體商,邀請他們答題。
2、邀請那些全部回答「yes」的廠商向你當面示範那些「yes」的軟體功能。
3、淘汰這些軟體商:回答不清楚、回答「no」、不敢示範那些「yes」的軟體功能。
4、從那些全部回答「yes」而且確實示範軟體功能真實不虛的軟體商之中,選擇報價最低的一家,直接推薦給老闆。
5、如果不幸證明全部檯面上的軟體商都是騙子的話,只好去我的SaaS官網免費註冊、試用,基於你的專業和敬業,回答你自己的問卷,並且相信自己的眼睛,而非耳朵。
手冊:https://terarows.gitlab.io/app2/
---參考資料---
http://www.itpub.net/thread-1892471-1-1.html
- 勞工:三班制:07:00 ~ 15:00、15:00 ~ 23:00、23:00 ~ 07:00。夜班提供夜點費。
- 遲到半小時內,扣薪N元。遲到M小時以上,視同請假半日。早退,(忘了詳情!)。未請假,曠職,扣薪X元。
- 有全勤獎。
- 小過:扣N元;大過,扣M元。小功:獎N元;大功,獎M元。
- 文員:高級主管不打卡,其餘須打卡。可填單報出差。
- 薪資:按層級表。也許有年終獎金。
- 年假/年資對照表。
- 外國勞工提供住宿。
- 中國廠有用餐制度、住房、保險。
- 台灣廠有勞保、健保、綜合所得扣繳憑單。
- 當然,打卡鐘24小時侍候。
多公司。在線切換,無須登出再登錄。
多國語言。在線切換,無須登出再登錄。
已翻譯正體中文、簡體中文、英文。按各種語言輸入料項目、供應商名、地址、人名..。
多時區。倫敦用戶輸入、顯示「2014-12-01 00:00:00 +0」,北京用戶輸入、顯示「2014-12-01
08:00:00 +8」。
支持料品批號、鞋子的尺碼、成衣的尺碼。
會計科目只有一段。
一些ERP要求:每一個料號指定一個多段會計科目編碼。例如:1111-00A-123。其中,「1111」代表『原料』、「00A」代表『00A部門』、「123」代表『123料號』。
PostERP只使用會計科目只有一段:「1111」,代表『原料』。
無縫整合會計:凡是與金額相關的交易,都轉拋會計分錄。會計人員的記帳工作趨近零。
會計分錄只有一個屏幕,無須多個屏幕來回翻找、比對、輸入。這個屏幕容納成本、供應商、客戶、應收帳款、應付帳款、銀行存摺帳號...等信息。
無須過帳、反過帳。結帳重點工作,只運行一個指令,處理:虛帳戶結轉實帳戶、本期實帳戶餘額復制成下期期初餘額。
支持先進先出、後進先出、移動加權平均成本。永續盤存制。
隨時反應每一個倉位中的每一個項目、每一個批號的最新成本,無須等到月結。
MRP同時考慮項目與批號(或尺碼):
1、需求數量(數量,時間)
2、倉庫現存可用量(數量)
3、BOM標準用量(數量)
4、BOM父項損耗率、子項損耗率(數量)
5、生產工藝(數量、時間)(目前從缺)
6、計劃單數量(數量,時間)
7、請購單數量(數量,時間)
8、採購單數量(數量,時間)
9、外包單數量(數量,時間)
10、生產單數量(數量,時間)
11、生產投料單數量(數量,時間)
12、生產記錄(數量、時間)
13、最小包裝量/不可分割數量(數量)
14、最低採購數量(數量)
15、最低生產批量/最低外包批量(數量)
16、時柵(時間)(目前從缺)
17、時格(時間)(目前從缺)
18、提前期(時間)
多層「一對多」單據。例如:在同一個屏幕的一張出貨單號含多種藥名,每個藥名含多個批號,每個批號含多個出貨日期,每個出貨日期含多個交貨目的地;自多個儲位撿貨。
各種語言在線求助:屏幕說明、字段說明、報表用途與參數說明、程序用途與參數說明。
備正體中文、簡體中文、英文文檔,供使用人以瀏覽器閱讀。
MIS人員在瘦客戶上面開發、修改、強化ERP,終端使用人在瘦客戶操作ERP。
瘦客戶內建報表引擎,無須外掛Crystal Report等:MIS人員設計報表樣板,終端使用人運行報表。100%整合在瘦客戶的menu中。
MIS人員設計的一份WYSIWYG報表樣板,台灣文員預覽或打印正體中文報表、中國廠員工人打印正體中文報表、印度user print英文report。可設計cross tab報表、含bar code報表、子報表(sub-report)。
伺服器軟件與瘦客戶之間的傳輸資料經過(1)加密(2)壓縮。不需要VPN、Citrix、Windows Remote Desktop。
提供類似Unix crontab的排程機制。
巨細靡遺記錄user的輸入、修改、刪除記錄。
每一筆記錄都允許附掛32767個文檔,例如:工程圖.png、標準作業程序說明.doc。
屏幕中的每一個table都允許匯出成文檔,供LibreOffice的Calc、M$ Excel等軟件匯入。
《不易聽到SAP專案失敗的原因》:
https://bit.ly/2DPkDZZ
https://bit.ly/2OOanYv
ethereum 交易 驗證
https://www.blocktempo.com/blockchain-technical-overview-structure-of-blockchain-ethereum/
3. 以私鑰簽署交易。
4. 廣播「加上簽名的交易封包」至鄰近的以太坊節點們。
https://ithelp.ithome.com.tw/m/articles/10215095
總而言之在區塊鏈接納這筆交易前,先試著用地址反推回原本的公鑰,再用公鑰解密當初這筆交易紀錄的簽章看看,如果公鑰解的開就可以代表是公鑰持有人本人所簽核的,這便是剛剛提到的"數位簽章"。
注意:這段比較像是一般加解密的方式,好像不是ethereum
https://ethereum.stackexchange.com/questions/13778/get-public-key-of-any-ethereum-account
ethereum 簽完的東西 可以 找出 public key
有public key 就可以知道是誰發出這個交易
https://zhuanlan.zhihu.com/p/30481292
签名完成了,我们如何验证某些签名后的数据是哪个账户签名的呢?在web3.js 发布1.0版本以前,验证签名只能通过智能合约的ecrecover函数来实现。新版的web3.js提供了web3.eth.accounts.recover函数用于验证签名。这里我们仍然使用传统的智能合约ecrecover方式。
ecrecover接收数据的哈希值以及r/s/v等参数作为输入,返回实施该签名的账户地址。因此我们只需要通过合约拿到实施签名的地址,和我们真正的地址进行对比,如果地址一致,就说明验证通过了。
這也是ethereum private key和address比較重要的原因了
https://learnblockchain.cn/books/geth/part3/sign-and-valid.html
3. 以私鑰簽署交易。
4. 廣播「加上簽名的交易封包」至鄰近的以太坊節點們。
https://ithelp.ithome.com.tw/m/articles/10215095
總而言之在區塊鏈接納這筆交易前,先試著用地址反推回原本的公鑰,再用公鑰解密當初這筆交易紀錄的簽章看看,如果公鑰解的開就可以代表是公鑰持有人本人所簽核的,這便是剛剛提到的"數位簽章"。
注意:這段比較像是一般加解密的方式,好像不是ethereum
https://ethereum.stackexchange.com/questions/13778/get-public-key-of-any-ethereum-account
ethereum 簽完的東西 可以 找出 public key
有public key 就可以知道是誰發出這個交易
https://zhuanlan.zhihu.com/p/30481292
签名完成了,我们如何验证某些签名后的数据是哪个账户签名的呢?在web3.js 发布1.0版本以前,验证签名只能通过智能合约的ecrecover函数来实现。新版的web3.js提供了web3.eth.accounts.recover函数用于验证签名。这里我们仍然使用传统的智能合约ecrecover方式。
ecrecover接收数据的哈希值以及r/s/v等参数作为输入,返回实施该签名的账户地址。因此我们只需要通过合约拿到实施签名的地址,和我们真正的地址进行对比,如果地址一致,就说明验证通过了。
這也是ethereum private key和address比較重要的原因了
https://learnblockchain.cn/books/geth/part3/sign-and-valid.html
linux .bashrc
# General Aliases
alias apk='apk --progress'
alias ll="ls -ltan"
alias hosts='cat /etc/hosts'
alias ..="cd .."
alias ...="cd ../.."
alias ....="cd ../../.."
alias untar="tar xzvkf"
alias mv="mv -nv"
alias cp="cp -i"
alias ip4="ip -4 addr"
alias ip6="ip -6 addr"
COL_YEL="\[\e[1;33m\]"
COL_GRA="\[\e[0;37m\]"
COL_WHI="\[\e[1;37m\]"
COL_GRE="\[\e[1;32m\]"
COL_RED="\[\e[1;31m\]"
# Bash Prompt
if test "$UID" -eq 0 ; then
_COL_USER=$COL_RED
_p=" #"
else
_COL_USER=$COL_GRE
_p=">"
fi
COLORIZED_PROMPT="${_COL_USER}\u${COL_WHI}@${COL_YEL}\h${COL_WHI}:\w${_p} \[\e[m\]"
case $TERM in
*term | rxvt | screen )
PS1="${COLORIZED_PROMPT}\[\e]0;\u@\h:\w\007\]" ;;
linux )
PS1="${COLORIZED_PROMPT}" ;;
* )
PS1="\u@\h:\w${_p} " ;;
esac
存股
https://www.youtube.com/watch?v=uqlUFvkyEUg
1. 折舊長 不能折舊短 代表長期經營
2. 能控制供應 多或少,像蘋果 有強大的控制供應能力,需求多不見得
3. 產品周期長
1. 折舊長 不能折舊短 代表長期經營
2. 能控制供應 多或少,像蘋果 有強大的控制供應能力,需求多不見得
3. 產品周期長
hyperledger leader peer endorsing peer committing peer
leader peer 是peer之間動態選舉選出的
endorsing peer 預設為每個peer都是,安裝chaincode後,實例化設定policy可以有變化
committing peer 預設為每個peer都是
https://medium.com/@kctheservant/transactions-in-hyperledger-fabric-50e068dda8a9
https://stackoverflow.com/questions/52893818/difference-between-endorsing-peer-and-committing-peer-in-hyperledger-fabric/52894994
endorsing peer 預設為每個peer都是,安裝chaincode後,實例化設定policy可以有變化
committing peer 預設為每個peer都是
https://medium.com/@kctheservant/transactions-in-hyperledger-fabric-50e068dda8a9
https://stackoverflow.com/questions/52893818/difference-between-endorsing-peer-and-committing-peer-in-hyperledger-fabric/52894994
kubernetes K3s Failed!
Failed reason: Node port range can't fixed!
K3s
##### install #####
curl -sfL https://get.k3s.io | sh -
##### login https://192.168.99.119:6443/ get username/password #####
more /etc/rancher/k3s/k3s.yaml
##### change node-port range #####
k3s server --kube-apiserver-arg --service-node-port-range=1-65535
##### install dashboard #####
k3s kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml --insecure-skip-tls-verify
k3s kubectl create secret generic kubernetes-dashboard-certs --from-file=certs -n kube-system
k3s kubectl apply -f dashboard-ClusterRoleBinding.yaml
k3s kubectl apply -f dashboard-adminuser.yaml
k3s kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')
Find admin-user-token
https://192.168.99.119:30393/
##### kill all #####
k3s-killall.sh
k3s-uninstall.sh
##### start k3s service #####
service k3s status ## check k3s is stop
service k3s start
Replace uglifyjs-webpack-plugin with terser-webpack-plugin
https://github.com/vuejs/vue-cli/issues/2245
https://github.com/webpack-contrib/terser-webpack-plugin
https://stackoverflow.com/questions/57360588/how-to-use-terser-with-webpack
https://juejin.im/post/5cbc40ea6fb9a068b65e2aa6
https://github.com/webpack-contrib/terser-webpack-plugin
https://stackoverflow.com/questions/57360588/how-to-use-terser-with-webpack
https://juejin.im/post/5cbc40ea6fb9a068b65e2aa6
npm install npm install terser-webpack-plugin
vue.config.js
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
chainWebpack: process.env.NODE_ENV === 'production'
? config => {
config.module.rules.delete('eslint');
config.optimization.minimizer([
new TerserPlugin({
terserOptions: {
parse: {
ecma: 8
},
compress: {
ecma : 5,
warnings : false,
comparisons: false,
inline : 2
},
mangle: {
reserved: ["BigInteger","ECPair","Point"],
safari10: true
},
output: {
ecma : 5,
comments : false,
ascii_only: true
}
},
cache: true,
parallel: true,
sourceMap: false, // false 可以減少一半的js大小,sourceMap為除錯使用
})
]);
}
: config => {
config.module.rules.delete('eslint');
},
devServer: {
host: '0.0.0.0',
port: '80',
//public: '0.0.0.0:80', //無效
disableHostCheck: true,
}
}
[轉]Kubernetes 调整 nodePort 端口范围
https://qhh.me/2019/08/pod 文件定义在 /etc/kubernetes/manifests/kube-apiserver.yaml15/Kubernetes-%E8%B0%83%E6%95%B4-nodePort-%E7%AB%AF%E5%8F%A3%E8%8C%83%E5%9B%B4/
在 command 下添加 --service-node-port-range=1-65535 参数,修改后会自动生效,无需其他操作:
在 command 下添加 --service-node-port-range=1-65535 参数,修改后会自动生效,无需其他操作:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --service-node-port-range=1-65535
- --advertise-address=192.168.26.10
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --insecure-port=0
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --requestheader-allowed-names=front-proxy-client
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --requestheader-group-headers=X-Remote-Group
- --requestheader-username-headers=X-Remote-User
- --secure-port=6443
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-cluster-ip-range=10.96.0.0/12
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
image: registry.aliyuncs.com/google_containers/kube-apiserver:v1.15.2
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 8
httpGet:
host: 192.168.26.10
path: /healthz
port: 6443
scheme: HTTPS
initialDelaySeconds: 15
timeoutSeconds: 15
name: kube-apiserver
resources:
requests:
cpu: 250m
volumeMounts:
- mountPath: /etc/ssl/certs
name: ca-certs
readOnly: true
- mountPath: /etc/pki
name: etc-pki
readOnly: true
- mountPath: /etc/kubernetes/pki
name: k8s-certs
readOnly: true
hostNetwork: true
priorityClassName: system-cluster-critical
volumes:
- hostPath:
path: /etc/ssl/certs
type: DirectoryOrCreate
name: ca-certs
- hostPath:
path: /etc/pki
type: DirectoryOrCreate
name: etc-pki
- hostPath:
path: /etc/kubernetes/pki
type: DirectoryOrCreate
name: k8s-certs
status: {}
k8s kubernetes Lesson 8 Error
Error: User "system:serviceaccount:kube-system:default" cannot get resource "namespaces"
https://github.com/fnproject/fn-helm/issues/21#issuecomment-545317241
kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'
helm init --upgrade --service-account tiller
docker registry Other Way
On root home
openssl req -nodes -newkey rsa:4096 -keyout certs/docker-registry.key -out certs/docker-registry.csr -subj "/C=/ST=/L=/O=/OU=/CN=docker-registry"
openssl x509 -req -sha256 -days 365 -in certs/docker-registry.csr -signkey certs/docker-registry.key -out certs/docker-registry.crt
docker run -dp 5000:5000 --name registry -v "$(pwd)"/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/docker-registry.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/docker-registry.key \
registry
nano /etc/hosts
> 192.168.99.118 docker-registry
cd /etc/docker
mkdir certs.d
cd certs.d
mkdir docker-registry:5000
cd docker-registry:5000
cp ~/certs/docker-registry.crt ca.crt
===== check registry is working
docker image pull busybox
docker image tag busybox docker-registry:5000/mybusybox
docker image push docker-registry:5000/mybusybox
docker run --rm docker-registry:5000/mybusybox echo "Hello from busybox"
//
docker rmi busybox docker-registry:5000/mybusybox
docker run --rm docker-registry:5000/mybusybox echo "Hello from busybox"
===== remove registry
docker container stop registry && docker container rm -v registry
https://jkzhao.github.io/2017/09/01/Registry%E7%A7%81%E6%9C%89%E4%BB%93%E5%BA%93%E6%90%AD%E5%BB%BA%E5%8F%8A%E8%AE%A4%E8%AF%81/
列出私有仓库中的所有镜像
curl -k -X GET https://docker-registry:5000/v2/_catalog >> {"repositories":["mybusybox"]}
curl --cacert certs/docker-registry.crt https://docker-registry:5000/v2/_catalog
curl -k https://docker-registry:5000/v2/_catalog
Hyperledger Hurley is the development environment toolset
https://github.com/worldsibu/hurley#readme
[轉]Hyperledger Composer is deprecated
https://hackernoon.com/hyperledger-composer-is-deprecated-kuzqu31cb
Hyperledger KC Tam
https://medium.com/@kctheservant
http://www.ledgertech.biz/kcarticles.html
===========
https://hyperledger-fabric.readthedocs.io/en/release-1.4/tutorials.html
http://www.ledgertech.biz/kcarticles.html
===========
https://hyperledger-fabric.readthedocs.io/en/release-1.4/tutorials.html
[轉]Hyperledger Fabric BYFN之配置進階篇
https://www.itread01.com/content/1550412200.html
byfn.sh down會呼叫networkdDown去銷燬整個網路和已生成的配置。byfn.sh restart則不會清理。
...........
注意
docker-compose down會停止和刪除容器,網路,映象和對映的卷。
只是停止服務的話的會最好使用docker-compose stop。
docker-compose up則是建立和啟動容器服務, 這裡用於啟動。
我們也可以參考例子fabric-samples/fabcar, 它會重用fabric-samples/basic-network中的start.sh, stop.sh允許停止和重啟。
這自然是有的,但是必須開啟orderer, peer等的持久化配置。
...........
7051是Peer啟動的gRPC, 一般是客戶端應用接入。
7053是事件埠(Peer Event)
..........
Fabric 1.1之後peer event做了完全不同的設計,訊息的監聽不在peer節點了,而是基於channel,這樣設計提供了對Peer資料更細粒度的採訪控制和提供了接收訊息的可靠性。(官方文件是這麼扯,我也有點疑惑) 主要提供兩種服務,Deliver(通知提交到記賬本的整個區塊內容) 和DeliverFiltered(過濾一些區塊減少訊息通知返回的區塊大小)
1. byfn.sh up down會清理所有容器映象,生成的配置檔案和證書,現實場景應該可以停止或恢復區塊鏈網路,該如何處理?
byfn.sh down會呼叫networkdDown去銷燬整個網路和已生成的配置。byfn.sh restart則不會清理。
...........
注意
docker-compose down會停止和刪除容器,網路,映象和對映的卷。
只是停止服務的話的會最好使用docker-compose stop。
docker-compose up則是建立和啟動容器服務, 這裡用於啟動。
我們也可以參考例子fabric-samples/fabcar, 它會重用fabric-samples/basic-network中的start.sh, stop.sh允許停止和重啟。
2. byfn.sh還是fabcar兩個例子即使重啟區塊鏈網路,通道需要創新建立,節點需要重新加入通道,鏈碼也要全要重新安裝,如果節點多維護起來就麻煩且費時,重啟的時候能讓通道,節點,鏈碼,State DB自動恢復?
這自然是有的,但是必須開啟orderer, peer等的持久化配置。
...........
orderer.example.com:
...
- /mnt/hyperledger/orderer:/var/hyperledger/production/orderer
peer0.org1.example.com:
...
- /mnt/hyperledger/org1/peer0:/var/hyperledger/production
couchdb:
...
- /mnt/hyperledger/couchdb:/opt/couchdb/data
4. Peer節點我們配置了兩個埠,配置用來做什麼?
ports:
- 7051:7051
- 7053:7053
7051是Peer啟動的gRPC, 一般是客戶端應用接入。
7053是事件埠(Peer Event)
..........
Fabric 1.1之後peer event做了完全不同的設計,訊息的監聽不在peer節點了,而是基於channel,這樣設計提供了對Peer資料更細粒度的採訪控制和提供了接收訊息的可靠性。(官方文件是這麼扯,我也有點疑惑) 主要提供兩種服務,Deliver(通知提交到記賬本的整個區塊內容) 和DeliverFiltered(過濾一些區塊減少訊息通知返回的區塊大小)
Docker 本身DNS是不穩定的
再分享早上碰到docker穩定度問題,docker內到外網是靠本機的iptables做Nat出去,早上就發現運行很久的docker container,突然不送資料到ELK,一查發現DNS掛了,這之前也碰到幾次了,基本上不是中心DNS掛了,而且Docker本身架構的DNS掛了,基本上只要重啟docker service後就正常,無需對主機重啟,而重啟docker service是一件很嚴重的事情,因為上面所有的服務都會一併被下線,之後還要在把運行的服務全部重新上線…
也許我的理解是錯誤,但只能以目前的情況來判斷,也許是iptables nat轉換的問題,但本機沒有重開機,理論上就沒有這個問題才是。
也許我的理解是錯誤,但只能以目前的情況來判斷,也許是iptables nat轉換的問題,但本機沒有重開機,理論上就沒有這個問題才是。
[轉]What's New in Ethereum Serenity (2.0)
http://kimiwublog.blogspot.com/2018/12/whats-new-in-ethereum-serenity-20.html
需注意,時間是2018/12/06 內容肯不會是最正確的
需注意,時間是2018/12/06 內容肯不會是最正確的
[DIY] HyperLedger
1. Use docker-machine crate docker vm.
And install docker-compose.2. https://github.com/hyperledger/fabric-samples
# Fetch bootstrap.sh from fabric repository using
curl -sS https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh -o ./scripts/bootstrap.sh
# Change file mode to executable
chmod +x ./scripts/bootstrap.sh
# Download binaries and docker images
#./scripts/bootstrap.sh [version] [ca version] [thirdparty_version]
./scripts/bootstrap.sh
Here use all default value.
3. https://hyperledger-fabric.readthedocs.io/en/latest/build_network.html
# Start Run
cd fabric-samples/first-network
./byfn.sh generate
./byfn.sh up
4. Hope you see
===================== Query successful on peer1.org2 on channel 'mychannel' ============================== All GOOD, BYFN execution completed ===========
5. check docker ps Need like this
PS:
1. See Error
2019-11-06 02:22:34.819 UTC [main] InitCmd -> ERRO 001 Cannot run peer because cannot init crypto, folder "/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp" does not exist
!!!!!!!!!!!!!!! Channel creation failed !!!!!!!!!!!!!!!!
========= ERROR !!! FAILED to execute End-2-End Scenario ===========
How to Fix
cd fabric-samples/first-network
./byfn.sh down
./byfn.sh generate #This maybe don't need
./byfn.sh up
6. How to querying chaincode again
https://learnblockchain.cn/2019/07/03/chaincode-run/
docker exec -it cli bash
root@73e65944cade:/opt/gopath/src/github.com/hyperledger/fabric/peer>
root@73e65944cade:/opt/gopath/src/github.com/hyperledger/fabric/peer> peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
# peer chaincode query This command come from e2etest that you first time See This message:
========= All GOOD, BYFN execution completed ===========
Then just find screen up, can see "peer chaincode query ooxxooxxooxx". Please copy it then use in here.
[轉]SSLH 是一款采用 C 语言编写的开源端口复用软件,目前支持 HTTP、SSL、SSH、OpenVPN、Tinc、XMPP 等多种协议识别
https://mp.weixin.qq.com/s/sHitMzs8KXXO6joX8QqO_A?fbclid=IwAR1UGD7gemvKRY-IkOCEHGDru6YLDkI2vXojKpLTc-AZ4gN2Zz849YwJKZ8
SSLH 是一款采用 C 语言编写的开源端口复用软件,目前支持 HTTP、SSL、SSH、OpenVPN、Tinc、XMPP 等多种协议识别。它主要运行于 *nix 环境,源代码托管在 GitHub 上。
项目地址:https://github.com/yrutschle/sslh
更简单地说,SSLH 允许我们在 Linux 系统上的同一端口上运行多个程序/服务。因此,您可以用同一端口来同时使用两种服务。如果你遇到大多数端口被防火墙阻止的情况,SSLH 就可以帮你派上大用场。下面我们就来看一个 SSL 和 SSH 同时复用同一端口的实例。
SSLH 是一款采用 C 语言编写的开源端口复用软件,目前支持 HTTP、SSL、SSH、OpenVPN、Tinc、XMPP 等多种协议识别。它主要运行于 *nix 环境,源代码托管在 GitHub 上。
项目地址:https://github.com/yrutschle/sslh
更简单地说,SSLH 允许我们在 Linux 系统上的同一端口上运行多个程序/服务。因此,您可以用同一端口来同时使用两种服务。如果你遇到大多数端口被防火墙阻止的情况,SSLH 就可以帮你派上大用场。下面我们就来看一个 SSL 和 SSH 同时复用同一端口的实例。
[轉][Flutter]还在大量使用TextEditingController?试试这种解决方案
当用户输入大量信息时,通常会采用TextField列表来解决,这时如果需要对每个TextField进行控制或监听就需要大量的TextEditingController。
这里提供另一种思路,解决大量使用TextEditingController和信息整理困难的问题。
定义一个Map:
Map _userInfo;
这个Map作用于所有需要收集用户信息的地方。
使用TextField时这样操作:
这里全程未单独定义TextEditingController,只使用TextField默认的方法进行控制,且可以获得以下好处:
1.节省大量代码,简化业务逻辑。
2.用户输入完成时即得到一个包含所有信息的Map。
3.输入的内容不会丢失,只要这个Map不被销毁,下次进入(或返回)这个页面时所有内容都在,不用要求用户再次输入,提升用户体验。
第一次发文章,如有问题,欢迎指正。
作者:xSILENCEx
链接:https://juejin.im/post/5d649bfce51d453b1d648314
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
这里提供另一种思路,解决大量使用TextEditingController和信息整理困难的问题。
定义一个Map:
Map
这个Map作用于所有需要收集用户信息的地方。
使用TextField时这样操作:
////邮箱
TextField(
controller: TextEditingController(text: _userInfo['account']),
onChanged: (value) {
_userInfo['account'] = value;
},
),
////昵称
TextField(
controller: TextEditingController(text: _userInfo['nickname']),
onChanged: (value) {
_userInfo['nickname'] = value;
},
),
////密码
TextField(
controller: TextEditingController(text: _userInfo['password']),
onChanged: (value) {
_userInfo['password'] = value;
},
),
这里全程未单独定义TextEditingController,只使用TextField默认的方法进行控制,且可以获得以下好处:
1.节省大量代码,简化业务逻辑。
2.用户输入完成时即得到一个包含所有信息的Map。
3.输入的内容不会丢失,只要这个Map不被销毁,下次进入(或返回)这个页面时所有内容都在,不用要求用户再次输入,提升用户体验。
第一次发文章,如有问题,欢迎指正。
作者:xSILENCEx
链接:https://juejin.im/post/5d649bfce51d453b1d648314
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
dart null list
減少null判斷情況
https://stackoverflow.com/questions/51036758/flutter-the-method-map-was-called-on-null
https://juejin.im/post/5c6d38b1f265da2dcc7feb69
https://cloud.tencent.com/developer/article/1370380
?? => if null
https://blog.csdn.net/ffa_ijj/article/details/85051156
這個判斷方式有個問題,當查詢回來為[],不是null情況下,在.first會出錯,因為 "No element"會出錯,但.toList()會正確,故改回
https://stackoverflow.com/questions/51036758/flutter-the-method-map-was-called-on-null
https://juejin.im/post/5c6d38b1f265da2dcc7feb69
https://cloud.tencent.com/developer/article/1370380
?? => if null
https://blog.csdn.net/ffa_ijj/article/details/85051156
return qres?.map((value) {
LinkedHashMap result = json.decode(value["jsonitem"]);
return result;
})?.first ?? new LinkedHashMap();
這個判斷方式有個問題,當查詢回來為[],不是null情況下,在.first會出錯,因為 "No element"會出錯,但.toList()會正確,故改回
if (qres == null || qres.isEmpty) return new Member(); // 待確定是否出錯
var resmap = qres.first;
return new Member.fromJson(json.decode(resmap["jsonitem"]));
git 不同專案使用不同帳號
git global 已設定,但其他專案需要設定其他的帳號
到專案目錄下的.git內,修改config檔案,補上
[user]
name = ooxx
email = ooxx@gmail.com
即可蓋過global
到專案目錄下的.git內,修改config檔案,補上
[user]
name = ooxx
email = ooxx@gmail.com
即可蓋過global
dart json serialisation dson
https://github.com/drails-dart/dart-serialise
https://github.com/parnham/dart-serialise
https://github.com/dart-league/dson
https://github.com/parnham/dart-serialise
https://github.com/dart-league/dson
json to dart flutter
https://javiercbk.github.io/json_to_dart/
https://ashamp.github.io/jsonToDartModel/
https://ashamp.github.io/jsonToDartModel/
[轉]Remix now force to use https, how can i continue to use http://remix.ethereum.org?
http://remix.ethereum.org
Not https://remix.ethereum.org
Not https://remix.ethereum.org
[轉]kubernetes高可用安装工具,一条命令,离线安装,包含所有依赖,内核负载不依赖haproxy keepalived,纯golang开发,99年证书,
https://github.com/fanux/sealos
[轉]datatable datatables nodejs
https://newcodingera.com/datatables-server-side-processing-using-nodejs-mysql/
https://1drv.ms/u/s!At2KEcnT9DVaiw88MAEXeML0bRf3
======================
datatables mongodb
https://github.com/deepikagunda/datatables
https://1drv.ms/u/s!At2KEcnT9DVaiw88MAEXeML0bRf3
Important! SqlInjection
======================
datatables mongodb
https://github.com/deepikagunda/datatables
[轉]secp256k1簽章的message,長度都必須是256 bits,也就是32 bytes
https://medium.com/@antonassocareer/web3-secp256k1-%E7%B0%BD%E7%AB%A0%E8%88%87solidity%E9%A9%97%E7%AB%A0-26ded518cfdc
那麼如果我們想要單純用私鑰簽章一段資料,不要有Ethereum定義的那些prefix的話,就必須要直接調用 secp256k1 這一包library了。不過在用之前要知道,所有要丟給secp256k1簽章的message,長度都必須是256 bits,也就是32 bytes。剛剛我們說web3的簽名函式丟什麼都可以,是因為它會幫我加上prefix之後再做sha3 Hash (keccak),最後一定會變成一個32 bytes的東西。如果我們自己純靠私要簽章訊息的話,也勢必要先通過這個函式來整理input長度
那麼如果我們想要單純用私鑰簽章一段資料,不要有Ethereum定義的那些prefix的話,就必須要直接調用 secp256k1 這一包library了。不過在用之前要知道,所有要丟給secp256k1簽章的message,長度都必須是256 bits,也就是32 bytes。剛剛我們說web3的簽名函式丟什麼都可以,是因為它會幫我加上prefix之後再做sha3 Hash (keccak),最後一定會變成一個32 bytes的東西。如果我們自己純靠私要簽章訊息的話,也勢必要先通過這個函式來整理input長度
sha3 Hash (keccak)
bip39 mnemonic bip32 seed ed25519 elliptic
const bip39 = require('bip39')
const bip32 = require('bip32');
const EC = require('elliptic').ec;
json =`test json file`
mnemonic = "簡 熙 夢 幾 聲 可 高 汪 煙 版 統 仇"
path = "m/2018'/5'/1'/0/1"
const sJWSinit = async () => {
console.log('-----sJWS Initial Start----- \n');
dkey = await DeriveKey(mnemonic, path)
console.log("\nGet dkey: %o \n", dkey)
console.log('\n-----elliptic ed25519 Start----- \n');
var EdDSA = require('elliptic').eddsa
var ec = new EdDSA('ed25519');
var eckeypair = ec.keyFromSecret(dkey.privateKey)
var privateKeyHex = new Buffer.from(eckeypair.getSecret()).toString('hex')
var publickeyHex = new Buffer.from(eckeypair.getPublic()).toString('hex')
console.log("private key hex: %o", privateKeyHex)
console.log("public key hex: %o\n", publickeyHex)
var signature = eckeypair.sign(json).toHex();
console.log("signature: %o\n", signature)
var ec2 = new EdDSA('ed25519');
var ec2keypair2 = ec2.keyFromPublic(publickeyHex, 'hex');
console.log("EdDSA json verify: %o", ec2keypair2.verify(json, signature));
}
(async () => {
console.log("--- aysnc sJWS init---")
await sJWSinit();
})();
async function DeriveKey(mnemonic, derivePath) {
if (bip39.validateMnemonic(mnemonic)) { console.log("mnemonic is fake!") }
return bip39.mnemonicToSeed(mnemonic).then((vseed)=>{
var root = bip32.fromSeed(vseed)
var PathNode = root.derivePath(derivePath)
console.log("# PATH 是 m/2018'/5'/1'/0/1/0 因為底下為derive(0),所以 path + '/0' \n")
console.log("privateKey (Hex): %o", PathNode.derive(0).privateKey.toString('hex'))
console.log("publicKey (Hex): %o", PathNode.derive(0).publicKey.toString('hex')) // 024ac10a81e3a0f86cb4dad68c6a26031d805a057f36048f80a5b91b1c2cb0588c 符合
return {
prv_buf: PathNode.derive(0).privateKey,
pub_buf: PathNode.derive(0).publicKey,
wif: PathNode.derive(0).toWIF(),
publicKey: PathNode.derive(0).publicKey.toString('hex'),
privateKey: PathNode.derive(0).privateKey.toString('hex'),
path: derivePath
}
}).catch((e) => {
console.error('handle error here: ', e.message)
})
}
bitcoinjs-lib HDNode.fromSeedBuffer error bip39 bip32 Address bitcoin ethereum
bitcoinSecp256r1.HDNode.fromSeedBuffer 無法使用,目前正確應該是用 bitcoinSecp256r1.bip32.fromSeed
jsrsasign 有異常
jsrsasign 有異常
const bip39 = require('bip39')
const bip32 = require('bip32');
const bitcoinSecp256r1 = require('bitcoinjs-lib')
const ethUtil = require('ethereumjs-util')
const EC = require('elliptic').ec;
// bitcoinSecp256r1.HDNode.fromSeedBuffer 無法使用,目前正確應該是用 bitcoinSecp256r1.bip32.fromSeed
mnemonic = "簡 熙 夢 幾 聲 可 高 汪 煙 版 統 仇"
path = "m/2018'/5'/1'/0/1"
type = "secp256r1"
// 驗證網頁 https://iancoleman.io/bip39/#chinese_traditional
if (bip39.validateMnemonic(mnemonic)) { console.log("mnemonic is fake!") }
const seed = bip39.mnemonicToSeed(mnemonic).then((vseed)=>{
var root = bip32.fromSeed(vseed)
var PathNode = root.derivePath(path)
console.log("---------------------------------------------")
console.log("# PATH 是 m/2018'/5'/1'/0/1/ \n")
console.log("Bitcoin Address: %o 符合 \n", getAddress(PathNode)) // 1GcgQJN7XgqkZkQcD4dzaZ7bjCFvQ6wxF2 符合 m/2018'/5'/1'/0/1
console.log("root toWIF: %o", root.toWIF())
console.log("PathNode toWIF: %o 符合", PathNode.toWIF()) // Kzq7FAYiWjDAcwU44FvcyCsCpJyLCD19n13FyQgLY6oBNajYcAYz 符合 m/2018'/5'/1'/0/1
console.log("--------------------------------------------- \n")
// 底下為derive(0),所以正確是 m/2018'/5'/1'/0/1/0 為 path + '/0'
console.log("---------------------------------------------")
console.log("# PATH 是 m/2018'/5'/1'/0/1/0 因為底下為derive(0),所以 path + '/0' \n")
console.log("privateKey (WIF): %o 符合", PathNode.derive(0).toWIF()) // L5ccMER4KyRn6pY6amvrFAHacpEsKrH1eTjDNeWwgXMnqjSCUU6N 符合
console.log("privateKey (Buffer): %o", PathNode.derive(0).privateKey)
console.log("privateKey (String): %o", PathNode.derive(0).privateKey.toString())
console.log("privateKey (Hex): %o", PathNode.derive(0).privateKey.toString('hex'))
console.log("privatekeyHex: %o \n", PathNode.derive(0).privkeyHex)
console.log("publicKey (Hex): %o 符合", PathNode.derive(0).publicKey.toString('hex')) //024ac10a81e3a0f86cb4dad68c6a26031d805a057f36048f80a5b91b1c2cb0588c 符合
console.log("Bitcoin Address: %o 符合", getAddress(PathNode.derive(0))) //1Gp8AuHiYyBixrvLkKtC4VDhxpvK8PmYEr 符合
console.log("--------------------------------------------- \n")
console.log('\n-----elliptic Initial Start----- \n');
var ec = new EC('p256');
let keyPair = ec.keyFromPrivate("83CFCC6EF1864C3303A5F8DEF2540167CB2DFA5DD22BB8D197B396972525FD56")
let pubKey = keyPair.getPublic();
console.log("pubKey: %o", pubKey)
// https://github.com/kjur/jsrsasign/issues/394
// sha512('aaa') => d6f644b19812e97b5d871658d6d3400ecd4787faeb9b8990c1e7608288664be77257104a58d033bcf1a0e0945ff06468ebe53e2dff36e248424c7273117dac09
let msgHash = 'd6f644b19812e97b5d871658d6d3400ecd4787faeb9b8990c1e7608288664be7'
let signatureBase64 = 'MEUCIBEcfv2o3UwqwV72CVuYi7HbjcoiuSQOULY5d+DuGt3UAiEAtoNrdNWvjfdz/vR6nPiD+RveKN5znBtYaIrRDp2K7Ks='
let signatureHex = Buffer.from(signatureBase64, 'base64').toString('hex');
let validSig = ec.verify(msgHash, signatureHex, pubKey);
console.log("Signature valid? %o \n", validSig);
// use json
var ec = new EC('secp256k1');
keyPair = ec.keyFromPrivate(dkey.publicKey)
pubKey = keyPair.getPublic();
console.log("pubKey: %o", pubKey)
var signature = keyPair.sign(json);
var derSign = signature.toDER();
//console.log("signature: %o", signature)
console.log("json verify: %o", keyPair.verify(json, derSign));
console.log('\n-----elliptic ed25519 Start----- \n');
var EdDSA = require('elliptic').eddsa
var ec2 = new EdDSA('ed25519');
var ec2keypair = ec2.keyFromSecret(dkey.privateKey)
//console.log("key: %o", key)
var signature = ec2keypair.sign(json).toHex();
console.log("signature: %o", signature)
var privateKeyHex = new Buffer(ec2keypair.getSecret()).toString('hex')
var publickeyHex = new Buffer(ec2keypair.getPublic()).toString('hex')
console.log("private key hex: %o", privateKeyHex)
console.log("public key hex: %o", publickeyHex)
var ec2keypair2 = ec2.keyFromPublic(publickeyHex, 'hex');
console.log("EdDSA json verify: %o", ec2keypair2.verify(json, signature));
bip39.mnemonicToSeed(mnemonic).then((vseed)=>{
var root = bitcoinSecp256r1.bip32.fromSeed(vseed)
var PathNode = root.derivePath(path)
console.log("bitcoinSecp256r1 privateKey (Hex): %o", PathNode.derive(0).privateKey.toString('hex'))
console.log("bitcoinSecp256r1 publicKey (Hex): %o", PathNode.derive(0).publicKey.toString('hex'))
const buf = Buffer.allocUnsafe(32);
new Buffer.from(msgHash).copy(buf, 0, 0, 32)
//msgbuf32 = new Buffer("01234567890123456789012345678901")
console.log("msgHash buf 32: %o", buf.toString("hex"))
var ecPair = bitcoinSecp256r1.ECPair.fromPrivateKey(PathNode.derive(0).privateKey)
var signstring = ecPair.sign(buf)
console.log("signstring: %o", signstring.toString("hex"))
var verifyresult = ecPair.verify(buf, signstring)
console.log("verify: %o", verifyresult)
})
})
DeriveKey(mnemonic, path, type).then((v)=>{
console.log("dkey: %o", v)
});
function getAddress (node, network) {
return bitcoinSecp256r1.payments.p2pkh({ pubkey: node.publicKey, network }).address
}
function getEthereumAddress(privkeyHex) {
const hexAddress = ethUtil.privateToAddress(Buffer.from(privkeyHex, 'hex')).toString('hex')
const checksumAddress = ethUtil.toChecksumAddress(hexAddress)
return checksumAddress
}
function DeriveKey(mnemonic, derivePath, type) {
switch (type) {
case "secp256r1":
if (bip39.validateMnemonic(mnemonic)) { console.log("mnemonic is fake!") }
return bip39.mnemonicToSeed(mnemonic).then((vseed)=>{
var root = bip32.fromSeed(vseed)
var PathNode = root.derivePath(derivePath)
console.log("# PATH 是 m/2018'/5'/1'/0/1/0 因為底下為derive(0),所以 path + '/0' \n")
console.log("privateKey (Hex): %o", PathNode.derive(0).privateKey.toString('hex'))
console.log("publicKey (Hex): %o 符合", PathNode.derive(0).publicKey.toString('hex')) // 024ac10a81e3a0f86cb4dad68c6a26031d805a057f36048f80a5b91b1c2cb0588c 符合
const buf = Buffer.allocUnsafe(32);
PathNode.derive(0).privateKey.copy(buf, 0, 0, 32)
console.log("Ethereum Address: %o 符合", getEthereumAddress(buf.toString('hex')) ) // 0xe020343a09086F53a203c9A0Ea76010049399575 符合
return {
pub_buf: PathNode.derive(0).publicKey,
wif: PathNode.derive(0).toWIF(),
publicKey: PathNode.derive(0).publicKey.toString('hex'),
privateKey: PathNode.derive(0).privateKey.toString('hex'),
ethAddress: getEthereumAddress(buf.toString('hex')),
path: derivePath
}
}).catch((e) => {
console.log('handle error here: ', e.message)
})
break;
default:
throw "type should be secp256k1 or secp256r1";
}
}
smart contract storage struct 未初始化 直接使用問題
https://github.com/knownsec/Ethereum-Smart-Contracts-Security-CheckList/blob/master/%E4%BB%A5%E5%A4%AA%E5%9D%8A%E6%99%BA%E8%83%BD%E5%90%88%E7%BA%A6%E5%AE%A1%E8%AE%A1CheckList.md#11-%E6%9C%AA%E5%88%9D%E5%A7%8B%E5%8C%96%E7%9A%84%E5%82%A8%E5%AD%98%E6%8C%87%E9%92%88
https://www.chaindd.com/3102377.html
https://blog.b9lab.com/storage-pointers-in-solidity-7dcfaa536089
https://medium.com/loom-network/ethereum-solidity-memory-vs-storage-how-to-initialize-an-array-inside-a-struct-184baf6aa2eb
Use delete or new
https://www.chaindd.com/3102377.html
https://blog.b9lab.com/storage-pointers-in-solidity-7dcfaa536089
https://medium.com/loom-network/ethereum-solidity-memory-vs-storage-how-to-initialize-an-array-inside-a-struct-184baf6aa2eb
Use delete or new
web3 deploy smart contract 1.2.1
Use https://remix.ethereum.org Get Contract json and data.
In remix website, Compile finish. See Compliation Details.
1. ABI: click ABI buttion, get data. Use http://jsonviewer.stack.hu/ remove space
2. Compliation Details -> WEB3DEPLOY -> get data
3. cContract.options.from need put correct.
var Web3 = require("web3");
var provider = new Web3.providers.HttpProvider("http://ganache:8545");
var web3 = new Web3(provider);
//abi
var cContract = new web3.eth.Contract([{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}])
//bytecode
cContract.options.data = '0x608060405234801561001057600080fd5b5060bf8061001f6000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a72305820765480c908e5e28e3233e18bfa422944b42cad5fc08b77d7b22d3ddd7016a1380029'
cContract.options.from = '0xoooxxxoooxxxoooxxxoooxxxoooxxx'
cContract.options.gas = '4700000'
console.log("web3 version: %o", web3.version)
web3.eth.getAccounts().then(o=>{console.log(o)})
cContract.deploy().send()
.on('error', (error) => {
console.log("error: %o", error)
})
.on('transactionHash', (transactionHash) => {
console.log("transactionHash: %o", transactionHash)
})
.on('receipt', (receipt) => {
// receipt will contain deployed contract address
console.log("receipt: %o", receipt)
console.log("receipt.contractAddress: %o", receipt.contractAddress)
})
.on('confirmation', (confirmationNumber, receipt) => {
console.log("confirmationNumber: %o", confirmationNumber)
console.log("confirmation receipt: %o", receipt)
})
.then(function(newContractInstance){
if (typeof newContractInstance.options.address !== 'undefined') {
console.log('Contract mined! address: ' + newContractInstance.options.address);
}else{
console.log("newContractInstance.address is undefined!")
}
});
HDWalletProvider web3 smart contract connection need to stop or pool
HDWalletProvider engine.stop()
p = HDWalletProvider(MNEMONIC_SYSTEM, "http://ganache:8545", 0);
p.engine.stop();
HDWalletProvider engine.stop() and pool
export function GetHdProvider(id: number, rpcurl: string) {
//return new HDWalletProvider(MNEMONIC_SYSTEM, url, id);
return id==0?Provider1Pool.acquire():Provider2Pool.acquire();
}
// Normal use
const hdProvider0 = await GetHdProvider(0, providerUrl).then(function(client) {return client}).catch(function(err) {throw new HttpException(err.toString(), HttpStatus.BAD_REQUEST);});
const hdProvider1 = await GetHdProvider(1, providerUrl).then(function(client) {return client}).catch(function(err) {throw new HttpException(err.toString(), HttpStatus.BAD_REQUEST);});
// Normal release
ReleaseHDProvider1(hdProvider1 );
// Pool code
const genericPool = require("generic-pool");
const opts = {
max: 10, // maximum size of the pool
min: 2, // minimum size of the pool
idleTimeoutMillis: 30000,
log: true
};
const factory0 = { // maybe different parms
create: function() {
return new HDWalletProvider(MNEMONIC_SYSTEM, RPCURL, 0);
},
destroy: function(client) {
client.engine.stop();
}
};
const factory1 = { // maybe different parms
create: function() {
return new HDWalletProvider(MNEMONIC_SYSTEM, RPCURL, 1);
},
destroy: function(client) {
client.engine.stop();
}
};
export function ReleaseHDProvider1(client){
Provider1Pool.release(client);
console.log("Provider1Pool status: pool.size %o pool.available %o pool.pending %o", Provider1Pool.size, Provider1Pool.available, Provider1Pool.pending)
}
export function ReleaseHDProvider2(client){
Provider2Pool.release(client);
console.log("Provider2Pool status: pool.size %o pool.available %o pool.pending %o", Provider2Pool.size, Provider2Pool.available, Provider2Pool.pending)
}
const Provider1Pool = genericPool.createPool(factory0, opts);
const Provider2Pool = genericPool.createPool(factory1, opts);
Project專案
Triton Ho https://www.facebook.com/groups/616369245163622/permalink/1736421439825058/
今天不寫發大財的事,直接寫一下project planning的雜感好了。
——————————————————————————————————————————————————————
最兇險的專案不是那些deadline定得很趕的專案,而是那些沒有deadline的專案!
別以為「沒有deadline」是真的沒有deadline耶。
你想一下,跟你大大聲說「錢不是問題的人」,最終有120%是「反正我又沒錢,錢當然不是問題啊~」。
公司不是做慈善的,也不是給工程師來試新玩具的。所有的專案,最終還是要談C/P值。
(再次一句:工程就是來談C/P的,不談C/P請去當藝術家)
所謂「沒有deadline」,這很可能代表……
A) 你的主管現在忙別的專案管理,暫時沒空管這專案。
(然後等他有空回來看這專案時,就會問為何拖這麼久還結不了案了)
B) 你沒法拒絕其他人的需求改動。(背景聲:反正沒deadline,改一下需求去做得更好吧)
C) 你沒法有效限制專案的複雜性,大量的over-engineering。(背景聲:反正沒deadline,寫得好一點/多留一點空間,這樣子未來才容易維護嘛)
A+B+C的後果是……老闆過了三個月,突然有空來看看大家做什麼時,發現這專案已經過了二個月還沒有做完。
然後要求:都做了三個月了,現在再給你二星期時間來結案耶!
結果是:
要麼結不了案,投入大量心力的專案成為上不了市場的垃圾。
要麼是勉強能結案,但是有大量的over-engineering和一堆因為需求改了又改下不好的程式加構。
——————————————————————————————————————————————————————
雖然一堆入市未深的工程師整天會罵專案deadline為何定得那麼硬,一天都不能改……
但是嘛,先不談專案,我們談一下去旅行好了。
去旅行嘛,第一件事當然是跟公司申請休假,然後把信用卡丟給另一半叫她幫忙規劃(註:這是高度危險動作,好孩子絕對別學)。
http://xn--axxxx-fg1h292av9ap80aa49or2n76lzmxyz1bcr7i.com/,sxxxxxxxxx.com,txxxxxxxxxx.com上天天比價比優惠看旅行遊記的生活~
然後如果沒定下死線?應該我進棺材那一天還是在計劃行程中~
(謎之聲:某人站在你身後,她看起來很火……)
計劃行程的死線能不能改?當然不行,公司休假可是改不動啊!!!
回到公司專案,為何公司死線定得那麼硬,很多時候都是跟業務宣傳/合約罰款有關的。
你想像一下,一個手游專案要上市,當然不是先等程式完成後才慢慢宣傳衝人氣的。
一個遊戲要上市,當然是先定好上市日期,然後預先數月就要慢慢地製造話題,找人來做宣傳,一步一步炒熱氣氛。
然後人氣炒到最高點,玩家的期待度到達最大時,遊戲同步開賣大賺特賺那一波。
如果突然發現有bug,遊戲要延期一個月?
sorry囉,遊戲消費是不理性的,過了那一波熱潮,本來會付錢的玩家們早就付給另一個遊戲了。
讓話題多炒熱一個月去等bug先修好?
你以為是炒青菜,你想炒多久就多久嗎?
先不談熱度能否額外維持一個月還不消散。但是,高人流的宣傳管道,網上KOL,不是你想付錢就能立即買到的!
如果你沒法如期把遊戲上市,那麼你這個遊戲很可能就賠本賠很很大囉。
——————————————————————————————————————————————————————
專案deadline不能改,商業社會就是這樣子了。
追求要完美的,你應該去當藝術家不是當工程師的。
怎去在deadline前做完專案,固然跟你是否有留下足夠的buffering有關。
但是,這跟你的專案怎計劃也有很大關係的。
上古時期,有一堆人(CMMI)覺得,只要把文件寫得好,每一個專案把工序所用的時間都記錄下來。
然後你就能越來越變得成熟,能很精準地預估下一個專案所需時間了~
然後這些CMMI人大約會覺得:把台北的象山步道走100次,就能很精準地預估爬玉山攻項要多少時間了(笑~)
(香港版本:走城門水塘100次,就能預估蚺蛇尖攻項要多少時間了)
會行山的人都知道:
地圖/網上文章只能給你一個很基本的大概,一個路線最終要用多少時間/體力,你只能親自走一次才能答出來。
然後嘛……
很多表面上看起來相同的軟體專案,真正做下去時才發現是全新的未知領域……
——————————————————————————————————————————————————————
如果專案deadline不能改,那麼能改的就是:軟件的質素了。
以去旅行為例:
如果你有非常充份的時間,你買機票時大可以找不同銀行的信用卡優惠,看看飛行里數計劃,看看連同旅行一起訂的優惠……
如果沒時間,http://xn--skyxxxxxxx-rl5q.com/,輸入出發和回程時間,那一家最便宜就按下去算了。
一個能賣錢的軟體專案,正常應該可以再拆分為多個sub-task和milestone的。
重點:
首先開始做的,應該是最困難/你最沒法預估開發難度的工作。
A)
專案越早階段要改動上市日期,你能成功改動宣傳計劃的可能性就越高。
B)
越早發現專案進度不理想。之後比較容易的sub-task,你還是能以減少testcase coverage,刪掉不重要功能這些手段去追回進度。
如果你把困難而且必要的工作放在最後才做,那麼任何的預估錯誤就是專案延期囉~
(註1:一堆人覺得堅持一定要先寫testcase才能寫程式的……要麼他真的很幸福沒遇上過要衝死線的專案,要麼他活在童話世界……)
(註2:deadline前做不完的軟體功能嘛……如果不是關鍵性的,看看能不能當成bug再後補囉~)
——————————————————————————————————————————————————————
專案到底先做什麼:
我們以(重要/不重要),和(容易/困難)來給每一個sub-task排一下:
如果你明知deadline是完全不合理,你怎也沒可能把全部重要功能都做完,那麼就先做(重要+容易),在死線前能多做一個功能就多一個功能。
否則,先做(重要+困難)的。因為(重要+容易)的工作,常常是總有一點時間可以偷下來的。越近deadline,你越會珍惜你每一秒不做多餘的事。
重要工作全做完後,然後是(不重要+容易),在deadline前能做多少就多少。
(不重要+困難)工作嘛,讓他留在backlog算了。
——————————————————————————————————————————————————————
後記:
在只發了一篇文下,RDBMS課程普通票全賣光了
(謎之聲:你不是說五分鐘會搶光嗎?)
歡迎來買石虎愛心票耶XD
https://datasci.kktix.cc/events/rdbms20191005
另外,高流量雜感的淺談,定在10/10和10/11的早上9:30—12:00(二天內容相同)
請繼續耐心等候正式報名頁面……
今天不寫發大財的事,直接寫一下project planning的雜感好了。
——————————————————————————————————————————————————————
最兇險的專案不是那些deadline定得很趕的專案,而是那些沒有deadline的專案!
別以為「沒有deadline」是真的沒有deadline耶。
你想一下,跟你大大聲說「錢不是問題的人」,最終有120%是「反正我又沒錢,錢當然不是問題啊~」。
公司不是做慈善的,也不是給工程師來試新玩具的。所有的專案,最終還是要談C/P值。
(再次一句:工程就是來談C/P的,不談C/P請去當藝術家)
所謂「沒有deadline」,這很可能代表……
A) 你的主管現在忙別的專案管理,暫時沒空管這專案。
(然後等他有空回來看這專案時,就會問為何拖這麼久還結不了案了)
B) 你沒法拒絕其他人的需求改動。(背景聲:反正沒deadline,改一下需求去做得更好吧)
C) 你沒法有效限制專案的複雜性,大量的over-engineering。(背景聲:反正沒deadline,寫得好一點/多留一點空間,這樣子未來才容易維護嘛)
A+B+C的後果是……老闆過了三個月,突然有空來看看大家做什麼時,發現這專案已經過了二個月還沒有做完。
然後要求:都做了三個月了,現在再給你二星期時間來結案耶!
結果是:
要麼結不了案,投入大量心力的專案成為上不了市場的垃圾。
要麼是勉強能結案,但是有大量的over-engineering和一堆因為需求改了又改下不好的程式加構。
——————————————————————————————————————————————————————
雖然一堆入市未深的工程師整天會罵專案deadline為何定得那麼硬,一天都不能改……
但是嘛,先不談專案,我們談一下去旅行好了。
去旅行嘛,第一件事當然是跟公司申請休假,然後把信用卡丟給另一半叫她幫忙規劃(註:這是高度危險動作,好孩子絕對別學)。
http://xn--axxxx-fg1h292av9ap80aa49or2n76lzmxyz1bcr7i.com/,sxxxxxxxxx.com,txxxxxxxxxx.com上天天比價比優惠看旅行遊記的生活~
然後如果沒定下死線?應該我進棺材那一天還是在計劃行程中~
(謎之聲:某人站在你身後,她看起來很火……)
計劃行程的死線能不能改?當然不行,公司休假可是改不動啊!!!
回到公司專案,為何公司死線定得那麼硬,很多時候都是跟業務宣傳/合約罰款有關的。
你想像一下,一個手游專案要上市,當然不是先等程式完成後才慢慢宣傳衝人氣的。
一個遊戲要上市,當然是先定好上市日期,然後預先數月就要慢慢地製造話題,找人來做宣傳,一步一步炒熱氣氛。
然後人氣炒到最高點,玩家的期待度到達最大時,遊戲同步開賣大賺特賺那一波。
如果突然發現有bug,遊戲要延期一個月?
sorry囉,遊戲消費是不理性的,過了那一波熱潮,本來會付錢的玩家們早就付給另一個遊戲了。
讓話題多炒熱一個月去等bug先修好?
你以為是炒青菜,你想炒多久就多久嗎?
先不談熱度能否額外維持一個月還不消散。但是,高人流的宣傳管道,網上KOL,不是你想付錢就能立即買到的!
如果你沒法如期把遊戲上市,那麼你這個遊戲很可能就賠本賠很很大囉。
——————————————————————————————————————————————————————
專案deadline不能改,商業社會就是這樣子了。
追求要完美的,你應該去當藝術家不是當工程師的。
怎去在deadline前做完專案,固然跟你是否有留下足夠的buffering有關。
但是,這跟你的專案怎計劃也有很大關係的。
上古時期,有一堆人(CMMI)覺得,只要把文件寫得好,每一個專案把工序所用的時間都記錄下來。
然後你就能越來越變得成熟,能很精準地預估下一個專案所需時間了~
然後這些CMMI人大約會覺得:把台北的象山步道走100次,就能很精準地預估爬玉山攻項要多少時間了(笑~)
(香港版本:走城門水塘100次,就能預估蚺蛇尖攻項要多少時間了)
會行山的人都知道:
地圖/網上文章只能給你一個很基本的大概,一個路線最終要用多少時間/體力,你只能親自走一次才能答出來。
然後嘛……
很多表面上看起來相同的軟體專案,真正做下去時才發現是全新的未知領域……
——————————————————————————————————————————————————————
如果專案deadline不能改,那麼能改的就是:軟件的質素了。
以去旅行為例:
如果你有非常充份的時間,你買機票時大可以找不同銀行的信用卡優惠,看看飛行里數計劃,看看連同旅行一起訂的優惠……
如果沒時間,http://xn--skyxxxxxxx-rl5q.com/,輸入出發和回程時間,那一家最便宜就按下去算了。
一個能賣錢的軟體專案,正常應該可以再拆分為多個sub-task和milestone的。
重點:
首先開始做的,應該是最困難/你最沒法預估開發難度的工作。
A)
專案越早階段要改動上市日期,你能成功改動宣傳計劃的可能性就越高。
B)
越早發現專案進度不理想。之後比較容易的sub-task,你還是能以減少testcase coverage,刪掉不重要功能這些手段去追回進度。
如果你把困難而且必要的工作放在最後才做,那麼任何的預估錯誤就是專案延期囉~
(註1:一堆人覺得堅持一定要先寫testcase才能寫程式的……要麼他真的很幸福沒遇上過要衝死線的專案,要麼他活在童話世界……)
(註2:deadline前做不完的軟體功能嘛……如果不是關鍵性的,看看能不能當成bug再後補囉~)
——————————————————————————————————————————————————————
專案到底先做什麼:
我們以(重要/不重要),和(容易/困難)來給每一個sub-task排一下:
如果你明知deadline是完全不合理,你怎也沒可能把全部重要功能都做完,那麼就先做(重要+容易),在死線前能多做一個功能就多一個功能。
否則,先做(重要+困難)的。因為(重要+容易)的工作,常常是總有一點時間可以偷下來的。越近deadline,你越會珍惜你每一秒不做多餘的事。
重要工作全做完後,然後是(不重要+容易),在deadline前能做多少就多少。
(不重要+困難)工作嘛,讓他留在backlog算了。
——————————————————————————————————————————————————————
後記:
在只發了一篇文下,RDBMS課程普通票全賣光了
(謎之聲:你不是說五分鐘會搶光嗎?)
歡迎來買石虎愛心票耶XD
https://datasci.kktix.cc/events/rdbms20191005
另外,高流量雜感的淺談,定在10/10和10/11的早上9:30—12:00(二天內容相同)
請繼續耐心等候正式報名頁面……
HEX 0x string to []byte to string DecodeString
https://play.golang.org/p/i90_qsN2Sz-
https://onlineutf8tools.com/convert-hexadecimal-to-utf8
package main
import (
"fmt"
"encoding/hex"
)
func main() {
id := "0x1dd84569ad60faa1b6838526ba7156388cf7c8d376ed0ccda9bce0419c2c3519"
fmt.Printf("Ori ID: %v \n\n", id)
fmt.Printf("Ori ID[2:]: %v \n\n", id[2:])
byteid := []byte(id)
fmt.Printf("===== Byte id ===== Decimal \n")
fmt.Printf("Byte ID: %v \n", byteid)
fmt.Printf("Byte ID 0x%x \n\n", byteid)
fmt.Printf("===== Decode(Byte id[2:]) ===== Decimal HEX \n")
byteid = []byte(id[2:])
fmt.Printf("Byte ID: %v \n", byteid)
fmt.Printf("Byte ID 0x%x \n\n", byteid)
n, _ := hex.Decode(byteid, byteid)
fmt.Printf("Byte ID[2:]: %v \n", byteid)
fmt.Printf("Byte ID[2:] 0x%x \n", byteid)
fmt.Printf("Byte ID[2:] [:n]: %v \n", byteid[:n])
fmt.Printf("Byte ID[2:] [:n] 0x%x \n\n", byteid[:n])
fmt.Printf("===== id ===== Decimal \n")
idbyte32 := covertStringTByte32(id)
fmt.Printf("Byte32 ID: %v \n", idbyte32 )
//fmt.Printf("String ID: %s \n", idbyte32 )
fmt.Printf("HEX ID: 0x%x \n\n", idbyte32 )
fmt.Printf("===== id[2:] ===== Decimal \n")
idbyte32 = covertStringT2Byte32(id)
fmt.Printf("Byte32 ID: %v \n", idbyte32 )
//fmt.Printf("String ID: %s \n", idbyte32 )
fmt.Printf("HEX ID: 0x%x \n\n", idbyte32 )
fmt.Printf("===== DecodeString(id[2:]) ===== HEX \n")
idbyte32 = covertStringDecodeStringByte32(id)
fmt.Printf("Byte32 ID: %v \n", idbyte32 )
//fmt.Printf("String ID: %s \n", idbyte32 )
fmt.Printf("HEX ID: 0x%x \n", idbyte32 )
}
func covertStringTByte32(t string) [32]byte {
var b32 [32]byte
copy(b32[:], t)
return b32
}
func covertStringT2Byte32(t string) [32]byte {
var b32 [32]byte
copy(b32[:], t[2:]) //remove 0x
return b32
}
func covertStringDecodeStringByte32(t string) [32]byte {
data, err := hex.DecodeString(t[2:])
if err != nil {
fmt.Printf("ERR \n")
}
fmt.Printf("DecodeString data: %v \n", data)
fmt.Printf("DecodeString data length: %v \n\n", len(data))
var b32 [32]byte
copy(b32[:], data)
return b32
}
Ori ID: 0x1dd84569ad60faa1b6838526ba7156388cf7c8d376ed0ccda9bce0419c2c3519
Ori ID[2:]: 1dd84569ad60faa1b6838526ba7156388cf7c8d376ed0ccda9bce0419c2c3519
===== Byte id ===== Decimal
Byte ID: [48 120 49 100 100 56 52 53 54 57 97 100 54 48 102 97 97 49 98 54 56 51 56 53 50 54 98 97 55 49 53 54 51 56 56 99 102 55 99 56 100 51 55 54 101 100 48 99 99 100 97 57 98 99 101 48 52 49 57 99 50 99 51 53 49 57]
Byte ID 0x307831646438343536396164363066616131623638333835323662613731353633383863663763386433373665643063636461396263653034313963326333353139
===== Decode(Byte id[2:]) ===== Decimal HEX
Byte ID: [49 100 100 56 52 53 54 57 97 100 54 48 102 97 97 49 98 54 56 51 56 53 50 54 98 97 55 49 53 54 51 56 56 99 102 55 99 56 100 51 55 54 101 100 48 99 99 100 97 57 98 99 101 48 52 49 57 99 50 99 51 53 49 57]
Byte ID 0x31646438343536396164363066616131623638333835323662613731353633383863663763386433373665643063636461396263653034313963326333353139
Byte ID[2:]: [29 216 69 105 173 96 250 161 182 131 133 38 186 113 86 56 140 247 200 211 118 237 12 205 169 188 224 65 156 44 53 25 56 99 102 55 99 56 100 51 55 54 101 100 48 99 99 100 97 57 98 99 101 48 52 49 57 99 50 99 51 53 49 57]
Byte ID[2:] 0x1dd84569ad60faa1b6838526ba7156388cf7c8d376ed0ccda9bce0419c2c35193863663763386433373665643063636461396263653034313963326333353139
Byte ID[2:] [:n]: [29 216 69 105 173 96 250 161 182 131 133 38 186 113 86 56 140 247 200 211 118 237 12 205 169 188 224 65 156 44 53 25]
Byte ID[2:] [:n] 0x1dd84569ad60faa1b6838526ba7156388cf7c8d376ed0ccda9bce0419c2c3519
===== id ===== Decimal
Byte32 ID: [48 120 49 100 100 56 52 53 54 57 97 100 54 48 102 97 97 49 98 54 56 51 56 53 50 54 98 97 55 49 53 54]
HEX ID: 0x3078316464383435363961643630666161316236383338353236626137313536
===== id[2:] ===== Decimal
Byte32 ID: [49 100 100 56 52 53 54 57 97 100 54 48 102 97 97 49 98 54 56 51 56 53 50 54 98 97 55 49 53 54 51 56]
HEX ID: 0x3164643834353639616436306661613162363833383532366261373135363338
===== DecodeString(id[2:]) ===== HEX
DecodeString data: [29 216 69 105 173 96 250 161 182 131 133 38 186 113 86 56 140 247 200 211 118 237 12 205 169 188 224 65 156 44 53 25]
DecodeString data length: 32
Byte32 ID: [29 216 69 105 173 96 250 161 182 131 133 38 186 113 86 56 140 247 200 211 118 237 12 205 169 188 224 65 156 44 53 25]
HEX ID: 0x1dd84569ad60faa1b6838526ba7156388cf7c8d376ed0ccda9bce0419c2c3519
Program exited.
https://onlineutf8tools.com/convert-hexadecimal-to-utf8
[轉]Go-JWT-RESTful身份认证教程
https://segmentfault.com/a/1190000020329813
1.什么是JWT
JWT(JSON Web Token)是一个非常轻巧的规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息,
一个JWT由三部分组成,Header头部、Claims载荷、Signature签名,
JWT原理类似我们加盖公章或手写签名的的过程,合同上写了很多条款,不是随便一张纸随便写啥都可以的,必须要一些证明,比如签名,比如盖章,JWT就是通过附加签名,保证传输过来的信息是真的,而不是伪造的,
它将用户信息加密到token里,服务器不保存任何用户信息,服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证,
2.JWT构成
一个JWT由三部分组成,Header头部、Claims载荷、Signature签名,
Header头部:头部,表明类型和加密算法
Claims载荷:声明,即载荷(承载的内容)
Signature签名:签名,这一部分是将header和claims进行base64转码后,并用header中声明的加密算法加盐(secret)后构成,即:
let tmpstr = base64(header)+base64(claims)
let signature = encrypt(tmpstr,secret)
//最后三者用"."连接,即:
let token = base64(header)+"."+base64(claims)+"."+signature
1.什么是JWT
JWT(JSON Web Token)是一个非常轻巧的规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息,
一个JWT由三部分组成,Header头部、Claims载荷、Signature签名,
JWT原理类似我们加盖公章或手写签名的的过程,合同上写了很多条款,不是随便一张纸随便写啥都可以的,必须要一些证明,比如签名,比如盖章,JWT就是通过附加签名,保证传输过来的信息是真的,而不是伪造的,
它将用户信息加密到token里,服务器不保存任何用户信息,服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证,
2.JWT构成
一个JWT由三部分组成,Header头部、Claims载荷、Signature签名,
Header头部:头部,表明类型和加密算法
Claims载荷:声明,即载荷(承载的内容)
Signature签名:签名,这一部分是将header和claims进行base64转码后,并用header中声明的加密算法加盐(secret)后构成,即:
let tmpstr = base64(header)+base64(claims)
let signature = encrypt(tmpstr,secret)
//最后三者用"."连接,即:
let token = base64(header)+"."+base64(claims)+"."+signature
oauth2 NewClient InsecureSkipVerify
https://github.com/terraform-providers/terraform-provider-github/blob/master/github/config.go
ctx := context.Background()
insecureClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
ctx = context.WithValue(ctx, oauth2.HTTPClient, insecureClient)
client := oauth2.NewClient(ctx, oauth2.StaticTokenSource(&oauth2.Token{
AccessToken: c.Param("accesstoken"),
TokenType: "Bearer",
}))
resp, err := client.Get("https://ory-hydra-login-consent:9020/openid/userinfo")
if err != nil {
return newHTTPError(400, "InvalidToken", err.Error())
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return newHTTPError(400, "InvalidToken", err.Error())
}
c.Logger().Debugf("resp: %s", body)
var t map[string]interface{}
err = json.Unmarshal(body, &t)
if err != nil {
return newHTTPError(400, "InvalidToken", err.Error())
}
return c.JSON(http.StatusOK, t)
Google code view
https://medium.com/@ryanyang1221/%E8%AE%93-google-%E6%95%99%E4%BD%A0-code-review-be251d4d81b4
golang go-ethereum contract string to [32]byte
func covertStringByte32(t string) [32]byte {
var b32 [32]byte
copy(b32[:], []byte(t))
return b32
}
func covertStringByte64(t string) [64]byte {
var b64 [64]byte
copy(b64[:], []byte(t))
return b64
}
golang test e2e
httpexpect star 1159
https://github.com/gavv/httpexpect
https://github.com/gavv/httpexpect/blob/master/_examples/echo_test.go
goconvey
https://segmentfault.com/a/1190000014924022
https://github.com/smartystreets/goconvey/
baloo star 652
https://github.com/h2non/baloo
frisby star 249
https://github.com/verdverm/frisby
apitest star 121
https://github.com/steinfletcher/apitest
https://github.com/gavv/httpexpect
https://github.com/gavv/httpexpect/blob/master/_examples/echo_test.go
goconvey
https://segmentfault.com/a/1190000014924022
https://github.com/smartystreets/goconvey/
baloo star 652
https://github.com/h2non/baloo
frisby star 249
https://github.com/verdverm/frisby
apitest star 121
https://github.com/steinfletcher/apitest
go mod custome model
https://stackoverflow.com/questions/52079662/go-get-cannot-find-local-packages-when-using-multiple-modules-in-a-repo
api/contracts/contract.go
package contracts
import (
"math/big"
"strings"
)....
func DeployContract(...)
api/contract.go
package main
import (
"api/contracts"
...)
func deployContract(c echo.Context) error {
address, tx, token, err := contracts.DeployContract(auth, client)
...
}
! Best Import is api/go.mod
module api
require (
api/contracts v0.0.0
...)
replace (
api/contracts v0.0.0 => ./contracts
...)
And api/contracts/go.mod
module api/contracts
require (
...)
solc abigen docker
sol file put in ~/contracts/sol
File list
~/contracts/sol/Contracts.solRUN
docker run -v ~/contracts:/sources ethereum/solc:0.4.23 -o /sources --abi --bin /sources/sol/Contract.sol
File list
~/contracts/sol/Contracts.sol~/contracts/Contracts.abi
~/contracts/Contracts.bin
1. 0.4.23 check your contract version. This example is pragma solidity ^0.4.23;
2. sol name need be changeed. This example is Contract.sol
3. Permission denied just use root
RUN
docker run -v ~/contracts:/sources ethereum/client-go:alltools-v1.9.2 abigen --abi /sources/Contract.abi --pkg contracts --type Contract --out /sources/Contract.go --bin /sources/Contract.bin
File list
~/contracts/sol/Contracts.sol~/contracts/Contracts.abi
~/contracts/Contracts.bin
~/contracts/Contracts.go
1. Contract.abi Contract.go must change by solc output
2. pkg is go source code:package contracts
3. type is go source code:type Cert struct { }
4. bin is go source code:be add "var ContractBin = ..." and "func DeployContract(..."
Error:
docker run -v ~/contracts/sol:/sources ethereum/client-go:alltools-v1.9.2 abigen --sol /sources/Cert.sol --pkg contracts --out /sources/Cert.go
Fatal: Failed to build Solidity contract: exec: "solc": executable file not found in $PATH
Source:
https://solidity.readthedocs.io/en/develop/installing-solidity.html#docker
https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts#generating-the-bindings
https://hub.docker.com/r/ethereum/client-go
https://github.com/ethereum/go-ethereum/blob/master/cmd/abigen/main.go
go-eth
https://medium.com/taipei-ethereum-meetup/%E4%BD%BF%E7%94%A8-go-%E8%88%87%E4%BB%A5%E5%A4%AA%E5%9D%8A%E5%8D%80%E5%A1%8A%E9%8F%88%E4%BA%92%E5%8B%95-%E4%B8%89-7b7b1f40c06a
https://github.com/sc0Vu/go-eth
https://github.com/sc0Vu/go-eth
golang echo rest demo
https://github.com/hyacinthus/restdemo/blob/master/GOLANG-RESTFUL-API.pdf
https://github.com/hyacinthus/restdemo
https://github.com/hyacinthus/restdemo
Cordova plugin Cache problem
https://stackoverflow.com/questions/26481507/how-to-force-cordova-to-recompile-added-plugins
Cordova plugin會cache
https://stackoverflow.com/questions/26481507/how-to-force-cordova-to-recompile-added-plugins
1、先remove
2、再加回
為了以上動作執行確實,不要直接去改plugin內容,採用下例方式:
1、建立 keep_plugs
2、將plugin放到裡面,然後再add到專案
3、修改後,重新執行remove、add
cordova plugin remove cordova-android-toast
cordova plugin add keep_plugs/cordova-android-toast
Cordova plugin會cache
https://stackoverflow.com/questions/26481507/how-to-force-cordova-to-recompile-added-plugins
1、先remove
2、再加回
為了以上動作執行確實,不要直接去改plugin內容,採用下例方式:
1、建立 keep_plugs
2、將plugin放到裡面,然後再add到專案
3、修改後,重新執行remove、add
cordova plugin remove cordova-android-toast
cordova plugin add keep_plugs/cordova-android-toast
[轉]Server Side Pagination Using Node and Mongo
https://codeforgeek.com/server-side-pagination-using-node-and-mongo/
Run ubuntu docker like VM
https://jimmylab.wordpress.com/2018/12/05/ssh-docker-container/
CMD ["/usr/sbin/sshd", "-D"]
https://hub.docker.com/r/rastasheep/ubuntu-sshd/
CMD ["/usr/sbin/sshd", "-D"]
https://hub.docker.com/r/rastasheep/ubuntu-sshd/
flutter dart json decode encode
Very Important!
zhangqi : [ .... ]
chaojiangeren: [ .... ]
zhangqi : [ {....}, {....} ]
chaojiangeren: [ {....}, {....} ]
If have this, json.decode can't List
List<dynamic> => List<ItemName>
List<dynamic> => List<ItemNameAddr>
{"107年工作":{"zhangqi":[{"name":"王大明1"},{"name":"孫小明1"}],"chaojiangeren":[{"name":"nnn1","addr":"aaa1"},{"name":"nnn2","addr":"aaa2"}]},"108年法會":{"zhangqi":[{"name":"王大明1"},{"name":"孫小明1"}]}}
zhangqi : [ .... ]
chaojiangeren: [ .... ]
zhangqi : [ {....}, {....} ]
chaojiangeren: [ {....}, {....} ]
Can't have \' or \"
If have this, json.decode can't List
List<dynamic> => List<ItemName>
List<dynamic> => List<ItemNameAddr>
List<ItemName> zhangqi = List<ItemName>();
zhangqi.add(new ItemName(name:"王大明1"));
zhangqi.add(new ItemName(name:"孫小明1"));
List<ItemNameAddr> chaojiangeren = List<ItemNameAddr>();
chaojiangeren.add(new ItemNameAddr(name:"nnn1", addr:"aaa1"));
chaojiangeren.add(new ItemNameAddr(name:"nnn2", addr:"aaa2"));
class ItemName {
String name;
ItemName({ this.name }) ;
ItemName.fromJson(Map<String, dynamic> json) : name = json['name'];
Map toJson() => {"name": name }; //給json.encode使用,沒有的話,會錯誤
}
class ItemNameAddr {
String name;
String addr;
ItemNameAddr({ this.name, this.addr }) ;
ItemNameAddr.fromJson(Map<String, dynamic> json) : name = json['name'], addr = json['addr'];
Map toJson() => {"name": name, "addr": addr}; //給json.encode使用,沒有的話,會錯誤
}
Future<List<ItemName>> getListItemName(List maps) async {
return new List<ItemName>.generate(maps.length, (i) {
return ItemName(
name: maps[i]['name'],
);
});
}
Future<List<ItemNameAddr>> getListItemNameAddr(List maps) async {
return new List<ItemNameAddr>.generate(maps.length, (i) {
return ItemNameAddr(
name: maps[i]['name'],
addr: maps[i]['addr'],
);
});
}
ethereum sign verify ECDSA part 3 Final ethereumjs-util Elliptic secp256k1
https://medium.com/@antonassocareer/web3-secp256k1-%E7%B0%BD%E7%AB%A0%E8%88%87solidity%E9%A9%97%E7%AB%A0-26ded518cfdc
Elliptic 和 secp256k1 各別需要不同的方式,請閱code
phone vs secp256k1 vs ethereumjs-util
那代表 phone 產生的是符合ethereum的格式
但因為 signed的長度不符合標準的 secp256k1 ,所以只能用ethereumjs-util的工具,從fromRpcSig 匯入處理,取得 s r v ,後就能進行處理了!
Elliptic 和 secp256k1 各別需要不同的方式,請閱code
const secp256k1 = require('secp256k1')
const ejsu = require('ethereumjs-util')
Web3 = require("web3")
var web3 = new Web3(Web3.givenProvider || 'ws://some.local-or-remote.node:8546');
// phone
// private key
// address 0xAD44A8ea9A9Bb5eF66F041BB921A687331729eB4
// Message Signature Has 0xa05ac71b16172777f683edbc48e9709cffd713a82630232d7c98e0f0df5201d60329658dba83b53fed49307e03d9663c0d2e4476c8b7925c2ed02cc7f881f5271b
// Message Hello
// public key 034e17dc4aef81e0ce6d16686be5e194274795375fc5525f1cdc46fe0b4643d5d6
console.log("\n----- phone -----\n")
var buf_Signed = Buffer.from(web3.utils.hexToBytes("0xa05ac71b16172777f683edbc48e9709cffd713a82630232d7c98e0f0df5201d60329658dba83b53fed49307e03d9663c0d2e4476c8b7925c2ed02cc7f881f5271b"))
var buf_pubkey = Buffer.from(web3.utils.hexToBytes("0x034e17dc4aef81e0ce6d16686be5e194274795375fc5525f1cdc46fe0b4643d5d6"))
console.log("\x1b[32m Public Key: \x1b[0m %s \nlength: %s \n", web3.utils.bytesToHex(buf_pubkey), buf_pubkey.length)
console.log("\x1b[32m singature: \x1b[0m %o \nlength: %s \n", buf_Signed, buf_Signed.length)
// ethereumjs-util import signature fromRpcSig
console.log("\n===== ethereumjs-util =====\n")
var fromSigned = ejsu.fromRpcSig(buf_Signed)
console.log("\x1b[32m fromSigned: %o \n", fromSigned)
var message = "Hello"
prefix = '\x19Ethereum Signed Message:\n' + message.length.toString()
console.log("\x1b[32m prefix: \x1b[0m %s \n", prefix)
var message2 = prefix + message
var buf_msgHash2 = ejsu.keccak256(message2); // this is ok
var buf_msgHash = Buffer.from(web3.utils.hexToBytes(web3.utils.soliditySha3(prefix, message)))
var ecrecover_public_key = ejsu.ecrecover(buf_msgHash, fromSigned.v, fromSigned.r, fromSigned.s)
console.log("\x1b[32m ecrecover_public_key: \x1b[0m %s \n", web3.utils.bytesToHex(ecrecover_public_key))
var address = ejsu.pubToAddress(ecrecover_public_key)
console.log("\x1b[32m address: \x1b[0m %s \n", web3.utils.bytesToHex(address))
console.log("\x1b[32m toChecksumAddress address: \x1b[0m %s \n", ejsu.toChecksumAddress(web3.utils.bytesToHex(address)))
console.log("\x1b[32m address is same address: \x1b[0m %s \n", ejsu.toChecksumAddress(web3.utils.bytesToHex(address)) == '0xAD44A8ea9A9Bb5eF66F041BB921A687331729eB4')
// Elliptic
console.log("\n===== Elliptic-util =====\n")
var EC = require('elliptic').ec;
var ec = new EC('secp256k1');
//var key = ec.genKeyPair();
var key = ec.keyFromPublic(buf_pubkey); // No Private Key
//r s https://github.com/ethereumjs/ethereumjs-util/blob/599ba5b1c7043a7e155e6032c50d7a01fc63aaf1/src/signature.ts#L70
var r = buf_Signed.slice(0, 32);
var s = buf_Signed.slice(32, 64);
console.log("\x1b[32m Elliptic verify: \x1b[0m %s \n", key.verify(buf_msgHash, {r: r, s: s}));
//secp256k1
console.log("\n===== secp256k1 =====\n")
var DER_signature = secp256k1.signatureExport(buf_Signed.slice(0, 64))
var signature = secp256k1.signatureImport(DER_signature)
console.log("\x1b[32m phone Signed -> DER Signed -> signatureImport: \x1b[0m %s \nlength: %s \n", web3.utils.bytesToHex(signature), signature.length)
console.log("\x1b[32m secp256k1 verify: \x1b[0m %s \n", secp256k1.verify(buf_msgHash, signature, buf_pubkey));
console.log("\n----- phone End -----\n")
// secp256k1
// private key random => 0x9fc00a13bf199dc5606da92d61438c680eeddec04f7a1833405c1466a81c9bd7
console.log("\n----- secp256k1 -----\n")
var buf_PrivateKey = Buffer.from(web3.utils.hexToBytes('0x9fc00a13bf199dc5606da92d61438c680eeddec04f7a1833405c1466a81c9bd7'))
var buf_PublicKey = secp256k1.publicKeyCreate(buf_PrivateKey)
console.log("\x1b[32m Public Key: \x1b[0m %s \nlength: %s \n", web3.utils.bytesToHex(buf_PublicKey), buf_PublicKey.length)
var buf_msg = Buffer.alloc(32, "Hello")
var singature = secp256k1.sign(buf_msg, buf_PrivateKey)
console.log("\x1b[32m singature: \x1b[0m %o \nlength: %s \n", singature, singature.signature.length)
console.log("\x1b[32m singature: \x1b[0m %s \n", web3.utils.bytesToHex(singature.signature))
var recover_public_key = secp256k1.recover(buf_msg, singature.signature, singature.recovery)
console.log("\x1b[32m Get Back Pubkey: \x1b[0m %s \n", web3.utils.bytesToHex(recover_public_key))
console.log("\x1b[32m recover_public_key is same PublicKey: \x1b[0m %s", web3.utils.bytesToHex(recover_public_key) == web3.utils.bytesToHex(buf_PublicKey))
console.log("\n----- secp256k1 End -----\n")
// ganache
// private key 0x75b25b96be4313c5a102bd4daa6bbeb71414f23e0ae15c0f93fa6d17866003da
// addresss 0xf8d3A2033ebfc7778CD59f676235a8E431b6eeD7
console.log("\n----- ganache -----\n")
// ganache part is OK
var buf_PrivateKey = Buffer.from(web3.utils.hexToBytes('0x75b25b96be4313c5a102bd4daa6bbeb71414f23e0ae15c0f93fa6d17866003da'))
var buf_PublicKey = ejsu.privateToPublic(buf_PrivateKey)
var buf_Address = ejsu.privateToAddress(buf_PrivateKey)
console.log("\x1b[32m Public Key: \x1b[0m %s \nlength: %s \n", web3.utils.bytesToHex(buf_PublicKey), buf_PublicKey.length)
console.log("\x1b[32m Address: \x1b[0m %s \nlength: %s \n", web3.utils.bytesToHex(buf_Address), buf_Address.length)
var message = "Hello"
var buf_msgHash = ejsu.keccak256(message);
var singature = ejsu.ecsign(buf_msgHash, buf_PrivateKey)
console.log("\x1b[32m singature: \x1b[0m %o \n", singature) // have r s v
var ecrecover_public_key = ejsu.ecrecover(buf_msgHash, singature.v, singature.r, singature.s)
console.log("\x1b[32m ecrecover_public_key: \x1b[0m %s \n", web3.utils.bytesToHex(ecrecover_public_key))
console.log("\x1b[32m ecrecover_public_key is same PublicKey: \x1b[0m %s", web3.utils.bytesToHex(ecrecover_public_key) == web3.utils.bytesToHex(buf_PublicKey))
console.log("\n----- ganache End -----\n")
Result
----- phone -----
Public Key: 0x034e17dc4aef81e0ce6d16686be5e194274795375fc5525f1cdc46fe0b4643d5d6
length: 33
singature: <Buffer a0 5a c7 1b 16 17 27 77 f6 83 ed bc 48 e9 70 9c ff d7 13 a8 26 30 23 2d 7c 98 e0 f0 df 52 01 d6 03 29 65 8d ba 83 b5 3f ed 49 30 7e 03 d9 66 3c 0d 2e ... >
length: 65
===== ethereumjs-util =====
fromSigned: { v: 27,
r:
<Buffer a0 5a c7 1b 16 17 27 77 f6 83 ed bc 48 e9 70 9c ff d7 13 a8 26 30 23 2d 7c 98 e0 f0 df 52 01 d6>,
s:
<Buffer 03 29 65 8d ba 83 b5 3f ed 49 30 7e 03 d9 66 3c 0d 2e 44 76 c8 b7 92 5c 2e d0 2c c7 f8 81 f5 27> }
prefix: Ethereum Signed Message:
5
ecrecover_public_key: 0x4e17dc4aef81e0ce6d16686be5e194274795375fc5525f1cdc46fe0b4643d5d66dcc58b79553ea878b6b514b8bd2552090d0fc810bd6f9b4d585f4709f43ed41
address: 0xad44a8ea9a9bb5ef66f041bb921a687331729eb4
toChecksumAddress address: 0xAD44A8ea9A9Bb5eF66F041BB921A687331729eB4
address is same address: true
===== Elliptic-util =====
Elliptic verify: true
===== secp256k1 =====
phone Signed -> DER Signed -> signatureImport: 0xa05ac71b16172777f683edbc48e9709cffd713a82630232d7c98e0f0df5201d60329658dba83b53fed49307e03d9663c0d2e4476c8b7925c2ed02cc7f881f527
length: 64
secp256k1 verify: true
----- phone End -----
----- secp256k1 -----
Public Key: 0x037ff17f569a94f4b91317a36b54dc9a77cdd6ce004a00821ca0ebc12acaa5188d
length: 33
singature: { signature:
<Buffer 79 a4 11 c0 85 bd 1a 2d 7b bd a5 eb 1e 19 d6 75 f1 40 07 27 f7 83 82 9f 1d f6 2a d2 86 3c 8a eb 62 04 03 78 64 96 f9 10 27 61 d0 ea 79 2b 40 65 d5 45 ... >,
recovery: 1 }
length: 64
singature: 0x79a411c085bd1a2d7bbda5eb1e19d675f1400727f783829f1df62ad2863c8aeb620403786496f9102761d0ea792b4065d545608d7905af427ffa2b181b103e28
Get Back Pubkey: 0x037ff17f569a94f4b91317a36b54dc9a77cdd6ce004a00821ca0ebc12acaa5188d
recover_public_key is same PublicKey: true
----- secp256k1 End -----
----- ganache -----
Public Key: 0xba5ca43c6d8c8ec41a0449ddc35dfee96afd0a112c4667b9d09925913799be627d1a779b6114c8541650c3b31bf88d360b1e3ebc973267003c7452fef6f2da2e
length: 64
Address: 0xf8d3a2033ebfc7778cd59f676235a8e431b6eed7
length: 20
singature: { r:
<Buffer b7 f7 ff 3c 78 8f 10 db e5 46 d4 10 2a 17 cd 99 1f d3 2c 5a c8 86 e9 31 83 bc 53 8d 5f 92 8f 81>,
s:
<Buffer 6d c1 0d 0e 71 42 73 f5 64 64 96 af d4 e1 3d 49 18 09 5f 42 27 e0 0b ae 1f 5a 59 ec af 39 fd e2>,
v: 27 }
ecrecover_public_key: 0xba5ca43c6d8c8ec41a0449ddc35dfee96afd0a112c4667b9d09925913799be627d1a779b6114c8541650c3b31bf88d360b1e3ebc973267003c7452fef6f2da2e
ecrecover_public_key is same PublicKey: true
----- ganache End -----
ethereum sign verify ECDSA part 2
Sure ethereum signature is 65, but secp256k1 is 64
RangeError: signature length is invalid
Web3 = require("web3")
var web3 = new Web3(Web3.givenProvider || 'ws://some.local-or-remote.node:8546');
const secp256k1 = require('secp256k1')
// or require('secp256k1/elliptic')
// if you want to use pure js implementation in node
//ethereum test https://github.com/ethereum/go-ethereum/blob/461291882edce0ac4a28f64c4e8725b7f57cbeae/crypto/signature_test.go
msg = web3.utils.hexToBytes("0xd301ce462d3e639518f482c7f03821fec1e602018630ce621e1e7851c12343a6")
signature = web3.utils.hexToBytes("0x638a54215d80a6713c8d523a6adc4e6e73652d859103a36b700851cb0e61b66b8ebfc1a610c57d732ec6e0a8f06a9a7a28df5051ece514702ff9cdff0b11f454")
pubkey = web3.utils.hexToBytes("0x03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138")
console.log(secp256k1.verify(msg, signature, pubKey))
ethereum sign verify ECDSA part 1
呼叫web3的部份,都需要使用ethereum geth,這部份有點麻煩
Call web3 must use ethereum, this mean need to run ganache or geth. No ok.
jsrsasign is offline to compute
ECDSA 相關的是 https://kjur.github.io/jsrsasign/sample/sample-ecdsa.html
=====
https://medium.com/@angellopozo/ethereum-signing-and-validating-13a2d7cb0ee3
https://dzone.com/articles/signing-and-verifying-ethereum-signatures
public address 只是 verify後拿來驗證是否相同
另一句話 verify後會產生public address,主要是拿sign後的值產生 r s v ,然後再用 合約的功能 ecrecover 處理
public address only for after verify product check
Other way to explain is
After verify get public address. Take signatures to make r s v, then use r s v with contract ecrecover(). ecrecover() run finish get public address.
=====
Use npm secp256k1. When you run code
https://etherscan.io/verifySig
Address
0xAD44A8ea9A9Bb5eF66F041BB921A687331729eB4
Message Signature Hash
0xa05ac71b16172777f683edbc48e9709cffd713a82630232d7c98e0f0df5201d60329658dba83b53fed49307e03d9663c0d2e4476c8b7925c2ed02cc7f881f5271b
Enter the original message that was signed
Hello
verify ok
Go back see code.
Message Signature Hash 長度 is 65
sigObj.signature.length is 64
So https://github.com/ethereum/go-ethereum/blob/dbb03fe9893dd19f6b1de1ee3b768317f22fd135/crypto/secp256k1/secp256.go#L159
This is Why. And
https://github.com/ethereum/go-ethereum/blob/dbb03fe9893dd19f6b1de1ee3b768317f22fd135/crypto/secp256k1/secp256.go#L114
Call web3 must use ethereum, this mean need to run ganache or geth. No ok.
jsrsasign is offline to compute
ECDSA 相關的是 https://kjur.github.io/jsrsasign/sample/sample-ecdsa.html
=====
https://medium.com/@angellopozo/ethereum-signing-and-validating-13a2d7cb0ee3
https://dzone.com/articles/signing-and-verifying-ethereum-signatures
public address 只是 verify後拿來驗證是否相同
另一句話 verify後會產生public address,主要是拿sign後的值產生 r s v ,然後再用 合約的功能 ecrecover 處理
public address only for after verify product check
Other way to explain is
After verify get public address. Take signatures to make r s v, then use r s v with contract ecrecover(). ecrecover() run finish get public address.
=====
Use npm secp256k1. When you run code
Web3 = require("web3")
var web3 = new Web3(Web3.givenProvider || 'ws://some.local-or-remote.node:8546');
const { randomBytes } = require('crypto')
const secp256k1 = require('secp256k1')
// or require('secp256k1/elliptic')
// if you want to use pure js implementation in node
// generate message to sign
const msg = Buffer.alloc(32, "Hello") //randomBytes(32)
// generate privKey
let privKey
do {
privKey = randomBytes(32)
} while (!secp256k1.privateKeyVerify(privKey))
var buf_privatekey = Buffer.from(web3.utils.hexToBytes('0x75b25b96be4313c5a102bd4daa6bbeb71414f23e0ae15c0f93fa6d17866003da'))
console.log("privatekey: %s", web3.utils.bytesToHex(buf_privatekey))
// get the public key in a compressed format
const pubKey = secp256k1.publicKeyCreate(privKey)
console.log("pubKey: %s", web3.utils.bytesToHex(pubKey))
// sign the message
const sigObj = secp256k1.sign(msg, privKey)
//Message Signature Hash 長度
console.log(Buffer.from(web3.utils.hexToBytes("0xa05ac71b16172777f683edbc48e9709cffd713a82630232d7c98e0f0df5201d60329658dba83b53fed49307e03d9663c0d2e4476c8b7925c2ed02cc7f881f5271b")).length)
//public key
pubkey = Buffer.from(web3.utils.hexToBytes("0x034e17dc4aef81e0ce6d16686be5e194274795375fc5525f1cdc46fe0b4643d5d6"))
//標準signature 是64
console.log(sigObj.signature.length)
console.log("secp256k1 sign finish hex: ", web3.utils.bytesToHex(sigObj.signature))
signature = Buffer.from(web3.utils.hexToBytes("0xa05ac71b16172777f683edbc48e9709cffd713a82630232d7c98e0f0df5201d60329658dba83b53fed49307e03d9663c0d2e4476c8b7925c2ed02cc7f881f5271b"))
console.log(web3.utils.bytesToHex(signature))
console.log(secp256k1.verify(msg, signature, pubKey))
// verify the signature
//console.log(secp256k1.verify(msg, sigObj.signature, pubKey))
// => true
https://etherscan.io/verifySig
Address
0xAD44A8ea9A9Bb5eF66F041BB921A687331729eB4
Message Signature Hash
0xa05ac71b16172777f683edbc48e9709cffd713a82630232d7c98e0f0df5201d60329658dba83b53fed49307e03d9663c0d2e4476c8b7925c2ed02cc7f881f5271b
Enter the original message that was signed
Hello
verify ok
Go back see code.
Message Signature Hash 長度 is 65
sigObj.signature.length is 64
So https://github.com/ethereum/go-ethereum/blob/dbb03fe9893dd19f6b1de1ee3b768317f22fd135/crypto/secp256k1/secp256.go#L159
This is Why. And
https://github.com/ethereum/go-ethereum/blob/dbb03fe9893dd19f6b1de1ee3b768317f22fd135/crypto/secp256k1/secp256.go#L114
node 8 nodejs ssl handshake error
const https = require('https');
export async function GetUserinfo(Token) {
console.log(process.env["NODE_TLS_REJECT_UNAUTHORIZED"])
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
console.log(process.env["NODE_TLS_REJECT_UNAUTHORIZED"])
const baseURL = 'https://openid.hydra:9001';
const userinfoURL = '/userinfo';
axios({
method: 'get',
headers: {
'Authorization': 'Bearer ' + Token,
'accept': 'application/json'
},
httpsAgent: new https.Agent({
rejectUnauthorized: false,
ecdhCurve: 'auto'
}),
url: userinfoURL,
baseURL: baseURL,
responseType: 'json'
}).then(function (response) {
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
No Need, No meanError: self signed certificate
Answer: rejectUnauthorized: false,
HTTPs requests to API fail: 'sslv3 alert handshake failure
Answer: ecdhCurve: 'auto'
Ory Hydra Authorization Code Exchange => access token Use openid-client
https://github.com/panva/node-openid-client/tree/v2.x
So watch https://github.com/panva/node-openid-client/tree/v2.x
node.js package use "openid-client": "2.5.0",
https://github.com/panva/node-openid-client/blob/f1b4282ac50f7e15fc195f66bf76409af4ec4b6b/lib/client.js
see if (params.code) { Can know use grant
https://github.com/panva/node-openid-client/tree/v2.x#custom-token-endpoint-grants
Important! WARNING: Node.js 12 or higher is required for openid-client@3 and above. For older Node.js versions use openid-client@2.
So watch https://github.com/panva/node-openid-client/tree/v2.x
node.js package use "openid-client": "2.5.0",
client.authorizationCallback have bug, nonce mismatch always have this error even see https://github.com/panva/node-openid-client/issues/150
Correct way
https://github.com/panva/node-openid-client/blob/f1b4282ac50f7e15fc195f66bf76409af4ec4b6b/lib/client.js
see if (params.code) { Can know use grant
https://github.com/panva/node-openid-client/tree/v2.x#custom-token-endpoint-grants
const hydraconfig= {
"oidurl": "https://openid.hydra:9001",
"redirectUri": "https://t.tt:9010/callback",
"clientid": "auth-code-client",
"clientsecretid": "secret"
}
//openid-client================
const { Issuer } = require('openid-client')
const hydraIssuer = await Issuer.discover(hydraconfig.oidurl) // => Promise
.then(function (hydradiscoverIssuer) {
console.log('Discovered issuer %s %O', hydradiscoverIssuer.issuer, hydradiscoverIssuer.metadata);
return hydradiscoverIssuer
});
const client = new hydraIssuer.Client({
client_id: hydraconfig.clientid,
client_secret: hydraconfig.clientsecretid
});
var tokenset = await client.grant({
grant_type: 'authorization_code',
code: code,
redirect_uri: hydraconfig.redirectUri,
code_verifier: '', //No value, because real use in Hydra login-consent. Not use client.authorizationUrl or client.authorizationPost
});
console.log(tokenset)
javascript console.log object
console.log('show value string, object %s %O', var.string, var.object);
Ory Hydra Authorization Code Exchange => access token
Before posts about Hydra get access token is use golang HydraOauthConfig.Exchange(ctx, code). This is easy way. But on front website like vue or other framework how to get access token.
https://github.com/ory/hydra/issues/631
Not Authorization: Bearer
YXV0aC1jb2RlLWNsaWVudDpzZWNyZXQ= => auth-code-client:secret
example: https://t.tt:9010/callback?code=cuNw76aEuckIJJyVssk2LJvqdLXffT-8Kx1s0tYFt6Y.v0Dxc2_yT9ga8c2moKx0fDbwRFVgwryAt5BJM7lOJlM&scope=openid%20offline&state=gczxkznmjkrksgytsemvwgkf
If code have error message, you need check before any step have incorrect.
https://www.ory.sh/docs/hydra/sdk/api#the-oauth-20-token-endpoint
You need to sure grant_type=authorization_code Not other options.
But SDK Document No any options example. Only suggestion you use lib. So you need to try many.
like follow
https://www.oauth.com/oauth2-servers/pkce/authorization-code-exchange/
https://community.ory.sh/t/how-configure-grant-implicit-flow/411/14
https://auth0.com/docs/api-auth/tutorials/authorization-code-grant-pkce
https://github.com/oauthjs/express-oauth-server/issues/55
https://www.jianshu.com/p/5cf2b7a45b75
http://www.passportjs.org/docs/oauth/
Then try out a ways.
OK. Mark is not important Required.
Use REST Client to test
POST https://openid.hydra:9001/oauth2/token
Authorization: Basic YXV0aC1jb2RlLWNsaWVudDpzZWNyZXQ=
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=cuNw76aEuckIJJyVssk2LJvqdLXffT-8Kx1s0tYFt6Y.v0Dxc2_yT9ga8c2moKx0fDbwRFVgwryAt5BJM7lOJlM
#&redirect_uri=https://certfront/oid/test/callback
#&scope=openid,offline
#&client_id=auth-code-client
#&code_verifier=
#&state=gczxkznmjkrksgytsemvwgkf
Import is: Authorization: Basic
https://github.com/ory/hydra/issues/631
Not Authorization: Bearer
base64(urlencode(client_id):urlencode(client_secret))
YXV0aC1jb2RlLWNsaWVudDpzZWNyZXQ= => auth-code-client:secret
code is callback code. When you login-consent finish step then callback to your set callback URL. Watch URL inside have code=
example: https://t.tt:9010/callback?code=cuNw76aEuckIJJyVssk2LJvqdLXffT-8Kx1s0tYFt6Y.v0Dxc2_yT9ga8c2moKx0fDbwRFVgwryAt5BJM7lOJlM&scope=openid%20offline&state=gczxkznmjkrksgytsemvwgkf
If code have error message, you need check before any step have incorrect.
In Ory Hydra get access token is not like sdk document
https://www.ory.sh/docs/hydra/sdk/api#the-oauth-20-token-endpoint
You need to sure grant_type=authorization_code Not other options.
But SDK Document No any options example. Only suggestion you use lib. So you need to try many.
like follow
https://www.oauth.com/oauth2-servers/pkce/authorization-code-exchange/
https://community.ory.sh/t/how-configure-grant-implicit-flow/411/14
https://auth0.com/docs/api-auth/tutorials/authorization-code-grant-pkce
https://github.com/oauthjs/express-oauth-server/issues/55
https://www.jianshu.com/p/5cf2b7a45b75
http://www.passportjs.org/docs/oauth/
Then try out a ways.
OK. Mark is not important Required.
#&redirect_uri=https://certfront/oid/test/callback
#&scope=openid,offline
#&client_id=auth-code-client
#&code_verifier=
#&state=gczxkznmjkrksgytsemvwgkf
oauth2 nodejs
https://peach.ebu.io/technical/tutorials/tuto-oauth2-client/
https://www.pveller.com/oauth2-with-passport-10-steps-recipe/
http://www.hitotec.com/authentification-oauth-avec-passportjs-pour-une-api-rest/
https://www.shangyang.me/2018/03/11/javascript-nodejs-passport-04-deepinto-oauth2-authenticate-process/
https://blog.yorkxin.org/2013/09/30/oauth2-4-1-auth-code-grant-flow.html
https://www.pveller.com/oauth2-with-passport-10-steps-recipe/
http://www.hitotec.com/authentification-oauth-avec-passportjs-pour-une-api-rest/
https://www.shangyang.me/2018/03/11/javascript-nodejs-passport-04-deepinto-oauth2-authenticate-process/
https://blog.yorkxin.org/2013/09/30/oauth2-4-1-auth-code-grant-flow.html
[轉]如何使用 OpenSSL 建立開發測試用途的自簽憑證 (Self-Signed Certificate)
https://blog.miniasp.com/post/2019/02/25/Creating-Self-signed-Certificate-using-OpenSSL
目前這個方式比較靠普
建立 ssl.conf 設定檔
目前這個方式比較靠普
建立 ssl.conf 設定檔
[req]
prompt = no
default_md = sha256
default_bits = 2048
distinguished_name = dn
x509_extensions = v3_req
[dn]
C = TW
ST = Taiwan
L = Taipei
O = Duotify Inc.
OU = IT Department
emailAddress = admin@example.com
CN = localhost
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.localhost
DNS.2 = localhost
DNS.3 = 192.168.2.100
openssl req -x509 -new -nodes -sha256 -utf8 -days 3650 -newkey rsa:2048 -keyout server.key -out server.crt -config ssl.conf
oauth2 nodejs vue
https://www.ory.sh/docs/hydra/integration#interacting-with-oauth-20
https://github.com/lelylan/simple-oauth2
https://www.jianshu.com/p/5cf2b7a45b75
https://github.com/lelylan/simple-oauth2
https://www.jianshu.com/p/5cf2b7a45b75
vue 3 image assets
Vue template Code
src/ooxx/ooxx/xx.vue
assets
File location:assets/img/tt.png
OK
Code:img src='@/assets/img/tt.png'
Become:
img src="/img/tt.f3b936ef.png"
Failed
Code:img src='@/img/nchcbclab.png'
img src='assets/img/tt.png'
img src='./assets/img/tt.png'
img src='../assets/img/tt.png'
img src="require('assets/img/tt.png') "
img src="require('./assets/img/tt.png') "
img src="require('../assets/img/tt.png') "
public
File location:public/img/tt.png
OK
Code:img src= '/img/nchcbclab.png'
Become:
img src= '/img/nchcbclab.png'
Failed
Code:img src= 'img/nchcbclab.png'
img src= 'public/img/nchcbclab.png'
img src='/public/img/nchcbclab.png'
vue 3 index.html
old vue index.html just copy to public directory.
If public don't have index.hmtl, be craeted by run "npm run serve". But public don't have index.html.
Only "npm run build" create index.html in dist driectory.
You delete all project index.html. This time still can run "npm run serve". Watch Website source code <title>Vue App</title>
Then put your custome into public directory then <title>Custome App ooxxooxx </title>
If public don't have index.hmtl, be craeted by run "npm run serve". But public don't have index.html.
Only "npm run build" create index.html in dist driectory.
More easy understand way:
You delete all project index.html. This time still can run "npm run serve". Watch Website source code <title>Vue App</title>
Then put your custome into public directory then <title>Custome App ooxxooxx </title>
vue babel 7
https://github.com/storybookjs/storybook/issues/5298
https://github.com/webpack/webpack/issues/4039
https://github.com/gmfe/Think/issues/67
Full code
https://github.com/webpack/webpack/issues/4039
https://github.com/gmfe/Think/issues/67
Cannot assign to read only property 'exports' of object '#<Object>'
babel.config.js
'sourceType': 'unambiguous', // 自动推断编译的模块类型(cjs,es6)
'ignore': [/@babel[/\\]runtime/], // 忽略 @babel/runtime
Full code
module.exports = {
'sourceType': 'unambiguous', // 自动推断编译的模块类型(cjs,es6)
'ignore': [/@babel[/\\]runtime/], // 忽略 @babel/runtime
presets: [
'@vue/app'
]
}
vue 3 vue-cli-service serve vue.config.js package.json docker
"serve": "vue-cli-service serve",
Some page modify vue-cli-service serve --host 0.0.0.0 --port 8978
This may in docker failed.
So some page modify vue.config.js
Use public is Failed!!
Use host port is Correct!!
PS:
.Put eslint is maybe get some eslint error, not about host ip port.
.disableHostCheck can remove for try by yourself env.
Some page modify vue-cli-service serve --host 0.0.0.0 --port 8978
This may in docker failed.
Error: listen EADDRNOTAVAIL: address not available
So some page modify vue.config.js
Use public is Failed!!
devServer: {
public: '0.0.0.0:80',
disableHostCheck: true,
}
Use host port is Correct!!
module.exports = {
chainWebpack: config => {
config.module.rules.delete('eslint');
},
devServer: {
host: '0.0.0.0',
port: '80',
//public: '0.0.0.0:80', //無效
disableHostCheck: true,
}
}
PS:
.Put eslint is maybe get some eslint error, not about host ip port.
.disableHostCheck can remove for try by yourself env.
Mirror Your Traffic Duplicate
goreplay
https://github.com/buger/goreplay
tcpcopy
https://github.com/session-replay-tools/tcpcopy
teeproxy
https://github.com/chrislusf/teeproxy
duplicator
https://github.com/agnoster/duplicator
goduplicator
https://github.com/mkevac/goduplicator
nginx mirror
haproxy mirror
https://github.com/buger/goreplay
tcpcopy
https://github.com/session-replay-tools/tcpcopy
teeproxy
https://github.com/chrislusf/teeproxy
duplicator
https://github.com/agnoster/duplicator
goduplicator
https://github.com/mkevac/goduplicator
nginx mirror
haproxy mirror
[轉]How to Mirror Your Traffic with Nginx
https://www.serverlab.ca/tutorials/linux/web-servers-linux/how-to-mirror-your-traffic-with-nginx/
ipfs
https://www.fil.club/view/77.html
端口8080是HTTP网关,它允许您使用浏览器查询ipfs数据(请参阅此示例)
端口4001是IPFS用于与其他节点通信的群集端口,端口5001用于本地API。
我们5001只会绑定127.0.0.1因为它不应该暴露给外界。
我们已经安装了数据和分段卷。
该data卷用于存储IPFS本地存储(配置和数据库),并且
staging是一个可用于暂存文件以供命令行使用的目录(例如ipfs add)。
如果您只使用API,则可以省略暂存目录卷。当然,随意将这些目录放在除了之外的其他地方/tmp。
端口8080是HTTP网关,它允许您使用浏览器查询ipfs数据(请参阅此示例)
端口4001是IPFS用于与其他节点通信的群集端口,端口5001用于本地API。
我们5001只会绑定127.0.0.1因为它不应该暴露给外界。
我们已经安装了数据和分段卷。
该data卷用于存储IPFS本地存储(配置和数据库),并且
staging是一个可用于暂存文件以供命令行使用的目录(例如ipfs add)。
如果您只使用API,则可以省略暂存目录卷。当然,随意将这些目录放在除了之外的其他地方/tmp。
[轉]用Go来做以太坊开发
https://github.com/miguelmota/ethereum-development-with-go-book
https://goethereumbook.org/zh/
https://goethereumbook.org/zh/
ethereum explorer
https://github.com/gobitfly/etherchain-light
https://github.com/gobitfly/erc20-explorer
https://github.com/carsenk/explorer
https://github.com/Capgemini-AIE/ethereum-docker/tree/master/monitored-geth-client
https://github.com/cubedro/eth-net-intelligence-api
https://github.com/cubedro/eth-netstats
Browse blocks and transactions
It's nice to have some simple analogue of Etherscan for your local chain browsing. It will be useful to examine transactions, balances, blocks and etc. It appeared that it is quite difficult to find an open-source good solution for geth. After several tries I found an acceptable solution called ETHExplorer V2. Clone it into explorer-v2 folder. To Dockerize it I had to make 2 changes. First create a Dockerfile
And change start script in package.json to "start": "http-server ./app -a 0.0.0.0 -p 8000 -c-1". This is required to allow connections to explorer from any IP (outside docker). Next we should create a service for explorer
https://github.com/gobitfly/erc20-explorer
https://github.com/carsenk/explorer
https://github.com/Capgemini-AIE/ethereum-docker/tree/master/monitored-geth-client
https://github.com/cubedro/eth-net-intelligence-api
https://github.com/cubedro/eth-netstats
Browse blocks and transactions
It's nice to have some simple analogue of Etherscan for your local chain browsing. It will be useful to examine transactions, balances, blocks and etc. It appeared that it is quite difficult to find an open-source good solution for geth. After several tries I found an acceptable solution called ETHExplorer V2. Clone it into explorer-v2 folder. To Dockerize it I had to make 2 changes. First create a Dockerfile
# ./explorer-v2/Dockerfile
FROM node:6
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY . /usr/src/app
RUN npm install && \
node_modules/.bin/bower install --allow-root
And change start script in package.json to "start": "http-server ./app -a 0.0.0.0 -p 8000 -c-1". This is required to allow connections to explorer from any IP (outside docker). Next we should create a service for explorer
./docker-compose.yml
# ...
explorer:
build: explorer-v2
container_name: explorer
command: npm start
ports:
- "8000:8000"
nestjs csrf
https://gitissue.com/repos/jiayisheji/blog
pass csrf https://github.com/expressjs/csurf/issues/21
pass csrf https://github.com/expressjs/csurf/issues/21
main.ts
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path'
import { AppModule } from './app.module';
import * as cookieSession from 'cookie-session';
import * as helmet from 'helmet';
import * as cookieParser from 'cookie-parser';
import * as csurf from 'csurf';
import * as rateLimit from 'express-rate-limit';
async function bootstrap() {
const app = await NestFactory.create(
AppModule,
);
app.init()
app.useStaticAssets(join(__dirname, '..', 'public'));
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('pug');
app.set('trust proxy', 1);
app.use(cookieSession({
name: 'session',
keys: ['key1', 'key2']
}));
//app.enableCors();
app.use(helmet());
app.use(cookieParser());
//app.use(csurf({ cookie: true })); //正常是這行,但有些API POST時需要略過csrf
app.use(function (req, res, next) {
var mw = csurf({ cookie: true });
// console.log(req.url) // check real get url
if (req.url === '/testpostcsrf') return next(); //pass csrf check
mw(req, res, next);
});
app.use(
rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
}),
);
await app.listen(3000);
}
bootstrap();
layout.pug
doctype html
html
head
title= title
meta(content= csrfToken, name='csrf-token')
body
block content
login.pug
extends layout
block content
h1 Please log in
if error
p.
#{error}
form(action="/login",method="POST")
input(type="hidden",name="_csrf",value=csrfToken)
input(type="hidden",name="challenge",value=challenge)
table(style="")
tr
td
input(type="email",id="email",name="email",placeholder="email@foobar.com")
td.
(Example: "foo@bar.com")
tr
td
input(type="password",id="password",name="password")
td.
(Example: "foobar")
input(type="checkbox",id="remember",name="remember",value="1")
label(for="remember") Remember me
br
input(type="submit",id="accept",value="Log in")
nest.js 让我们用Nestjs来重写一个CNode
https://gitissue.com/repos/jiayisheji/blog
這網站中的 让我们用Nestjs来重写一个CNode(上、中、下) 幫了大忙,減少大量的浪費時間
===========
html -> jade/pug
http://html2jade.aaron-powell.com/
when you html meta want to become jade/pug
layout.pug
這網站中的 让我们用Nestjs来重写一个CNode(上、中、下) 幫了大忙,減少大量的浪費時間
===========
html -> jade/pug
http://html2jade.aaron-powell.com/
when you html meta want to become jade/pug
layout.pug
meta(content= csrfToken, name='csrf-token')
OpenID hydra docker-compose hydra-login-consent-node mariadb
docker-compose
version: '3.3'
services:
ory-hydra-postgres:
image: postgres:9.6
#restart: always
environment:
- POSTGRES_USER=hydra
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=hydra
volumes:
- hydradata:/var/lib/postgresql/data:rw
networks:
- openid
# 第一次執行postgres要做資料庫格式建立 PS: network依佈屬環境為主 docker network ls 確認
# docker run -it --rm \
# --network openid \
# oryd/hydra:latest \
# migrate sql --yes postgres://hydra:secret@ory-hydra-postgres:5432/hydra?sslmode=disable
ory-hydra:
image: oryd/hydra:latest
restart: unless-stopped
ports:
- "9001:4444"
- "9002:4445"
environment:
- SECRETS_SYSTEM=this_needs_to_be_the_same_a
- DSN=postgres://hydra:secret@ory-hydra-postgres:5432/hydra?sslmode=disable
- URLS_SELF_ISSUER=https://openid.hydra:9001/
- URLS_CONSENT=http://192.168.99.100:9020/consent
- URLS_LOGIN=http://192.168.99.100:9020/login
- LOG_LEVEL=debug
- OAUTH2_EXPOSE_INTERNAL_ERRORS=true
- SERVE_PUBLIC_CORS_ENABLED=true
- SERVE_PUBLIC_CORS_ALLOWED_METHODS=POST,GET,PUT,DELETE
- SERVE_ADMIN_CORS_ENABLED=true
- SERVE_ADMIN_CORS_ALLOWED_METHODS=POST,GET,PUT,DELETE
- SERVE_TLS_KEY_BASE64=LS0tLS1CRUdJTiBFQyBQQVJBTUVURVJTLS0tLS0KQmdVcmdRUUFJZz09Ci0tLS0tRU5EIEVDIFBBUkFNRVRFUlMtLS0tLQotLS0tLUJFR0lOIEVDIFBSSVZBVEUgS0VZLS0tLS0KTUlHa0FnRUJCRENLbkdnVnFJVzdZaW5iUWV5UEd5UTQ0R3U2VVFEelU5SENLYjMzTWlmeFJYRTBkbnU2KzdadQowdEJUcUhQRHVMeWdCd1lGSzRFRUFDS2haQU5pQUFSbng1Nk9jeGNyRWRsYmU4TXRSdUVxWGV2OEREcmh6ZWJGCjM4NlI4Q2RQWDRlUWI2Zll6ekFUL3V3STBsTDdvRmlEWEM3Q0JLWmZUcTdFSzN4TzNXWlpSSjJrMEQ3TnNLd2cKVEpZenJxT0JpczBNeGtva2FUWVVyemhKMXBKY3lmWT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
- SERVE_TLS_CERT_BASE64=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNQVENDQWNLZ0F3SUJBZ0lKQU13RjRiVDRvSnh0TUFvR0NDcUdTTTQ5QkFNQ01Gd3hDekFKQmdOVkJBWVQKQWtGVk1STXdFUVlEVlFRSURBcFRiMjFsTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbgphWFJ6SUZCMGVTQk1kR1F4RlRBVEJnTlZCQU1NREc5d1pXNXBaQzVvZVdSeVlUQWVGdzB4T1RBMk1UY3dNVEl4Ck16ZGFGdzB5T1RBMk1UUXdNVEl4TXpkYU1Gd3hDekFKQmdOVkJBWVRBa0ZWTVJNd0VRWURWUVFJREFwVGIyMWwKTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXhGVEFUQmdOVgpCQU1NREc5d1pXNXBaQzVvZVdSeVlUQjJNQkFHQnlxR1NNNDlBZ0VHQlN1QkJBQWlBMklBQkdmSG5vNXpGeXNSCjJWdDd3eTFHNFNwZDYvd01PdUhONXNYZnpwSHdKMDlmaDVCdnA5alBNQlArN0FqU1V2dWdXSU5jTHNJRXBsOU8KcnNRcmZFN2RabGxFbmFUUVBzMndyQ0JNbGpPdW80R0t6UXpHU2lScE5oU3ZPRW5Xa2x6SjlxTlFNRTR3SFFZRApWUjBPQkJZRUZHK3Z6ZkIxYmVnM1VadEpYRXZWOWRNa1hvNmdNQjhHQTFVZEl3UVlNQmFBRkcrdnpmQjFiZWczClVadEpYRXZWOWRNa1hvNmdNQXdHQTFVZEV3UUZNQU1CQWY4d0NnWUlLb1pJemowRUF3SURhUUF3WmdJeEFMUHYKODZFSFRUVElLcEJHdlQrY2NWN3djSC84SFIrc2xhZC9ZUFhLUlZwd2RDbzUyZVRPV3BDS2dGamtHNEJhd1FJeApBTGxGZFgwbEk2ZzhXS3lhRTVmKzJGZEkxYWVqQ0Ftd0xPTTZTRFJhNFVHbitDa2VwOEljeG1CTDIvQmUzSVZ6CjhnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
networks:
- openid
# 快速建立 auth-doce-client PS: network依佈屬環境為主 docker network ls 確認
#docker run --rm -it \
# -e HYDRA_ADMIN_URL=https://ory-hydra:4445 \
# --network openid \
# oryd/hydra:latest \
# clients create --skip-tls-verify \
# --id auth-code-client \
# --secret secret \
# --grant-types authorization_code,refresh_token \
# --response-types code,id_token,token \
# --scope openid,offline,photos.read \
# --callbacks https://t.tt:9010/callback
ory-hydra-login-consent:
#image: oryd/hydra-login-consent-node:latest
build:
context: hydra-login-consent-node/
restart: unless-stopped
ports:
- "9020:3000"
environment:
- HYDRA_ADMIN_URL=https://ory-hydra:4445
- NODE_TLS_REJECT_UNAUTHORIZED=0
volumes:
- hydraloginconsent:/usr/src/app:rw
depends_on:
- mariadb
networks:
- openid
mariadb:
image: mariadb:10.4.6
#restart: always
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=openid
command: ['--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci']
#第一次使執行db_init_sql.txt
networks:
- openid
adminer:
image: adminer
restart: always
ports:
- 8080:8080
depends_on:
- mariadb
networks:
- openid
volumes:
hydradata:
hydraloginconsent:
networks:
openid:
driver: bridge
Use adminer test maraidb: http://192.168.99.100:8080 root/secret
mariadb init
DROP DATABASE IF EXISTS `openid`;
CREATE DATABASE `openid` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
USE `openid`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text COLLATE utf8mb4_unicode_ci NOT NULL,
`email` text COLLATE utf8mb4_unicode_ci NOT NULL,
`password` text COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `user` (`id`, `name`, `email`, `password`) VALUES
(1, 'foobar', 'foo@bar.com', '3858f62230ac3c915f300c664312c63f');
ory-hydra-login-consent modify package.json add
"md5": "^2.2.1",
"mysql": "^2.17.1"
ory-hydra-login-consent add db/database.js
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'mariadb',
user : 'root',
password : 'secret',
database: 'openid'
});
var query=function(sql,options,callback){
pool.getConnection(function(err,conn){
pool.query
if(err){
callback(err,null,null);
}else{
conn.query(sql,options,function(err,results,fields){
//释放连接
conn.release();
//事件驱动回调
callback(err,results,fields);
});
}
});
};
module.exports = {query, pool}
ory-hydra-login-consent modify routes/login.js
...
router.post('/', csrfProtection, function (req, res, next) {
// The challenge is now a hidden input field, so let's take it from the request body instead
var challenge = req.body.challenge;
var sql = "select count(*) as count from user where email = ? and password = ?"
var params = [req.body.email, md5(req.body.password)]
//db.get(sql, params, (err, row) => {
pool.query(sql, params, (err, row) => {
if (err) {
res.status(400).json({"db error":err.message});
return;
}
if(!(row.count==1)){ //找不到
res.render('login', {
csrfToken: req.csrfToken(),
challenge: challenge,
error: 'The username / password combination is not correct'
});
return;
}
hydra.acceptLoginRequest(challenge, {
// Subject is an alias for user ID. A subject can be a random string, a UUID, an email address, ....
subject: req.body.email,
// This tells hydra to remember the browser and automatically authenticate the user in future requests. This will
// set the "skip" parameter in the other route to true on subsequent requests!
remember: Boolean(req.body.remember),
// When the session expires, in seconds. Set this to 0 so it will never expire.
remember_for: 3600,
// Sets which "level" (e.g. 2-factor authentication) of authentication the user has. The value is really arbitrary
// and optional. In the context of OpenID Connect, a value of 0 indicates the lowest authorization level.
// acr: '0',
})
.then(function (response) {
// All we need to do now is to redirect the user back to hydra!
res.redirect(response.redirect_to);
})
// This will handle any error that happens when making HTTP calls to hydra
.catch(function (error) {
next(error);
});
});
// Let's check if the user provided valid credentials. Of course, you'd use a database or some third-party service
// for this!
// if (!(req.body.email === 'foo@bar.com' && req.body.password === 'foobar')) {
// // Looks like the user provided invalid credentials, let's show the ui again...
// res.render('login', {
// csrfToken: req.csrfToken(),
// challenge: challenge,
// error: 'The username / password combination is not correct'
// });
// return;
// }
// Seems like the user authenticated! Let's tell hydra...
// hydra.acceptLoginRequest(challenge, {
// // Subject is an alias for user ID. A subject can be a random string, a UUID, an email address, ....
// subject: 'foo@bar.com',
// // This tells hydra to remember the browser and automatically authenticate the user in future requests. This will
// // set the "skip" parameter in the other route to true on subsequent requests!
// remember: Boolean(req.body.remember),
// // When the session expires, in seconds. Set this to 0 so it will never expire.
// remember_for: 3600,
// // Sets which "level" (e.g. 2-factor authentication) of authentication the user has. The value is really arbitrary
// // and optional. In the context of OpenID Connect, a value of 0 indicates the lowest authorization level.
// // acr: '0',
// })
// .then(function (response) {
// // All we need to do now is to redirect the user back to hydra!
// res.redirect(response.redirect_to);
// })
// // This will handle any error that happens when making HTTP calls to hydra
// .catch(function (error) {
// next(error);
// });
// You could also deny the login request which tells hydra that no one authenticated!
// hydra.rejectLoginRequest(challenge, {
// error: 'invalid_request',
// error_description: 'The user did something stupid...'
// })
// .then(function (response) {
// // All we need to do now is to redirect the browser back to hydra!
// res.redirect(response.redirect_to);
// })
// // This will handle any error that happens when making HTTP calls to hydra
// .catch(function (error) {
// next(error);
// });
});
https://t.tt:9010 When login id/pwd, can use adminer change database user email/password.
OpenID hydra docker-compose
docker-compose
version: '3.3'
services:
ory-hydra-postgres:
image: postgres:9.6
#restart: always
environment:
- POSTGRES_USER=hydra
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=hydra
volumes:
- hydradata:/var/lib/postgresql/data:rw
networks:
- openid
# 第一次執行postgres要做資料庫格式建立 PS: network依佈屬環境為主 docker network ls 確認
# docker run -it --rm \
# --network openid \
# oryd/hydra:latest \
# migrate sql --yes postgres://hydra:secret@ory-hydra-postgres:5432/hydra?sslmode=disable
ory-hydra:
image: oryd/hydra:latest
restart: unless-stopped
ports:
- "9001:4444"
- "9002:4445"
environment:
- SECRETS_SYSTEM=this_needs_to_be_the_same_a
- DSN=postgres://hydra:secret@ory-hydra-postgres:5432/hydra?sslmode=disable
- URLS_SELF_ISSUER=https://openid.hydra:9001/
- URLS_CONSENT=http://192.168.99.100:9020/consent
- URLS_LOGIN=http://192.168.99.100:9020/login
- LOG_LEVEL=debug
- OAUTH2_EXPOSE_INTERNAL_ERRORS=true
- SERVE_PUBLIC_CORS_ENABLED=true
- SERVE_PUBLIC_CORS_ALLOWED_METHODS=POST,GET,PUT,DELETE
- SERVE_ADMIN_CORS_ENABLED=true
- SERVE_ADMIN_CORS_ALLOWED_METHODS=POST,GET,PUT,DELETE
- SERVE_TLS_KEY_BASE64=LS0tLS1CRUdJTiBFQyBQQVJBTUVURVJTLS0tLS0KQmdVcmdRUUFJZz09Ci0tLS0tRU5EIEVDIFBBUkFNRVRFUlMtLS0tLQotLS0tLUJFR0lOIEVDIFBSSVZBVEUgS0VZLS0tLS0KTUlHa0FnRUJCRENLbkdnVnFJVzdZaW5iUWV5UEd5UTQ0R3U2VVFEelU5SENLYjMzTWlmeFJYRTBkbnU2KzdadQowdEJUcUhQRHVMeWdCd1lGSzRFRUFDS2haQU5pQUFSbng1Nk9jeGNyRWRsYmU4TXRSdUVxWGV2OEREcmh6ZWJGCjM4NlI4Q2RQWDRlUWI2Zll6ekFUL3V3STBsTDdvRmlEWEM3Q0JLWmZUcTdFSzN4TzNXWlpSSjJrMEQ3TnNLd2cKVEpZenJxT0JpczBNeGtva2FUWVVyemhKMXBKY3lmWT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
- SERVE_TLS_CERT_BASE64=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNQVENDQWNLZ0F3SUJBZ0lKQU13RjRiVDRvSnh0TUFvR0NDcUdTTTQ5QkFNQ01Gd3hDekFKQmdOVkJBWVQKQWtGVk1STXdFUVlEVlFRSURBcFRiMjFsTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbgphWFJ6SUZCMGVTQk1kR1F4RlRBVEJnTlZCQU1NREc5d1pXNXBaQzVvZVdSeVlUQWVGdzB4T1RBMk1UY3dNVEl4Ck16ZGFGdzB5T1RBMk1UUXdNVEl4TXpkYU1Gd3hDekFKQmdOVkJBWVRBa0ZWTVJNd0VRWURWUVFJREFwVGIyMWwKTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXhGVEFUQmdOVgpCQU1NREc5d1pXNXBaQzVvZVdSeVlUQjJNQkFHQnlxR1NNNDlBZ0VHQlN1QkJBQWlBMklBQkdmSG5vNXpGeXNSCjJWdDd3eTFHNFNwZDYvd01PdUhONXNYZnpwSHdKMDlmaDVCdnA5alBNQlArN0FqU1V2dWdXSU5jTHNJRXBsOU8KcnNRcmZFN2RabGxFbmFUUVBzMndyQ0JNbGpPdW80R0t6UXpHU2lScE5oU3ZPRW5Xa2x6SjlxTlFNRTR3SFFZRApWUjBPQkJZRUZHK3Z6ZkIxYmVnM1VadEpYRXZWOWRNa1hvNmdNQjhHQTFVZEl3UVlNQmFBRkcrdnpmQjFiZWczClVadEpYRXZWOWRNa1hvNmdNQXdHQTFVZEV3UUZNQU1CQWY4d0NnWUlLb1pJemowRUF3SURhUUF3WmdJeEFMUHYKODZFSFRUVElLcEJHdlQrY2NWN3djSC84SFIrc2xhZC9ZUFhLUlZwd2RDbzUyZVRPV3BDS2dGamtHNEJhd1FJeApBTGxGZFgwbEk2ZzhXS3lhRTVmKzJGZEkxYWVqQ0Ftd0xPTTZTRFJhNFVHbitDa2VwOEljeG1CTDIvQmUzSVZ6CjhnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
networks:
- openid
#這行非常重要,docker成功運行後,要進geht console執行 admin.addPeer("enode://444a16729d32431bbdaa594272e3509cdeaaf3c995ffb583589163d35f8b36ad14394ab037ac186525f579700e6500cacfb1f953fdf066fa05da0e1d409f7f79@140.110.18.199:30301")
ory-hydra-login-consent:
#image: oryd/hydra-login-consent-node:latest
build:
context: hydra-login-consent-node/
restart: unless-stopped
ports:
- "9020:3000"
environment:
- HYDRA_ADMIN_URL=https://ory-hydra:4445
- NODE_TLS_REJECT_UNAUTHORIZED=0
volumes:
- hydraloginconsent:/usr/src/app:rw
networks:
- openid
# 快速建立 auth-doce-client PS: network依佈屬環境為主 docker network ls 確認
#docker run --rm -it \
# -e HYDRA_ADMIN_URL=https://ory-hydra:4445 \
# --network openid \
# oryd/hydra:latest \
# clients create --skip-tls-verify \
# --id auth-code-client \
# --secret secret \
# --grant-types authorization_code,refresh_token \
# --response-types code,id_token,token \
# --scope openid,offline,photos.read \
# --callbacks https://t.tt:9010/callback
volumes:
hydradata:
hydraloginconsent:
networks:
openid:
driver: bridge
ory-hydra-login-consent download
https://github.com/ory/hydra-login-consent-nodeDirectory name is hydra-login-consent-node
go run main.go
https://sueboy.blogspot.com/2019/06/openid-hydra-cant-finish-error.htmlBroswer https://t.tt:9010
Helm charts for running and operating Hyperledger Fabric in Kubernetes
https://github.com/apggroeifabriek/pivt
hyperledger crypetogen
https://hyperledger-fabric.readthedocs.io/en/release-1.4/commands/cryptogen.html
cryptogen is an utility for generating Hyperledger Fabric key material. It is provided as a means of preconfiguring a network for
cryptogen is an utility for generating Hyperledger Fabric key material. It is provided as a means of preconfiguring a network for
testing purposes
. It would normally not be used in the operation of a production network.
訂閱:
文章 (Atom)



