Unverified Commit c22e26c7 authored by lyh169's avatar lyh169 Committed by GitHub

Merge branch 'master' into compact_del_store

parents 974114fb 88e98f0f
...@@ -116,8 +116,12 @@ chain33_SignRawTx() { ...@@ -116,8 +116,12 @@ chain33_SignRawTx() {
local txHex="$1" local txHex="$1"
local priKey="$2" local priKey="$2"
local MAIN_HTTP=$3 local MAIN_HTTP=$3
local expire="120s"
if [ -n "$4" ]; then
expire=$4
fi
local req='"method":"Chain33.SignRawTx","params":[{"privkey":"'"$priKey"'","txHex":"'"$txHex"'","expire":"120s"}]' local req='"method":"Chain33.SignRawTx","params":[{"privkey":"'"$priKey"'","txHex":"'"$txHex"'","expire":"'"$expire"'"}]'
signedTx=$(curl -ksd "{$req}" "${MAIN_HTTP}" | jq -r ".result") signedTx=$(curl -ksd "{$req}" "${MAIN_HTTP}" | jq -r ".result")
if [ "$signedTx" != null ]; then if [ "$signedTx" != null ]; then
......
...@@ -10,17 +10,18 @@ function dapp_test_rpc() { ...@@ -10,17 +10,18 @@ function dapp_test_rpc() {
if [ -d dapptest ]; then if [ -d dapptest ]; then
cp "$DAPP_TEST_COMMON" dapptest/ cp "$DAPP_TEST_COMMON" dapptest/
cd dapptest || return cd dapptest || return
rm -f "retries.log"
rm -f "jobs.log" rm -f "jobs.log"
rm -rf "outdir"
dapps=$(find . -maxdepth 1 -type d ! -name dapptest ! -name . | sed 's/^\.\///' | sort) dapps=$(find . -maxdepth 1 -type d ! -name dapptest ! -name . | sed 's/^\.\///' | sort)
echo "dapps list: $dapps" echo "dapps list: $dapps"
set +e set +e
parallel -k --joblog ./jobs.log 'echo tried {} >>./retries.log; ./{}/"'"${RPC_TESTFILE}"'" "'"$ip"'"' ::: "$dapps" parallel -k --results outdir --joblog ./jobs.log ./{}/"${RPC_TESTFILE}" "$ip" ::: "$dapps"
local ret=$? local ret=$?
# retries 3 times if one dapp fail if [ $ret -ne 0 ]; then
echo "============ # retried dapps log: =============" wrongdapps=$(awk '{print $7,$9 }' jobs.log | grep -a 1 | awk -F '/' '{print $2}')
cat ./retries.log parallel -k 'cat ./outdir/1/{}/stderr; cat ./outdir/1/{}/stdout' ::: "$wrongdapps"
fi
echo "============ # check dapps test log: =============" echo "============ # check dapps test log: ============="
cat ./jobs.log cat ./jobs.log
set -e set -e
......
...@@ -255,6 +255,7 @@ ForkParacrossCommitTx=0 ...@@ -255,6 +255,7 @@ ForkParacrossCommitTx=0
ForkLoopCheckCommitTxDone=0 ForkLoopCheckCommitTxDone=0
#平行链分阶段自共识支持合约配置,缺省是0 #平行链分阶段自共识支持合约配置,缺省是0
ForkParaSelfConsStages=0 ForkParaSelfConsStages=0
ForkParaAssetTransferRbk=0
[fork.sub.evm] [fork.sub.evm]
Enable=0 Enable=0
......
...@@ -3,7 +3,7 @@ module github.com/33cn/plugin ...@@ -3,7 +3,7 @@ module github.com/33cn/plugin
go 1.12 go 1.12
require ( require (
github.com/33cn/chain33 v0.0.0-20191126060956-6354775a8a5d github.com/33cn/chain33 v0.0.0-20191206075140-7c09cb251660
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v0.3.1
github.com/NebulousLabs/Sia v1.3.7 github.com/NebulousLabs/Sia v1.3.7
github.com/btcsuite/btcd v0.0.0-20181013004428-67e573d211ac github.com/btcsuite/btcd v0.0.0-20181013004428-67e573d211ac
......
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/33cn/chain33 v0.0.0-20191126060956-6354775a8a5d h1:O23Rws82Z7xtnMMEXWh96tmfGcH3z7Ec+3X1ltzAYeM= github.com/33cn/chain33 v0.0.0-20191206075140-7c09cb251660 h1:m8T+ouTlPXP1e/SUPldWXSwFimfvmj/uo2hontTCts8=
github.com/33cn/chain33 v0.0.0-20191126060956-6354775a8a5d/go.mod h1:4I8n+Zyf3t0UKM5jjpqJY627Tub62oXkLsdzIv4r6rQ= github.com/33cn/chain33 v0.0.0-20191206075140-7c09cb251660/go.mod h1:4I8n+Zyf3t0UKM5jjpqJY627Tub62oXkLsdzIv4r6rQ=
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 h1:PqzgE6kAMi81xWQA2QIVxjWkFHptGgC547vchpUbtFo= github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 h1:PqzgE6kAMi81xWQA2QIVxjWkFHptGgC547vchpUbtFo=
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
_ "github.com/33cn/plugin/plugin/dapp/privacy" //auto gen _ "github.com/33cn/plugin/plugin/dapp/privacy" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/relay" //auto gen _ "github.com/33cn/plugin/plugin/dapp/relay" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/retrieve" //auto gen _ "github.com/33cn/plugin/plugin/dapp/retrieve" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/storage" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/ticket" //auto gen _ "github.com/33cn/plugin/plugin/dapp/ticket" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/token" //auto gen _ "github.com/33cn/plugin/plugin/dapp/token" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/trade" //auto gen _ "github.com/33cn/plugin/plugin/dapp/trade" //auto gen
......
This diff is collapsed.
FROM ubuntu:16.04
WORKDIR /root
COPY chain33 chain33
COPY chain33-cli chain33-cli
COPY entrypoint.sh entrypoint.sh
COPY chain33.toml chain33*.toml ./
CMD ["/root/chain33", "-f", "/root/chain33.toml"]
op := "start"
.PHONY: docker-compose help
docker-compose: ## build docker-compose for chain33 run
@./docker-compose.sh $(op)
docker-compose-down: ## build docker-compose for chain33 run
@cd temp ;docker-compose down;cd ..
help: ## Display this help screen
@printf "Help doc:\nUsage: make docker-compose op=[command]\n"
@printf "[command]\n"
@printf "[nodegroup]: create super node group if not create \n"
@printf "[wallet]: set node wallet private key if not set \n"
\ No newline at end of file
paraName="test"
#genesisAccount=""
#genesisAmount=100000000
#mainStartHeight=4800000
#authAccount=()
#authPrikey=()
##docker8901端口暴露到宿主机的端口
#authPort=("18901" "18902" "18903" "18904")
#
##需要和chain33 主链保持一致
#superManager="['1JmFaA6unrCFYEWPGRi7uuXY1KthTJxJEP']"
#
##nodegroup create
##申请超级账户需要在主链冻结币数量
#authFrozenCoins=0
#nodeGroupApplier=""
#applierPrikey=""
#superManagerPrikey=""
#### 测试链配置,主链配置需要把如下测试链配置屏蔽 ##########
genesisAccount="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
genesisAmount=100000000
mainStartHeight=4000000
authAccount=( "1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4" "1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR" "1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k" "1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs")
authPrikey=("0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b" "0x19c069234f9d3e61135fefbeb7791b149cdf6af536f26bebb310d4cd22c3fee4" "0x7a80a1f75d7360c6123c32a78ecf978c1ac55636f87892df38d8b85a9aeff115" "0xcacb1f5d51700aea07fca2246ab43b0917d70405c65edea9b5063d72eb5c6b71")
authPort=("18901" "18902" "18903" "18904")
#需要和chain33 主链保持一致
superManager="['12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv']"
tokenApprs="['12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv']"
#nodegroup create
authFrozenCoins=0
nodeGroupApplier="1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4"
applierPrikey="0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b"
superManagerPrikey="4257D8692EF7FE13C68B65D6A52F03933DB2FA5CE8FAF210B5B8B80C721CED01"
\ No newline at end of file
#!/usr/bin/env bash
# shellcheck disable=SC2154
set -e
PWD=$(cd "$(dirname "$0")" && pwd)
export PATH="$PWD:$PATH"
buildpath="temp"
NODE1="$buildpath""_parachain1_1"
CLI="docker exec ${NODE1} /root/chain33-cli"
NODE2="$buildpath""_parachain2_1"
NODE3="$buildpath""_parachain3_1"
NODE4="$buildpath""_parachain4_1"
CHAIN33_CLI="chain33-cli"
containers=("${NODE1}" "${NODE2}" "${NODE3}" "${NODE4}")
## global config ###
sedfix=""
if [ "$(uname)" == "Darwin" ]; then
sedfix=".bak"
fi
# shellcheck source=/dev/null
source config
####################
function para_init() {
local index=1
for auth in "${authAccount[@]}"; do
tomlfile="chain33.para.$index.toml"
para_set_toml "$tomlfile"
sed -i $sedfix 's/^authAccount=.*/authAccount="'''"$auth"'''"/g' "$tomlfile"
((index++))
done
}
function para_set_toml() {
cp chain33.para.toml "${1}"
sed -i $sedfix 's/^Title.*/Title="user.p.'''"$paraName"'''."/g' "${1}"
sed -i $sedfix 's/^startHeight=.*/startHeight='''"$mainStartHeight"'''/g' "${1}"
# rpc
sed -i $sedfix 's/^jrpcBindAddr=.*/jrpcBindAddr="0.0.0.0:8901"/g' "${1}"
sed -i $sedfix 's/^grpcBindAddr=.*/grpcBindAddr="0.0.0.0:8902"/g' "${1}"
sed -i $sedfix 's/^whitelist=.*/whitelist=["localhost","127.0.0.1","0.0.0.0"]/g' "${1}"
if [ -n "$superManager" ]; then
# shellcheck disable=SC1004
sed -i $sedfix 's/^superManager=.*/superManager='''"$superManager"'''/g' "${1}"
fi
if [ -n "$tokenApprs" ]; then
# shellcheck disable=SC1004
sed -i $sedfix 's/^tokenApprs=.*/tokenApprs='''"$tokenApprs"'''/g' "${1}"
fi
}
function para_set_wallet() {
echo "=========== # para set wallet ============="
for ((i = 0; i < ${#authAccount[@]}; i++)); do
para_import_wallet "${authPort[$i]}" "${authPrikey[$i]}"
done
}
function para_import_wallet() {
local key=$2
local port=$1
echo "=========== # save seed to wallet ============="
./$CHAIN33_CLI --rpc_laddr "http://localhost:$port" seed save -p 1314fuzamei -s "tortoise main civil member grace happy century convince father cage beach hip maid merry rib"
echo "=========== # unlock wallet ============="
./$CHAIN33_CLI --rpc_laddr "http://localhost:$port" wallet unlock -p 1314fuzamei -t 0
echo "=========== # import private key ============="
echo "key: ${key}"
./$CHAIN33_CLI --rpc_laddr "http://localhost:$port" account import_key -k "${key}" -l "paraAuthAccount"
echo "=========== # close auto mining ============="
./$CHAIN33_CLI --rpc_laddr "http://localhost:$port" wallet auto_mine -f 0
echo "=========== # wallet status ============="
./$CHAIN33_CLI --rpc_laddr "http://localhost:$port" wallet status
}
function start() {
echo "=========== # docker-compose ps ============="
docker-compose ps
docker-compose down
# create and run docker-compose container
docker-compose up --build -d
local SLEEP=10
echo "=========== sleep ${SLEEP}s ============="
sleep ${SLEEP}
docker-compose ps
# query node run status
echo "status"
check_docker_status
# ./chain33-cli --rpc_laddr http://localhost:18901 block last_header
$CLI --rpc_laddr http://localhost:8901 block last_header
}
function check_docker_status() {
status=$(docker-compose ps | grep parachain1_1 | awk '{print $6}')
statusPara=$(docker-compose ps | grep parachain1_1 | awk '{print $3}')
if [ "${status}" == "Exit" ] || [ "${statusPara}" == "Exit" ]; then
echo "=========== chain33 service Exit logs ========== "
docker-compose logs parachain1
echo "=========== chain33 service Exit logs End========== "
fi
}
function check_docker_container() {
echo "============== check_docker_container ==============================="
for con in "${containers[@]}"; do
runing=$(docker inspect "${con}" | jq '.[0].State.Running')
if [ ! "${runing}" ]; then
docker inspect "${con}"
echo "check ${con} not actived!"
exit 1
fi
done
}
function query_tx() {
sleep 5
local times=100
while true; do
ret=$(${CLI} --rpc_laddr http://localhost:8901 tx query -s "${1}" | jq -r ".tx.hash")
echo "query hash is ${1}, return ${ret} "
if [ "${ret}" != "${1}" ]; then
sleep 5
times=$((times - 1))
if [ $times -le 0 ]; then
echo "query tx=$1 failed"
exit 1
fi
else
echo "query tx=$1 success"
break
fi
done
}
function create_yml() {
touch docker-compose.yml
cat >>docker-compose.yml <<EOF
version: '3'
services:
EOF
for ((i = 1; i <= ${#authAccount[@]}; i++)); do
cat >>docker-compose.yml <<EOF
parachain$i:
build:
context: .
entrypoint: /root/entrypoint.sh
environment:
PARAFILE: "/root/chain33.para.$i.toml"
ports:
- "1890$i:8901"
volumes:
- "../storage/parachain$i/paradatadir:/root/paradatadir"
- "../storage/parachain$i/logs:/root/logs"
EOF
done
}
function create_storage() {
mkdir -p storage
cd storage
for ((i = 0; i < ${#authAccount[@]}; i++)); do
dirfile="parachain$i"
mkdir -p "$dirfile"
done
cd ..
}
function create_build() {
rm -rf $buildpath
mkdir -p $buildpath
cp chain33* Dockerfile ./*.sh "$buildpath"/
cd $buildpath
create_yml
}
function para_create_nodegroup() {
echo "=========== # para chain create node group ============="
local auths=""
for auth in "${authAccount[@]}"; do
if [ -z $auths ]; then
auths="$auth"
else
auths="$auths,$auth"
fi
done
echo "auths=$auths"
##apply
txhash=$(${CLI} --rpc_laddr http://localhost:8901 --paraName "user.p.$paraName." send para nodegroup apply -a "$auths" -c "${authFrozenCoins}" -k "$applierPrikey")
echo "tx=$txhash"
query_tx "${txhash}"
id=$txhash
echo "need super manager approve id=$txhash"
if [ -n "$superManagerPrikey" ]; then
echo "=========== # para chain approve node group ============="
##approve
txhash=$(${CLI} --rpc_laddr http://localhost:8901 --paraName "user.p.$paraName." send para nodegroup approve -i "$id" -c "${authFrozenCoins}" -k "$superManagerPrikey")
echo "tx=$txhash"
query_tx "${CLI}" "${txhash}"
status=$(${CLI} --rpc_laddr http://localhost:8901 --paraName "user.p.$paraName." para nodegroup status | jq -r ".status")
if [ "$status" != 2 ]; then
echo "status not approve status=$status"
exit 1
fi
${CLI} --rpc_laddr http://localhost:8901 --paraName "user.p.$paraName." para nodegroup addrs
fi
echo "======== super node group config end ==================="
}
function main() {
echo "==============================parachain startup op=$1========================================================"
### init para ####
if [ "$1" == "start" ]; then
create_storage
create_build
para_init
### start docker ####
start
### finish ###
check_docker_container
fi
if [ "$1" == "nodegroup" ]; then
para_create_nodegroup
fi
if [ "$1" == "wallet" ]; then
para_set_wallet
fi
echo "===============================parachain startup end========================================================="
}
# run script
main "$1"
version: '3'
services:
\ No newline at end of file
#!/usr/bin/env bash
/root/chain33 -f "$PARAFILE"
This diff is collapsed.
...@@ -37,6 +37,7 @@ func ParcCmd() *cobra.Command { ...@@ -37,6 +37,7 @@ func ParcCmd() *cobra.Command {
paraConfigCmd(), paraConfigCmd(),
GetParaInfoCmd(), GetParaInfoCmd(),
GetParaListCmd(), GetParaListCmd(),
GetParaAssetTransCmd(),
IsSyncCmd(), IsSyncCmd(),
GetHeightCmd(), GetHeightCmd(),
GetBlockInfoCmd(), GetBlockInfoCmd(),
...@@ -936,6 +937,35 @@ func getNodeGroupAddrsCmd() *cobra.Command { ...@@ -936,6 +937,35 @@ func getNodeGroupAddrsCmd() *cobra.Command {
return cmd return cmd
} }
func addParaAssetTranCmdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("hash", "s", "", "asset transfer tx hash")
cmd.MarkFlagRequired("hash")
}
func paraAssetTransfer(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
hash, _ := cmd.Flags().GetString("hash")
params := types.ReqString{
Data: hash,
}
var res pt.ParacrossAssetRsp
ctx := jsonclient.NewRPCCtx(rpcLaddr, "paracross.GetAssetTxResult", params, &res)
ctx.Run()
}
// GetParaAssetTransCmd get para chain asset transfer info
func GetParaAssetTransCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "asset_tranfer",
Short: "Get para chain cross asset transfer info",
Run: paraAssetTransfer,
}
addParaAssetTranCmdFlags(cmd)
return cmd
}
func nodeGroup(cmd *cobra.Command, args []string) { func nodeGroup(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
paraName, _ := cmd.Flags().GetString("paraName") paraName, _ := cmd.Flags().GetString("paraName")
......
...@@ -318,10 +318,10 @@ func updateCommitAddrs(stat *pt.ParacrossHeightStatus, nodes map[string]struct{} ...@@ -318,10 +318,10 @@ func updateCommitAddrs(stat *pt.ParacrossHeightStatus, nodes map[string]struct{}
func (a *action) Commit(commit *pt.ParacrossCommitAction) (*types.Receipt, error) { func (a *action) Commit(commit *pt.ParacrossCommitAction) (*types.Receipt, error) {
cfg := a.api.GetConfig() cfg := a.api.GetConfig()
if cfg.IsPara() && cfg.IsDappFork(a.height, pt.ParaX, pt.ForkParaSelfConsStages) { if cfg.IsPara() && cfg.IsDappFork(commit.Status.Height, pt.ParaX, pt.ForkParaSelfConsStages) {
//分叉之后,key不存在,自共识没配置也认为不支持自共识 //分叉之后,key不存在,自共识没配置也认为不支持自共识
isSelfConsOn, err := isSelfConsOn(a.db, commit.Status.Height) isSelfConsOn, err := isSelfConsOn(a.db, commit.Status.Height)
if err != nil { if err != nil && errors.Cause(err) != pt.ErrKeyNotExist {
return nil, err return nil, err
} }
if !isSelfConsOn { if !isSelfConsOn {
...@@ -748,7 +748,7 @@ func (a *action) isAllowConsensJump(commit *pt.ParacrossCommitAction, titleStatu ...@@ -748,7 +748,7 @@ func (a *action) isAllowConsensJump(commit *pt.ParacrossCommitAction, titleStatu
} }
func (a *action) execCrossTx(tx *types.TransactionDetail, crossTxHash []byte) (*types.Receipt, error) { func execCrossTx(a *action, tx *types.TransactionDetail, crossTxHash []byte) (*types.Receipt, error) {
if !bytes.HasSuffix(tx.Tx.Execer, []byte(pt.ParaX)) { if !bytes.HasSuffix(tx.Tx.Execer, []byte(pt.ParaX)) {
return nil, nil return nil, nil
} }
...@@ -762,7 +762,7 @@ func (a *action) execCrossTx(tx *types.TransactionDetail, crossTxHash []byte) (* ...@@ -762,7 +762,7 @@ func (a *action) execCrossTx(tx *types.TransactionDetail, crossTxHash []byte) (*
if payload.Ty == pt.ParacrossActionAssetWithdraw { if payload.Ty == pt.ParacrossActionAssetWithdraw {
receiptWithdraw, err := a.assetWithdraw(payload.GetAssetWithdraw(), tx.Tx) receiptWithdraw, err := a.assetWithdraw(payload.GetAssetWithdraw(), tx.Tx)
if err != nil { if err != nil {
clog.Crit("paracross.Commit Decode Tx failed", "error", err, "txHash", hex.EncodeToString(crossTxHash)) clog.Crit("paracross.Commit withdraw Tx failed", "error", err, "txHash", hex.EncodeToString(crossTxHash))
return nil, errors.Cause(err) return nil, errors.Cause(err)
} }
...@@ -773,6 +773,31 @@ func (a *action) execCrossTx(tx *types.TransactionDetail, crossTxHash []byte) (* ...@@ -773,6 +773,31 @@ func (a *action) execCrossTx(tx *types.TransactionDetail, crossTxHash []byte) (*
} }
func rollbackCrossTx(a *action, tx *types.TransactionDetail, crossTxHash []byte) (*types.Receipt, error) {
if !bytes.HasSuffix(tx.Tx.Execer, []byte(pt.ParaX)) {
return nil, nil
}
var payload pt.ParacrossAction
err := types.Decode(tx.Tx.Payload, &payload)
if err != nil {
clog.Crit("paracross.Commit.rollbackCrossTx Decode Tx failed", "error", err, "txHash", hex.EncodeToString(crossTxHash))
return nil, err
}
if payload.Ty == pt.ParacrossActionAssetTransfer {
receipt, err := a.assetTransferRollback(payload.GetAssetTransfer(), tx.Tx)
if err != nil {
clog.Crit("paracross.Commit rbk Tx failed", "error", err, "txHash", hex.EncodeToString(crossTxHash))
return nil, errors.Cause(err)
}
clog.Debug("paracross.Commit rollbackCrossTx", "txHash", hex.EncodeToString(crossTxHash), "mainHeight", a.height)
return receipt, nil
}
return nil, nil
}
func getCrossTxHashsByRst(api client.QueueProtocolAPI, status *pt.ParacrossNodeStatus) ([][]byte, []byte, error) { func getCrossTxHashsByRst(api client.QueueProtocolAPI, status *pt.ParacrossNodeStatus) ([][]byte, []byte, error) {
//只获取跨链tx //只获取跨链tx
cfg := api.GetConfig() cfg := api.GetConfig()
...@@ -781,11 +806,13 @@ func getCrossTxHashsByRst(api client.QueueProtocolAPI, status *pt.ParacrossNodeS ...@@ -781,11 +806,13 @@ func getCrossTxHashsByRst(api client.QueueProtocolAPI, status *pt.ParacrossNodeS
clog.Error("getCrossTxHashs decode rst", "CrossTxResult", string(status.TxResult), "paraHeight", status.Height) clog.Error("getCrossTxHashs decode rst", "CrossTxResult", string(status.TxResult), "paraHeight", status.Height)
return nil, nil, types.ErrInvalidParam return nil, nil, types.ErrInvalidParam
} }
clog.Debug("getCrossTxHashsByRst", "height", status.Height, "txResult", string(status.TxResult))
//空块 if !cfg.IsDappFork(status.MainBlockHeight, pt.ParaX, pt.ForkParaAssetTransferRbk) {
if len(rst) == 0 { if len(rst) == 0 {
return nil, nil, nil return nil, nil, nil
} }
}
blockDetail, err := GetBlock(api, status.MainBlockHash) blockDetail, err := GetBlock(api, status.MainBlockHash)
if err != nil { if err != nil {
...@@ -800,6 +827,7 @@ func getCrossTxHashsByRst(api client.QueueProtocolAPI, status *pt.ParacrossNodeS ...@@ -800,6 +827,7 @@ func getCrossTxHashsByRst(api client.QueueProtocolAPI, status *pt.ParacrossNodeS
} }
paraCrossHashs := FilterParaCrossTxHashes(paraAllTxs) paraCrossHashs := FilterParaCrossTxHashes(paraAllTxs)
crossRst := util.CalcBitMapByBitMap(paraCrossHashs, baseHashs, rst) crossRst := util.CalcBitMapByBitMap(paraCrossHashs, baseHashs, rst)
clog.Debug("getCrossTxHashsByRst.crossRst", "height", status.Height, "txResult", hex.EncodeToString(crossRst), "len", len(paraCrossHashs))
return paraCrossHashs, crossRst, nil return paraCrossHashs, crossRst, nil
...@@ -858,6 +886,24 @@ func getCrossTxHashs(api client.QueueProtocolAPI, status *pt.ParacrossNodeStatus ...@@ -858,6 +886,24 @@ func getCrossTxHashs(api client.QueueProtocolAPI, status *pt.ParacrossNodeStatus
} }
func crossTxProc(a *action, txHash []byte, fn func(*action, *types.TransactionDetail, []byte) (*types.Receipt, error)) (*types.Receipt, error) {
tx, err := GetTx(a.api, txHash)
if err != nil {
clog.Crit("paracross.Commit Load Tx failed", "error", err, "txHash", hex.EncodeToString(txHash))
return nil, err
}
if tx == nil {
clog.Error("paracross.Commit Load Tx nil", "error", err, "txHash", hex.EncodeToString(txHash))
return nil, types.ErrHashNotExist
}
receiptCross, err := fn(a, tx, txHash)
if err != nil {
clog.Error("paracross.Commit execCrossTx", "error", err)
return nil, errors.Cause(err)
}
return receiptCross, nil
}
func (a *action) execCrossTxs(status *pt.ParacrossNodeStatus) (*types.Receipt, error) { func (a *action) execCrossTxs(status *pt.ParacrossNodeStatus) (*types.Receipt, error) {
var receipt types.Receipt var receipt types.Receipt
...@@ -866,28 +912,30 @@ func (a *action) execCrossTxs(status *pt.ParacrossNodeStatus) (*types.Receipt, e ...@@ -866,28 +912,30 @@ func (a *action) execCrossTxs(status *pt.ParacrossNodeStatus) (*types.Receipt, e
clog.Error("paracross.Commit getCrossTxHashs", "err", err.Error()) clog.Error("paracross.Commit getCrossTxHashs", "err", err.Error())
return nil, err return nil, err
} }
if len(crossTxHashs) == 0 {
return &receipt, nil
}
for i := 0; i < len(crossTxHashs); i++ { for i := 0; i < len(crossTxHashs); i++ {
clog.Debug("paracross.Commit commitDone", "do cross number", i, "hash", hex.EncodeToString(crossTxHashs[i]), clog.Debug("paracross.Commit commitDone", "do cross number", i, "hash", hex.EncodeToString(crossTxHashs[i]),
"res", util.BitMapBit(crossTxResult, uint32(i))) "res", util.BitMapBit(crossTxResult, uint32(i)))
if util.BitMapBit(crossTxResult, uint32(i)) { if util.BitMapBit(crossTxResult, uint32(i)) {
tx, err := GetTx(a.api, crossTxHashs[i]) receiptCross, err := crossTxProc(a, crossTxHashs[i], execCrossTx)
if err != nil { if err != nil {
clog.Crit("paracross.Commit Load Tx failed", "para title", title, "para height", status.Height, clog.Error("paracross.Commit execCrossTx", "para title", status.Title, "para height", status.Height,
"para tx index", i, "error", err, "txHash", hex.EncodeToString(crossTxHashs[i])) "para tx index", i, "error", err)
return nil, err return nil, errors.Cause(err)
} }
if tx == nil { if receiptCross == nil {
clog.Error("paracross.Commit Load Tx failed", "para title", title, "para height", status.Height, continue
"para tx index", i, "error", err, "txHash", hex.EncodeToString(crossTxHashs[i]))
return nil, types.ErrHashNotExist
} }
receiptCross, err := a.execCrossTx(tx, crossTxHashs[i]) receipt.KV = append(receipt.KV, receiptCross.KV...)
receipt.Logs = append(receipt.Logs, receiptCross.Logs...)
} else {
clog.Error("paracross.Commit commitDone", "do cross number", i, "hash",
hex.EncodeToString(crossTxHashs[i]), "para res", util.BitMapBit(crossTxResult, uint32(i)))
cfg := a.api.GetConfig()
if cfg.IsDappFork(a.height, pt.ParaX, pt.ForkParaAssetTransferRbk) {
receiptCross, err := crossTxProc(a, crossTxHashs[i], rollbackCrossTx)
if err != nil { if err != nil {
clog.Error("paracross.Commit execCrossTx", "para title", title, "para height", status.Height, clog.Error("paracross.Commit rollbackCrossTx", "para title", status.Title, "para height", status.Height,
"para tx index", i, "error", err) "para tx index", i, "error", err)
return nil, errors.Cause(err) return nil, errors.Cause(err)
} }
...@@ -896,10 +944,7 @@ func (a *action) execCrossTxs(status *pt.ParacrossNodeStatus) (*types.Receipt, e ...@@ -896,10 +944,7 @@ func (a *action) execCrossTxs(status *pt.ParacrossNodeStatus) (*types.Receipt, e
} }
receipt.KV = append(receipt.KV, receiptCross.KV...) receipt.KV = append(receipt.KV, receiptCross.KV...)
receipt.Logs = append(receipt.Logs, receiptCross.Logs...) receipt.Logs = append(receipt.Logs, receiptCross.Logs...)
} else { }
clog.Error("paracross.Commit commitDone", "do cross number", i, "hash",
hex.EncodeToString(crossTxHashs[i]),
"para res", util.BitMapBit(crossTxResult, uint32(i)))
} }
} }
......
...@@ -87,6 +87,24 @@ func (a *action) assetWithdraw(withdraw *types.AssetsWithdraw, withdrawTx *types ...@@ -87,6 +87,24 @@ func (a *action) assetWithdraw(withdraw *types.AssetsWithdraw, withdrawTx *types
return assetWithdrawBalance(paraAcc, a.fromaddr, withdraw.Amount) return assetWithdrawBalance(paraAcc, a.fromaddr, withdraw.Amount)
} }
func (a *action) assetTransferRollback(transfer *types.AssetsTransfer, transferTx *types.Transaction) (*types.Receipt, error) {
cfg := a.api.GetConfig()
isPara := cfg.IsPara()
//主链处理分支
if !isPara {
accDB, err := createAccount(cfg, a.db, transfer.Cointoken)
if err != nil {
return nil, errors.Wrap(err, "assetTransferToken call account.NewAccountDB failed")
}
execAddr := address.ExecAddress(pt.ParaX)
fromAcc := address.ExecAddress(string(transferTx.Execer))
clog.Debug("paracross.AssetTransferRbk ", "execer", string(transferTx.Execer),
"transfer.txHash", hex.EncodeToString(transferTx.Hash()), "curTx", hex.EncodeToString(a.tx.Hash()))
return accDB.ExecTransfer(fromAcc, transferTx.From(), execAddr, transfer.Amount)
}
return nil, nil
}
func createAccount(cfg *types.Chain33Config, db db.KV, symbol string) (*account.DB, error) { func createAccount(cfg *types.Chain33Config, db db.KV, symbol string) (*account.DB, error) {
var accDB *account.DB var accDB *account.DB
var err error var err error
......
...@@ -228,6 +228,7 @@ func setMinerTxResultFork(cfg *types.Chain33Config, status *pt.ParacrossNodeStat ...@@ -228,6 +228,7 @@ func setMinerTxResultFork(cfg *types.Chain33Config, status *pt.ParacrossNodeStat
//主链自己过滤平行链tx, 对平行链执行失败的tx主链无法识别,主链和平行链需要获取相同的最初的tx map //主链自己过滤平行链tx, 对平行链执行失败的tx主链无法识别,主链和平行链需要获取相同的最初的tx map
//全部平行链tx结果 //全部平行链tx结果
status.TxResult = []byte(hex.EncodeToString(util.CalcSingleBitMap(curTxHashs, receipts))) status.TxResult = []byte(hex.EncodeToString(util.CalcSingleBitMap(curTxHashs, receipts)))
clog.Debug("setMinerTxResultFork", "height", status.Height, "txResult", string(status.TxResult))
//ForkLoopCheckCommitTxDone 后只保留全部txreseult 结果 //ForkLoopCheckCommitTxDone 后只保留全部txreseult 结果
if !pt.IsParaForkHeight(cfg, status.MainBlockHeight, pt.ForkLoopCheckCommitTxDone) { if !pt.IsParaForkHeight(cfg, status.MainBlockHeight, pt.ForkLoopCheckCommitTxDone) {
......
...@@ -218,11 +218,15 @@ func (p *Paracross) Query_GetDoneTitleHeight(in *pt.ReqParacrossTitleHeight) (ty ...@@ -218,11 +218,15 @@ func (p *Paracross) Query_GetDoneTitleHeight(in *pt.ReqParacrossTitleHeight) (ty
} }
// Query_GetAssetTxResult query get asset tx reseult // Query_GetAssetTxResult query get asset tx reseult
func (p *Paracross) Query_GetAssetTxResult(in *types.ReqHash) (types.Message, error) { func (p *Paracross) Query_GetAssetTxResult(in *types.ReqString) (types.Message, error) {
if in == nil { if in == nil || in.Data == "" {
return nil, types.ErrInvalidParam return nil, types.ErrInvalidParam
} }
return p.paracrossGetAssetTxResult(in.Hash) hash, err := common.FromHex(in.Data)
if err != nil {
return nil, errors.Wrap(err, "fromHex")
}
return p.paracrossGetAssetTxResult(hash)
} }
// Query_GetMainBlockHash query get mainblockHash by tx // Query_GetMainBlockHash query get mainblockHash by tx
...@@ -296,7 +300,7 @@ func listLocalTitles(db dbm.KVDB) (types.Message, error) { ...@@ -296,7 +300,7 @@ func listLocalTitles(db dbm.KVDB) (types.Message, error) {
MostSameCommit: st.MostSameCommit, MostSameCommit: st.MostSameCommit,
Title: st.Title, Title: st.Title,
Height: st.Height, Height: st.Height,
TxResult: hex.EncodeToString(st.TxResult), TxResult: string(st.TxResult),
} }
resp.Titles = append(resp.Titles, rst) resp.Titles = append(resp.Titles, rst)
...@@ -400,13 +404,34 @@ func (p *Paracross) paracrossGetAssetTxResult(hash []byte) (types.Message, error ...@@ -400,13 +404,34 @@ func (p *Paracross) paracrossGetAssetTxResult(hash []byte) (types.Message, error
return nil, err return nil, err
} }
var result pt.ParacrossAsset var rst pt.ParacrossAsset
err = types.Decode(value, &result) err = types.Decode(value, &rst)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &result, nil rsp := &pt.ParacrossAssetRsp{
From: rst.From,
To: rst.To,
Amount: rst.Amount,
Exec: rst.Exec,
Symbol: rst.Symbol,
Height: rst.Height,
CommitDoneHeight: rst.CommitDoneHeight,
ParaHeight: rst.ParaHeight,
}
rsp.TxHash = common.ToHex(rst.TxHash)
rsp.IsWithdraw = "false"
if rst.IsWithdraw {
rsp.IsWithdraw = "true"
}
rsp.Success = "false"
if rst.Success {
rsp.Success = "true"
}
return rsp, nil
} }
//Query_GetSelfConsStages get self consensus stages configed //Query_GetSelfConsStages get self consensus stages configed
......
...@@ -385,6 +385,23 @@ message ParacrossAsset { ...@@ -385,6 +385,23 @@ message ParacrossAsset {
bool success = 23; bool success = 23;
} }
message ParacrossAssetRsp {
// input
string from = 1;
string to = 2;
string isWithdraw = 3;
string txHash = 4;
int64 amount = 5;
string exec = 6;
string symbol = 7;
// 主链部分
int64 height = 10;
// 平行链部分
int64 commitDoneHeight = 21;
int64 paraHeight = 22;
string success = 23;
}
message ParaLocalDbBlock { message ParaLocalDbBlock {
int64 height = 1; int64 height = 1;
bytes mainHash = 2; bytes mainHash = 2;
...@@ -408,6 +425,6 @@ service paracross { ...@@ -408,6 +425,6 @@ service paracross {
rpc ListTitles(ReqNil) returns (RespParacrossTitles) {} rpc ListTitles(ReqNil) returns (RespParacrossTitles) {}
rpc GetDoneTitleHeight(ReqParacrossTitleHeight) returns (RespParacrossDone) {} rpc GetDoneTitleHeight(ReqParacrossTitleHeight) returns (RespParacrossDone) {}
rpc GetTitleHeight(ReqParacrossTitleHeight) returns (ParacrossHeightStatusRsp) {} rpc GetTitleHeight(ReqParacrossTitleHeight) returns (ParacrossHeightStatusRsp) {}
rpc GetAssetTxResult(ReqHash) returns (ParacrossAsset) {} rpc GetAssetTxResult(ReqString) returns (ParacrossAssetRsp) {}
rpc IsSync(ReqNil) returns (IsCaughtUp) {} rpc IsSync(ReqNil) returns (IsCaughtUp) {}
} }
\ No newline at end of file
...@@ -118,20 +118,20 @@ func (c *channelClient) GetDoneTitleHeight(ctx context.Context, req *pt.ReqParac ...@@ -118,20 +118,20 @@ func (c *channelClient) GetDoneTitleHeight(ctx context.Context, req *pt.ReqParac
return nil, types.ErrDecode return nil, types.ErrDecode
} }
func (c *channelClient) GetAssetTxResult(ctx context.Context, req *types.ReqHash) (*pt.ParacrossAsset, error) { func (c *channelClient) GetAssetTxResult(ctx context.Context, req *types.ReqString) (*pt.ParacrossAssetRsp, error) {
cfg := c.GetConfig() cfg := c.GetConfig()
data, err := c.Query(pt.GetExecName(cfg), "GetAssetTxResult", req) data, err := c.Query(pt.GetExecName(cfg), "GetAssetTxResult", req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if resp, ok := data.(*pt.ParacrossAsset); ok { if resp, ok := data.(*pt.ParacrossAssetRsp); ok {
return resp, nil return resp, nil
} }
return nil, types.ErrDecode return nil, types.ErrDecode
} }
// GetAssetTxResult get asset tx result // GetAssetTxResult get asset tx result
func (c *Jrpc) GetAssetTxResult(req *types.ReqHash, result *interface{}) error { func (c *Jrpc) GetAssetTxResult(req *types.ReqString, result *interface{}) error {
if req == nil { if req == nil {
return types.ErrInvalidParam return types.ErrInvalidParam
} }
......
...@@ -124,8 +124,8 @@ func TestChannelClient_GetAssetTxResult(t *testing.T) { ...@@ -124,8 +124,8 @@ func TestChannelClient_GetAssetTxResult(t *testing.T) {
api.On("GetConfig", mock.Anything).Return(cfg, nil) api.On("GetConfig", mock.Anything).Return(cfg, nil)
client := newGrpc(api) client := newGrpc(api)
client.Init("paracross", nil, nil, nil) client.Init("paracross", nil, nil, nil)
req := &types.ReqHash{} req := &types.ReqString{}
api.On("Query", pt.GetExecName(cfg), "GetAssetTxResult", req).Return(&pt.ParacrossAsset{}, nil) api.On("Query", pt.GetExecName(cfg), "GetAssetTxResult", req).Return(&pt.ParacrossAssetRsp{}, nil)
_, err := client.GetAssetTxResult(context.Background(), req) _, err := client.GetAssetTxResult(context.Background(), req)
assert.Nil(t, err) assert.Nil(t, err)
} }
...@@ -135,9 +135,9 @@ func TestJrpc_GetAssetTxResult(t *testing.T) { ...@@ -135,9 +135,9 @@ func TestJrpc_GetAssetTxResult(t *testing.T) {
api := new(mocks.QueueProtocolAPI) api := new(mocks.QueueProtocolAPI)
api.On("GetConfig", mock.Anything).Return(cfg, nil) api.On("GetConfig", mock.Anything).Return(cfg, nil)
j := newJrpc(api) j := newJrpc(api)
req := &types.ReqHash{} req := &types.ReqString{}
var result interface{} var result interface{}
api.On("Query", pt.GetExecName(cfg), "GetAssetTxResult", req).Return(&pt.ParacrossAsset{}, nil) api.On("Query", pt.GetExecName(cfg), "GetAssetTxResult", req).Return(&pt.ParacrossAssetRsp{}, nil)
err := j.GetAssetTxResult(req, &result) err := j.GetAssetTxResult(req, &result)
assert.Nil(t, err) assert.Nil(t, err)
} }
......
This diff is collapsed.
...@@ -26,6 +26,8 @@ var ( ...@@ -26,6 +26,8 @@ var (
MainLoopCheckCommitTxDoneForkHeight = "mainLoopCheckCommitTxDoneForkHeight" MainLoopCheckCommitTxDoneForkHeight = "mainLoopCheckCommitTxDoneForkHeight"
// ForkParaSelfConsStages 平行链自共识分阶段共识 // ForkParaSelfConsStages 平行链自共识分阶段共识
ForkParaSelfConsStages = "ForkParaSelfConsStages" ForkParaSelfConsStages = "ForkParaSelfConsStages"
// ForkParaAssetTransferRbk 平行链资产转移平行链失败主链回滚
ForkParaAssetTransferRbk = "ForkParaAssetTransferRbk"
// ParaConsSubConf sub // ParaConsSubConf sub
ParaConsSubConf = "consensus.sub.para" ParaConsSubConf = "consensus.sub.para"
...@@ -52,6 +54,8 @@ func InitFork(cfg *types.Chain33Config) { ...@@ -52,6 +54,8 @@ func InitFork(cfg *types.Chain33Config) {
cfg.RegisterDappFork(ParaX, "ForkParacrossWithdrawFromParachain", 1298600) cfg.RegisterDappFork(ParaX, "ForkParacrossWithdrawFromParachain", 1298600)
cfg.RegisterDappFork(ParaX, ForkCommitTx, 1850000) cfg.RegisterDappFork(ParaX, ForkCommitTx, 1850000)
cfg.RegisterDappFork(ParaX, ForkLoopCheckCommitTxDone, 3230000) cfg.RegisterDappFork(ParaX, ForkLoopCheckCommitTxDone, 3230000)
cfg.RegisterDappFork(ParaX, ForkParaAssetTransferRbk, 4500000)
//只在平行链启用 //只在平行链启用
cfg.RegisterDappFork(ParaX, ForkParaSelfConsStages, types.MaxHeight) cfg.RegisterDappFork(ParaX, ForkParaSelfConsStages, types.MaxHeight)
} }
......
all:
chmod +x ./build.sh
./build.sh $(OUT) $(FLAG)
#!/bin/sh
# 官方ci集成脚本
strpwd=$(pwd)
strcmd=${strpwd##*dapp/}
strapp=${strcmd%/cmd*}
OUT_DIR="${1}/$strapp"
#FLAG=$2
mkdir -p "${OUT_DIR}"
cp ./build/* "${OUT_DIR}"
/*Package commands implement dapp client commands*/
package commands
import (
"github.com/spf13/cobra"
)
/*
* 实现合约对应客户端
*/
// Cmd storage client command
func Cmd() *cobra.Command {
cmd := &cobra.Command{
Use: "storage",
Short: "storage command",
Args: cobra.MinimumNArgs(1),
}
cmd.AddCommand(
//add sub command
)
return cmd
}
package crypto
import (
"crypto/aes"
"crypto/cipher"
)
type AES struct {
key []byte
//iv的长度必须等于block块的大小,这里是16字节,固定
iv []byte
}
//AES 密钥长度为 16,24,32 字节,三种
func NewAES(key, iv []byte) *AES {
return &AES{key: key, iv: iv}
}
func (a *AES) Encrypt(origData []byte) ([]byte, error) {
block, err := aes.NewCipher(a.key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
origData = PKCS5Padding(origData, blockSize)
// origData = ZeroPadding(origData, block.BlockSize())
blockMode := cipher.NewCBCEncrypter(block, a.iv[:blockSize])
crypted := make([]byte, len(origData))
// 根据CryptBlocks方法的说明,如下方式初始化crypted也可以
// crypted := origData
blockMode.CryptBlocks(crypted, origData)
return crypted, nil
}
func (a *AES) Decrypt(crypted []byte) ([]byte, error) {
block, err := aes.NewCipher(a.key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, a.iv[:blockSize])
origData := make([]byte, len(crypted))
// origData := crypted
blockMode.CryptBlocks(origData, crypted)
origData = PKCS5UnPadding(origData)
// origData = ZeroUnPadding(origData)
return origData, nil
}
package crypto
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/assert"
)
//DES 加解密测试
func TestAes(t *testing.T) {
aes := NewAES(keys[2], ivs[0])
result, err := aes.Encrypt(contents[1])
if err != nil {
t.Error(err)
}
t.Log(base64.StdEncoding.EncodeToString(result))
origData, err := aes.Decrypt(result)
if err != nil {
t.Error(err)
}
assert.Equal(t, contents[1], origData)
}
package crypto
import "bytes"
type Crypto interface {
Encrypt(origData []byte) ([]byte, error)
Decrypt(crypted []byte) ([]byte, error)
}
func ZeroPadding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{0}, padding)
return append(ciphertext, padtext...)
}
func ZeroUnPadding(origData []byte) []byte {
return bytes.TrimRightFunc(origData, func(r rune) bool {
return r == rune(0)
})
}
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func PKCS5UnPadding(origData []byte) []byte {
length := len(origData)
// 去掉最后一个字节 unpadding 次
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
package crypto
import (
"crypto/cipher"
"crypto/des"
)
type DES struct {
key []byte
//iv的长度必须等于block块的大小
iv []byte
}
func NewDES(key, iv []byte) *DES {
return &DES{key: key, iv: iv}
}
func (d *DES) Encrypt(origData []byte) ([]byte, error) {
block, err := des.NewCipher(d.key)
if err != nil {
return nil, err
}
origData = PKCS5Padding(origData, block.BlockSize())
// origData = ZeroPadding(origData, block.BlockSize())
blockMode := cipher.NewCBCEncrypter(block, d.iv[:block.BlockSize()])
crypted := make([]byte, len(origData))
// 根据CryptBlocks方法的说明,如下方式初始化crypted也可以
// crypted := origData
blockMode.CryptBlocks(crypted, origData)
return crypted, nil
}
// 密钥key长度固定8字节
func (d *DES) Decrypt(crypted []byte) ([]byte, error) {
block, err := des.NewCipher(d.key)
if err != nil {
return nil, err
}
blockMode := cipher.NewCBCDecrypter(block, d.iv[:block.BlockSize()])
origData := make([]byte, len(crypted))
// origData := crypted
blockMode.CryptBlocks(origData, crypted)
origData = PKCS5UnPadding(origData)
// origData = ZeroUnPadding(origData)
return origData, nil
}
type TripleDES struct {
key []byte
//iv的长度必须等于block块的大小
iv []byte
}
func NewTripleDES(key, iv []byte) *TripleDES {
return &TripleDES{key: key, iv: iv}
}
// 3DES加密 24字节
func (d *TripleDES) Encrypt(origData []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(d.key)
if err != nil {
return nil, err
}
origData = PKCS5Padding(origData, block.BlockSize())
// origData = ZeroPadding(origData, block.BlockSize())
blockMode := cipher.NewCBCEncrypter(block, d.iv[:block.BlockSize()])
crypted := make([]byte, len(origData))
blockMode.CryptBlocks(crypted, origData)
return crypted, nil
}
// 3DES解密
func (d *TripleDES) Decrypt(crypted []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(d.key)
if err != nil {
return nil, err
}
blockMode := cipher.NewCBCDecrypter(block, d.iv[:block.BlockSize()])
origData := make([]byte, len(crypted))
// origData := crypted
blockMode.CryptBlocks(origData, crypted)
origData = PKCS5UnPadding(origData)
// origData = ZeroUnPadding(origData)
return origData, nil
}
package crypto
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/assert"
)
var (
contents = [][]byte{
[]byte("1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4"),
[]byte("1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR"),
[]byte("1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k"),
[]byte("1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"),
}
keys = [][]byte{
[]byte("123456ab"),
[]byte("G2F4ED5m123456abx6vDrScs"),
[]byte("G2F4ED5m123456abx6vDrScsHD3psX7k"),
}
ivs = [][]byte{
[]byte("1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4"),
[]byte("1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR"),
[]byte("1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k"),
[]byte("1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"),
}
)
//DES 加解密测试
func TestDes(t *testing.T) {
des := NewDES(keys[0], ivs[0])
result, err := des.Encrypt(contents[0])
if err != nil {
t.Error(err)
}
t.Log(base64.StdEncoding.EncodeToString(result))
origData, err := des.Decrypt(result)
if err != nil {
t.Error(err)
}
assert.Equal(t, contents[0], origData)
}
//3DES 加解密测试
func Test3Des(t *testing.T) {
des := NewTripleDES(keys[1], ivs[1])
result, err := des.Encrypt(contents[0])
if err != nil {
t.Error(err)
}
t.Log(base64.StdEncoding.EncodeToString(result))
origData, err := des.Decrypt(result)
if err != nil {
t.Error(err)
}
assert.Equal(t, contents[0], origData)
}
package executor
import (
"github.com/33cn/chain33/types"
storagetypes "github.com/33cn/plugin/plugin/dapp/storage/types"
)
/*
* 实现交易的链上执行接口
* 关键数据上链(statedb)并生成交易回执(log)
*/
func (s *storage) Exec_ContentStorage(payload *storagetypes.ContentOnlyNotaryStorage, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newStorageAction(s, tx, index)
return action.ContentStorage(&storagetypes.Storage{Value: &storagetypes.Storage_ContentStorage{ContentStorage: payload}})
}
func (s *storage) Exec_HashStorage(payload *storagetypes.HashOnlyNotaryStorage, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newStorageAction(s, tx, index)
return action.HashStorage(&storagetypes.Storage{Value: &storagetypes.Storage_HashStorage{HashStorage: payload}})
}
func (s *storage) Exec_LinkStorage(payload *storagetypes.LinkNotaryStorage, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newStorageAction(s, tx, index)
return action.LinkStorage(&storagetypes.Storage{Value: &storagetypes.Storage_LinkStorage{LinkStorage: payload}})
}
func (s *storage) Exec_EncryptStorage(payload *storagetypes.EncryptNotaryStorage, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newStorageAction(s, tx, index)
return action.EncryptStorage(&storagetypes.Storage{Value: &storagetypes.Storage_EncryptStorage{EncryptStorage: payload}})
}
func (s *storage) Exec_EncryptShareStorage(payload *storagetypes.EncryptShareNotaryStorage, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newStorageAction(s, tx, index)
return action.EncryptShareStorage(&storagetypes.Storage{Value: &storagetypes.Storage_EncryptShareStorage{EncryptShareStorage: payload}})
}
package executor
import (
"github.com/33cn/chain33/types"
)
/*
* 实现区块回退时本地执行的数据清除
*/
// ExecDelLocal 回退自动删除,重写基类
func (s *storage) ExecDelLocal(tx *types.Transaction, receipt *types.ReceiptData, index int) (*types.LocalDBSet, error) {
kvs, err := s.DelRollbackKV(tx, tx.Execer)
if err != nil {
return nil, err
}
dbSet := &types.LocalDBSet{}
dbSet.KV = append(dbSet.KV, kvs...)
return dbSet, nil
}
package executor
import (
"github.com/33cn/chain33/types"
storagetypes "github.com/33cn/plugin/plugin/dapp/storage/types"
)
/*
* 实现交易相关数据本地执行,数据不上链
* 非关键数据,本地存储(localDB), 用于辅助查询,效率高
*/
func (s *storage) ExecLocal_ContentStorage(payload *storagetypes.ContentOnlyNotaryStorage, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
//implement code
return s.addAutoRollBack(tx, dbSet.KV), nil
}
func (s *storage) ExecLocal_HashStorage(payload *storagetypes.HashOnlyNotaryStorage, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
//implement code
return s.addAutoRollBack(tx, dbSet.KV), nil
}
func (s *storage) ExecLocal_LinkStorage(payload *storagetypes.LinkNotaryStorage, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
//implement code
return s.addAutoRollBack(tx, dbSet.KV), nil
}
func (s *storage) ExecLocal_EncryptStorage(payload *storagetypes.EncryptNotaryStorage, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
//implement code
return s.addAutoRollBack(tx, dbSet.KV), nil
}
func (s *storage) ExecLocal_EncryptShareStorage(payload *storagetypes.EncryptShareNotaryStorage, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
//implement code
return s.addAutoRollBack(tx, dbSet.KV), nil
}
//设置自动回滚
func (s *storage) addAutoRollBack(tx *types.Transaction, kv []*types.KeyValue) *types.LocalDBSet {
dbSet := &types.LocalDBSet{}
dbSet.KV = s.AddRollbackKV(tx, tx.Execer, kv)
return dbSet
}
package executor
/*
* 用户合约存取kv数据时,key值前缀需要满足一定规范
* 即key = keyPrefix + userKey
* 需要字段前缀查询时,使用’-‘作为分割符号
*/
var (
//KeyPrefixStateDB state db key必须前缀
KeyPrefixStateDB = "mavl-storage-"
//KeyPrefixLocalDB local db的key必须前缀
KeyPrefixLocalDB = "LODB-storage-"
)
// Key Storage to save key
func Key(txHash string) (key []byte) {
key = append(key, []byte(KeyPrefixStateDB)...)
key = append(key, []byte(txHash)...)
return key
}
package executor
import (
"github.com/33cn/chain33/types"
storagetypes "github.com/33cn/plugin/plugin/dapp/storage/types"
)
//从statedb 读取原始数据
func (s *storage) Query_QueryStorage(in *storagetypes.QueryStorage) (types.Message, error) {
return QueryStorage(s.GetStateDB(), in)
}
//通过状态查询ids
func (s *storage) Query_BatchQueryStorage(in *storagetypes.BatchQueryStorage) (types.Message, error) {
return BatchQueryStorage(s.GetStateDB(), in)
}
package executor
import (
log "github.com/33cn/chain33/common/log/log15"
drivers "github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
storagetypes "github.com/33cn/plugin/plugin/dapp/storage/types"
)
/*
* 执行器相关定义
* 重载基类相关接口
*/
var (
//日志
elog = log.New("module", "storage.executor")
)
var driverName = storagetypes.StorageX
// Init register dapp
func Init(name string, cfg *types.Chain33Config, sub []byte) {
drivers.Register(cfg, GetName(), newStorage, cfg.GetDappFork(driverName, "Enable"))
InitExecType()
}
// InitExecType Init Exec Type
func InitExecType() {
ety := types.LoadExecutorType(driverName)
ety.InitFuncList(types.ListMethod(&storage{}))
}
type storage struct {
drivers.DriverBase
}
func newStorage() drivers.Driver {
t := &storage{}
t.SetChild(t)
t.SetExecutorType(types.LoadExecutorType(driverName))
return t
}
// GetName get driver name
func GetName() string {
return newStorage().GetName()
}
func (s *storage) GetDriverName() string {
return driverName
}
// CheckTx 实现自定义检验交易接口,供框架调用
func (s *storage) CheckTx(tx *types.Transaction, index int) error {
// implement code
return nil
}
package executor
import (
"math/rand"
"testing"
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/client"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/crypto"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/queue"
des "github.com/33cn/plugin/plugin/dapp/storage/crypto"
oty "github.com/33cn/plugin/plugin/dapp/storage/types"
"github.com/stretchr/testify/assert"
"strings"
)
type execEnv struct {
blockTime int64
blockHeight int64
difficulty uint64
}
var (
PrivKeyA = "0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b" // 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4
Nodes = [][]byte{
[]byte("1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4"),
[]byte("1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR"),
[]byte("1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k"),
[]byte("1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"),
}
contents = [][]byte{
[]byte("1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4"),
[]byte("1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR"),
[]byte("1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k"),
[]byte("1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"),
}
keys = [][]byte{
[]byte("123456ab"),
[]byte("G2F4ED5m123456abx6vDrScs"),
[]byte("G2F4ED5m123456abx6vDrScsHD3psX7k"),
}
ivs = [][]byte{
[]byte("1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4"),
[]byte("1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR"),
[]byte("1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k"),
[]byte("1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"),
}
)
var (
r *rand.Rand
)
func init() {
r = rand.New(rand.NewSource(types.Now().UnixNano()))
}
func TestOrace(t *testing.T) {
cfg := types.NewChain33Config(strings.Replace(types.GetDefaultCfgstring(), "Title=\"local\"", "Title=\"chain33\"", 1))
Init(oty.StorageX, cfg, nil)
total := 100 * types.Coin
accountA := types.Account{
Balance: total,
Frozen: 0,
Addr: string(Nodes[0]),
}
accountB := types.Account{
Balance: total,
Frozen: 0,
Addr: string(Nodes[1]),
}
accountC := types.Account{
Balance: total,
Frozen: 0,
Addr: string(Nodes[2]),
}
accountD := types.Account{
Balance: total,
Frozen: 0,
Addr: string(Nodes[3]),
}
execAddr := address.ExecAddress(oty.StorageX)
stateDB, _ := dbm.NewGoMemDB("1", "2", 1000)
_, _, kvdb := util.CreateTestDB()
accA, _ := account.NewAccountDB(cfg, "coins", "bty", stateDB)
accA.SaveExecAccount(execAddr, &accountA)
accB, _ := account.NewAccountDB(cfg, "coins", "bty", stateDB)
accB.SaveExecAccount(execAddr, &accountB)
accC, _ := account.NewAccountDB(cfg, "coins", "bty", stateDB)
accC.SaveExecAccount(execAddr, &accountC)
accD, _ := account.NewAccountDB(cfg, "coins", "bty", stateDB)
accD.SaveExecAccount(execAddr, &accountD)
env := execEnv{
10,
cfg.GetDappFork(oty.StorageX, "Enable"),
1539918074,
}
// publish event
ety := types.LoadExecutorType(oty.StorageX)
tx, err := ety.Create("ContentStorage", &oty.ContentOnlyNotaryStorage{Content: contents[0]})
assert.Nil(t, err)
tx, err = types.FormatTx(cfg, oty.StorageX, tx)
assert.Nil(t, err)
tx, err = signTx(tx, PrivKeyA)
assert.Nil(t, err)
t.Log("tx", tx)
exec := newStorage()
q := queue.New("channel")
q.SetConfig(cfg)
api, _ := client.New(q.Client(), nil)
exec.SetAPI(api)
exec.SetStateDB(stateDB)
exec.SetLocalDB(kvdb)
exec.SetEnv(1, env.blockTime, env.difficulty)
receipt, err := exec.Exec(tx, int(1))
if err != nil {
t.Error(err)
}
for _, kv := range receipt.KV {
stateDB.Set(kv.Key, kv.Value)
}
receiptDate := &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err := exec.ExecLocal(tx, receiptDate, int(1))
if err != nil {
t.Error(err)
}
for _, kv := range set.KV {
kvdb.Set(kv.Key, kv.Value)
}
txhash := common.ToHex(tx.Hash())
t.Log("txhash:", txhash)
//根据hash查询存储得明文内容
msg, err := exec.Query(oty.FuncNameQueryStorage, types.Encode(&oty.QueryStorage{
TxHash: txhash}))
if err != nil {
t.Error(err)
}
t.Log(msg)
reply := msg.(*oty.Storage)
assert.Equal(t, contents[0], reply.GetContentStorage().Content)
//根据hash批量查询存储数据
msg, err = exec.Query(oty.FuncNameBatchQueryStorage, types.Encode(&oty.BatchQueryStorage{
TxHashs: []string{txhash}}))
if err != nil {
t.Error(err)
}
t.Log(msg)
reply2 := msg.(*oty.BatchReplyStorage)
assert.Equal(t, contents[0], reply2.Storages[0].GetContentStorage().Content)
tx, err = ety.Create("HashStorage", &oty.HashOnlyNotaryStorage{Hash: common.Sha256(contents[0])})
assert.Nil(t, err)
tx, err = types.FormatTx(cfg, oty.StorageX, tx)
assert.Nil(t, err)
tx, err = signTx(tx, PrivKeyA)
assert.Nil(t, err)
t.Log("tx", tx)
exec.SetEnv(env.blockHeight+1, env.blockTime+20, env.difficulty+1)
receipt, err = exec.Exec(tx, int(1))
if err != nil {
t.Error(err)
}
for _, kv := range receipt.KV {
stateDB.Set(kv.Key, kv.Value)
}
receiptDate = &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err = exec.ExecLocal(tx, receiptDate, int(1))
if err != nil {
t.Error(err)
}
for _, kv := range set.KV {
kvdb.Set(kv.Key, kv.Value)
}
txhash = common.ToHex(tx.Hash())
t.Log("txhash:", txhash)
//根据hash查询存储得明文内容
msg, err = exec.Query(oty.FuncNameQueryStorage, types.Encode(&oty.QueryStorage{
TxHash: txhash}))
if err != nil {
t.Error(err)
}
t.Log(msg)
reply = msg.(*oty.Storage)
assert.Equal(t, common.Sha256(contents[0]), reply.GetHashStorage().Hash)
//根据hash批量查询存储数据
msg, err = exec.Query(oty.FuncNameBatchQueryStorage, types.Encode(&oty.BatchQueryStorage{
TxHashs: []string{txhash}}))
if err != nil {
t.Error(err)
}
t.Log(msg)
reply2 = msg.(*oty.BatchReplyStorage)
assert.Equal(t, common.Sha256(contents[0]), reply2.Storages[0].GetHashStorage().Hash)
//存储链接地址
tx, err = ety.Create("LinkStorage", &oty.LinkNotaryStorage{Hash: common.Sha256(contents[0]), Link: contents[0]})
assert.Nil(t, err)
tx, err = types.FormatTx(cfg, oty.StorageX, tx)
assert.Nil(t, err)
tx, err = signTx(tx, PrivKeyA)
assert.Nil(t, err)
t.Log("tx", tx)
exec.SetEnv(env.blockHeight+1, env.blockTime+20, env.difficulty+1)
receipt, err = exec.Exec(tx, int(1))
if err != nil {
t.Error(err)
}
for _, kv := range receipt.KV {
stateDB.Set(kv.Key, kv.Value)
}
receiptDate = &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err = exec.ExecLocal(tx, receiptDate, int(1))
if err != nil {
t.Error(err)
}
for _, kv := range set.KV {
kvdb.Set(kv.Key, kv.Value)
}
txhash = common.ToHex(tx.Hash())
t.Log("txhash:", txhash)
//根据hash查询存储得明文内容
msg, err = exec.Query(oty.FuncNameQueryStorage, types.Encode(&oty.QueryStorage{
TxHash: txhash}))
if err != nil {
t.Error(err)
}
t.Log(msg)
reply = msg.(*oty.Storage)
assert.Equal(t, common.Sha256(contents[0]), reply.GetLinkStorage().Hash)
//根据hash批量查询存储数据
msg, err = exec.Query(oty.FuncNameBatchQueryStorage, types.Encode(&oty.BatchQueryStorage{
TxHashs: []string{txhash}}))
if err != nil {
t.Error(err)
}
t.Log(msg)
reply2 = msg.(*oty.BatchReplyStorage)
assert.Equal(t, common.Sha256(contents[0]), reply2.Storages[0].GetLinkStorage().Hash)
//加密存储
aes := des.NewAES(keys[2], ivs[0])
crypted, err := aes.Encrypt(contents[0])
if err != nil {
t.Error(err)
}
tx, err = ety.Create("EncryptStorage", &oty.EncryptNotaryStorage{ContentHash: common.Sha256(contents[0]), EncryptContent: crypted, Nonce: ivs[0]})
assert.Nil(t, err)
tx, err = types.FormatTx(cfg, oty.StorageX, tx)
assert.Nil(t, err)
tx, err = signTx(tx, PrivKeyA)
assert.Nil(t, err)
t.Log("tx", tx)
exec.SetEnv(env.blockHeight+1, env.blockTime+20, env.difficulty+1)
receipt, err = exec.Exec(tx, int(1))
if err != nil {
t.Error(err)
}
for _, kv := range receipt.KV {
stateDB.Set(kv.Key, kv.Value)
}
receiptDate = &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err = exec.ExecLocal(tx, receiptDate, int(1))
if err != nil {
t.Error(err)
}
for _, kv := range set.KV {
kvdb.Set(kv.Key, kv.Value)
}
txhash = common.ToHex(tx.Hash())
t.Log("txhash:", txhash)
//根据hash查询存储得明文内容
msg, err = exec.Query(oty.FuncNameQueryStorage, types.Encode(&oty.QueryStorage{
TxHash: txhash}))
if err != nil {
t.Error(err)
}
t.Log(msg)
reply = msg.(*oty.Storage)
assert.Equal(t, common.Sha256(contents[0]), reply.GetEncryptStorage().ContentHash)
assert.Equal(t, crypted, reply.GetEncryptStorage().EncryptContent)
assert.Equal(t, ivs[0], reply.GetEncryptStorage().Nonce)
}
func signTx(tx *types.Transaction, hexPrivKey string) (*types.Transaction, error) {
signType := types.SECP256K1
c, err := crypto.New(types.GetSignName(oty.StorageX, signType))
if err != nil {
return tx, err
}
bytes, err := common.FromHex(hexPrivKey[:])
if err != nil {
return tx, err
}
privKey, err := c.PrivKeyFromBytes(bytes)
if err != nil {
return tx, err
}
tx.Sign(int32(signType), privKey)
return tx, nil
}
package executor
import (
"fmt"
"github.com/33cn/chain33/common"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
storagetypes "github.com/33cn/plugin/plugin/dapp/storage/types"
"github.com/gogo/protobuf/proto"
)
type StorageAction struct {
db dbm.KV
txhash []byte
fromaddr string
blocktime int64
height int64
index int
}
func newStorageAction(s *storage, tx *types.Transaction, index int) *StorageAction {
hash := tx.Hash()
fromaddr := tx.From()
return &StorageAction{s.GetStateDB(), hash, fromaddr,
s.GetBlockTime(), s.GetHeight(), index}
}
func (s *StorageAction) GetKVSet(payload proto.Message) (kvset []*types.KeyValue) {
kvset = append(kvset, &types.KeyValue{Key: Key(common.ToHex(s.txhash)), Value: types.Encode(payload)})
return kvset
}
func (s *StorageAction) ContentStorage(payload proto.Message) (*types.Receipt, error) {
//TODO 这里可以加具体得文本内容限制,超过指定大小的数据不容许写到状态数据库中
var logs []*types.ReceiptLog
log := &types.ReceiptLog{Ty: storagetypes.TyContentStorageLog}
logs = append(logs, log)
kv := s.GetKVSet(payload)
receipt := &types.Receipt{Ty: types.ExecOk, KV: kv, Logs: logs}
return receipt, nil
}
func (s *StorageAction) HashStorage(payload proto.Message) (*types.Receipt, error) {
var logs []*types.ReceiptLog
log := &types.ReceiptLog{Ty: storagetypes.TyHashStorageLog}
logs = append(logs, log)
kv := s.GetKVSet(payload)
receipt := &types.Receipt{Ty: types.ExecOk, KV: kv, Logs: logs}
return receipt, nil
}
func (s *StorageAction) LinkStorage(payload proto.Message) (*types.Receipt, error) {
var logs []*types.ReceiptLog
log := &types.ReceiptLog{Ty: storagetypes.TyLinkStorageLog}
logs = append(logs, log)
kv := s.GetKVSet(payload)
receipt := &types.Receipt{Ty: types.ExecOk, KV: kv, Logs: logs}
return receipt, nil
}
func (s *StorageAction) EncryptStorage(payload proto.Message) (*types.Receipt, error) {
var logs []*types.ReceiptLog
log := &types.ReceiptLog{Ty: storagetypes.TyEncryptStorageLog}
logs = append(logs, log)
kv := s.GetKVSet(payload)
receipt := &types.Receipt{Ty: types.ExecOk, KV: kv, Logs: logs}
return receipt, nil
}
func (s *StorageAction) EncryptShareStorage(payload proto.Message) (*types.Receipt, error) {
var logs []*types.ReceiptLog
log := &types.ReceiptLog{Ty: storagetypes.TyEncryptShareStorageLog}
logs = append(logs, log)
kv := s.GetKVSet(payload)
receipt := &types.Receipt{Ty: types.ExecOk, KV: kv, Logs: logs}
return receipt, nil
}
func QueryStorageByTxHash(db dbm.KV, txhash string) (*storagetypes.Storage, error) {
data, err := db.Get(Key(txhash))
if err != nil {
elog.Debug("QueryStorage", "get", err)
return nil, err
}
var storage storagetypes.Storage
//decode
err = types.Decode(data, &storage)
if err != nil {
elog.Debug("QueryStorage", "decode", err)
return nil, err
}
return &storage, nil
}
func QueryStorage(db dbm.KV, in *storagetypes.QueryStorage) (types.Message, error) {
if in.TxHash == "" {
return nil, fmt.Errorf("txhash can't equail nil")
}
return QueryStorageByTxHash(db, in.TxHash)
}
func BatchQueryStorage(db dbm.KV, in *storagetypes.BatchQueryStorage) (types.Message, error) {
if len(in.TxHashs) > 10 {
return nil, fmt.Errorf("The number of batch queries is too large! the maximux is %d,but current num %d", 10, len(in.TxHashs))
}
var storage storagetypes.BatchReplyStorage
for _, txhash := range in.TxHashs {
msg, err := QueryStorageByTxHash(db, txhash)
if err != nil {
return msg, err
}
storage.Storages = append(storage.Storages, msg)
}
return &storage, nil
}
package types
import (
"github.com/33cn/chain33/pluginmgr"
"github.com/33cn/plugin/plugin/dapp/storage/commands"
"github.com/33cn/plugin/plugin/dapp/storage/executor"
"github.com/33cn/plugin/plugin/dapp/storage/rpc"
storagetypes "github.com/33cn/plugin/plugin/dapp/storage/types"
)
/*
* 初始化dapp相关的组件
*/
func init() {
pluginmgr.Register(&pluginmgr.PluginBase{
Name: storagetypes.StorageX,
ExecName: executor.GetName(),
Exec: executor.Init,
Cmd: commands.Cmd,
RPC: rpc.Init,
})
}
all:
./create_protobuf.sh
#!/bin/sh
# proto生成命令,将pb.go文件生成到types/目录下, chain33_path支持引用chain33框架的proto文件
chain33_path=$(go list -f '{{.Dir}}' "github.com/33cn/chain33")
protoc --go_out=plugins=grpc:../types ./*.proto --proto_path=. --proto_path="${chain33_path}/types/proto/"
syntax = "proto3";
package types;
//后面如果有其他数据模型可继续往上面添加
message Storage {
oneof value {
ContentOnlyNotaryStorage contentStorage = 1;
HashOnlyNotaryStorage hashStorage = 2;
LinkNotaryStorage linkStorage = 3;
EncryptNotaryStorage encryptStorage = 4;
EncryptShareNotaryStorage encryptShareStorage = 5;
}
}
message StorageAction {
oneof value {
ContentOnlyNotaryStorage contentStorage = 1;
HashOnlyNotaryStorage hashStorage = 2;
LinkNotaryStorage linkStorage = 3;
EncryptNotaryStorage encryptStorage = 4;
EncryptShareNotaryStorage encryptShareStorage = 5;
}
int32 ty = 6;
}
// 内容存证模型
message ContentOnlyNotaryStorage {
//长度需要小于512k
bytes content = 1;
}
//哈希存证模型,推荐使用sha256哈希,限制256位得摘要值
message HashOnlyNotaryStorage {
//长度固定为32字节
bytes hash = 1;
}
// 链接存证模型
message LinkNotaryStorage {
//存证内容的链接,可以写入URL,或者其他可用于定位源文件得线索.
bytes link = 1;
//源文件得hash值,推荐使用sha256哈希,限制256位得摘要值
bytes hash = 2;
}
// 隐私存证模型,如果一个文件需要存证,且不公开内容,可以选择将源文件通过对称加密算法加密后上链
message EncryptNotaryStorage {
//存证明文内容的hash值,推荐使用sha256哈希,限制256位得摘要值
bytes contentHash = 1;
//源文件得密文,由加密key及nonce对明文加密得到该值。
bytes encryptContent = 2;
//加密iv,通过AES进行加密时制定随机生成的iv,解密时需要使用该值
bytes nonce = 3;
}
// 隐私存证模型
message EncryptContentOnlyNotaryStorage {
//存证内容的hash值,推荐使用sha256哈希,限制256位得摘要值
// bytes contentHash = 1;
//源文件得密文。
bytes encryptContent = 1;
//加密iv,通过AES进行加密时制定随机生成的iv,解密时需要使用该值
bytes nonce = 2;
}
// 分享隐私存证模型,需要完备的sdk或者相应的密钥库支持
message EncryptShareNotaryStorage {
//存证明文内容的hash值,推荐使用sha256哈希,限制256位得摘要值
bytes contentHash = 1;
//源文件得密文。
bytes encryptContent = 2;
//密钥的kdf推导路径。密钥tree父节点根据该路径可以推导出私钥key
bytes keyName = 3;
//加密key的wrap key。加密key随机生成,对明文进行加密,该key有私密key进行key wrap后公开。
//使用时,通过私密key对wrap key解密得到加密key对密文进行解密。
bytes keyWrap = 4;
//加密iv,通过AES进行加密时制定随机生成的iv,解密时需要使用该值
bytes nonce = 5;
}
service storage {
}
//根据txhash去状态数据库中查询存储内容
message QueryStorage {
string txHash = 1;
}
//批量查询有可能导致数据库崩溃
message BatchQueryStorage {
repeated string txHashs = 1;
}
message BatchReplyStorage {
repeated Storage storages = 1;
}
\ No newline at end of file
package rpc
/*
* 实现json rpc和grpc service接口
* json rpc用Jrpc结构作为接收实例
* grpc使用channelClient结构作为接收实例
*/
package rpc
import (
rpctypes "github.com/33cn/chain33/rpc/types"
storagetypes "github.com/33cn/plugin/plugin/dapp/storage/types"
)
/*
* rpc相关结构定义和初始化
*/
// 实现grpc的service接口
type channelClient struct {
rpctypes.ChannelClient
}
// Jrpc 实现json rpc调用实例
type Jrpc struct {
cli *channelClient
}
// Grpc grpc
type Grpc struct {
*channelClient
}
// Init init rpc
func Init(name string, s rpctypes.RPCServer) {
cli := &channelClient{}
grpc := &Grpc{channelClient: cli}
cli.Init(name, s, &Jrpc{cli: cli}, grpc)
//存在grpc service时注册grpc server,需要生成对应的pb.go文件
storagetypes.RegisterStorageServer(s.GRPC(), grpc)
}
package types
import (
"github.com/33cn/chain33/types"
)
/*
* 交易相关类型定义
* 交易action通常有对应的log结构,用于交易回执日志记录
* 每一种action和log需要用id数值和name名称加以区分
*/
// action类型id和name,这些常量可以自定义修改
const (
TyUnknowAction = iota
TyContentStorageAction
TyHashStorageAction
TyLinkStorageAction
TyEncryptStorageAction
TyEncryptShareStorageAction
NameContentStorageAction = "ContentStorage"
NameHashStorageAction = "HashStorage"
NameLinkStorageAction = "LinkStorage"
NameEncryptStorageAction = "EncryptStorage"
NameEncryptShareStorageAction = "EncryptShareStorage"
FuncNameQueryStorage = "QueryStorage"
FuncNameBatchQueryStorage = "BatchQueryStorage"
)
// log类型id值
const (
TyUnknownLog = iota
TyContentStorageLog
TyHashStorageLog
TyLinkStorageLog
TyEncryptStorageLog
TyEncryptShareStorageLog
)
var (
//StorageX 执行器名称定义
StorageX = "storage"
//定义actionMap
actionMap = map[string]int32{
NameContentStorageAction: TyContentStorageAction,
NameHashStorageAction: TyHashStorageAction,
NameLinkStorageAction: TyLinkStorageAction,
NameEncryptStorageAction: TyEncryptStorageAction,
NameEncryptShareStorageAction: TyEncryptShareStorageAction,
}
//定义log的id和具体log类型及名称,填入具体自定义log类型
logMap = map[int64]*types.LogInfo{
//LogID: {Ty: reflect.TypeOf(LogStruct), Name: LogName},
}
//tlog = log.New("module", "storage.types")
)
// init defines a register function
func init() {
types.AllowUserExec = append(types.AllowUserExec, []byte(StorageX))
//注册合约启用高度
types.RegFork(StorageX, InitFork)
types.RegExec(StorageX, InitExecutor)
}
// InitFork defines register fork
func InitFork(cfg *types.Chain33Config) {
cfg.RegisterDappFork(StorageX, "Enable", 0)
}
// InitExecutor defines register executor
func InitExecutor(cfg *types.Chain33Config) {
types.RegistorExecutor(StorageX, NewType(cfg))
}
type StorageType struct {
types.ExecTypeBase
}
func NewType(cfg *types.Chain33Config) *StorageType {
c := &StorageType{}
c.SetChild(c)
c.SetConfig(cfg)
return c
}
// GetPayload 获取合约action结构
func (s *StorageType) GetPayload() types.Message {
return &StorageAction{}
}
// GeTypeMap 获取合约action的id和name信息
func (s *StorageType) GetTypeMap() map[string]int32 {
return actionMap
}
// GetLogMap 获取合约log相关信息
func (s *StorageType) GetLogMap() map[int64]*types.LogInfo {
return logMap
}
This diff is collapsed.
...@@ -29,6 +29,7 @@ func TicketCmd() *cobra.Command { ...@@ -29,6 +29,7 @@ func TicketCmd() *cobra.Command {
CountTicketCmd(), CountTicketCmd(),
CloseTicketCmd(), CloseTicketCmd(),
GetColdAddrByMinerCmd(), GetColdAddrByMinerCmd(),
listTicketCmd(),
) )
return cmd return cmd
...@@ -97,6 +98,41 @@ func countTicket(cmd *cobra.Command, args []string) { ...@@ -97,6 +98,41 @@ func countTicket(cmd *cobra.Command, args []string) {
ctx.Run() ctx.Run()
} }
// listTicketCmd get ticket count
func listTicketCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "Get ticket id list",
Run: listTicket,
}
cmd.Flags().StringP("miner_acct", "m", "", "miner address (optional)")
cmd.Flags().Int32P("status", "s", 1, "ticket status (default 1:opened tickets)")
return cmd
}
func listTicket(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
minerAddr, _ := cmd.Flags().GetString("miner_acct")
status, _ := cmd.Flags().GetInt32("status")
if minerAddr != "" {
var params rpctypes.Query4Jrpc
params.Execer = ty.TicketX
params.FuncName = "TicketList"
req := ty.TicketList{Addr: minerAddr, Status: status}
params.Payload = types.MustPBToJSON(&req)
var res ty.ReplyTicketList
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.Query", params, &res)
ctx.Run()
return
}
var res []ty.Ticket
ctx := jsonclient.NewRPCCtx(rpcLaddr, "ticket.GetTicketList", nil, &res)
ctx.Run()
}
// CloseTicketCmd close all accessible tickets // CloseTicketCmd close all accessible tickets
func CloseTicketCmd() *cobra.Command { func CloseTicketCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
......
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package executor_test
import (
"testing"
apimock "github.com/33cn/chain33/client/mocks"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
executor "github.com/33cn/plugin/plugin/dapp/ticket/executor"
pty "github.com/33cn/plugin/plugin/dapp/ticket/types"
)
type execEnv struct {
blockTime int64 // 1539918074
blockHeight int64
index int
difficulty uint64
txHash string
}
var (
Symbol = "TEST"
SymbolA = "TESTA"
AssetExecToken = "token"
AssetExecPara = "paracross"
PrivKeyA = "0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b" // 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4
PrivKeyB = "0x19c069234f9d3e61135fefbeb7791b149cdf6af536f26bebb310d4cd22c3fee4" // 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR
PrivKeyC = "0x7a80a1f75d7360c6123c32a78ecf978c1ac55636f87892df38d8b85a9aeff115" // 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k
PrivKeyD = "0xcacb1f5d51700aea07fca2246ab43b0917d70405c65edea9b5063d72eb5c6b71" // 1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs
Nodes = [][]byte{
[]byte("1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4"),
[]byte("1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR"),
[]byte("1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k"),
[]byte("1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"),
}
//chain33TestCfg = types.NewChain33Config(strings.Replace(types.GetDefaultCfgstring(), "Title=\"local\"", "Title=\"chain33\"", 1))
)
func Test_Exec_Bind_Unbind(t *testing.T) {
chain33TestCfg := mock33.GetAPI().GetConfig()
env := execEnv{
1539918074,
10000,
2,
1539918074,
"hash",
}
_, ldb, kvdb := util.CreateTestDB()
api := new(apimock.QueueProtocolAPI)
api.On("GetConfig", mock.Anything).Return(chain33TestCfg, nil)
driver, err := dapp.LoadDriver("ticket", 1000)
assert.Nil(t, err)
driver.SetAPI(api)
driver.SetEnv(env.blockHeight, env.blockTime, env.difficulty)
driver.SetStateDB(kvdb)
driver.SetLocalDB(kvdb)
priv, err := FromPrivkey(PrivKeyA)
assert.Nil(t, err)
bindTx := createBindMiner(t, chain33TestCfg, string(Nodes[1]), string(Nodes[0]), priv)
receipt, err := driver.Exec(bindTx, env.index)
if err != nil {
assert.Nil(t, err, "exec failed")
return
}
assert.Equal(t, 1, len(receipt.KV))
assert.Equal(t, executor.BindKey(string(Nodes[0])), receipt.KV[0].Key)
var bindInfo pty.TicketBind
err = types.Decode(receipt.KV[0].Value, &bindInfo)
assert.Nil(t, err)
assert.Equal(t, string(Nodes[1]), bindInfo.MinerAddress)
assert.Equal(t, string(Nodes[0]), bindInfo.ReturnAddress)
unbindTx := createBindMiner(t, chain33TestCfg, "", string(Nodes[0]), priv)
receipt, err = driver.Exec(unbindTx, env.index)
if err != nil {
assert.Nil(t, err, "exec failed")
return
}
assert.Equal(t, 1, len(receipt.KV))
assert.Equal(t, executor.BindKey(string(Nodes[0])), receipt.KV[0].Key)
var bindInfo2 pty.TicketBind
err = types.Decode(receipt.KV[0].Value, &bindInfo2)
assert.Nil(t, err)
assert.Equal(t, "", bindInfo2.MinerAddress)
assert.Equal(t, string(Nodes[0]), bindInfo2.ReturnAddress)
ldb.Close()
}
func FromPrivkey(hexPrivKey string) (crypto.PrivKey, error) {
signType := types.SECP256K1
c, err := crypto.New(types.GetSignName("ticket", signType))
if err != nil {
return nil, err
}
bytes, err := common.FromHex(hexPrivKey[:])
if err != nil {
return nil, err
}
return c.PrivKeyFromBytes(bytes)
}
...@@ -3,6 +3,7 @@ package executor_test ...@@ -3,6 +3,7 @@ package executor_test
import ( import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"os"
"strings" "strings"
"testing" "testing"
...@@ -26,8 +27,9 @@ var mock33 *testnode.Chain33Mock ...@@ -26,8 +27,9 @@ var mock33 *testnode.Chain33Mock
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
mock33 = testnode.New("testdata/chain33.cfg.toml", nil) mock33 = testnode.New("testdata/chain33.cfg.toml", nil)
mock33.Listen() mock33.Listen()
m.Run() code := m.Run()
mock33.Close() mock33.Close()
os.Exit(code)
} }
func TestTicketPrice(t *testing.T) { func TestTicketPrice(t *testing.T) {
......
...@@ -51,7 +51,7 @@ func NewDB(cfg *types.Chain33Config, id, minerAddress, returnWallet string, bloc ...@@ -51,7 +51,7 @@ func NewDB(cfg *types.Chain33Config, id, minerAddress, returnWallet string, bloc
t.MinerAddress = minerAddress t.MinerAddress = minerAddress
t.ReturnAddress = returnWallet t.ReturnAddress = returnWallet
t.CreateTime = blocktime t.CreateTime = blocktime
t.Status = 1 t.Status = ty.TicketOpened
t.IsGenesis = isGenesis t.IsGenesis = isGenesis
t.prevstatus = 0 t.prevstatus = 0
//height == 0 的情况下,不去改变 genesis block //height == 0 的情况下,不去改变 genesis block
...@@ -73,11 +73,11 @@ func NewDB(cfg *types.Chain33Config, id, minerAddress, returnWallet string, bloc ...@@ -73,11 +73,11 @@ func NewDB(cfg *types.Chain33Config, id, minerAddress, returnWallet string, bloc
// GetReceiptLog get receipt // GetReceiptLog get receipt
func (t *DB) GetReceiptLog() *types.ReceiptLog { func (t *DB) GetReceiptLog() *types.ReceiptLog {
log := &types.ReceiptLog{} log := &types.ReceiptLog{}
if t.Status == 1 { if t.Status == ty.TicketOpened {
log.Ty = ty.TyLogNewTicket log.Ty = ty.TyLogNewTicket
} else if t.Status == 2 { } else if t.Status == ty.TicketMined {
log.Ty = ty.TyLogMinerTicket log.Ty = ty.TyLogMinerTicket
} else if t.Status == 3 { } else if t.Status == ty.TicketClosed {
log.Ty = ty.TyLogCloseTicket log.Ty = ty.TyLogCloseTicket
} }
r := &ty.ReceiptTicket{} r := &ty.ReceiptTicket{}
...@@ -305,7 +305,7 @@ func (action *Action) TicketMiner(miner *ty.TicketMiner, index int) (*types.Rece ...@@ -305,7 +305,7 @@ func (action *Action) TicketMiner(miner *ty.TicketMiner, index int) (*types.Rece
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ticket.Status != 1 { if ticket.Status != ty.TicketOpened {
return nil, types.ErrCoinBaseTicketStatus return nil, types.ErrCoinBaseTicketStatus
} }
cfg := ty.GetTicketMinerParam(chain33Cfg, action.height) cfg := ty.GetTicketMinerParam(chain33Cfg, action.height)
...@@ -330,7 +330,7 @@ func (action *Action) TicketMiner(miner *ty.TicketMiner, index int) (*types.Rece ...@@ -330,7 +330,7 @@ func (action *Action) TicketMiner(miner *ty.TicketMiner, index int) (*types.Rece
} }
} }
prevstatus := ticket.Status prevstatus := ticket.Status
ticket.Status = 2 ticket.Status = ty.TicketMined
ticket.MinerValue = miner.Reward ticket.MinerValue = miner.Reward
if chain33Cfg.IsFork(action.height, "ForkMinerTime") { if chain33Cfg.IsFork(action.height, "ForkMinerTime") {
ticket.MinerTime = action.blocktime ticket.MinerTime = action.blocktime
...@@ -383,20 +383,20 @@ func (action *Action) TicketClose(tclose *ty.TicketClose) (*types.Receipt, error ...@@ -383,20 +383,20 @@ func (action *Action) TicketClose(tclose *ty.TicketClose) (*types.Receipt, error
return nil, err return nil, err
} }
//ticket 的生成时间超过 2天,可提款 //ticket 的生成时间超过 2天,可提款
if ticket.Status != 2 && ticket.Status != 1 { if ticket.Status != ty.TicketMined && ticket.Status != ty.TicketOpened {
tlog.Error("ticket", "id", ticket.GetTicketId(), "status", ticket.GetStatus()) tlog.Error("ticket", "id", ticket.GetTicketId(), "status", ticket.GetStatus())
return nil, ty.ErrTicketClosed return nil, ty.ErrTicketClosed
} }
if !ticket.IsGenesis { if !ticket.IsGenesis {
//分成两种情况 //分成两种情况
if ticket.Status == 1 && action.blocktime-ticket.GetCreateTime() < cfg.TicketWithdrawTime { if ticket.Status == ty.TicketOpened && action.blocktime-ticket.GetCreateTime() < cfg.TicketWithdrawTime {
return nil, ty.ErrTime return nil, ty.ErrTime
} }
//已经挖矿成功了 //已经挖矿成功了
if ticket.Status == 2 && action.blocktime-ticket.GetCreateTime() < cfg.TicketWithdrawTime { if ticket.Status == ty.TicketMined && action.blocktime-ticket.GetCreateTime() < cfg.TicketWithdrawTime {
return nil, ty.ErrTime return nil, ty.ErrTime
} }
if ticket.Status == 2 && action.blocktime-ticket.GetMinerTime() < cfg.TicketMinerWaitTime { if ticket.Status == ty.TicketMined && action.blocktime-ticket.GetMinerTime() < cfg.TicketMinerWaitTime {
return nil, ty.ErrTime return nil, ty.ErrTime
} }
} }
...@@ -405,7 +405,7 @@ func (action *Action) TicketClose(tclose *ty.TicketClose) (*types.Receipt, error ...@@ -405,7 +405,7 @@ func (action *Action) TicketClose(tclose *ty.TicketClose) (*types.Receipt, error
return nil, types.ErrFromAddr return nil, types.ErrFromAddr
} }
prevstatus := ticket.Status prevstatus := ticket.Status
ticket.Status = 3 ticket.Status = ty.TicketClosed
tickets[i] = &DB{*ticket, prevstatus} tickets[i] = &DB{*ticket, prevstatus}
} }
var logs []*types.ReceiptLog var logs []*types.ReceiptLog
......
...@@ -28,11 +28,13 @@ func bindMiner(cfg *types.Chain33Config, param *ty.ReqBindMiner) (*ty.ReplyBindM ...@@ -28,11 +28,13 @@ func bindMiner(cfg *types.Chain33Config, param *ty.ReqBindMiner) (*ty.ReplyBindM
// CreateBindMiner 创建绑定挖矿 // CreateBindMiner 创建绑定挖矿
func (g *channelClient) CreateBindMiner(ctx context.Context, in *ty.ReqBindMiner) (*ty.ReplyBindMiner, error) { func (g *channelClient) CreateBindMiner(ctx context.Context, in *ty.ReqBindMiner) (*ty.ReplyBindMiner, error) {
if in.BindAddr != "" {
err := address.CheckAddress(in.BindAddr) err := address.CheckAddress(in.BindAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = address.CheckAddress(in.OriginAddr) }
err := address.CheckAddress(in.OriginAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -141,3 +143,25 @@ func (c *Jrpc) SetAutoMining(in *ty.MinerFlag, result *rpctypes.Reply) error { ...@@ -141,3 +143,25 @@ func (c *Jrpc) SetAutoMining(in *ty.MinerFlag, result *rpctypes.Reply) error {
*result = reply *result = reply
return nil return nil
} }
// GetTicketList get ticket list info
func (g *channelClient) GetTicketList(ctx context.Context, in *types.ReqNil) ([]*ty.Ticket, error) {
inn := *in
data, err := g.ExecWalletFunc(ty.TicketX, "WalletGetTickets", &inn)
if err != nil {
return nil, err
}
return data.(*ty.ReplyWalletTickets).Tickets, nil
}
// GetTicketList get ticket list info
func (c *Jrpc) GetTicketList(in *types.ReqNil, result *interface{}) error {
resp, err := c.cli.GetTicketList(context.Background(), &types.ReqNil{})
if err != nil {
return err
}
*result = resp
return nil
}
...@@ -58,6 +58,15 @@ func TestChannelClient_BindMiner(t *testing.T) { ...@@ -58,6 +58,15 @@ func TestChannelClient_BindMiner(t *testing.T) {
} }
_, err := client.CreateBindMiner(context.Background(), in) _, err := client.CreateBindMiner(context.Background(), in)
assert.Nil(t, err) assert.Nil(t, err)
var in2 = &ty.ReqBindMiner{
BindAddr: "",
OriginAddr: "1Jn2qu84Z1SUUosWjySggBS9pKWdAP3tZt",
Amount: 10000 * types.Coin,
CheckBalance: false,
}
_, err = client.CreateBindMiner(context.Background(), in2)
assert.Nil(t, err)
} }
func testGetTicketCountOK(t *testing.T) { func testGetTicketCountOK(t *testing.T) {
......
...@@ -13,6 +13,18 @@ import ( ...@@ -13,6 +13,18 @@ import (
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
) )
// 0 -> 未成熟 1 -> 可挖矿 2 -> 已挖成功 3-> 已关闭
const (
//TicketInit ticket init status
TicketInit = iota
//TicketOpened ticket opened status
TicketOpened
//TicketMined ticket mined status
TicketMined
//TicketClosed ticket closed status
TicketClosed
)
const ( const (
//log for ticket //log for ticket
......
...@@ -406,13 +406,13 @@ func (policy *ticketPolicy) forceCloseTicketList(height int64, priv crypto.PrivK ...@@ -406,13 +406,13 @@ func (policy *ticketPolicy) forceCloseTicketList(height int64, priv crypto.PrivK
cfg := ty.GetTicketMinerParam(chain33Cfg, height) cfg := ty.GetTicketMinerParam(chain33Cfg, height)
for _, t := range tlist { for _, t := range tlist {
if !t.IsGenesis { if !t.IsGenesis {
if t.Status == 1 && now-t.GetCreateTime() < cfg.TicketWithdrawTime { if t.Status == ty.TicketOpened && now-t.GetCreateTime() < cfg.TicketWithdrawTime {
continue continue
} }
if t.Status == 2 && now-t.GetCreateTime() < cfg.TicketWithdrawTime { if t.Status == ty.TicketMined && now-t.GetCreateTime() < cfg.TicketWithdrawTime {
continue continue
} }
if t.Status == 2 && now-t.GetMinerTime() < cfg.TicketMinerWaitTime { if t.Status == ty.TicketMined && now-t.GetMinerTime() < cfg.TicketMinerWaitTime {
continue continue
} }
} }
......
...@@ -40,10 +40,16 @@ func TestForceCloseTicketList(t *testing.T) { ...@@ -40,10 +40,16 @@ func TestForceCloseTicketList(t *testing.T) {
wallet.api = qapi wallet.api = qapi
ticket.walletOperate = wallet ticket.walletOperate = wallet
t1 := &ty.Ticket{Status: 1, IsGenesis: false} t1 := &ty.Ticket{Status: ty.TicketOpened, IsGenesis: false}
t2 := &ty.Ticket{Status: 2, IsGenesis: false} t2 := &ty.Ticket{Status: ty.TicketMined, IsGenesis: false}
t3 := &ty.Ticket{Status: 3, IsGenesis: false} t3 := &ty.Ticket{Status: ty.TicketClosed, IsGenesis: false}
tlist := []*ty.Ticket{t1, t2, t3}
now := types.Now().Unix()
t4 := &ty.Ticket{Status: ty.TicketOpened, IsGenesis: false, CreateTime: now}
t5 := &ty.Ticket{Status: ty.TicketMined, IsGenesis: false, CreateTime: now}
t6 := &ty.Ticket{Status: ty.TicketMined, IsGenesis: false, MinerTime: now}
tlist := []*ty.Ticket{t1, t2, t3, t4, t5, t6}
r1, r2 := ticket.forceCloseTicketList(0, nil, tlist) r1, r2 := ticket.forceCloseTicketList(0, nil, tlist)
assert.Equal(t, []byte(sendhash), r1) assert.Equal(t, []byte(sendhash), r1)
...@@ -69,9 +75,9 @@ func TestCloseTicketsByAddr(t *testing.T) { ...@@ -69,9 +75,9 @@ func TestCloseTicketsByAddr(t *testing.T) {
wallet.api = qapi wallet.api = qapi
ticket.walletOperate = wallet ticket.walletOperate = wallet
t1 := &ty.Ticket{Status: 1, IsGenesis: false} t1 := &ty.Ticket{Status: ty.TicketOpened, IsGenesis: false}
t2 := &ty.Ticket{Status: 2, IsGenesis: false} t2 := &ty.Ticket{Status: ty.TicketMined, IsGenesis: false}
t3 := &ty.Ticket{Status: 3, IsGenesis: false} t3 := &ty.Ticket{Status: ty.TicketClosed, IsGenesis: false}
tlist := &ty.ReplyTicketList{Tickets: []*ty.Ticket{t1, t2, t3}} tlist := &ty.ReplyTicketList{Tickets: []*ty.Ticket{t1, t2, t3}}
qapi.On("Query", ty.TicketX, "TicketList", mock.Anything).Return(tlist, nil) qapi.On("Query", ty.TicketX, "TicketList", mock.Anything).Return(tlist, nil)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment