Skip to content
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

Add EqualAddresses to OperationDescription #46

Merged
merged 4 commits into from
Jun 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ test:
${TEST_SCRIPT}

test-cover:
${TEST_SCRIPT} -coverprofile=c.out -covermode=count
${GOVERALLS_CMD} -coverprofile=c.out -repotoken ${COVERALLS_TOKEN}
if [ "${COVERALLS_TOKEN}" ]; then ${TEST_SCRIPT} -coverprofile=c.out -covermode=count; ${GOVERALLS_CMD} -coverprofile=c.out -repotoken ${COVERALLS_TOKEN}; fi

add-license:
${ADDLICENCE_SCRIPT} .
Expand Down
66 changes: 55 additions & 11 deletions parser/match_operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ type Descriptions struct {
// will error if all groups of operations aren't opposites.
OppositeAmounts [][]int

// EqualAddresses are specified using the operation indicies of
// OperationDescriptions to handle out of order matches. MatchOperations
// will error if all groups of operations addresses aren't equal.
EqualAddresses [][]int

// ErrUnmatched indicates that an error should be returned
// if all operations cannot be matched to a description.
ErrUnmatched bool
Expand Down Expand Up @@ -343,6 +348,33 @@ func oppositeAmounts(a *types.Operation, b *types.Operation) error {
return nil
}

// equalAddresses returns an error if a slice of operations do not have
// equal addresses.
func equalAddresses(ops []*types.Operation) error {
if len(ops) <= 1 {
return fmt.Errorf("cannot check equality of %d operations", len(ops))
}

base := ""

for _, op := range ops {
if op.Account == nil {
return fmt.Errorf("account is nil")
}

if len(base) == 0 {
base = op.Account.Address
continue
}

if base != op.Account.Address {
return fmt.Errorf("%s is not equal to %s", base, op.Account.Address)
}
}

return nil
}

func matchIndexValid(matches []*Match, index int) error {
if index >= len(matches) {
return fmt.Errorf(
Expand All @@ -360,27 +392,39 @@ func matchIndexValid(matches []*Match, index int) error {
return nil
}

// comparisonMatch ensures collections of *types.Operations
// have either equal or opposite amounts.
func comparisonMatch(
descriptions *Descriptions,
matches []*Match,
) error {
for _, amountMatch := range descriptions.EqualAmounts {
func checkOps(requests [][]int, matches []*Match, valid func([]*types.Operation) error) error {
for _, batch := range requests {
ops := []*types.Operation{}
for _, reqIndex := range amountMatch {
for _, reqIndex := range batch {
if err := matchIndexValid(matches, reqIndex); err != nil {
return fmt.Errorf("%w: equal amounts comparison error", err)
return fmt.Errorf("%w: index %d not valid", err, reqIndex)
}

ops = append(ops, matches[reqIndex].Operations...)
}

if err := equalAmounts(ops); err != nil {
return fmt.Errorf("%w: operations not equal", err)
if err := valid(ops); err != nil {
return fmt.Errorf("%w operations not valid", err)
}
}

return nil
}

// comparisonMatch ensures collections of *types.Operations
// have either equal or opposite amounts.
func comparisonMatch(
descriptions *Descriptions,
matches []*Match,
) error {
if err := checkOps(descriptions.EqualAmounts, matches, equalAmounts); err != nil {
return fmt.Errorf("%w: operation amounts not equal", err)
}

if err := checkOps(descriptions.EqualAddresses, matches, equalAddresses); err != nil {
return fmt.Errorf("%w: operation addresses not equal", err)
}

for _, amountMatch := range descriptions.OppositeAmounts {
if len(amountMatch) != oppositesLength { // cannot have opposites without exactly 2
return fmt.Errorf("cannot check opposites of %d operations", len(amountMatch))
Expand Down
109 changes: 107 additions & 2 deletions parser/match_operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,49 @@ func TestMatchOperations(t *testing.T) {
},
err: false,
},
"simple transfer (with missing account error)": {
operations: []*types.Operation{
{
Account: &types.AccountIdentifier{
Address: "addr2",
},
Amount: &types.Amount{
Value: "100",
},
},
{
Amount: &types.Amount{
Value: "-100",
},
},
},
descriptions: &Descriptions{
OppositeAmounts: [][]int{{0, 1}},
EqualAddresses: [][]int{{0, 1}},
OperationDescriptions: []*OperationDescription{
{
Account: &AccountDescription{
Exists: false,
},
Amount: &AmountDescription{
Exists: true,
Sign: NegativeAmountSign,
},
},
{
Account: &AccountDescription{
Exists: true,
},
Amount: &AmountDescription{
Exists: true,
Sign: PositiveAmountSign,
},
},
},
},
matches: nil,
err: true,
},
"simple transfer (check type)": {
operations: []*types.Operation{
{
Expand Down Expand Up @@ -498,7 +541,7 @@ func TestMatchOperations(t *testing.T) {
matches: nil,
err: true,
},
"simple transfer (with sender metadata)": {
"simple transfer (with sender metadata) and non-equal addresses": {
operations: []*types.Operation{
{
Account: &types.AccountIdentifier{
Expand Down Expand Up @@ -526,6 +569,68 @@ func TestMatchOperations(t *testing.T) {
},
descriptions: &Descriptions{
OppositeAmounts: [][]int{{0, 1}},
EqualAddresses: [][]int{{0, 1}},
OperationDescriptions: []*OperationDescription{
{
Account: &AccountDescription{
Exists: true,
SubAccountExists: true,
SubAccountAddress: "sub",
SubAccountMetadataKeys: []*MetadataDescription{
{
Key: "validator",
ValueKind: reflect.String,
},
},
},
Amount: &AmountDescription{
Exists: true,
Sign: NegativeAmountSign,
},
},
{
Account: &AccountDescription{
Exists: true,
},
Amount: &AmountDescription{
Exists: true,
Sign: PositiveAmountSign,
},
},
},
},
matches: nil,
err: true,
},
"simple transfer (with sender metadata)": {
operations: []*types.Operation{
{
Account: &types.AccountIdentifier{
Address: "addr1",
},
Amount: &types.Amount{
Value: "100",
},
},
{}, // extra op ignored
{
Account: &types.AccountIdentifier{
Address: "addr1",
SubAccount: &types.SubAccountIdentifier{
Address: "sub",
Metadata: map[string]interface{}{
"validator": "10",
},
},
},
Amount: &types.Amount{
Value: "-100",
},
},
},
descriptions: &Descriptions{
OppositeAmounts: [][]int{{0, 1}},
EqualAddresses: [][]int{{0, 1}},
OperationDescriptions: []*OperationDescription{
{
Account: &AccountDescription{
Expand Down Expand Up @@ -579,7 +684,7 @@ func TestMatchOperations(t *testing.T) {
Operations: []*types.Operation{
{
Account: &types.AccountIdentifier{
Address: "addr2",
Address: "addr1",
},
Amount: &types.Amount{
Value: "100",
Expand Down