Skip to content

Commit

Permalink
Merge pull request #1764 from dedis/java_skipblock_hash_1703
Browse files Browse the repository at this point in the history
Check block integrity when necessary
  • Loading branch information
jeffallen authored Mar 21, 2019
2 parents 68fb642 + e635f32 commit c5b18f2
Show file tree
Hide file tree
Showing 18 changed files with 729 additions and 51 deletions.
103 changes: 93 additions & 10 deletions byzcoin/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ package byzcoin
import (
"bytes"
"errors"
"fmt"
"math"
"time"

"go.dedis.ch/kyber/v3"
"go.dedis.ch/kyber/v3/sign/schnorr"

"go.dedis.ch/cothority/v3"
"go.dedis.ch/cothority/v3/darc"
"go.dedis.ch/cothority/v3/darc/expression"
"go.dedis.ch/cothority/v3/skipchain"
"go.dedis.ch/kyber/v3"
"go.dedis.ch/kyber/v3/sign/schnorr"
"go.dedis.ch/onet/v3"
"go.dedis.ch/onet/v3/log"
"go.dedis.ch/onet/v3/network"
Expand Down Expand Up @@ -56,14 +56,30 @@ func NewLedger(msg *CreateGenesisBlock, keep bool) (*Client, *CreateGenesisBlock
} else {
c = NewClient(nil, msg.Roster)
}
reply := &CreateGenesisBlockResponse{}
if err := c.SendProtobuf(msg.Roster.List[0], msg, reply); err != nil {

reply, err := newLedgerWithClient(msg, c)
if err != nil {
return nil, nil, err
}
c.ID = reply.Skipblock.CalculateHash()

c.ID = reply.Skipblock.Hash
return c, reply, nil
}

func newLedgerWithClient(msg *CreateGenesisBlock, c *Client) (*CreateGenesisBlockResponse, error) {
reply := &CreateGenesisBlockResponse{}
if err := c.SendProtobuf(msg.Roster.List[0], msg, reply); err != nil {
return nil, err
}

// checks if the returned genesis block has the same parameters
if err := verifyGenesisBlock(reply.Skipblock, msg); err != nil {
return nil, err
}

return reply, nil
}

// AddTransaction adds a transaction. It does not return any feedback
// on the transaction. Use GetProof to find out if the transaction
// was committed. The Client's Roster and ID should be initialized before
Expand Down Expand Up @@ -91,8 +107,8 @@ func (c *Client) AddTransactionAndWait(tx ClientTransaction, wait int) (*AddTxRe
}

// GetProof returns a proof for the key stored in the skipchain by sending a
// message to the node on index 0 of the roster. The proof can be verified with
// the genesis skipblock and can prove the existence or the absence of the key.
// message to the node on index 0 of the roster. The proof can prove the existence
// or the absence of the key. Note that the integrity of the proof is verified.
// The Client's Roster and ID should be initialized before calling this method
// (see NewClientFromConfig).
func (c *Client) GetProof(key []byte) (*GetProofResponse, error) {
Expand All @@ -105,6 +121,13 @@ func (c *Client) GetProof(key []byte) (*GetProofResponse, error) {
if err != nil {
return nil, err
}

// verify the integrity of the proof only
err = reply.Proof.Verify(c.ID)
if err != nil {
return nil, err
}

return reply, nil
}

Expand Down Expand Up @@ -251,13 +274,14 @@ func (c *Client) WaitProof(id InstanceID, interval time.Duration, value []byte)
// StreamTransactions sends a streaming request to the service. If successful,
// the handler will be called whenever a new response (a new block) is
// available. This function blocks, the streaming stops if the client or the
// service stops.
// service stops. Only the integrity of the new block is verified.
func (c *Client) StreamTransactions(handler func(StreamingResponse, error)) error {
req := StreamingRequest{
ID: c.ID,
}
conn, err := c.Stream(c.Roster.List[0], &req)
if err != nil {
handler(StreamingResponse{}, err)
return err
}
for {
Expand All @@ -266,7 +290,15 @@ func (c *Client) StreamTransactions(handler func(StreamingResponse, error)) erro
handler(StreamingResponse{}, err)
return nil
}
handler(resp, nil)

if resp.Block.CalculateHash().Equal(resp.Block.Hash) {
// send the block only if the integrity is correct
handler(resp, nil)
} else {
err := fmt.Errorf("got a corrupted block from %v", c.Roster.List[0])
log.Warn(err.Error())
handler(StreamingResponse{}, err)
}
}
}

Expand Down Expand Up @@ -417,3 +449,54 @@ func DefaultGenesisMsg(v Version, r *onet.Roster, rules []string, ids ...darc.Id
}
return &m, nil
}

