Commit 2138931a authored by liuyuhang's avatar liuyuhang Committed by 33cn

add comment cmd

parent 19ee94ab
......@@ -53,6 +53,8 @@ func AutonomyCmd() *cobra.Command {
cmd.AddCommand(
TransferFundCmd(),
CommentProposalCmd(),
ShowProposalCommentCmd(),
)
return cmd
......
......@@ -257,4 +257,84 @@ func transferFund(cmd *cobra.Command, args []string) {
var res string
ctx := jsonrpc.NewRPCCtx(rpcLaddr, "autonomy.TransferFundTx", params, &res)
ctx.RunWithoutMarshal()
}
\ No newline at end of file
}
// CommentProposalCmd 评论提案
func CommentProposalCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "comment",
Short: "comment proposal",
Run: commentProposal,
}
addCommentProposalflags(cmd)
return cmd
}
func addCommentProposalflags(cmd *cobra.Command) {
cmd.Flags().StringP("proposalID", "p", "", "proposal ID")
cmd.MarkFlagRequired("proposalID")
cmd.Flags().StringP("repCmtHash", "r", "", "reply Comment hash")
cmd.Flags().StringP("comment", "c", "", "comment")
cmd.MarkFlagRequired("comment")
}
func commentProposal(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
proposalID, _:= cmd.Flags().GetString("proposalID")
repCmtHash, _:= cmd.Flags().GetString("repCmtHash")
comment, _:= cmd.Flags().GetString("comment")
params := &auty.Comment{
ProposalID:proposalID,
RepCmtHash:repCmtHash,
Comment:comment,
}
var res string
ctx := jsonrpc.NewRPCCtx(rpcLaddr, "autonomy.CommentProposalTx", params, &res)
ctx.RunWithoutMarshal()
}
// ShowProposalCommentCmd 显示提案评论查询信息
func ShowProposalCommentCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "showComment",
Short: "show proposal comment info",
Run: showProposalComment,
}
addShowProposalCommentflags(cmd)
return cmd
}
func addShowProposalCommentflags(cmd *cobra.Command) {
cmd.Flags().StringP("proposalID", "p", "", "proposal ID")
cmd.MarkFlagRequired("proposalID")
cmd.Flags().Int32P("count", "c", 0, "count")
cmd.Flags().Int32P("direction", "d", 0, "direction")
cmd.Flags().Int64P("index", "i", 0, "index")
}
func showProposalComment(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
propID, _:= cmd.Flags().GetString("proposalID")
count, _ := cmd.Flags().GetInt32("count")
direction, _ := cmd.Flags().GetInt32("direction")
index, _ := cmd.Flags().GetInt64("index")
var params rpctypes.Query4Jrpc
var rep interface{}
params.Execer = auty.AutonomyX
req := auty.ReqQueryProposalComment{
ProposalID: propID,
Count: count,
Direction: direction,
Index: index,
}
params.FuncName = auty.ListProposalComment
params.Payload = types.MustPBToJSON(&req)
rep = &auty.ReplyQueryProposalComment{}
ctx := jsonrpc.NewRPCCtx(rpcLaddr, "Chain33.Query", params, rep)
ctx.Run()
}
......@@ -16,6 +16,7 @@ var (
alog = log.New("module", "execs.autonomy")
driverName = auty.AutonomyX
autonomyAddr = address.ExecAddress(auty.AutonomyX)
autonomyFundAddr = address.ExecAddress("autonomyfund")
)
......
......@@ -220,7 +220,7 @@ func (a *action) votePropBoard(voteProb *auty.VoteProposalBoard) (*types.Receipt
// 首次进入投票期,即将提案金转入自治系统地址
if cur.Status == auty.AutonomyStatusProposalBoard {
receipt, err := a.coinsAccount.ExecTransferFrozen(cur.Address, autonomyAddr, a.execaddr, cur.CurRule.ProposalAmount)
receipt, err := a.coinsAccount.ExecTransferFrozen(cur.Address, autonomyFundAddr, a.execaddr, cur.CurRule.ProposalAmount)
if err != nil {
alog.Error("votePropBoard ", "addr", cur.Address, "execaddr", a.execaddr, "ExecTransferFrozen amount fail", err)
return nil, err
......@@ -311,7 +311,7 @@ func (a *action) tmintPropBoard(tmintProb *auty.TerminateProposalBoard) (*types.
// 未进行投票情况下,符合提案关闭的也需要扣除提案费用
if cur.Status == auty.AutonomyStatusProposalBoard {
receipt, err := a.coinsAccount.ExecTransferFrozen(cur.Address, autonomyAddr, a.execaddr, cur.CurRule.ProposalAmount)
receipt, err := a.coinsAccount.ExecTransferFrozen(cur.Address, autonomyFundAddr, a.execaddr, cur.CurRule.ProposalAmount)
if err != nil {
alog.Error("votePropBoard ", "addr", a.fromaddr, "execaddr", a.execaddr, "ExecTransferFrozen amount fail", err)
return nil, err
......
......@@ -319,7 +319,7 @@ func voteProposalBoard(t *testing.T, env *execEnv, exec drivers.Driver, stateDB
accCoin.SetDB(stateDB)
account := accCoin.LoadExecAccount(AddrA, address.ExecAddress(auty.AutonomyX))
require.Equal(t, int64(0), account.Frozen)
account = accCoin.LoadExecAccount(autonomyAddr, address.ExecAddress(auty.AutonomyX))
account = accCoin.LoadExecAccount(autonomyFundAddr, address.ExecAddress(auty.AutonomyX))
require.Equal(t, int64(proposalAmount), account.Balance)
// status
value, err := stateDB.Get(propBoardID(proposalID))
......
......@@ -75,4 +75,9 @@ func (a *Autonomy) ExecDelLocal_VotePropRule(payload *auty.VoteProposalRule, tx
// ExecDelLocal_TmintPropRule 终止提案规则
func (a *Autonomy) ExecDelLocal_TmintPropRule(payload *auty.TerminateProposalRule, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return a.execDelLocalRule(receiptData)
}
// ExecDelLocal_TmintPropRule 终止提案规则
func (a *Autonomy) ExecDelLocal_Comment(payload *auty.Comment, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return a.execDelLocalComment(receiptData)
}
\ No newline at end of file
......@@ -38,18 +38,27 @@ func (a *action) propProject(prob *auty.ProposalProject) (*types.Receipt, error)
return nil, err
}
var logs []*types.ReceiptLog
var kv []*types.KeyValue
// 冻结提案金
receipt, err := a.coinsAccount.ExecFrozen(a.fromaddr, a.execaddr, rule.ProposalAmount)
if err != nil {
alog.Error("propProject ", "addr", a.fromaddr, "execaddr", a.execaddr, "ExecFrozen amount", rule.ProposalAmount)
alog.Error("propProject ", "addr", a.fromaddr, "execaddr", a.execaddr, "ExecFrozen proposal amount", rule.ProposalAmount, "error", err)
return nil, err
}
var logs []*types.ReceiptLog
var kv []*types.KeyValue
logs = append(logs, receipt.Logs...)
kv = append(kv, receipt.KV...)
// 冻结项目金
receiptPrj, err := a.coinsAccount.ExecFrozen(autonomyFundAddr, a.execaddr, prob.Amount)
if err != nil {
alog.Error("propProject ", "addr", a.fromaddr, "execaddr", a.execaddr, "ExecFrozen project amount", prob.Amount, "error", err)
return nil, err
}
logs = append(logs, receiptPrj.Logs...)
kv = append(kv, receiptPrj.KV...)
var isPubVote bool
if prob.Amount >= rule.LargeProjectAmount {
isPubVote = true
......@@ -107,6 +116,7 @@ func (a *action) rvkPropProject(rvkProb *auty.RevokeProposalProject) (*types.Rec
var logs []*types.ReceiptLog
var kv []*types.KeyValue
// 解冻提案金
receipt, err := a.coinsAccount.ExecActive(a.fromaddr, a.execaddr, cur.CurRule.ProposalAmount)
if err != nil {
alog.Error("rvkPropProject ", "addr", a.fromaddr, "execaddr", a.execaddr, "ExecActive amount", cur.CurRule.ProposalAmount, "err", err)
......@@ -115,6 +125,15 @@ func (a *action) rvkPropProject(rvkProb *auty.RevokeProposalProject) (*types.Rec
logs = append(logs, receipt.Logs...)
kv = append(kv, receipt.KV...)
// 解冻项目金
receiptPrj, err := a.coinsAccount.ExecActive(autonomyFundAddr, a.execaddr, cur.PropProject.Amount)
if err != nil {
alog.Error("rvkPropProject ", "addr", a.fromaddr, "execaddr", a.execaddr, "ExecActive project amount", cur.PropProject.Amount, "error", err)
return nil, err
}
logs = append(logs, receiptPrj.Logs...)
kv = append(kv, receiptPrj.KV...)
cur.Status = auty.AutonomyStatusRvkPropProject
kv = append(kv, &types.KeyValue{Key: propProjectID(rvkProb.ProposalID), Value: types.Encode(cur)})
......@@ -191,7 +210,7 @@ func (a *action) votePropProject(voteProb *auty.VoteProposalProject) (*types.Rec
// 首次进入投票期,即将提案金转入自治系统地址
if cur.Status == auty.AutonomyStatusProposalProject {
receipt, err := a.coinsAccount.ExecTransferFrozen(cur.Address, autonomyAddr, a.execaddr, cur.CurRule.ProposalAmount)
receipt, err := a.coinsAccount.ExecTransferFrozen(cur.Address, autonomyFundAddr, a.execaddr, cur.CurRule.ProposalAmount)
if err != nil {
alog.Error("votePropProject ", "addr", cur.Address, "execaddr", a.execaddr, "ExecTransferFrozen amount fail", err)
return nil, err
......@@ -218,9 +237,9 @@ func (a *action) votePropProject(voteProb *auty.VoteProposalProject) (*types.Rec
} else {
cur.Status = auty.AutonomyStatusTmintPropProject
// 提案通过,将工程金额从基金付款给承包商
receipt, err := a.coinsAccount.ExecTransfer(autonomyAddr, cur.PropProject.ToAddr, a.execaddr, cur.PropProject.Amount)
receipt, err := a.coinsAccount.ExecTransferFrozen(autonomyFundAddr, cur.PropProject.ToAddr, a.execaddr, cur.PropProject.Amount)
if err != nil {
alog.Error("votePropProject ", "addr", cur.Address, "execaddr", a.execaddr, "ExecTransfer to contractor project amount fail", err)
alog.Error("votePropProject ", "addr", cur.Address, "execaddr", a.execaddr, "ExecTransferFrozen to contractor project amount fail", err)
return nil, err
}
logs = append(logs, receipt.Logs...)
......@@ -304,8 +323,17 @@ func (a *action) pubVotePropProject(voteProb *auty.PubVoteProposalProject) (*typ
if cur.PubVote.TotalVotes != 0 &&
float32(cur.PubVote.OpposeVotes) / float32(cur.PubVote.TotalVotes) >= float32(cur.CurRule.PubOpposeRatio) / 100.0 {
cur.PubVote.PubPass = false
cur.PubVote.PubPass = false
cur.PropProject.RealEndBlockHeight = a.height
// 解冻项目金
receiptPrj, err := a.coinsAccount.ExecActive(autonomyFundAddr, a.execaddr, cur.PropProject.Amount)
if err != nil {
alog.Error("pubVotePropProject ", "addr", a.fromaddr, "execaddr", a.execaddr, "ExecActive project amount", cur.PropProject.Amount, "error", err)
return nil, err
}
logs = append(logs, receiptPrj.Logs...)
kv = append(kv, receiptPrj.KV...)
}
key := propProjectID(voteProb.ProposalID)
......@@ -394,9 +422,9 @@ func (a *action) tmintPropProject(tmintProb *auty.TerminateProposalProject) (*ty
// 如果为提案状态,则判断是否需要扣除提案费
if cur.Status == auty.AutonomyStatusProposalProject && a.height > end {
receipt, err := a.coinsAccount.ExecTransferFrozen(cur.Address, autonomyAddr, a.execaddr, cur.CurRule.ProposalAmount)
receipt, err := a.coinsAccount.ExecTransferFrozen(cur.Address, autonomyFundAddr, a.execaddr, cur.CurRule.ProposalAmount)
if err != nil {
alog.Error("votePropProject ", "addr", cur.Address, "execaddr", a.execaddr, "ExecTransferFrozen amount fail", err)
alog.Error("tmintPropProject ", "addr", cur.Address, "execaddr", a.execaddr, "ExecTransferFrozen amount fail", err)
return nil, err
}
logs = append(logs, receipt.Logs...)
......@@ -404,15 +432,24 @@ func (a *action) tmintPropProject(tmintProb *auty.TerminateProposalProject) (*ty
}
if (cur.PubVote.Publicity && cur.PubVote.PubPass) || // 需要公示且公示通过
(!cur.PubVote.Publicity && cur.BoardVoteRes.Pass){ // 不需要公示且董事会通过
(!cur.PubVote.Publicity && cur.BoardVoteRes.Pass) { // 不需要公示且董事会通过
// 提案通过,将工程金额从基金付款给承包商
receipt, err := a.coinsAccount.ExecTransfer(autonomyAddr, cur.PropProject.ToAddr, a.execaddr, cur.PropProject.Amount)
receipt, err := a.coinsAccount.ExecTransferFrozen(autonomyFundAddr, cur.PropProject.ToAddr, a.execaddr, cur.PropProject.Amount)
if err != nil {
alog.Error("tmintPropProject ", "addr", cur.Address, "execaddr", a.execaddr, "ExecTransfer to contractor project amount fail", err)
alog.Error("tmintPropProject ", "addr", cur.Address, "execaddr", a.execaddr, "ExecTransferFrozen to contractor project amount fail", err)
return nil, err
}
logs = append(logs, receipt.Logs...)
kv = append(kv, receipt.KV...)
} else {
// 解冻项目金
receiptPrj, err := a.coinsAccount.ExecActive(autonomyFundAddr, a.execaddr, cur.PropProject.Amount)
if err != nil {
alog.Error("tmintPropProject ", "addr", a.fromaddr, "execaddr", a.execaddr, "ExecActive project amount", cur.PropProject.Amount, "error", err)
return nil, err
}
logs = append(logs, receiptPrj.Logs...)
kv = append(kv, receiptPrj.KV...)
}
cur.Status = auty.AutonomyStatusTmintPropProject
......
......@@ -59,6 +59,17 @@ func InitRule(stateDB dbm.KV) {
stateDB.Set(activeRuleID(), types.Encode(rule))
}
func InitFund(stateDB dbm.KV, amount int64) {
accountA := types.Account{
Balance: amount,
Frozen: 0,
Addr: autonomyFundAddr,
}
accCoin := account.NewCoinsAccount()
accCoin.SetDB(stateDB)
accCoin.SaveExecAccount(autonomyAddr, &accountA)
}
func TestPropProject(t *testing.T) {
env, exec, _, _ := InitEnv()
......@@ -110,6 +121,7 @@ func TestPropProject(t *testing.T) {
func TestRevokeProposalProject(t *testing.T) {
env, exec, stateDB, kvdb := InitEnv()
InitBoard(stateDB)
InitFund(stateDB, testProjectAmount)
// PropProject
testPropProject(t, env, exec, stateDB, kvdb, true)
//RevokeProposalProject
......@@ -119,6 +131,7 @@ func TestRevokeProposalProject(t *testing.T) {
func TestVoteProposalProject(t *testing.T) {
env, exec, stateDB, kvdb := InitEnv()
InitBoard(stateDB)
InitFund(stateDB, testProjectAmount)
// PropProject
testPropProject(t, env, exec, stateDB, kvdb, true)
//voteProposalProject
......@@ -131,6 +144,7 @@ func TestPubVoteProposalProject(t *testing.T) {
env, exec, stateDB, kvdb := InitEnv()
InitBoard(stateDB)
InitRule(stateDB)
InitFund(stateDB, testProjectAmount)
// PropProject
testPropProject(t, env, exec, stateDB, kvdb, true)
// voteProposalProject
......@@ -145,6 +159,7 @@ func TestPubVoteProposalProject(t *testing.T) {
func TestTerminateProposalProject(t *testing.T) {
env, exec, stateDB, kvdb := InitEnv()
InitBoard(stateDB)
InitFund(stateDB, testProjectAmount)
// PropProject
testPropProject(t, env, exec, stateDB, kvdb, true)
//terminateProposalProject
......@@ -360,8 +375,8 @@ func checkVoteProposalProjectResult(t *testing.T, stateDB dbm.KV, proposalID str
accCoin.SetDB(stateDB)
account := accCoin.LoadExecAccount(AddrA, address.ExecAddress(auty.AutonomyX))
require.Equal(t, int64(0), account.Frozen)
account = accCoin.LoadExecAccount(autonomyAddr, address.ExecAddress(auty.AutonomyX))
require.Equal(t, int64(proposalAmount) - testProjectAmount, account.Balance)
account = accCoin.LoadExecAccount(autonomyFundAddr, address.ExecAddress(auty.AutonomyX))
require.Equal(t, int64(proposalAmount), account.Balance)
// status
value, err := stateDB.Get(propProjectID(proposalID))
require.NoError(t, err)
......@@ -459,8 +474,8 @@ func checkPubVoteProposalProjectResult(t *testing.T, stateDB dbm.KV, proposalID
accCoin.SetDB(stateDB)
account := accCoin.LoadExecAccount(AddrA, address.ExecAddress(auty.AutonomyX))
require.Equal(t, int64(0), account.Frozen)
account = accCoin.LoadExecAccount(autonomyAddr, address.ExecAddress(auty.AutonomyX))
require.Equal(t, int64(proposalAmount), account.Balance)
account = accCoin.LoadExecAccount(autonomyFundAddr, address.ExecAddress(auty.AutonomyX))
require.Equal(t, int64(proposalAmount) + testProjectAmount, account.Balance)
// status
value, err := stateDB.Get(propProjectID(proposalID))
require.NoError(t, err)
......
......@@ -39,4 +39,10 @@ func (a *Autonomy) Query_GetProposalRule(in *types.ReqString) (types.Message, er
// Query_ListProposalRule 批量查询
func (a *Autonomy) Query_ListProposalRule(in *auty.ReqQueryProposalRule) (types.Message, error) {
return a.listProposalRule(in)
}
// Query_ListProposalComment 批量查询提案评论
func (a *Autonomy) Query_ListProposalComment(in *auty.ReqQueryProposalComment) (types.Message, error) {
return a.listProposalComment(in)
}
\ No newline at end of file
......@@ -173,7 +173,74 @@ func (a *Autonomy) execLocalComment(receiptData *types.ReceiptData) (*types.Loca
func saveCommentHeightIndex(res *auty.ReceiptProposalComment) (kvs []*types.KeyValue) {
kv := &types.KeyValue{}
kv.Key = calcCommentHeight(res.Cmt.ProposalID, dapp.HeightIndexStr(res.Height, int64(res.Index)))
kv.Value = types.Encode(&types.ReqString{Data:res.Cmt.Comment})
kv.Value = types.Encode(&auty.RelationCmt{RepCmtHash: res.Cmt.RepCmtHash, Comment: res.Cmt.Comment})
kvs = append(kvs, kv)
return kvs
}
func (a *Autonomy) execDelLocalComment(receiptData *types.ReceiptData) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
var set []*types.KeyValue
for _, log := range receiptData.Logs {
switch log.Ty {
case auty.TyLogCommentProp:
{
var receipt auty.ReceiptProposalComment
err := types.Decode(log.Log, &receipt)
if err != nil {
return nil, err
}
kv := delCommentHeightIndex(&receipt)
set = append(set, kv...)
}
default:
break
}
}
dbSet.KV = append(dbSet.KV, set...)
return dbSet, nil
}
func delCommentHeightIndex(res *auty.ReceiptProposalComment) (kvs []*types.KeyValue) {
kv := &types.KeyValue{}
kv.Key = calcCommentHeight(res.Cmt.ProposalID, dapp.HeightIndexStr(res.Height, int64(res.Index)))
kv.Value = nil
kvs = append(kvs, kv)
return kvs
}
func (a *Autonomy) listProposalComment(req *auty.ReqQueryProposalComment) (types.Message, error) {
if req == nil {
return nil, types.ErrInvalidParam
}
var key []byte
var values [][]byte
var err error
localDb := a.GetLocalDB()
if req.GetIndex() == -1 {
key = nil
} else { //翻页查找指定的txhash列表
heightstr := genHeightIndexStr(req.GetIndex())
key = calcCommentHeight(req.ProposalID, heightstr)
}
prefix := calcCommentHeight(req.ProposalID, "")
values, err = localDb.List(prefix, key, req.Count, req.GetDirection())
if err != nil {
return nil, err
}
if len(values) == 0 {
return nil, types.ErrNotFound
}
var rep auty.ReplyQueryProposalComment
for _, value := range values {
cmt := &auty.RelationCmt{}
err = types.Decode(value, cmt)
if err != nil {
return nil, err
}
rep.RltCmt = append(rep.RltCmt, cmt)
}
return &rep, nil
}
\ No newline at end of file
......@@ -188,7 +188,7 @@ func (a *action) votePropRule(voteProb *auty.VoteProposalRule) (*types.Receipt,
// 首次进入投票期,即将提案金转入自治系统地址
if cur.Status == auty.AutonomyStatusProposalRule {
receipt, err := a.coinsAccount.ExecTransferFrozen(cur.Address, autonomyAddr, a.execaddr, cur.CurRule.ProposalAmount)
receipt, err := a.coinsAccount.ExecTransferFrozen(cur.Address, autonomyFundAddr, a.execaddr, cur.CurRule.ProposalAmount)
if err != nil {
alog.Error("votePropRule ", "addr", cur.Address, "execaddr", a.execaddr, "ExecTransferFrozen amount fail", err)
return nil, err
......@@ -281,7 +281,7 @@ func (a *action) tmintPropRule(tmintProb *auty.TerminateProposalRule) (*types.Re
// 未进行投票情况下,符合提案关闭的也需要扣除提案费用
if cur.Status == auty.AutonomyStatusProposalRule {
receipt, err := a.coinsAccount.ExecTransferFrozen(cur.Address, autonomyAddr, a.execaddr, cur.CurRule.ProposalAmount)
receipt, err := a.coinsAccount.ExecTransferFrozen(cur.Address, autonomyFundAddr, a.execaddr, cur.CurRule.ProposalAmount)
if err != nil {
alog.Error("votePropRule ", "addr", a.fromaddr, "execaddr", a.execaddr, "ExecTransferFrozen amount fail", err)
return nil, err
......@@ -315,7 +315,7 @@ func (a *action) transfer(tf *auty.TransferFund) (*types.Receipt, error) {
var logs []*types.ReceiptLog
var kv []*types.KeyValue
receipt, err := a.coinsAccount.ExecTransfer(a.fromaddr, autonomyAddr, a.execaddr, tf.Amount)
receipt, err := a.coinsAccount.ExecTransfer(a.fromaddr, autonomyFundAddr, a.execaddr, tf.Amount)
if err != nil {
alog.Error("autonomy transfer ", "addr", a.fromaddr, "amount", tf.Amount, "ExecTransfer fail", err)
return nil, err
......
......@@ -263,7 +263,7 @@ func voteProposalRule(t *testing.T, env *execEnv, exec drivers.Driver, stateDB d
accCoin.SetDB(stateDB)
account := accCoin.LoadExecAccount(AddrA, address.ExecAddress(auty.AutonomyX))
require.Equal(t, int64(0), account.Frozen)
account = accCoin.LoadExecAccount(autonomyAddr, address.ExecAddress(auty.AutonomyX))
account = accCoin.LoadExecAccount(autonomyFundAddr, address.ExecAddress(auty.AutonomyX))
require.Equal(t, int64(proposalAmount), account.Balance)
// status
value, err := stateDB.Get(propRuleID(proposalID))
......
......@@ -80,11 +80,29 @@ message TransferFund {
// Comment action
message Comment {
string proposalID = 1;
string comment = 2;
string repCmtHash = 2;
string comment = 3;
}
message ReceiptProposalComment {
Comment cmt = 1;
int64 height = 2;
int32 index = 3;
}
// query
message ReqQueryProposalComment {
string proposalID = 1;
int32 count = 2;
int32 direction = 3;
int64 index = 4;
}
message RelationCmt {
string repCmtHash = 1;
string comment = 2;
}
message ReplyQueryProposalComment {
repeated RelationCmt rltCmt = 1;
}
\ No newline at end of file
......@@ -199,4 +199,18 @@ func (c *Jrpc) TransferFundTx(parm *auty.TransferFund, result *interface{}) erro
*result = hex.EncodeToString(reply.Data)
return nil
}
// CommentProposalTx 提案评论
func (c *Jrpc) CommentProposalTx(parm *auty.Comment, result *interface{}) error {
if parm == nil {
return types.ErrInvalidParam
}
reply, err := c.cli.commentProposal(context.Background(), parm)
if err != nil {
return err
}
*result = hex.EncodeToString(reply.Data)
return nil
}
\ No newline at end of file
......@@ -222,4 +222,19 @@ func (c *channelClient) transferFund(ctx context.Context, head *auty.TransferFun
return nil, err
}
return &types.UnsignTx{Data: data}, nil
}
func (c *channelClient) commentProposal(ctx context.Context, head *auty.Comment) (*types.UnsignTx, error) {
val := &auty.AutonomyAction{
Ty: auty.AutonomyActionCommentProp,
Value: &auty.AutonomyAction_CommentProp{CommentProp: head},
}
tx := &types.Transaction{
Payload: types.Encode(val),
}
data, err := types.FormatTxEncode(types.ExecName(auty.AutonomyX), tx)
if err != nil {
return nil, err
}
return &types.UnsignTx{Data: data}, nil
}
\ No newline at end of file
......@@ -48,6 +48,9 @@ It has these top-level messages:
TransferFund
Comment
ReceiptProposalComment
ReqQueryProposalComment
RelationCmt
ReplyQueryProposalComment
*/
package types
......
......@@ -23,6 +23,7 @@ const (
AutonomyActionTmintPropRule
AutonomyActionTransfer
AutonomyActionCommentProp
//log for autonomy
TyLogPropBoard = 2101
......@@ -80,6 +81,8 @@ const (
GetProposalRule = "GetProposalRule"
// ListProposalRule
ListProposalRule = "ListProposalRule"
// ListProposalComment
ListProposalComment = "ListProposalComment"
)
......
......@@ -336,7 +336,8 @@ func (m *TransferFund) GetNote() string {
// Comment action
type Comment struct {
ProposalID string `protobuf:"bytes,1,opt,name=proposalID" json:"proposalID,omitempty"`
Comment string `protobuf:"bytes,2,opt,name=comment" json:"comment,omitempty"`
RepCmtHash string `protobuf:"bytes,2,opt,name=repCmtHash" json:"repCmtHash,omitempty"`
Comment string `protobuf:"bytes,3,opt,name=comment" json:"comment,omitempty"`
}
func (m *Comment) Reset() { *m = Comment{} }
......@@ -351,6 +352,13 @@ func (m *Comment) GetProposalID() string {
return ""
}
func (m *Comment) GetRepCmtHash() string {
if m != nil {
return m.RepCmtHash
}
return ""
}
func (m *Comment) GetComment() string {
if m != nil {
return m.Comment
......@@ -390,6 +398,87 @@ func (m *ReceiptProposalComment) GetIndex() int32 {
return 0
}
// query
type ReqQueryProposalComment struct {
ProposalID string `protobuf:"bytes,1,opt,name=proposalID" json:"proposalID,omitempty"`
Count int32 `protobuf:"varint,2,opt,name=count" json:"count,omitempty"`
Direction int32 `protobuf:"varint,3,opt,name=direction" json:"direction,omitempty"`
Index int64 `protobuf:"varint,4,opt,name=index" json:"index,omitempty"`
}
func (m *ReqQueryProposalComment) Reset() { *m = ReqQueryProposalComment{} }
func (m *ReqQueryProposalComment) String() string { return proto.CompactTextString(m) }
func (*ReqQueryProposalComment) ProtoMessage() {}
func (*ReqQueryProposalComment) Descriptor() ([]byte, []int) { return fileDescriptor4, []int{12} }
func (m *ReqQueryProposalComment) GetProposalID() string {
if m != nil {
return m.ProposalID
}
return ""
}
func (m *ReqQueryProposalComment) GetCount() int32 {
if m != nil {
return m.Count
}
return 0
}
func (m *ReqQueryProposalComment) GetDirection() int32 {
if m != nil {
return m.Direction
}
return 0
}
func (m *ReqQueryProposalComment) GetIndex() int64 {
if m != nil {
return m.Index
}
return 0
}
type RelationCmt struct {
RepCmtHash string `protobuf:"bytes,1,opt,name=repCmtHash" json:"repCmtHash,omitempty"`
Comment string `protobuf:"bytes,2,opt,name=comment" json:"comment,omitempty"`
}
func (m *RelationCmt) Reset() { *m = RelationCmt{} }
func (m *RelationCmt) String() string { return proto.CompactTextString(m) }
func (*RelationCmt) ProtoMessage() {}
func (*RelationCmt) Descriptor() ([]byte, []int) { return fileDescriptor4, []int{13} }
func (m *RelationCmt) GetRepCmtHash() string {
if m != nil {
return m.RepCmtHash
}
return ""
}
func (m *RelationCmt) GetComment() string {
if m != nil {
return m.Comment
}
return ""
}
type ReplyQueryProposalComment struct {
RltCmt []*RelationCmt `protobuf:"bytes,1,rep,name=rltCmt" json:"rltCmt,omitempty"`
}
func (m *ReplyQueryProposalComment) Reset() { *m = ReplyQueryProposalComment{} }
func (m *ReplyQueryProposalComment) String() string { return proto.CompactTextString(m) }
func (*ReplyQueryProposalComment) ProtoMessage() {}
func (*ReplyQueryProposalComment) Descriptor() ([]byte, []int) { return fileDescriptor4, []int{14} }
func (m *ReplyQueryProposalComment) GetRltCmt() []*RelationCmt {
if m != nil {
return m.RltCmt
}
return nil
}
func init() {
proto.RegisterType((*AutonomyProposalRule)(nil), "types.AutonomyProposalRule")
proto.RegisterType((*ProposalRule)(nil), "types.ProposalRule")
......@@ -403,48 +492,55 @@ func init() {
proto.RegisterType((*TransferFund)(nil), "types.TransferFund")
proto.RegisterType((*Comment)(nil), "types.Comment")
proto.RegisterType((*ReceiptProposalComment)(nil), "types.ReceiptProposalComment")
proto.RegisterType((*ReqQueryProposalComment)(nil), "types.ReqQueryProposalComment")
proto.RegisterType((*RelationCmt)(nil), "types.RelationCmt")
proto.RegisterType((*ReplyQueryProposalComment)(nil), "types.ReplyQueryProposalComment")
}
func init() { proto.RegisterFile("rule.proto", fileDescriptor4) }
var fileDescriptor4 = []byte{
// 594 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x4d, 0x6f, 0xd3, 0x40,
0x10, 0x95, 0xe3, 0xb8, 0xa9, 0x87, 0x52, 0xa5, 0xdb, 0x50, 0x59, 0x05, 0x21, 0xcb, 0x07, 0x14,
0x81, 0x14, 0xc4, 0x97, 0x2a, 0xb8, 0x41, 0x00, 0x81, 0xd4, 0x03, 0x2c, 0x15, 0x77, 0x63, 0x4f,
0x12, 0xab, 0xf6, 0xae, 0x59, 0xaf, 0xa3, 0xe6, 0xc0, 0x99, 0x1f, 0xcd, 0x05, 0xed, 0x87, 0x13,
0xbb, 0x89, 0x0a, 0xbd, 0xed, 0x9b, 0x7d, 0x3b, 0x3b, 0x33, 0xfb, 0xde, 0x02, 0x88, 0x3a, 0xc7,
0x49, 0x29, 0xb8, 0xe4, 0xc4, 0x93, 0xab, 0x12, 0xab, 0xd3, 0xbb, 0x79, 0xc2, 0x8b, 0x82, 0x33,
0x13, 0x8d, 0x7e, 0xf7, 0x60, 0xf4, 0xb6, 0x96, 0x9c, 0xf1, 0x62, 0xf5, 0x45, 0xf0, 0x92, 0x57,
0x71, 0x4e, 0xeb, 0x1c, 0xc9, 0x53, 0xd8, 0x2f, 0x05, 0x2f, 0xd5, 0x3a, 0x70, 0x42, 0x67, 0x7c,
0xe7, 0xf9, 0xf1, 0x44, 0x67, 0x98, 0xb4, 0x69, 0x74, 0x4d, 0x22, 0x4f, 0x60, 0x90, 0xd4, 0x42,
0xf3, 0x7b, 0x9a, 0x7f, 0x64, 0xf9, 0x2a, 0x34, 0xe5, 0x6c, 0x96, 0xcd, 0x69, 0xc3, 0x20, 0xcf,
0x00, 0x96, 0x5c, 0x22, 0xc5, 0xaa, 0xce, 0x65, 0xe0, 0x76, 0xf8, 0xdf, 0xd7, 0x1b, 0xb4, 0x45,
0x22, 0x27, 0xb0, 0x57, 0xc9, 0x58, 0xd6, 0x55, 0xd0, 0x0f, 0x9d, 0xb1, 0x47, 0x2d, 0x22, 0x01,
0x0c, 0xe2, 0x34, 0x15, 0x58, 0x55, 0x81, 0x17, 0x3a, 0x63, 0x9f, 0x36, 0x50, 0x9d, 0x58, 0x60,
0x36, 0x5f, 0xc8, 0x60, 0x2f, 0x74, 0xc6, 0x2e, 0xb5, 0x88, 0x8c, 0xc0, 0xcb, 0x58, 0x8a, 0x57,
0xc1, 0x40, 0x27, 0x32, 0x20, 0xfa, 0xe3, 0xc0, 0x41, 0x67, 0x02, 0x04, 0xfa, 0x2b, 0x8c, 0x85,
0xee, 0xde, 0xa3, 0x7a, 0xad, 0x8e, 0x16, 0x9c, 0xc9, 0x85, 0x6e, 0xd1, 0xa3, 0x06, 0x90, 0x21,
0xb8, 0x69, 0xbc, 0xd2, 0x6d, 0x78, 0x54, 0x2d, 0xd5, 0x30, 0xd4, 0xe8, 0xa7, 0xb3, 0xb9, 0xae,
0x76, 0xf7, 0x30, 0x2c, 0x83, 0x3c, 0x86, 0x61, 0x25, 0x63, 0x21, 0xdf, 0xe5, 0x3c, 0xb9, 0xfc,
0x64, 0x2a, 0xf6, 0x74, 0xc5, 0x5b, 0x71, 0xf2, 0x08, 0x0e, 0x91, 0xa5, 0x6d, 0xa6, 0xe9, 0xed,
0x5a, 0x94, 0x4c, 0x80, 0x08, 0x8c, 0xf3, 0x0f, 0x5d, 0xee, 0x40, 0x73, 0x77, 0xec, 0x44, 0x2f,
0x81, 0x50, 0x5c, 0xf2, 0x4b, 0xec, 0x8c, 0xe0, 0x21, 0x40, 0x69, 0xf1, 0xe7, 0xf7, 0x7a, 0x10,
0x3e, 0x6d, 0x45, 0xa2, 0x73, 0x18, 0xaa, 0xd7, 0xba, 0xcd, 0x19, 0xfd, 0x5e, 0x65, 0x29, 0xf8,
0xd2, 0xe8, 0x64, 0x9f, 0x36, 0x30, 0x3a, 0x83, 0x7b, 0x17, 0x28, 0x8a, 0x8c, 0xc5, 0xb7, 0x4b,
0x19, 0xfd, 0x82, 0x63, 0x8a, 0x09, 0x66, 0xa5, 0xbc, 0x26, 0xe1, 0x7e, 0x29, 0x70, 0x69, 0xe5,
0x7b, 0xdf, 0xbe, 0xc0, 0x2e, 0xb5, 0x53, 0x4d, 0x24, 0xaf, 0xb4, 0x84, 0x05, 0x32, 0x69, 0x25,
0x7c, 0xe3, 0x99, 0x86, 0x1b, 0x2d, 0xe0, 0xe8, 0x9c, 0x27, 0x71, 0xde, 0xb9, 0xfc, 0x6c, 0xcb,
0x3f, 0x37, 0x26, 0xdb, 0xf8, 0xe8, 0x14, 0xf6, 0x95, 0x43, 0x91, 0xc9, 0x2a, 0xe8, 0x85, 0xee,
0xd8, 0xa7, 0x6b, 0x1c, 0x5d, 0xc1, 0x88, 0xe2, 0xcf, 0xaf, 0x35, 0x8a, 0xae, 0x59, 0x37, 0xde,
0x70, 0x3a, 0xde, 0x18, 0x81, 0x97, 0xf0, 0xda, 0xb6, 0xe3, 0x51, 0x03, 0xc8, 0x03, 0xf0, 0xd3,
0x4c, 0x60, 0x22, 0x33, 0xce, 0xac, 0x68, 0x37, 0x81, 0x8d, 0x3b, 0xfa, 0x5a, 0x2c, 0xd6, 0x1d,
0xdf, 0xe0, 0x84, 0x62, 0x99, 0xaf, 0xb6, 0xef, 0x7e, 0x0d, 0x7e, 0x53, 0xbb, 0xba, 0xde, 0xfd,
0x57, 0xa7, 0x1b, 0x76, 0xf4, 0x06, 0x0e, 0x2e, 0x44, 0xcc, 0xaa, 0x19, 0x8a, 0x8f, 0x35, 0x4b,
0x55, 0x1b, 0x71, 0xa1, 0xeb, 0x75, 0x8c, 0x61, 0x0d, 0x52, 0x4e, 0x64, 0x5c, 0x1a, 0xbd, 0xf8,
0x54, 0xaf, 0xa3, 0x29, 0x0c, 0xa6, 0x66, 0x2c, 0xff, 0xa3, 0x38, 0x3b, 0x41, 0x9b, 0xa1, 0x81,
0xd1, 0x42, 0x75, 0xd5, 0x11, 0x4e, 0x93, 0x33, 0x04, 0x37, 0x29, 0xa4, 0x7d, 0xb9, 0x43, 0xdb,
0x8f, 0xdd, 0xa4, 0x6a, 0xab, 0xf5, 0xbb, 0xf4, 0x76, 0xff, 0x2e, 0x6e, 0xeb, 0x77, 0xf9, 0xb1,
0xa7, 0xbf, 0xdb, 0x17, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x1c, 0x0f, 0xac, 0x99, 0x92, 0x05,
0x00, 0x00,
// 671 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x4d, 0x6f, 0xd3, 0x4a,
0x14, 0x95, 0xe3, 0x38, 0xa9, 0x6f, 0xfb, 0xaa, 0x76, 0x9a, 0xd7, 0xe7, 0xd7, 0xf7, 0x54, 0x45,
0x5e, 0xa0, 0xa8, 0x48, 0x41, 0x7c, 0xa9, 0x82, 0x1d, 0x04, 0x68, 0x91, 0xba, 0x80, 0xa1, 0x62,
0x3f, 0xd8, 0xb7, 0x8d, 0x55, 0x7b, 0xc6, 0x8c, 0xc7, 0x51, 0xb3, 0x60, 0xc5, 0x82, 0x1f, 0xcd,
0x06, 0xcd, 0x78, 0xdc, 0xd8, 0x49, 0xd4, 0x52, 0x89, 0x9d, 0xcf, 0x9d, 0x33, 0x1f, 0xe7, 0xcc,
0xb9, 0x63, 0x00, 0x59, 0xa6, 0x38, 0xce, 0xa5, 0x50, 0x82, 0x78, 0x6a, 0x9e, 0x63, 0x71, 0xf0,
0x57, 0x1a, 0x89, 0x2c, 0x13, 0xbc, 0xaa, 0x86, 0x3f, 0x3a, 0x30, 0x78, 0x55, 0x2a, 0xc1, 0x45,
0x36, 0xff, 0x20, 0x45, 0x2e, 0x0a, 0x96, 0xd2, 0x32, 0x45, 0xf2, 0x08, 0x36, 0x72, 0x29, 0x72,
0xfd, 0x1d, 0x38, 0x43, 0x67, 0xb4, 0xf9, 0x64, 0x6f, 0x6c, 0x56, 0x18, 0x37, 0x69, 0xf4, 0x86,
0x44, 0x1e, 0x42, 0x3f, 0x2a, 0xa5, 0xe1, 0x77, 0x0c, 0x7f, 0xd7, 0xf2, 0x75, 0x69, 0x22, 0xf8,
0x45, 0x72, 0x49, 0x6b, 0x06, 0x79, 0x0c, 0x30, 0x13, 0x0a, 0x29, 0x16, 0x65, 0xaa, 0x02, 0xb7,
0xc5, 0xff, 0x7c, 0x33, 0x40, 0x1b, 0x24, 0xb2, 0x0f, 0xbd, 0x42, 0x31, 0x55, 0x16, 0x41, 0x77,
0xe8, 0x8c, 0x3c, 0x6a, 0x11, 0x09, 0xa0, 0xcf, 0xe2, 0x58, 0x62, 0x51, 0x04, 0xde, 0xd0, 0x19,
0xf9, 0xb4, 0x86, 0x7a, 0xc6, 0x14, 0x93, 0xcb, 0xa9, 0x0a, 0x7a, 0x43, 0x67, 0xe4, 0x52, 0x8b,
0xc8, 0x00, 0xbc, 0x84, 0xc7, 0x78, 0x1d, 0xf4, 0xcd, 0x42, 0x15, 0x08, 0x7f, 0x3a, 0xb0, 0xd5,
0x72, 0x80, 0x40, 0x77, 0x8e, 0x4c, 0x1a, 0xf5, 0x1e, 0x35, 0xdf, 0x7a, 0x6a, 0x26, 0xb8, 0x9a,
0x1a, 0x89, 0x1e, 0xad, 0x00, 0xd9, 0x01, 0x37, 0x66, 0x73, 0x23, 0xc3, 0xa3, 0xfa, 0x53, 0x9b,
0xa1, 0xad, 0x9f, 0x5c, 0x5c, 0x9a, 0xd3, 0xae, 0x37, 0xc3, 0x32, 0xc8, 0x11, 0xec, 0x14, 0x8a,
0x49, 0xf5, 0x3a, 0x15, 0xd1, 0xd5, 0x69, 0x75, 0x62, 0xcf, 0x9c, 0x78, 0xa5, 0x4e, 0x1e, 0xc0,
0x36, 0xf2, 0xb8, 0xc9, 0xac, 0xb4, 0x2d, 0x55, 0xc9, 0x18, 0x88, 0x44, 0x96, 0xbe, 0x6d, 0x73,
0xfb, 0x86, 0xbb, 0x66, 0x24, 0x7c, 0x06, 0x84, 0xe2, 0x4c, 0x5c, 0x61, 0xcb, 0x82, 0x43, 0x80,
0xdc, 0xe2, 0xf7, 0x6f, 0x8c, 0x11, 0x3e, 0x6d, 0x54, 0xc2, 0x33, 0xd8, 0xd1, 0xb7, 0x75, 0x9f,
0x39, 0xe6, 0xbe, 0xf2, 0x5c, 0x8a, 0x59, 0x95, 0x93, 0x0d, 0x5a, 0xc3, 0xf0, 0x18, 0xfe, 0x3e,
0x47, 0x99, 0x25, 0x9c, 0xdd, 0x6f, 0xc9, 0xf0, 0x1b, 0xec, 0x51, 0x8c, 0x30, 0xc9, 0xd5, 0x52,
0x84, 0xbb, 0xb9, 0xc4, 0x99, 0x8d, 0xef, 0x7f, 0xf6, 0x06, 0xd6, 0xa5, 0x9d, 0x1a, 0x22, 0x79,
0x6e, 0x22, 0x2c, 0x91, 0x2b, 0x1b, 0xe1, 0x5b, 0xe7, 0xd4, 0xdc, 0x70, 0x0a, 0xbb, 0x67, 0x22,
0x62, 0x69, 0x6b, 0xf3, 0xe3, 0x95, 0xfe, 0xb9, 0x75, 0xb1, 0x45, 0x1f, 0x1d, 0xc0, 0x86, 0xee,
0x50, 0xe4, 0xaa, 0x08, 0x3a, 0x43, 0x77, 0xe4, 0xd3, 0x1b, 0x1c, 0x5e, 0xc3, 0x80, 0xe2, 0xd7,
0x8f, 0x25, 0xca, 0x76, 0xb3, 0x2e, 0x7a, 0xc3, 0x69, 0xf5, 0xc6, 0x00, 0xbc, 0x48, 0x94, 0x56,
0x8e, 0x47, 0x2b, 0x40, 0xfe, 0x07, 0x3f, 0x4e, 0x24, 0x46, 0x2a, 0x11, 0xdc, 0x86, 0x76, 0x51,
0x58, 0x74, 0x47, 0xd7, 0x84, 0xc5, 0x76, 0xc7, 0x27, 0xd8, 0xa7, 0x98, 0xa7, 0xf3, 0xd5, 0xbd,
0x5f, 0x80, 0x5f, 0x9f, 0x5d, 0x6f, 0xef, 0xde, 0xa5, 0x74, 0xc1, 0x0e, 0x5f, 0xc2, 0xd6, 0xb9,
0x64, 0xbc, 0xb8, 0x40, 0xf9, 0xae, 0xe4, 0xb1, 0x96, 0xc1, 0x32, 0x73, 0x5e, 0xa7, 0x6a, 0xd8,
0x0a, 0xe9, 0x4e, 0xe4, 0x42, 0x55, 0x79, 0xf1, 0xa9, 0xf9, 0x0e, 0x23, 0xe8, 0x4f, 0x2a, 0x5b,
0xee, 0x4c, 0xdc, 0x21, 0x80, 0xc4, 0x7c, 0x92, 0xa9, 0x53, 0x56, 0x4c, 0xed, 0x22, 0x8d, 0x8a,
0x4e, 0xa4, 0x75, 0xd8, 0xb8, 0xe1, 0xd3, 0x1a, 0x86, 0x53, 0xad, 0xba, 0x15, 0xac, 0x7a, 0xcf,
0x21, 0xb8, 0x51, 0xa6, 0xec, 0xcd, 0x6e, 0x5b, 0xbd, 0x76, 0x90, 0xea, 0xa1, 0xc6, 0xeb, 0xd3,
0x59, 0xff, 0xfa, 0xb8, 0xcd, 0xd7, 0xe7, 0xbb, 0x03, 0xff, 0x2c, 0x5f, 0xed, 0xef, 0xea, 0xfb,
0x73, 0xb7, 0x7c, 0x02, 0x9b, 0x14, 0x53, 0xa6, 0x19, 0x93, 0x4c, 0x2d, 0x19, 0xe7, 0xdc, 0x66,
0x5c, 0xa7, 0x6d, 0xdc, 0x09, 0xfc, 0xbb, 0x1a, 0x97, 0x5a, 0xcf, 0x11, 0xf4, 0x64, 0xaa, 0x26,
0xc6, 0x3e, 0x1d, 0x17, 0x52, 0xbf, 0x8d, 0x8b, 0xad, 0xa9, 0x65, 0x7c, 0xe9, 0x99, 0xdf, 0xd4,
0xd3, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x28, 0x01, 0x71, 0xa1, 0xca, 0x06, 0x00, 0x00,
}
......@@ -62,6 +62,9 @@ func (a *AutonomyType) GetLogMap() map[int64]*types.LogInfo {
TyLogRvkPropRule: {Ty: reflect.TypeOf(ReceiptProposalRule{}), Name: "LogRvkPropRule"},
TyLogVotePropRule: {Ty: reflect.TypeOf(ReceiptProposalRule{}), Name: "LogVotePropRule"},
TyLogTmintPropRule: {Ty: reflect.TypeOf(ReceiptProposalRule{}), Name: "LogTmintPropRule"},
TyLogCommentProp: {Ty: reflect.TypeOf(ReceiptProposalComment{}), Name: "LogCommentProp"},
}
}
......@@ -90,5 +93,6 @@ func (a *AutonomyType) GetTypeMap() map[string]int32 {
"TmintPropRule": AutonomyActionTmintPropRule,
"Transfer": AutonomyActionTransfer,
"CommentProp": AutonomyActionCommentProp,
}
}
\ No newline at end of file
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