Commit f82a7587 authored by hezhengjun's avatar hezhengjun

Merge branch 'master' into add_support_metrics_0109

parents abb1817a 83b42f56
...@@ -312,6 +312,12 @@ Enable=0 ...@@ -312,6 +312,12 @@ Enable=0
[fork.sub.jsvm] [fork.sub.jsvm]
Enable=0 Enable=0
[fork.sub.issuance]
Enable=0
[fork.sub.collateralize]
Enable=0
#对已有的平行链如果不是从0开始同步数据,需要设置这个kvmvccmavl的对应平行链高度的fork,如果从0开始同步,statehash会跟以前mavl的不同 #对已有的平行链如果不是从0开始同步数据,需要设置这个kvmvccmavl的对应平行链高度的fork,如果从0开始同步,statehash会跟以前mavl的不同
[fork.sub.store-kvmvccmavl] [fork.sub.store-kvmvccmavl]
ForkKvmvccmavl=0 ForkKvmvccmavl=0
......
This diff is collapsed.
...@@ -38,7 +38,7 @@ enableTxQuickIndex=true ...@@ -38,7 +38,7 @@ enableTxQuickIndex=true
seeds=["127.0.0.1:13802"] seeds=["127.0.0.1:13802"]
enable=false enable=false
isSeed=false isSeed=false
serverStart=true serverStart=false
innerSeedEnable=false innerSeedEnable=false
useGithub=false useGithub=false
innerBounds=300 innerBounds=300
...@@ -108,12 +108,14 @@ peersURL="http://127.0.0.1:9021" ...@@ -108,12 +108,14 @@ peersURL="http://127.0.0.1:9021"
# raft共识用到,指示raft集群中只读节点的IP(只同步日志,不参与raft共识) # raft共识用到,指示raft集群中只读节点的IP(只同步日志,不参与raft共识)
readOnlyPeersURL="" readOnlyPeersURL=""
addPeersURL="" addPeersURL=""
#raft共识用到,默认raft中多少条记录打包一个snapshot(这里为了测试调整小一点) #raft中多少条记录打包一个snapshot,默认为10000(这里为了测试调整小一点)
defaultSnapCount=2 defaultSnapCount=2
#raft共识用到,默认raft中写区块时间间隔 #raft中写区块时间间隔,默认为1秒
writeBlockSeconds=1 writeBlockSeconds=1
#raft共识用到,默认raft中leader发送心跳包时间间隔 #raft中leader发送心跳包时间间隔,默认为1秒
heartbeatTick=1 heartbeatTick=1
#raft中leader打包空区块的时间间隔,默认为0,表示不打包空区块
emptyBlockInterval=120
# =============== raft共识配置参数 =========================== # =============== raft共识配置参数 ===========================
[store] [store]
...@@ -134,7 +136,7 @@ dbCache=16 ...@@ -134,7 +136,7 @@ dbCache=16
signType="secp256k1" signType="secp256k1"
[wallet.sub.ticket] [wallet.sub.ticket]
minerdisable=false minerdisable=true
minerwhitelist=["*"] minerwhitelist=["*"]
minerWaitTime="1s" minerWaitTime="1s"
......
...@@ -19,10 +19,11 @@ var ( ...@@ -19,10 +19,11 @@ var (
rlog = log.New("module", "raft") rlog = log.New("module", "raft")
genesis string genesis string
genesisBlockTime int64 genesisBlockTime int64
defaultSnapCount uint64 = 1000 defaultSnapCount uint64 = 10000
snapshotCatchUpEntriesN uint64 = 1000 snapshotCatchUpEntriesN uint64 = 10000
writeBlockSeconds int64 = 1 writeBlockSeconds int64 = 1
heartbeatTick = 1 heartbeatTick = 1
emptyBlockInterval int64
isLeader = false isLeader = false
mux atomic.Value mux atomic.Value
confChangeC chan raftpb.ConfChange confChangeC chan raftpb.ConfChange
...@@ -40,6 +41,7 @@ type subConfig struct { ...@@ -40,6 +41,7 @@ type subConfig struct {
DefaultSnapCount int64 `json:"defaultSnapCount"` DefaultSnapCount int64 `json:"defaultSnapCount"`
WriteBlockSeconds int64 `json:"writeBlockSeconds"` WriteBlockSeconds int64 `json:"writeBlockSeconds"`
HeartbeatTick int32 `json:"heartbeatTick"` HeartbeatTick int32 `json:"heartbeatTick"`
EmptyBlockInterval int64 `json:"emptyBlockInterval"`
} }
func init() { func init() {
...@@ -64,7 +66,7 @@ func NewRaftCluster(cfg *types.Consensus, sub []byte) queue.Module { ...@@ -64,7 +66,7 @@ func NewRaftCluster(cfg *types.Consensus, sub []byte) queue.Module {
//TODO 当传入的参数异常时,返回给主函数的是个nil,这时候需要做异常处理 //TODO 当传入的参数异常时,返回给主函数的是个nil,这时候需要做异常处理
return nil return nil
} }
// 默认1000个Entry打一个snapshot // 默认10000个Entry打一个snapshot
if subcfg.DefaultSnapCount > 0 { if subcfg.DefaultSnapCount > 0 {
defaultSnapCount = uint64(subcfg.DefaultSnapCount) defaultSnapCount = uint64(subcfg.DefaultSnapCount)
snapshotCatchUpEntriesN = uint64(subcfg.DefaultSnapCount) snapshotCatchUpEntriesN = uint64(subcfg.DefaultSnapCount)
...@@ -77,6 +79,11 @@ func NewRaftCluster(cfg *types.Consensus, sub []byte) queue.Module { ...@@ -77,6 +79,11 @@ func NewRaftCluster(cfg *types.Consensus, sub []byte) queue.Module {
if subcfg.HeartbeatTick > 0 { if subcfg.HeartbeatTick > 0 {
heartbeatTick = int(subcfg.HeartbeatTick) heartbeatTick = int(subcfg.HeartbeatTick)
} }
// write empty block interval in second
if subcfg.EmptyBlockInterval > 0 {
emptyBlockInterval = subcfg.EmptyBlockInterval
}
var b *Client var b *Client
getSnapshot := func() ([]byte, error) { return b.getSnapshot() } getSnapshot := func() ([]byte, error) { return b.getSnapshot() }
// raft集群的建立,1. 初始化两条channel: propose channel用于客户端和raft底层交互, commit channel用于获取commit消息 // raft集群的建立,1. 初始化两条channel: propose channel用于客户端和raft底层交互, commit channel用于获取commit消息
...@@ -96,12 +103,13 @@ func NewRaftCluster(cfg *types.Consensus, sub []byte) queue.Module { ...@@ -96,12 +103,13 @@ func NewRaftCluster(cfg *types.Consensus, sub []byte) queue.Module {
//采用context来统一管理所有服务 //采用context来统一管理所有服务
ctx, stop := context.WithCancel(context.Background()) ctx, stop := context.WithCancel(context.Background())
// propose channel // propose channel
proposeC := make(chan *types.Block) proposeC := make(chan BlockInfo)
confChangeC = make(chan raftpb.ConfChange) confChangeC = make(chan raftpb.ConfChange)
commitC, errorC, snapshotterReady, validatorC := NewRaftNode(ctx, int(subcfg.NodeID), subcfg.IsNewJoinNode, peers, readOnlyPeers, addPeers, getSnapshot, proposeC, confChangeC) node, commitC, errorC, snapshotterReady, validatorC := NewRaftNode(ctx, int(subcfg.NodeID), subcfg.IsNewJoinNode, peers, readOnlyPeers, addPeers, getSnapshot, proposeC, confChangeC)
//启动raft删除节点操作监听 //启动raft删除节点操作监听
go serveHTTPRaftAPI(ctx, int(subcfg.RaftAPIPort), confChangeC, errorC) go serveHTTPRaftAPI(ctx, int(subcfg.RaftAPIPort), confChangeC, errorC)
// 监听commit channel,取block // 监听commit channel,取block
b = NewBlockstore(ctx, cfg, <-snapshotterReady, proposeC, commitC, errorC, validatorC, stop) b = NewBlockstore(ctx, cfg, <-snapshotterReady, proposeC, commitC, errorC, validatorC, stop)
node.SetClient(b)
return b return b
} }
...@@ -6,6 +6,7 @@ package raft ...@@ -6,6 +6,7 @@ package raft
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
...@@ -14,7 +15,6 @@ import ( ...@@ -14,7 +15,6 @@ import (
"sync" "sync"
"time" "time"
"github.com/33cn/chain33/types"
"github.com/coreos/etcd/etcdserver/stats" "github.com/coreos/etcd/etcdserver/stats"
"github.com/coreos/etcd/pkg/fileutil" "github.com/coreos/etcd/pkg/fileutil"
typec "github.com/coreos/etcd/pkg/types" typec "github.com/coreos/etcd/pkg/types"
...@@ -24,7 +24,6 @@ import ( ...@@ -24,7 +24,6 @@ import (
"github.com/coreos/etcd/snap" "github.com/coreos/etcd/snap"
"github.com/coreos/etcd/wal" "github.com/coreos/etcd/wal"
"github.com/coreos/etcd/wal/walpb" "github.com/coreos/etcd/wal/walpb"
"github.com/golang/protobuf/proto"
) )
var ( var (
...@@ -32,9 +31,10 @@ var ( ...@@ -32,9 +31,10 @@ var (
) )
type raftNode struct { type raftNode struct {
proposeC <-chan *types.Block client *Client
proposeC <-chan BlockInfo
confChangeC <-chan raftpb.ConfChange confChangeC <-chan raftpb.ConfChange
commitC chan<- *types.Block commitC chan<- *BlockInfo
errorC chan<- error errorC chan<- error
id int id int
bootstrapPeers []string bootstrapPeers []string
...@@ -65,13 +65,21 @@ type raftNode struct { ...@@ -65,13 +65,21 @@ type raftNode struct {
restartC chan struct{} restartC chan struct{}
} }
type Node struct {
*raftNode
}
func (node *Node) SetClient(client *Client) {
node.client = client
}
// NewRaftNode create raft node // NewRaftNode create raft node
func NewRaftNode(ctx context.Context, id int, join bool, peers []string, readOnlyPeers []string, addPeers []string, getSnapshot func() ([]byte, error), proposeC <-chan *types.Block, func NewRaftNode(ctx context.Context, id int, join bool, peers []string, readOnlyPeers []string, addPeers []string, getSnapshot func() ([]byte, error), proposeC <-chan BlockInfo,
confChangeC <-chan raftpb.ConfChange) (<-chan *types.Block, <-chan error, <-chan *snap.Snapshotter, <-chan bool) { confChangeC <-chan raftpb.ConfChange) (*Node, <-chan *BlockInfo, <-chan error, <-chan *snap.Snapshotter, <-chan bool) {
rlog.Info("Enter consensus raft") rlog.Info("Enter consensus raft")
// commit channel // commit channel
commitC := make(chan *types.Block) commitC := make(chan *BlockInfo)
errorC := make(chan error) errorC := make(chan error)
rc := &raftNode{ rc := &raftNode{
proposeC: proposeC, proposeC: proposeC,
...@@ -94,7 +102,7 @@ func NewRaftNode(ctx context.Context, id int, join bool, peers []string, readOnl ...@@ -94,7 +102,7 @@ func NewRaftNode(ctx context.Context, id int, join bool, peers []string, readOnl
} }
go rc.startRaft() go rc.startRaft()
return commitC, errorC, rc.snapshotterReady, rc.validatorC return &Node{rc}, commitC, errorC, rc.snapshotterReady, rc.validatorC
} }
// 启动raft节点 // 启动raft节点
...@@ -224,7 +232,7 @@ func (rc *raftNode) serveChannels() { ...@@ -224,7 +232,7 @@ func (rc *raftNode) serveChannels() {
if !ok { if !ok {
rc.proposeC = nil rc.proposeC = nil
} else { } else {
out, err := proto.Marshal(prop) out, err := json.Marshal(prop)
if err != nil { if err != nil {
rlog.Error(fmt.Sprintf("failed to marshal block:%v ", err.Error())) rlog.Error(fmt.Sprintf("failed to marshal block:%v ", err.Error()))
} }
...@@ -257,6 +265,10 @@ func (rc *raftNode) serveChannels() { ...@@ -257,6 +265,10 @@ func (rc *raftNode) serveChannels() {
case <-ticker.C: case <-ticker.C:
rc.node.Tick() rc.node.Tick()
case rd := <-rc.node.Ready(): case rd := <-rc.node.Ready():
if !rc.checkEntries(rd.Entries) || !rc.checkEntries(rd.CommittedEntries) {
rc.stop()
return
}
rc.wal.Save(rd.HardState, rd.Entries) rc.wal.Save(rd.HardState, rd.Entries)
if !raft.IsEmptySnap(rd.Snapshot) { if !raft.IsEmptySnap(rd.Snapshot) {
rc.saveSnap(rd.Snapshot) rc.saveSnap(rd.Snapshot)
...@@ -283,6 +295,25 @@ func (rc *raftNode) serveChannels() { ...@@ -283,6 +295,25 @@ func (rc *raftNode) serveChannels() {
} }
} }
func (rc *raftNode) checkEntries(ents []raftpb.Entry) bool {
for i := range ents {
if ents[i].Type == raftpb.EntryNormal && len(ents[i].Data) != 0 {
info := &BlockInfo{}
if err := json.Unmarshal(ents[i].Data, info); err != nil {
rlog.Error("checkEntries Unmarshal BlockInfo fail", "err", err)
return false
}
if rc.client != nil {
if !rc.client.CheckBlockInfo(info) {
rlog.Error("checkEntries CheckBlockInfo fail")
return false
}
}
}
}
return true
}
func (rc *raftNode) updateValidator() { func (rc *raftNode) updateValidator() {
//TODO 这块监听后期需要根据场景进行优化? //TODO 这块监听后期需要根据场景进行优化?
...@@ -325,6 +356,7 @@ func (rc *raftNode) updateValidator() { ...@@ -325,6 +356,7 @@ func (rc *raftNode) updateValidator() {
} }
} }
func (rc *raftNode) Status() raft.Status { func (rc *raftNode) Status() raft.Status {
rc.stopMu.RLock() rc.stopMu.RLock()
defer rc.stopMu.RUnlock() defer rc.stopMu.RUnlock()
...@@ -383,15 +415,13 @@ func (rc *raftNode) maybeTriggerSnapshot() { ...@@ -383,15 +415,13 @@ func (rc *raftNode) maybeTriggerSnapshot() {
return return
} }
appliedIndex := rc.appliedIndex rlog.Info(fmt.Sprintf("start snapshot [applied index: %d | last snapshot index: %d]", rc.appliedIndex, rc.snapshotIndex))
snapshotIndex := rc.snapshotIndex data, err := rc.getSnapshot()
confState := rc.confState
rlog.Info(fmt.Sprintf("start snapshot [applied index: %d | last snapshot index: %d]", appliedIndex, snapshotIndex))
ents, err := rc.raftStorage.Entries(appliedIndex, appliedIndex+1, 2)
if err != nil { if err != nil {
rlog.Error(fmt.Sprintf("Err happened when get snapshot:%v", err.Error())) rlog.Error("getSnapshot fail", "err", err)
panic(err)
} }
snapShot, err := rc.raftStorage.CreateSnapshot(appliedIndex, &confState, ents[0].Data) snapShot, err := rc.raftStorage.CreateSnapshot(rc.appliedIndex, &rc.confState, data)
if err != nil { if err != nil {
panic(err) panic(err)
} }
...@@ -400,15 +430,15 @@ func (rc *raftNode) maybeTriggerSnapshot() { ...@@ -400,15 +430,15 @@ func (rc *raftNode) maybeTriggerSnapshot() {
} }
compactIndex := uint64(1) compactIndex := uint64(1)
if appliedIndex > snapshotCatchUpEntriesN { if rc.appliedIndex > snapshotCatchUpEntriesN {
compactIndex = appliedIndex - snapshotCatchUpEntriesN compactIndex = rc.appliedIndex - snapshotCatchUpEntriesN
} }
if err := rc.raftStorage.Compact(compactIndex); err != nil { if err := rc.raftStorage.Compact(compactIndex); err != nil {
panic(err) panic(err)
} }
rlog.Info(fmt.Sprintf("compacted log at index %d", compactIndex)) rlog.Info(fmt.Sprintf("compacted log at index %d", compactIndex))
rc.snapshotIndex = appliedIndex rc.snapshotIndex = rc.appliedIndex
} }
func (rc *raftNode) publishSnapshot(snapshotToSave raftpb.Snapshot) { func (rc *raftNode) publishSnapshot(snapshotToSave raftpb.Snapshot) {
...@@ -487,12 +517,13 @@ func (rc *raftNode) publishEntries(ents []raftpb.Entry) bool { ...@@ -487,12 +517,13 @@ func (rc *raftNode) publishEntries(ents []raftpb.Entry) bool {
break break
} }
// 解码 // 解码
block := &types.Block{} info := &BlockInfo{}
if err := proto.Unmarshal(ents[i].Data, block); err != nil { if err := json.Unmarshal(ents[i].Data, info); err != nil {
rlog.Error(fmt.Sprintf("failed to unmarshal: %v", err.Error())) rlog.Error("Unmarshal BlockInfo fail", "err", err)
break
} }
select { select {
case rc.commitC <- block: case rc.commitC <- info:
case <-rc.ctx.Done(): case <-rc.ctx.Done():
return false return false
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package main package main
import ( import (
"encoding/json"
"flag" "flag"
"fmt" "fmt"
"log" "log"
...@@ -18,7 +19,6 @@ import ( ...@@ -18,7 +19,6 @@ import (
raftsnap "github.com/coreos/etcd/snap" raftsnap "github.com/coreos/etcd/snap"
"github.com/coreos/etcd/wal" "github.com/coreos/etcd/wal"
"github.com/coreos/etcd/wal/walpb" "github.com/coreos/etcd/wal/walpb"
"github.com/golang/protobuf/proto"
) )
func main() { func main() {
...@@ -94,12 +94,12 @@ func main() { ...@@ -94,12 +94,12 @@ func main() {
break break
} }
// 解码 // 解码
block := &Block{} info := &BlockInfo{}
if err := proto.Unmarshal(e.Data, block); err != nil { if err := json.Unmarshal(e.Data, info); err != nil {
log.Printf("failed to unmarshal: %v", err) log.Printf("failed to unmarshal: %v", err)
break break
} }
msg = fmt.Sprintf("%s\t BlockHeight:%d", msg, block.Height) msg = fmt.Sprintf("%s\tHeight=%d\tHash=%s", msg, info.Height, info.Hash)
case raftpb.EntryConfChange: case raftpb.EntryConfChange:
msg = fmt.Sprintf("%s\tconf", msg) msg = fmt.Sprintf("%s\tconf", msg)
var r raftpb.ConfChange var r raftpb.ConfChange
...@@ -133,21 +133,7 @@ func genIDSlice(a []uint64) []types.ID { ...@@ -133,21 +133,7 @@ func genIDSlice(a []uint64) []types.ID {
return ids return ids
} }
// Block struct type BlockInfo struct {
type Block struct { Height int64 `json:"height"`
Version int64 `protobuf:"varint,1,opt,name=version" json:"version,omitempty"` Hash string `json:"hash"`
ParentHash []byte `protobuf:"bytes,2,opt,name=parentHash,proto3" json:"parentHash,omitempty"`
TxHash []byte `protobuf:"bytes,3,opt,name=txHash,proto3" json:"txHash,omitempty"`
StateHash []byte `protobuf:"bytes,4,opt,name=stateHash,proto3" json:"stateHash,omitempty"`
Height int64 `protobuf:"varint,5,opt,name=height" json:"height,omitempty"`
BlockTime int64 `protobuf:"varint,6,opt,name=blockTime" json:"blockTime,omitempty"`
//Signature *Signature `protobuf:"bytes,8,opt,name=signature" json:"signature,omitempty"`
//Txs []*Transaction `protobuf:"bytes,7,rep,name=txs" json:"txs,omitempty"`
} }
// Reset method
func (m *Block) Reset() { *m = Block{} }
func (m *Block) String() string { return proto.CompactTextString(m) }
// ProtoMessage method
func (*Block) ProtoMessage() {}
all:
chmod +x ./build.sh
./build.sh $(OUT) $(FLAG)
\ No newline at end of file
#!/usr/bin/env bash
strpwd=$(pwd)
strcmd=${strpwd##*dapp/}
strapp=${strcmd%/cmd*}
OUT_TESTDIR="${1}/dapptest/$strapp"
mkdir -p "${OUT_TESTDIR}"
cp ./test/test-rpc.sh "${OUT_TESTDIR}"
#!/usr/bin/env bash
# shellcheck disable=SC2128
set -e
set -o pipefail
# shellcheck source=/dev/null
source ../dapp-test-common.sh
function main() {
echo "Collateralize cases has integrated in Issuance test"
}
chain33_debug_function main "$1"
This diff is collapsed.
// 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"
pty "github.com/33cn/plugin/plugin/dapp/collateralize/types"
)
var clog = log.New("module", "execs.collateralize")
var driverName = pty.CollateralizeX
func InitExecType() {
ety := types.LoadExecutorType(driverName)
ety.InitFuncList(types.ListMethod(&Collateralize{}))
}
// Init collateralize
func Init(name string, cfg *types.Chain33Config, sub []byte) {
driverName := GetName()
if name != driverName {
panic("system dapp can't be rename")
}
if sub != nil {
types.MustDecode(sub, &cfg)
}
drivers.Register(cfg, driverName, newCollateralize, cfg.GetDappFork(driverName, "Enable"))
InitExecType()
}
// GetName for Collateralize
func GetName() string {
return newCollateralize().GetName()
}
// Collateralize driver
type Collateralize struct {
drivers.DriverBase
}
func newCollateralize() drivers.Driver {
c := &Collateralize{}
c.SetChild(c)
c.SetExecutorType(types.LoadExecutorType(driverName))
return c
}
// GetDriverName for Collateralize
func (c *Collateralize) GetDriverName() string {
return pty.CollateralizeX
}
// CheckReceiptExecOk return true to check if receipt ty is ok
func (c *Collateralize) CheckReceiptExecOk() bool {
return true
}
// ExecutorOrder 设置localdb的EnableRead
func (c *Collateralize) ExecutorOrder() int64 {
cfg := c.GetAPI().GetConfig()
if cfg.IsFork(c.GetHeight(), "ForkLocalDBAccess") {
return drivers.ExecLocalSameTime
}
return c.DriverBase.ExecutorOrder()
}
This diff is collapsed.
This diff is collapsed.
// 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
/*
waiting for update
*/
// 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/collateralize/types"
)
// Exec_Create Action
func (c *Collateralize) Exec_Create(payload *pty.CollateralizeCreate, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewCollateralizeAction(c, tx, index)
return actiondb.CollateralizeCreate(payload)
}
// Exec_Borrow Action
func (c *Collateralize) Exec_Borrow(payload *pty.CollateralizeBorrow, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewCollateralizeAction(c, tx, index)
return actiondb.CollateralizeBorrow(payload)
}
// Exec_Repay Action
func (c *Collateralize) Exec_Repay(payload *pty.CollateralizeRepay, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewCollateralizeAction(c, tx, index)
return actiondb.CollateralizeRepay(payload)
}
// Exec_Repay Action
func (c *Collateralize) Exec_Append(payload *pty.CollateralizeAppend, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewCollateralizeAction(c, tx, index)
return actiondb.CollateralizeAppend(payload)
}
// Exec_Feed Action
func (c *Collateralize) Exec_Feed(payload *pty.CollateralizeFeed, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewCollateralizeAction(c, tx, index)
return actiondb.CollateralizeFeed(payload)
}
// Exec_Retrieve Action
func (c *Collateralize) Exec_Retrieve(payload *pty.CollateralizeRetrieve, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewCollateralizeAction(c, tx, index)
return actiondb.CollateralizeRetrieve(payload)
}
// Exec_Close Action
func (c *Collateralize) Exec_Manage(payload *pty.CollateralizeManage, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewCollateralizeAction(c, tx, index)
return actiondb.CollateralizeManage(payload)
}
// 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/collateralize/types"
)
func (c *Collateralize) execDelLocal(tx *types.Transaction, receiptData *types.ReceiptData) (*types.LocalDBSet, error) {
kvs, err := c.DelRollbackKV(tx, tx.Execer)
if err != nil {
return nil, err
}
dbSet := &types.LocalDBSet{}
dbSet.KV = append(dbSet.KV, kvs...)
return dbSet, nil
}
// ExecDelLocal_Create Action
func (c *Collateralize) ExecDelLocal_Create(payload *pty.CollateralizeCreate, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, receiptData)
}
// ExecDelLocal_Borrow Action
func (c *Collateralize) ExecDelLocal_Borrow(payload *pty.CollateralizeBorrow, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, receiptData)
}
// ExecDelLocal_Repay Action
func (c *Collateralize) ExecDelLocal_Repay(payload *pty.CollateralizeRepay, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, receiptData)
}
// ExecDelLocal_Append Action
func (c *Collateralize) ExecDelLocal_Append(payload *pty.CollateralizeAppend, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, receiptData)
}
// ExecDelLocal_Feed Action
func (c *Collateralize) ExecDelLocal_Feed(payload *pty.CollateralizeFeed, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, receiptData)
}
// ExecDelLocal_Retrieve Action
func (c *Collateralize) ExecDelLocal_Retrieve(payload *pty.CollateralizeRetrieve, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, receiptData)
}
// ExecDelLocal_Manage Action
func (c *Collateralize) ExecDelLocal_Manage(payload *pty.CollateralizeManage, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, 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/common"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/collateralize/types"
)
func (c *Collateralize) execLocal(tx *types.Transaction, receipt *types.ReceiptData) (*types.LocalDBSet, error) {
set := &types.LocalDBSet{}
for _, item := range receipt.Logs {
if item.Ty >= pty.TyLogCollateralizeCreate && item.Ty <= pty.TyLogCollateralizeRetrieve {
var collateralizeLog pty.ReceiptCollateralize
err := types.Decode(item.Log, &collateralizeLog)
if err != nil {
return nil, err
}
if item.Ty == pty.TyLogCollateralizeCreate || item.Ty == pty.TyLogCollateralizeRetrieve {
collTable := pty.NewCollateralizeTable(c.GetLocalDB())
err = collTable.Replace(&pty.ReceiptCollateralize{CollateralizeId: collateralizeLog.CollateralizeId, Status: collateralizeLog.Status,
AccountAddr: collateralizeLog.AccountAddr})
if err != nil {
return nil, err
}
kvs, err := collTable.Save()
if err != nil {
return nil, err
}
set.KV = append(set.KV, kvs...)
} else {
recordTable := pty.NewRecordTable(c.GetLocalDB())
err = recordTable.Replace(&pty.ReceiptCollateralize{CollateralizeId: collateralizeLog.CollateralizeId, Status: collateralizeLog.Status,
RecordId: collateralizeLog.RecordId, AccountAddr: collateralizeLog.AccountAddr})
if err != nil {
return nil, err
}
kvs, err := recordTable.Save()
if err != nil {
return nil, err
}
set.KV = append(set.KV, kvs...)
}
}
}
set.KV = c.AddRollbackKV(tx, []byte(pty.CollateralizeX), set.KV)
return set, nil
}
// ExecLocal_Create Action
func (c *Collateralize) ExecLocal_Create(payload *pty.CollateralizeCreate, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, receiptData)
}
// ExecLocal_Borrow Action
func (c *Collateralize) ExecLocal_Borrow(payload *pty.CollateralizeBorrow, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, receiptData)
}
// ExecLocal_Repay Action
func (c *Collateralize) ExecLocal_Repay(payload *pty.CollateralizeRepay, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, receiptData)
}
// ExecLocal_Repay Action
func (c *Collateralize) ExecLocal_Append(payload *pty.CollateralizeAppend, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, receiptData)
}
// ExecLocal_Feed Action
func (c *Collateralize) ExecLocal_Feed(payload *pty.CollateralizeFeed, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, receiptData)
}
// ExecLocal_Retrieve Action
func (c *Collateralize) ExecLocal_Retrieve(payload *pty.CollateralizeRetrieve, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, receiptData)
}
// ExecLocal_Manage Action
func (c *Collateralize) ExecLocal_Manage(payload *pty.CollateralizeManage, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, 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"
pty "github.com/33cn/plugin/plugin/dapp/collateralize/types"
)
func (c *Collateralize) Query_CollateralizeInfoByID(req *pty.ReqCollateralizeInfo) (types.Message, error) {
coll, err := queryCollateralizeByID(c.GetStateDB(), req.CollateralizeId)
if err != nil {
clog.Error("Query_CollateralizeInfoByID", "id", req.CollateralizeId, "error", err)
return nil, err
}
info := &pty.RepCollateralizeCurrentInfo{
Status: coll.Status,
TotalBalance: coll.TotalBalance,
DebtCeiling: coll.DebtCeiling,
LiquidationRatio: coll.LiquidationRatio,
StabilityFeeRatio: coll.StabilityFeeRatio,
CreateAddr: coll.CreateAddr,
Balance: coll.Balance,
Period: coll.Period,
CollateralizeId: coll.CollateralizeId,
CollBalance: coll.CollBalance,
}
info.BorrowRecords = append(info.BorrowRecords, coll.BorrowRecords...)
info.BorrowRecords = append(info.BorrowRecords, coll.InvalidRecords...)
return info, nil
}
func (c *Collateralize) Query_CollateralizeInfoByIDs(req *pty.ReqCollateralizeInfos) (types.Message, error) {
infos := &pty.RepCollateralizeCurrentInfos{}
for _, id := range req.CollateralizeIds {
coll, err := queryCollateralizeByID(c.GetStateDB(), id)
if err != nil {
clog.Error("Query_CollateralizeInfoByID", "id", id, "error", err)
return nil, err
}
info := &pty.RepCollateralizeCurrentInfo{
Status: coll.Status,
TotalBalance: coll.TotalBalance,
DebtCeiling: coll.DebtCeiling,
LiquidationRatio: coll.LiquidationRatio,
StabilityFeeRatio: coll.StabilityFeeRatio,
CreateAddr: coll.CreateAddr,
Balance: coll.Balance,
Period: coll.Period,
CollateralizeId: coll.CollateralizeId,
CollBalance: coll.CollBalance,
}
info.BorrowRecords = append(info.BorrowRecords, coll.BorrowRecords...)
info.BorrowRecords = append(info.BorrowRecords, coll.InvalidRecords...)
infos.Infos = append(infos.Infos, info)
}
return infos, nil
}
func (c *Collateralize) Query_CollateralizeByStatus(req *pty.ReqCollateralizeByStatus) (types.Message, error) {
ids := &pty.RepCollateralizeIDs{}
collIDRecords, err := queryCollateralizeByStatus(c.GetLocalDB(), req.Status, req.CollID)
if err != nil {
clog.Error("Query_CollateralizeByStatus", "get collateralize record error", err)
return nil, err
}
ids.IDs = append(ids.IDs, collIDRecords...)
return ids, nil
}
func (c *Collateralize) Query_CollateralizeByAddr(req *pty.ReqCollateralizeByAddr) (types.Message, error) {
ids := &pty.RepCollateralizeIDs{}
collIDRecords, err := queryCollateralizeByAddr(c.GetLocalDB(), req.Addr, req.Status, req.CollID)
if err != nil {
clog.Error("Query_CollateralizeByAddr", "get collateralize record error", err)
return nil, err
}
ids.IDs = append(ids.IDs, collIDRecords...)
return ids, nil
}
func (c *Collateralize) Query_CollateralizeRecordByID(req *pty.ReqCollateralizeRecord) (types.Message, error) {
ret := &pty.RepCollateralizeRecord{}
issuRecord, err := queryCollateralizeRecordByID(c.GetStateDB(), req.CollateralizeId, req.RecordId)
if err != nil {
clog.Error("Query_IssuanceRecordByID", "get collateralize record error", err)
return nil, err
}
ret.Record = issuRecord
return ret, nil
}
func (c *Collateralize) Query_CollateralizeRecordByAddr(req *pty.ReqCollateralizeRecordByAddr) (types.Message, error) {
ret := &pty.RepCollateralizeRecords{}
records, err := queryCollateralizeRecordByAddr(c.GetStateDB(), c.GetLocalDB(), req.Addr, req.Status, req.CollateralizeId, req.RecordId)
if err != nil {
clog.Error("Query_CollateralizeRecordByAddr", "get collateralize record error", err)
return nil, err
}
if req.Status == 0 {
ret.Records = records
} else {
for _, record := range records {
if record.Status == req.Status {
ret.Records = append(ret.Records, record)
}
}
}
return ret, nil
}
func (c *Collateralize) Query_CollateralizeRecordByStatus(req *pty.ReqCollateralizeRecordByStatus) (types.Message, error) {
ret := &pty.RepCollateralizeRecords{}
records, err := queryCollateralizeRecordByStatus(c.GetStateDB(), c.GetLocalDB(), req.Status, req.CollateralizeId, req.RecordId)
if err != nil {
clog.Error("Query_CollateralizeRecordByStatus", "get collateralize record error", err)
return nil, err
}
ret.Records = records
return ret, nil
}
func (c *Collateralize) Query_CollateralizeConfig(req *pty.ReqCollateralizeRecordByAddr) (types.Message, error) {
config, err := getCollateralizeConfig(c.GetStateDB())
if err != nil {
clog.Error("Query_CollateralizeConfig", "get collateralize config error", err)
return nil, err
}
balance, err := getCollBalance(config.TotalBalance, c.GetLocalDB(), c.GetStateDB())
if err != nil {
clog.Error("Query_CollateralizeInfoByID", "error", err)
return nil, err
}
ret := &pty.RepCollateralizeConfig{
TotalBalance: config.TotalBalance,
DebtCeiling: config.DebtCeiling,
LiquidationRatio: config.LiquidationRatio,
StabilityFeeRatio: config.StabilityFeeRatio,
Period: config.Period,
Balance: balance,
CurrentTime: config.CurrentTime,
}
return ret, nil
}
func (c *Collateralize) Query_CollateralizePrice(req *pty.ReqCollateralizeRecordByAddr) (types.Message, error) {
price, err := getLatestPrice(c.GetStateDB())
if err != nil {
clog.Error("Query_CollateralizePrice", "error", err)
return nil, err
}
return &pty.RepCollateralizePrice{Price: price}, nil
}
func (c *Collateralize) Query_CollateralizeUserBalance(req *pty.ReqCollateralizeRecordByAddr) (types.Message, error) {
balance, err := queryCollateralizeUserBalance(c.GetStateDB(), c.GetLocalDB(), req.Addr)
if err != nil {
clog.Error("Query_CollateralizeRecordByAddr", "get collateralize record error", err)
return nil, err
}
return &pty.RepCollateralizeUserBalance{Balance: balance}, 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 collateralize
import (
"github.com/33cn/chain33/pluginmgr"
"github.com/33cn/plugin/plugin/dapp/collateralize/commands"
"github.com/33cn/plugin/plugin/dapp/collateralize/executor"
"github.com/33cn/plugin/plugin/dapp/collateralize/types"
)
func init() {
pluginmgr.Register(&pluginmgr.PluginBase{
Name: types.CollateralizeX,
ExecName: executor.GetName(),
Exec: executor.Init,
Cmd: commands.CollateralizeCmd,
})
}
all:
sh ./create_protobuf.sh
syntax = "proto3";
package types;
// 放贷信息
message Collateralize {
string collateralizeId = 1; //放贷ID,一期放贷对应一个ID
int64 totalBalance = 2; //当期放贷的总金额(ccny)
int64 debtCeiling = 3; //单用户可借出的限额(ccny)
int64 liquidationRatio = 4; //清算比例
int64 stabilityFeeRatio = 5; //稳定费率
string createAddr = 6; //创建人地址
int64 balance = 7; //放贷剩余金额(ccny)
repeated BorrowRecord borrowRecords = 8; //借贷记录
repeated BorrowRecord InvalidRecords = 9; //失效的借贷记录
int32 status = 10;//当期借贷的状态,是否关闭
int64 latestLiquidationPrice = 11;//最高清算价格
int64 period = 12;//借贷最大期限
int64 latestExpireTime = 13;//最近超期时间
int64 collBalance = 14;//抵押bty
int32 preStatus = 15;//上一个状态
}
// 借出记录
message BorrowRecord {
string accountAddr = 1; //借贷人地址
int64 startTime = 2; //借贷时间
int64 collateralValue = 3; //抵押物价值(bty)
int64 collateralPrice = 4; //抵押物价格
int64 debtValue = 5; //债务价值(ccny)
int64 liquidationPrice = 6; //抵押物清算价格
int32 status = 7; //抵押状态,是否被清算
int64 liquidateTime = 8; //清算时间
int64 expireTime = 9; //超时清算时间
int32 preStatus = 10;//上一次抵押状态,用于告警恢复
string recordId = 11;//借贷id,标识一次借出记录
string collateralizeId = 12;//放贷id
}
// 资产价格记录
message AssetPriceRecord {
int64 recordTime = 1; //价格记录时间
int64 btyPrice = 2; //bty价格
int64 btcPrice = 3; //btc价格
int64 ethPrice = 4; //eth价格
}
// action
message CollateralizeAction {
oneof value {
CollateralizeCreate create = 1; //创建一期借贷
CollateralizeBorrow borrow = 2; //借贷
CollateralizeRepay repay = 3; //清算
CollateralizeAppend append = 4; //追加
CollateralizeFeed feed = 5; //喂价
CollateralizeRetrieve retrieve = 6; //收回
CollateralizeManage manage = 7; //全局配置
}
int32 ty = 10;
}
message CollateralizeManage {
int64 debtCeiling = 1; //单用户可借出的限额(ccny)
int64 liquidationRatio = 2; //清算比例
int64 stabilityFeeRatio = 3; //稳定费
int64 period = 4; //合约期限
int64 totalBalance = 5; //放贷总量
int64 currentTime = 6; //设置时间
}
message CollateralizeAddr {
repeated string superAddrs = 1; //大户地址
}
// 创建放贷
message CollateralizeCreate {
int64 totalBalance = 1; //可借贷总金额
}
// 质押借出
message CollateralizeBorrow {
string collateralizeId = 1; //借贷期数ID
int64 value = 2; //借贷价值(ccny)
}
// 质押清算
message CollateralizeRepay {
string collateralizeId = 1; //借贷期数ID
string recordId = 2; //借贷ID
}
// 追加抵押物
message CollateralizeAppend {
string collateralizeId = 1; //借贷期数ID
string recordId = 2; //借贷ID
int64 collateralValue = 3; //追加价值(bty)
}
// 喂价
message CollateralizeFeed {
int32 collType = 1; //抵押物价格类型(1,bty,2,btc,3,eth...)
repeated int64 price = 2; //喂价
repeated int64 volume = 3; //成交量
}
// 收回
message CollateralizeRetrieve {
string collateralizeId = 1; //借贷期数ID
int64 balance = 2; //收回金额
}
// exec_local 放贷信息
message ReceiptCollateralize {
string collateralizeId = 1;
string accountAddr = 3;
string recordId = 4;
int32 status = 5;
}
// exec_local 放贷记录信息列表
message CollateralizeRecords {
repeated ReceiptCollateralize records = 1;
}
// 根据ID查询一期放贷信息
message ReqCollateralizeInfo {
string collateralizeId = 1;
}
// 返回一期放贷信息
message RepCollateralizeCurrentInfo {
int32 status = 1;//当期借贷的状态,是否关闭
int64 totalBalance = 2; //当期可借贷的总金额(ccny)
int64 debtCeiling = 3; //单用户可借出的限额(ccny)
int64 liquidationRatio = 4; //清算比例
int64 stabilityFeeRatio = 5; //稳定费
string createAddr = 6; //创建人地址
int64 balance = 7; //剩余可借贷金额(ccny)
int64 period = 8; //合约期限
string collateralizeId = 9; //放贷ID
int64 collBalance = 10;//抵押bty
repeated BorrowRecord borrowRecords = 11;//借贷记录
}
// 根据ID列表查询多期放贷信息
message ReqCollateralizeInfos {
repeated string collateralizeIds = 1;
}
// 返回多期放贷信息
message RepCollateralizeCurrentInfos {
repeated RepCollateralizeCurrentInfo infos = 1;
}
// 根据放贷状态查询
message ReqCollateralizeByStatus {
int32 status = 1;
string collID = 2;
}
// 根据用户地址查询
message ReqCollateralizeByAddr {
string addr = 1;
int32 status = 2;
string collID = 3;
}
// 返回放贷ID列表
message RepCollateralizeIDs {
repeated string IDs = 1;
}
// 根据地址和借贷ID混合查询具体借贷记录
message ReqCollateralizeRecordByAddr {
string collateralizeId = 1;
string addr = 2;
int32 status = 3;
string recordId = 4;
}
// 根据状态和借贷ID混合查询具体借贷记录
message ReqCollateralizeRecordByStatus {
string collateralizeId = 1;
int32 status = 2;
string recordId = 3;
}
// 返回借贷记录
message RepCollateralizeRecords {
repeated BorrowRecord records = 1;
}
// 精确查找借贷记录
message ReqCollateralizeRecord {
string collateralizeId = 1;
string recordId = 2;
}
// 返回借贷记录
message RepCollateralizeRecord {
BorrowRecord record = 1;
}
// 返回放贷配置
message RepCollateralizeConfig {
int64 debtCeiling = 1; //单用户可借出的限额(ccny)
int64 liquidationRatio = 2; //清算比例
int64 stabilityFeeRatio = 3; //稳定费
int64 period = 4; //合约期限
int64 totalBalance = 5; //放贷总量
int64 balance = 6; //剩余放贷额度
int64 currentTime = 7; //设置时间
}
// 返回最新抵押物价格
message RepCollateralizePrice {
int64 price = 1; //当前抵押物最新价格
}
// 返回用户借贷总额
message RepCollateralizeUserBalance {
int64 balance = 1; //返回用户借贷总额
}
\ No newline at end of file
#!/bin/sh
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/"
## 借贷合约表结构
### 放贷表coller表结构
字段名称|类型|说明
---|---|---
collateralizeId|string|放贷ID,主键
accountAddr|string|大户地址
recordId|string|借贷ID
status|int32|放贷状态(1:已放贷 2:已收回)
### 放贷表coller表索引
索引名|说明
---|---
status|根据放贷状态查询放贷ID
addr|根据大户地址查询放贷ID
addr_status|根据放贷状态和大户地址查询放贷ID
### 借贷表borrow表结构
字段名称|类型|说明
---|---|---
recordId|string|借贷ID,主键
collateralizeId|string|放贷ID
accountAddr|string|用户地址
status|int32|借贷状态(1:已发行 2:价格清算告警 3:价格清算 4:超时清算告警 5:超时清算 6:已清算)
### 放贷表borrow表索引
索引名|说明
---|---
status|根据借贷状态查询借贷ID
addr|根据大户地址查询借贷ID
addr_status|根据借贷状态和用户地址查询借贷ID
id_status|根据放贷ID和借贷状态查询借贷ID
id_addr|根据放贷ID和用户地址查询借贷ID
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
// 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"
// Errors for lottery
var (
ErrRiskParam = errors.New("ErrRiskParam")
ErrCollateralizeStatus = errors.New("ErrCollateralizeStatus")
ErrCollateralizeExceedDebtCeiling = errors.New("ErrCollateralizeExceedDebtCeiling")
ErrPriceInvalid = errors.New("ErrPriceInvalid")
ErrAssetType = errors.New("ErrAssetType")
ErrRecordNotExist = errors.New("ErrRecordNotExist")
ErrCollateralizeErrCloser = errors.New("ErrCollateralizeErrCloser")
ErrRepayValueInsufficient = errors.New("ErrRepayValueInsufficient")
ErrCollateralizeAccountExist = errors.New("ErrCollateralizeAccountExist")
ErrCollateralizeLowBalance = errors.New("ErrCollateralizeLowBalance")
ErrCollateralizeBalanceInvalid = errors.New("ErrCollateralizeBalanceInvalid")
ErrPermissionDeny = errors.New("ErrPermissionDeny")
ErrCollateralizeRecordNotEmpty = errors.New("ErrCollateralizeRecordNotEmpty")
)
package types
import (
"fmt"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/db/table"
"github.com/33cn/chain33/types"
)
var opt = &table.Option{
Prefix: "LODB-collateralize",
Name: "coller",
Primary: "collateralizeid",
Index: []string{"status", "addr", "addr_status"},
}
//NewTable 新建表
func NewCollateralizeTable(kvdb db.KV) *table.Table {
rowmeta := NewCollatetalizeRow()
table, err := table.NewTable(rowmeta, kvdb, opt)
if err != nil {
panic(err)
}
return table
}
//CollatetalizeRow table meta 结构
type CollatetalizeRow struct {
*ReceiptCollateralize
}
//NewIssuanceRow 新建一个meta 结构
func NewCollatetalizeRow() *CollatetalizeRow {
return &CollatetalizeRow{ReceiptCollateralize: &ReceiptCollateralize{}}
}
//CreateRow 新建数据行
func (tx *CollatetalizeRow) CreateRow() *table.Row {
return &table.Row{Data: &ReceiptCollateralize{}}
}
//SetPayload 设置数据
func (tx *CollatetalizeRow) SetPayload(data types.Message) error {
if txdata, ok := data.(*ReceiptCollateralize); ok {
tx.ReceiptCollateralize = txdata
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (tx *CollatetalizeRow) Get(key string) ([]byte, error) {
if key == "collateralizeid" {
return []byte(tx.CollateralizeId), nil
} else if key == "status" {
return []byte(fmt.Sprintf("%2d", tx.Status)), nil
} else if key == "addr" {
return []byte(tx.AccountAddr), nil
} else if key == "addr_status" {
return []byte(fmt.Sprintf("%s:%2d", tx.AccountAddr, tx.Status)), nil
}
return nil, types.ErrNotFound
}
var optRecord = &table.Option{
Prefix: "LODB-collateralize",
Name: "borrow",
Primary: "borrowid",
Index: []string{"status", "addr", "addr_status", "id_status", "id_addr"},
}
// NewRecordTable 借贷记录表
func NewRecordTable(kvdb db.KV) *table.Table {
rowmeta := NewRecordRow()
table, err := table.NewTable(rowmeta, kvdb, optRecord)
if err != nil {
panic(err)
}
return table
}
//CollatetalizeRow table meta 结构
type CollateralizeRecordRow struct {
*ReceiptCollateralize
}
//NewIssuanceRow 新建一个meta 结构
func NewRecordRow() *CollateralizeRecordRow {
return &CollateralizeRecordRow{ReceiptCollateralize: &ReceiptCollateralize{}}
}
//CreateRow 新建数据行
func (tx *CollateralizeRecordRow) CreateRow() *table.Row {
return &table.Row{Data: &ReceiptCollateralize{}}
}
//SetPayload 设置数据
func (tx *CollateralizeRecordRow) SetPayload(data types.Message) error {
if txdata, ok := data.(*ReceiptCollateralize); ok {
tx.ReceiptCollateralize = txdata
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (tx *CollateralizeRecordRow) Get(key string) ([]byte, error) {
if key == "borrowid" {
return []byte(tx.RecordId), nil
} else if key == "status" {
return []byte(fmt.Sprintf("%2d", tx.Status)), nil
} else if key == "addr" {
return []byte(tx.AccountAddr), nil
} else if key == "addr_status" {
return []byte(fmt.Sprintf("%s:%2d", tx.AccountAddr, tx.Status)), nil
} else if key == "id_status" {
return []byte(fmt.Sprintf("%s:%2d", tx.CollateralizeId, tx.Status)), nil
} else if key == "id_addr" {
return []byte(fmt.Sprintf("%s:%s", tx.CollateralizeId, tx.AccountAddr)), nil
}
return nil, types.ErrNotFound
}
// 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
// CollateralizeCreateTx for construction
type CollateralizeCreateTx struct {
TotalBalance float64 `json:"totalBalance"`
Fee int64 `json:"fee"`
}
// CollateralizeBorrowTx for construction
type CollateralizeBorrowTx struct {
CollateralizeID string `json:"collateralizeId"`
Value float64 `json:"value"`
Fee int64 `json:"fee"`
}
// CollateralizeRepayTx for construction
type CollateralizeRepayTx struct {
CollateralizeID string `json:"collateralizeId"`
RecordID string `json:"recordID"`
Fee int64 `json:"fee"`
}
// CollateralizeAppednTx for construction
type CollateralizeAppendTx struct {
CollateralizeID string `json:"collateralizeId"`
RecordID string `json:"recordID"`
Value float64 `json:"value"`
Fee int64 `json:"fee"`
}
// CollateralizeFeedTx for construction
type CollateralizeFeedTx struct {
Price []float64 `json:"price"`
Volume []int64 `json:"volume"`
Fee int64 `json:"fee"`
}
// CollateralizeRetrieveTx for construction
type CollateralizeRetrieveTx struct {
CollateralizeID string `json:"collateralizeId"`
Balance float64 `json:"Balance"`
Fee int64 `json:"fee"`
}
// CollateralizeManageTx for construction
type CollateralizeManageTx struct {
DebtCeiling float64 `json:"debtCeiling"`
LiquidationRatio float64 `json:"liquidationRatio"`
StabilityFeeRatio float64 `json:"stabilityFeeRatio"`
Period int64 `json:"period"`
TotalBalance float64 `json:"totalBalance"`
Fee int64 `json:"fee"`
}
// 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
//Collateralize op
const (
CollateralizeActionCreate = 1 + iota
CollateralizeActionBorrow
CollateralizeActionRepay
CollateralizeActionAppend
CollateralizeActionFeed
CollateralizeActionRetrieve
CollateralizeActionManage
//log for Collateralize
TyLogCollateralizeCreate = 731
TyLogCollateralizeBorrow = 732
TyLogCollateralizeRepay = 733
TyLogCollateralizeAppend = 734
TyLogCollateralizeFeed = 735
TyLogCollateralizeRetrieve = 736
)
// Collateralize name
const (
CollateralizeX = "collateralize"
CCNYTokenName = "CCNY"
CollateralizePreLiquidationRatio = 1.1 * 1e4 //TODO 预清算比例,抵押物价值跌到借出ccny价值110%的时候开始清算
)
//Collateralize status
const (
CollateralizeStatusCreated = 1 + iota
CollateralizeStatusClose
)
//暂时只支持bty
//const (
// CollateralizeAssetTypeBty = 1 + iota
// CollateralizeAssetTypeBtc
// CollateralizeAssetTypeEth
//)
const (
CollateralizeUserStatusCreate = 1 + iota
CollateralizeUserStatusWarning
CollateralizeUserStatusSystemLiquidate
CollateralizeUserStatusExpire
CollateralizeUserStatusExpireLiquidate
CollateralizeUserStatusClose
)
...@@ -21,7 +21,7 @@ var driverName = exchangetypes.ExchangeX ...@@ -21,7 +21,7 @@ var driverName = exchangetypes.ExchangeX
// Init register dapp // Init register dapp
func Init(name string, cfg *types.Chain33Config, sub []byte) { func Init(name string, cfg *types.Chain33Config, sub []byte) {
drivers.Register(cfg, GetName(), newExchange, cfg.GetDappFork(driverName, "Enable")) drivers.Register(cfg, GetName(), NewExchange, cfg.GetDappFork(driverName, "Enable"))
InitExecType() InitExecType()
} }
...@@ -35,7 +35,7 @@ type exchange struct { ...@@ -35,7 +35,7 @@ type exchange struct {
drivers.DriverBase drivers.DriverBase
} }
func newExchange() drivers.Driver { func NewExchange() drivers.Driver {
t := &exchange{} t := &exchange{}
t.SetChild(t) t.SetChild(t)
t.SetExecutorType(types.LoadExecutorType(driverName)) t.SetExecutorType(types.LoadExecutorType(driverName))
...@@ -44,7 +44,7 @@ func newExchange() drivers.Driver { ...@@ -44,7 +44,7 @@ func newExchange() drivers.Driver {
// GetName get driver name // GetName get driver name
func GetName() string { func GetName() string {
return newExchange().GetName() return NewExchange().GetName()
} }
func (e *exchange) GetDriverName() string { func (e *exchange) GetDriverName() string {
......
This diff is collapsed.
# Title为local,表示此配置文件为本地单节点的配置。此时本地节点所在的链上只有这一个节点,共识模块一般采用solo模式。
Title="local"
TestNet=true
FixTime=false
[log]
# 日志级别,支持debug(dbug)/info/warn/error(eror)/crit
loglevel = "info"
logConsoleLevel = "info"
# 日志文件名,可带目录,所有生成的日志文件都放到此目录下
logFile = "logs/chain33.log"
# 单个日志文件的最大值(单位:兆)
maxFileSize = 300
# 最多保存的历史日志文件个数
maxBackups = 100
# 最多保存的历史日志消息(单位:天)
maxAge = 28
# 日志文件名是否使用本地时间(否则使用UTC时间)
localTime = true
# 历史日志文件是否压缩(压缩格式为gz)
compress = true
# 是否打印调用源文件和行号
callerFile = false
# 是否打印调用方法
callerFunction = false
[blockchain]
# 缓存区块的个数
defCacheSize=128
# 同步区块时一次最多申请获取的区块个数
maxFetchBlockNum=128
# 向对端节点请求同步区块的时间间隔
timeoutSeconds=5
# 使用的数据库类型
driver="leveldb"
# 数据库文件目录
dbPath="datadir"
# 数据库缓存大小
dbCache=64
# 是否为单节点
singleMode=true
# 同步区块批量写数据库时,是否需要立即写磁盘,非固态硬盘的电脑可以设置为false,以提高性能
batchsync=false
# 是否记录添加或者删除区块的序列,若节点作为主链节点,为平行链节点提供服务,需要设置为true
isRecordBlockSequence=true
# 是否为平行链节点
isParaChain=false
# 是否开启交易快速查询索引
enableTxQuickIndex=false
[p2p]
# P2P服务监听端口号
port=13802
# 种子节点,格式为ip:port,多个节点以逗号分隔,如seeds=["10.0.0.1:13802","10.0.0.2:13802","10.0.0.3:13802"]
seeds=[]
# 是否启动P2P服务
enable=true
# 是否为种子节点
isSeed=false
# 是否作为服务端,对外提供服务
serverStart=true
# 是否使用内置的种子节点
innerSeedEnable=false
# 是否使用Github获取种子节点
useGithub=false
# 最多的接入节点个数
innerBounds=300
# 使用的数据库类型
driver="leveldb"
# 数据库文件目录
dbPath="datadir/addrbook"
# 数据库缓存大小
dbCache=4
# GRPC请求日志文件
grpcLogFile="grpc33.log"
# p2p版本号,不同的测试网络选用不同的version
version=200
verMix=200
verMax=200
[rpc]
# jrpc绑定地址
jrpcBindAddr="localhost:8801"
# grpc绑定地址
grpcBindAddr="localhost:8802"
# 白名单列表,允许访问的IP地址,默认是“*”,允许所有IP访问
whitelist=["127.0.0.1"]
# jrpc方法请求白名单,默认是“*”,允许访问所有RPC方法
jrpcFuncWhitelist=["*"]
# jrpc方法请求黑名单,禁止调用黑名单里配置的rpc方法,一般和白名单配合使用,默认是空
# jrpcFuncBlacklist=["xxxx"]
# grpc方法请求白名单,默认是“*”,允许访问所有RPC方法
grpcFuncWhitelist=["*"]
# grpc方法请求黑名单,禁止调用黑名单里配置的rpc方法,一般和白名单配合使用,默认是空
# grpcFuncBlacklist=["xxx"]
# 是否开启https
enableTLS=false
# 证书文件,证书和私钥文件可以用cli工具生成
certFile="cert.pem"
# 私钥文件
keyFile="key.pem"
[mempool]
# mempool队列名称,可配,timeline,score,price
name="timeline"
# mempool缓存容量大小,默认10240
poolCacheSize=10240
# 最小得交易手续费用,这个没有默认值,必填,一般是100000
minTxFee=100000
# 每个账户在mempool中得最大交易数量,默认100
maxTxNumPerAccount=10000
# timeline 是默认的先来先进的按时间排序
[mempool.sub.timeline]
# mempool缓存容量大小,默认10240
poolCacheSize=10240
# 最小得交易手续费用,这个没有默认值,必填,一般是100000
minTxFee=100000
# 每个账户在mempool中得最大交易数量,默认100
maxTxNumPerAccount=10000
# score是分数队列模式(分数=常量a*手续费/交易字节数-常量b*时间*定量c,按分数排队,高的优先,常量a,b和定量c可配置),按分数来排序
[mempool.sub.score]
# mempool缓存容量大小,默认10240
poolCacheSize=10240
# 最小得交易手续费用,这个没有默认值,必填,一般是100000
minTxFee=100000
# 每个账户在mempool中得最大交易数量,默认100
maxTxNumPerAccount=10000
# 时间占价格比例
timeParam=1
# 手续费相对于时间的一个合适的常量,取当前unix时间戳前四位数,排队时手续费高1e-5的分数~=快1s的分数
priceConstant=1544
# 常量比例
pricePower=1
# price是价格队列模式(价格=手续费/交易字节数,价格高者优先,同价则时间早优先)
[mempool.sub.price]
# mempool缓存容量大小,默认10240
poolCacheSize=10240
# 最小得交易手续费用,这个没有默认值,必填,一般是100000
minTxFee=100000
# 每个账户在mempool中得最大交易数量,默认100
maxTxNumPerAccount=10000
[consensus]
#共识名,可选项有solo,ticket,raft,tendermint,para
name="solo"
#是否开启挖矿,开启挖矿才能创建区块
minerstart=true
#创世区块时间(UTC时间)
genesisBlockTime=1514533394
#创世交易地址
genesis="1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs"
[mver.consensus]
#基金账户地址
fundKeyAddr = "1BQXS6TxaYYG5mADaWij4AxhZZUTpw95a5"
#用户回报
coinReward = 18
#发展基金回报
coinDevFund = 12
#ticket价格
ticketPrice = 10000
#挖矿难度
powLimitBits = "0x1f00ffff"
#每次调整难度的最大的范围,如果设置成 4 的话,范围是 (1/4 - 4),一次不能增加 4倍以上的难度,或者难度减少为 原来的 1/4 ,这个参数,是为了难度不会突然爆增加或者减少
retargetAdjustmentFactor = 4
#表示如果区块时间大于当前时间 16s ,那么区块就会判定为无效区块。
futureBlockTime = 16
#ticket冻结时长
ticketFrozenTime = 5 #5s only for test
ticketWithdrawTime = 10 #10s only for test
ticketMinerWaitTime = 2 #2s only for test
#区块包含最多交易数
maxTxNumber = 1600 #160
#调整挖矿难度的间隔,(ps:难度不是每个区块都调整的,而是每隔 targetTimespan / targetTimePerBlock 块调整一次)
targetTimespan = 2304
#每个区块打包的目标时间
targetTimePerBlock = 16
# 仅保留这一项,其他consensus相关的配置全部删除
[consensus.sub.solo]
#创世交易地址
genesis="1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs"
#创世区块时间(UTC时间)
genesisBlockTime=1514533394
#获取交易间隔时长,单位纳秒
waitTxMs=10
[store]
# 数据存储格式名称,目前支持mavl,kvdb,kvmvcc,mpt
name="mavl"
# 数据存储驱动类别,目前支持leveldb,goleveldb,memdb,gobadgerdb,ssdb,pegasus
driver="leveldb"
# 数据文件存储路径
dbPath="datadir/mavltree"
# Cache大小
dbCache=128
# 数据库版本
localdbVersion="1.0.0"
[store.sub.mavl]
# 是否使能mavl加前缀
enableMavlPrefix=false
# 是否使能MVCC,如果mavl中enableMVCC为true此处必须为true
enableMVCC=false
# 是否使能mavl数据裁剪
enableMavlPrune=false
# 裁剪高度间隔
pruneHeight=10000
[wallet]
# 交易发送最低手续费,单位0.00000001BTY(1e-8),默认100000,即0.001BTY
minFee=100000
# walletdb驱动名,支持leveldb/memdb/gobadgerdb/ssdb/pegasus
driver="leveldb"
# walletdb路径
dbPath="wallet"
# walletdb缓存大小
dbCache=16
# 钱包发送交易签名方式
signType="secp256k1"
[wallet.sub.ticket]
# 是否关闭ticket自动挖矿,默认false
minerdisable=false
# 允许购买ticket挖矿的白名单地址,默认配置“*”,允许所有地址购买
minerwhitelist=["*"]
[exec]
#执行器执行是否免费
isFree=false
#执行器执行所需最小费用,低于Mempool和Wallet设置的MinFee,在minExecFee = 0 的情况下,isFree = true才会生效
minExecFee=100000
#是否开启stat插件
enableStat=false
#是否开启MVCC插件
enableMVCC=false
alias=["token1:token","token2:token","token3:token"]
[exec.sub.token]
#是否保存token交易信息
saveTokenTxList=true
#token审批人地址
tokenApprs = [
"1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs",
"1Q8hGLfoGe63efeWa8fJ4Pnukhkngt6poK",
"1LY8GFia5EiyoTodMLfkB5PHNNpXRqxhyB",
"1GCzJDS6HbgTQ2emade7mEJGGWFfA15pS9",
"1JYB8sxi4He5pZWHCd3Zi2nypQ4JMB6AxN",
"12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv",
]
[exec.sub.cert]
# 是否启用证书验证和签名
enable=false
# 加密文件路径
cryptoPath="authdir/crypto"
# 带证书签名类型,支持"auth_ecdsa", "auth_sm2"
signType="auth_ecdsa"
[exec.sub.relay]
#relay执行器保存BTC头执行权限地址
genesis="1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs"
[exec.sub.manage]
#manage执行器超级管理员地址
superManager=[
"1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs",
"12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv",
"1Q8hGLfoGe63efeWa8fJ4Pnukhkngt6poK"
]
package test
import (
"github.com/33cn/chain33/types"
"github.com/golang/protobuf/proto"
)
type Cli interface {
Send(tx *types.Transaction, hexKey string) ([]*types.ReceiptLog, error)
Query(fn string, msg proto.Message) ([]byte, error)
GetExecAccount(addr string, exec string, symbol string) (*types.Account, error) // 获取 addr 在本合约中 exec 执行器下面的 symbol 子账户
}
package main
import (
"fmt"
"github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/exchange/test"
et "github.com/33cn/plugin/plugin/dapp/exchange/types"
)
var (
cli *test.GRPCCli
PrivKeyA = "0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b" // 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4
PrivKeyB = "0x19c069234f9d3e61135fefbeb7791b149cdf6af536f26bebb310d4cd22c3fee4" // 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR
)
// 批量测试前,先确保测试账户有足够的币和钱
func main() {
cli = test.NewGRPCCli("localhost:8802")
go buy()
go sell()
select {}
}
func sell() {
req := &et.LimitOrder{
LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"},
Price: 1,
Amount: types.Coin,
Op: et.OpSell,
}
ety := types.LoadExecutorType(et.ExchangeX)
// 卖 2000 次,需 2000*1=2000 个 bty
for i := 0; i < 2000; i++ {
fmt.Println("sell ", i)
tx, err := ety.Create("LimitOrder", req)
if err != nil {
panic(err)
}
go cli.SendTx(tx, PrivKeyA)
}
}
func buy() {
req := &et.LimitOrder{
LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"},
Price: 1,
Amount: types.Coin,
Op: et.OpBuy,
}
ety := types.LoadExecutorType(et.ExchangeX)
// 买 2000 次,需 2000*1=2000 个 ccny
for i := 0; i < 2000; i++ {
fmt.Println("buy ", i)
tx, err := ety.Create("LimitOrder", req)
if err != nil {
panic(err)
}
go cli.SendTx(tx, PrivKeyB)
}
}
This diff is collapsed.
package test
import (
"fmt"
"log"
"time"
"github.com/33cn/chain33/common/db"
"github.com/33cn/plugin/plugin/dapp/exchange/executor"
"github.com/golang/protobuf/proto"
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/client"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/queue"
et "github.com/33cn/plugin/plugin/dapp/exchange/types"
)
type ExecCli struct {
ldb db.KVDB
sdb db.DB
height int64
blockTime int64
difficulty uint64
q queue.Queue
cfg *types.Chain33Config
execAddr string
accA *account.DB //exec account
accA1 *account.DB //exec token account
accB *account.DB
accB1 *account.DB
accC *account.DB
accC1 *account.DB
accD *account.DB
accD1 *account.DB
}
var (
Nodes = []string{
"1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4",
"1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR",
"1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k",
"1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs",
}
)
func NewExecCli() *ExecCli {
dir, sdb, ldb := util.CreateTestDB()
log.Println(dir)
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
executor.Init(et.ExchangeX, cfg, nil)
total := 100000000 * types.Coin
accountA := &types.Account{
Balance: total,
Frozen: 0,
Addr: Nodes[0],
}
accountB := &types.Account{
Balance: total,
Frozen: 0,
Addr: Nodes[1],
}
accountC := &types.Account{
Balance: total,
Frozen: 0,
Addr: Nodes[2],
}
accountD := &types.Account{
Balance: total,
Frozen: 0,
Addr: Nodes[3],
}
execAddr := address.ExecAddress(et.ExchangeX)
accA, _ := account.NewAccountDB(cfg, "coins", "bty", sdb)
accA.SaveExecAccount(execAddr, accountA)
accB, _ := account.NewAccountDB(cfg, "coins", "bty", sdb)
accB.SaveExecAccount(execAddr, accountB)
accC, _ := account.NewAccountDB(cfg, "coins", "bty", sdb)
accC.SaveExecAccount(execAddr, accountC)
accD, _ := account.NewAccountDB(cfg, "coins", "bty", sdb)
accD.SaveExecAccount(execAddr, accountD)
accA1, _ := account.NewAccountDB(cfg, "token", "CCNY", sdb)
accA1.SaveExecAccount(execAddr, accountA)
accB1, _ := account.NewAccountDB(cfg, "token", "CCNY", sdb)
accB1.SaveExecAccount(execAddr, accountB)
accC1, _ := account.NewAccountDB(cfg, "token", "CCNY", sdb)
accC1.SaveExecAccount(execAddr, accountC)
accD1, _ := account.NewAccountDB(cfg, "token", "CCNY", sdb)
accD1.SaveExecAccount(execAddr, accountD)
q := queue.New("channel")
q.SetConfig(cfg)
return &ExecCli{
ldb: ldb,
sdb: sdb,
height: 1,
blockTime: time.Now().Unix(),
difficulty: 1539918074,
q: q,
cfg: cfg,
execAddr: execAddr,
accA: accA,
accA1: accA1,
accB: accB,
accB1: accB1,
accC: accC,
accC1: accC1,
accD: accD,
accD1: accD1,
}
}
func (c *ExecCli) Send(tx *types.Transaction, hexKey string) ([]*types.ReceiptLog, error) {
var err error
tx, err = types.FormatTx(c.cfg, et.ExchangeX, tx)
if err != nil {
return nil, err
}
tx, err = signTx(tx, hexKey)
if err != nil {
return nil, err
}
exec := executor.NewExchange()
if err := exec.CheckTx(tx, int(1)); err != nil {
return nil, err
}
c.height++
c.blockTime += 10
c.difficulty++
api, _ := client.New(c.q.Client(), nil)
exec.SetAPI(api)
exec.SetStateDB(c.sdb)
exec.SetLocalDB(c.ldb)
exec.SetEnv(c.height, c.blockTime, c.difficulty)
receipt, err := exec.Exec(tx, int(1))
if err != nil {
return nil, err
}
for _, kv := range receipt.KV {
c.sdb.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 {
return nil, err
}
for _, kv := range set.KV {
c.ldb.Set(kv.Key, kv.Value)
}
//save to database
util.SaveKVList(c.sdb, set.KV)
return receipt.Logs, nil
}
func (c *ExecCli) Query(fn string, msg proto.Message) ([]byte, error) {
api, _ := client.New(c.q.Client(), nil)
exec := executor.NewExchange()
exec.SetAPI(api)
exec.SetStateDB(c.sdb)
exec.SetLocalDB(c.ldb)
exec.SetEnv(c.height, c.blockTime, c.difficulty)
r, err := exec.Query(fn, types.Encode(msg))
if err != nil {
return nil, err
}
return types.Encode(r), nil
}
func signTx(tx *types.Transaction, hexPrivKey string) (*types.Transaction, error) {
signType := types.SECP256K1
c, err := crypto.New(types.GetSignName("", 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
}
func (c *ExecCli) GetExecAccount(addr string, exec string, symbol string) (*types.Account, error) {
//mavl-{coins}-{bty}-exec-{26htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp}:{1JmFaA6unrCFYEWPGRi7uuXY1KthTJxJEP}
//mavl-{token}-{ccny}-exec-{26htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp}:{1JmFaA6unrCFYEWPGRi7uuXY1KthTJxJEP}
key := []byte(fmt.Sprintf("mavl-%s-%s-exec-%s:%s", exec, symbol, c.execAddr, addr))
bytes, err := c.sdb.Get(key)
if err != nil {
return nil, err
}
var acc types.Account
err = types.Decode(bytes, &acc)
if err != nil {
return nil, err
}
return &acc, nil
}
package test
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/exchange/executor"
et "github.com/33cn/plugin/plugin/dapp/exchange/types"
tt "github.com/33cn/plugin/plugin/dapp/token/types"
"github.com/golang/protobuf/proto"
"google.golang.org/grpc"
)
type GRPCCli struct {
client types.Chain33Client
}
func NewGRPCCli(grpcAddr string) *GRPCCli {
conn, err := grpc.Dial(grpcAddr, grpc.WithInsecure())
if err != nil {
panic(err)
}
client := types.NewChain33Client(conn)
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
executor.Init(et.ExchangeX, cfg, nil)
return &GRPCCli{
client: client,
}
}
func (c *GRPCCli) Send(tx *types.Transaction, hexKey string) ([]*types.ReceiptLog, error) {
txHash, logs, err := c.sendAndWaitReceipt(tx, hexKey)
if txHash != nil {
fmt.Println("txHash: ", common.ToHex(txHash))
}
if err != nil {
return nil, parseError(err)
}
for _, l := range logs {
if l.Ty == types.TyLogErr {
return nil, errors.New(string(l.Log))
}
}
return logs, nil
}
func (c *GRPCCli) Query(fn string, msg proto.Message) ([]byte, error) {
ss := strings.Split(fn, ".")
var in types.ChainExecutor
if len(ss) == 2 {
in.Driver = ss[0]
in.FuncName = ss[1]
} else {
in.Driver = et.ExchangeX
in.FuncName = fn
}
in.Param = types.Encode(msg)
r, err := c.client.QueryChain(context.Background(), &in)
if err != nil {
return nil, err
}
if !r.IsOk {
return nil, errors.New(string(r.Msg))
}
return r.Msg, nil
}
func (c *GRPCCli) GetExecAccount(addr string, exec string, symbol string) (*types.Account, error) {
if exec == "coins" {
// bty
var addrs []string
addrs = append(addrs, addr)
params := &types.ReqBalance{
Addresses: addrs,
Execer: et.ExchangeX,
}
accs, err := c.client.GetBalance(context.Background(), params)
if err != nil {
return nil, err
}
return accs.Acc[0], nil
}
// token: ccny
param := &tt.ReqAccountTokenAssets{
Address: addr,
Execer: et.ExchangeX,
}
msg, err := c.Query("token.GetAccountTokenAssets", param)
if err != nil {
return nil, err
}
var resp tt.ReplyAccountTokenAssets
err = types.Decode(msg, &resp)
if err != nil {
return nil, err
}
for _, v := range resp.TokenAssets {
if v.Symbol == symbol {
return v.Account, nil
}
}
return nil, types.ErrNotFound
}
// 发送交易并等待执行结果
// 如果交易非法,返回错误信息
// 如果交易执行成功,返回 交易哈希、回报
func (c *GRPCCli) sendAndWaitReceipt(tx *types.Transaction, hexKey string) (txHash []byte, logs []*types.ReceiptLog, err error) {
r, err := c.SendTx(tx, hexKey)
if err != nil {
// rpc error: code = Unknown desc = ErrNotBank
return nil, nil, err
}
if !r.IsOk {
return nil, nil, errors.New(string(r.Msg))
}
time.Sleep(time.Second)
d, _ := c.client.QueryTransaction(context.Background(), &types.ReqHash{Hash: r.Msg})
return r.Msg, d.Receipt.Logs, nil
}
func (c *GRPCCli) SendTx(tx *types.Transaction, hexKey string) (reply *types.Reply, err error) {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
tx, err = types.FormatTx(cfg, et.ExchangeX, tx)
if err != nil {
return nil, err
}
tx, err = signTx(tx, hexKey)
if err != nil {
return nil, err
}
return c.client.SendTransaction(context.Background(), tx)
}
func parseError(err error) error {
// rpc error: code = Unknown desc = ErrNotBank
str := err.Error()
sep := "desc = "
i := strings.Index(str, sep)
if i != -1 {
return errors.New(str[i+len(sep):])
}
return err
}
#!/usr/bin/env bash
BUILD=$(cd "$(dirname "$0")" && cd ../../../../build && pwd)
echo "$BUILD"
cd "$BUILD" || return
seed=$(./chain33-cli seed generate -l 0)
echo "$seed"
./chain33-cli seed save -p bty123456 -s "$seed"
sleep 1
./chain33-cli wallet unlock -p bty123456
sleep 1
./chain33-cli account list
## account
#genesis -- 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
./chain33-cli account import_key -k 3990969DF92A5914F7B71EEB9A4E58D6E255F32BF042FEA5318FC8B3D50EE6E8 -l genesis
#A -- 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4
./chain33-cli account import_key -k 0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b -l A
#B -- 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR
./chain33-cli account import_key -k 0x19c069234f9d3e61135fefbeb7791b149cdf6af536f26bebb310d4cd22c3fee4 -l B
#C -- 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k
./chain33-cli account import_key -k 0x7a80a1f75d7360c6123c32a78ecf978c1ac55636f87892df38d8b85a9aeff115 -l C
#D -- 1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs
./chain33-cli account import_key -k 0xcacb1f5d51700aea07fca2246ab43b0917d70405c65edea9b5063d72eb5c6b71 -l D
## config token
./chain33-cli send config config_tx -c token-finisher -o add -v 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
sleep 1
./chain33-cli config query -k token-finisher
./chain33-cli send config config_tx -c token-blacklist -o add -v BTY -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
sleep 1
./chain33-cli config query -k token-blacklist
## 10亿
./chain33-cli send token precreate -f 0.001 -i "test ccny" -n "ccny" -a 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs -p 0 -s CCNY -t 1000000000 -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
sleep 1
./chain33-cli token precreated
./chain33-cli send token finish -s CCNY -f 0.001 -a 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
sleep 1
./chain33-cli token created
./chain33-cli token balance -a 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs -s CCNY -e token
## transfer bty
./chain33-cli send coins transfer -a 10000 -t 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4 -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
./chain33-cli send coins transfer -a 10000 -t 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
./chain33-cli send coins transfer -a 10000 -t 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
./chain33-cli send coins transfer -a 10000 -t 1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
./chain33-cli account list
## send bty to execer, 每人10000 bty
./chain33-cli send coins send_exec -e exchange -a 10000 -k 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4
./chain33-cli send coins send_exec -e exchange -a 10000 -k 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR
./chain33-cli send coins send_exec -e exchange -a 10000 -k 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k
./chain33-cli send coins send_exec -e exchange -a 10000 -k 1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs
echo "account balance in execer"
./chain33-cli account balance -e exchange -a 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4
./chain33-cli account balance -e exchange -a 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR
./chain33-cli account balance -e exchange -a 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k
./chain33-cli account balance -e exchange -a 1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs
##transfer token,每人2亿 ccny
./chain33-cli send token transfer -a 200000000 -s CCNY -t 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4 -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
./chain33-cli send token transfer -a 200000000 -s CCNY -t 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
./chain33-cli send token transfer -a 200000000 -s CCNY -t 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
./chain33-cli send token transfer -a 200000000 -s CCNY -t 1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
## send token to excer
./chain33-cli send token send_exec -a 200000000 -e exchange -s CCNY -k 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4
./chain33-cli send token send_exec -a 200000000 -e exchange -s CCNY -k 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR
./chain33-cli send token send_exec -a 200000000 -e exchange -s CCNY -k 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k
./chain33-cli send token send_exec -a 200000000 -e exchange -s CCNY -k 1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs
echo "token balance in execer"
./chain33-cli token balance -e exchange -s CCNY -a "1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k 1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
_ "github.com/33cn/plugin/plugin/dapp/autonomy" //auto gen _ "github.com/33cn/plugin/plugin/dapp/autonomy" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/blackwhite" //auto gen _ "github.com/33cn/plugin/plugin/dapp/blackwhite" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/cert" //auto gen _ "github.com/33cn/plugin/plugin/dapp/cert" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/collateralize" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/dposvote" //auto gen _ "github.com/33cn/plugin/plugin/dapp/dposvote" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/echo" //auto gen _ "github.com/33cn/plugin/plugin/dapp/echo" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/evm" //auto gen _ "github.com/33cn/plugin/plugin/dapp/evm" //auto gen
...@@ -11,6 +12,7 @@ import ( ...@@ -11,6 +12,7 @@ import (
_ "github.com/33cn/plugin/plugin/dapp/game" //auto gen _ "github.com/33cn/plugin/plugin/dapp/game" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/guess" //auto gen _ "github.com/33cn/plugin/plugin/dapp/guess" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/hashlock" //auto gen _ "github.com/33cn/plugin/plugin/dapp/hashlock" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/issuance" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/js" //auto gen _ "github.com/33cn/plugin/plugin/dapp/js" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/lottery" //auto gen _ "github.com/33cn/plugin/plugin/dapp/lottery" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/multisig" //auto gen _ "github.com/33cn/plugin/plugin/dapp/multisig" //auto gen
......
all:
chmod +x ./build.sh
./build.sh $(OUT) $(FLAG)
\ No newline at end of file
#!/usr/bin/env bash
strpwd=$(pwd)
strcmd=${strpwd##*dapp/}
strapp=${strcmd%/cmd*}
OUT_TESTDIR="${1}/dapptest/$strapp"
mkdir -p "${OUT_TESTDIR}"
cp ./test/test-rpc.sh "${OUT_TESTDIR}"
This diff is collapsed.
This diff is collapsed.
// 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
/*
waiting for update
*/
// 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/issuance/types"
)
// Exec_Create Action
func (c *Issuance) Exec_Create(payload *pty.IssuanceCreate, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewIssuanceAction(c, tx, index)
return actiondb.IssuanceCreate(payload)
}
// Exec_Borrow Action
func (c *Issuance) Exec_Debt(payload *pty.IssuanceDebt, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewIssuanceAction(c, tx, index)
return actiondb.IssuanceDebt(payload)
}
// Exec_Repay Action
func (c *Issuance) Exec_Repay(payload *pty.IssuanceRepay, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewIssuanceAction(c, tx, index)
return actiondb.IssuanceRepay(payload)
}
// Exec_Feed Action
func (c *Issuance) Exec_Feed(payload *pty.IssuanceFeed, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewIssuanceAction(c, tx, index)
return actiondb.IssuanceFeed(payload)
}
// Exec_Close Action
func (c *Issuance) Exec_Close(payload *pty.IssuanceClose, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewIssuanceAction(c, tx, index)
return actiondb.IssuanceClose(payload)
}
// Exec_Manage Action
func (c *Issuance) Exec_Manage(payload *pty.IssuanceManage, tx *types.Transaction, index int) (*types.Receipt, error) {
actiondb := NewIssuanceAction(c, tx, index)
return actiondb.IssuanceManage(payload)
}
// 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/issuance/types"
)
func (c *Issuance) execDelLocal(tx *types.Transaction, receiptData *types.ReceiptData) (*types.LocalDBSet, error) {
kvs, err := c.DelRollbackKV(tx, tx.Execer)
if err != nil {
return nil, err
}
dbSet := &types.LocalDBSet{}
dbSet.KV = append(dbSet.KV, kvs...)
return dbSet, nil
}
// ExecDelLocal_Create Action
func (c *Issuance) ExecDelLocal_Create(payload *pty.IssuanceCreate, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, receiptData)
}
// ExecDelLocal_Debt Action
func (c *Issuance) ExecDelLocal_Debt(payload *pty.IssuanceDebt, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, receiptData)
}
// ExecDelLocal_Repay Action
func (c *Issuance) ExecDelLocal_Repay(payload *pty.IssuanceRepay, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, receiptData)
}
// ExecDelLocal_Feed Action
func (c *Issuance) ExecDelLocal_Feed(payload *pty.IssuanceFeed, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, receiptData)
}
// ExecDelLocal_Close Action
func (c *Issuance) ExecDelLocal_Close(payload *pty.IssuanceClose, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, receiptData)
}
// ExecDelLocal_Manage Action
func (c *Issuance) ExecDelLocal_Manage(payload *pty.IssuanceManage, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execDelLocal(tx, 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/common"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/issuance/types"
)
func (c *Issuance) execLocal(tx *types.Transaction, receipt *types.ReceiptData) (*types.LocalDBSet, error) {
set := &types.LocalDBSet{}
for _, item := range receipt.Logs {
if item.Ty >= pty.TyLogIssuanceCreate && item.Ty <= pty.TyLogIssuanceClose {
var issuanceLog pty.ReceiptIssuance
err := types.Decode(item.Log, &issuanceLog)
if err != nil {
return nil, err
}
if item.Ty == pty.TyLogIssuanceCreate || item.Ty == pty.TyLogIssuanceClose {
IDtable := pty.NewIssuanceTable(c.GetLocalDB())
err = IDtable.Replace(&pty.ReceiptIssuanceID{IssuanceId: issuanceLog.IssuanceId, Status: issuanceLog.Status})
if err != nil {
return nil, err
}
kvs, err := IDtable.Save()
if err != nil {
return nil, err
}
set.KV = append(set.KV, kvs...)
} else {
recordTable := pty.NewRecordTable(c.GetLocalDB())
err = recordTable.Replace(&pty.ReceiptIssuance{IssuanceId: issuanceLog.IssuanceId, Status: issuanceLog.Status,
DebtId: issuanceLog.DebtId, AccountAddr: issuanceLog.AccountAddr})
if err != nil {
return nil, err
}
kvs, err := recordTable.Save()
if err != nil {
return nil, err
}
set.KV = append(set.KV, kvs...)
}
}
}
set.KV = c.AddRollbackKV(tx, []byte(pty.IssuanceX), set.KV)
return set, nil
}
// ExecLocal_Create Action
func (c *Issuance) ExecLocal_Create(payload *pty.IssuanceCreate, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, receiptData)
}
// ExecLocal_Debt Action
func (c *Issuance) ExecLocal_Debt(payload *pty.IssuanceDebt, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, receiptData)
}
// ExecLocal_Repay Action
func (c *Issuance) ExecLocal_Repay(payload *pty.IssuanceRepay, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, receiptData)
}
// ExecLocal_Feed Action
func (c *Issuance) ExecLocal_Feed(payload *pty.IssuanceFeed, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, receiptData)
}
// ExecLocal_Close Action
func (c *Issuance) ExecLocal_Close(payload *pty.IssuanceClose, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, receiptData)
}
// ExecLocal_Manage Action
func (c *Issuance) ExecLocal_Manage(payload *pty.IssuanceManage, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(tx, 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 (
log "github.com/33cn/chain33/common/log/log15"
drivers "github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/issuance/types"
)
var clog = log.New("module", "execs.issuance")
var driverName = pty.IssuanceX
func InitExecType() {
ety := types.LoadExecutorType(driverName)
ety.InitFuncList(types.ListMethod(&Issuance{}))
}
// Init issuance
func Init(name string, cfg *types.Chain33Config, sub []byte) {
driverName := GetName()
if name != driverName {
panic("system dapp can't be rename")
}
if sub != nil {
types.MustDecode(sub, &cfg)
}
drivers.Register(cfg, driverName, newIssuance, cfg.GetDappFork(driverName, "Enable"))
InitExecType()
}
// GetName for Issuance
func GetName() string {
return newIssuance().GetName()
}
// Issuance driver
type Issuance struct {
drivers.DriverBase
}
func newIssuance() drivers.Driver {
c := &Issuance{}
c.SetChild(c)
c.SetExecutorType(types.LoadExecutorType(driverName))
return c
}
// GetDriverName for Issuance
func (c *Issuance) GetDriverName() string {
return pty.IssuanceX
}
// CheckReceiptExecOk return true to check if receipt ty is ok
func (c *Issuance) CheckReceiptExecOk() bool {
return true
}
// ExecutorOrder 设置localdb的EnableRead
func (c *Issuance) ExecutorOrder() int64 {
cfg := c.GetAPI().GetConfig()
if cfg.IsFork(c.GetHeight(), "ForkLocalDBAccess") {
return drivers.ExecLocalSameTime
}
return c.DriverBase.ExecutorOrder()
}
This diff is collapsed.
This diff is collapsed.
// 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/issuance/types"
)
func (c *Issuance) Query_IssuanceInfoByID(req *pty.ReqIssuanceInfo) (types.Message, error) {
issu, err := queryIssuanceByID(c.GetStateDB(), req.IssuanceId)
if err != nil {
clog.Error("Query_IssuanceInfoByID", "id", req.IssuanceId, "error", err)
return nil, err
}
return &pty.RepIssuanceCurrentInfo{
Status: issu.Status,
TotalBalance: issu.TotalBalance,
DebtCeiling: issu.DebtCeiling,
LiquidationRatio: issu.LiquidationRatio,
Balance: issu.Balance,
CollateralValue: issu.CollateralValue,
DebtValue: issu.DebtValue,
Period: issu.Period,
IssuId: issu.IssuanceId,
CreateTime: issu.CreateTime,
}, nil
}
func (c *Issuance) Query_IssuanceInfoByIDs(req *pty.ReqIssuanceInfos) (types.Message, error) {
infos := &pty.RepIssuanceCurrentInfos{}
for _, id := range req.IssuanceIds {
issu, err := queryIssuanceByID(c.GetStateDB(), id)
if err != nil {
clog.Error("Query_IssuanceInfoByID", "id", id, "error", err)
return nil, err
}
infos.Infos = append(infos.Infos, &pty.RepIssuanceCurrentInfo{
Status: issu.Status,
TotalBalance: issu.TotalBalance,
DebtCeiling: issu.DebtCeiling,
LiquidationRatio: issu.LiquidationRatio,
Balance: issu.Balance,
CollateralValue: issu.CollateralValue,
DebtValue: issu.DebtValue,
Period: issu.Period,
IssuId: issu.IssuanceId,
CreateTime: issu.CreateTime,
})
}
return infos, nil
}
func (c *Issuance) Query_IssuanceByStatus(req *pty.ReqIssuanceByStatus) (types.Message, error) {
ids := &pty.RepIssuanceIDs{}
issuIDs, err := queryIssuanceByStatus(c.GetLocalDB(), req.Status, req.IssuanceId)
if err != nil {
clog.Error("Query_IssuanceByStatus", "get issuance error", err)
return nil, err
}
ids.IDs = append(ids.IDs, issuIDs...)
return ids, nil
}
func (c *Issuance) Query_IssuanceRecordByID(req *pty.ReqIssuanceRecords) (types.Message, error) {
ret := &pty.RepIssuanceDebtInfo{}
issuRecord, err := queryIssuanceRecordByID(c.GetStateDB(), req.IssuanceId, req.DebtId)
if err != nil {
clog.Error("Query_IssuanceRecordByID", "get issuance record error", err)
return nil, err
}
ret.Record = issuRecord
return ret, nil
}
func (c *Issuance) Query_IssuanceRecordsByAddr(req *pty.ReqIssuanceRecords) (types.Message, error) {
ret := &pty.RepIssuanceRecords{}
records, err := queryIssuanceRecordByAddr(c.GetStateDB(), c.GetLocalDB(), req.Addr, req.Status, req.DebtId)
if err != nil {
clog.Error("Query_IssuanceDebtInfoByAddr", "get issuance record error", err)
return nil, err
}
if req.Status == 0 {
ret.Records = records
} else {
for _, record := range records {
if record.Status == req.Status {
ret.Records = append(ret.Records, record)
}
}
}
return ret, nil
}
func (c *Issuance) Query_IssuanceRecordsByStatus(req *pty.ReqIssuanceRecords) (types.Message, error) {
ret := &pty.RepIssuanceRecords{}
records, err := queryIssuanceRecordsByStatus(c.GetStateDB(), c.GetLocalDB(), req.Status, req.DebtId)
if err != nil {
clog.Error("Query_IssuanceDebtInfoByStatus", "get issuance record error", err)
return nil, err
}
ret.Records = append(ret.Records, records...)
return ret, nil
}
func (c *Issuance) Query_IssuancePrice(req *pty.ReqIssuanceRecords) (types.Message, error) {
price, err := getLatestPrice(c.GetStateDB())
if err != nil {
clog.Error("Query_IssuancePrice", "error", err)
return nil, err
}
return &pty.RepIssuancePrice{Price: price}, nil
}
func (c *Issuance) Query_IssuanceUserBalance(req *pty.ReqIssuanceRecords) (types.Message, error) {
balance, err := queryIssuanceUserBalance(c.GetStateDB(), c.GetLocalDB(), req.Addr)
if err != nil {
clog.Error("Query_IssuanceRecordByAddr", "get issuance record error", err)
return nil, err
}
return &pty.RepIssuanceUserBalance{Balance: balance}, 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 issuance
import (
"github.com/33cn/chain33/pluginmgr"
"github.com/33cn/plugin/plugin/dapp/issuance/commands"
"github.com/33cn/plugin/plugin/dapp/issuance/executor"
"github.com/33cn/plugin/plugin/dapp/issuance/types"
)
func init() {
pluginmgr.Register(&pluginmgr.PluginBase{
Name: types.IssuanceX,
ExecName: executor.GetName(),
Exec: executor.Init,
Cmd: commands.IssuanceCmd,
})
}
all:
sh ./create_protobuf.sh
#!/bin/sh
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 Issuance {
string issuanceId = 1; //发行ID,一期发行对应一个ID
int64 totalBalance = 2; //当期发行的总金额(ccny)
int64 debtCeiling = 3; //单用户可借出的限额(ccny)
int64 liquidationRatio = 4; //清算比例
int64 collateralValue = 5; //抵押物总数量(bty)
int64 debtValue = 6; //产生的ccny数量
repeated DebtRecord debtRecords = 7; //大户抵押记录
repeated DebtRecord invalidRecords = 8; //大户抵押记录
int32 status = 9; //当期发行的状态,是否关闭
int64 latestLiquidationPrice = 10;//最高清算价格
int64 period = 11;//发行最大期限
int64 latestExpireTime = 12;//最近超期时间
int64 createTime = 13;//创建时间
int64 balance = 14;//剩余可发行ccny
string issuerAddr = 15;//发行地址
}
// 抵押记录
message DebtRecord {
string accountAddr = 1; //抵押人地址
int64 startTime = 2; //抵押时间
int64 collateralValue = 3; //抵押物价值(bty)
int64 collateralPrice = 4; //抵押物价格
int64 debtValue = 5; //债务价值(ccny)
int64 liquidationPrice = 6; //抵押物清算价格
int32 status = 7; //抵押状态,是否被清算
int64 liquidateTime = 8; //清算时间
int64 expireTime = 9; //超时清算时间
int32 preStatus = 10;//上一次抵押状态,用于告警恢复
string debtId = 11;//借贷id
string issuId = 12;//发行id
}
// 资产价格记录
message IssuanceAssetPriceRecord {
int64 recordTime = 1; //价格记录时间
int64 btyPrice = 2; //bty价格
}
// action
message IssuanceAction {
oneof value {
IssuanceCreate create = 1; //创建一期发行
IssuanceDebt debt = 2; //抵押
IssuanceRepay repay = 3; //清算
IssuanceFeed feed = 4; //喂价
IssuanceClose close = 5; //关闭
IssuanceManage manage = 6; //全局配置
}
int32 ty = 10;
}
message IssuanceManage {
repeated string superAddrs = 1; //大户地址
}
// 创建发行
message IssuanceCreate {
int64 totalBalance = 1; //发行总金额
int64 debtCeiling = 2; //单用户可借出的限额(ccny)
int64 liquidationRatio = 3; //清算比例
int64 period = 4; //发行最大期限
}
// 抵押
message IssuanceDebt {
string issuanceId = 1; //发行ID
int64 value = 2; //借贷金额(ccny)
}
// 质押清算
message IssuanceRepay {
string issuanceId = 1; //发行ID
string debtId = 2; //抵押ID
}
// 喂价
message IssuanceFeed {
int32 collType = 1; //抵押物价格类型(1,bty,2,btc,3,eth...)
repeated int64 price = 2; //喂价
repeated int64 volume = 3; //成交量
}
// 借贷关闭
message IssuanceClose {
string issuanceId = 1; //发行ID
}
// exec_local 发行信息
message ReceiptIssuance {
string issuanceId = 1;
string accountAddr = 2;
string debtId = 3;
int32 status = 4;
}
// exec_local issuid记录信息
message ReceiptIssuanceID {
string issuanceId = 1;
int32 status = 2;
}
// exec_local 抵押记录信息列表
message IssuanceRecords {
repeated ReceiptIssuance records = 1;
}
// 根据ID查询发行信息
message ReqIssuanceInfo {
string issuanceId = 1;
}
// 返回一期发行信息
message RepIssuanceCurrentInfo {
int32 status = 1; //当期发行的状态,是否关闭
int64 totalBalance = 2; //当期发行总金额(ccny)
int64 debtCeiling = 3; //单用户可借出的限额(ccny)
int64 liquidationRatio = 4; //清算比例
int64 balance = 5; //剩余可借贷金额(ccny)
int64 collateralValue = 6; //抵押物总数量(bty)
int64 debtValue = 7; //产生的ccny数量
int64 period = 8; //发行最大期限
string issuId = 9; //发行ID
int64 createTime = 10;//创建时间
}
// 根据ID列表查询多期发行信息
message ReqIssuanceInfos {
repeated string issuanceIds = 1;
}
// 返回多期发行信息
message RepIssuanceCurrentInfos {
repeated RepIssuanceCurrentInfo infos = 1;
}
// 根据发行状态查询
message ReqIssuanceByStatus {
int32 status = 1;
string issuanceId = 2;
}
// 返回发行ID列表
message RepIssuanceIDs {
repeated string IDs = 1;
}
// 根据用户地址查询抵押记录
message ReqIssuanceRecords {
string issuanceId = 1;
string addr = 2;
int32 status = 3;
string debtId = 4;
}
// 返回记录列表
message RepIssuanceRecords {
repeated DebtRecord records = 1;
}
// 返回记录
message RepIssuanceDebtInfo {
DebtRecord record = 1;
}
// 返回最新抵押物价格
message RepIssuancePrice {
int64 price = 1; //当前抵押物最新价格
}
// 返回用户发行总额
message RepIssuanceUserBalance {
int64 balance = 1; //返回用户发行总额
}
\ No newline at end of file
## 发行合约表结构
### 总发行表issuer表结构
字段名称|类型|说明
---|---|---
issuanceId|string|总发行ID,主键
status|int32|发行状态(1:已发行 2:已下线)
### 总发行表issuer表索引
索引名|说明
---|---
status|根据发行状态查询总发行ID
### 大户发行表debt表结构
字段名称|类型|说明
---|---|---
debtId|string|大户发行ID,主键
issuanceId|string|总发行ID
accountAddr|string|用户地址
status|int32|发行状态(1:已发行 2:价格清算告警 3:价格清算 4:超时清算告警 5:超时清算 6:关闭)
### 大户发行表debt表索引
索引名|说明
---|---
status|根据大户发行状态查询大户发行ID
addr|根据大户地址查询大户发行ID
addr_status|根据发行状态和大户地址查询大户发行ID
// 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"
// Errors for lottery
var (
ErrRiskParam = errors.New("ErrRiskParam")
ErrIssuanceRepeatHash = errors.New("ErrIssuanceRepeatHash")
ErrIssuanceStatus = errors.New("ErrIssuanceStatus")
ErrIssuanceExceedDebtCeiling = errors.New("ErrIssuanceExceedDebtCeiling")
ErrPriceInvalid = errors.New("ErrPriceInvalid")
ErrAssetType = errors.New("ErrAssetType")
ErrRecordNotExist = errors.New("ErrRecordNotExist")
ErrIssuanceErrCloser = errors.New("ErrIssuanceErrCloser")
ErrRepayValueInsufficient = errors.New("ErrRepayValueInsufficient")
ErrIssuanceAccountExist = errors.New("ErrIssuanceAccountExist")
ErrIssuanceLowBalance = errors.New("ErrIssuanceLowBalance")
ErrIssuanceBalanceInvalid = errors.New("ErrIssuanceBalanceInvalid")
ErrPermissionDeny = errors.New("ErrPermissionDeny")
ErrIssuanceRecordNotEmpty = errors.New("ErrIssuanceRecordNotEmpty")
)
// 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"
"reflect"
"github.com/33cn/chain33/common/address"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types"
)
var (
llog = log.New("module", "exectype."+IssuanceX)
)
func init() {
types.AllowUserExec = append(types.AllowUserExec, []byte(IssuanceX))
types.RegFork(IssuanceX, InitFork)
types.RegExec(IssuanceX, InitExecutor)
}
func InitFork(cfg *types.Chain33Config) {
cfg.RegisterDappFork(IssuanceX, "Enable", 0)
}
func InitExecutor(cfg *types.Chain33Config) {
types.RegistorExecutor(IssuanceX, NewType(cfg))
}
// IssuanceType def
type IssuanceType struct {
types.ExecTypeBase
}
// NewType method
func NewType(cfg *types.Chain33Config) *IssuanceType {
c := &IssuanceType{}
c.SetChild(c)
c.SetConfig(cfg)
return c
}
// GetName 获取执行器名称
func (issuance *IssuanceType) GetName() string {
return IssuanceX
}
// GetLogMap method
func (issuance *IssuanceType) GetLogMap() map[int64]*types.LogInfo {
return map[int64]*types.LogInfo{
TyLogIssuanceCreate: {Ty: reflect.TypeOf(ReceiptIssuance{}), Name: "LogIssuanceCreate"},
TyLogIssuanceDebt: {Ty: reflect.TypeOf(ReceiptIssuance{}), Name: "LogIssuanceDebt"},
TyLogIssuanceRepay: {Ty: reflect.TypeOf(ReceiptIssuance{}), Name: "LogIssuanceRepay"},
TyLogIssuanceFeed: {Ty: reflect.TypeOf(ReceiptIssuance{}), Name: "LogIssuanceFeed"},
TyLogIssuanceClose: {Ty: reflect.TypeOf(ReceiptIssuance{}), Name: "LogIssuanceClose"},
}
}
// GetPayload method
func (issuance *IssuanceType) GetPayload() types.Message {
return &IssuanceAction{}
}
// CreateTx method
func (issuance IssuanceType) CreateTx(action string, message json.RawMessage) (*types.Transaction, error) {
llog.Debug("Issuance.CreateTx", "action", action)
cfg := issuance.GetConfig()
if action == "IssuanceCreate" {
var param IssuanceCreateTx
err := json.Unmarshal(message, &param)
if err != nil {
llog.Error("CreateTx", "Error", err)
return nil, types.ErrInvalidParam
}
return CreateRawIssuanceCreateTx(cfg, &param)
} else if action == "IssuanceDebt" {
var param IssuanceDebtTx
err := json.Unmarshal(message, &param)
if err != nil {
llog.Error("CreateTx", "Error", err)
return nil, types.ErrInvalidParam
}
return CreateRawIssuanceDebtTx(cfg, &param)
} else if action == "IssuanceRepay" {
var param IssuanceRepayTx
err := json.Unmarshal(message, &param)
if err != nil {
llog.Error("CreateTx", "Error", err)
return nil, types.ErrInvalidParam
}
return CreateRawIssuanceRepayTx(cfg, &param)
} else if action == "IssuancePriceFeed" {
var param IssuanceFeedTx
err := json.Unmarshal(message, &param)
if err != nil {
llog.Error("CreateTx", "Error", err)
return nil, types.ErrInvalidParam
}
return CreateRawIssuanceFeedTx(cfg, &param)
} else if action == "IssuanceClose" {
var param IssuanceCloseTx
err := json.Unmarshal(message, &param)
if err != nil {
llog.Error("CreateTx", "Error", err)
return nil, types.ErrInvalidParam
}
return CreateRawIssuanceCloseTx(cfg, &param)
} else if action == "IssuanceManage" {
var param IssuanceManageTx
err := json.Unmarshal(message, &param)
if err != nil {
llog.Error("CreateTx", "Error", err)
return nil, types.ErrInvalidParam
}
return CreateRawIssuanceManageTx(cfg, &param)
} else {
return nil, types.ErrNotSupport
}
}
// GetTypeMap method
func (issuance IssuanceType) GetTypeMap() map[string]int32 {
return map[string]int32{
"Create": IssuanceActionCreate,
"Debt": IssuanceActionDebt,
"Repay": IssuanceActionRepay,
"Feed": IssuanceActionFeed,
"Close": IssuanceActionClose,
"Manage": IssuanceActionManage,
}
}
// CreateRawIssuanceCreateTx method
func CreateRawIssuanceCreateTx(cfg *types.Chain33Config, parm *IssuanceCreateTx) (*types.Transaction, error) {
if parm == nil {
llog.Error("CreateRawIssuanceCreateTx", "parm", parm)
return nil, types.ErrInvalidParam
}
v := &IssuanceCreate{
TotalBalance: int64(math.Trunc((parm.TotalBalance+0.0000001)*1e4)) * 1e4,
DebtCeiling: int64(math.Trunc((parm.DebtCeiling+0.0000001)*1e4)) * 1e4,
LiquidationRatio: int64(math.Trunc((parm.LiquidationRatio + 0.0000001) * 1e4)),
Period: parm.Period,
}
create := &IssuanceAction{
Ty: IssuanceActionCreate,
Value: &IssuanceAction_Create{v},
}
tx := &types.Transaction{
Execer: []byte(cfg.ExecName(IssuanceX)),
Payload: types.Encode(create),
Fee: parm.Fee,
To: address.ExecAddress(cfg.ExecName(IssuanceX)),
}
name := cfg.ExecName(IssuanceX)
tx, err := types.FormatTx(cfg, name, tx)
if err != nil {
return nil, err
}
return tx, nil
}
// CreateRawIssuanceDebtTx method
func CreateRawIssuanceDebtTx(cfg *types.Chain33Config, parm *IssuanceDebtTx) (*types.Transaction, error) {
if parm == nil {
llog.Error("CreateRawIssuanceBorrowTx", "parm", parm)
return nil, types.ErrInvalidParam
}
v := &IssuanceDebt{
IssuanceId: parm.IssuanceID,
Value: int64(math.Trunc((parm.Value+0.0000001)*1e4)) * 1e4,
}
debt := &IssuanceAction{
Ty: IssuanceActionDebt,
Value: &IssuanceAction_Debt{v},
}
tx := &types.Transaction{
Execer: []byte(cfg.ExecName(IssuanceX)),
Payload: types.Encode(debt),
Fee: parm.Fee,
To: address.ExecAddress(cfg.ExecName(IssuanceX)),
}
name := cfg.ExecName(IssuanceX)
tx, err := types.FormatTx(cfg, name, tx)
if err != nil {
return nil, err
}
return tx, nil
}
// CreateRawIssuanceRepayTx method
func CreateRawIssuanceRepayTx(cfg *types.Chain33Config, parm *IssuanceRepayTx) (*types.Transaction, error) {
if parm == nil {
llog.Error("CreateRawIssuanceRepayTx", "parm", parm)
return nil, types.ErrInvalidParam
}
v := &IssuanceRepay{
IssuanceId: parm.IssuanceID,
DebtId: parm.DebtID,
}
repay := &IssuanceAction{
Ty: IssuanceActionRepay,
Value: &IssuanceAction_Repay{v},
}
tx := &types.Transaction{
Execer: []byte(cfg.ExecName(IssuanceX)),
Payload: types.Encode(repay),
Fee: parm.Fee,
To: address.ExecAddress(cfg.ExecName(IssuanceX)),
}
name := cfg.ExecName(IssuanceX)
tx, err := types.FormatTx(cfg, name, tx)
if err != nil {
return nil, err
}
return tx, nil
}
// CreateRawIssuanceFeedTx method
func CreateRawIssuanceFeedTx(cfg *types.Chain33Config, parm *IssuanceFeedTx) (*types.Transaction, error) {
if parm == nil {
llog.Error("CreateRawIssuancePriceFeedTx", "parm", parm)
return nil, types.ErrInvalidParam
}
v := &IssuanceFeed{
Volume: parm.Volume,
}
for _, r := range parm.Price {
v.Price = append(v.Price, int64(math.Trunc(r*1e4)))
}
feed := &IssuanceAction{
Ty: IssuanceActionFeed,
Value: &IssuanceAction_Feed{v},
}
tx := &types.Transaction{
Execer: []byte(cfg.ExecName(IssuanceX)),
Payload: types.Encode(feed),
Fee: parm.Fee,
To: address.ExecAddress(cfg.ExecName(IssuanceX)),
}
name := cfg.ExecName(IssuanceX)
tx, err := types.FormatTx(cfg, name, tx)
if err != nil {
return nil, err
}
return tx, nil
}
// CreateRawIssuanceCloseTx method
func CreateRawIssuanceCloseTx(cfg *types.Chain33Config, parm *IssuanceCloseTx) (*types.Transaction, error) {
if parm == nil {
llog.Error("CreateRawIssuanceCloseTx", "parm", parm)
return nil, types.ErrInvalidParam
}
v := &IssuanceClose{
IssuanceId: parm.IssuanceID,
}
close := &IssuanceAction{
Ty: IssuanceActionClose,
Value: &IssuanceAction_Close{v},
}
tx := &types.Transaction{
Execer: []byte(cfg.ExecName(IssuanceX)),
Payload: types.Encode(close),
Fee: parm.Fee,
To: address.ExecAddress(cfg.ExecName(IssuanceX)),
}
name := cfg.ExecName(IssuanceX)
tx, err := types.FormatTx(cfg, name, tx)
if err != nil {
return nil, err
}
return tx, nil
}
// CreateRawIssuanceManageTx method
func CreateRawIssuanceManageTx(cfg *types.Chain33Config, parm *IssuanceManageTx) (*types.Transaction, error) {
if parm == nil {
llog.Error("CreateRawIssuanceManageTx", "parm", parm)
return nil, types.ErrInvalidParam
}
v := &IssuanceManage{SuperAddrs: parm.Addr}
manage := &IssuanceAction{
Ty: IssuanceActionManage,
Value: &IssuanceAction_Manage{v},
}
tx := &types.Transaction{
Execer: []byte(cfg.ExecName(IssuanceX)),
Payload: types.Encode(manage),
Fee: parm.Fee,
To: address.ExecAddress(cfg.ExecName(IssuanceX)),
}
name := cfg.ExecName(IssuanceX)
tx, err := types.FormatTx(cfg, name, tx)
if err != nil {
return nil, err
}
return tx, nil
}
This diff is collapsed.
package types
import (
"fmt"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/db/table"
"github.com/33cn/chain33/types"
)
var opt = &table.Option{
Prefix: "LODB-issuance",
Name: "issuer",
Primary: "issuanceid",
Index: []string{"status"},
}
//NewTable 新建表
func NewIssuanceTable(kvdb db.KV) *table.Table {
rowmeta := NewIssuanceRow()
table, err := table.NewTable(rowmeta, kvdb, opt)
if err != nil {
panic(err)
}
return table
}
//IssuanceRow table meta 结构
type IssuanceRow struct {
*ReceiptIssuanceID
}
//NewIssuanceRow 新建一个meta 结构
func NewIssuanceRow() *IssuanceRow {
return &IssuanceRow{ReceiptIssuanceID: &ReceiptIssuanceID{}}
}
//CreateRow 新建数据行
func (tx *IssuanceRow) CreateRow() *table.Row {
return &table.Row{Data: &ReceiptIssuanceID{}}
}
//SetPayload 设置数据
func (tx *IssuanceRow) SetPayload(data types.Message) error {
if txdata, ok := data.(*ReceiptIssuanceID); ok {
tx.ReceiptIssuanceID = txdata
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (tx *IssuanceRow) Get(key string) ([]byte, error) {
if key == "issuanceid" {
return []byte(tx.IssuanceId), nil
} else if key == "status" {
return []byte(fmt.Sprintf("%2d", tx.Status)), nil
}
return nil, types.ErrNotFound
}
var optRecord = &table.Option{
Prefix: "LODB-issuance",
Name: "debt",
Primary: "debtid",
Index: []string{"status", "addr", "addr_status"},
}
// NewRecordTable 大户发行记录表
func NewRecordTable(kvdb db.KV) *table.Table {
rowmeta := NewRecordRow()
table, err := table.NewTable(rowmeta, kvdb, optRecord)
if err != nil {
panic(err)
}
return table
}
//IssuanceRow table meta 结构
type IssuanceRecordRow struct {
*ReceiptIssuance
}
//NewIssuanceRow 新建一个meta 结构
func NewRecordRow() *IssuanceRecordRow {
return &IssuanceRecordRow{ReceiptIssuance: &ReceiptIssuance{}}
}
//CreateRow 新建数据行
func (tx *IssuanceRecordRow) CreateRow() *table.Row {
return &table.Row{Data: &ReceiptIssuance{}}
}
//SetPayload 设置数据
func (tx *IssuanceRecordRow) SetPayload(data types.Message) error {
if txdata, ok := data.(*ReceiptIssuance); ok {
tx.ReceiptIssuance = txdata
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (tx *IssuanceRecordRow) Get(key string) ([]byte, error) {
if key == "debtid" {
return []byte(tx.DebtId), nil
} else if key == "status" {
return []byte(fmt.Sprintf("%2d", tx.Status)), nil
} else if key == "addr" {
return []byte(tx.AccountAddr), nil
} else if key == "addr_status" {
return []byte(fmt.Sprintf("%s:%2d", tx.AccountAddr, tx.Status)), nil
}
return nil, types.ErrNotFound
}
// 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
// IssuanceCreateTx for construction
type IssuanceCreateTx struct {
DebtCeiling float64 `json:"debtCeiling"`
LiquidationRatio float64 `json:"liquidationRatio"`
Period int64 `json:"period"`
TotalBalance float64 `json:"totalBalance"`
Fee int64 `json:"fee"`
}
// IssuanceDebtTx for construction
type IssuanceDebtTx struct {
IssuanceID string `json:"issuanceId"`
Value float64 `json:"value"`
Fee int64 `json:"fee"`
}
// IssuanceRepayTx for construction
type IssuanceRepayTx struct {
IssuanceID string `json:"issuanceId"`
DebtID string `json:"debtId"`
Fee int64 `json:"fee"`
}
// IssuanceFeedTx for construction
type IssuanceFeedTx struct {
Price []float64 `json:"price"`
Volume []int64 `json:"volume"`
Fee int64 `json:"fee"`
}
// IssuanceCloseTx for construction
type IssuanceCloseTx struct {
IssuanceID string `json:"issuanceId"`
Fee int64 `json:"fee"`
}
// IssuanceManageTx for construction
type IssuanceManageTx struct {
Addr []string `json:"addr"`
Fee int64 `json:"fee"`
}
// 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
//Issuance op
const (
IssuanceActionCreate = 1 + iota // 创建借贷
IssuanceActionDebt // 大户抵押
IssuanceActionRepay // 大户清算
IssuanceActionFeed // 发行合约喂价
IssuanceActionClose // 关闭借贷
IssuanceActionManage // 借贷管理
//log for Issuance
TyLogIssuanceCreate = 741
TyLogIssuanceDebt = 742
TyLogIssuanceRepay = 743
TyLogIssuanceFeed = 745
TyLogIssuanceClose = 746
)
// Issuance name
const (
IssuanceX = "issuance"
CCNYTokenName = "CCNY"
IssuancePreLiquidationRatio = 11000 //TODO 预清算比例,抵押物价值跌到借出ccny价值110%的时候开始清算
)
//Issuance status
const (
IssuanceStatusCreated = 1 + iota
IssuanceStatusClose
)
const (
IssuanceUserStatusCreate = 1 + iota
IssuanceUserStatusWarning
IssuanceUserStatusSystemLiquidate
IssuanceUserStatusExpire
IssuanceUserStatusExpireLiquidate
IssuanceUserStatusClose
)
const (
PriceFeedKey = "issuance-price-feed"
GuarantorKey = "issuance-guarantor"
ManageKey = "issuance-manage"
)
// 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/33cn/chain33/types"
"github.com/33cn/chain33/util"
pty "github.com/33cn/plugin/plugin/dapp/privacy/types"
"github.com/stretchr/testify/assert"
)
const (
testStateCheck = iota + 1
testStateExec
testStateExecLocal
testStateExecDelLocal
)
func testExec(mock *testExecMock, tcArr []*testcase, priv string, t *testing.T) {
exec := mock.exec
for i, tc := range tcArr {
signPriv := priv
if tc.priv != "" {
signPriv = tc.priv
}
tx, err := createTx(mock, tc.payload, signPriv, tc.systemCreate)
assert.NoErrorf(t, err, "createTxErr, testIndex=%d", tc.index)
if err != nil {
continue
}
if len(tc.testSign) > 0 {
tx.Signature.Signature = append([]byte(""), tc.testSign...)
}
if tc.testFee > 0 {
tx.Fee = tc.testFee
}
err = exec.CheckTx(tx, i)
assert.Equalf(t, tc.expectCheckErr, err, "checkTx err index %d", tc.index)
if tc.testState == testStateCheck {
continue
}
recp, err := exec.Exec(tx, i)
recpData := &types.ReceiptData{
Ty: recp.GetTy(),
Logs: recp.GetLogs(),
}
if err == nil && len(recp.GetKV()) > 0 {
util.SaveKVList(mock.stateDB, recp.KV)
mock.addBlockTx(tx, recpData)
}
assert.Equalf(t, tc.expectExecErr, err, "execTx err index %d", tc.index)
if tc.testState == testStateExec {
continue
}
kvSet, err := exec.ExecLocal(tx, recpData, i)
for _, kv := range kvSet.GetKV() {
err := mock.localDB.Set(kv.Key, kv.Value)
assert.Nil(t, err)
}
assert.Equalf(t, tc.expectExecLocalErr, err, "execLocalTx err index %d", tc.index)
if tc.testState == testStateExecLocal {
continue
}
kvSet, err = exec.ExecDelLocal(tx, recpData, i)
for _, kv := range kvSet.GetKV() {
err := mock.localDB.Set(kv.Key, kv.Value)
assert.Nil(t, err)
}
assert.Equalf(t, tc.expectExecDelErr, err, "execDelLocalTx err index %d", tc.index)
}
}
func TestPrivacy_CheckTx(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
//用于测试双花
testKeyImage := []byte("testKeyImage")
mock.stateDB.Set(calcPrivacyKeyImageKey("coins", "bty", testKeyImage), []byte("testval"))
tcArr := []*testcase{
{
index: 1,
payload: &pty.Public2Privacy{},
expectCheckErr: types.ErrInvalidParam,
},
{
index: 2,
payload: &pty.Public2Privacy{Tokenname: "bty"},
},
{
index: 4,
payload: &pty.Privacy2Public{Tokenname: "bty", Input: &pty.PrivacyInput{Keyinput: []*pty.KeyInput{}}},
expectCheckErr: pty.ErrNilUtxoInput,
},
{
index: 5,
payload: &pty.Privacy2Privacy{Tokenname: "bty", Input: &pty.PrivacyInput{Keyinput: []*pty.KeyInput{{}}}},
expectCheckErr: pty.ErrNilUtxoOutput,
},
{
index: 6,
payload: &pty.Privacy2Public{Tokenname: "bty", Input: &pty.PrivacyInput{Keyinput: []*pty.KeyInput{{}}}},
expectCheckErr: pty.ErrRingSign,
},
{
index: 7,
payload: &pty.Privacy2Public{Tokenname: "bty", Input: &pty.PrivacyInput{Keyinput: []*pty.KeyInput{{KeyImage: testKeyImage}}}},
expectCheckErr: pty.ErrDoubleSpendOccur,
testSign: types.Encode(&types.RingSignature{Items: []*types.RingSignatureItem{{Pubkey: [][]byte{[]byte("test")}}}}),
},
{
index: 8,
payload: &pty.Privacy2Public{Tokenname: "bty", Input: &pty.PrivacyInput{Keyinput: []*pty.KeyInput{{UtxoGlobalIndex: []*pty.UTXOGlobalIndex{{}}}}}},
expectCheckErr: pty.ErrPubkeysOfUTXO,
testSign: types.Encode(&types.RingSignature{Items: []*types.RingSignatureItem{{Pubkey: [][]byte{[]byte("test")}}}}),
},
{
index: 9,
payload: &pty.Privacy2Public{Tokenname: "bty", Input: &pty.PrivacyInput{Keyinput: []*pty.KeyInput{{}}}},
expectCheckErr: pty.ErrPrivacyTxFeeNotEnough,
testSign: types.Encode(&types.RingSignature{Items: []*types.RingSignatureItem{{Pubkey: [][]byte{[]byte("test")}}}}),
},
{
index: 10,
payload: &pty.Privacy2Public{Tokenname: "bty", Input: &pty.PrivacyInput{Keyinput: []*pty.KeyInput{{}}}},
expectCheckErr: pty.ErrPrivacyTxFeeNotEnough,
testSign: types.Encode(&types.RingSignature{Items: []*types.RingSignatureItem{{Pubkey: [][]byte{[]byte("test")}}}}),
testFee: pty.PrivacyTxFee,
},
}
for _, tc := range tcArr {
tc.systemCreate = true
tc.testState = testStateCheck
}
testExec(mock, tcArr, testPrivateKeys[0], t)
}
func TestPrivacy_Exec_Public2Privacy(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
tcArr := []*testcase{
{
index: 1,
payload: &pty.ReqCreatePrivacyTx{
AssetExec: "btc-coins",
Tokenname: "btc",
Amount: types.Coin,
Pubkeypair: testPubkeyPairs[0],
},
expectExecErr: types.ErrExecNameNotAllow,
},
{
index: 2,
payload: &pty.ReqCreatePrivacyTx{
Amount: types.Coin * 10001,
Pubkeypair: testPubkeyPairs[0],
},
expectExecErr: types.ErrNoBalance,
},
{
index: 2,
payload: &pty.ReqCreatePrivacyTx{
Amount: types.Coin,
Pubkeypair: testPubkeyPairs[0],
},
},
}
for _, tc := range tcArr {
req := tc.payload.(*pty.ReqCreatePrivacyTx)
req.Type = types.PrivacyTypePublic2Privacy
tc.testState = testStateExec
}
testExec(mock, tcArr, testPrivateKeys[0], t)
}
func TestPrivacy_Exec_Privacy2Privacy(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
tcArr := []*testcase{
{
index: 1,
payload: &pty.ReqCreatePrivacyTx{
Type: types.PrivacyTypePublic2Privacy,
Amount: types.Coin * 9,
Pubkeypair: testPubkeyPairs[0],
},
},
{
index: 2,
payload: &pty.ReqCreatePrivacyTx{
Amount: types.Coin,
Pubkeypair: testPubkeyPairs[1],
From: testAddrs[0],
},
},
}
for _, tc := range tcArr {
req := tc.payload.(*pty.ReqCreatePrivacyTx)
if req.Type == 0 {
req.Type = types.PrivacyTypePrivacy2Privacy
}
tc.testState = testStateExec
}
testExec(mock, tcArr, testPrivateKeys[0], t)
}
func TestPrivacy_Exec_Privacy2Public(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
tcArr := []*testcase{
{
index: 1,
payload: &pty.ReqCreatePrivacyTx{
Type: types.PrivacyTypePublic2Privacy,
Amount: types.Coin * 9,
Pubkeypair: testPubkeyPairs[0],
},
},
{
index: 2,
payload: &pty.ReqCreatePrivacyTx{
Amount: types.Coin,
Pubkeypair: testPubkeyPairs[1],
From: testAddrs[0],
To: testAddrs[1],
},
},
}
for _, tc := range tcArr {
req := tc.payload.(*pty.ReqCreatePrivacyTx)
if req.Type == 0 {
req.Type = types.PrivacyTypePrivacy2Public
}
tc.testState = testStateExec
}
testExec(mock, tcArr, testPrivateKeys[0], t)
}
func TestPrivacy_ExecLocal(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
tcArr := []*testcase{
{
index: 1,
payload: &pty.ReqCreatePrivacyTx{
Type: types.PrivacyTypePublic2Privacy,
Amount: types.Coin * 9,
Pubkeypair: testPubkeyPairs[0],
},
},
{
index: 2,
payload: &pty.ReqCreatePrivacyTx{
Type: types.PrivacyTypePrivacy2Privacy,
Amount: types.Coin,
Pubkeypair: testPubkeyPairs[1],
From: testAddrs[0],
},
},
{
index: 3,
payload: &pty.ReqCreatePrivacyTx{
Type: types.PrivacyTypePrivacy2Public,
Amount: types.Coin,
Pubkeypair: testPubkeyPairs[1],
From: testAddrs[0],
To: testAddrs[1],
},
},
}
for _, tc := range tcArr {
tc.testState = testStateExecLocal
}
testExec(mock, tcArr, testPrivateKeys[0], t)
}
func TestPrivacy_ExecDelLocal(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
tcArr := []*testcase{
{
index: 1,
payload: &pty.ReqCreatePrivacyTx{
Type: types.PrivacyTypePublic2Privacy,
Amount: types.Coin * 9,
Pubkeypair: testPubkeyPairs[0],
},
},
{
index: 2,
payload: &pty.ReqCreatePrivacyTx{
Type: types.PrivacyTypePrivacy2Privacy,
Amount: types.Coin,
Pubkeypair: testPubkeyPairs[1],
From: testAddrs[0],
},
},
{
index: 3,
payload: &pty.ReqCreatePrivacyTx{
Type: types.PrivacyTypePrivacy2Public,
Amount: types.Coin,
Pubkeypair: testPubkeyPairs[1],
From: testAddrs[0],
To: testAddrs[1],
},
},
}
for _, tc := range tcArr {
tc.testState = testStateExecDelLocal
}
testExec(mock, tcArr, testPrivateKeys[0], t)
}
...@@ -206,7 +206,7 @@ func (p *privacy) CheckTx(tx *types.Transaction, index int) error { ...@@ -206,7 +206,7 @@ func (p *privacy) CheckTx(tx *types.Transaction, index int) error {
err := types.Decode(tx.Payload, &action) err := types.Decode(tx.Payload, &action)
if err != nil { if err != nil {
privacylog.Error("PrivacyTrading CheckTx", "txhash", txhashstr, "Decode tx.Payload error", err) privacylog.Error("PrivacyTrading CheckTx", "txhash", txhashstr, "Decode tx.Payload error", err)
return err return types.ErrActionNotSupport
} }
privacylog.Debug("PrivacyTrading CheckTx", "txhash", txhashstr, "action type ", action.Ty) privacylog.Debug("PrivacyTrading CheckTx", "txhash", txhashstr, "action type ", action.Ty)
assertExec := action.GetAssertExec() assertExec := action.GetAssertExec()
...@@ -214,32 +214,32 @@ func (p *privacy) CheckTx(tx *types.Transaction, index int) error { ...@@ -214,32 +214,32 @@ func (p *privacy) CheckTx(tx *types.Transaction, index int) error {
if token == "" { if token == "" {
return types.ErrInvalidParam return types.ErrInvalidParam
} }
if pty.ActionPublic2Privacy == action.Ty { if pty.ActionPublic2Privacy == action.Ty && action.GetPublic2Privacy() != nil {
return nil return nil
} }
input := action.GetInput() input := action.GetInput()
output := action.GetOutput() //无论是私对私还是私对公, input都不能为空
if input == nil || output == nil { if len(input.GetKeyinput()) == 0 {
privacylog.Error("PrivacyTrading CheckTx", "txhash", txhashstr, "input", input, "output", output) privacylog.Error("PrivacyTrading CheckTx", "txhash", txhashstr)
return nil return pty.ErrNilUtxoInput
} }
//如果是私到私 或者私到公,交易费扣除则需要utxo实现,交易费并不生成真正的UTXO,也是即时燃烧掉而已
var amount int64
keyinput := input.Keyinput
if action.Ty == pty.ActionPrivacy2Public && action.GetPrivacy2Public() != nil { output := action.GetOutput()
amount = action.GetPrivacy2Public().Amount //私对私必须有utxo输出
if action.GetPrivacy2Privacy() != nil && len(output.GetKeyoutput()) == 0 {
privacylog.Error("PrivacyTrading CheckTx", "txhash", txhashstr)
return pty.ErrNilUtxoOutput
} }
// check sign
var ringSignature types.RingSignature var ringSignature types.RingSignature
if err := types.Decode(tx.Signature.Signature, &ringSignature); err != nil { if err := types.Decode(tx.Signature.Signature, &ringSignature); err != nil {
privacylog.Error("PrivacyTrading CheckTx", "txhash", txhashstr, "Decode tx.Signature.Signature error ", err) privacylog.Error("PrivacyTrading CheckTx", "txhash", txhashstr, "Decode tx.Signature.Signature error ", err)
return err return pty.ErrRingSign
} }
totalInput := int64(0) totalInput := int64(0)
totalOutput := int64(0) keyinput := input.GetKeyinput()
inputCnt := len(keyinput) keyImages := make([][]byte, len(keyinput))
keyImages := make([][]byte, inputCnt)
keys := make([][]byte, 0) keys := make([][]byte, 0)
pubkeys := make([][]byte, 0) pubkeys := make([][]byte, 0)
for i, input := range keyinput { for i, input := range keyinput {
...@@ -273,18 +273,20 @@ func (p *privacy) CheckTx(tx *types.Transaction, index int) error { ...@@ -273,18 +273,20 @@ func (p *privacy) CheckTx(tx *types.Transaction, index int) error {
cfg := p.GetAPI().GetConfig() cfg := p.GetAPI().GetConfig()
if !cfg.IsPara() && (assertExec == "" || assertExec == "coins") { if !cfg.IsPara() && (assertExec == "" || assertExec == "coins") {
for _, output := range output.Keyoutput { totalOutput := int64(0)
totalOutput += output.Amount for _, output := range output.GetKeyoutput() {
totalOutput += output.GetAmount()
} }
if tx.Fee < pty.PrivacyTxFee { if tx.Fee < pty.PrivacyTxFee {
privacylog.Error("PrivacyTrading CheckTx", "txhash", txhashstr, "fee set:", tx.Fee, "required:", pty.PrivacyTxFee, " error ErrPrivacyTxFeeNotEnough") privacylog.Error("PrivacyTrading CheckTx", "txhash", txhashstr, "fee set:", tx.Fee, "required:", pty.PrivacyTxFee, " error ErrPrivacyTxFeeNotEnough")
return pty.ErrPrivacyTxFeeNotEnough return pty.ErrPrivacyTxFeeNotEnough
} }
//如果是私到私 或者私到公,交易费扣除则需要utxo实现,交易费并不生成真正的UTXO,也是即时燃烧掉而已
var feeAmount int64 var feeAmount int64
if action.Ty == pty.ActionPrivacy2Privacy { if action.Ty == pty.ActionPrivacy2Privacy {
feeAmount = totalInput - totalOutput feeAmount = totalInput - totalOutput
} else { } else if action.Ty == pty.ActionPrivacy2Public && action.GetPrivacy2Public() != nil {
feeAmount = totalInput - totalOutput - amount feeAmount = totalInput - totalOutput - action.GetPrivacy2Public().Amount
} }
if feeAmount < pty.PrivacyTxFee { if feeAmount < pty.PrivacyTxFee {
......
// 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/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/privacy/types"
"github.com/stretchr/testify/assert"
)
var (
execTestCases = []*testcase{
{
index: 1,
payload: &pty.ReqCreatePrivacyTx{
Type: types.PrivacyTypePublic2Privacy,
Amount: types.Coin,
Pubkeypair: testPubkeyPairs[0],
},
},
}
)
type queryTestCase struct {
index int
funcName string
params types.Message
expectErr error
expectReply types.Message
disableReplyCheck bool
}
func testQuery(mock *testExecMock, tcArr []*queryTestCase, t *testing.T) {
for _, tc := range tcArr {
reply, err := mock.exec.Query(tc.funcName, types.Encode(tc.params))
assert.Equalf(t, tc.expectErr, err, "queryTest index=%d", tc.index)
if err == nil && !tc.disableReplyCheck {
assert.Equalf(t, tc.expectReply, reply, "queryTest index=%d", tc.index)
}
}
}
func TestPrivacy_Query_ShowAmountsOfUTXO(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
for _, tc := range execTestCases {
tc.testState = testStateExecLocal
}
testExec(mock, execTestCases, testPrivateKeys[0], t)
queryCases := []*queryTestCase{
{
index: 1,
params: &pty.ReqPrivacyToken{
Token: "btc",
},
expectErr: types.ErrNotFound,
},
{
index: 2,
params: &pty.ReqPrivacyToken{
Token: "bty",
},
expectReply: &pty.ReplyPrivacyAmounts{
AmountDetail: []*pty.AmountDetail{
{Amount: types.Coin, Count: 1},
},
},
},
}
for _, tc := range queryCases {
tc.funcName = "ShowAmountsOfUTXO"
}
testQuery(mock, queryCases, t)
}
func TestPrivacy_Query_ShowUTXOs4SpecifiedAmount(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
for _, tc := range execTestCases {
tc.testState = testStateExecLocal
}
testExec(mock, execTestCases, testPrivateKeys[0], t)
queryCases := []*queryTestCase{
{
index: 1,
params: &pty.ReqPrivacyToken{
Token: "bty",
},
expectErr: types.ErrNotFound,
},
{
index: 2,
params: &pty.ReqPrivacyToken{
Token: "bty",
Amount: types.Coin,
},
disableReplyCheck: true,
},
}
for _, tc := range queryCases {
tc.funcName = "ShowUTXOs4SpecifiedAmount"
}
testQuery(mock, queryCases, t)
}
func TestPrivacy_Query_GetUTXOGlobalIndex(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
for _, tc := range execTestCases {
tc.testState = testStateExecLocal
}
testExec(mock, execTestCases, testPrivateKeys[0], t)
queryCases := []*queryTestCase{
{
index: 1,
params: &pty.ReqUTXOGlobalIndex{},
disableReplyCheck: true,
},
{
index: 2,
params: &pty.ReqUTXOGlobalIndex{
Tokenname: "btc",
MixCount: 1,
Amount: []int64{types.Coin},
},
disableReplyCheck: true,
expectErr: types.ErrNotFound,
},
{
index: 3,
params: &pty.ReqUTXOGlobalIndex{
Tokenname: "bty",
MixCount: 1,
Amount: []int64{types.Coin, types.Coin * 2},
},
disableReplyCheck: true,
expectErr: types.ErrNotFound,
},
{
index: 4,
params: &pty.ReqUTXOGlobalIndex{
Tokenname: "bty",
MixCount: 1,
Amount: []int64{types.Coin},
},
disableReplyCheck: true,
},
}
for _, tc := range queryCases {
tc.funcName = "GetUTXOGlobalIndex"
}
testQuery(mock, queryCases, t)
}
func TestPrivacy_Query_GetTxsByAddr(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
for _, tc := range execTestCases {
tc.testState = testStateExecLocal
}
testExec(mock, execTestCases, testPrivateKeys[0], t)
queryCases := []*queryTestCase{
{
index: 1,
params: &types.ReqAddr{
Addr: testAddrs[0],
},
expectErr: types.ErrNotFound,
},
}
for _, tc := range queryCases {
tc.funcName = "GetTxsByAddr"
}
testQuery(mock, queryCases, t)
}
// 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 (
"errors"
"fmt"
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/client"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/crypto"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/log"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/wallet"
wcom "github.com/33cn/chain33/wallet/common"
pty "github.com/33cn/plugin/plugin/dapp/privacy/types"
pwallet "github.com/33cn/plugin/plugin/dapp/privacy/wallet"
)
var (
initBalance = types.Coin * 10000
initHeight = int64(100)
// 测试的私钥
testPrivateKeys = []string{
"0x8dea7332c7bb3e3b0ce542db41161fd021e3cfda9d7dabacf24f98f2dfd69558",
"0x920976ffe83b5a98f603b999681a0bc790d97e22ffc4e578a707c2234d55cc8a",
"0xb59f2b02781678356c231ad565f73699753a28fd3226f1082b513ebf6756c15c",
}
// 测试的地址
testAddrs = []string{
"1EDDghAtgBsamrNEtNmYdQzC1QEhLkr87t",
"13cS5G1BDN2YfGudsxRxr7X25yu6ZdgxMU",
"1JSRSwp16NvXiTjYBYK9iUQ9wqp3sCxz2p",
}
// 测试的隐私公钥对
testPubkeyPairs = []string{
"92fe6cfec2e19cd15f203f83b5d440ddb63d0cb71559f96dc81208d819fea85886b08f6e874fca15108d244b40f9086d8c03260d4b954a40dfb3cbe41ebc7389",
"6326126c968a93a546d8f67d623ad9729da0e3e4b47c328a273dfea6930ffdc87bcc365822b80b90c72d30e955e7870a7a9725e9a946b9e89aec6db9455557eb",
"44bf54abcbae297baf3dec4dd998b313eafb01166760f0c3a4b36509b33d3b50239de0a5f2f47c2fc98a98a382dcd95a2c5bf1f4910467418a3c2595b853338e",
}
// exec privacy addr
execAddr = "1FeyE6VDZ4FYgpK1n2okWMDAtPkwBuooQd"
testPolicy = pwallet.New()
testPolicyName = pty.PrivacyX + "test"
testCfg = types.NewChain33Config(types.GetDefaultCfgstring())
)
func init() {
log.SetLogLevel("error")
Init(pty.PrivacyX, testCfg, nil)
wcom.RegisterPolicy(testPolicyName, testPolicy)
}
type testExecMock struct {
dbDir string
localDB dbm.KVDB
stateDB dbm.DB
exec dapp.Driver
wallet *walletMock
policy wcom.WalletBizPolicy
cfg *types.Chain33Config
q queue.Queue
qapi client.QueueProtocolAPI
}
type testcase struct {
payload types.Message
expectExecErr error
expectCheckErr error
expectExecLocalErr error
expectExecDelErr error
priv string
index int
systemCreate bool
testState int
testSign []byte
testFee int64
}
// InitEnv init env
func (mock *testExecMock) InitEnv() {
mock.cfg = testCfg
util.ResetDatadir(mock.cfg.GetModuleConfig(), "$TEMP/")
mock.q = queue.New("channel")
mock.q.SetConfig(mock.cfg)
mock.qapi, _ = client.New(mock.q.Client(), nil)
mock.initExec()
mock.initWallet()
}
func (mock *testExecMock) FreeEnv() {
util.CloseTestDB(mock.dbDir, mock.stateDB)
}
func (mock *testExecMock) initExec() {
mock.dbDir, mock.stateDB, mock.localDB = util.CreateTestDB()
exec := newPrivacy()
exec.SetAPI(mock.qapi)
exec.SetStateDB(mock.stateDB)
exec.SetLocalDB(mock.localDB)
exec.SetEnv(100, 1539918074, 1539918074)
mock.exec = exec
}
func (mock *testExecMock) initWallet() {
mock.wallet = &walletMock{}
mock.wallet.Wallet = wallet.New(mock.cfg)
mock.policy = testPolicy
mock.wallet.SetQueueClient(mock.q.Client())
mock.policy.Init(mock.wallet, nil)
seed, _ := mock.wallet.GenSeed(1)
mock.wallet.SaveSeed("abcd1234", seed.Seed)
mock.wallet.ProcWalletUnLock(&types.WalletUnLock{Passwd: "abcd1234"})
accCoin := account.NewCoinsAccount(mock.cfg)
accCoin.SetDB(mock.stateDB)
for index, addr := range testAddrs {
account := &types.Account{
Balance: initBalance,
Addr: addr,
}
accCoin.SaveAccount(account)
accCoin.SaveExecAccount(execAddr, account)
privBytes, _ := common.FromHex(testPrivateKeys[index])
bpriv := wcom.CBCEncrypterPrivkey([]byte(mock.wallet.Password), privBytes)
was := &types.WalletAccountStore{
Privkey: common.ToHex(bpriv),
Label: fmt.Sprintf("label%d", index),
Addr: addr,
TimeStamp: types.Now().String(),
}
mock.wallet.SetWalletAccount(false, addr, was)
}
mock.wallet.GetAPI().ExecWalletFunc(testPolicyName, "EnablePrivacy", &pty.ReqEnablePrivacy{Addrs: testAddrs})
}
func (mock *testExecMock) addBlockTx(tx *types.Transaction, receipt *types.ReceiptData) {
block := &types.BlockDetail{
Block: &types.Block{
Height: initHeight,
},
Receipts: []*types.ReceiptData{receipt},
}
batch := mock.wallet.GetDBStore().NewBatch(true)
defer batch.Write()
mock.policy.OnAddBlockTx(block, tx, 0, batch)
}
func createTx(mock *testExecMock, payload types.Message, priv string, systemCreate bool) (*types.Transaction, error) {
c, err := crypto.New(crypto.GetName(types.SECP256K1))
if err != nil {
return nil, err
}
bytes, err := common.FromHex(priv[:])
if err != nil {
return nil, err
}
privKey, err := c.PrivKeyFromBytes(bytes)
if err != nil {
return nil, err
}
if systemCreate {
action, _ := buildAction(payload)
tx, err := types.CreateFormatTx(mock.cfg, mock.cfg.ExecName(pty.PrivacyX), types.Encode(action))
if err != nil {
return nil, err
}
tx.Sign(int32(types.SECP256K1), privKey)
return tx, nil
}
req := payload.(*pty.ReqCreatePrivacyTx)
if req.GetAssetExec() == "" {
req.AssetExec = "coins"
}
reply, err := mock.wallet.GetAPI().ExecWalletFunc(testPolicyName, "CreateTransaction", payload)
if err != nil {
return nil, errors.New("createTxErr:" + err.Error())
}
signTxReq := &types.ReqSignRawTx{
TxHex: common.ToHex(types.Encode(reply)),
}
_, signTx, err := mock.policy.SignTransaction(privKey, signTxReq)
if err != nil {
return nil, errors.New("signPrivacyTxErr:" + err.Error())
}
signTxBytes, _ := common.FromHex(signTx)
tx := &types.Transaction{}
err = types.Decode(signTxBytes, tx)
if err != nil {
return nil, err
}
return tx, nil
}
func buildAction(param types.Message) (types.Message, error) {
action := &pty.PrivacyAction{
Value: nil,
Ty: 0,
}
if val, ok := param.(*pty.Public2Privacy); ok {
action.Value = &pty.PrivacyAction_Public2Privacy{Public2Privacy: val}
action.Ty = pty.ActionPublic2Privacy
} else if val, ok := param.(*pty.Privacy2Privacy); ok {
action.Value = &pty.PrivacyAction_Privacy2Privacy{Privacy2Privacy: val}
action.Ty = pty.ActionPrivacy2Privacy
} else if val, ok := param.(*pty.Privacy2Public); ok {
action.Value = &pty.PrivacyAction_Privacy2Public{Privacy2Public: val}
action.Ty = pty.ActionPrivacy2Public
} else {
return nil, types.ErrActionNotSupport
}
return action, nil
}
type walletMock struct {
*wallet.Wallet
}
func (w *walletMock) GetBlockHeight() int64 {
return initHeight + types.PrivacyMaturityDegree
}
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