func verifyGenesisBlock(actual *skipchain.SkipBlock, expected *CreateGenesisBlock) error {
if !actual.CalculateHash().Equal(actual.Hash) {
return errors.New("got a corrupted block")
}

// check the block is like the proposal
ok, err := actual.Roster.Equal(&expected.Roster)
if err != nil {
return err
}
if !ok {
return errors.New("wrong roster in genesis block")
}

darcID, err := extractDarcID(actual)
if err != nil {
return err
}

if !darcID.Equal(expected.GenesisDarc.GetID()) {
return errors.New("wrong darc spawned")
}

return nil
}

func extractDarcID(sb *skipchain.SkipBlock) (darc.ID, error) {
var data DataBody
err := protobuf.Decode(sb.Payload, &data)
if err != nil {
return nil, fmt.Errorf("fail to decode data: %v", err)
}

if len(data.TxResults) != 1 || len(data.TxResults[0].ClientTransaction.Instructions) != 1 {
return nil, errors.New("genesis darc tx should only have one instruction")
}

instr := data.TxResults[0].ClientTransaction.Instructions[0]
if instr.Spawn == nil {
return nil, errors.New("didn't get a spawn instruction")
}

var darc darc.Darc
err = protobuf.Decode(instr.Spawn.Args.Search("darc"), &darc)
if err != nil {
return nil, fmt.Errorf("fail to decode the darc: %v", err)
}

return darc.GetID(), nil
}
150 changes: 150 additions & 0 deletions byzcoin/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,98 @@ import (
"github.com/stretchr/testify/require"
"go.dedis.ch/cothority/v3"
"go.dedis.ch/cothority/v3/darc"
"go.dedis.ch/cothority/v3/skipchain"
"go.dedis.ch/onet/v3"
"go.dedis.ch/onet/v3/log"
"go.dedis.ch/onet/v3/network"
"go.dedis.ch/protobuf"
)

func init() {
// register a service for the test that will do nothing but reply with a chosen response
onet.RegisterNewServiceWithSuite(testServiceName, pairingSuite, newTestService)
}

func TestClient_NewLedgerCorrupted(t *testing.T) {
l := onet.NewTCPTest(cothority.Suite)
servers, roster, _ := l.GenTree(3, true)
defer l.CloseAll()

service := servers[0].Service(testServiceName).(*corruptedService)
signer := darc.NewSignerEd25519(nil, nil)
msg, err := DefaultGenesisMsg(CurrentVersion, roster, []string{"spawn:dummy"}, signer.Identity())
require.Nil(t, err)
c := &Client{
Client: onet.NewClient(cothority.Suite, testServiceName),
Roster: *roster,
}

sb := skipchain.NewSkipBlock()
service.CreateGenesisBlockResponse = &CreateGenesisBlockResponse{Skipblock: sb}

sb.Roster = &onet.Roster{ID: onet.RosterID{}}
sb.Hash = sb.CalculateHash()
_, err = newLedgerWithClient(msg, c)
require.Error(t, err)
require.Equal(t, "wrong roster in genesis block", err.Error())

sb.Roster = roster
sb.Payload = []byte{1, 2, 3}
sb.Hash = sb.CalculateHash()
_, err = newLedgerWithClient(msg, c)
require.Error(t, err)
require.Contains(t, err.Error(), "fail to decode data:")

sb.Payload = []byte{}
sb.Hash = sb.CalculateHash()
_, err = newLedgerWithClient(msg, c)
require.Error(t, err)
require.Equal(t, "genesis darc tx should only have one instruction", err.Error())

data := &DataBody{
TxResults: []TxResult{
TxResult{ClientTransaction: ClientTransaction{Instructions: []Instruction{Instruction{}}}},
},
}
sb.Payload, err = protobuf.Encode(data)
sb.Hash = sb.CalculateHash()
require.NoError(t, err)
_, err = newLedgerWithClient(msg, c)
require.Error(t, err)
require.Equal(t, "didn't get a spawn instruction", err.Error())

data.TxResults[0].ClientTransaction.Instructions[0].Spawn = &Spawn{
Args: []Argument{
Argument{
Name: "darc",
Value: []byte{1, 2, 3},
},
},
}
sb.Payload, err = protobuf.Encode(data)
sb.Hash = sb.CalculateHash()
require.NoError(t, err)
_, err = newLedgerWithClient(msg, c)
require.Error(t, err)
require.Contains(t, err.Error(), "fail to decode the darc:")

darcBytes, _ := protobuf.Encode(&darc.Darc{})
data.TxResults[0].ClientTransaction.Instructions[0].Spawn = &Spawn{
Args: []Argument{
Argument{
Name: "darc",
Value: darcBytes,
},
},
}
sb.Payload, err = protobuf.Encode(data)
sb.Hash = sb.CalculateHash()
require.NoError(t, err)
_, err = newLedgerWithClient(msg, c)
require.Error(t, err)
require.Equal(t, "wrong darc spawned", err.Error())
}

