Skip to content

Commit

Permalink
message chain construction
Browse files Browse the repository at this point in the history
  • Loading branch information
vyzo committed Aug 4, 2020
1 parent e323fa7 commit 89d42b5
Showing 1 changed file with 145 additions and 6 deletions.
151 changes: 145 additions & 6 deletions chain/messagepool/selection.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package messagepool

import (
"slice"
"time"

"github.com/filecoin-project/go-address"
Expand All @@ -11,7 +12,6 @@ import (
)

type msgChain struct {
actor address.Address
msgs []*types.SignedMessage
gasReward uint64
gasLimit uint64
Expand Down Expand Up @@ -45,7 +45,7 @@ func (mp *MessagePool) selectMessages(ts *types.TipSet) []*types.SignedMessage {
}

// 2. Sort the chains
slice.Sort(chains, func(i, j int) {
slice.Sort(chains, func(i, j int) bool {
return chains[i].Before(chains[j])
})

Expand Down Expand Up @@ -83,7 +83,7 @@ tailLoop:
// trim
chains[last].Trim(gasLimit, al)

// push down
// push down if it hasn't been invalidated
if mc.valid {
for i := last; i < len(chains)-1; i++ {
if chains[i].Before(chains[i+1]) {
Expand All @@ -110,16 +110,155 @@ tailLoop:
continue tailLoop
}

// the merge loop ended after processing all the chains -- mark the end.
// the merge loop ended after processing all the chains and we probably still have gas to spare
// -- mark the end.
last = len(chains)
}

return result
}

func (mp *MessagePool) createMessageChains(actor address.Address, mset *msgSet, ts *types.TipSet) []*msgChain {
// TODO
return nil
// collect all messages

This comment has been minimized.

Copy link
@creativcoder

creativcoder Mar 22, 2021

Hey @vyzo, Hope you are doing great! Couldn't find a way to contact you on other places, so reaching out here. Currently, we are implementing the optimal msg selection algorithm in forest and I had a couple of questions related to the implementation and the invariants around the various steps as lotus is the reference implementation and there's quite less written about the optimal message selection in the spec. Kindly let me know, if it's possible to connect with you on the same and the medium (slack filecoin channel or email). Thanks :)

This comment has been minimized.

Copy link
@vyzo

vyzo Mar 22, 2021

Author Contributor

Hi there!

Email is probably best, you can reach me at vyzo at protocol dot ai; I would also include @Kubuxu kubuxu at protocol dot ai in the discussion as we worked on the optimal selection algorithm together.

It might also be of benefit to record the outcome of the discussion in a public medium (an issue in lotus will do) for the benefit of the community.

This comment has been minimized.

Copy link
@creativcoder

creativcoder Mar 23, 2021

Hey @vyzo Thank you for taking the time to help out. Yeah, totally agree on public discussion. Even better if we can make the outcome of the discussion go as a section in filecoin spec on optimal selection. Will email you and @Kubuxu with what I have understood so far and what remains unclear.

msgs := make([]*types.SignedMessage, 0, len(mset.msgs))
for _, m := range mset.msgs {
msgs = append(msgs, m)
}

// sort by nonce
slice.Sort(msgs, func(i, j int) bool {
return msgs[i].Nonce < msgs[j].Nonce
})

// sanity checks:
// - there can be no gaps in nonces, starting from the current actor nonce
// if there is a gap, drop messages after the gap, we can't include them
// - all messages must have minimum gas and the total gas for the candidate messages
// cannot exceed the block limit; drop all messages that exceed the limit
// - the total gasReward cannot exceed the actor's balance; drop all messages that exceed
// the balance
al := func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*types.Actor, error) {
return a
}

minGas := guessgas.MinGas
a, _ := mp.api.StateGetActor(actor, nil)
curNonce := a.Nonce
balance := a.Balance
gasLimit := 0
i := 0
rewards := make([]uint64, 0, len(msgs))
for i = 0; i < len(msgs); i++ {
m := msgs[i]
if m.Nonce != curNonce {
break
}
curNonce++

if m.GasLimit < minGas {
break
}

gasLimit += m.GasLimit
if gasLimit > build.BlockGasLimit {
break
}

gasUsed, _ := gasguess.GuessGasUsed(context.TODO(), types.EmptyTSK, m, al)
gasReward := gasUsed * m.GasPrice
if gasReward > balance {
break
}
balance -= gasReward
rewards = append(rewards, gasReward)
}

// check we have a sane set of messages to construct the chains
if i > 0 {
msgs = msgs[:i]
} else {
return nil
}

// ok, now we can construct the chains using the messages we have
// invariant: each chain has a bigger gasPerf than the next -- otherwise they can be merged
// and increase the gasPerf of the first chain
// We do this in two passes:
// - in the first pass we create chains that aggreagate messages with non-decreasing gasPerf
// - in the second pass we merge chains to maintain the invariant.
var chains []*msgChain
var curChain *msgChain

newChain := func(m *types.SignedMessage, i int) *msgChain {
chain := new(msgChain)
chain.msgs = []*types.SignedMessage{m}
chain.gasReward = rewards[i]
chain.gasLimit = m.GasLimit
chain.gasPerf = curChain.gasRewards * build.BlockGasLimit / curChain.GasLimit
chain.valid = true
return chain
}

// create the individual chains
for i, m := range msgs {
if curChain == nil {
curChain = newChain(m, i)
continue
}

gasReward = curChain.gasReward + rewards[i]
gasLimit = curChain.gasLimit + m.GasLimit
gasPerf = gasReward * build.BlockGasLimit / gasLimit

// try to add the message to the current chain -- if it decreases the gasPerf, then make a
// new chain
if gasPerf < curChain.gasPerf {
chains = append(chains, curChain)
curChain = newChain(m, i)
} else {
curChain.msgs = append(curChain.msgs, m)
curChain.gasReward = gasReward
curChain.gasLimit = gasLimit
curChain.gasPerf = gasPerf
}
}
chains = append(chains, curChain)

// merge chains to maintain the invariant
for {
merged := 0

for i := len(chains) - 1; i > 0; i-- {
if chains[i].gasPerf > chains[i-1].gasPerf {
chains[i-1].msgs = append(chains[i-1].msgs, chains[i].msgs...)
chains[i-1].gasReward += chains[i].gasReward
chains[i-1].gasLimit += chains[i].gasLimit
chains[i-1].gasperf = chains[i-1].gasReward * build.BlockGasLimit / chains[i-1].gasLimit
chains[i].valid = false
merged++
}
}

if merged == 0 {
break
}

// drop invalidated chains
newChains = make([]*msgChain, 0, len(chains)-merged)
for _, c := range chains {
if c.valid {
newChains = append(newChains, c)
}
}
chains = newChains
}

// link dependent chains
for i := 0; i < len(chains)-1; i++ {
chains[i].next = chains[i+1]
}

return chains
}

func (self *msgChain) Before(other *msgChain) bool {
Expand Down

0 comments on commit 89d42b5

Please sign in to comment.