This repository has been archived by the owner on Aug 3, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #24 from regen-network/alex/election_workflow
Election workflow
- Loading branch information
Showing
30 changed files
with
4,572 additions
and
659 deletions.
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
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,126 @@ | ||
package group | ||
|
||
import ( | ||
"bytes" | ||
"crypto/sha256" | ||
"encoding/hex" | ||
"encoding/json" | ||
"fmt" | ||
"regexp" | ||
"strings" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/types/errors" | ||
) | ||
|
||
var ( | ||
// AddressLength is the length of all addresses | ||
// You can modify it in init() before any addresses are calculated, | ||
// but it must not change during the lifetime of the kvstore | ||
AddressLength = 20 | ||
|
||
// it must have (?s) flags, otherwise it errors when last section contains 0x20 (newline) | ||
perm = regexp.MustCompile(`(?s)^([a-zA-Z0-9_\-]{3,8})/([a-zA-Z0-9_\-]{3,8})/(.+)$`) | ||
) | ||
|
||
// Condition is a specially formatted array, containing | ||
// information on who can authorize an action. | ||
// It is of the format: | ||
// | ||
// sprintf("%s/%s/%s", extension, type, data) | ||
type Condition []byte | ||
|
||
func NewCondition(ext, typ string, data []byte) Condition { | ||
pre := fmt.Sprintf("%s/%s/", ext, typ) | ||
return append([]byte(pre), data...) | ||
} | ||
|
||
// Parse will extract the sections from the Condition bytes | ||
// and verify it is properly formatted | ||
func (c Condition) Parse() (string, string, []byte, error) { | ||
chunks := perm.FindSubmatch(c) | ||
if len(chunks) == 0 { | ||
return "", "", nil, errors.Wrapf(ErrInvalid, "condition: %X", []byte(c)) | ||
|
||
} | ||
// returns [all, match1, match2, match3] | ||
return string(chunks[1]), string(chunks[2]), chunks[3], nil | ||
} | ||
|
||
// Address will convert a Condition into an Address | ||
func (c Condition) Address() sdk.AccAddress { | ||
return newAddress(c) | ||
} | ||
|
||
// Equals checks if two permissions are the same | ||
func (a Condition) Equals(b Condition) bool { | ||
return bytes.Equal(a, b) | ||
} | ||
|
||
// String returns a human readable string. | ||
// We keep the extension and type in ascii and | ||
// hex-encode the binary data | ||
func (c Condition) String() string { | ||
ext, typ, data, err := c.Parse() | ||
if err != nil { | ||
return fmt.Sprintf("Invalid Condition: %X", []byte(c)) | ||
} | ||
return fmt.Sprintf("%s/%s/%X", ext, typ, data) | ||
} | ||
|
||
// Validate returns an error if the Condition is not the proper format | ||
func (c Condition) Validate() error { | ||
if len(c) == 0 { | ||
return ErrEmpty | ||
} | ||
if !perm.Match(c) { | ||
return errors.Wrapf(ErrInvalid, "condition: %X", []byte(c)) | ||
} | ||
return nil | ||
} | ||
|
||
func (c Condition) MarshalJSON() ([]byte, error) { | ||
var serialized string | ||
if c != nil { | ||
serialized = c.String() | ||
} | ||
return json.Marshal(serialized) | ||
} | ||
|
||
func (c *Condition) UnmarshalJSON(raw []byte) error { | ||
var enc string | ||
if err := json.Unmarshal(raw, &enc); err != nil { | ||
return errors.Wrap(err, "cannot decode json") | ||
} | ||
return c.deserialize(enc) | ||
} | ||
|
||
// deserialize from human readable string. | ||
func (c *Condition) deserialize(source string) error { | ||
// No value zero the address. | ||
if len(source) == 0 { | ||
*c = nil | ||
return nil | ||
} | ||
|
||
args := strings.Split(source, "/") | ||
if len(args) != 3 { | ||
return errors.Wrap(ErrInvalid, "invalid condition format") | ||
} | ||
data, err := hex.DecodeString(args[2]) | ||
if err != nil { | ||
return errors.Wrapf(ErrInvalid, "malformed condition data: %s", err) | ||
} | ||
*c = NewCondition(args[0], args[1], data) | ||
return nil | ||
} | ||
|
||
// newAddress hashes and truncates into the proper size | ||
func newAddress(data []byte) sdk.AccAddress { | ||
if data == nil { | ||
return nil | ||
} | ||
// h := blake2b.Sum256(data) | ||
h := sha256.Sum256(data) | ||
return h[:sdk.AddrLen] | ||
} |
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,71 @@ | ||
package group_test | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/cosmos/cosmos-sdk/types/errors" | ||
"github.com/cosmos/modules/incubator/group" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestConditionUnmarshalJSON(t *testing.T) { | ||
cases := map[string]struct { | ||
json string | ||
wantErr *errors.Error | ||
wantCondition group.Condition | ||
}{ | ||
"default decoding": { | ||
json: `"foo/bar/636f6e646974696f6e64617461"`, | ||
wantCondition: group.NewCondition("foo", "bar", []byte("conditiondata")), | ||
}, | ||
"invalid condition format": { | ||
json: `"foo/636f6e646974696f6e64617461"`, | ||
wantErr: group.ErrInvalid, | ||
}, | ||
"invalid condition data": { | ||
json: `"foo/bar/zzzzz"`, | ||
wantErr: group.ErrInvalid, | ||
}, | ||
"zero address": { | ||
json: `""`, | ||
wantCondition: nil, | ||
}, | ||
} | ||
|
||
for testName, tc := range cases { | ||
t.Run(testName, func(t *testing.T) { | ||
var got group.Condition | ||
err := json.Unmarshal([]byte(tc.json), &got) | ||
if !tc.wantErr.Is(err) { | ||
t.Fatalf("got error: %+v", err) | ||
} | ||
if err == nil && !got.Equals(tc.wantCondition) { | ||
t.Fatalf("expected %q but got condition: %q", tc.wantCondition, got) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestConditionMarshalJSON(t *testing.T) { | ||
cases := map[string]struct { | ||
source group.Condition | ||
wantJson string | ||
}{ | ||
"cond encoding": { | ||
source: group.NewCondition("foo", "bar", []byte("conditiondata")), | ||
wantJson: `"foo/bar/636F6E646974696F6E64617461"`, | ||
}, | ||
"nil encoding": { | ||
source: nil, | ||
wantJson: `""`, | ||
}, | ||
} | ||
for testName, tc := range cases { | ||
t.Run(testName, func(t *testing.T) { | ||
got, err := json.Marshal(tc.source) | ||
assert.Nil(t, err) | ||
assert.Equal(t, tc.wantJson, string(got)) | ||
}) | ||
} | ||
} |
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
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
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
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
Oops, something went wrong.