Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions cmd/maliciousvote-submit/README.md
Original file line number Diff line number Diff line change
@@ -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\"}"
```

72 changes: 72 additions & 0 deletions cmd/maliciousvote-submit/json_encoding_helper.go
Original file line number Diff line number Diff line change
@@ -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
}
23 changes: 23 additions & 0 deletions cmd/maliciousvote-submit/json_encoding_helper_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
140 changes: 140 additions & 0 deletions cmd/maliciousvote-submit/main.go
Original file line number Diff line number Diff line change
@@ -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)
}

}
Loading