-
-
Notifications
You must be signed in to change notification settings - Fork 49
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: avoid tracking unbounded balances #1665
Changes from all commits
8152bd2
264318d
8f9d592
98eb728
df6dc3f
4329650
1c3e608
9e7d8b7
c4f59ef
9e71571
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,15 @@ type parseVisitor struct { | |
varIdx map[string]machine.Address | ||
// needBalances store for each account, the set of assets needed | ||
neededBalances map[machine.Address]map[machine.Address]struct{} | ||
|
||
// The sources accounts that aren't unbounded | ||
// that is, @world or sources that appear within a | ||
// '.. allowing unboundeed overdraft' clause | ||
writeLockAccounts map[machine.Address]struct{} | ||
|
||
// all the accounts that appear in either the destination | ||
// or in the balance() function | ||
readLockAccounts map[machine.Address]struct{} | ||
} | ||
|
||
// Allocates constants if it hasn't already been, | ||
|
@@ -580,6 +589,7 @@ func (p *parseVisitor) VisitVars(c *parser.VarListDeclContext) *CompileError { | |
Account: *accAddr, | ||
Asset: *assAddr, | ||
}) | ||
p.readLockAccounts[*accAddr] = struct{}{} | ||
if err != nil { | ||
return LogicError(c, err) | ||
} | ||
|
@@ -674,12 +684,14 @@ func CompileFull(input string) CompileArtifacts { | |
} | ||
|
||
visitor := parseVisitor{ | ||
errListener: errListener, | ||
instructions: make([]byte, 0), | ||
resources: make([]program.Resource, 0), | ||
varIdx: make(map[string]machine.Address), | ||
neededBalances: make(map[machine.Address]map[machine.Address]struct{}), | ||
sources: map[machine.Address]struct{}{}, | ||
errListener: errListener, | ||
instructions: make([]byte, 0), | ||
resources: make([]program.Resource, 0), | ||
varIdx: make(map[string]machine.Address), | ||
neededBalances: make(map[machine.Address]map[machine.Address]struct{}), | ||
sources: map[machine.Address]struct{}{}, | ||
writeLockAccounts: map[machine.Address]struct{}{}, | ||
readLockAccounts: map[machine.Address]struct{}{}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here the diff is weird because of golang's formatting, but the actual diff is this: + writeLockAccounts: map[machine.Address]struct{}{},
+ readLockAccounts: map[machine.Address]struct{}{},
} |
||
} | ||
|
||
err := visitor.VisitScript(tree) | ||
|
@@ -688,17 +700,24 @@ func CompileFull(input string) CompileArtifacts { | |
return artifacts | ||
} | ||
|
||
sources := make(machine.Addresses, 0) | ||
for address := range visitor.sources { | ||
sources = append(sources, address) | ||
readLockAccounts := make(machine.Addresses, 0) | ||
for address := range visitor.readLockAccounts { | ||
readLockAccounts = append(readLockAccounts, address) | ||
} | ||
sort.Stable(readLockAccounts) | ||
|
||
writeLockAccounts := make(machine.Addresses, 0) | ||
for address := range visitor.writeLockAccounts { | ||
writeLockAccounts = append(writeLockAccounts, address) | ||
} | ||
sort.Stable(sources) | ||
sort.Stable(writeLockAccounts) | ||
|
||
artifacts.Program = &program.Program{ | ||
Instructions: visitor.instructions, | ||
Resources: visitor.resources, | ||
NeededBalances: visitor.neededBalances, | ||
Sources: sources, | ||
Instructions: visitor.instructions, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actual diff: Instructions: visitor.instructions,
Resources: visitor.resources,
NeededBalances: visitor.neededBalances,
- Sources: sources,
+ReadLockAccounts: readLockAccounts,
+WriteLockAccounts: writeLockAccounts, |
||
Resources: visitor.resources, | ||
NeededBalances: visitor.neededBalances, | ||
ReadLockAccounts: readLockAccounts, | ||
WriteLockAccounts: writeLockAccounts, | ||
Comment on lines
+703
to
+720
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New logic for handling ReadLockAccounts and WriteLockAccounts The addition of logic to process Consider extracting the logic for processing func processLockAccounts(accounts map[machine.Address]struct{}) machine.Addresses {
result := make(machine.Addresses, 0, len(accounts))
for address := range accounts {
result = append(result, address)
}
sort.Stable(result)
return result
}
// Usage in CompileFull:
readLockAccounts := processLockAccounts(visitor.readLockAccounts)
writeLockAccounts := processLockAccounts(visitor.writeLockAccounts) This refactoring would reduce code duplication and make the |
||
} | ||
|
||
return artifacts | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ import ( | |
"encoding/binary" | ||
"fmt" | ||
"math/big" | ||
"slices" | ||
|
||
"github.com/formancehq/ledger/internal/machine" | ||
|
||
|
@@ -139,16 +140,16 @@ func (m *Machine) withdrawAlways(account machine.AccountAddress, mon machine.Mon | |
if accBalance, ok := m.Balances[account]; ok { | ||
if balance, ok := accBalance[mon.Asset]; ok { | ||
accBalance[mon.Asset] = balance.Sub(mon.Amount) | ||
return &machine.Funding{ | ||
Asset: mon.Asset, | ||
Parts: []machine.FundingPart{{ | ||
Account: account, | ||
Amount: mon.Amount, | ||
}}, | ||
}, nil | ||
} | ||
} | ||
return nil, fmt.Errorf("missing %v balance from %v", mon.Asset, account) | ||
|
||
return &machine.Funding{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of emitting the error when the account/asset balance is not found, return the required needed amount. |
||
Asset: mon.Asset, | ||
Parts: []machine.FundingPart{{ | ||
Account: account, | ||
Amount: mon.Amount, | ||
}}, | ||
}, nil | ||
} | ||
|
||
func (m *Machine) credit(account machine.AccountAddress, funding machine.Funding) { | ||
|
@@ -170,8 +171,16 @@ func (m *Machine) repay(funding machine.Funding) { | |
if part.Account == "world" { | ||
continue | ||
} | ||
balance := m.Balances[part.Account][funding.Asset] | ||
m.Balances[part.Account][funding.Asset] = balance.Add(part.Amount) | ||
accountBalance, ok := m.Balances[part.Account] | ||
if !ok { | ||
// no asset: the source has to be an unbounded source | ||
// which NEVER appears as bounded | ||
// this means we don't need to track it's balance | ||
continue | ||
} | ||
|
||
balance := accountBalance[funding.Asset] | ||
accountBalance[funding.Asset] = balance.Add(part.Amount) | ||
} | ||
} | ||
|
||
|
@@ -574,6 +583,9 @@ func (m *Machine) ResolveResources(ctx context.Context, store Store) ([]string, | |
if err != nil { | ||
return nil, nil, err | ||
} | ||
if val.GetType() == machine.TypeAccount { | ||
involvedAccountsMap[machine.Address(idx)] = string(val.(machine.AccountAddress)) | ||
} | ||
case program.VariableAccountBalance: | ||
acc, _ := m.getResource(res.Account) | ||
address := string((*acc).(machine.AccountAddress)) | ||
|
@@ -607,16 +619,19 @@ func (m *Machine) ResolveResources(ctx context.Context, store Store) ([]string, | |
m.Resources = append(m.Resources, val) | ||
} | ||
|
||
involvedAccounts := make([]string, 0) | ||
involvedSources := make([]string, 0) | ||
for _, accountAddress := range involvedAccountsMap { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
involvedAccounts = append(involvedAccounts, accountAddress) | ||
readLockAccounts := make([]string, 0) | ||
for _, accountAddress := range m.Program.ReadLockAccounts { | ||
readLockAccounts = append(readLockAccounts, involvedAccountsMap[accountAddress]) | ||
} | ||
for _, machineAddress := range m.Program.Sources { | ||
involvedSources = append(involvedSources, involvedAccountsMap[machineAddress]) | ||
|
||
writeLockAccounts := make([]string, 0) | ||
for _, machineAddress := range m.Program.WriteLockAccounts { | ||
writeLockAccounts = append(writeLockAccounts, involvedAccountsMap[machineAddress]) | ||
} | ||
|
||
return involvedAccounts, involvedSources, nil | ||
slices.Sort(readLockAccounts) | ||
slices.Sort(writeLockAccounts) | ||
return readLockAccounts, writeLockAccounts, nil | ||
} | ||
|
||
func (m *Machine) SetVarsFromJSON(vars map[string]string) error { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: this is the only usage of the
ResolveResources
method