Commit eaaeeb54 authored by vipwzw's avatar vipwzw Committed by 33cn

update chain33

parent 60e4efbc
package init
import (
_ "github.com/33cn/plugin/plugin/consensus/para" //auto gen
_ "github.com/33cn/plugin/plugin/consensus/pbft" //auto gen
_ "github.com/33cn/plugin/plugin/consensus/raft" //auto gen
_ "github.com/33cn/plugin/plugin/consensus/tendermint" //auto gen
_ "github.com/33cn/plugin/plugin/consensus/ticket" //auto gen
)
package init
import (
_ "github.com/33cn/plugin/plugin/crypto/ecdsa" //auto gen
_ "github.com/33cn/plugin/plugin/crypto/sm2" //auto gen
)
package init
import (
_ "github.com/33cn/plugin/plugin/dapp/blackwhite" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/cert" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/evm" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/game" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/hashlock" //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/norm" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/paracross" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/pokerbull" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/privacy" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/relay" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/retrieve" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/ticket" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/token" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/trade" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/unfreeze" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/valnode" //auto gen
)
package init
import (
_ "github.com/33cn/plugin/plugin/store/kvdb" //auto gen
_ "github.com/33cn/plugin/plugin/store/kvmvcc" //auto gen
_ "github.com/33cn/plugin/plugin/store/mpt" //auto gen
)
......@@ -6,9 +6,9 @@ dist: xenial
notifications:
email: false
jobs:
matrix:
include:
- stage: check_fmt
- name: check_fmt
sudo: require
go:
- "1.9"
......@@ -23,13 +23,13 @@ jobs:
- make checkgofmt && make fmt_go
- make linter
- stage: unit-test
- name: unit-test
go: "1.9.x"
install: skip
script:
- make test
- stage: coverage
- name: coverage
if: branch = master
go:
- "1.9.x"
......@@ -41,7 +41,7 @@ jobs:
after_success:
- bash <(curl -s https://codecov.io/bash)
- stage: deploy
- name: deploy
sudo: required
services:
- docker
......
......@@ -8,7 +8,6 @@ package db
import (
"bytes"
"errors"
"fmt"
"github.com/33cn/chain33/types"
......@@ -16,7 +15,7 @@ import (
)
//ErrNotFoundInDb error
var ErrNotFoundInDb = errors.New("ErrNotFoundInDb")
var ErrNotFoundInDb = types.ErrNotFound
//Lister 列表接口
type Lister interface {
......
......@@ -11,6 +11,7 @@ import (
"fmt"
"github.com/33cn/chain33/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
......@@ -234,3 +235,26 @@ func testDBIteratorDel(t *testing.T, db DB) {
batch.Write()
}
}
func testLevelDBBatch(t *testing.T, db DB) {
batch := db.NewBatch(false)
batch.Set([]byte("hello"), []byte("world"))
err := batch.Write()
assert.Nil(t, err)
v, err := db.Get([]byte("hello"))
assert.Nil(t, err)
assert.Equal(t, v, []byte("world"))
//set and del
batch.Set([]byte("hello1"), []byte("world"))
batch.Set([]byte("hello2"), []byte("world"))
batch.Set([]byte("hello3"), []byte("world"))
batch.Set([]byte("hello4"), []byte("world"))
batch.Set([]byte("hello5"), []byte("world"))
batch.Delete([]byte("hello1"))
err = batch.Write()
assert.Nil(t, err)
v, err = db.Get([]byte("hello1"))
assert.Equal(t, err, types.ErrNotFound)
assert.Nil(t, v)
}
......@@ -43,6 +43,18 @@ func TestGoLevelDBIteratorDel(t *testing.T) {
testDBIteratorDel(t, leveldb)
}
func TestLevelDBBatch(t *testing.T) {
dir, err := ioutil.TempDir("", "goleveldb")
require.NoError(t, err)
t.Log(dir)
leveldb, err := NewGoLevelDB("goleveldb", dir, 128)
require.NoError(t, err)
defer leveldb.Close()
testLevelDBBatch(t, leveldb)
}
// leveldb边界测试
func TestGoLevelDBBoundary(t *testing.T) {
dir, err := ioutil.TempDir("", "goleveldb")
......
......@@ -55,7 +55,6 @@ func (db *ListHelper) List(prefix, key []byte, count, direction int32) (values [
return db.IteratorScanFromFirst(prefix, count)
}
return db.IteratorScanFromLast(prefix, count)
}
if count == 1 && direction == ListSeek {
it := db.db.Iterator(prefix, nil, true)
......
// 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 table
import (
"math"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
)
//Count 计数器
type Count struct {
prefix string
name string
kvdb db.KV
num int64
keydata []byte
}
//NewCount 创建一个计数器
func NewCount(prefix string, name string, kvdb db.KV) *Count {
keydata := []byte(prefix + "#" + name)
return &Count{
prefix: prefix,
name: name,
kvdb: kvdb,
keydata: keydata,
num: math.MinInt64,
}
}
func (c *Count) getKey() []byte {
return c.keydata
}
//Save 保存kv
func (c *Count) Save() (kvs []*types.KeyValue, err error) {
if c.num == math.MinInt64 {
return nil, nil
}
var i types.Int64
i.Data = c.num
item := &types.KeyValue{Key: c.getKey(), Value: types.Encode(&i)}
kvs = append(kvs, item)
return
}
//Get count
func (c *Count) Get() (int64, error) {
if c.num == math.MinInt64 {
data, err := c.kvdb.Get(c.getKey())
if err == types.ErrNotFound {
c.num = 0
} else if err != nil {
return 0, err
}
var num types.Int64
err = types.Decode(data, &num)
if err != nil {
return 0, err
}
c.num = num.Data
}
return c.num, nil
}
//Inc 增加1
func (c *Count) Inc() (num int64, err error) {
c.num, err = c.Get()
if err != nil {
return 0, err
}
c.num++
return c.num, nil
}
//Dec 减少1
func (c *Count) Dec() (num int64, err error) {
c.num, err = c.Get()
if err != nil {
return 0, err
}
c.num--
return c.num, nil
}
//Set 这个操作要谨慎使用
func (c *Count) Set(i int64) {
c.num = i
}
// 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 table
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCount(t *testing.T) {
dir, leveldb, kvdb := getdb()
defer dbclose(dir, leveldb)
count := NewCount("prefix", "name#hello", kvdb)
count.Inc()
count.Dec()
count.Inc()
i, err := count.Get()
assert.Nil(t, err)
assert.Equal(t, i, int64(1))
kvs, err := count.Save()
assert.Nil(t, err)
setKV(leveldb, kvs)
count = NewCount("prefix", "name#hello", kvdb)
i, err = count.Get()
assert.Nil(t, err)
assert.Equal(t, i, int64(1))
count.Set(2)
i, err = count.Get()
assert.Nil(t, err)
assert.Equal(t, i, int64(2))
}
// 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 table
import "errors"
//table 中的错误处理
var (
ErrEmptyPrimaryKey = errors.New("ErrEmptyPrimaryKey")
ErrPrimaryKey = errors.New("ErrPrimaryKey")
ErrIndexKey = errors.New("ErrIndexKey")
ErrTooManyIndex = errors.New("ErrTooManyIndex")
ErrTablePrefixOrTableName = errors.New("ErrTablePrefixOrTableName")
ErrDupPrimaryKey = errors.New("ErrDupPrimaryKey")
)
// 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 table
import (
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
)
//Query 列表查询结构
type Query struct {
table *Table
kvdb db.KVDB
}
//ListIndex 根据索引查询列表
//index 用哪个index
//prefix 必须要符合的前缀, 可以为空
//primaryKey 开始查询的位置(不包含数据本身)
//count 最多取的数量
//direction 方向
func (query *Query) ListIndex(indexName string, prefix []byte, primaryKey []byte, count, direction int32) (rows []*Row, err error) {
if indexName == "" {
return query.ListPrimary(prefix, primaryKey, count, direction)
}
p := query.table.indexPrefix(indexName)
var k []byte
if len(primaryKey) > 0 {
row, err := query.table.GetData(primaryKey)
if err != nil {
return nil, err
}
key, err := query.table.index(row, indexName)
if err != nil {
return nil, err
}
//如果存在prefix
if prefix != nil {
p2 := commonPrefix(prefix, key)
if len(p2) != len(prefix) {
return nil, types.ErrNotFound
}
p = append(p, p2...)
}
k = query.table.getIndexKey(indexName, key, row.Primary)
} else {
//这个情况下 k == nil
p = append(p, prefix...)
}
values, err := query.kvdb.List(p, k, count, direction)
if err != nil {
return nil, err
}
for _, value := range values {
row, err := query.table.GetData(value)
if err != nil {
return nil, err
}
rows = append(rows, row)
}
return rows, nil
}
//ListPrimary list primary data
func (query *Query) ListPrimary(prefix []byte, primaryKey []byte, count, direction int32) (rows []*Row, err error) {
p := query.table.primaryPrefix()
var k []byte
if primaryKey != nil {
if prefix != nil {
p2 := commonPrefix(prefix, primaryKey)
if len(p2) != len(prefix) {
return nil, types.ErrNotFound
}
p = append(p, p2...)
}
k = append(p, primaryKey...)
} else {
p = append(p, prefix...)
}
values, err := query.kvdb.List(p, k, count, direction)
if err != nil {
return nil, err
}
for _, value := range values {
row, err := query.table.getRow(value)
if err != nil {
return nil, err
}
rows = append(rows, row)
}
return rows, nil
}
func commonPrefix(key1, key2 []byte) []byte {
l1 := len(key1)
l2 := len(key2)
l := min(l1, l2)
for i := 0; i < l; i++ {
if key1[i] != key2[i] {
return key1[:i]
}
}
return key1[0:l]
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
// 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 table 实现一个基于kv的关系型数据库的表格功能
package table
import (
"bytes"
"encoding/binary"
"fmt"
"strings"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
)
//设计结构:
/*
核心: 平衡
save:
数据保存:
tableprefix + tablename + Primary -> data
index:
tableprefix + tablemetaname + index + primary -> primary
read:
list by Primary -> 直接读出数据
list by index
根据index 先计算要读出的 primary list
从数据table读出数据(根据 primary key)
del:
利用 primaryKey + index 删除所有的 数据 和 索引
*/
//指出是 添加 还是 删除 行
//primary key auto 的del 需要指定 primary key
const (
None = iota
Add
Del
)
//meta key
const meta = "#m#"
const data = "#d#"
//RowMeta 定义行的操作
type RowMeta interface {
CreateRow() *Row
SetPayload(types.Message) error
Get(key string) ([]byte, error)
}
//Row 行操作
type Row struct {
Ty int
Primary []byte
Data types.Message
}
func encodeInt64(p int64) ([]byte, error) {
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, p)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func decodeInt64(p []byte) (int64, error) {
buf := bytes.NewBuffer(p)
var i int64
err := binary.Read(buf, binary.LittleEndian, &i)
if err != nil {
return 0, err
}
return i, nil
}
//Encode row
func (row *Row) Encode() ([]byte, error) {
b, err := encodeInt64(int64(len(row.Primary)))
if err != nil {
return nil, err
}
b = append(b, row.Primary...)
b = append(b, types.Encode(row.Data)...)
return b, nil
}
//DecodeRow from data
func DecodeRow(data []byte) ([]byte, []byte, error) {
if len(data) <= 8 {
return nil, nil, types.ErrDecode
}
l, err := decodeInt64(data[:8])
if err != nil {
return nil, nil, err
}
if len(data) < int(l)+8 {
return nil, nil, types.ErrDecode
}
return data[8 : int(l)+8], data[int(l)+8:], nil
}
//Table 定一个表格, 并且添加 primary key, index key
type Table struct {
meta RowMeta
rows []*Row
rowmap map[string]*Row
kvdb db.KV
opt *Option
autoinc *Count
dataprefix string
metaprefix string
}
//Option table 的选项
type Option struct {
Prefix string
Name string
Primary string
Index []string
}
//NewTable 新建一个表格
//primary 可以为: auto, 由系统自动创建
//index 可以为nil
func NewTable(rowmeta RowMeta, kvdb db.KV, opt *Option) (*Table, error) {
if len(opt.Index) > 16 {
return nil, ErrTooManyIndex
}
for _, index := range opt.Index {
if strings.Contains(index, "#") {
return nil, ErrIndexKey
}
}
if opt.Primary == "" {
opt.Primary = "auto"
}
if _, err := getPrimaryKey(rowmeta, opt.Primary); err != nil {
return nil, err
}
//不允许有#
if strings.Contains(opt.Prefix, "#") || strings.Contains(opt.Name, "#") {
return nil, ErrTablePrefixOrTableName
}
dataprefix := opt.Prefix + "#" + opt.Name + data
metaprefix := opt.Prefix + "#" + opt.Name + meta
count := NewCount(opt.Prefix, opt.Name+"#autoinc#", kvdb)
return &Table{
meta: rowmeta,
kvdb: kvdb,
rowmap: make(map[string]*Row),
opt: opt,
autoinc: count,
dataprefix: dataprefix,
metaprefix: metaprefix}, nil
}
func getPrimaryKey(meta RowMeta, primary string) ([]byte, error) {
if primary == "" {
return nil, ErrEmptyPrimaryKey
}
if strings.Contains(primary, "#") {
return nil, ErrPrimaryKey
}
if primary != "auto" {
key, err := meta.Get(primary)
return key, err
}
return nil, nil
}
func (table *Table) addRowCache(row *Row) {
table.rowmap[string(row.Primary)] = row
table.rows = append(table.rows, row)
}
func (table *Table) findRow(primary []byte) (*Row, error) {
if row, ok := table.rowmap[string(primary)]; ok {
return row, nil
}
return table.GetData(primary)
}
func (table *Table) checkIndex(data types.Message) error {
err := table.meta.SetPayload(data)
if err != nil {
return err
}
if _, err := getPrimaryKey(table.meta, table.opt.Primary); err != nil {
return err
}
for i := 0; i < len(table.opt.Index); i++ {
_, err := table.meta.Get(table.opt.Index[i])
if err != nil {
return err
}
}
return nil
}
func (table *Table) getPrimaryAuto() ([]byte, error) {
i, err := table.autoinc.Inc()
if err != nil {
return nil, err
}
return []byte(pad(i)), nil
}
//primaryKey 获取主键
//1. auto 的情况下,只能自增。
//2. 没有auto的情况下从数据中取
func (table *Table) primaryKey(data types.Message) (primaryKey []byte, err error) {
if table.opt.Primary == "auto" {
primaryKey, err = table.getPrimaryAuto()
if err != nil {
return nil, err
}
} else {
primaryKey, err = table.getPrimaryFromData(data)
}
return
}
func (table *Table) getPrimaryFromData(data types.Message) (primaryKey []byte, err error) {
err = table.meta.SetPayload(data)
if err != nil {
return nil, err
}
primaryKey, err = getPrimaryKey(table.meta, table.opt.Primary)
if err != nil {
return nil, err
}
return
}
//Replace 如果有重复的,那么替换
func (table *Table) Replace(data types.Message) error {
if err := table.checkIndex(data); err != nil {
return err
}
primaryKey, err := table.primaryKey(data)
if err != nil {
return err
}
//如果是auto的情况,一定是添加
if table.opt.Primary == "auto" {
table.addRowCache(&Row{Data: data, Primary: primaryKey, Ty: Add})
return nil
}
//如果没有找到行, 那么添加
row, err := table.findRow(primaryKey)
if err == types.ErrNotFound {
table.addRowCache(&Row{Data: data, Primary: primaryKey, Ty: Add})
return nil
}
//更新数据
delrow := *row
delrow.Ty = Del
//update 是一个del 和 update 的组合
table.addRowCache(&delrow)
table.addRowCache(&Row{Data: data, Primary: primaryKey, Ty: Add})
return nil
}
//Add 在表格中添加一行
func (table *Table) Add(data types.Message) error {
if err := table.checkIndex(data); err != nil {
return err
}
primaryKey, err := table.primaryKey(data)
if err != nil {
return err
}
//find in cache + db
_, err = table.findRow(primaryKey)
if err != types.ErrNotFound {
return ErrDupPrimaryKey
}
//检查cache中是否有重复,有重复也返回错误
table.addRowCache(&Row{Data: data, Primary: primaryKey, Ty: Add})
return nil
}
//Update 更新数据库
func (table *Table) Update(primaryKey []byte, newdata types.Message) (err error) {
if err := table.checkIndex(newdata); err != nil {
return err
}
p1, err := table.getPrimaryFromData(newdata)
if err != nil {
return err
}
if !bytes.Equal(p1, primaryKey) {
return types.ErrInvalidParam
}
row, err := table.findRow(primaryKey)
//查询发生错误
if err != nil {
return err
}
delrow := *row
delrow.Ty = Del
//update 是一个del 和 update 的组合
table.addRowCache(&delrow)
table.addRowCache(&Row{Data: newdata, Primary: primaryKey, Ty: Add})
return nil
}
//Del 在表格中删除一行(包括删除索引)
func (table *Table) Del(primaryKey []byte) error {
row, err := table.findRow(primaryKey)
if err != nil {
return err
}
delrow := *row
delrow.Ty = Del
table.addRowCache(&delrow)
return nil
}
//getDataKey data key 构造
func (table *Table) getDataKey(primaryKey []byte) []byte {
return append([]byte(table.dataprefix), primaryKey...)
}
//GetIndexKey data key 构造
func (table *Table) getIndexKey(indexName string, index, primaryKey []byte) []byte {
key := table.indexPrefix(indexName)
key = append(key, index...)
key = append(key, []byte("#")...)
key = append(key, primaryKey...)
return key
}
func (table *Table) primaryPrefix() []byte {
return []byte(table.dataprefix)
}
func (table *Table) indexPrefix(indexName string) []byte {
key := append([]byte(table.metaprefix), []byte(indexName+"#")...)
return key
}
func (table *Table) index(row *Row, indexName string) ([]byte, error) {
err := table.meta.SetPayload(row.Data)
if err != nil {
return nil, err
}
return table.meta.Get(indexName)
}
func (table *Table) getData(primaryKey []byte) ([]byte, error) {
key := table.getDataKey(primaryKey)
value, err := table.kvdb.Get(key)
if err != nil {
return nil, err
}
return value, nil
}
//GetData 根据主键获取数据
func (table *Table) GetData(primaryKey []byte) (*Row, error) {
value, err := table.getData(primaryKey)
if err != nil {
return nil, err
}
return table.getRow(value)
}
func (table *Table) getRow(value []byte) (*Row, error) {
primary, data, err := DecodeRow(value)
if err != nil {
return nil, err
}
row := table.meta.CreateRow()
row.Primary = primary
err = types.Decode(data, row.Data)
if err != nil {
return nil, err
}
return row, nil
}
//Save 保存表格
func (table *Table) Save() (kvs []*types.KeyValue, err error) {
for _, row := range table.rows {
kvlist, err := table.saveRow(row)
if err != nil {
return nil, err
}
kvs = append(kvs, kvlist...)
}
kvlist, err := table.autoinc.Save()
if err != nil {
return nil, err
}
kvs = append(kvs, kvlist...)
//del cache
table.rowmap = make(map[string]*Row)
table.rows = nil
return kvs, nil
}
func pad(i int64) string {
return fmt.Sprintf("%020d", i)
}
func (table *Table) saveRow(row *Row) (kvs []*types.KeyValue, err error) {
if row.Ty == Del {
return table.delRow(row)
}
return table.addRow(row)
}
func (table *Table) delRow(row *Row) (kvs []*types.KeyValue, err error) {
deldata := &types.KeyValue{Key: table.getDataKey(row.Primary)}
kvs = append(kvs, deldata)
for _, index := range table.opt.Index {
indexkey, err := table.index(row, index)
if err != nil {
return nil, err
}
delindex := &types.KeyValue{Key: table.getIndexKey(index, indexkey, row.Primary)}
kvs = append(kvs, delindex)
}
return kvs, nil
}
func (table *Table) addRow(row *Row) (kvs []*types.KeyValue, err error) {
data, err := row.Encode()
if err != nil {
return nil, err
}
adddata := &types.KeyValue{Key: table.getDataKey(row.Primary), Value: data}
kvs = append(kvs, adddata)
for _, index := range table.opt.Index {
indexkey, err := table.index(row, index)
if err != nil {
return nil, err
}
addindex := &types.KeyValue{Key: table.getIndexKey(index, indexkey, row.Primary), Value: row.Primary}
kvs = append(kvs, addindex)
}
return kvs, nil
}
//GetQuery 获取查询结构
func (table *Table) GetQuery(kvdb db.KVDB) *Query {
return &Query{table: table, kvdb: kvdb}
}
// 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 table
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
)
func TestTransactinList(t *testing.T) {
dir, leveldb, kvdb := getdb()
defer dbclose(dir, leveldb)
opt := &Option{
Prefix: "prefix",
Name: "name",
Primary: "Hash",
Index: []string{"From", "To"},
}
table, err := NewTable(NewTransactionRow(), kvdb, opt)
assert.Nil(t, err)
addr1, priv := util.Genaddress()
tx1 := util.CreateNoneTx(priv)
err = table.Add(tx1)
assert.Nil(t, err)
tx2 := util.CreateNoneTx(priv)
err = table.Add(tx2)
assert.Nil(t, err)
addr2, priv := util.Genaddress()
tx3 := util.CreateNoneTx(priv)
err = table.Add(tx3)
assert.Nil(t, err)
tx4 := util.CreateNoneTx(priv)
err = table.Add(tx4)
assert.Nil(t, err)
//添加一个无效的类型
err = table.Add(nil)
assert.Equal(t, types.ErrTypeAsset, err)
kvs, err := table.Save()
assert.Nil(t, err)
assert.Equal(t, len(kvs), 12)
//save to database
setKV(leveldb, kvs)
//测试查询
query := table.GetQuery(kvdb)
rows, err := query.ListIndex("From", []byte(addr1), nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 2, len(rows))
if bytes.Compare(tx1.Hash(), tx2.Hash()) > 0 {
assert.Equal(t, true, proto.Equal(tx1, rows[0].Data))
assert.Equal(t, true, proto.Equal(tx2, rows[1].Data))
} else {
assert.Equal(t, true, proto.Equal(tx1, rows[1].Data))
assert.Equal(t, true, proto.Equal(tx2, rows[0].Data))
}
//prefix full
rows, err = query.ListIndex("From", []byte(addr2), nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 2, len(rows))
if bytes.Compare(tx3.Hash(), tx4.Hash()) > 0 {
assert.Equal(t, true, proto.Equal(tx3, rows[0].Data))
assert.Equal(t, true, proto.Equal(tx4, rows[1].Data))
} else {
assert.Equal(t, true, proto.Equal(tx3, rows[1].Data))
assert.Equal(t, true, proto.Equal(tx4, rows[0].Data))
}
//prefix part
rows, err = query.ListIndex("From", []byte(addr2[0:10]), nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 2, len(rows))
if bytes.Compare(tx3.Hash(), tx4.Hash()) > 0 {
assert.Equal(t, true, proto.Equal(tx3, rows[0].Data))
assert.Equal(t, true, proto.Equal(tx4, rows[1].Data))
} else {
assert.Equal(t, true, proto.Equal(tx3, rows[1].Data))
assert.Equal(t, true, proto.Equal(tx4, rows[0].Data))
}
//count
rows, err = query.ListIndex("From", []byte(addr2[0:10]), nil, 1, 0)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
if bytes.Compare(tx3.Hash(), tx4.Hash()) > 0 {
assert.Equal(t, true, proto.Equal(tx3, rows[0].Data))
} else {
assert.Equal(t, true, proto.Equal(tx4, rows[0].Data))
}
primary := rows[0].Primary
//primary
rows, err = query.ListIndex("From", nil, primary, 1, 0)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
if bytes.Compare(tx3.Hash(), tx4.Hash()) > 0 {
assert.Equal(t, true, proto.Equal(tx4, rows[0].Data))
} else {
assert.Equal(t, true, proto.Equal(tx3, rows[0].Data))
}
//prefix + primary
rows, err = query.ListIndex("From", []byte(addr2[0:10]), primary, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
if bytes.Compare(tx3.Hash(), tx4.Hash()) > 0 {
assert.Equal(t, true, proto.Equal(tx4, rows[0].Data))
} else {
assert.Equal(t, true, proto.Equal(tx3, rows[0].Data))
}
rows, err = query.ListIndex("From", []byte(addr1[0:10]), primary, 0, 0)
assert.Equal(t, types.ErrNotFound, err)
assert.Equal(t, 0, len(rows))
//ListPrimary all
rows, err = query.ListPrimary(nil, nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 4, len(rows))
primary = rows[0].Primary
rows, err = query.ListPrimary(primary[0:10], nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
rows, err = query.ListPrimary(nil, primary, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 3, len(rows))
}
func TestTransactinListAuto(t *testing.T) {
dir, leveldb, kvdb := getdb()
defer dbclose(dir, leveldb)
opt := &Option{
Prefix: "prefix",
Name: "name",
Primary: "",
Index: []string{"From", "To"},
}
table, err := NewTable(NewTransactionRow(), kvdb, opt)
assert.Nil(t, err)
addr1, priv := util.Genaddress()
tx1 := util.CreateNoneTx(priv)
err = table.Add(tx1)
assert.Nil(t, err)
tx2 := util.CreateNoneTx(priv)
err = table.Add(tx2)
assert.Nil(t, err)
addr2, priv := util.Genaddress()
tx3 := util.CreateNoneTx(priv)
err = table.Add(tx3)
assert.Nil(t, err)
tx4 := util.CreateNoneTx(priv)
err = table.Add(tx4)
assert.Nil(t, err)
//添加一个无效的类型
err = table.Add(nil)
assert.Equal(t, types.ErrTypeAsset, err)
kvs, err := table.Save()
assert.Nil(t, err)
assert.Equal(t, len(kvs), 13)
//save to database
setKV(leveldb, kvs)
//测试查询
query := table.GetQuery(kvdb)
rows, err := query.ListIndex("From", []byte(addr1), nil, 0, db.ListASC)
assert.Nil(t, err)
assert.Equal(t, 2, len(rows))
assert.Equal(t, true, proto.Equal(tx1, rows[0].Data))
assert.Equal(t, true, proto.Equal(tx2, rows[1].Data))
//prefix full
rows, err = query.ListIndex("From", []byte(addr2), nil, 0, db.ListASC)
assert.Nil(t, err)
assert.Equal(t, 2, len(rows))
assert.Equal(t, true, proto.Equal(tx3, rows[0].Data))
assert.Equal(t, true, proto.Equal(tx4, rows[1].Data))
//prefix part
rows, err = query.ListIndex("From", []byte(addr2[0:10]), nil, 0, db.ListASC)
assert.Nil(t, err)
assert.Equal(t, 2, len(rows))
assert.Equal(t, true, proto.Equal(tx3, rows[0].Data))
assert.Equal(t, true, proto.Equal(tx4, rows[1].Data))
//count
rows, err = query.ListIndex("From", []byte(addr2[0:10]), nil, 1, db.ListASC)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
assert.Equal(t, true, proto.Equal(tx3, rows[0].Data))
primary := rows[0].Primary
//primary
rows, err = query.ListIndex("From", nil, primary, 1, db.ListASC)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
assert.Equal(t, true, proto.Equal(tx4, rows[0].Data))
//prefix + primary
rows, err = query.ListIndex("From", []byte(addr2[0:10]), primary, 0, db.ListASC)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
assert.Equal(t, true, proto.Equal(tx4, rows[0].Data))
rows, err = query.ListIndex("From", []byte(addr1[0:10]), primary, 0, db.ListASC)
assert.Equal(t, types.ErrNotFound, err)
assert.Equal(t, 0, len(rows))
//ListPrimary all
rows, err = query.ListPrimary(nil, nil, 0, db.ListASC)
assert.Nil(t, err)
assert.Equal(t, 4, len(rows))
primary = rows[0].Primary
rows, err = query.ListPrimary(primary, nil, 0, db.ListASC)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
rows, err = query.ListPrimary(nil, primary, 0, db.ListASC)
assert.Nil(t, err)
assert.Equal(t, 3, len(rows))
}
func mergeDup(kvs []*types.KeyValue) (kvset []*types.KeyValue) {
maplist := make(map[string]*types.KeyValue)
for _, kv := range kvs {
if item, ok := maplist[string(kv.Key)]; ok {
item.Value = kv.Value //更新item 的value
} else {
maplist[string(kv.Key)] = kv
kvset = append(kvset, kv)
}
}
return kvset
}
func setKV(kvdb db.DB, kvs []*types.KeyValue) {
batch := kvdb.NewBatch(true)
for i := 0; i < len(kvs); i++ {
if kvs[i].Value == nil {
batch.Delete(kvs[i].Key)
continue
}
batch.Set(kvs[i].Key, kvs[i].Value)
}
err := batch.Write()
if err != nil {
panic(err)
}
}
func printKV(kvs []*types.KeyValue) {
for i := 0; i < len(kvs); i++ {
fmt.Println("KV", i, string(kvs[i].Key), common.ToHex(kvs[i].Value))
}
}
func TestRow(t *testing.T) {
rowmeta := NewTransactionRow()
row := rowmeta.CreateRow()
_, priv := util.Genaddress()
tx1 := util.CreateNoneTx(priv)
row.Data = tx1
row.Primary = tx1.Hash()
data, err := row.Encode()
assert.Nil(t, err)
primary, protodata, err := DecodeRow(data)
assert.Nil(t, err)
assert.Equal(t, primary, row.Primary)
var tx types.Transaction
err = types.Decode(protodata, &tx)
assert.Nil(t, err)
assert.Equal(t, proto.Equal(&tx, tx1), true)
}
func TestDel(t *testing.T) {
dir, leveldb, kvdb := getdb()
defer dbclose(dir, leveldb)
opt := &Option{
Prefix: "prefix",
Name: "name",
Primary: "Hash",
Index: []string{"From", "To"},
}
table, err := NewTable(NewTransactionRow(), kvdb, opt)
assert.Nil(t, err)
addr1, priv := util.Genaddress()
tx1 := util.CreateNoneTx(priv)
err = table.Add(tx1)
assert.Nil(t, err)
_, priv = util.Genaddress()
tx2 := util.CreateNoneTx(priv)
err = table.Add(tx2)
assert.Nil(t, err)
//删除掉一个
err = table.Del(tx1.Hash())
assert.Nil(t, err)
//save 然后从列表中读取
kvs, err := table.Save()
assert.Nil(t, err)
assert.Equal(t, len(kvs), 9)
//save to database
setKV(leveldb, kvs)
//printKV(kvs)
query := table.GetQuery(kvdb)
rows, err := query.ListIndex("From", []byte(addr1[0:10]), nil, 0, 0)
assert.Equal(t, types.ErrNotFound, err)
assert.Equal(t, 0, len(rows))
}
func printAllKey(db db.DB) {
it := db.Iterator(nil, nil, false)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
fmt.Println("db.allkey", string(it.Key()))
}
}
func TestUpdate(t *testing.T) {
dir, leveldb, kvdb := getdb()
defer dbclose(dir, leveldb)
opt := &Option{
Prefix: "prefix",
Name: "name",
Primary: "Hash",
Index: []string{"From", "To"},
}
table, err := NewTable(NewTransactionRow(), kvdb, opt)
assert.Nil(t, err)
_, priv := util.Genaddress()
tx1 := util.CreateNoneTx(priv)
err = table.Add(tx1)
assert.Nil(t, err)
err = table.Update([]byte("hello"), tx1)
assert.Equal(t, err, types.ErrInvalidParam)
tx1.Signature = nil
err = table.Update(tx1.Hash(), tx1)
assert.Nil(t, err)
kvs, err := table.Save()
assert.Nil(t, err)
assert.Equal(t, len(kvs), 9)
//save to database
setKV(leveldb, kvs)
query := table.GetQuery(kvdb)
rows, err := query.ListIndex("From", []byte(tx1.From()), nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, rows[0].Data.(*types.Transaction).From(), tx1.From())
}
func TestReplace(t *testing.T) {
dir, leveldb, kvdb := getdb()
defer dbclose(dir, leveldb)
opt := &Option{
Prefix: "prefix",
Name: "name",
Primary: "Hash",
Index: []string{"From", "To"},
}
table, err := NewTable(NewTransactionRow(), kvdb, opt)
assert.Nil(t, err)
addr1, priv := util.Genaddress()
tx1 := util.CreateNoneTx(priv)
err = table.Add(tx1)
assert.Nil(t, err)
err = table.Add(tx1)
assert.Equal(t, err, ErrDupPrimaryKey)
//不改变hash,改变签名
tx1.Signature = nil
err = table.Replace(tx1)
assert.Nil(t, err)
//save 然后从列表中读取
kvs, err := table.Save()
assert.Nil(t, err)
assert.Equal(t, len(kvs), 9)
//save to database
setKV(leveldb, kvs)
query := table.GetQuery(kvdb)
_, err = query.ListIndex("From", []byte(addr1[0:10]), nil, 0, 0)
assert.Equal(t, err, types.ErrNotFound)
rows, err := query.ListIndex("From", []byte(tx1.From()), nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, rows[0].Data.(*types.Transaction).From(), tx1.From())
}
type TransactionRow struct {
*types.Transaction
}
func NewTransactionRow() *TransactionRow {
return &TransactionRow{Transaction: &types.Transaction{}}
}
func (tx *TransactionRow) CreateRow() *Row {
return &Row{Data: &types.Transaction{}}
}
func (tx *TransactionRow) SetPayload(data types.Message) error {
if txdata, ok := data.(*types.Transaction); ok {
tx.Transaction = txdata
return nil
}
return types.ErrTypeAsset
}
func (tx *TransactionRow) Get(key string) ([]byte, error) {
if key == "Hash" {
return tx.Hash(), nil
} else if key == "From" {
return []byte(tx.From()), nil
} else if key == "To" {
return []byte(tx.To), nil
}
return nil, types.ErrNotFound
}
func getdb() (string, db.DB, db.KVDB) {
dir, err := ioutil.TempDir("", "goleveldb")
if err != nil {
panic(err)
}
leveldb, err := db.NewGoLevelDB("goleveldb", dir, 128)
if err != nil {
panic(err)
}
return dir, leveldb, db.NewKVDB(leveldb)
}
func dbclose(dir string, dbm db.DB) {
os.RemoveAll(dir)
dbm.Close()
}
......@@ -105,7 +105,7 @@ func CreateRawTx(cmd *cobra.Command, to string, amount float64, note string, isW
transfer.Ty = cty.CoinsActionTransfer
}
} else {
v := &cty.CoinsAction_Withdraw{Withdraw: &types.AssetsWithdraw{Amount: amountInt64, Note: []byte(note), ExecName: execName}}
v := &cty.CoinsAction_Withdraw{Withdraw: &types.AssetsWithdraw{Amount: amountInt64, Note: []byte(note), ExecName: execName, To: to}}
transfer.Value = v
transfer.Ty = cty.CoinsActionWithdraw
}
......
......@@ -97,7 +97,7 @@ func (mem *Mempool) eventProcess() {
func (mem *Mempool) eventTx(msg queue.Message) {
if !mem.getSync() {
msg.Reply(mem.client.NewMessage("", types.EventReply, &types.Reply{Msg: []byte(types.ErrNotSync.Error())}))
mlog.Error("wrong tx", "err", types.ErrNotSync.Error())
mlog.Debug("wrong tx", "err", types.ErrNotSync.Error())
} else {
checkedMsg := mem.checkTxs(msg)
select {
......
......@@ -111,7 +111,9 @@ func CreateTxWithExecer(priv crypto.PrivKey, execer string) *types.Transaction {
tx := &types.Transaction{Execer: []byte(execer), Payload: []byte("none")}
tx.To = address.ExecAddress(execer)
tx, _ = types.FormatTx(execer, tx)
tx.Sign(types.SECP256K1, priv)
if priv != nil {
tx.Sign(types.SECP256K1, priv)
}
return tx
}
......
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