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,10 +806,12 @@ func getCrossTxHashsByRst(api client.QueueProtocolAPI, status *pt.ParacrossNodeS ...@@ -781,10 +806,12 @@ 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)
...@@ -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,14 @@ func (a *action) execCrossTxs(status *pt.ParacrossNodeStatus) (*types.Receipt, e ...@@ -866,28 +912,14 @@ 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]))
return nil, err
}
if tx == nil {
clog.Error("paracross.Commit Load Tx failed", "para title", title, "para height", status.Height,
"para tx index", i, "error", err, "txHash", hex.EncodeToString(crossTxHashs[i]))
return nil, types.ErrHashNotExist
}
receiptCross, err := a.execCrossTx(tx, crossTxHashs[i])
if err != nil {
clog.Error("paracross.Commit execCrossTx", "para title", 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)
} }
...@@ -898,8 +930,21 @@ func (a *action) execCrossTxs(status *pt.ParacrossNodeStatus) (*types.Receipt, e ...@@ -898,8 +930,21 @@ func (a *action) execCrossTxs(status *pt.ParacrossNodeStatus) (*types.Receipt, e
receipt.Logs = append(receipt.Logs, receiptCross.Logs...) receipt.Logs = append(receipt.Logs, receiptCross.Logs...)
} else { } else {
clog.Error("paracross.Commit commitDone", "do cross number", i, "hash", clog.Error("paracross.Commit commitDone", "do cross number", i, "hash",
hex.EncodeToString(crossTxHashs[i]), hex.EncodeToString(crossTxHashs[i]), "para res", util.BitMapBit(crossTxResult, uint32(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 {
clog.Error("paracross.Commit rollbackCrossTx", "para title", status.Title, "para height", status.Height,
"para tx index", i, "error", err)
return nil, errors.Cause(err)
}
if receiptCross == nil {
continue
}
receipt.KV = append(receipt.KV, receiptCross.KV...)
receipt.Logs = append(receipt.Logs, receiptCross.Logs...)
}
} }
} }
......
...@@ -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) {
err := address.CheckAddress(in.BindAddr) if in.BindAddr != "" {
if err != nil { err := address.CheckAddress(in.BindAddr)
return nil, err if err != nil {
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