Commit 610aa1ed authored by hxzqlh's avatar hxzqlh

Merge branch 'lhl-exchange'

parents 90d2f148 6df24193
......@@ -21,7 +21,7 @@ var driverName = exchangetypes.ExchangeX
// Init register dapp
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()
}
......@@ -35,7 +35,7 @@ type exchange struct {
drivers.DriverBase
}
func newExchange() drivers.Driver {
func NewExchange() drivers.Driver {
t := &exchange{}
t.SetChild(t)
t.SetExecutorType(types.LoadExecutorType(driverName))
......@@ -44,7 +44,7 @@ func newExchange() drivers.Driver {
// GetName get driver name
func GetName() string {
return newExchange().GetName()
return NewExchange().GetName()
}
func (e *exchange) GetDriverName() string {
......
......@@ -437,81 +437,294 @@ func TestExchange(t *testing.T) {
assert.NotEqual(t, nil, err)
err = Exec_RevokeOrder(t, orderID, PrivKeyB, stateDB, kvdb, env)
assert.Equal(t, nil, err)
//清理环境,重建数据库
util.CloseTestDB(dir, stateDB)
total = 1000 * types.Coin
accountA = types.Account{
Balance: total,
Frozen: 0,
Addr: Nodes[0],
}
accountB = types.Account{
Balance: total,
Frozen: 0,
Addr: Nodes[1],
}
dir, stateDB, kvdb = util.CreateTestDB()
defer util.CloseTestDB(dir, stateDB)
//execAddr := address.ExecAddress(et.ExchangeX)
accA, _ = account.NewAccountDB(cfg, "coins", "bty", stateDB)
accA.SaveExecAccount(execAddr, &accountA)
accB, _ = account.NewAccountDB(cfg, "coins", "bty", stateDB)
accB.SaveExecAccount(execAddr, &accountB)
accA1, _ = account.NewAccountDB(cfg, "token", "CCNY", stateDB)
accA1.SaveExecAccount(execAddr, &accountA)
accB1, _ = account.NewAccountDB(cfg, "token", "CCNY", stateDB)
accB1.SaveExecAccount(execAddr, &accountB)
env = &execEnv{
10,
1,
1539918074,
}
/*
//批量测试,同一个区块内出现多笔可以撮合的买卖交易
用例说明:
1.在同一个区块内,出现如下:
10笔买单
20笔卖单
50笔买单
20笔卖单
50笔买单
100笔卖单
*/
acc = accB1.LoadExecAccount(Nodes[1], execAddr)
assert.Equal(t, 1000*types.Coin, acc.Balance)
acc = accA.LoadExecAccount(Nodes[0], execAddr)
assert.Equal(t, 1000*types.Coin, acc.Balance)
var txs []*types.Transaction
for i := 0; i < 10; i++ {
tx, err := CreateLimitOrder(&et.LimitOrder{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Price: types.Coin, Amount: types.Coin, Op: et.OpBuy}, PrivKeyB)
assert.Equal(t, nil, err)
txs = append(txs, tx)
}
for i := 0; i < 20; i++ {
tx, err := CreateLimitOrder(&et.LimitOrder{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Price: types.Coin, Amount: types.Coin, Op: et.OpSell}, PrivKeyA)
assert.Equal(t, nil, err)
txs = append(txs, tx)
}
for i := 0; i < 50; i++ {
tx, err := CreateLimitOrder(&et.LimitOrder{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Price: types.Coin, Amount: types.Coin, Op: et.OpBuy}, PrivKeyB)
assert.Equal(t, nil, err)
txs = append(txs, tx)
}
for i := 0; i < 20; i++ {
tx, err := CreateLimitOrder(&et.LimitOrder{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Price: types.Coin, Amount: types.Coin, Op: et.OpSell}, PrivKeyA)
assert.Equal(t, nil, err)
txs = append(txs, tx)
}
for i := 0; i < 50; i++ {
tx, err := CreateLimitOrder(&et.LimitOrder{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Price: types.Coin, Amount: types.Coin, Op: et.OpBuy}, PrivKeyB)
assert.Equal(t, nil, err)
txs = append(txs, tx)
}
for i := 0; i < 100; i++ {
tx, err := CreateLimitOrder(&et.LimitOrder{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Price: types.Coin, Amount: types.Coin, Op: et.OpSell}, PrivKeyA)
assert.Equal(t, nil, err)
txs = append(txs, tx)
}
err = Exec_Block(t, stateDB, kvdb, env, txs...)
assert.Equal(t, nil, err)
acc = accB1.LoadExecAccount(Nodes[1], execAddr)
assert.Equal(t, 890*types.Coin, acc.Balance)
acc = accA.LoadExecAccount(Nodes[0], execAddr)
assert.Equal(t, 860*types.Coin, acc.Balance)
assert.Equal(t, 30*types.Coin, acc.Frozen)
//根据op查询市场深度
marketDepthList, err = Exec_QueryMarketDepth(&et.QueryMarketDepth{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Op: et.OpBuy}, stateDB, kvdb)
assert.NotEqual(t, nil, err)
//assert.Equal(t, (200-et.MaxMatchCount)*types.Coin, marketDepthList.List[0].GetAmount())
marketDepthList, err = Exec_QueryMarketDepth(&et.QueryMarketDepth{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Op: et.OpSell}, stateDB, kvdb)
assert.Equal(t, nil, err)
assert.Equal(t, 30*types.Coin, marketDepthList.List[0].GetAmount())
//清理环境,重建数据库
util.CloseTestDB(dir, stateDB)
total = 1000 * types.Coin
accountA = types.Account{
Balance: total,
Frozen: 0,
Addr: Nodes[0],
}
accountB = types.Account{
Balance: total,
Frozen: 0,
Addr: Nodes[1],
}
dir, stateDB, kvdb = util.CreateTestDB()
defer util.CloseTestDB(dir, stateDB)
//execAddr := address.ExecAddress(et.ExchangeX)
accA, _ = account.NewAccountDB(cfg, "coins", "bty", stateDB)
accA.SaveExecAccount(execAddr, &accountA)
accB, _ = account.NewAccountDB(cfg, "coins", "bty", stateDB)
accB.SaveExecAccount(execAddr, &accountB)
accA1, _ = account.NewAccountDB(cfg, "token", "CCNY", stateDB)
accA1.SaveExecAccount(execAddr, &accountA)
accB1, _ = account.NewAccountDB(cfg, "token", "CCNY", stateDB)
accB1.SaveExecAccount(execAddr, &accountB)
env = &execEnv{
10,
1,
1539918074,
}
/*
//批量测试,同个区块内出现多笔可以撮合的买卖交易
用例说明:
1.在同一区块内,出现如下:
100笔卖单
50笔买单
20笔卖单
100笔买单
*/
acc = accB1.LoadExecAccount(Nodes[1], execAddr)
assert.Equal(t, 1000*types.Coin, acc.Balance)
acc = accA.LoadExecAccount(Nodes[0], execAddr)
assert.Equal(t, 1000*types.Coin, acc.Balance)
txs = nil
for i := 0; i < 100; i++ {
tx, err := CreateLimitOrder(&et.LimitOrder{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Price: types.Coin, Amount: types.Coin, Op: et.OpSell}, PrivKeyA)
assert.Equal(t, nil, err)
txs = append(txs, tx)
}
for i := 0; i < 50; i++ {
tx, err := CreateLimitOrder(&et.LimitOrder{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Price: types.Coin, Amount: types.Coin, Op: et.OpBuy}, PrivKeyB)
assert.Equal(t, nil, err)
txs = append(txs, tx)
}
for i := 0; i < 20; i++ {
tx, err := CreateLimitOrder(&et.LimitOrder{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Price: types.Coin, Amount: types.Coin, Op: et.OpSell}, PrivKeyA)
assert.Equal(t, nil, err)
txs = append(txs, tx)
}
for i := 0; i < 100; i++ {
tx, err := CreateLimitOrder(&et.LimitOrder{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Price: types.Coin, Amount: types.Coin, Op: et.OpBuy}, PrivKeyB)
assert.Equal(t, nil, err)
txs = append(txs, tx)
}
err = Exec_Block(t, stateDB, kvdb, env, txs...)
assert.Equal(t, nil, err)
acc = accB1.LoadExecAccount(Nodes[1], execAddr)
assert.Equal(t, 850*types.Coin, acc.Balance)
assert.Equal(t, 30*types.Coin, acc.Frozen)
acc = accA.LoadExecAccount(Nodes[0], execAddr)
assert.Equal(t, 880*types.Coin, acc.Balance)
//根据op查询市场深度
marketDepthList, err = Exec_QueryMarketDepth(&et.QueryMarketDepth{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Op: et.OpSell}, stateDB, kvdb)
assert.NotEqual(t, nil, err)
marketDepthList, err = Exec_QueryMarketDepth(&et.QueryMarketDepth{LeftAsset: &et.Asset{Symbol: "bty", Execer: "coins"},
RightAsset: &et.Asset{Execer: "token", Symbol: "CCNY"}, Op: et.OpBuy}, stateDB, kvdb)
assert.Equal(t, nil, err)
assert.Equal(t, 30*types.Coin, marketDepthList.List[0].GetAmount())
//根据状态地址查询订单信息
//分页查询
count = 0
primaryKey = ""
for {
orderList, err := Exec_QueryOrderList(et.Completed, Nodes[1], primaryKey, stateDB, kvdb)
if err != nil {
break
}
count = count + len(orderList.List)
if orderList.PrimaryKey == "" {
break
}
primaryKey = orderList.PrimaryKey
}
assert.Equal(t, 120, count)
count = 0
primaryKey = ""
for {
orderList, err := Exec_QueryOrderList(et.Ordered, Nodes[1], primaryKey, stateDB, kvdb)
if err != nil {
break
}
count = count + len(orderList.List)
if orderList.PrimaryKey == "" {
break
}
primaryKey = orderList.PrimaryKey
}
assert.Equal(t, 30, count)
}
func Exec_LimitOrder(t *testing.T, limitOrder *et.LimitOrder, privKey string, stateDB db.DB, kvdb db.KVDB, env *execEnv) error {
func CreateLimitOrder(limitOrder *et.LimitOrder, privKey string) (tx *types.Transaction, err error) {
ety := types.LoadExecutorType(et.ExchangeX)
tx, err := ety.Create("LimitOrder", limitOrder)
tx, err = ety.Create("LimitOrder", limitOrder)
if err != nil {
return err
return nil, err
}
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
tx, err = types.FormatTx(cfg, et.ExchangeX, tx)
if err != nil {
return err
return nil, err
}
tx, err = signTx(tx, privKey)
if err != nil {
return err
}
exec := newExchange()
e := exec.(*exchange)
err = e.CheckTx(tx, 1)
if err != nil {
return err
}
q := queue.New("channel")
q.SetConfig(cfg)
api, _ := client.New(q.Client(), nil)
exec.SetAPI(api)
exec.SetStateDB(stateDB)
exec.SetLocalDB(kvdb)
env.blockHeight = env.blockHeight + 1
env.blockTime = env.blockTime + 20
env.difficulty = env.difficulty + 1
exec.SetEnv(env.blockHeight, env.blockTime, env.difficulty)
receipt, err := exec.Exec(tx, int(1))
if err != nil {
return err
}
for _, kv := range receipt.KV {
stateDB.Set(kv.Key, kv.Value)
}
receiptData := &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err := exec.ExecLocal(tx, receiptData, int(1))
if err != nil {
return err
}
for _, kv := range set.KV {
kvdb.Set(kv.Key, kv.Value)
return nil, err
}
//save to database
util.SaveKVList(stateDB, set.KV)
assert.Equal(t, types.ExecOk, int(receipt.Ty))
return nil
return tx, nil
}
func Exec_RevokeOrder(t *testing.T, orderID int64, privKey string, stateDB db.DB, kvdb db.KVDB, env *execEnv) error {
func CreateRevokeOrder(orderID int64, privKey string) (tx *types.Transaction, err error) {
ety := types.LoadExecutorType(et.ExchangeX)
tx, err := ety.Create("RevokeOrder", &et.RevokeOrder{OrderID: orderID})
tx, err = ety.Create("RevokeOrder", &et.RevokeOrder{OrderID: orderID})
if err != nil {
return err
return nil, err
}
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
tx, err = types.FormatTx(cfg, et.ExchangeX, tx)
if err != nil {
return err
return nil, err
}
tx, err = signTx(tx, privKey)
if err != nil {
return err
return nil, err
}
exec := newExchange()
return tx, nil
}
//模拟区块中交易得执行过程
func Exec_Block(t *testing.T, stateDB db.DB, kvdb db.KVDB, env *execEnv, txs ...*types.Transaction) error {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
exec := NewExchange()
e := exec.(*exchange)
err = e.CheckTx(tx, 1)
assert.Nil(t, err)
for index, tx := range txs {
err := e.CheckTx(tx, index)
if err != nil {
t.Log(err.Error())
return err
}
}
q := queue.New("channel")
q.SetConfig(cfg)
api, _ := client.New(q.Client(), nil)
......@@ -522,31 +735,50 @@ func Exec_RevokeOrder(t *testing.T, orderID int64, privKey string, stateDB db.DB
env.blockTime = env.blockTime + 20
env.difficulty = env.difficulty + 1
exec.SetEnv(env.blockHeight, env.blockTime, env.difficulty)
receipt, err := exec.Exec(tx, int(1))
for index, tx := range txs {
receipt, err := exec.Exec(tx, index)
if err != nil {
t.Log(err.Error())
return err
}
for _, kv := range receipt.KV {
stateDB.Set(kv.Key, kv.Value)
}
receiptData := &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err := exec.ExecLocal(tx, receiptData, index)
if err != nil {
t.Log(err.Error())
return err
}
for _, kv := range set.KV {
kvdb.Set(kv.Key, kv.Value)
}
//save to database
util.SaveKVList(stateDB, set.KV)
assert.Equal(t, types.ExecOk, int(receipt.Ty))
}
return nil
}
func Exec_LimitOrder(t *testing.T, limitOrder *et.LimitOrder, privKey string, stateDB db.DB, kvdb db.KVDB, env *execEnv) error {
tx, err := CreateLimitOrder(limitOrder, privKey)
if err != nil {
return err
}
for _, kv := range receipt.KV {
stateDB.Set(kv.Key, kv.Value)
}
receiptData := &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err := exec.ExecLocal(tx, receiptData, int(1))
return Exec_Block(t, stateDB, kvdb, env, tx)
}
func Exec_RevokeOrder(t *testing.T, orderID int64, privKey string, stateDB db.DB, kvdb db.KVDB, env *execEnv) error {
tx, err := CreateRevokeOrder(orderID, privKey)
if err != nil {
return err
}
for _, kv := range set.KV {
kvdb.Set(kv.Key, kv.Value)
}
//save to database
util.SaveKVList(stateDB, set.KV)
assert.Equal(t, types.ExecOk, int(receipt.Ty))
return nil
return Exec_Block(t, stateDB, kvdb, env, tx)
}
func Exec_QueryOrderList(status int32, addr string, primaryKey string, stateDB db.KV, kvdb db.KVDB) (*et.OrderList, error) {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
exec := newExchange()
exec := NewExchange()
q := queue.New("channel")
q.SetConfig(cfg)
api, _ := client.New(q.Client(), nil)
......@@ -563,7 +795,7 @@ func Exec_QueryOrderList(status int32, addr string, primaryKey string, stateDB d
func Exec_QueryOrder(orderID int64, stateDB db.KV, kvdb db.KVDB) (*et.Order, error) {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
exec := newExchange()
exec := NewExchange()
q := queue.New("channel")
q.SetConfig(cfg)
api, _ := client.New(q.Client(), nil)
......@@ -581,7 +813,7 @@ func Exec_QueryOrder(orderID int64, stateDB db.KV, kvdb db.KVDB) (*et.Order, err
func Exec_QueryMarketDepth(query *et.QueryMarketDepth, stateDB db.KV, kvdb db.KVDB) (*et.MarketDepthList, error) {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
exec := newExchange()
exec := NewExchange()
q := queue.New("channel")
q.SetConfig(cfg)
api, _ := client.New(q.Client(), nil)
......@@ -599,7 +831,7 @@ func Exec_QueryMarketDepth(query *et.QueryMarketDepth, stateDB db.KV, kvdb db.KV
func Exec_QueryHistoryOrder(query *et.QueryHistoryOrderList, stateDB db.KV, kvdb db.KVDB) (*et.OrderList, error) {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
exec := newExchange()
exec := NewExchange()
q := queue.New("channel")
q.SetConfig(cfg)
api, _ := client.New(q.Client(), nil)
......
......@@ -45,11 +45,6 @@ func (a *Action) GetKVSet(order *et.Order) (kvset []*types.KeyValue) {
return kvset
}
//缓存
func (a *Action) updateStateDBCache(order *et.Order) {
a.statedb.Set(calcOrderKey(order.OrderID), types.Encode(order))
}
//反转
func (a *Action) OpSwap(op int32) int32 {
if op == et.OpBuy {
......@@ -59,7 +54,7 @@ func (a *Action) OpSwap(op int32) int32 {
}
//计算实际花费
func (a *Action) calcActualCost(op int32, amount int64, price int64) int64 {
func CalcActualCost(op int32, amount int64, price int64) int64 {
if op == et.OpBuy {
return SafeMul(amount, price)
}
......@@ -148,7 +143,7 @@ func (a *Action) LimitOrder(payload *et.LimitOrder) (*types.Receipt, error) {
amount := SafeMul(payload.GetAmount(), payload.GetPrice())
rightAccount := rightAssetDB.LoadExecAccount(a.fromaddr, a.execaddr)
if rightAccount.Balance < amount {
elog.Error("LimitOrder.BalanceCheck", "addr", a.fromaddr, "execaddr", a.execaddr, "amount", amount, "err", et.ErrAssetBalance.Error())
elog.Error("limit check right balance", "addr", a.fromaddr, "avail", rightAccount.Balance, "need", amount)
return nil, et.ErrAssetBalance
}
return a.matchLimitOrder(payload, leftAssetDB, rightAssetDB)
......@@ -158,7 +153,7 @@ func (a *Action) LimitOrder(payload *et.LimitOrder) (*types.Receipt, error) {
amount := payload.GetAmount()
leftAccount := leftAssetDB.LoadExecAccount(a.fromaddr, a.execaddr)
if leftAccount.Balance < amount {
elog.Error("LimitOrder.BalanceCheck", "addr", a.fromaddr, "execaddr", a.execaddr, "amount", amount, "err", et.ErrAssetBalance.Error())
elog.Error("limit check left balance", "addr", a.fromaddr, "avail", leftAccount.Balance, "need", amount)
return nil, et.ErrAssetBalance
}
return a.matchLimitOrder(payload, leftAssetDB, rightAssetDB)
......@@ -174,11 +169,11 @@ func (a *Action) RevokeOrder(payload *et.RevokeOrder) (*types.Receipt, error) {
return nil, err
}
if order.Addr != a.fromaddr {
elog.Error("RevokeOrder.OrderCheck", "addr", a.fromaddr, "order.addr", order.Addr, "order.status", order.Status, "err", et.ErrAddr.Error())
elog.Error("RevokeOrder.OrderCheck", "addr", a.fromaddr, "order.addr", order.Addr, "order.status", order.Status)
return nil, et.ErrAddr
}
if order.Status == et.Completed || order.Status == et.Revoked {
elog.Error("RevokeOrder.OrderCheck", "addr", a.fromaddr, "order.addr", order.Addr, "order.status", order.Status, "err", et.ErrOrderSatus.Error())
elog.Error("RevokeOrder.OrderCheck", "addr", a.fromaddr, "order.addr", order.Addr, "order.status", order.Status)
return nil, et.ErrOrderSatus
}
leftAsset := order.GetLimitOrder().GetLeftAsset()
......@@ -193,15 +188,15 @@ func (a *Action) RevokeOrder(payload *et.RevokeOrder) (*types.Receipt, error) {
if err != nil {
return nil, err
}
amount := a.calcActualCost(et.OpBuy, balance, price)
amount := CalcActualCost(et.OpBuy, balance, price)
rightAccount := rightAssetDB.LoadExecAccount(a.fromaddr, a.execaddr)
if rightAccount.Frozen < amount {
elog.Error("RevokeOrder.BalanceCheck", "addr", a.fromaddr, "execaddr", a.execaddr, "amount", amount, "err", et.ErrAssetBalance.Error())
elog.Error("revoke check right frozen", "addr", a.fromaddr, "avail", rightAccount.Frozen, "amount", amount)
return nil, et.ErrAssetBalance
}
receipt, err := rightAssetDB.ExecActive(a.fromaddr, a.execaddr, amount)
if err != nil {
elog.Error("RevokeOrder.ExecActive", "addr", a.fromaddr, "execaddr", a.execaddr, "amount", amount, "err", err.Error())
elog.Error("RevokeOrder.ExecActive", "addr", a.fromaddr, "amount", amount, "err", err.Error())
return nil, err
}
logs = append(logs, receipt.Logs...)
......@@ -212,15 +207,15 @@ func (a *Action) RevokeOrder(payload *et.RevokeOrder) (*types.Receipt, error) {
if err != nil {
return nil, err
}
amount := a.calcActualCost(et.OpSell, balance, price)
amount := CalcActualCost(et.OpSell, balance, price)
leftAccount := leftAssetDB.LoadExecAccount(a.fromaddr, a.execaddr)
if leftAccount.Frozen < amount {
elog.Error("RevokeOrder.BalanceCheck", "addr", a.fromaddr, "execaddr", a.execaddr, "amount", amount, "err", et.ErrAssetBalance.Error())
elog.Error("revoke check left frozen", "addr", a.fromaddr, "avail", leftAccount.Frozen, "amount", amount)
return nil, et.ErrAssetBalance
}
receipt, err := leftAssetDB.ExecActive(a.fromaddr, a.execaddr, amount)
if err != nil {
elog.Error("RevokeOrder.ExecActive", "addr", a.fromaddr, "execaddr", a.execaddr, "amount", amount, "err", err.Error())
elog.Error("RevokeOrder.ExecActive", "addr", a.fromaddr, "amount", amount, "err", err.Error())
return nil, err
}
logs = append(logs, receipt.Logs...)
......@@ -317,7 +312,7 @@ func (a *Action) matchLimitOrder(payload *et.LimitOrder, leftAccountDB, rightAcc
continue
}
//撮合,指针传递
log, kv, err := a.matchModel(leftAccountDB, rightAccountDB, payload, matchorder, or, re)
log, kv, err := a.matchModel(leftAccountDB, rightAccountDB, payload, matchorder, or, re) // payload, or redundant
if err != nil {
return nil, err
}
......@@ -351,18 +346,20 @@ func (a *Action) matchLimitOrder(payload *et.LimitOrder, leftAccountDB, rightAcc
//未完成的订单需要冻结剩余未成交的资金
if payload.Op == et.OpBuy {
receipt, err := rightAccountDB.ExecFrozen(a.fromaddr, a.execaddr, a.calcActualCost(et.OpBuy, or.Balance, payload.Price))
amount := CalcActualCost(et.OpBuy, or.Balance, payload.Price)
receipt, err := rightAccountDB.ExecFrozen(a.fromaddr, a.execaddr, amount)
if err != nil {
elog.Error("LimitOrder.ExecFrozen", "addr", a.fromaddr, "execaddr", a.execaddr, "amount", a.calcActualCost(et.OpBuy, or.Balance, payload.Price), "err", err.Error())
elog.Error("LimitOrder.ExecFrozen", "addr", a.fromaddr, "amount", amount, "err", err.Error())
return nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
}
if payload.Op == et.OpSell {
receipt, err := leftAccountDB.ExecFrozen(a.fromaddr, a.execaddr, a.calcActualCost(et.OpSell, or.Balance, payload.Price))
amount := CalcActualCost(et.OpSell, or.Balance, payload.Price)
receipt, err := leftAccountDB.ExecFrozen(a.fromaddr, a.execaddr, amount)
if err != nil {
elog.Error("LimitOrder.ExecFrozen", "addr", a.fromaddr, "execaddr", a.execaddr, "amount", a.calcActualCost(et.OpSell, or.Balance, payload.Price), "err", err.Error())
elog.Error("LimitOrder.ExecFrozen", "addr", a.fromaddr, "amount", amount, "err", err.Error())
return nil, err
}
logs = append(logs, receipt.Logs...)
......@@ -370,6 +367,7 @@ func (a *Action) matchLimitOrder(payload *et.LimitOrder, leftAccountDB, rightAcc
}
//更新order状态
kvs = append(kvs, a.GetKVSet(or)...)
re.Order = or
receiptlog := &types.ReceiptLog{Ty: et.TyLimitOrderLog, Log: types.Encode(re)}
logs = append(logs, receiptlog)
receipts := &types.Receipt{Ty: types.ExecOk, KV: kvs, Logs: logs}
......@@ -380,140 +378,65 @@ func (a *Action) matchLimitOrder(payload *et.LimitOrder, leftAccountDB, rightAcc
func (a *Action) matchModel(leftAccountDB, rightAccountDB *account.DB, payload *et.LimitOrder, matchorder *et.Order, or *et.Order, re *et.ReceiptExchange) ([]*types.ReceiptLog, []*types.KeyValue, error) {
var logs []*types.ReceiptLog
var kvs []*types.KeyValue
//TODO 这里得逻辑是否需要调整?当匹配的单数过多,会导致receipt日志数量激增,理论上存在日志存储攻击,需要加下最大匹配深度,防止这种攻击发生
var matched int64
//先判断挂单得额度够不够,只有两种状态,大于等于,或者小于
if matchorder.GetBalance() >= or.GetBalance() {
if payload.Op == et.OpSell {
//转移冻结资产
receipt, err := rightAccountDB.ExecTransferFrozen(matchorder.Addr, a.fromaddr, a.execaddr, a.calcActualCost(matchorder.GetLimitOrder().Op, or.GetBalance(), payload.Price))
if err != nil {
elog.Error("matchLimitOrder.ExecTransferFrozen", "addr", matchorder.Addr, "execaddr", a.execaddr, "amount", a.calcActualCost(matchorder.GetLimitOrder().Op, or.GetBalance(), payload.Price), "err", err.Error())
return nil, nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
//解冻多余资金
if payload.Price < matchorder.GetLimitOrder().Price {
receipt, err := rightAccountDB.ExecActive(matchorder.Addr, a.execaddr, a.calcActualCost(matchorder.GetLimitOrder().Op, or.GetBalance(), matchorder.GetLimitOrder().Price-payload.Price))
if err != nil {
elog.Error("matchLimitOrder.ExecActive", "addr", matchorder.Addr, "execaddr", a.execaddr, "amount", a.calcActualCost(matchorder.GetLimitOrder().Op, or.GetBalance(), matchorder.GetLimitOrder().Price-payload.Price), "err", err.Error())
return nil, nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
}
//将达成交易的相应资产结算
receipt, err = leftAccountDB.ExecTransfer(a.fromaddr, matchorder.Addr, a.execaddr, a.calcActualCost(payload.Op, or.GetBalance(), payload.Price))
if err != nil {
elog.Error("matchLimitOrder.ExecTransfer", "addr", a.fromaddr, "execaddr", a.execaddr, "amount", a.calcActualCost(payload.Op, or.GetBalance(), payload.Price), "err", err.Error())
return nil, nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
//卖单成交得平均价格始终与自身挂单价格相同
or.AVGPrice = payload.Price
//计算matchOrder平均成交价格
matchorder.AVGPrice = caclAVGPrice(matchorder, payload.Price, payload.Amount)
}
if payload.Op == et.OpBuy {
//转移冻结资产
receipt, err := leftAccountDB.ExecTransferFrozen(matchorder.Addr, a.fromaddr, a.execaddr, a.calcActualCost(matchorder.GetLimitOrder().Op, or.GetBalance(), matchorder.GetLimitOrder().Price))
if err != nil {
elog.Error("matchLimitOrder.ExecTransferFrozen", "addr", matchorder.Addr, "execaddr", a.execaddr, "amount", a.calcActualCost(matchorder.GetLimitOrder().Op, or.GetBalance(), matchorder.GetLimitOrder().Price), "err", err.Error())
return nil, nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
//将达成交易的相应资产结算
receipt, err = rightAccountDB.ExecTransfer(a.fromaddr, matchorder.Addr, a.execaddr, a.calcActualCost(payload.Op, or.GetBalance(), matchorder.GetLimitOrder().Price))
if err != nil {
elog.Error("matchLimitOrder.ExecTransfer", "addr", a.fromaddr, "execaddr", a.execaddr, "amount", a.calcActualCost(payload.Op, or.GetBalance(), matchorder.GetLimitOrder().Price), "err", err.Error())
return nil, nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
//买单得话,价格选取卖单的价格
or.AVGPrice = matchorder.GetLimitOrder().Price
//计算matchOrder平均成交价格
matchorder.AVGPrice = caclAVGPrice(matchorder, matchorder.GetLimitOrder().Price, payload.Amount)
}
// match receiptorder,涉及赋值先手顺序,代码顺序不可变
matchorder.Status = func(a, b int64) int32 {
if a > b {
return et.Ordered
}
return et.Completed
}(matchorder.GetBalance(), or.GetBalance())
matchorder.Balance = matchorder.GetBalance() - or.GetBalance()
//记录本次成交得量
matchorder.Executed = or.GetBalance()
a.updateStateDBCache(matchorder)
kvs = append(kvs, a.GetKVSet(matchorder)...)
or.Executed = or.Executed + or.GetBalance()
or.Status = et.Completed
or.Balance = 0
//update receipt order
re.Order = or
re.MatchOrders = append(re.MatchOrders, matchorder)
a.updateStateDBCache(or)
kvs = append(kvs, a.GetKVSet(or)...)
return logs, kvs, nil
matched = or.GetBalance()
} else {
matched = matchorder.GetBalance()
}
if payload.Op == et.OpSell {
//转移冻结资产
receipt, err := rightAccountDB.ExecTransferFrozen(matchorder.Addr, a.fromaddr, a.execaddr, a.calcActualCost(matchorder.GetLimitOrder().Op, matchorder.GetBalance(), payload.Price))
amount := CalcActualCost(matchorder.GetLimitOrder().Op, matched, payload.Price)
receipt, err := rightAccountDB.ExecTransferFrozen(matchorder.Addr, a.fromaddr, a.execaddr, amount)
if err != nil {
elog.Error("matchLimitOrder.ExecTransferFrozen", "addr", matchorder.Addr, "execaddr", a.execaddr, "amount", a.calcActualCost(matchorder.GetLimitOrder().Op, matchorder.GetBalance(), payload.Price), "err", err.Error())
elog.Error("matchLimitOrder.ExecTransferFrozen", "addr", matchorder.Addr, "amount", amount, "err", err)
return nil, nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
//解冻成交部分 多余资金
//解冻多余资金
if payload.Price < matchorder.GetLimitOrder().Price {
receipt, err := rightAccountDB.ExecActive(matchorder.Addr, a.execaddr, a.calcActualCost(matchorder.GetLimitOrder().Op, matchorder.GetBalance(), matchorder.GetLimitOrder().Price-payload.Price))
amount := CalcActualCost(matchorder.GetLimitOrder().Op, matched, matchorder.GetLimitOrder().Price-payload.Price)
receipt, err := rightAccountDB.ExecActive(matchorder.Addr, a.execaddr, amount)
if err != nil {
elog.Error("matchLimitOrder.ExecActive", "addr", matchorder.Addr, "execaddr", a.execaddr, "amount", a.calcActualCost(matchorder.GetLimitOrder().Op, or.GetBalance(), matchorder.GetLimitOrder().Price-payload.Price), "err", err.Error())
elog.Error("matchLimitOrder.ExecActive", "addr", matchorder.Addr, "amount", amount, "err", err.Error())
return nil, nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
}
//将达成交易的相应资产结算
receipt, err = leftAccountDB.ExecTransfer(a.fromaddr, matchorder.Addr, a.execaddr, a.calcActualCost(payload.Op, matchorder.GetBalance(), payload.Price))
amount = CalcActualCost(payload.Op, matched, payload.Price)
receipt, err = leftAccountDB.ExecTransfer(a.fromaddr, matchorder.Addr, a.execaddr, amount)
if err != nil {
elog.Error("matchLimitOrder.ExecTransfer", "addr", a.fromaddr, "execaddr", a.execaddr, "amount", a.calcActualCost(payload.Op, matchorder.GetBalance(), payload.Price), "err", err.Error())
elog.Error("matchLimitOrder.ExecTransfer", "addr", a.fromaddr, "amount", amount, "err", err.Error())
return nil, nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
//买单得话,价格选取卖单的价格
//卖单成交得平均价格始终与自身挂单价格相同
or.AVGPrice = payload.Price
//计算matchOrder平均成交价格
matchorder.AVGPrice = caclAVGPrice(matchorder, payload.Price, matchorder.GetBalance())
matchorder.AVGPrice = caclAVGPrice(matchorder, payload.Price, matched) //TODO
}
if payload.Op == et.OpBuy {
//转移冻结资产
receipt, err := leftAccountDB.ExecTransferFrozen(matchorder.Addr, a.fromaddr, a.execaddr, a.calcActualCost(matchorder.GetLimitOrder().Op, matchorder.GetBalance(), matchorder.GetLimitOrder().Price))
amount := CalcActualCost(matchorder.GetLimitOrder().Op, matched, matchorder.GetLimitOrder().Price)
receipt, err := leftAccountDB.ExecTransferFrozen(matchorder.Addr, a.fromaddr, a.execaddr, amount)
if err != nil {
elog.Error("matchLimitOrder.ExecTransferFrozen", "addr", matchorder.Addr, "execaddr", a.execaddr, "amount", a.calcActualCost(matchorder.GetLimitOrder().Op, matchorder.GetBalance(), matchorder.GetLimitOrder().Price), "err", err.Error())
elog.Error("matchLimitOrder.ExecTransferFrozen", "addr", matchorder.Addr, "amount", amount, "err", err.Error())
return nil, nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
//将达成交易的相应资产结算
receipt, err = rightAccountDB.ExecTransfer(a.fromaddr, matchorder.Addr, a.execaddr, a.calcActualCost(payload.Op, matchorder.GetBalance(), matchorder.GetLimitOrder().Price))
amount = CalcActualCost(payload.Op, matched, matchorder.GetLimitOrder().Price)
receipt, err = rightAccountDB.ExecTransfer(a.fromaddr, matchorder.Addr, a.execaddr, amount)
if err != nil {
elog.Error("matchLimitOrder.ExecTransfer", "addr", a.fromaddr, "execaddr", a.execaddr, "amount", a.calcActualCost(payload.Op, matchorder.GetBalance(), matchorder.GetLimitOrder().Price), "err", err.Error())
elog.Error("matchLimitOrder.ExecTransfer", "addr", a.fromaddr, "amount", amount, "err", err.Error())
return nil, nil, err
}
logs = append(logs, receipt.Logs...)
......@@ -522,21 +445,37 @@ func (a *Action) matchModel(leftAccountDB, rightAccountDB *account.DB, payload *
//买单得话,价格选取卖单的价格
or.AVGPrice = matchorder.GetLimitOrder().Price
//计算matchOrder平均成交价格
matchorder.AVGPrice = caclAVGPrice(matchorder, matchorder.GetLimitOrder().Price, matchorder.GetBalance())
matchorder.AVGPrice = caclAVGPrice(matchorder, matchorder.GetLimitOrder().Price, matched) //TODO
}
if matched == matchorder.GetBalance() {
matchorder.Status = et.Completed
} else {
matchorder.Status = et.Ordered
}
if matched == or.GetBalance() {
or.Status = et.Completed
} else {
or.Status = et.Ordered
}
//涉及赋值先后顺序,不可颠倒
or.Balance = or.Balance - matchorder.Balance
or.Executed = or.Executed + matchorder.Balance
or.Status = et.Ordered
a.updateStateDBCache(or)
if matched == or.GetBalance() {
matchorder.Balance -= matched
matchorder.Executed = matched
kvs = append(kvs, a.GetKVSet(matchorder)...)
// match receiptorder
matchorder.Executed = matchorder.Balance
matchorder.Status = et.Completed
matchorder.Balance = 0
a.updateStateDBCache(matchorder)
kvs = append(kvs, a.GetKVSet(matchorder)...)
or.Executed += matched // TODO why not += ?
or.Balance = 0
kvs = append(kvs, a.GetKVSet(or)...) //or complete
} else {
or.Balance -= matched
or.Executed += matched
matchorder.Executed = matched
matchorder.Balance = 0
kvs = append(kvs, a.GetKVSet(matchorder)...) //matchorder complete
}
re.Order = or
re.MatchOrders = append(re.MatchOrders, matchorder)
......@@ -620,7 +559,7 @@ func QueryMarketDepth(localdb dbm.KV, left, right *et.Asset, op int32, primaryKe
rows, err = table.ListIndex("price", prefix, []byte(primaryKey), count, Direction(op))
}
if err != nil {
elog.Error("QueryMarketDepth.", "left", left, "right", right, "err", err.Error())
//elog.Error("QueryMarketDepth.", "left", left, "right", right, "err", err.Error())
return nil, err
}
......
# 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/gogo/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
Genesis = "0x3990969DF92A5914F7B71EEB9A4E58D6E255F32BF042FEA5318FC8B3D50EE6E8" // 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
PrivKeyA = "0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b" // 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4
PrivKeyB = "0x19c069234f9d3e61135fefbeb7791b149cdf6af536f26bebb310d4cd22c3fee4" // 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR
PrivKeyC = "0x7a80a1f75d7360c6123c32a78ecf978c1ac55636f87892df38d8b85a9aeff115" // 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k
PrivKeyD = "0xcacb1f5d51700aea07fca2246ab43b0917d70405c65edea9b5063d72eb5c6b71" // 1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs
Nodes = []string{
"1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4",
"1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR",
"1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k",
"1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs",
}
)
// 批量测试前,先确保测试账户有足够的币和钱
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)
}
}
package test
import (
"testing"
"github.com/33cn/plugin/plugin/dapp/exchange/executor"
"github.com/33cn/chain33/types"
et "github.com/33cn/plugin/plugin/dapp/exchange/types"
"github.com/stretchr/testify/assert"
)
var (
PrivKeyA = "0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b" // 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4
PrivKeyB = "0x19c069234f9d3e61135fefbeb7791b149cdf6af536f26bebb310d4cd22c3fee4" // 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR
PrivKeyC = "0x7a80a1f75d7360c6123c32a78ecf978c1ac55636f87892df38d8b85a9aeff115" // 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k
PrivKeyD = "0xcacb1f5d51700aea07fca2246ab43b0917d70405c65edea9b5063d72eb5c6b71" // 1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs
coin = "bty"
token = "CCNY"
leftAsset = &et.Asset{Symbol: coin, Execer: "coins"}
rightAsset = &et.Asset{Symbol: token, Execer: "token"}
cli Cli
orderId int64
)
func init() {
cli = NewExecCli()
//cli = NewGRPCCli(":8802")
}
func TestLimitOrder(t *testing.T) {
//A 挂买 4x10
req := &et.LimitOrder{LeftAsset: leftAsset, RightAsset: rightAsset, Price: 4, Amount: 10 * types.Coin, Op: et.OpBuy}
testPlaceLimitOrder(t, req, Nodes[0], PrivKeyA)
}
func TestOrderList(t *testing.T) {
orderList, err := getOrderList(et.Ordered, Nodes[0], "")
assert.Nil(t, err)
t.Log(orderList)
orderId = orderList.List[0].OrderID
}
func TestGetOrder(t *testing.T) {
order, err := getOrder(orderId)
assert.Nil(t, err)
t.Log(order)
}
func TestMarketDepth(t *testing.T) {
depth, err := getMarketDepth(&et.QueryMarketDepth{LeftAsset: leftAsset, RightAsset: rightAsset, Op: et.OpBuy})
t.Log(depth, err)
}
func TestMatch(t *testing.T) {
//B 挂卖 4x5
req := &et.LimitOrder{LeftAsset: leftAsset, RightAsset: rightAsset, Price: 4, Amount: 5 * types.Coin, Op: et.OpSell}
doLimitOrder(req, PrivKeyB)
}
func TestHistoryOrderList(t *testing.T) {
historyq := &et.QueryHistoryOrderList{
LeftAsset: leftAsset,
RightAsset: rightAsset,
}
historyOrderList, err := getHistoryOrderList(historyq)
assert.Nil(t, err)
t.Log(historyOrderList)
}
func TestRevokeOrder(t *testing.T) {
//A 撤回未完成订单
testRevokeLimitOrder(t, orderId, Nodes[0], PrivKeyA)
}
func TestSample0(t *testing.T) {
depth, _ := getMarketDepth(&et.QueryMarketDepth{LeftAsset: leftAsset, RightAsset: rightAsset, Op: et.OpBuy})
assert.Nil(t, depth)
depth, _ = getMarketDepth(&et.QueryMarketDepth{LeftAsset: leftAsset, RightAsset: rightAsset, Op: et.OpBuy})
assert.Nil(t, depth)
}
//买卖单价格相同,测试正常撮合流程,查询功能是否可用
//1.先挂数量是10的买单。
//2.然后再挂数量是5的吃单
//3.最后撤销未成交部分的买单
func TestCase1(t *testing.T) {
//先挂数量是10的买单
req := &et.LimitOrder{LeftAsset: leftAsset, RightAsset: rightAsset, Price: 4, Amount: 10 * types.Coin, Op: et.OpBuy}
_, err := doLimitOrder(req, PrivKeyA)
assert.Nil(t, err)
orderList, err := getOrderList(et.Ordered, Nodes[0], "")
assert.Nil(t, err)
//根据订单号,查询订单详情
orderID1 := orderList.List[0].OrderID
order, err := getOrder(orderID1)
assert.Nil(t, err)
assert.Equal(t, int32(et.Ordered), order.Status)
assert.Equal(t, 10*types.Coin, order.GetBalance())
//根据op查询市场深度
q := &et.QueryMarketDepth{LeftAsset: leftAsset, RightAsset: rightAsset, Op: et.OpBuy}
marketDepthList, err := getMarketDepth(q)
assert.Nil(t, err)
assert.Equal(t, 10*types.Coin, marketDepthList.List[0].GetAmount())
//然后再挂数量是5的吃单
req = &et.LimitOrder{LeftAsset: leftAsset, RightAsset: rightAsset, Price: 4, Amount: 5 * types.Coin, Op: et.OpSell}
_, err = doLimitOrder(req, PrivKeyB)
assert.Nil(t, err)
orderList, err = getOrderList(et.Completed, Nodes[1], "")
assert.Nil(t, err)
orderID2 := orderList.List[0].OrderID
//查询订单1详情
order, err = getOrder(orderID1)
assert.Nil(t, err)
//订单1的状态应该还是ordered
assert.Equal(t, int32(et.Ordered), order.Status)
assert.Equal(t, 5*types.Coin, order.Balance)
//order2状态是completed
order, err = getOrder(orderID2)
assert.Nil(t, err)
assert.Equal(t, int32(et.Completed), order.Status)
//根据op查询市场深度
q = &et.QueryMarketDepth{LeftAsset: leftAsset, RightAsset: rightAsset, Op: et.OpBuy}
marketDepthList, err = getMarketDepth(q)
assert.Nil(t, err)
//市场深度应该改变
assert.Equal(t, 5*types.Coin, marketDepthList.List[0].GetAmount())
//查询历史成交
q2 := &et.QueryHistoryOrderList{LeftAsset: leftAsset, RightAsset: rightAsset}
orderList, err = getHistoryOrderList(q2)
assert.Nil(t, err)
assert.Equal(t, orderID2, orderList.List[0].OrderID)
//撤回未完成的订单
_, err = doRevokeOrder(orderID1, PrivKeyA)
assert.Nil(t, err)
//查询订单1详情
order, err = getOrder(orderID1)
assert.Nil(t, err)
//订单1的状态应该Revoked
assert.Equal(t, int32(et.Revoked), order.Status)
assert.Equal(t, 5*types.Coin, order.Balance)
//根据op查询市场深度
q = &et.QueryMarketDepth{LeftAsset: leftAsset, RightAsset: rightAsset, Op: et.OpBuy}
_, err = getMarketDepth(q)
assert.NotNil(t, err)
//根据原有状态去查看买单是否被改变
//原有ordered状态的数据应该被删除
_, err = getOrderList(et.Ordered, Nodes[0], "")
assert.NotNil(t, err)
}
func BenchmarkOrder(b *testing.B) {
req := &et.LimitOrder{LeftAsset: leftAsset, RightAsset: rightAsset, Price: 1, Amount: 10 * types.Coin, Op: et.OpSell}
ety := types.LoadExecutorType(et.ExchangeX)
tx, _ := ety.Create("LimitOrder", req)
for i := 0; i < b.N; i++ {
cli.Send(tx, PrivKeyA)
}
}
func doLimitOrder(req *et.LimitOrder, privKey string) ([]*types.ReceiptLog, error) {
ety := types.LoadExecutorType(et.ExchangeX)
tx, err := ety.Create("LimitOrder", req)
if err != nil {
return nil, err
}
return cli.Send(tx, privKey)
}
func doRevokeOrder(orderID int64, privKey string) ([]*types.ReceiptLog, error) {
ety := types.LoadExecutorType(et.ExchangeX)
tx, err := ety.Create("RevokeOrder", &et.RevokeOrder{OrderID: orderID})
if err != nil {
return nil, err
}
return cli.Send(tx, privKey)
}
func getOrderList(status int32, addr string, primaryKey string) (*et.OrderList, error) {
msg, err := cli.Query(et.FuncNameQueryOrderList, &et.QueryOrderList{Status: status, Address: addr, PrimaryKey: primaryKey})
if err != nil {
return nil, err
}
var resp et.OrderList
err = types.Decode(msg, &resp)
if err != nil {
return nil, err
}
return &resp, nil
}
func getOrder(orderID int64) (*et.Order, error) {
msg, err := cli.Query(et.FuncNameQueryOrder, &et.QueryOrder{OrderID: orderID})
if err != nil {
return nil, err
}
var resp et.Order
err = types.Decode(msg, &resp)
if err != nil {
return nil, err
}
return &resp, nil
}
func getMarketDepth(query *et.QueryMarketDepth) (*et.MarketDepthList, error) {
msg, err := cli.Query(et.FuncNameQueryMarketDepth, query)
if err != nil {
return nil, err
}
var resp et.MarketDepthList
err = types.Decode(msg, &resp)
if err != nil {
return nil, err
}
return &resp, nil
}
func getHistoryOrderList(query *et.QueryHistoryOrderList) (*et.OrderList, error) {
msg, err := cli.Query(et.FuncNameQueryHistoryOrderList, query)
if err != nil {
return nil, err
}
var resp et.OrderList
err = types.Decode(msg, &resp)
if err != nil {
return nil, err
}
return &resp, nil
}
func testPlaceLimitOrder(t *testing.T, req *et.LimitOrder, addr string, privkey string) {
accPrev, err := cli.GetExecAccount(addr, "coins", coin)
assert.Nil(t, err)
t.Log(accPrev)
tokenPrev, err := cli.GetExecAccount(addr, "token", token)
assert.Nil(t, err)
t.Log(tokenPrev)
_, err = doLimitOrder(req, privkey)
assert.Nil(t, err)
accAfter, err := cli.GetExecAccount(addr, "coins", coin)
assert.Nil(t, err)
t.Log(accAfter)
tokenAfter, err := cli.GetExecAccount(addr, "token", token)
assert.Nil(t, err)
t.Log(tokenAfter)
cost := executor.CalcActualCost(req.Op, req.Amount, req.Price)
t.Log(req.Amount, req.Price, cost)
// bty/ccny
if req.Op == et.OpBuy {
// bty
assert.Equal(t, accAfter.Balance, accPrev.Balance)
assert.Equal(t, accAfter.Frozen, accPrev.Frozen)
// ccny
assert.Equal(t, tokenAfter.Balance, tokenPrev.Balance-cost)
assert.Equal(t, tokenAfter.Frozen, tokenPrev.Frozen+cost)
} else {
// bty
assert.Equal(t, accAfter.Balance, accPrev.Balance-cost)
assert.Equal(t, accAfter.Frozen, accPrev.Frozen+cost)
// ccny
assert.Equal(t, tokenAfter.Balance, tokenPrev.Balance)
assert.Equal(t, tokenAfter.Frozen, tokenPrev.Frozen)
}
}
func testRevokeLimitOrder(t *testing.T, orderID int64, addr string, privkey string) {
order, err := getOrder(orderID)
assert.Nil(t, err)
assert.NotNil(t, order)
lo := order.Value.(*et.Order_LimitOrder).LimitOrder
assert.NotNil(t, lo)
accPrev, err := cli.GetExecAccount(addr, "coins", coin)
assert.Nil(t, err)
t.Log(accPrev)
tokenPrev, err := cli.GetExecAccount(addr, "token", token)
assert.Nil(t, err)
t.Log(tokenPrev)
_, err = doRevokeOrder(orderID, privkey)
assert.Nil(t, err)
accAfter, err := cli.GetExecAccount(addr, "coins", coin)
assert.Nil(t, err)
t.Log(accAfter)
tokenAfter, err := cli.GetExecAccount(addr, "token", token)
assert.Nil(t, err)
t.Log(tokenAfter)
cost := executor.CalcActualCost(lo.Op, order.Balance, lo.Price)
// bty/ccny
if lo.Op == et.OpBuy {
// bty
assert.Equal(t, accAfter.Balance, accPrev.Balance)
assert.Equal(t, accAfter.Frozen, accPrev.Frozen)
// ccny
assert.Equal(t, tokenAfter.Balance, tokenPrev.Balance+cost)
assert.Equal(t, tokenAfter.Frozen, tokenPrev.Frozen-cost)
} else {
// bty
assert.Equal(t, accAfter.Balance, accPrev.Balance+cost)
assert.Equal(t, accAfter.Frozen, accPrev.Frozen-cost)
// ccny
assert.Equal(t, tokenAfter.Balance, tokenPrev.Balance)
assert.Equal(t, tokenAfter.Frozen, tokenPrev.Frozen)
}
}
package test
import (
"fmt"
"log"
"time"
"github.com/33cn/chain33/common/db"
"github.com/33cn/plugin/plugin/dapp/exchange/executor"
"github.com/gogo/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/gogo/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
var addrs []string
addrs = append(addrs, addr)
param := &tt.ReqTokenBalance{
Addresses: addrs,
TokenSymbol: symbol,
Execer: et.ExchangeX,
}
msg, err := c.Query("token.GetAccountTokenBalance", param)
if err != nil {
return nil, err
}
var resp tt.ReplyAccountTokenAssets
err = types.Decode(msg, &resp)
if err != nil {
return nil, err
}
return resp.TokenAssets[0].Account, nil
}
// 发送交易并等待执行结果
// 如果交易非法,返回错误信息
// 如果交易执行成功,返回 交易哈希、回报
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
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_config -k token-finisher
./chain33-cli send config config_tx -c token-blacklist -o add -v BTY -k 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs
sleep 1
./chain33-cli config query_config -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"
......@@ -59,6 +59,13 @@ func (t *token) Query_GetAccountTokenAssets(in *tokenty.ReqAccountTokenAssets) (
return t.getAccountTokenAssets(in)
}
func (t *token) Query_GetAccountTokenBalance(in *tokenty.ReqTokenBalance) (types.Message, error) {
if in == nil {
return nil, types.ErrInvalidParam
}
return t.getAccountTokenBalance(in)
}
// Query_GetTxByToken 获取token相关交易
func (t *token) Query_GetTxByToken(in *tokenty.ReqTokenTx) (types.Message, error) {
if in == nil {
......
......@@ -124,6 +124,39 @@ func (t *token) getAccountTokenAssets(req *tokenty.ReqAccountTokenAssets) (types
return reply, nil
}
func (t *token) getAccountTokenBalance(req *tokenty.ReqTokenBalance) (types.Message, error) {
var reply = &tokenty.ReplyAccountTokenAssets{}
cfg := t.GetAPI().GetConfig()
for _, addr := range req.Addresses {
assets, err := t.queryTokenAssetsKey(addr)
if err != nil {
return nil, err
}
for _, asset := range assets.Datas {
if asset != req.TokenSymbol {
continue
}
acc, err := account.NewAccountDB(cfg, t.GetName(), asset, t.GetStateDB())
if err != nil {
return nil, err
}
var acc1 *types.Account
if req.Execer == t.GetName() {
acc1 = acc.LoadAccount(addr)
} else if req.Execer != "" {
execAddress := address.ExecAddress(req.Execer)
acc1 = acc.LoadExecAccount(addr, execAddress)
}
if acc1 == nil {
continue
}
tokenAsset := &tokenty.TokenAsset{Symbol: asset, Account: acc1}
reply.TokenAssets = append(reply.TokenAssets, tokenAsset)
}
}
return reply, nil
}
func (t *token) getAddrReceiverforTokens(addrTokens *tokenty.ReqAddrTokens) (types.Message, error) {
var reply = &tokenty.ReplyAddrRecvForTokens{}
db := t.GetLocalDB()
......
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