-
Notifications
You must be signed in to change notification settings - Fork 375
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: std re-organization #2425
Closed
+426
−0
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// Package chain contains core types and their helper functions to deal with the | ||
// blockchain. | ||
package chain | ||
|
||
import ( | ||
iruntime "internal/runtime" | ||
) | ||
|
||
// Address represents a bech32-encoded blockchain address. | ||
type Address string | ||
|
||
func (a Address) IsValid() bool { | ||
// TODO: make stdlib crypto/bech32 | ||
_, _, ok := DecodeBech32(string(a)) | ||
return ok | ||
} | ||
|
||
func NewAddressFromImportPath(importPath string) { | ||
// TODO: find better name | ||
panic("not implemented") | ||
} | ||
|
||
// A Realm represents an on-chain entity which has an address. This is generally | ||
// an Externally Owned Account (ie. a "user") or a code realm, which also has an | ||
// associated import path. | ||
type Realm struct { | ||
address Address | ||
importPath string | ||
} | ||
|
||
func (r Realm) Address() Address { | ||
return r.addr | ||
} | ||
|
||
func (r Realm) ImportPath() string { | ||
return r.importPath | ||
} | ||
|
||
func (r Realm) IsUser() bool { | ||
return r.importPath == "" | ||
} | ||
|
||
// NewCodeRealm creates a new realm, representing a code realm with a published | ||
// import path, existing on-chain. | ||
func NewCodeRealm(importPath string) Realm { | ||
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. |
||
if !iruntime.IsTesting() && !iruntime.IsRuntime() { | ||
panic("realms may only be created in testing or by the runtime package") | ||
} | ||
return Realm{ | ||
address: NewAddressFromImportPath(importPath), | ||
importPath: importPath, | ||
} | ||
} | ||
|
||
// NewUserRealm creates a new realm, representing a regular user. | ||
func NewUserRealm(address Address) Realm { | ||
if !iruntime.IsTesting() && !iruntime.IsRuntime() { | ||
panic("realms may only be created in testing or by the runtime package") | ||
} | ||
return Realm{ | ||
address: address, | ||
} | ||
} | ||
|
||
// Banker is an interface to access the blockchain's native banker. | ||
type Banker interface { | ||
// Balance returns the balance in the bank for the given address, as a list | ||
// of coins. | ||
Balance(addr Address) (dst Coins) | ||
|
||
// Supply returns the circulating supply of the given denomination. | ||
Supply(denom string) int64 | ||
|
||
// Send sends the given Coins between the from and to addresses. | ||
Send(from, to Address, amt Coins) | ||
|
||
// Issue issues the coin with the given denomination | ||
Issue(addr Address, denom string, amount int64) | ||
Burn(addr Address, denom string, amount int64) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
package chain | ||
|
||
// Coin holds some amount of one currency. | ||
// A negative amount is invalid. | ||
type Coin struct { | ||
Denom string `json:"denom"` | ||
Amount int64 `json:"amount"` | ||
} | ||
|
||
// NewCoin returns a new coin with a denomination and amount | ||
func NewCoin(denom string, amount int64) Coin { | ||
return Coin{ | ||
Denom: denom, | ||
Amount: amount, | ||
} | ||
} | ||
|
||
// String provides a human-readable representation of a coin | ||
func (c Coin) String() string { | ||
return strconv.Itoa(int(c.Amount)) + c.Denom | ||
} | ||
|
||
// IsGTE returns true if they are the same type and the receiver is | ||
// an equal or greater value | ||
func (c Coin) IsGTE(other Coin) bool { | ||
mustMatchDenominations(c.Denom, other.Denom) | ||
|
||
return c.Amount >= other.Amount | ||
} | ||
|
||
// IsLT returns true if they are the same type and the receiver is | ||
// a smaller value | ||
func (c Coin) IsLT(other Coin) bool { | ||
mustMatchDenominations(c.Denom, other.Denom) | ||
|
||
return c.Amount < other.Amount | ||
} | ||
|
||
// IsEqual returns true if the two sets of Coins have the same value | ||
func (c Coin) IsEqual(other Coin) bool { | ||
mustMatchDenominations(c.Denom, other.Denom) | ||
|
||
return c.Amount == other.Amount | ||
} | ||
|
||
// Add adds amounts of two coins with same denom. | ||
// If the coins differ in denom then it panics. | ||
// An overflow or underflow panics. | ||
// An invalid result panics. | ||
func (c Coin) Add(other Coin) Coin { | ||
mustMatchDenominations(c.Denom, other.Denom) | ||
|
||
sum, ok := overflow.Add64(c.Amount, other.Amount) | ||
if !ok { | ||
panic("coin add overflow/underflow: " + strconv.Itoa(int(c.Amount)) + " +/- " + strconv.Itoa(int(other.Amount))) | ||
} | ||
|
||
c.Amount = sum | ||
return c | ||
} | ||
|
||
// Sub subtracts amounts of two coins with same denom. | ||
// If the coins differ in denom then it panics. | ||
// An overflow or underflow panics. | ||
// An invalid result panics. | ||
func (c Coin) Sub(other Coin) Coin { | ||
mustMatchDenominations(c.Denom, other.Denom) | ||
|
||
dff, ok := overflow.Sub64(c.Amount, other.Amount) | ||
if !ok { | ||
panic("coin sub overflow/underflow: " + strconv.Itoa(int(c.Amount)) + " +/- " + strconv.Itoa(int(other.Amount))) | ||
} | ||
c.Amount = dff | ||
|
||
return c | ||
} | ||
|
||
// IsPositive returns true if coin amount is positive. | ||
func (c Coin) IsPositive() bool { | ||
return c.Amount > 0 | ||
} | ||
|
||
// IsNegative returns true if the coin amount is negative and false otherwise. | ||
func (c Coin) IsNegative() bool { | ||
return c.Amount < 0 | ||
} | ||
|
||
// IsZero returns true if the amount of given coin is zero | ||
func (c Coin) IsZero() bool { | ||
return c.Amount == 0 | ||
} | ||
|
||
func mustMatchDenominations(denomA, denomB string) { | ||
if denomA != denomB { | ||
panic("incompatible coin denominations: " + denomA + ", " + denomB) | ||
} | ||
} | ||
|
||
// Coins is a set of Coin, one per currency | ||
type Coins []Coin | ||
|
||
// NewCoins returns a new set of Coins given one or more Coins | ||
// Consolidates any denom duplicates into one, keeping the properties of a mathematical set | ||
func NewCoins(coins ...Coin) Coins { | ||
coinMap := make(map[string]int64) | ||
|
||
for _, coin := range coins { | ||
if currentAmount, exists := coinMap[coin.Denom]; exists { | ||
var ok bool | ||
if coinMap[coin.Denom], ok = overflow.Add64(currentAmount, coin.Amount); !ok { | ||
panic("coin sub overflow/underflow: " + strconv.Itoa(int(currentAmount)) + " +/- " + strconv.Itoa(int(coin.Amount))) | ||
} | ||
} else { | ||
coinMap[coin.Denom] = coin.Amount | ||
} | ||
} | ||
|
||
var setCoins Coins | ||
for denom, amount := range coinMap { | ||
setCoins = append(setCoins, NewCoin(denom, amount)) | ||
} | ||
|
||
return setCoins | ||
} | ||
|
||
// String returns the string representation of Coins | ||
func (cz Coins) String() string { | ||
if len(cz) == 0 { | ||
return "" | ||
} | ||
|
||
res := "" | ||
for i, c := range cz { | ||
if i > 0 { | ||
res += "," | ||
} | ||
res += c.String() | ||
} | ||
|
||
return res | ||
} | ||
|
||
// AmountOf returns the amount of a specific coin from the Coins set | ||
func (cz Coins) AmountOf(denom string) int64 { | ||
for _, c := range cz { | ||
if c.Denom == denom { | ||
return c.Amount | ||
} | ||
} | ||
|
||
return 0 | ||
} | ||
|
||
// Add adds a Coin to the Coins set | ||
func (cz Coins) Add(b Coins) Coins { | ||
c := Coins{} | ||
for _, ac := range cz { | ||
bc := b.AmountOf(ac.Denom) | ||
ac.Amount += bc | ||
c = append(c, ac) | ||
} | ||
|
||
for _, bc := range b { | ||
cc := c.AmountOf(bc.Denom) | ||
if cc == 0 { | ||
c = append(c, bc) | ||
} | ||
} | ||
|
||
return c | ||
} | ||
|
||
// expandNative expands for usage within natively bound functions. | ||
func (cz Coins) expandNative() (denoms []string, amounts []int64) { | ||
denoms = make([]string, len(cz)) | ||
amounts = make([]int64, len(cz)) | ||
for i, coin := range cz { | ||
denoms[i] = coin.Denom | ||
amounts[i] = coin.Amount | ||
} | ||
|
||
return denoms, amounts | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package bech32 | ||
|
||
// ConvertBits converts a byte slice where each byte is encoding fromBits bits, | ||
// to a byte slice where each byte is encoding toBits bits. | ||
func ConvertBits(data []byte, fromBits, toBits uint8, pad bool) ([]byte, error) { | ||
if fromBits < 1 || fromBits > 8 || toBits < 1 || toBits > 8 { | ||
return nil, ErrInvalidBitGroups{} | ||
} | ||
|
||
// Determine the maximum size the resulting array can have after base | ||
// conversion, so that we can size it a single time. This might be off | ||
// by a byte depending on whether padding is used or not and if the input | ||
// data is a multiple of both fromBits and toBits, but we ignore that and | ||
// just size it to the maximum possible. | ||
maxSize := len(data)*int(fromBits)/int(toBits) + 1 | ||
|
||
// The final bytes, each byte encoding toBits bits. | ||
regrouped := make([]byte, 0, maxSize) | ||
|
||
// Keep track of the next byte we create and how many bits we have | ||
// added to it out of the toBits goal. | ||
nextByte := byte(0) | ||
filledBits := uint8(0) | ||
|
||
for _, b := range data { | ||
|
||
// Discard unused bits. | ||
b <<= 8 - fromBits | ||
|
||
// How many bits remaining to extract from the input data. | ||
remFromBits := fromBits | ||
for remFromBits > 0 { | ||
// How many bits remaining to be added to the next byte. | ||
remToBits := toBits - filledBits | ||
|
||
// The number of bytes to next extract is the minimum of | ||
// remFromBits and remToBits. | ||
toExtract := remFromBits | ||
if remToBits < toExtract { | ||
toExtract = remToBits | ||
} | ||
|
||
// Add the next bits to nextByte, shifting the already | ||
// added bits to the left. | ||
nextByte = (nextByte << toExtract) | (b >> (8 - toExtract)) | ||
|
||
// Discard the bits we just extracted and get ready for | ||
// next iteration. | ||
b <<= toExtract | ||
remFromBits -= toExtract | ||
filledBits += toExtract | ||
|
||
// If the nextByte is completely filled, we add it to | ||
// our regrouped bytes and start on the next byte. | ||
if filledBits == toBits { | ||
regrouped = append(regrouped, nextByte) | ||
filledBits = 0 | ||
nextByte = 0 | ||
} | ||
} | ||
} | ||
|
||
// We pad any unfinished group if specified. | ||
if pad && filledBits > 0 { | ||
nextByte <<= toBits - filledBits | ||
regrouped = append(regrouped, nextByte) | ||
filledBits = 0 | ||
nextByte = 0 | ||
} | ||
|
||
// Any incomplete group must be <= 4 bits, and all zeroes. | ||
if filledBits > 0 && (filledBits > 4 || nextByte != 0) { | ||
return nil, ErrInvalidIncompleteGroup{} | ||
} | ||
|
||
return regrouped, nil | ||
} | ||
|
||
func Encode(prefix string, data []byte) (string, error) { | ||
converted, err := ConvertBits(data, 8, 5, true) | ||
if err != nil { | ||
return "", errors.Wrap(err, "encoding bech32 failed") | ||
} | ||
return bech32.Encode(hrp, converted) | ||
} | ||
|
||
func Decode(s string) (prefix string, data []byte, err error) { | ||
hrp, data, err := bech32.DecodeNoLimit(bech) | ||
if err != nil { | ||
return "", nil, errors.Wrap(err, "decoding bech32 failed") | ||
} | ||
converted, err := ConvertBits(data, 5, 8, false) | ||
if err != nil { | ||
return "", nil, errors.Wrap(err, "decoding bech32 failed") | ||
} | ||
return hrp, converted, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// Package runtime contains some runtime information, which is used by packages | ||
// like "chain" which cannot import "runtime" to avoid circular dependencies. | ||
package runtime | ||
|
||
// IsTesting returns whether the caller's caller is a function within a | ||
// _test.gno or a _filetest.gno file. | ||
func IsTesting() bool // injected | ||
|
||
// IsRuntime returns whether the caller's caller is the "runtime" package. | ||
func IsRuntime() bool // injected |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
What's the problem with
PkgPath
?Edit: I think we should keep
pkgPath
overimportPath
. Because ultimately, the package path is an identifier which, yes, can be imported, but it's an identifier first, just as astd.Address
is an identifier for an account. This one is the identifier for the realm and its assets, and the cool thing is that it's importable. It's like we would say that Go's package path can be imported, so let's not name themImportPath
. But we can alsogo mod download
them, so let's not rename themgoModDownloadPath
either. I think we should be clear that the identifier is the unique identifier of a package, and thenimportPath
is currently the same 1-1, but maybe in the future, we'll support import aliases for versioning or to support, for instance,r/morgan/foo
andr/g1morganxxx/foo
to be the samepkgPath
but differentimportPath
.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.
I am for a different name that pkgpath, because it conflicts most of the time when explaining the topics, making it very confusing. possibly
URL
,URI
,ID
, justpath
, etc. in the end its a unique identifier for code, and it can be used to look up code or import code.