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

Omniledger sharding #1674

Open
wants to merge 62 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
95c5d1f
Added initial test.sh
qmng Sep 21, 2018
e9af07a
Added initial api, app, contracts, service files
qmng Sep 21, 2018
7003c63
Changed var names
qmng Sep 21, 2018
96a9660
Completed create sharding
qmng Oct 3, 2018
2e67003
Merge branch 'master' into omniledger_sharding
qmng Oct 3, 2018
2d5d82d
Added test file for service
qmng Oct 3, 2018
c511aee
Add checking function for CreateOmniLedger request and some fixes
qmng Oct 3, 2018
440ced4
Fix argument marshalling
qmng Oct 3, 2018
867256d
Update omniledger service test
qmng Oct 15, 2018
ab4e8ac
Fix oladmin test
qmng Oct 15, 2018
ae158c2
Fix service
qmng Oct 15, 2018
8e3e654
fix oladmin app
qmng Oct 15, 2018
866e859
Update api and oldadmin app
qmng Oct 15, 2018
e38d125
Try to move sharding from service to omniledger contract
qmng Oct 24, 2018
22cd77a
Add code for requesting new epoch
qmng Nov 11, 2018
cad03df
Reworked service test
qmng Nov 11, 2018
99288c2
Merge master and fixed compilation problems
qmng Nov 11, 2018
f73ad05
Fix service test
qmng Nov 12, 2018
5c378f5
Move changeRoster function to omniledger/lib
qmng Nov 13, 2018
301e976
Add api test
qmng Nov 13, 2018
89cb20c
Add lib test
qmng Nov 13, 2018
863686e
Fix api
qmng Nov 13, 2018
adcdef4
Add encode and decode duration functions and tests
qmng Nov 18, 2018
b8ed014
Fix code for new epoch
qmng Nov 18, 2018
deb4fd2
Remove unused import
qmng Nov 18, 2018
740aa0f
Fix service test
qmng Nov 19, 2018
c8f4c8b
Fix api to correctly return latest roster
qmng Nov 19, 2018
d7956a0
Fix test.sh
qmng Nov 21, 2018
cb56e92
Merge branch 'master' into omniledger_sharding
qmng Nov 21, 2018
2f938b1
Fix new epoch invoke in contract config
qmng Nov 21, 2018
ec6b0a2
Refactored ol contracts
qmng Nov 21, 2018
6579556
Fix ol api and service
qmng Nov 21, 2018
20a64f0
Fix ol tests
qmng Nov 21, 2018
2c66951
Refactor sharding function, move it and write tests
qmng Nov 22, 2018
a6aa49c
Fix decode errors in request new epoch
qmng Nov 25, 2018
caa412b
Fix ol service
qmng Nov 25, 2018
2277651
Fix signer counter problem in new epoch func
qmng Nov 25, 2018
8a1cbbf
Fix identity problem in shard ledger creation
qmng Nov 25, 2018
b6ca107
Refactor ol contracts to use new sharding func
qmng Nov 25, 2018
fb89a83
Add new CLI command: status
qmng Nov 25, 2018
ac3b3c3
Temporary changes
qmng Nov 26, 2018
d04a6ec
correctly creating new roster
ineiti Nov 27, 2018
4aee701
renamed var in sharding func
qmng Nov 27, 2018
998ae72
Changed var names in invoke new epoch
qmng Nov 27, 2018
7ed8297
Simplified change roster func
qmng Nov 27, 2018
4bba998
Correctly change shard client roster between new epoch instructions
qmng Nov 27, 2018
b68746c
Merge branch 'master' into omniledger_sharding
qmng Dec 2, 2018
cc23bf5
Added comments
qmng Dec 2, 2018
f83d532
Refactored duration enconding, decoding test
qmng Dec 2, 2018
9a36e3d
Merge remote-tracking branch 'origin/stop_viewchange' into temp2
qmng Dec 4, 2018
2b4b8dc
Add logging lines for debugging purposes
qmng Dec 5, 2018
78170f7
checking if we're responsible for chain
ineiti Dec 10, 2018
641db2d
Fix sharding in new epoch, now uses correct instance id instead of de…
qmng Dec 12, 2018
3ebfc08
Added comments
qmng Dec 16, 2018
8052855
Fix order problem in change roster
qmng Dec 20, 2018
e98f46e
Correct spelling mistake
qmng Jan 11, 2019
bbac8e6
Fix mistake in ChangeRoster, now order is changed immediately when al…
qmng Jan 11, 2019
177b8a5
Refactored ChangeRoster tests
qmng Jan 11, 2019
d1a6653
Correct text in tests
qmng Jan 11, 2019
d9ac44e
Fix error in change roster, condition was incorrect
qmng Jan 16, 2019
b75975d
Add missing file closing statement
qmng Jan 24, 2019
1ca8aba
Merge branch 'master' into omniledger_sharding
qmng Jan 24, 2019
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
35 changes: 35 additions & 0 deletions byzcoin/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/dedis/cothority/byzcoin/viewchange"
"github.com/dedis/cothority/darc"
"github.com/dedis/cothority/darc/expression"
lib "github.com/dedis/cothority/omniledger/lib"
"github.com/dedis/cothority/skipchain"
"github.com/dedis/onet"
"github.com/dedis/onet/log"
Expand Down Expand Up @@ -260,6 +261,40 @@ func (c *contractConfig) Invoke(rst ReadOnlyStateTrie, inst Instruction, coins [

sc, err = updateRosterScs(rst, darcID, req.Roster)
return
case "new_epoch":
// Decode instruction's arguments
shardIndBuf := inst.Invoke.Args.Search("shard-index")
shardInd, _ := binary.Varint(shardIndBuf)

proofBuf := inst.Invoke.Args.Search("epoch")
proof := &Proof{}
err = protobuf.DecodeWithConstructors(proofBuf, proof, network.DefaultConstructors(cothority.Suite))
if err != nil {
return
}

// Retrieve new roster in the IB instance from the proof
omniCC := &lib.ChainConfig{}
err = proof.VerifyAndDecode(cothority.Suite, "omniledgerepoch", omniCC)
if err != nil {
return
}
targetRoster := omniCC.ShardRosters[shardInd]

// Get the current instance to find the roster of the shard
conf := &ChainConfig{}
conf, err = loadConfigFromTrie(rst)
if err != nil {
return
}
oldRoster := conf.Roster

// Compute the roster resulting from the next change,
// then apply the changes
tempRoster := lib.ChangeRoster(oldRoster, targetRoster)
sc, err = updateRosterScs(rst, darcID, tempRoster)
log.Print("UPDATED SHARD", tempRoster.List, targetRoster.List)
return
default:
err = errors.New("invalid invoke command: " + inst.Invoke.Command)
return
Expand Down
317 changes: 317 additions & 0 deletions omniledger/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
package omniledger

import (
"encoding/binary"
"errors"

"github.com/dedis/cothority"
bc "github.com/dedis/cothority/byzcoin"
"github.com/dedis/cothority/darc"
lib "github.com/dedis/cothority/omniledger/lib"
"github.com/dedis/cothority/skipchain"
"github.com/dedis/onet"
"github.com/dedis/onet/log"
"github.com/dedis/protobuf"
)

// ServiceName is used for registration on the onet.
const ServiceName = "OmniLedger"

// Client is a structure to communicate with the OmniLedger service.
type Client struct {
*onet.Client
ID skipchain.SkipBlockID
Roster onet.Roster
}

// NewClient returns a new Client structure which can be used to communicate
// with an omnilegdger ledger.
// Input:
// - ID - The ID of the ledger we want to connect
// - Roster - A roster of nodes which will be contacted
// to communicate with the ledger
// Output:
// - A Client structure
func NewClient(ID skipchain.SkipBlockID, Roster onet.Roster) *Client {
return &Client{
Client: onet.NewClient(cothority.Suite, ServiceName),
ID: ID,
Roster: Roster,
}
}

// NewOmniLedger sets up a new OmniLedger ledger.
// Input:
// - req - A CreateOmniledger structure
// Output:
// - A client connected to the newly created OmniLedger
// - A CreateOmniledgerResponse struct in case of success, nil otherwise
// - An error if any, nil otherwise
func NewOmniLedger(req *CreateOmniLedger) (*Client, *CreateOmniLedgerResponse,
error) {
// Connect to an OL client
c := NewClient(nil, req.Roster)

// Create a new public/private key pair
owner := darc.NewSignerEd25519(nil, nil)

// Create genesis message for the IB
ibMsg, err := bc.DefaultGenesisMsg(req.Version,
&req.Roster, []string{"spawn:darc", "spawn:omniledgerepoch", "invoke:request_new_epoch"}, owner.Identity())
if err != nil {
return nil, nil, err
}

d := ibMsg.GenesisDarc

// Encode the instruction arguments
darcBuf, err := protobuf.Encode(&d)
if err != nil {
return nil, nil, err
}

scBuff := make([]byte, 8)
binary.PutVarint(scBuff, int64(req.ShardCount))

esBuff := lib.EncodeDuration(req.EpochSize)

rosterBuf, err := protobuf.Encode(&(req.Roster))
if err != nil {
return nil, nil, err
}

tsBuf := make([]byte, 8)
binary.BigEndian.PutUint64(tsBuf, uint64(req.Timestamp.Unix()))

// Define the spawn instruction
instr := bc.Instruction{
InstanceID: bc.NewInstanceID(d.GetBaseID()),
Spawn: &bc.Spawn{
ContractID: ContractOmniledgerEpochID,
Args: []bc.Argument{
bc.Argument{Name: "darc", Value: darcBuf},
bc.Argument{Name: "roster", Value: rosterBuf},
bc.Argument{Name: "shardCount", Value: scBuff},
bc.Argument{Name: "epochSize", Value: esBuff},
bc.Argument{Name: "timestamp", Value: tsBuf},
},
},
SignerCounter: []uint64{1},
}

// Create the transaction
spawnTx := &bc.ClientTransaction{
Instructions: bc.Instructions{instr},
}
spawnTx.SignWith(owner)

// Fill the request
req.IBGenesisMsg = ibMsg
req.SpawnTx = spawnTx
req.OwnerID = owner.Identity()

// Send the request and get the reply
reply := &CreateOmniLedgerResponse{}
reply.Owner = owner
err = c.SendProtobuf(req.Roster.List[0], req, reply)
if err != nil {
return nil, nil, err
}

c.ID = reply.IDSkipBlock.CalculateHash()

return c, reply, nil
}

// NewEpoch requests the start of a new epoch.
// Input:
// - req - A NewEpoch structure
// Output:
// - A NewEpochResponse struct in case of success, nil otherwise
// - An error if any, nil otherwise
func (c *Client) NewEpoch(req *NewEpoch) (*NewEpochResponse, error) {
// Connect to IB via client
ibClient := bc.NewClient(req.IBID, req.IBRoster)

// Fetch old roster from proof
oldCC := &lib.ChainConfig{}
gpr, err := ibClient.GetProof(req.OLInstanceID.Slice())
if err != nil {
return nil, err
}
err = gpr.Proof.VerifyAndDecode(cothority.Suite, ContractOmniledgerEpochID, oldCC)
oldRosters := oldCC.ShardRosters

// Get IB signer counter
signerCtrs, err := ibClient.GetSignerCounters(req.Owner.Identity().String())
if err != nil {
return nil, err
}
if len(signerCtrs.Counters) != 1 {
return nil, errors.New("incorrect signer counter length")
}

// Prepare and send request_new_epoch instruction to IB
tsBuf := make([]byte, 8)
binary.BigEndian.PutUint64(tsBuf, uint64(req.Timestamp.Unix()))

reqNewEpoch := bc.Instruction{
InstanceID: req.OLInstanceID,
Invoke: &bc.Invoke{
Command: "request_new_epoch",
Args: []bc.Argument{
bc.Argument{Name: "timestamp", Value: tsBuf},
},
},
SignerCounter: []uint64{signerCtrs.Counters[0] + 1},
}
tx := bc.ClientTransaction{
Instructions: []bc.Instruction{reqNewEpoch},
}
tx.SignWith(req.Owner)

req.ReqNewEpochTx = &tx

// Send tx via the OL service
reply := &NewEpochResponse{}
olClient := NewClient(req.IBID, req.IBRoster)
err = olClient.SendProtobuf(req.IBRoster.List[0], req, reply)
if err != nil {
return nil, err
}

// Get latest/new chain config from service response
latestCC := &lib.ChainConfig{}
err = reply.ReqNewEpochProof.VerifyAndDecode(cothority.Suite, ContractOmniledgerEpochID, latestCC)
if err != nil {
return nil, err
}

// Encode request new epoch proof, will be sent to shards
proofBuf, err := protobuf.Encode(reply.ReqNewEpochProof)
if err != nil {
return nil, err
}

// This double for loop is responsible for applying the shard roster changes.
// The outer loop iterates over the shards while the inner loop sends the changes for that shard, one by one.
for i := 0; i < len(latestCC.ShardRosters); i++ {
//log.Print("SENDING TO SHARD", i)
oldRoster := oldRosters[i]
newRoster := latestCC.ShardRosters[i]
changesCount := getRosterChangesCount(oldRoster, newRoster)
log.Print("OLD ROSTER:", oldRoster.List)
log.Print("NEW ROSTER:", newRoster.List)
log.Print("#CHANGES:", changesCount)

log.Printf("SHARD ID: %x", req.ShardIDs[i])
shardClient := bc.NewClient(req.ShardIDs[i], oldRoster)

shardIndBuff := make([]byte, 8)
binary.PutVarint(shardIndBuff, int64(i))

newEpoch := bc.Instruction{
InstanceID: bc.NewInstanceID(nil),
Invoke: &bc.Invoke{
Command: "new_epoch",
Args: []bc.Argument{
bc.Argument{Name: "epoch", Value: proofBuf},
bc.Argument{Name: "shard-index", Value: shardIndBuff},
bc.Argument{Name: "ib-ID", Value: req.IBID},
},
},
}

// Fetch counter of each shard
shardSignerCounter, err := shardClient.GetSignerCounters(req.Owner.Identity().String())
if err != nil {
return nil, err
}

tempRoster := oldRoster
for j := 0; j < changesCount; j++ {
log.Print("ACTUALLY SENDING IT", j, "TO", shardClient.Roster.List[0].Address.String())
// The signer counter must be incremented => tx must be updated and resigned
newEpoch.SignerCounter = []uint64{shardSignerCounter.Counters[0] + uint64(j+1)}
tx.Instructions[0] = newEpoch
tx.SignWith(req.Owner)

tempRoster = lib.ChangeRoster(tempRoster, newRoster)
log.Print(tempRoster.List, newRoster.List)
shardClient.AddTransactionAndWait(tx, 5)

// The client roster must be updated to ensure it will be able to contact a node in the current shard roster state

shardClient.Roster = tempRoster
}
}

clientReply := &NewEpochResponse{
IBRoster: *latestCC.Roster,
}

return clientReply, nil
}

func getRosterChangesCount(oldRoster, newRoster onet.Roster) int {
smallRoster := oldRoster.List
largeRoster := newRoster.List

if len(smallRoster) > len(largeRoster) {
temp := smallRoster
smallRoster = largeRoster
largeRoster = temp
}

changesCount := 0

smallSet := make(map[string]bool)
for _, node := range smallRoster {
id := node.ID.String()
smallSet[id] = true
}

largeSet := make(map[string]bool)
for _, node := range largeRoster {
id := node.ID.String()
largeSet[id] = true

if _, ok := smallSet[id]; !ok {
changesCount++
}
}

for _, node := range smallRoster {
id := node.ID.String()
if _, ok := largeSet[id]; !ok {
changesCount++
}
}

return changesCount
}

// GetStatus gets the current omniledger and shards rosters
// according to the omniledger.
// Input:
// - req - A GetStatus struct
// Output:
// - A GetStatusResponse in case of success, nil otherwise
// - An error if any, nil otherwise
func (c *Client) GetStatus(req *GetStatus) (*GetStatusResponse, error) {
ibClient := bc.NewClient(req.IBID, req.IBRoster)
gpr, err := ibClient.GetProof(req.OLInstanceID.Slice())
if err != nil {
return nil, err
}

cc := &lib.ChainConfig{}
err = gpr.Proof.VerifyAndDecode(cothority.Suite, ContractOmniledgerEpochID, cc)

reply := &GetStatusResponse{
IBRoster: *cc.Roster,
ShardRosters: cc.ShardRosters,
}

return reply, nil
}
Loading