-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
R4R: Store Accessor Types #4514
Changes from 26 commits
9375649
16eea68
f73215e
cf0b8f2
e25f652
2b17a39
dc84e1e
19d7155
6764bc4
8affaa7
a7898b4
3939bd4
f609249
7da69c1
600445a
94e2cdc
8d9e779
92d0fb4
5d913b7
699b0ba
97edd89
24e621a
971a642
fcad487
6de0ed5
6dc912f
63c6f3c
b6441c0
4d723ae
244d2c2
7bf4d57
7471bdf
04543f8
10d1c46
09e6600
6f5f633
4116bb7
8040c81
828badd
21bd8d0
9b3b120
abd9fec
a6dadef
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 |
---|---|---|
|
@@ -49,6 +49,12 @@ func (ctx CLIContext) QueryStore(key cmn.HexBytes, storeName string) ([]byte, in | |
return ctx.queryStore(key, storeName, "key") | ||
} | ||
|
||
// QueryABCI performs a query to a Tendermint node with the provide RequestQuery. | ||
// It returns the ResultQuery obtained from the query. | ||
func (ctx CLIContext) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { | ||
return ctx.queryABCI(req) | ||
} | ||
|
||
// QuerySubspace performs a query to a Tendermint node with the provided | ||
// store name and subspace. It returns key value pair and height of the query | ||
// upon success or an error if the query fails. | ||
|
@@ -72,21 +78,18 @@ func (ctx CLIContext) GetFromName() string { | |
return ctx.FromName | ||
} | ||
|
||
// query performs a query to a Tendermint node with the provided store name | ||
// and path. It returns the result and height of the query upon success | ||
// or an error if the query fails. | ||
func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height int64, err error) { | ||
func (ctx CLIContext) queryABCI(req abci.RequestQuery) (resp abci.ResponseQuery, err error) { | ||
node, err := ctx.GetNode() | ||
if err != nil { | ||
return res, height, err | ||
return resp, err | ||
} | ||
|
||
// When a client did not provide a query height, manually query for it so it can | ||
// be injected downstream into responses. | ||
if ctx.Height == 0 { | ||
status, err := node.Status() | ||
if err != nil { | ||
return res, height, err | ||
return resp, err | ||
} | ||
ctx = ctx.WithHeight(status.SyncInfo.LatestBlockHeight) | ||
} | ||
|
@@ -96,24 +99,40 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height i | |
Prove: !ctx.TrustNode, | ||
} | ||
|
||
result, err := node.ABCIQueryWithOptions(path, key, opts) | ||
result, err := node.ABCIQueryWithOptions(req.Path, req.Data, opts) | ||
if err != nil { | ||
return res, height, err | ||
return | ||
} | ||
|
||
resp := result.Response | ||
resp = result.Response | ||
if !resp.IsOK() { | ||
return res, height, errors.New(resp.Log) | ||
err = errors.New(resp.Log) | ||
return | ||
} | ||
|
||
// data from trusted node or subspace query doesn't need verification | ||
if ctx.TrustNode || !isQueryStoreWithProof(path) { | ||
return resp.Value, resp.Height, nil | ||
if ctx.TrustNode || !isQueryStoreWithProof(req.Path) { | ||
return resp, nil | ||
} | ||
|
||
err = ctx.verifyProof(path, resp) | ||
err = ctx.verifyProof(req.Path, resp) | ||
if err != nil { | ||
return res, height, err | ||
return | ||
} | ||
|
||
return | ||
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. naked return in func |
||
} | ||
|
||
// query performs a query to a Tendermint node with the provided store name | ||
// and path. It returns the result and height of the query upon success | ||
// or an error if the query fails. | ||
func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height int64, err error) { | ||
resp, err := ctx.queryABCI(abci.RequestQuery{ | ||
Path: path, | ||
Data: key, | ||
}) | ||
if err != nil { | ||
return | ||
} | ||
|
||
return resp.Value, resp.Height, nil | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package state | ||
|
||
// Boolean is a bool typed wrapper for Value. | ||
// | ||
// false <-> []byte{0x00} | ||
// true <-> []byte{0x01} | ||
type Boolean struct { | ||
alexanderbez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Value | ||
} | ||
|
||
func (v Value) Boolean() Boolean { | ||
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. All these functions should be moved to func (v Value) Type() Type { |
||
return Boolean{v} | ||
} | ||
|
||
// Get decodes and returns the stored boolean value if it exists. It will panic | ||
// if the value exists but is not boolean type. | ||
func (v Boolean) Get(ctx Context) (res bool) { | ||
v.Value.Get(ctx, &res) | ||
return | ||
} | ||
|
||
// GetSafe decodes and returns the stored boolean value. It will return an error | ||
// if the value does not exist or not boolean. | ||
func (v Boolean) GetSafe(ctx Context) (res bool, err error) { | ||
err = v.Value.GetSafe(ctx, &res) | ||
return | ||
} | ||
|
||
// Set encodes and sets the boolean argument to the state. | ||
func (v Boolean) Set(ctx Context, value bool) { | ||
v.Value.Set(ctx, value) | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,54 @@ | ||||||
package state | ||||||
|
||||||
// Enum is a byte typed wrapper for Value. | ||||||
// x <-> []byte{x} | ||||||
type Enum struct { | ||||||
alexanderbez marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
Value | ||||||
} | ||||||
|
||||||
func (v Value) Enum() Enum { | ||||||
return Enum{v} | ||||||
} | ||||||
|
||||||
// Get decodes and returns the stored byte value if it exists. It will panic if | ||||||
// the value exists but is not byte type. | ||||||
func (v Enum) Get(ctx Context) (res byte) { | ||||||
return v.Value.GetRaw(ctx)[0] | ||||||
} | ||||||
|
||||||
// GetSafe decodes and returns the stored byte value. It will returns an error | ||||||
// if the value does not exists or not byte. | ||||||
func (v Enum) GetSafe(ctx Context) (res byte, err error) { | ||||||
bz := v.Value.GetRaw(ctx) | ||||||
if bz == nil { | ||||||
return res, ErrEmptyValue() | ||||||
} | ||||||
return bz[0], nil // TODO: check length | ||||||
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. We should take care of this TODO if possible now. |
||||||
} | ||||||
|
||||||
// Set encodes and sets the byte argument to the state. | ||||||
func (v Enum) Set(ctx Context, value byte) { | ||||||
v.Value.SetRaw(ctx, []byte{value}) | ||||||
} | ||||||
|
||||||
// Incr increments the stored value, and returns the updated value. | ||||||
func (v Enum) Incr(ctx Context) (res byte) { | ||||||
res = v.Get(ctx) + 1 | ||||||
v.Set(ctx, res) | ||||||
return | ||||||
} | ||||||
|
||||||
// Transit checks whether the stored value matching with the "from" argument. | ||||||
// If it matches, it stores the "to" argument to the state and returns true. | ||||||
func (v Enum) Transit(ctx Context, from, to byte) bool { | ||||||
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. What is this used for? 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. I don't understand the name either 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 v.Get(ctx) != from { | ||||||
return false | ||||||
} | ||||||
v.Set(ctx, to) | ||||||
return true | ||||||
} | ||||||
|
||||||
func (v Enum) Query(ctx CLIContext) (res byte, proof *Proof, err error) { | ||||||
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.
type ABCIQuerier interface {
QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error)
}
Suggested change
|
||||||
value, proof, err := v.Value.QueryRaw(ctx) | ||||||
return value[0], proof, err | ||||||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,56 @@ | ||||||
package state | ||||||
|
||||||
import ( | ||||||
"fmt" | ||||||
) | ||||||
|
||||||
fedekunze marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
type GetSafeErrorType byte | ||||||
fedekunze marked this conversation as resolved.
Show resolved
Hide resolved
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.
Suggested change
|
||||||
|
||||||
const ( | ||||||
ErrTypeEmptyValue GetSafeErrorType = iota | ||||||
ErrTypeUnmarshal | ||||||
) | ||||||
|
||||||
func (ty GetSafeErrorType) Format(msg string) (res string) { | ||||||
switch ty { | ||||||
case ErrTypeEmptyValue: | ||||||
res = fmt.Sprintf("Empty Value found") | ||||||
case ErrTypeUnmarshal: | ||||||
res = fmt.Sprintf("Error while unmarshal") | ||||||
default: | ||||||
panic("Unknown error type") | ||||||
} | ||||||
|
||||||
if msg != "" { | ||||||
res = fmt.Sprintf("%s: %s", res, msg) | ||||||
} | ||||||
|
||||||
return | ||||||
} | ||||||
|
||||||
type GetSafeError 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.
Suggested change
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. I really don't understand why we need an error wrapper type like this one. What happens if |
||||||
ty GetSafeErrorType | ||||||
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.
Suggested change
|
||||||
inner error | ||||||
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.
Suggested change
|
||||||
} | ||||||
|
||||||
var _ error = (*GetSafeError)(nil) // TODO: sdk.Error | ||||||
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. ??? on TODO |
||||||
|
||||||
func (err *GetSafeError) Error() string { | ||||||
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. This probably doesn't need to operate on a pointer receiver. |
||||||
if err.inner == nil { | ||||||
return err.ty.Format("") | ||||||
} | ||||||
return err.ty.Format(err.inner.Error()) | ||||||
} | ||||||
|
||||||
func ErrEmptyValue() *GetSafeError { | ||||||
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. Why do we need to return pointers? |
||||||
return &GetSafeError{ | ||||||
ty: ErrTypeEmptyValue, | ||||||
} | ||||||
} | ||||||
|
||||||
func ErrUnmarshal(err error) *GetSafeError { | ||||||
return &GetSafeError{ | ||||||
ty: ErrTypeUnmarshal, | ||||||
inner: err, | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package state | ||
|
||
import ( | ||
"encoding/binary" | ||
"fmt" | ||
"strconv" | ||
) | ||
|
||
// IntEncoding is an enum type defining the integer serialization scheme. | ||
// All encoding schemes preserves order. | ||
type IntEncoding byte | ||
alexanderbez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const ( | ||
// Dec is human readable decimal encoding scheme. | ||
// Has fixed length of 20 bytes. | ||
Dec IntEncoding = iota | ||
// Hex is human readable hexadecimal encoding scheme | ||
// Has fixed length of 16 bytes. | ||
Hex | ||
// Bin is machine readable big endian encoding scheme | ||
// Has fixed length of 8 bytes | ||
Bin | ||
) | ||
|
||
// Indexer is a integer typed key wrapper for Mapping. Except for the type | ||
// checking, it does not alter the behaviour. All keys are encoded depending on | ||
// the IntEncoding. | ||
type Indexer struct { | ||
m Mapping | ||
mossid marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
enc IntEncoding | ||
mossid marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// NewIndexer constructs the Indexer with a predetermined prefix and IntEncoding. | ||
func NewIndexer(m Mapping, enc IntEncoding) Indexer { | ||
return Indexer{ | ||
m: m, | ||
enc: enc, | ||
} | ||
} | ||
|
||
// EncodeInt provides order preserving integer encoding function. | ||
func EncodeInt(index uint64, enc IntEncoding) (res []byte) { | ||
switch enc { | ||
case Dec: | ||
// return decimal number index, 20-length 0 padded | ||
return []byte(fmt.Sprintf("%020d", index)) | ||
|
||
case Hex: | ||
// return hexadecimal number index, 20-length 0 padded | ||
return []byte(fmt.Sprintf("%016x", index)) | ||
|
||
case Bin: | ||
// return bigendian encoded number index, 8-length | ||
res = make([]byte, 8) | ||
binary.BigEndian.PutUint64(res, index) | ||
return | ||
|
||
default: | ||
panic("invalid IntEncoding") | ||
} | ||
} | ||
|
||
// DecodeInt provides integer decoding function, inversion of EncodeInt. | ||
func DecodeInt(bz []byte, enc IntEncoding) (res uint64, err error) { | ||
switch enc { | ||
case Dec: | ||
return strconv.ParseUint(string(bz), 10, 64) | ||
|
||
case Hex: | ||
return strconv.ParseUint(string(bz), 16, 64) | ||
|
||
case Bin: | ||
return binary.BigEndian.Uint64(bz), nil | ||
|
||
default: | ||
panic("invalid IntEncoding") | ||
} | ||
} | ||
|
||
// Value returns the Value corresponding to the provided index. | ||
func (ix Indexer) Value(index uint64) Value { | ||
return ix.m.Value(EncodeInt(index, ix.enc)) | ||
} | ||
|
||
// Get decodes and sets the stored value to the pointer if it exists. It will | ||
// panic if the value exists but not unmarshalable. | ||
func (ix Indexer) Get(ctx Context, index uint64, ptr interface{}) { | ||
ix.Value(index).Get(ctx, ptr) | ||
} | ||
|
||
// GetSafe decodes and sets the stored value to the pointer. It will return an | ||
// error if the value does not exist or unmarshalable. | ||
func (ix Indexer) GetSafe(ctx Context, index uint64, ptr interface{}) error { | ||
return ix.Value(index).GetSafe(ctx, ptr) | ||
} | ||
|
||
// Set encodes and sets the argument to the state. | ||
func (ix Indexer) Set(ctx Context, index uint64, o interface{}) { | ||
ix.Value(index).Set(ctx, o) | ||
} | ||
|
||
// Has returns true if the stored value is not nil. | ||
func (ix Indexer) Has(ctx Context, index uint64) bool { | ||
return ix.Value(index).Exists(ctx) | ||
} | ||
|
||
// Delete removes the stored value. | ||
func (ix Indexer) Delete(ctx Context, index uint64) { | ||
ix.Value(index).Delete(ctx) | ||
} | ||
|
||
// Prefix returns a new Indexer with the updated prefix | ||
func (ix Indexer) Prefix(prefix []byte) Indexer { | ||
return Indexer{ | ||
m: ix.m.Prefix(prefix), | ||
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. ix.m undefined (type Indexer has no field or method m) (from |
||
|
||
enc: ix.enc, | ||
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. ix.enc undefined (type Indexer has no field or method enc) (from |
||
} | ||
} |
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.
unnecessary leading newline (from
whitespace
)