Unverified Commit 748d60d9 authored by vipwzw's avatar vipwzw Committed by GitHub

Merge pull request #105 from linj-disanbo/exec_unfreeze

Exec unfreeze
parents 4914e587 74c4a650
...@@ -16,5 +16,6 @@ import ( ...@@ -16,5 +16,6 @@ import (
_ "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
_ "github.com/33cn/plugin/plugin/dapp/unfreeze" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/valnode" //auto gen _ "github.com/33cn/plugin/plugin/dapp/valnode" //auto gen
) )
all:
chmod +x ./build.sh
./build.sh $(OUT) $(FLAG)
\ No newline at end of file
#!/usr/bin/env bash
output_dir=${1}
strpwd=$(pwd)
strcmd=${strpwd##*dapp/}
strapp=${strcmd%/cmd*}
OUT_DIR="${output_dir}/$strapp"
[ ! -e "${OUT_DIR}" ] && mkdir -p "${OUT_DIR}"
# shellcheck disable=SC2086
cp ./build/* "${OUT_DIR}"
#!/usr/bin/env bash
CLI="docker exec ${NODE3} /root/chain33-cli"
beneficiary=12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv
beneficiary_key=0x4257d8692ef7fe13c68b65d6a52f03933db2fa5ce8faf210b5b8b80c721ced01
#owner=14KEKbYtKKQm4wMthSK9J4La4nAiidGozt
owner_key=CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944
unfreeze_exec_addr=15YsqAuXeEXVHgm6RVx4oJaAAnhtwqnu3H
function unfreeze_test() {
echo "=========== # unfreeze test ============="
echo "=== 1 check exec addr"
result=$($CLI exec addr -e unfreeze)
if [ "${result}" != "${unfreeze_exec_addr}" ]; then
echo "unfreeze exec addr is not right, expect ${unfreeze_exec_addr} result ${result}"
exit 1
fi
block_wait "${CLI}" 2
echo "=== 2 prepare: transfer bty to unfreeze "
result=$($CLI send coins transfer -a 5 -n test -t ${unfreeze_exec_addr} -k ${owner_key})
echo "${result}"
block_wait "${CLI}" 2
echo "=== 3 create unfreeze tx"
tx_hash=$(${CLI} send unfreeze create fix_amount -a 0.01 -e coins -s bty -b ${beneficiary} -p 20 -t 2 -k ${owner_key})
block_wait "${CLI}" 2
unfreeze_id=$(${CLI} tx query -s "${tx_hash}" | jq ".receipt.logs[2].log.current.unfreezeID")
unfreeze_id2=${unfreeze_id#\"}
uid=${unfreeze_id2%\"}
echo "==== 4 check some message "
sleep 20
withdraw=$(${CLI} unfreeze show_withdraw --id "${uid}" | jq ".availableAmount")
if [ "${withdraw}" = "0" ]; then
echo "create unfreeze failed, expect withdraw shoult >0 "
exit 1
fi
echo "==== 5 withdraw"
${CLI} send unfreeze withdraw --id "${uid}" -k "${beneficiary_key}"
block_wait "${CLI}" 2
remaining=$(${CLI} unfreeze show --id "${uid}" | jq ".remaining")
if [ "${remaining}" = '"200000000"' ]; then
echo "withdraw failed, expect remaining < 200000000, result ${remaining}"
exit 1
fi
echo "==== 6 termenate"
${CLI} send unfreeze terminate --id "${uid}" -k "${owner_key}"
block_wait "${CLI}" 2
remaining=$(${CLI} unfreeze show --id "${uid}" | jq ".remaining")
if [ "${remaining}" != '"0"' ]; then
echo "terminate failed, expect remaining 0, result ${remaining}"
exit 1
fi
echo "==================== unfreeze test end"
}
function unfreeze() {
if [ "${2}" == "init" ]; then
return
elif [ "${2}" == "config" ]; then
return
elif [ "${2}" == "test" ]; then
unfreeze_test "${1}"
fi
}
// 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 commands
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"math"
"os"
"strings"
"github.com/33cn/chain33/rpc/jsonclient"
rpctypes "github.com/33cn/chain33/rpc/types"
"github.com/spf13/cobra"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/unfreeze/types"
)
// Cmd unfreeze 客户端主程序
func Cmd() *cobra.Command {
cmd := &cobra.Command{
Use: "unfreeze",
Short: "Unfreeze construct management",
Args: cobra.MinimumNArgs(1),
}
cmd.AddCommand(createCmd())
cmd.AddCommand(withdrawCmd())
cmd.AddCommand(terminateCmd())
cmd.AddCommand(showCmd())
cmd.AddCommand(queryWithdrawCmd())
return cmd
}
func createCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: "create unfreeze construct",
}
cmd.AddCommand(fixAmountCmd())
cmd.AddCommand(leftCmd())
return cmd
}
func createFlag(cmd *cobra.Command) *cobra.Command {
cmd.PersistentFlags().StringP("beneficiary", "b", "", "address of beneficiary")
cmd.MarkFlagRequired("beneficiary")
cmd.PersistentFlags().StringP("asset_exec", "e", "", "asset exec")
cmd.MarkFlagRequired("asset_exec")
cmd.PersistentFlags().StringP("asset_symbol", "s", "", "asset symbol")
cmd.MarkFlagRequired("asset_symbol")
cmd.PersistentFlags().Float64P("total", "t", 0, "total count of asset")
cmd.MarkFlagRequired("total")
cmd.PersistentFlags().Int64P("start_ts", "", 0, "effect, UTC timestamp")
//cmd.MarkFlagRequired("start_ts")
return cmd
}
func checkAmount(amount float64) error {
if amount < 0 || amount > float64(types.MaxCoin/types.Coin) {
return types.ErrAmount
}
return nil
}
func getCreateFlags(cmd *cobra.Command) (*pty.UnfreezeCreate, error) {
beneficiary, _ := cmd.Flags().GetString("beneficiary")
exec, _ := cmd.Flags().GetString("asset_exec")
symbol, _ := cmd.Flags().GetString("asset_symbol")
total, _ := cmd.Flags().GetFloat64("total")
startTs, _ := cmd.Flags().GetInt64("start_ts")
if err := checkAmount(total); err != nil {
return nil, types.ErrAmount
}
totalInt64 := int64(math.Trunc((total+0.0000001)*1e4)) * 1e4
unfreeze := &pty.UnfreezeCreate{
StartTime: startTs,
AssetExec: exec,
AssetSymbol: symbol,
TotalCount: totalInt64,
Beneficiary: beneficiary,
Means: "",
}
return unfreeze, nil
}
func fixAmountCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "fix_amount",
Short: "create fix amount means unfreeze construct",
Run: fixAmount,
}
cmd = createFlag(cmd)
cmd.Flags().Float64P("amount", "a", 0, "amount every period")
cmd.MarkFlagRequired("amount")
cmd.Flags().Int64P("period", "p", 0, "period in second")
cmd.MarkFlagRequired("period")
return cmd
}
func fixAmount(cmd *cobra.Command, args []string) {
create, err := getCreateFlags(cmd)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
amount, _ := cmd.Flags().GetFloat64("amount")
if err = checkAmount(amount); err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
amountInt64 := int64(math.Trunc((amount+0.0000001)*1e4)) * 1e4
period, _ := cmd.Flags().GetInt64("period")
create.Means = pty.FixAmountX
create.MeansOpt = &pty.UnfreezeCreate_FixAmount{FixAmount: &pty.FixAmount{Period: period, Amount: amountInt64}}
paraName, _ := cmd.Flags().GetString("paraName")
tx, err := pty.CreateUnfreezeCreateTx(paraName, create)
if err != nil {
fmt.Printf("Create Tx frailed: %s", err)
return
}
outputTx(tx)
}
func outputTx(tx *types.Transaction) {
txHex := types.Encode(tx)
fmt.Println(hex.EncodeToString(txHex))
}
func leftCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "left_proportion",
Short: "create left proportion means unfreeze construct",
Run: left,
}
cmd = createFlag(cmd)
cmd.Flags().Int64P("ten_thousandth", "", 0, "input/10000 of total")
cmd.MarkFlagRequired("amount")
cmd.Flags().Int64P("period", "p", 0, "period in second")
cmd.MarkFlagRequired("period")
return cmd
}
func left(cmd *cobra.Command, args []string) {
create, err := getCreateFlags(cmd)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
tenThousandth, _ := cmd.Flags().GetInt64("ten_thousandth")
period, _ := cmd.Flags().GetInt64("period")
create.Means = pty.FixAmountX
create.MeansOpt = &pty.UnfreezeCreate_LeftProportion{
LeftProportion: &pty.LeftProportion{Period: period, TenThousandth: tenThousandth}}
paraName, _ := cmd.Flags().GetString("paraName")
tx, err := pty.CreateUnfreezeCreateTx(paraName, create)
if err != nil {
fmt.Printf("Create Tx frailed: %s", err)
return
}
outputTx(tx)
}
func withdrawCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "withdraw",
Short: "withdraw asset from construct",
Run: withdraw,
}
cmd.Flags().StringP("id", "", "", "unfreeze construct id")
cmd.MarkFlagRequired("id")
return cmd
}
func terminateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "terminate",
Short: "terminate construct",
Run: terminate,
}
cmd.Flags().StringP("id", "", "", "unfreeze construct id")
cmd.MarkFlagRequired("id")
return cmd
}
func showCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "show",
Short: "show construct",
Run: show,
}
cmd.Flags().StringP("id", "", "", "unfreeze construct id")
cmd.MarkFlagRequired("id")
return cmd
}
func queryWithdrawCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "show_withdraw",
Short: "show available withdraw amount of one unfreeze construct",
Run: queryWithdraw,
}
cmd.Flags().StringP("id", "", "", "unfreeze construct id")
cmd.MarkFlagRequired("id")
return cmd
}
func withdraw(cmd *cobra.Command, args []string) {
id, _ := cmd.Flags().GetString("id")
paraName, _ := cmd.Flags().GetString("paraName")
tx, err := pty.CreateUnfreezeWithdrawTx(paraName, &pty.UnfreezeWithdraw{UnfreezeID: id})
if err != nil {
fmt.Printf("Create Tx frailed: %s", err)
return
}
outputTx(tx)
}
func terminate(cmd *cobra.Command, args []string) {
id, _ := cmd.Flags().GetString("id")
paraName, _ := cmd.Flags().GetString("paraName")
tx, err := pty.CreateUnfreezeTerminateTx(paraName, &pty.UnfreezeTerminate{UnfreezeID: id})
if err != nil {
fmt.Printf("Create Tx frailed: %s", err)
return
}
outputTx(tx)
}
func queryWithdraw(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
paraName, _ := cmd.Flags().GetString("paraName")
id, _ := cmd.Flags().GetString("id")
cli, err := jsonclient.NewJSONClient(rpcLaddr)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
param := &rpctypes.Query4Jrpc{
Execer: getRealExecName(paraName, pty.UnfreezeX),
FuncName: "GetUnfreezeWithdraw",
Payload: types.MustPBToJSON(&types.ReqString{Data: id}),
}
var resp pty.ReplyQueryUnfreezeWithdraw
err = cli.Call("Chain33.Query", param, &resp)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
jsonOutput(&resp)
}
func show(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
paraName, _ := cmd.Flags().GetString("paraName")
id, _ := cmd.Flags().GetString("id")
cli, err := jsonclient.NewJSONClient(rpcLaddr)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
param := &rpctypes.Query4Jrpc{
Execer: getRealExecName(paraName, pty.UnfreezeX),
FuncName: "GetUnfreeze",
Payload: types.MustPBToJSON(&types.ReqString{Data: id}),
}
var resp pty.Unfreeze
err = cli.Call("Chain33.Query", param, &resp)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
jsonOutput(&resp)
}
func getRealExecName(paraName string, name string) string {
if strings.HasPrefix(name, "user.p.") {
return name
}
return paraName + name
}
func jsonOutput(resp types.Message) {
data, err := types.PBToJSON(resp)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
var buf bytes.Buffer
err = json.Indent(&buf, data, "", " ")
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
fmt.Println(buf.String())
}
// 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 unfreeze 提供了定期解冻合约的实现
// 功能描述:定期解冻合约帮助用户锁定一定量的币, 按在指定的规制解冻给受益人,
// 适用于分期付款, 分期支付形式的员工激励等情景。
//
// 合约提供了3类操作
// 1. 创建定期解冻合约:创建时需要指定支付的资产和总量,以及定期解冻的形式。
// 1. 受益人提币:受益人提走解冻了的资产。
// 1. 发起人终止合约: 发起人可以终止合约的履行。
//
// 解冻的形式目前支持两种
// 1. 固定数额解冻:指定时间间隔,解冻固定的资产。
// 1. 按剩余量的固定比例解冻:指定时间间隔,按剩余量的固定比例解冻。 这种方式,越到后面解冻的越少。
// 说明:在合约创建时, 就可以解冻一次。
// 举例, 一个固定数额解冻和合约, 总量为100, 一个月解冻10. 创建时可以由受益人提走10, 第一个月后又可以提走10.
// 在受益人没有及时提币的情况下, 受益人在一段时间之后可以一次性提走本该解冻的所有的币。 即解冻的币是按指定
// 形式解冻的,和受益人的提币时间和次数等都不会影响解冻的进程。
package unfreeze
// 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
// 定期解冻token
// 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
import (
"github.com/33cn/chain33/account"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/unfreeze/types"
)
// Exec_Create 执行创建冻结合约
func (u *Unfreeze) Exec_Create(payload *pty.UnfreezeCreate, tx *types.Transaction, index int) (*types.Receipt, error) {
if payload.AssetExec == "" || payload.AssetSymbol == "" || payload.TotalCount <= 0 || payload.Means == "" {
return nil, types.ErrInvalidParam
}
unfreeze, err := u.newEntity(payload, tx)
if err != nil {
uflog.Error("unfreeze create entity", "addr", tx.From(), "payload", payload)
return nil, err
}
receipt1, err := u.create(unfreeze)
if err != nil {
uflog.Error("unfreeze create order", "addr", tx.From(), "unfreeze", unfreeze)
return nil, err
}
acc, err := account.NewAccountDB(payload.AssetExec, payload.AssetSymbol, u.GetStateDB())
if err != nil {
uflog.Error("unfreeze create new account", "addr", tx.From(), "execAddr",
dapp.ExecAddress(string(tx.Execer)), "exec", payload.AssetExec, "symbol", payload.AssetSymbol)
return nil, err
}
receipt, err := acc.ExecFrozen(unfreeze.Initiator, dapp.ExecAddress(string(tx.Execer)), payload.TotalCount)
if err != nil {
uflog.Error("unfreeze create exec frozen", "addr", tx.From(), "execAddr", dapp.ExecAddress(string(tx.Execer)),
"ExecFrozen amount", payload.TotalCount, "exec", payload.AssetExec, "symbol", payload.AssetSymbol)
return nil, err
}
return mergeReceipt(receipt, receipt1)
}
// Exec_Withdraw 执行冻结合约中提币
func (u *Unfreeze) Exec_Withdraw(payload *pty.UnfreezeWithdraw, tx *types.Transaction, index int) (*types.Receipt, error) {
unfreeze, err := loadUnfreeze(payload.UnfreezeID, u.GetStateDB())
if err != nil {
return nil, err
}
if unfreeze.Beneficiary != tx.From() {
uflog.Error("unfreeze withdraw no privilege", "beneficiary", unfreeze.Beneficiary, "txFrom", tx.From())
return nil, pty.ErrNoPrivilege
}
if unfreeze.Remaining <= 0 {
uflog.Error("unfreeze withdraw no asset")
return nil, pty.ErrUnfreezeEmptied
}
amount, receipt1, err := u.withdraw(unfreeze)
if err != nil {
uflog.Error("unfreeze withdraw withdraw", "err", err, "unfreeze", unfreeze)
return nil, err
}
acc, err := account.NewAccountDB(unfreeze.AssetExec, unfreeze.AssetSymbol, u.GetStateDB())
if err != nil {
return nil, err
}
execAddr := dapp.ExecAddress(string(tx.Execer))
receipt, err := acc.ExecTransferFrozen(unfreeze.Initiator, tx.From(), execAddr, amount)
if err != nil {
uflog.Error("unfreeze withdraw transfer", "execaddr", execAddr, "err", err, "from", unfreeze.Initiator,
"remain", unfreeze.Remaining, "withdraw", amount)
return nil, err
}
return mergeReceipt(receipt, receipt1)
}
// Exec_Terminate 执行终止冻结合约
func (u *Unfreeze) Exec_Terminate(payload *pty.UnfreezeTerminate, tx *types.Transaction, index int) (*types.Receipt, error) {
unfreeze, err := loadUnfreeze(payload.UnfreezeID, u.GetStateDB())
if err != nil {
return nil, err
}
if tx.From() != unfreeze.Initiator {
uflog.Error("unfreeze terminate no privilege", "err", pty.ErrUnfreezeID, "initiator",
unfreeze.Initiator, "from", tx.From())
return nil, pty.ErrNoPrivilege
}
amount, receipt1, err := u.terminator(unfreeze)
if err != nil {
uflog.Error("unfreeze terminate ", "err", err, "unfreeze", unfreeze)
return nil, err
}
acc, err := account.NewAccountDB(unfreeze.AssetExec, unfreeze.AssetSymbol, u.GetStateDB())
if err != nil {
return nil, err
}
execAddr := dapp.ExecAddress(string(tx.Execer))
receipt, err := acc.ExecActive(unfreeze.Initiator, execAddr, amount)
if err != nil {
uflog.Error("unfreeze terminate ", "addr", unfreeze.Initiator, "execaddr", execAddr, "err", err)
return nil, err
}
return mergeReceipt(receipt, receipt1)
}
func (u *Unfreeze) newEntity(payload *pty.UnfreezeCreate, tx *types.Transaction) (*pty.Unfreeze, error) {
id := unfreezeID(tx.Hash())
unfreeze := &pty.Unfreeze{
UnfreezeID: string(id),
StartTime: payload.StartTime,
AssetExec: payload.AssetExec,
AssetSymbol: payload.AssetSymbol,
TotalCount: payload.TotalCount,
Remaining: payload.TotalCount,
Initiator: tx.From(),
Beneficiary: payload.Beneficiary,
Means: payload.Means,
}
if unfreeze.StartTime == 0 {
unfreeze.StartTime = u.GetBlockTime()
}
means, err := newMeans(payload.Means)
if err != nil {
return nil, err
}
unfreeze, err = means.setOpt(unfreeze, payload)
if err != nil {
return nil, err
}
return unfreeze, nil
}
// 创建解冻状态
func (u *Unfreeze) create(unfreeze *pty.Unfreeze) (*types.Receipt, error) {
k := []byte(unfreeze.UnfreezeID)
v := types.Encode(unfreeze)
err := u.GetStateDB().Set(k, v)
if err != nil {
return nil, err
}
receiptLog := getUnfreezeLog(nil, unfreeze)
return &types.Receipt{Ty: types.ExecOk,
KV: []*types.KeyValue{{Key: k, Value: v}}, Logs: []*types.ReceiptLog{receiptLog}}, nil
}
func mergeReceipt(r1 *types.Receipt, r2 *types.Receipt) (*types.Receipt, error) {
r1.Logs = append(r1.Logs, r2.Logs...)
r1.KV = append(r1.KV, r2.KV...)
r1.Ty = types.ExecOk
return r1, nil
}
func getUnfreezeLog(prev, cur *pty.Unfreeze) *types.ReceiptLog {
log := &types.ReceiptLog{}
log.Ty = pty.TyLogCreateUnfreeze
r := &pty.ReceiptUnfreeze{Prev: prev, Current: cur}
log.Log = types.Encode(r)
return log
}
// 提取解冻币
func (u *Unfreeze) withdraw(unfreeze *pty.Unfreeze) (int64, *types.Receipt, error) {
means, err := newMeans(unfreeze.Means)
if err != nil {
return 0, nil, err
}
frozen, err := means.calcFrozen(unfreeze, u.GetBlockTime())
if err != nil {
return 0, nil, err
}
unfreezeOld := *unfreeze
unfreeze, amount := withdraw(unfreeze, frozen)
receiptLog := getUnfreezeLog(&unfreezeOld, unfreeze)
k := []byte(unfreeze.UnfreezeID)
v := types.Encode(unfreeze)
err = u.GetStateDB().Set(k, v)
if err != nil {
return 0, nil, err
}
return amount, &types.Receipt{Ty: types.ExecOk, KV: []*types.KeyValue{{Key: k, Value: v}},
Logs: []*types.ReceiptLog{receiptLog}}, nil
}
// 中止定期解冻
func (u *Unfreeze) terminator(unfreeze *pty.Unfreeze) (int64, *types.Receipt, error) {
if unfreeze.Remaining <= 0 {
return 0, nil, pty.ErrUnfreezeEmptied
}
unfreezeOld := *unfreeze
amount := unfreeze.Remaining
unfreeze.Remaining = 0
receiptLog := getUnfreezeLog(&unfreezeOld, unfreeze)
k := []byte(unfreeze.UnfreezeID)
v := types.Encode(unfreeze)
err := u.GetStateDB().Set(k, v)
if err != nil {
return 0, nil, err
}
return amount, &types.Receipt{Ty: types.ExecOk, KV: []*types.KeyValue{{Key: k, Value: v}},
Logs: []*types.ReceiptLog{receiptLog}}, nil
}
func loadUnfreeze(id string, db dbm.KV) (*pty.Unfreeze, error) {
value, err := db.Get([]byte(id))
if err != nil {
uflog.Error("unfreeze terminate get", "id", id, "err", err)
return nil, err
}
var unfreeze pty.Unfreeze
err = types.Decode(value, &unfreeze)
if err != nil {
uflog.Error("unfreeze terminate decode", "err", err)
return nil, err
}
return &unfreeze, nil
}
// 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
import (
"github.com/33cn/chain33/types"
uf "github.com/33cn/plugin/plugin/dapp/unfreeze/types"
)
func (u *Unfreeze) execDelLocal(receiptData *types.ReceiptData) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
if receiptData.GetTy() != types.ExecOk {
return dbSet, nil
}
for _, log := range receiptData.Logs {
switch log.Ty {
case uf.TyLogCreateUnfreeze, uf.TyLogWithdrawUnfreeze, uf.TyLogTerminateUnfreeze:
var receipt uf.ReceiptUnfreeze
err := types.Decode(log.Log, &receipt)
if err != nil {
return nil, err
}
kv := u.rollbackUnfreezeCreate(&receipt)
dbSet.KV = append(dbSet.KV, kv...)
}
}
return dbSet, nil
}
// ExecDelLocal_Create 本地撤销执行创建冻结合约
func (u *Unfreeze) ExecDelLocal_Create(payload *uf.UnfreezeCreate, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return u.execDelLocal(receiptData)
}
// ExecDelLocal_Withdraw 本地撤销执行冻结合约中提币
func (u *Unfreeze) ExecDelLocal_Withdraw(payload *uf.UnfreezeWithdraw, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return u.execDelLocal(receiptData)
}
// ExecDelLocal_Terminate 本地撤销执行冻结合约的终止
func (u *Unfreeze) ExecDelLocal_Terminate(payload *uf.UnfreezeTerminate, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return u.execDelLocal(receiptData)
}
// 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
import (
"github.com/33cn/chain33/types"
uf "github.com/33cn/plugin/plugin/dapp/unfreeze/types"
)
func (u *Unfreeze) execLocal(receiptData *types.ReceiptData) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
if receiptData.GetTy() != types.ExecOk {
return dbSet, nil
}
for _, log := range receiptData.Logs {
switch log.Ty {
case uf.TyLogCreateUnfreeze, uf.TyLogWithdrawUnfreeze, uf.TyLogTerminateUnfreeze:
var receipt uf.ReceiptUnfreeze
err := types.Decode(log.Log, &receipt)
if err != nil {
return nil, err
}
kv := u.saveUnfreezeCreate(&receipt)
dbSet.KV = append(dbSet.KV, kv...)
default:
}
}
return dbSet, nil
}
// ExecLocal_Create 本地执行创建冻结合约
func (u *Unfreeze) ExecLocal_Create(payload *uf.UnfreezeCreate, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return u.execLocal(receiptData)
}
// ExecLocal_Withdraw 本地执行提币
func (u *Unfreeze) ExecLocal_Withdraw(payload *uf.UnfreezeWithdraw, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return u.execLocal(receiptData)
}
// ExecLocal_Terminate 本地执行终止冻结合约
func (u *Unfreeze) ExecLocal_Terminate(payload *uf.UnfreezeTerminate, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return u.execLocal(receiptData)
}
func localKeys(res *uf.ReceiptUnfreeze, value []byte) (kvs []*types.KeyValue) {
kvs = append(kvs, &types.KeyValue{Key: initKey(res.Current.Initiator), Value: value})
kvs = append(kvs, &types.KeyValue{Key: beneficiaryKey(res.Current.Beneficiary), Value: value})
return
}
func (u *Unfreeze) saveUnfreezeCreate(res *uf.ReceiptUnfreeze) (kvs []*types.KeyValue) {
kvs = localKeys(res, []byte(res.Current.UnfreezeID))
return
}
func (u *Unfreeze) rollbackUnfreezeCreate(res *uf.ReceiptUnfreeze) (kvs []*types.KeyValue) {
kvs = localKeys(res, nil)
return
}
// 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
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/common/crypto"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/unfreeze/types"
)
type execEnv struct {
blockTime int64
blockHeight int64
difficulty uint64
}
var (
Symbol = "TEST"
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"),
}
)
func TestUnfreeze(t *testing.T) {
total := int64(100000)
accountA := types.Account{
Balance: total,
Frozen: 0,
Addr: string(Nodes[0]),
}
accountB := types.Account{
Balance: total,
Frozen: 0,
Addr: string(Nodes[1]),
}
execAddr := address.ExecAddress(pty.UnfreezeX)
stateDB, _ := dbm.NewGoMemDB("1", "2", 100)
accA, _ := account.NewAccountDB(AssetExecPara, Symbol, stateDB)
accA.SaveExecAccount(execAddr, &accountA)
accB, _ := account.NewAccountDB(AssetExecPara, Symbol, stateDB)
accB.SaveExecAccount(execAddr, &accountB)
env := execEnv{
10,
2,
1539918074,
}
ty := pty.UnfreezeType{}
// 创建
opt := &pty.FixAmount{Period: 10, Amount: 2}
p1 := &pty.UnfreezeCreate{
StartTime: 10,
AssetExec: AssetExecPara,
AssetSymbol: Symbol,
TotalCount: 10000,
Beneficiary: string(Nodes[1]),
Means: "FixAmount",
MeansOpt: &pty.UnfreezeCreate_FixAmount{FixAmount: opt},
}
createTx, err := ty.RPC_UnfreezeCreateTx(p1)
if err != nil {
t.Error("RPC_UnfreezeCreateTx", "err", err)
}
createTx, err = signTx(createTx, PrivKeyA)
if err != nil {
t.Error("RPC_UnfreezeCreateTx sign", "err", err)
}
exec := newUnfreeze()
exec.SetStateDB(stateDB)
exec.SetEnv(env.blockHeight, env.blockTime, env.difficulty)
receipt, err := exec.Exec(createTx, int(1))
assert.Nil(t, err)
assert.NotNil(t, receipt)
//t.Log(receipt)
accTmp := accA.LoadExecAccount(accountA.Addr, execAddr)
assert.Equal(t, total-p1.TotalCount, accTmp.Balance)
assert.Equal(t, p1.TotalCount, accTmp.Frozen)
receiptDate := &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err := exec.ExecLocal(createTx, receiptDate, int(1))
assert.Nil(t, err)
assert.NotNil(t, set)
// 提币
p2 := &pty.UnfreezeWithdraw{
UnfreezeID: string(unfreezeID(createTx.Hash())),
}
withdrawTx, err := ty.RPC_UnfreezeWithdrawTx(p2)
if err != nil {
t.Error("RPC_UnfreezeWithdrawTx", "err", err)
}
withdrawTx, err = signTx(withdrawTx, PrivKeyB)
if err != nil {
t.Error("RPC_UnfreezeWithdrawTx sign", "err", err)
}
blockTime := int64(10)
exec.SetEnv(env.blockHeight+1, env.blockTime+blockTime, env.difficulty)
receipt, err = exec.Exec(withdrawTx, 1)
assert.Nil(t, err)
assert.NotNil(t, receipt)
//t.Log(receipt)
accATmp := accA.LoadExecAccount(accountA.Addr, execAddr)
accBTmp := accB.LoadExecAccount(accountB.Addr, execAddr)
assert.Equal(t, total-p1.TotalCount, accATmp.Balance)
u := pty.Unfreeze{}
e := types.Decode(receipt.KV[2].Value, &u)
assert.Nil(t, e)
assert.Equal(t, u.Remaining, accATmp.Frozen)
assert.Equal(t, accountB.Balance+p1.TotalCount-u.Remaining, accBTmp.Balance)
receiptDate2 := &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err = exec.ExecLocal(withdrawTx, receiptDate2, int(1))
assert.Nil(t, err)
assert.NotNil(t, set)
// 不是受益人提币
{
p2 := &pty.UnfreezeWithdraw{
UnfreezeID: string(unfreezeID(createTx.Hash())),
}
withdrawTx, err := ty.RPC_UnfreezeWithdrawTx(p2)
if err != nil {
t.Error("RPC_UnfreezeWithdrawTx", "err", err)
}
withdrawTx, err = signTx(withdrawTx, PrivKeyC)
if err != nil {
t.Error("RPC_UnfreezeWithdrawTx sign", "err", err)
}
blockTime := int64(10)
exec.SetEnv(env.blockHeight+1, env.blockTime+blockTime, env.difficulty)
receipt, err = exec.Exec(withdrawTx, 1)
assert.Equal(t, pty.ErrNoPrivilege, err)
assert.Nil(t, receipt)
}
// 不是创建者终止
{
p3 := &pty.UnfreezeTerminate{
UnfreezeID: string(unfreezeID(createTx.Hash())),
}
terminateTx, err := ty.RPC_UnfreezeTerminateTx(p3)
if err != nil {
t.Error("RPC_UnfreezeTerminateTx", "err", err)
}
terminateTx, err = signTx(terminateTx, PrivKeyC)
if err != nil {
t.Error("RPC_UnfreezeTerminateTx sign", "err", err)
}
receipt, err = exec.Exec(terminateTx, 1)
assert.Equal(t, pty.ErrNoPrivilege, err)
assert.Nil(t, receipt)
}
// 终止
p3 := &pty.UnfreezeTerminate{
UnfreezeID: string(unfreezeID(createTx.Hash())),
}
terminateTx, err := ty.RPC_UnfreezeTerminateTx(p3)
if err != nil {
t.Error("RPC_UnfreezeTerminateTx", "err", err)
}
terminateTx, err = signTx(terminateTx, PrivKeyA)
if err != nil {
t.Error("RPC_UnfreezeTerminateTx sign", "err", err)
}
receipt, err = exec.Exec(terminateTx, 1)
assert.Nil(t, err)
assert.NotNil(t, receipt)
//t.Log(receipt)
accATmp = accA.LoadExecAccount(accountA.Addr, execAddr)
assert.Equal(t, total+total, accATmp.Balance+accBTmp.Balance)
assert.Equal(t, int64(0), accATmp.Frozen)
receiptDate3 := &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err = exec.ExecLocal(terminateTx, receiptDate3, int(1))
assert.Nil(t, err)
assert.NotNil(t, set)
// 终止后不能继续提币
{
p2 := &pty.UnfreezeWithdraw{
UnfreezeID: string(unfreezeID(createTx.Hash())),
}
withdrawTx, err := ty.RPC_UnfreezeWithdrawTx(p2)
if err != nil {
t.Error("RPC_UnfreezeWithdrawTx", "err", err)
}
withdrawTx, err = signTx(withdrawTx, PrivKeyB)
if err != nil {
t.Error("RPC_UnfreezeWithdrawTx sign", "err", err)
}
blockTime := int64(10)
exec.SetEnv(env.blockHeight+1, env.blockTime+blockTime+blockTime, env.difficulty)
receipt, err = exec.Exec(withdrawTx, 1)
assert.Equal(t, pty.ErrUnfreezeEmptied, err)
assert.Nil(t, receipt)
}
req := types.ReqString{Data: string(unfreezeID(createTx.Hash()))}
_, err = exec.Query("GetUnfreeze", types.Encode(&req))
assert.Nil(t, err)
_, err = exec.Query("GetUnfreezeWithdraw", types.Encode(&req))
assert.Nil(t, err)
_, err = exec.ExecDelLocal(terminateTx, receiptDate3, int(1))
assert.Nil(t, err)
_, err = exec.ExecDelLocal(withdrawTx, receiptDate2, int(1))
assert.Nil(t, err)
_, err = exec.ExecDelLocal(createTx, receiptDate, int(1))
assert.Nil(t, err)
}
func signTx(tx *types.Transaction, hexPrivKey string) (*types.Transaction, error) {
signType := types.SECP256K1
c, err := crypto.New(types.GetSignName(pty.UnfreezeX, 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
}
// 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
import (
"fmt"
"github.com/33cn/chain33/common"
pty "github.com/33cn/plugin/plugin/dapp/unfreeze/types"
)
var (
id = "mavl-" + pty.UnfreezeX + "-"
initLocal = "LODB-" + pty.UnfreezeX + "-init-"
beneficiaryLocal = "LODB-" + pty.UnfreezeX + "-beneficiary-"
)
func unfreezeID(txHash []byte) []byte {
return []byte(fmt.Sprintf("%s%s", id, common.Bytes2Hex(txHash)))
}
func initKey(init string) []byte {
return []byte(fmt.Sprintf("%s%s", initLocal, init))
}
func beneficiaryKey(beneficiary string) []byte {
return []byte(fmt.Sprintf("%s%s", beneficiaryLocal, beneficiary))
}
// 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
import (
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/unfreeze/types"
)
// Means 解冻算法接口
type Means interface {
setOpt(unfreeze *pty.Unfreeze, from *pty.UnfreezeCreate) (*pty.Unfreeze, error)
calcFrozen(unfreeze *pty.Unfreeze, now int64) (int64, error)
}
func newMeans(means string) (Means, error) {
if means == "FixAmount" {
return &fixAmount{}, nil
} else if means == "LeftProportion" {
return &leftProportion{}, nil
}
return nil, types.ErrNotSupport
}
type fixAmount struct {
}
func (opt *fixAmount) setOpt(unfreeze *pty.Unfreeze, from *pty.UnfreezeCreate) (*pty.Unfreeze, error) {
o := from.GetFixAmount()
if o == nil {
return nil, types.ErrInvalidParam
}
if o.Amount <= 0 || o.Period <= 0 {
return nil, types.ErrInvalidParam
}
unfreeze.MeansOpt = &pty.Unfreeze_FixAmount{FixAmount: from.GetFixAmount()}
return unfreeze, nil
}
func (opt *fixAmount) calcFrozen(unfreeze *pty.Unfreeze, now int64) (int64, error) {
means := unfreeze.GetFixAmount()
if means == nil {
return 0, types.ErrInvalidParam
}
unfreezeTimes := (now + means.Period - unfreeze.StartTime) / means.Period
unfreezeAmount := means.Amount * unfreezeTimes
if unfreeze.TotalCount <= unfreezeAmount {
return 0, nil
}
return unfreeze.TotalCount - unfreezeAmount, nil
}
type leftProportion struct {
}
func (opt *leftProportion) setOpt(unfreeze *pty.Unfreeze, from *pty.UnfreezeCreate) (*pty.Unfreeze, error) {
o := from.GetLeftProportion()
if o == nil {
return nil, types.ErrInvalidParam
}
if o.Period <= 0 || o.TenThousandth <= 0 {
return nil, types.ErrInvalidParam
}
unfreeze.MeansOpt = &pty.Unfreeze_LeftProportion{LeftProportion: from.GetLeftProportion()}
return unfreeze, nil
}
func (opt *leftProportion) calcFrozen(unfreeze *pty.Unfreeze, now int64) (int64, error) {
means := unfreeze.GetLeftProportion()
if means == nil {
return 0, types.ErrInvalidParam
}
unfreezeTimes := (now + means.Period - unfreeze.StartTime) / means.Period
frozen := float64(unfreeze.TotalCount)
for i := int64(0); i < unfreezeTimes; i++ {
frozen = frozen * float64(10000-means.TenThousandth) / 10000
}
return int64(frozen), nil
}
func withdraw(unfreeze *pty.Unfreeze, frozen int64) (*pty.Unfreeze, int64) {
if unfreeze.Remaining == 0 {
return unfreeze, 0
}
amount := unfreeze.Remaining - frozen
unfreeze.Remaining = frozen
return unfreeze, amount
}
package executor
import (
"testing"
"github.com/stretchr/testify/assert"
pty "github.com/33cn/plugin/plugin/dapp/unfreeze/types"
)
func TestCalcFrozen(t *testing.T) {
m, err := newMeans("LeftProportion")
assert.Nil(t, err)
assert.NotNil(t, m)
cases := []struct {
start int64
now int64
period int64
total int64
tenThousandth int64
expect int64
}{
{10000, 10001, 10, 10000, 2, 9998},
{10000, 10011, 10, 10000, 2, 9996},
{10000, 10001, 10, 1e17, 2, 9998 * 1e13},
{10000, 10011, 10, 1e17, 2, 9998 * 9998 * 1e9},
}
for _, c := range cases {
c := c
t.Run("test LeftProportion", func(t *testing.T) {
create := pty.UnfreezeCreate{
StartTime: c.start,
AssetExec: "coins",
AssetSymbol: "bty",
TotalCount: c.total,
Beneficiary: "x",
Means: "LeftProportion",
MeansOpt: &pty.UnfreezeCreate_LeftProportion{
LeftProportion: &pty.LeftProportion{
Period: c.period,
TenThousandth: c.tenThousandth,
},
},
}
u := &pty.Unfreeze{
TotalCount: c.total,
Means: "LeftProportion",
StartTime: c.start,
MeansOpt: &pty.Unfreeze_LeftProportion{
LeftProportion: &pty.LeftProportion{
Period: c.period,
TenThousandth: c.tenThousandth,
},
},
}
u, err = m.setOpt(u, &create)
assert.Nil(t, err)
f, err := m.calcFrozen(u, c.now)
assert.Nil(t, err)
assert.Equal(t, c.expect, f)
})
}
}
// 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
import (
"time"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/unfreeze/types"
)
// Query_GetUnfreezeWithdraw 查询合约可提币量
func (u *Unfreeze) Query_GetUnfreezeWithdraw(in *types.ReqString) (types.Message, error) {
return QueryWithdraw(u.GetStateDB(), in.GetData())
}
// Query_GetUnfreeze 查询合约状态
func (u *Unfreeze) Query_GetUnfreeze(in *types.ReqString) (types.Message, error) {
return QueryUnfreeze(u.GetStateDB(), in.GetData())
}
// QueryWithdraw 查询可提币状态
func QueryWithdraw(stateDB dbm.KV, unfreezeID string) (types.Message, error) {
unfreeze, err := loadUnfreeze(unfreezeID, stateDB)
if err != nil {
uflog.Error("QueryWithdraw ", "unfreezeID", unfreezeID, "err", err)
return nil, err
}
currentTime := time.Now().Unix()
reply := &pty.ReplyQueryUnfreezeWithdraw{UnfreezeID: unfreezeID}
available, err := getWithdrawAvailable(unfreeze, currentTime)
if err != nil {
return nil, err
}
reply.AvailableAmount = available
return reply, nil
}
func getWithdrawAvailable(unfreeze *pty.Unfreeze, calcTime int64) (int64, error) {
means, err := newMeans(unfreeze.Means)
if err != nil {
return 0, err
}
frozen, err := means.calcFrozen(unfreeze, calcTime)
if err != nil {
return 0, err
}
_, amount := withdraw(unfreeze, frozen)
return amount, nil
}
// QueryUnfreeze 查询合约状态
func QueryUnfreeze(stateDB dbm.KV, unfreezeID string) (types.Message, error) {
unfreeze, err := loadUnfreeze(unfreezeID, stateDB)
if err != nil {
uflog.Error("QueryUnfreeze ", "unfreezeID", unfreezeID, "err", err)
return nil, err
}
return unfreeze, nil
}
// 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
import (
log "github.com/33cn/chain33/common/log/log15"
drivers "github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
uf "github.com/33cn/plugin/plugin/dapp/unfreeze/types"
)
var uflog = log.New("module", "execs.unfreeze")
var driverName = uf.UnfreezeX
func init() {
ety := types.LoadExecutorType(driverName)
ety.InitFuncList(types.ListMethod(&Unfreeze{}))
}
// Init 重命名执行器名称
func Init(name string, sub []byte) {
drivers.Register(GetName(), newUnfreeze, 0)
}
// Unfreeze 执行器结构体
type Unfreeze struct {
drivers.DriverBase
}
func newUnfreeze() drivers.Driver {
t := &Unfreeze{}
t.SetChild(t)
t.SetExecutorType(types.LoadExecutorType(driverName))
return t
}
// GetName 获得执行器名字
func GetName() string {
return newUnfreeze().GetName()
}
// GetDriverName 获得驱动名字
func (u *Unfreeze) GetDriverName() string {
return driverName
}
// 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 unfreeze
import (
"github.com/33cn/chain33/pluginmgr"
"github.com/33cn/plugin/plugin/dapp/unfreeze/commands"
"github.com/33cn/plugin/plugin/dapp/unfreeze/executor"
"github.com/33cn/plugin/plugin/dapp/unfreeze/rpc"
uf "github.com/33cn/plugin/plugin/dapp/unfreeze/types"
)
func init() {
pluginmgr.Register(&pluginmgr.PluginBase{
Name: uf.PackageName,
ExecName: executor.GetName(),
Exec: executor.Init,
Cmd: commands.Cmd,
RPC: rpc.Init,
})
}
// 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.
all:
./create_protobuf.sh
#!/bin/sh
protoc --go_out=plugins=grpc:../types ./*.proto --proto_path=. --proto_path="$GOPATH/src/github.com/33cn/chain33/types/proto/"
// 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.
syntax = "proto3";
import "common.proto";
package types;
message Unfreeze {
//解冻交易ID(唯一识别码)
string unfreezeID = 1;
//开始时间
int64 startTime = 2;
//币种
string assetExec = 3;
string assetSymbol = 4;
//冻结总额
int64 totalCount = 5;
//发币人地址
string initiator = 6;
//收币人地址
string beneficiary = 7;
//解冻剩余币数
int64 remaining = 8;
//解冻方式(百分比;固额)
string means = 9;
oneof meansOpt {
FixAmount fixAmount = 10;
LeftProportion leftProportion = 11;
}
}
// 按时间固定额度解冻
message FixAmount {
int64 period = 1;
int64 amount = 2;
}
// 固定时间间隔按余量百分比解冻
message LeftProportion {
int64 period = 1;
int64 tenThousandth = 2;
}
// message for execs.unfreeze
message UnfreezeAction {
oneof value {
UnfreezeCreate create = 1;
UnfreezeWithdraw withdraw = 2;
UnfreezeTerminate terminate = 3;
}
int32 ty = 4;
}
// action
message UnfreezeCreate {
int64 startTime = 1;
string assetExec = 2;
string assetSymbol = 3;
int64 totalCount = 4;
string beneficiary = 5;
string means = 6;
oneof meansOpt {
FixAmount fixAmount = 7;
LeftProportion leftProportion = 8;
}
}
message UnfreezeWithdraw {
string unfreezeID = 1;
}
message UnfreezeTerminate {
string unfreezeID = 1;
}
// receipt
message ReceiptUnfreeze {
Unfreeze prev = 1;
Unfreeze current = 2;
}
// query
message ReplyQueryUnfreezeWithdraw {
string unfreezeID = 1;
int64 availableAmount = 2;
}
// TODO 类型应该大写还是小写
service unfreeze {
rpc GetUnfreezeWithdraw(ReqString) returns (ReplyQueryUnfreezeWithdraw) {}
rpc QueryUnfreeze(ReqString) returns (Unfreeze) {}
}
\ No newline at end of file
// 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 rpc
import (
"context"
"encoding/hex"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/unfreeze/types"
)
// GetUnfreeze 获得冻结合约
func (c *channelClient) GetUnfreeze(ctx context.Context, in *types.ReqString) (*pty.Unfreeze, error) {
v, err := c.Query(pty.UnfreezeX, "GetUnfreeze", in)
if err != nil {
return nil, err
}
if resp, ok := v.(*pty.Unfreeze); ok {
return resp, nil
}
return nil, types.ErrDecode
}
// GetUnfreezeWithdraw 获得冻结合约可提币量
func (c *channelClient) GetUnfreezeWithdraw(ctx context.Context, in *types.ReqString) (*pty.ReplyQueryUnfreezeWithdraw, error) {
v, err := c.Query(pty.UnfreezeX, "GetUnfreezeWithdraw", in)
if err != nil {
return nil, err
}
if resp, ok := v.(*pty.ReplyQueryUnfreezeWithdraw); ok {
return resp, nil
}
return nil, types.ErrDecode
}
// GetUnfreeze 获得冻结合约
func (c *Jrpc) GetUnfreeze(in *types.ReqString, result *interface{}) error {
v, err := c.cli.GetUnfreeze(context.Background(), in)
if err != nil {
return err
}
*result = v
return nil
}
// GetUnfreezeWithdraw 获得冻结合约可提币量
func (c *Jrpc) GetUnfreezeWithdraw(in *types.ReqString, result *interface{}) error {
v, err := c.cli.GetUnfreezeWithdraw(context.Background(), in)
if err != nil {
return err
}
*result = v
return nil
}
// CreateRawUnfreezeCreate 创建冻结合约
func (c *Jrpc) CreateRawUnfreezeCreate(param *pty.UnfreezeCreate, result *interface{}) error {
if param == nil {
return types.ErrInvalidParam
}
data, err := types.CallCreateTx(types.ExecName(pty.UnfreezeX), "UnfreezeCreateTX", param)
if err != nil {
return err
}
*result = hex.EncodeToString(data)
return nil
}
// CreateRawUnfreezeWithdraw 创建提币交易
func (c *Jrpc) CreateRawUnfreezeWithdraw(param *pty.UnfreezeWithdraw, result *interface{}) error {
if param == nil {
return types.ErrInvalidParam
}
data, err := types.CallCreateTx(types.ExecName(pty.UnfreezeX), "UnfreezeWithdrawTx", param)
if err != nil {
return err
}
*result = hex.EncodeToString(data)
return nil
}
// CreateRawUnfreezeTerminate 终止冻结合约
func (c *Jrpc) CreateRawUnfreezeTerminate(param *pty.UnfreezeTerminate, result *interface{}) error {
if param == nil {
return types.ErrInvalidParam
}
data, err := types.CallCreateTx(types.ExecName(pty.UnfreezeX), "UnfreezeTerminateTx", param)
if err != nil {
return err
}
*result = hex.EncodeToString(data)
return nil
}
// 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 rpc
import (
"github.com/33cn/chain33/rpc/types"
)
// Jrpc json rpc struct
type Jrpc struct {
cli *channelClient
}
// Grpc grpc struct
type Grpc struct {
*channelClient
}
type channelClient struct {
types.ChannelClient
}
// Init init grpc param
func Init(name string, s types.RPCServer) {
cli := &channelClient{}
grpc := &Grpc{channelClient: cli}
cli.Init(name, s, &Jrpc{cli: cli}, grpc)
}
// 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 types
//unfreeze action ty
const (
UnfreezeActionCreate = iota + 1
UnfreezeActionWithdraw
UnfreezeActionTerminate
//log for unfreeze
TyLogCreateUnfreeze = 2001 // TODO 修改具体编号
TyLogWithdrawUnfreeze = 2002
TyLogTerminateUnfreeze = 2003
)
const (
// Action_CreateUnfreeze Action 名字
Action_CreateUnfreeze = "createUnfreeze"
// Action_WithdrawUnfreeze Action 名字
Action_WithdrawUnfreeze = "withdrawUnfreeze"
// Action_TerminateUnfreeze Action 名字
Action_TerminateUnfreeze = "terminateUnfreeze"
)
const (
// FuncName_QueryUnfreezeWithdraw 查询方法名
FuncName_QueryUnfreezeWithdraw = "QueryUnfreezeWithdraw"
)
//包的名字可以通过配置文件来配置
//建议用github的组织名称,或者用户名字开头, 再加上自己的插件的名字
//如果发生重名,可以通过配置文件修改这些名字
var (
PackageName = "chain33.unfreeze"
RPCName = "Chain33.Unfreeze"
UnfreezeX = "unfreeze"
ExecerUnfreeze = []byte(UnfreezeX)
FixAmountX = "FixAmount"
LeftProportionX = "LeftProportion"
SupportMeans = []string{"FixAmount", "LeftProportion"}
)
// 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 types
import "errors"
var (
// ErrUnfreezeEmptied 没有可提币量
ErrUnfreezeEmptied = errors.New("ErrUnfreezeEmptied")
// ErrUnfreezeMeans 解冻币算法错误
ErrUnfreezeMeans = errors.New("ErrUnfreezeMeans")
// ErrUnfreezeID 冻结合约ID错误
ErrUnfreezeID = errors.New("ErrUnfreezeID")
// ErrNoPrivilege 没有权限
ErrNoPrivilege = errors.New("ErrNoPrivilege")
)
// 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 types
// 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 types
import (
"encoding/json"
"math/rand"
"reflect"
"time"
"github.com/33cn/chain33/common/address"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types"
)
var name string
var tlog = log.New("module", name)
func init() {
name = UnfreezeX
types.AllowUserExec = append(types.AllowUserExec, []byte(UnfreezeX))
// init executor type
types.RegistorExecutor(name, NewType())
}
//getRealExecName
func getRealExecName(paraName string) string {
return types.ExecName(paraName + UnfreezeX)
}
// NewType 生成新的基础类型
func NewType() *UnfreezeType {
c := &UnfreezeType{}
c.SetChild(c)
return c
}
// UnfreezeType 基础类型结构体
type UnfreezeType struct {
types.ExecTypeBase
}
// GetLogMap 获得日志类型列表
func (u *UnfreezeType) GetLogMap() map[int64]*types.LogInfo {
return map[int64]*types.LogInfo{
TyLogCreateUnfreeze: {Ty: reflect.TypeOf(ReceiptUnfreeze{}), Name: "LogCreateUnfreeze"},
TyLogWithdrawUnfreeze: {Ty: reflect.TypeOf(ReceiptUnfreeze{}), Name: "LogWithdrawUnfreeze"},
TyLogTerminateUnfreeze: {Ty: reflect.TypeOf(ReceiptUnfreeze{}), Name: "LogTerminateUnfreeze"},
}
}
// GetPayload 获得空的Unfreeze 的 Payload
func (u *UnfreezeType) GetPayload() types.Message {
return &UnfreezeAction{}
}
// GetTypeMap 获得Action 方法列表
func (u *UnfreezeType) GetTypeMap() map[string]int32 {
return map[string]int32{
"Create": UnfreezeActionCreate,
"Withdraw": UnfreezeActionWithdraw,
"Terminate": UnfreezeActionTerminate,
}
}
// CreateTx 创建交易
func (u UnfreezeType) CreateTx(action string, message json.RawMessage) (*types.Transaction, error) {
tlog.Debug("UnfreezeType.CreateTx", "action", action)
if action == Action_CreateUnfreeze {
var param UnfreezeCreate
err := json.Unmarshal(message, &param)
if err != nil {
tlog.Error("CreateTx", "Error", err)
return nil, types.ErrInvalidParam
}
return u.RPC_UnfreezeCreateTx(&param)
} else if action == Action_WithdrawUnfreeze {
var param UnfreezeWithdraw
err := json.Unmarshal(message, &param)
if err != nil {
tlog.Error("CreateTx", "Error", err)
return nil, types.ErrInvalidParam
}
return u.RPC_UnfreezeWithdrawTx(&param)
} else if action == Action_TerminateUnfreeze {
var param UnfreezeTerminate
err := json.Unmarshal(message, &param)
if err != nil {
tlog.Error("CreateTx", "Error", err)
return nil, types.ErrInvalidParam
}
return u.RPC_UnfreezeTerminateTx(&param)
}
return nil, types.ErrNotSupport
}
// RPC_UnfreezeCreateTx 创建冻结合约交易入口
func (u UnfreezeType) RPC_UnfreezeCreateTx(parm *UnfreezeCreate) (*types.Transaction, error) {
return CreateUnfreezeCreateTx(types.GetParaName(), parm)
}
// CreateUnfreezeCreateTx 创建冻结合约交易
func CreateUnfreezeCreateTx(title string, parm *UnfreezeCreate) (*types.Transaction, error) {
if parm == nil {
tlog.Error("RPC_UnfreezeCreateTx", "parm", parm)
return nil, types.ErrInvalidParam
}
if parm.AssetExec == "" || parm.AssetSymbol == "" || parm.TotalCount <= 0 || parm.Means == "" {
tlog.Error("RPC_UnfreezeCreateTx", "parm", parm)
return nil, types.ErrInvalidParam
}
if !supportMeans(parm.Means) {
tlog.Error("RPC_UnfreezeCreateTx not support means", "parm", parm)
return nil, types.ErrInvalidParam
}
create := &UnfreezeAction{
Ty: UnfreezeActionCreate,
Value: &UnfreezeAction_Create{parm},
}
tx := &types.Transaction{
Execer: []byte(getRealExecName(title)),
Payload: types.Encode(create),
Nonce: rand.New(rand.NewSource(time.Now().UnixNano())).Int63(),
To: address.ExecAddress(getRealExecName(types.GetParaName())),
}
tx.SetRealFee(types.GInt("MinFee"))
return tx, nil
}
// RPC_UnfreezeWithdrawTx 创建提币交易入口
func (u UnfreezeType) RPC_UnfreezeWithdrawTx(parm *UnfreezeWithdraw) (*types.Transaction, error) {
return CreateUnfreezeWithdrawTx(types.GetParaName(), parm)
}
// CreateUnfreezeWithdrawTx 创建提币交易
func CreateUnfreezeWithdrawTx(title string, parm *UnfreezeWithdraw) (*types.Transaction, error) {
if parm == nil {
tlog.Error("RPC_UnfreezeWithdrawTx", "parm", parm)
return nil, types.ErrInvalidParam
}
v := &UnfreezeWithdraw{
UnfreezeID: parm.UnfreezeID,
}
withdraw := &UnfreezeAction{
Ty: UnfreezeActionWithdraw,
Value: &UnfreezeAction_Withdraw{v},
}
tx := &types.Transaction{
Execer: []byte(getRealExecName(title)),
Payload: types.Encode(withdraw),
Nonce: rand.New(rand.NewSource(time.Now().UnixNano())).Int63(),
To: address.ExecAddress(getRealExecName(types.GetParaName())),
}
tx.SetRealFee(types.GInt("MinFee"))
return tx, nil
}
// RPC_UnfreezeTerminateTx 创建终止冻结合约入口
func (u UnfreezeType) RPC_UnfreezeTerminateTx(parm *UnfreezeTerminate) (*types.Transaction, error) {
return CreateUnfreezeTerminateTx(types.GetParaName(), parm)
}
// CreateUnfreezeTerminateTx 创建终止冻结合约
func CreateUnfreezeTerminateTx(title string, parm *UnfreezeTerminate) (*types.Transaction, error) {
if parm == nil {
tlog.Error("RPC_UnfreezeTerminateTx", "parm", parm)
return nil, types.ErrInvalidParam
}
v := &UnfreezeTerminate{
UnfreezeID: parm.UnfreezeID,
}
terminate := &UnfreezeAction{
Ty: UnfreezeActionTerminate,
Value: &UnfreezeAction_Terminate{v},
}
tx := &types.Transaction{
Execer: []byte(getRealExecName(title)),
Payload: types.Encode(terminate),
Nonce: rand.New(rand.NewSource(time.Now().UnixNano())).Int63(),
To: address.ExecAddress(getRealExecName(types.GetParaName())),
}
tx.SetRealFee(types.GInt("MinFee"))
return tx, nil
}
func supportMeans(means string) bool {
for _, m := range SupportMeans {
if m == means {
return true
}
}
return false
}
This diff is collapsed.
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