-
Notifications
You must be signed in to change notification settings - Fork 7
feat: color as first class citizen #160
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
Changes from all commits
4cc77bd
2e331d8
8573d13
538bc72
b7a85ff
a645069
6f2030c
b19ba60
05b7a74
135270f
7537f3c
3cd4c05
8d256e9
922b2f2
479b8b2
042da10
e4d8a5c
c50e3c1
52682e7
0b23746
171f11a
125882b
f14aa09
6a42661
f5a5dea
0584049
5183658
b18cbe6
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 |
|---|---|---|
|
|
@@ -11,7 +11,6 @@ import ( | |
| "github.com/formancehq/numscript/internal/interpreter" | ||
| "github.com/formancehq/numscript/internal/parser" | ||
| "github.com/formancehq/numscript/internal/specs_format" | ||
| "github.com/formancehq/numscript/internal/utils" | ||
| "github.com/spf13/cobra" | ||
| ) | ||
|
|
||
|
|
@@ -114,10 +113,12 @@ func makeSpecsFile( | |
| defaultBalance *big.Int, | ||
| ) (specs_format.Specs, error) { | ||
|
|
||
| store := TestInitStore{ | ||
| store := &TestInitStore{ | ||
| DefaultBalance: defaultBalance, | ||
| Balances: make(interpreter.Balances), | ||
| Meta: make(interpreter.AccountsMetadata), | ||
| StaticStore: interpreter.StaticStore{ | ||
| Balances: interpreter.Balances{}, | ||
| Meta: make(interpreter.AccountsMetadata), | ||
| }, | ||
| } | ||
|
|
||
| res, iErr := interpreter.RunProgram( | ||
|
|
@@ -147,7 +148,7 @@ func makeSpecsFile( | |
| program, | ||
| vars, | ||
| featureFlags, | ||
| &missingFundsErr.Needed, | ||
| defaultBalance, | ||
| ) | ||
| } | ||
|
|
||
|
|
@@ -161,7 +162,7 @@ func makeSpecsFile( | |
|
|
||
| specs := specs_format.Specs{ | ||
| Schema: "https://raw.githubusercontent.com/formancehq/numscript/main/specs.schema.json", | ||
| Balances: store.Balances, | ||
| Balances: store.StaticStore.Balances, | ||
| Vars: vars, | ||
| FeatureFlags: featureFlags_, | ||
| TestCases: []specs_format.TestCase{ | ||
|
|
@@ -200,40 +201,52 @@ func runTestInitCmd(opts testInitArgs) error { | |
|
|
||
| type TestInitStore struct { | ||
| DefaultBalance *big.Int | ||
| Balances interpreter.Balances | ||
| Meta interpreter.AccountsMetadata | ||
| StaticStore interpreter.StaticStore | ||
| } | ||
|
|
||
| func (s TestInitStore) GetBalances(_ context.Context, q interpreter.BalanceQuery) (interpreter.Balances, error) { | ||
| outputBalance := interpreter.Balances{} | ||
| for queriedAccount, queriedCurrencies := range q { | ||
|
|
||
| for _, curr := range queriedCurrencies { | ||
| amt := utils.NestedMapGetOrPutDefault(s.Balances, queriedAccount, curr, func() *big.Int { | ||
| return new(big.Int).Set(s.DefaultBalance) | ||
| }) | ||
| func (s *TestInitStore) GetBalances(ctx context.Context, q interpreter.BalanceQuery) (interpreter.Balances, error) { | ||
| balances, err := s.StaticStore.GetBalances(ctx, q) | ||
|
Contributor
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. 🟠 [major] Materialize wildcard balance queries in test-init For asset-scaling sources the interpreter queries a wildcard asset like |
||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| outputAccountBalance := utils.MapGetOrPutDefault(outputBalance, queriedAccount, func() interpreter.AccountBalance { | ||
| return interpreter.AccountBalance{} | ||
| }) | ||
| type key struct{ account, asset, color string } | ||
|
|
||
| outputAccountBalance[curr] = new(big.Int).Set(amt) | ||
| } | ||
| // StaticStore.GetBalances materializes a zero-amount row for every queried | ||
| // (account, asset, color), so its output can't tell a known account from an | ||
| // unknown one. Track what we've actually funded ourselves instead. | ||
| stored := make(map[key]struct{}, len(s.StaticStore.Balances)) | ||
| for _, b := range s.StaticStore.Balances { | ||
| stored[key{b.Account, b.Asset, b.Color}] = struct{}{} | ||
| } | ||
|
|
||
| return outputBalance, nil | ||
| } | ||
| for i := range balances { | ||
|
Contributor
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. 🟠 [major] Seed defaults for catch-all balance queries For
Contributor
Author
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. true, but out of scope for this pr. It's a pre-existing bug, about an experimental, undocumented feature, and our pr has higher priority
Contributor
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. 🟠 [major] Seed defaults for wildcard test-init queries For
Contributor
Author
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. scaling is out of scope for the PR |
||
| b := &balances[i] | ||
| k := key{b.Account, b.Asset, b.Color} | ||
| if _, ok := stored[k]; ok { | ||
| continue | ||
| } | ||
|
|
||
| func (s TestInitStore) GetAccountsMetadata(c context.Context, q interpreter.MetadataQuery) (interpreter.AccountsMetadata, error) { | ||
| outputMeta := interpreter.AccountsMetadata{} | ||
| for queriedAccount, queriedCurrencies := range q { | ||
| for _, curr := range queriedCurrencies { | ||
| outputAccountMeta := utils.MapGetOrPutDefault(outputMeta, queriedAccount, func() interpreter.AccountMetadata { | ||
| return interpreter.AccountMetadata{} | ||
| }) | ||
| outputAccountMeta[curr] = "" | ||
| // Unknown (account, asset, color): fund it with the default balance, and | ||
| // remember it so later queries (and the generated specs file) see it. | ||
| amount := new(big.Int) | ||
| if s.DefaultBalance != nil { | ||
| amount.Set(s.DefaultBalance) | ||
| } | ||
| b.Amount = amount | ||
|
|
||
| s.StaticStore.Balances = append(s.StaticStore.Balances, interpreter.BalanceRow{ | ||
| Account: b.Account, | ||
| Asset: b.Asset, | ||
| Color: b.Color, | ||
| Amount: new(big.Int).Set(amount), | ||
| }) | ||
| stored[k] = struct{}{} | ||
| } | ||
|
|
||
| return outputMeta, nil | ||
| return balances, nil | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| } | ||
|
|
||
| func (s *TestInitStore) GetAccountsMetadata(c context.Context, q interpreter.MetadataQuery) (interpreter.AccountsMetadata, error) { | ||
|
Contributor
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. 🟠 [major] Preserve metadata stubs in test init When
Contributor
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. 🟠 [major] Preserve metadata stubbing in test-init For scripts that call
Contributor
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. 🟠 [major] Preserve metadata stubs in test-init When |
||
| return s.StaticStore.GetAccountsMetadata(c, q) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,18 +4,18 @@ import ( | |
| "fmt" | ||
| "math/big" | ||
| "slices" | ||
| "strconv" | ||
| "strings" | ||
|
|
||
| "github.com/formancehq/numscript/internal/utils" | ||
| ) | ||
|
|
||
| func assetToScaledAsset(asset string) string { | ||
| parts := strings.Split(asset, "/") | ||
| func assetToScaledAsset(asset Asset) Asset { | ||
| strAsset := string(asset) | ||
| parts := strings.Split(strAsset, "/") | ||
| if len(parts) == 1 { | ||
| return asset + "/*" | ||
| return Asset(strAsset + "/*") | ||
| } | ||
| return parts[0] + "/*" | ||
| return Asset(parts[0] + "/*") | ||
| } | ||
|
|
||
| func buildScaledAsset(baseAsset string, scale int64) string { | ||
|
|
@@ -25,25 +25,17 @@ func buildScaledAsset(baseAsset string, scale int64) string { | |
| return fmt.Sprintf("%s/%d", baseAsset, scale) | ||
| } | ||
|
|
||
| func getAssetScale(asset string) (string, int64) { | ||
| parts := strings.Split(asset, "/") | ||
| if len(parts) == 2 { | ||
| scale, err := strconv.ParseInt(parts[1], 10, 64) | ||
| if err == nil { | ||
| return parts[0], scale | ||
| } | ||
| // fallback if parsing fails | ||
| return parts[0], 0 | ||
| } | ||
| return asset, 0 | ||
| } | ||
|
|
||
| func getAssets(balance AccountBalance, baseAsset string) map[int64]*big.Int { | ||
| func getAssets(accountBalances []AccountBalance, baseAsset string) map[int64]*big.Int { | ||
| result := make(map[int64]*big.Int) | ||
| for asset, amount := range balance { | ||
| if strings.HasPrefix(asset, baseAsset) { | ||
| _, scale := getAssetScale(asset) | ||
| result[scale] = amount | ||
| for _, accBalance := range accountBalances { | ||
|
Contributor
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. 🟠 [major] Do not let scaling spend colored balances When a script has cached a colored balance for the same account/base asset and then uses |
||
| if accBalance.Color != "" { | ||
| // scaling converts only uncolored balances, and emits uncolored | ||
| // postings, so a colored balance must not be treated as available | ||
| continue | ||
| } | ||
| accBalanceAsset, scale := Asset(accBalance.Asset).GetBaseAndScale() | ||
| if accBalanceAsset == baseAsset { | ||
| result[scale] = new(big.Int).Set(accBalance.Amount) | ||
| } | ||
| } | ||
| return result | ||
|
|
||
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.
🟠 [major] Seed default balances for scaling wildcard queries
For
test-initon an asset-scaling script, the preload query asks for a wildcard asset likeUSD/*. Starting from an empty store,StaticStore.GetBalancesreturns no rows for that catch-all query, so this wrapper never creates a default balance and the interpreter later raisesInvalidUnboundedAddressInScalingAddressinstead of generating a specs file. The previous store funded the queried wildcard, so this regressestest-initfor scaling sources without preexisting balances.