func TestClient_GetProof(t *testing.T) {
l := onet.NewTCPTest(cothority.Suite)
servers, roster, _ := l.GenTree(3, true)
Expand Down Expand Up @@ -64,6 +150,29 @@ func TestClient_GetProof(t *testing.T) {
require.Equal(t, value, v0)
}

func TestClient_GetProofCorrupted(t *testing.T) {
l := onet.NewTCPTest(cothority.Suite)
servers, roster, _ := l.GenTree(3, true)
defer l.CloseAll()

service := servers[0].Service(testServiceName).(*corruptedService)

c := &Client{
Client: onet.NewClient(cothority.Suite, testServiceName),
Roster: *roster,
}

sb := skipchain.NewSkipBlock()
sb.Data = []byte{1, 2, 3}
service.GetProofResponse = &GetProofResponse{
Proof: Proof{Latest: *sb},
}

_, err := c.GetProof([]byte{})
require.Error(t, err)
require.Contains(t, err.Error(), "Error while decoding field")
}

// Create a streaming client and add blocks in the background. The client
// should receive valid blocks.
func TestClient_Streaming(t *testing.T) {
Expand Down Expand Up @@ -159,3 +268,44 @@ func TestClient_Streaming(t *testing.T) {
require.Nil(t, err)
}
}

const testServiceName = "TestByzCoin"

type corruptedService struct {
*Service

// corrupted replies
GetProofResponse *GetProofResponse
CreateGenesisBlockResponse *CreateGenesisBlockResponse
}

func newTestService(c *onet.Context) (onet.Service, error) {
s := &Service{
ServiceProcessor: onet.NewServiceProcessor(c),
contracts: make(map[string]ContractFn),
txBuffer: newTxBuffer(),
storage: &bcStorage{},
darcToSc: make(map[string]skipchain.SkipBlockID),
stateChangeCache: newStateChangeCache(),
stateChangeStorage: newStateChangeStorage(c),
heartbeatsTimeout: make(chan string, 1),
closeLeaderMonitorChan: make(chan bool, 1),
heartbeats: newHeartbeats(),
viewChangeMan: newViewChangeManager(),
streamingMan: streamingManager{},
closed: true,
}

cs := &corruptedService{Service: s}
err := s.RegisterHandlers(cs.GetProof, cs.CreateGenesisBlock)

return cs, err
}

func (cs *corruptedService) GetProof(req *GetProof) (resp *GetProofResponse, err error) {
return cs.GetProofResponse, nil
}

func (cs *corruptedService) CreateGenesisBlock(req *CreateGenesisBlock) (*CreateGenesisBlockResponse, error) {
return cs.CreateGenesisBlockResponse, nil
}
20 changes: 7 additions & 13 deletions byzcoin/bcadmin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
Expand All @@ -15,27 +16,23 @@ import (
"time"

"github.com/BurntSushi/toml"
"go.dedis.ch/cothority/v3/byzcoin/contracts"
"go.dedis.ch/kyber/v3/suites"
"go.dedis.ch/kyber/v3/util/encoding"
"go.dedis.ch/protobuf"

"github.com/qantik/qrgo"
"go.dedis.ch/cothority/v3"
"go.dedis.ch/cothority/v3/byzcoin"
"go.dedis.ch/cothority/v3/byzcoin/bcadmin/lib"
"go.dedis.ch/cothority/v3/byzcoin/contracts"
"go.dedis.ch/cothority/v3/darc"
"go.dedis.ch/cothority/v3/darc/expression"
"go.dedis.ch/cothority/v3/skipchain"
"go.dedis.ch/kyber/v3/suites"
"go.dedis.ch/kyber/v3/util/encoding"
"go.dedis.ch/kyber/v3/util/random"
"go.dedis.ch/onet/v3"
"go.dedis.ch/onet/v3/app"
"go.dedis.ch/onet/v3/cfgpath"
"go.dedis.ch/onet/v3/log"
"go.dedis.ch/onet/v3/network"

"encoding/json"

"github.com/qantik/qrgo"
"go.dedis.ch/protobuf"
"gopkg.in/urfave/cli.v1"
)

Expand Down Expand Up @@ -326,10 +323,7 @@ func create(c *cli.Context) error {
}
req.BlockInterval = interval

cl := onet.NewClient(cothority.Suite, byzcoin.ServiceName)

var resp byzcoin.CreateGenesisBlockResponse
err = cl.SendProtobuf(r.List[0], req, &resp)
_, resp, err := byzcoin.NewLedger(req, false)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit c5b18f2

Please sign in to comment.