feat: add a tool for submitting evidence of malicious voting (#1660)

* feat: add a tool for submitting evidence of malicious voting
This commit is contained in:
NathanBSC 2023-05-31 14:36:31 +08:00 committed by GitHub
parent 3d8753cae6
commit 86446edf55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 534 additions and 10 deletions

@ -0,0 +1,21 @@
## maliciousvote-submit
A tool for submitting the evidence of malicious voting
### Options
```
GLOBAL OPTIONS:
--sender value raw private key in hex format without 0x prefix; check permission on your own
--node value rpc endpoint, http,https,ws,wss,ipc are supported
--chainId value chainId, can get by eth_chainId (default: 0)
--evidence value params for submitFinalityViolationEvidence in json format; string
--help, -h show help
--version, -v print the version
```
### Evidence
can be extracted from logs generated by MaliciousVoteMonitor
### Example
```
./build/bin/maliciousvote-submit --chainId 714 --sender 59ba8068eb256d520179e903f43dacf6d8d57d72bd306e1bd603fdb812345678 --node ws://localhost:8545 --evidence "{\"VoteA\":{\"SrcNum\":6948,\"SrcHash\":\"dc58ff5dca8deefb7b03904ef2837e5f8b0e84ec147f021d4ff08343635540d3\",\"TarNum\":6949,\"TarHash\":\"24726f05534dc55c36ecc364951025abada0defa6d1b53bcb6b637f583b59996\",\"Sig\":\"9379a0626f962b828ed21fb34a6b6de034a23651c2e0c12b907293cf8f21d4fdd559e6f9c7f450a4243d33ad7aa5783d0e51e70979631d82819c254dfb130dfe924f057f7e2b4e64195fc7562f1cb0c45486c9cc3e6cc5679b4c0b5744bf33b5\"},\"VoteB\":{\"SrcNum\":6947,\"SrcHash\":\"24726f05534dc55c36ecc364951025abada0defa6d1b53bcb6b637f583b59996\",\"TarNum\":6950,\"TarHash\":\"6257f70ea6439b84d910595064a6e44e55ba0f2abc0c887346c420a60a5ef119\",\"Sig\":\"af9b500877d64277e80eea7c42b8d6ae5744d715625344ef6ddc66fa4e1dcb3e94568c79e018239641b724bacaa93046052d13f87b655d58b7afecf4e31036d5eca911e8c7436deea68c1e64ef7ed527ed25416039e4e7352f9b089cfb86481f\"},\"VoteAddr\":\"98b94137e4e2d4e628dcbc4a05d554f44950a7498040d3276d49c265708229127cd20e48c773cdc7a898b3bb572a17bf\"}"
```

@ -0,0 +1,72 @@
package main
import (
"encoding/json"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
func (s SlashIndicatorVoteData) ToWrapper() *types.SlashIndicatorVoteDataWrapper {
wrapper := &types.SlashIndicatorVoteDataWrapper{
SrcNum: s.SrcNum,
TarNum: s.TarNum,
}
if len(s.Sig) != types.BLSSignatureLength {
log.Crit("wrong length of sig", "wanted", types.BLSSignatureLength, "get", len(s.Sig))
}
wrapper.SrcHash = common.Bytes2Hex(s.SrcHash[:])
wrapper.TarHash = common.Bytes2Hex(s.TarHash[:])
wrapper.Sig = common.Bytes2Hex(s.Sig)
return wrapper
}
func (s *SlashIndicatorVoteData) FromWrapper(wrapper *types.SlashIndicatorVoteDataWrapper) {
if len(wrapper.SrcHash) != common.HashLength*2 {
log.Crit("wrong length of SrcHash", "wanted", common.HashLength*2, "get", len(wrapper.SrcHash))
}
if len(wrapper.TarHash) != common.HashLength*2 {
log.Crit("wrong length of TarHash", "wanted", common.HashLength*2, "get", len(wrapper.TarHash))
}
if len(wrapper.Sig) != types.BLSSignatureLength*2 {
log.Crit("wrong length of Sig", "wanted", types.BLSSignatureLength*2, "get", len(wrapper.Sig))
}
s.SrcNum = wrapper.SrcNum
s.TarNum = wrapper.TarNum
copy(s.SrcHash[:], common.Hex2Bytes(wrapper.SrcHash))
copy(s.TarHash[:], common.Hex2Bytes(wrapper.TarHash))
s.Sig = common.Hex2Bytes(wrapper.Sig)
}
func (s SlashIndicatorFinalityEvidence) MarshalJSON() ([]byte, error) {
wrapper := &types.SlashIndicatorFinalityEvidenceWrapper{
VoteA: *s.VoteA.ToWrapper(),
VoteB: *s.VoteB.ToWrapper(),
}
if len(s.VoteAddr) != types.BLSPublicKeyLength {
log.Crit("wrong length of VoteAddr", "wanted", types.BLSPublicKeyLength, "get", len(s.VoteAddr))
}
wrapper.VoteAddr = common.Bytes2Hex(s.VoteAddr)
return json.Marshal(wrapper)
}
func (s *SlashIndicatorFinalityEvidence) UnmarshalJSON(data []byte) error {
var wrapper = &types.SlashIndicatorFinalityEvidenceWrapper{}
if err := json.Unmarshal(data, wrapper); err != nil {
log.Crit("failed to Unmarshal", "error", err)
}
s.VoteA.FromWrapper(&wrapper.VoteA)
s.VoteB.FromWrapper(&wrapper.VoteB)
if len(wrapper.VoteAddr) != types.BLSPublicKeyLength*2 {
log.Crit("wrong length of VoteAddr", "wanted", types.BLSPublicKeyLength*2, "get", len(wrapper.VoteAddr))
}
s.VoteAddr = common.Hex2Bytes(wrapper.VoteAddr)
return nil
}

@ -0,0 +1,23 @@
package main
import (
"os"
"testing"
"github.com/ethereum/go-ethereum/log"
)
func TestSlashIndicatorFinalityEvidenceEncoding(t *testing.T) {
log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
evidence := `{"VoteA":{"SrcNum":1234,"SrcHash":"36068b819f244d27b5411d975f9ffd6d18c6084b50fb5595104ffd9de561a9f8","TarNum":1234,"TarHash":"36068b819f244d27b5411d975f9ffd6d18c6084b50fb5595104ffd9de561a9f8","Sig":"893682ebf26440a06daaff5695945ee2012146268f800c217bad9906ac64dc46996cd435e3e829529aa0445b52530070893682ebf26440a06daaff5695945ee2012146268f800c217bad9906ac64dc46996cd435e3e829529aa0445b52530070"},"VoteB":{"SrcNum":1234,"SrcHash":"36068b819f244d27b5411d975f9ffd6d18c6084b50fb5595104ffd9de561a9f8","TarNum":1234,"TarHash":"36068b819f244d27b5411d975f9ffd6d18c6084b50fb5595104ffd9de561a9f8","Sig":"893682ebf26440a06daaff5695945ee2012146268f800c217bad9906ac64dc46996cd435e3e829529aa0445b52530070893682ebf26440a06daaff5695945ee2012146268f800c217bad9906ac64dc46996cd435e3e829529aa0445b52530070"},"VoteAddr":"893682ebf26440a06daaff5695945ee2012146268f800c217bad9906ac64dc46996cd435e3e829529aa0445b52530070"}`
slashIndicatorFinalityEvidence := &SlashIndicatorFinalityEvidence{}
if err := slashIndicatorFinalityEvidence.UnmarshalJSON([]byte(evidence)); err != nil {
log.Crit("SlashIndicatorFinalityEvidence UnmarshalJSON failed")
}
if output, err := slashIndicatorFinalityEvidence.MarshalJSON(); err != nil {
log.Crit("SlashIndicatorFinalityEvidence MarshalJSON failed")
} else if string(output) != evidence {
log.Crit("SlashIndicatorFinalityEvidence UnmarshalJSON MarshalJSON mismatch", "output", string(output), "evidence", evidence)
}
}

@ -0,0 +1,140 @@
// submit the evidence of malicious voting
package main
import (
"context"
"fmt"
"math/big"
"os"
"strings"
"time"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/systemcontracts"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/log"
"gopkg.in/urfave/cli.v1"
)
var (
// Git SHA1 commit hash of the release (set via linker flags)
gitCommit = ""
gitDate = ""
app *cli.App
senderFlag = cli.StringFlag{
Name: "sender",
Usage: "raw private key in hex format without 0x prefix; check permission on your own",
}
nodeFlag = cli.StringFlag{
Name: "node",
Usage: "rpc endpoint, http,https,ws,wss,ipc are supported",
}
chainIdFlag = cli.UintFlag{
Name: "chainId",
Usage: "chainId, can get by eth_chainId",
}
evidenceFlag = cli.StringFlag{
Name: "evidence",
Usage: "params for submitFinalityViolationEvidence in json format; string",
}
)
func init() {
app = flags.NewApp(gitCommit, gitDate, "a tool for submitting the evidence of malicious voting")
app.Flags = []cli.Flag{
senderFlag,
nodeFlag,
chainIdFlag,
evidenceFlag,
}
app.Action = submitMaliciousVotes
cli.CommandHelpTemplate = flags.AppHelpTemplate
}
func submitMaliciousVotes(c *cli.Context) {
// get sender
senderRawKey := c.GlobalString(senderFlag.Name)
if senderRawKey == "" {
log.Crit("no sender specified (--sender)")
}
sender, err := crypto.HexToECDSA(senderRawKey)
if err != nil {
log.Crit("get sender failed", "error", err)
} else {
log.Info("get sender success")
}
// connect to the given URL
nodeURL := c.GlobalString(nodeFlag.Name)
if nodeURL == "" {
log.Crit("no node specified (--node)")
}
client, err := ethclient.Dial(nodeURL)
if err != nil {
log.Crit("Error connecting to client", "nodeURL", nodeURL, "error", err)
} else {
// when nodeURL is type of http or https, err==nil not mean successfully connected
if !strings.HasPrefix(nodeURL, "http") {
log.Info("Successfully connected to client", "nodeURL", nodeURL)
}
}
// get chainId
chainId := c.GlobalUint(chainIdFlag.Name)
if chainId == 0 {
log.Crit("no chainId specified (--chainId)")
} else {
log.Info("get chainId success", "chainId", chainId)
}
// get evidence
evidenceJson := c.GlobalString(evidenceFlag.Name)
if evidenceJson == "" {
log.Crit("no evidence specified (--evidence)")
}
var evidence SlashIndicatorFinalityEvidence
if err = evidence.UnmarshalJSON([]byte(evidenceJson)); err != nil {
log.Crit("Error parsing evidence", "error", err)
} else {
log.Info("get evidence success")
}
ops, _ := bind.NewKeyedTransactorWithChainID(sender, big.NewInt(int64(chainId)))
//ops.GasLimit = 800000
slashIndicator, _ := NewSlashIndicator(common.HexToAddress(systemcontracts.SlashContract), client)
tx, err := slashIndicator.SubmitFinalityViolationEvidence(ops, evidence)
if err != nil {
log.Crit("submitMaliciousVotes:", "error", err)
}
var rc *types.Receipt
for i := 0; i < 180; i++ {
rc, err = client.TransactionReceipt(context.Background(), tx.Hash())
if err == nil && rc.Status != 0 {
log.Info("submitMaliciousVotes: submit evidence success", "receipt", rc)
break
}
if rc != nil && rc.Status == 0 {
log.Crit("submitMaliciousVotes: tx failed: ", "error", err, "receipt", rc)
}
time.Sleep(100 * time.Millisecond)
}
if rc == nil {
log.Crit("submitMaliciousVotes: submit evidence failed")
}
}
func main() {
log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

@ -0,0 +1,217 @@
// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package main
import (
"errors"
"math/big"
"strings"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
)
// SlashIndicatorFinalityEvidence is an auto generated low-level Go binding around an user-defined struct.
type SlashIndicatorFinalityEvidence struct {
VoteA SlashIndicatorVoteData
VoteB SlashIndicatorVoteData
VoteAddr []byte
}
// SlashIndicatorVoteData is an auto generated low-level Go binding around an user-defined struct.
type SlashIndicatorVoteData struct {
SrcNum *big.Int
SrcHash [32]byte
TarNum *big.Int
TarHash [32]byte
Sig []byte
}
// SlashIndicatorMetaData contains all meta data concerning the SlashIndicator contract.
var SlashIndicatorMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"srcNum\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"srcHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"tarNum\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"tarHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"sig\",\"type\":\"bytes\"}],\"internalType\":\"structSlashIndicator.VoteData\",\"name\":\"voteA\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"srcNum\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"srcHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"tarNum\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"tarHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"sig\",\"type\":\"bytes\"}],\"internalType\":\"structSlashIndicator.VoteData\",\"name\":\"voteB\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"voteAddr\",\"type\":\"bytes\"}],\"internalType\":\"structSlashIndicator.FinalityEvidence\",\"name\":\"_evidence\",\"type\":\"tuple\"}],\"name\":\"submitFinalityViolationEvidence\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
}
// SlashIndicatorABI is the input ABI used to generate the binding from.
// Deprecated: Use SlashIndicatorMetaData.ABI instead.
var SlashIndicatorABI = SlashIndicatorMetaData.ABI
// SlashIndicator is an auto generated Go binding around an Ethereum contract.
type SlashIndicator struct {
SlashIndicatorCaller // Read-only binding to the contract
SlashIndicatorTransactor // Write-only binding to the contract
SlashIndicatorFilterer // Log filterer for contract events
}
// SlashIndicatorCaller is an auto generated read-only Go binding around an Ethereum contract.
type SlashIndicatorCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// SlashIndicatorTransactor is an auto generated write-only Go binding around an Ethereum contract.
type SlashIndicatorTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// SlashIndicatorFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type SlashIndicatorFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// SlashIndicatorSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type SlashIndicatorSession struct {
Contract *SlashIndicator // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// SlashIndicatorCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type SlashIndicatorCallerSession struct {
Contract *SlashIndicatorCaller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// SlashIndicatorTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type SlashIndicatorTransactorSession struct {
Contract *SlashIndicatorTransactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// SlashIndicatorRaw is an auto generated low-level Go binding around an Ethereum contract.
type SlashIndicatorRaw struct {
Contract *SlashIndicator // Generic contract binding to access the raw methods on
}
// SlashIndicatorCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type SlashIndicatorCallerRaw struct {
Contract *SlashIndicatorCaller // Generic read-only contract binding to access the raw methods on
}
// SlashIndicatorTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type SlashIndicatorTransactorRaw struct {
Contract *SlashIndicatorTransactor // Generic write-only contract binding to access the raw methods on
}
// NewSlashIndicator creates a new instance of SlashIndicator, bound to a specific deployed contract.
func NewSlashIndicator(address common.Address, backend bind.ContractBackend) (*SlashIndicator, error) {
contract, err := bindSlashIndicator(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &SlashIndicator{SlashIndicatorCaller: SlashIndicatorCaller{contract: contract}, SlashIndicatorTransactor: SlashIndicatorTransactor{contract: contract}, SlashIndicatorFilterer: SlashIndicatorFilterer{contract: contract}}, nil
}
// NewSlashIndicatorCaller creates a new read-only instance of SlashIndicator, bound to a specific deployed contract.
func NewSlashIndicatorCaller(address common.Address, caller bind.ContractCaller) (*SlashIndicatorCaller, error) {
contract, err := bindSlashIndicator(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &SlashIndicatorCaller{contract: contract}, nil
}
// NewSlashIndicatorTransactor creates a new write-only instance of SlashIndicator, bound to a specific deployed contract.
func NewSlashIndicatorTransactor(address common.Address, transactor bind.ContractTransactor) (*SlashIndicatorTransactor, error) {
contract, err := bindSlashIndicator(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &SlashIndicatorTransactor{contract: contract}, nil
}
// NewSlashIndicatorFilterer creates a new log filterer instance of SlashIndicator, bound to a specific deployed contract.
func NewSlashIndicatorFilterer(address common.Address, filterer bind.ContractFilterer) (*SlashIndicatorFilterer, error) {
contract, err := bindSlashIndicator(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &SlashIndicatorFilterer{contract: contract}, nil
}
// bindSlashIndicator binds a generic wrapper to an already deployed contract.
func bindSlashIndicator(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(SlashIndicatorABI))
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_SlashIndicator *SlashIndicatorRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _SlashIndicator.Contract.SlashIndicatorCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_SlashIndicator *SlashIndicatorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _SlashIndicator.Contract.SlashIndicatorTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_SlashIndicator *SlashIndicatorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _SlashIndicator.Contract.SlashIndicatorTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_SlashIndicator *SlashIndicatorCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _SlashIndicator.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_SlashIndicator *SlashIndicatorTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _SlashIndicator.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_SlashIndicator *SlashIndicatorTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _SlashIndicator.Contract.contract.Transact(opts, method, params...)
}
// SubmitFinalityViolationEvidence is a paid mutator transaction binding the contract method 0xcc844b73.
//
// Solidity: function submitFinalityViolationEvidence(((uint256,bytes32,uint256,bytes32,bytes),(uint256,bytes32,uint256,bytes32,bytes),bytes) _evidence) returns()
func (_SlashIndicator *SlashIndicatorTransactor) SubmitFinalityViolationEvidence(opts *bind.TransactOpts, _evidence SlashIndicatorFinalityEvidence) (*types.Transaction, error) {
return _SlashIndicator.contract.Transact(opts, "submitFinalityViolationEvidence", _evidence)
}
// SubmitFinalityViolationEvidence is a paid mutator transaction binding the contract method 0xcc844b73.
//
// Solidity: function submitFinalityViolationEvidence(((uint256,bytes32,uint256,bytes32,bytes),(uint256,bytes32,uint256,bytes32,bytes),bytes) _evidence) returns()
func (_SlashIndicator *SlashIndicatorSession) SubmitFinalityViolationEvidence(_evidence SlashIndicatorFinalityEvidence) (*types.Transaction, error) {
return _SlashIndicator.Contract.SubmitFinalityViolationEvidence(&_SlashIndicator.TransactOpts, _evidence)
}
// SubmitFinalityViolationEvidence is a paid mutator transaction binding the contract method 0xcc844b73.
//
// Solidity: function submitFinalityViolationEvidence(((uint256,bytes32,uint256,bytes32,bytes),(uint256,bytes32,uint256,bytes32,bytes),bytes) _evidence) returns()
func (_SlashIndicator *SlashIndicatorTransactorSession) SubmitFinalityViolationEvidence(_evidence SlashIndicatorFinalityEvidence) (*types.Transaction, error) {
return _SlashIndicator.Contract.SubmitFinalityViolationEvidence(&_SlashIndicator.TransactOpts, _evidence)
}

@ -1,7 +1,8 @@
package monitor
import (
"github.com/ethereum/go-ethereum/common"
"encoding/json"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
@ -59,27 +60,37 @@ func (m *MaliciousVoteMonitor) ConflictDetect(newVote *types.VoteEnvelope, pendi
}
for ; blockNumber <= pendingBlockNumber+upperLimitOfVoteBlockNumber; blockNumber++ {
if voteDataBuffer.Contains(blockNumber) {
voteData, ok := voteDataBuffer.Get(blockNumber)
voteEnvelope, ok := voteDataBuffer.Get(blockNumber)
if !ok {
log.Error("Failed to get voteData info from LRU cache.")
continue
}
maliciousVote := false
if blockNumber == targetNumber {
log.Warn("violate rule1", "VoteAddress", common.Bytes2Hex(newVote.VoteAddress[:]), "voteExisted", voteData.(*types.VoteData), "newVote", newVote.Data)
violateRule1Counter.Inc(1)
// prepare message for slashing
return true
} else if (blockNumber < targetNumber && voteData.(*types.VoteData).SourceNumber > sourceNumber) ||
(blockNumber > targetNumber && voteData.(*types.VoteData).SourceNumber < sourceNumber) {
log.Warn("violate rule2", "VoteAddress", common.Bytes2Hex(newVote.VoteAddress[:]), "voteExisted", voteData.(*types.VoteData), "newVote", newVote.Data)
maliciousVote = true
} else if (blockNumber < targetNumber && voteEnvelope.(*types.VoteEnvelope).Data.SourceNumber > sourceNumber) ||
(blockNumber > targetNumber && voteEnvelope.(*types.VoteEnvelope).Data.SourceNumber < sourceNumber) {
violateRule2Counter.Inc(1)
// prepare message for slashing
maliciousVote = true
}
if maliciousVote {
evidence := types.NewSlashIndicatorFinalityEvidenceWrapper(voteEnvelope.(*types.VoteEnvelope), newVote)
if evidence != nil {
if evidenceJson, err := json.Marshal(evidence); err == nil {
log.Warn("MaliciousVote", "evidence", string(evidenceJson))
} else {
log.Warn("MaliciousVote, Marshal evidence failed")
}
} else {
log.Warn("MaliciousVote, construct evidence failed")
}
return true
}
}
}
// for simplicity, Just override even if the targetNumber has existed.
voteDataBuffer.Add(newVote.Data.TargetNumber, newVote.Data)
voteDataBuffer.Add(newVote.Data.TargetNumber, newVote)
return false
}

@ -1,6 +1,8 @@
package types
import (
"bytes"
"math/big"
"sync/atomic"
"github.com/pkg/errors"
@ -89,3 +91,41 @@ func (vote *VoteEnvelope) Verify() error {
}
return nil
}
type SlashIndicatorVoteDataWrapper struct {
SrcNum *big.Int
SrcHash string
TarNum *big.Int
TarHash string
Sig string
}
type SlashIndicatorFinalityEvidenceWrapper struct {
VoteA SlashIndicatorVoteDataWrapper
VoteB SlashIndicatorVoteDataWrapper
VoteAddr string
}
func NewSlashIndicatorFinalityEvidenceWrapper(vote1, vote2 *VoteEnvelope) *SlashIndicatorFinalityEvidenceWrapper {
if !bytes.Equal(vote1.VoteAddress[:], vote1.VoteAddress[:]) ||
vote1.Data == nil || vote2.Data == nil {
return nil
}
return &SlashIndicatorFinalityEvidenceWrapper{
VoteA: SlashIndicatorVoteDataWrapper{
SrcNum: big.NewInt(int64(vote1.Data.SourceNumber)),
SrcHash: common.Bytes2Hex(vote1.Data.SourceHash[:]),
TarNum: big.NewInt(int64(vote1.Data.TargetNumber)),
TarHash: common.Bytes2Hex(vote1.Data.TargetHash[:]),
Sig: common.Bytes2Hex(vote1.Signature[:]),
},
VoteB: SlashIndicatorVoteDataWrapper{
SrcNum: big.NewInt(int64(vote2.Data.SourceNumber)),
SrcHash: common.Bytes2Hex(vote2.Data.SourceHash[:]),
TarNum: big.NewInt(int64(vote2.Data.TargetNumber)),
TarHash: common.Bytes2Hex(vote2.Data.TargetHash[:]),
Sig: common.Bytes2Hex(vote2.Signature[:]),
},
VoteAddr: common.Bytes2Hex(vote1.VoteAddress[:]),
}
}