<commit_before>package api

import (
	"golang.org/x/net/context"

	"chain/api/smartcontracts/orderbook"
	"chain/api/smartcontracts/voting"
	"chain/api/txbuilder"
	"chain/cos/bc"
	"chain/database/pg"
	"chain/errors"
	"chain/net/http/httpjson"
)

const votingRightsPageSize = 100

type globalFindOrder struct {
	OfferedAssetIDs []bc.AssetID `json:"offered_asset_ids"`
	PaymentAssetIDs []bc.AssetID `json:"payment_asset_ids"`
}

func findOrders(ctx context.Context, req globalFindOrder) ([]*orderbook.OpenOrder, error) {
	qvals := httpjson.Request(ctx).URL.Query()
	if status, ok := qvals["status"]; !ok || status[0] != "open" {
		// TODO(tessr): find closed orders
		return nil, errors.Wrap(httpjson.ErrBadRequest, "unimplemented: find all orders")
	}
	orders, err := orderbook.FindOpenOrders(ctx, req.OfferedAssetIDs, req.PaymentAssetIDs)
	if err != nil {
		return nil, errors.Wrap(err, "finding orders by offered and payment asset ids")
	}

	return orders, nil
}

func findAccountOrders(ctx context.Context, accountID string) ([]*orderbook.OpenOrder, error) {
	qvals := httpjson.Request(ctx).URL.Query()
	if status, ok := qvals["status"]; !ok || status[0] != "open" {
		// TODO(tessr): find closed orders
		return nil, errors.Wrap(httpjson.ErrBadRequest, "unimplemented: find all orders")
	}
	if aids, ok := qvals["asset_id"]; ok {
		var assetIDs []bc.AssetID
		for _, id := range aids {
			var assetID bc.AssetID
			err := assetID.UnmarshalText([]byte(id))
			if err != nil {
				return nil, errors.Wrap(httpjson.ErrBadRequest, "invalid assetID")
			}
			assetIDs = append(assetIDs, assetID)
		}
		orders, err := orderbook.FindOpenOrdersBySellerAndAsset(ctx, accountID, assetIDs)
		if err != nil {
			return nil, errors.Wrap(err, "finding orders by seller and asset")
		}
		return orders, nil
	}
	orders, err := orderbook.FindOpenOrdersBySeller(ctx, accountID)
	if err != nil {
		return nil, err
	}
	return orders, nil
}

func findAccountVotingRights(ctx context.Context, accountID string) (map[string]interface{}, error) {
	prev, limit, err := getPageData(ctx, votingRightsPageSize)
	if err != nil {
		return nil, err
	}

	rightsWithUTXOs, last, err := voting.FindRightsForAccount(ctx, accountID, prev, limit)
	if err != nil {
		return nil, errors.Wrap(err, "finding account voting rights")
	}

	rights := make([]map[string]interface{}, 0, len(rightsWithUTXOs))
	for _, r := range rightsWithUTXOs {
		var actionTypes []string
		if r.Outpoint.Hash == r.UTXO.Hash && r.Outpoint.Index == r.UTXO.Index {
			actionTypes = append(actionTypes, "votingright-authenticate", "votingright-transfer", "votingright-delegate")
		} else {
			actionTypes = append(actionTypes, "votingright-recall")
		}

		rightToken := map[string]interface{}{
			"asset_id":       r.AssetID,
			"action_types":   actionTypes,
			"transaction_id": r.UTXO.Hash,
			"index":          r.UTXO.Index,
		}
		rights = append(rights, rightToken)
	}
	return map[string]interface{}{
		"balances": rights,
		"last":     last,
	}, nil
}

// parseVotingBuildRequest parses `votingright` BuildRequest sources and
// destinations. Unlike other asset types, voting request inputs and
// outputs need data from each other in order to build the correct
// txbuilder.Reservers and txbuilder.Receivers.
//
// This function will pair the votingright sources and destinations up by
// asset ID, and use the information from both to construct the
// txbuilder.Sources and txbuilder.Destinations.
func parseVotingBuildRequest(ctx context.Context, sources []*Source, destinations []*Destination) (srcs []*txbuilder.Source, dsts []*txbuilder.Destination, err error) {
	var (
		srcsByAssetID = map[bc.AssetID]*Source{}
		dstsByAssetID = map[bc.AssetID]*Destination{}
	)
	// Pair voting rights up by asset id.
	for _, src := range sources {
		if src.AssetID == nil {
			return nil, nil, errors.WithDetail(ErrBadBuildRequest, "asset type unspecified")
		}
		if _, ok := srcsByAssetID[*src.AssetID]; ok {
			return nil, nil, errors.WithDetail(ErrBadBuildRequest, "voting right asset appears twice as source")
		}
		srcsByAssetID[*src.AssetID] = src
	}
	for _, dst := range destinations {
		if dst.AssetID == nil {
			return nil, nil, errors.WithDetail(ErrBadBuildRequest, "asset type unspecified")
		}
		if _, ok := dstsByAssetID[*dst.AssetID]; ok {
			return nil, nil, errors.WithDetail(ErrBadBuildRequest, "voting right asset appears twice as destination")
		}
		dstsByAssetID[*dst.AssetID] = dst
	}
	if len(sources) > len(destinations) {
		// Both the source and destination must be provided in the same build
		// request. This is unavoidable because:
		// - the output contract script requires knowledge of the input's chain of ownership
		// - the sigscript needs to provide the new contract parameters
		// The only exception is issuing new voting right tokens. Then there
		// will be more destinations than sources.
		return nil, nil, errors.WithDetailf(ErrBadBuildRequest,
			"voting right source and destinations must be provided in the same build request")
	}

	for assetID, dst := range dstsByAssetID {
		// Validate the destination.
		if dst.Amount != 1 {
			return nil, nil, errors.WithDetailf(ErrBadBuildRequest, "voting right amount can only be 1")
		}

		src, ok := srcsByAssetID[assetID]
		if !ok {
			// If there is no votingright source, then assume this is an attempt
			// to issue into a new asset into a voting right contract.
			if dst.AdminScript == nil {
				return nil, nil, errors.WithDetailf(ErrBadBuildRequest, "voting right issuance requires a voting system admin script")
			}
			holder, err := dst.buildAddress(ctx)
			if err != nil {
				return nil, nil, err
			}
			dsts = append(dsts, &txbuilder.Destination{
				AssetAmount: bc.AssetAmount{AssetID: assetID, Amount: 1},
				Metadata:    dst.Metadata,
				Receiver:    voting.RightIssuance(ctx, dst.AdminScript, holder),
			})
			continue
		}

		// Validate the source.
		if src.TxHash == nil {
			src.TxHash = src.TxHashAsID
		}
		if src.TxHash == nil || src.Index == nil {
			return nil, nil, errors.WithDetailf(ErrBadBuildRequest, "bad voting right source")
		}
		if src.Amount != 1 {
			return nil, nil, errors.WithDetailf(ErrBadBuildRequest, "voting right amount can only be 1")
		}
		out := bc.Outpoint{Hash: *src.TxHash, Index: *src.Index}<commit_msg>api: voting right amount should be omitted<commit_after>package api

import (
	"golang.org/x/net/context"

	"chain/api/smartcontracts/orderbook"
	"chain/api/smartcontracts/voting"
	"chain/api/txbuilder"
	"chain/cos/bc"
	"chain/database/pg"
	"chain/errors"
	"chain/net/http/httpjson"
)

const votingRightsPageSize = 100

type globalFindOrder struct {
	OfferedAssetIDs []bc.AssetID `json:"offered_asset_ids"`
	PaymentAssetIDs []bc.AssetID `json:"payment_asset_ids"`
}

func findOrders(ctx context.Context, req globalFindOrder) ([]*orderbook.OpenOrder, error) {
	qvals := httpjson.Request(ctx).URL.Query()
	if status, ok := qvals["status"]; !ok || status[0] != "open" {
		// TODO(tessr): find closed orders
		return nil, errors.Wrap(httpjson.ErrBadRequest, "unimplemented: find all orders")
	}
	orders, err := orderbook.FindOpenOrders(ctx, req.OfferedAssetIDs, req.PaymentAssetIDs)
	if err != nil {
		return nil, errors.Wrap(err, "finding orders by offered and payment asset ids")
	}

	return orders, nil
}

func findAccountOrders(ctx context.Context, accountID string) ([]*orderbook.OpenOrder, error) {
	qvals := httpjson.Request(ctx).URL.Query()
	if status, ok := qvals["status"]; !ok || status[0] != "open" {
		// TODO(tessr): find closed orders
		return nil, errors.Wrap(httpjson.ErrBadRequest, "unimplemented: find all orders")
	}
	if aids, ok := qvals["asset_id"]; ok {
		var assetIDs []bc.AssetID
		for _, id := range aids {
			var assetID bc.AssetID
			err := assetID.UnmarshalText([]byte(id))
			if err != nil {
				return nil, errors.Wrap(httpjson.ErrBadRequest, "invalid assetID")
			}
			assetIDs = append(assetIDs, assetID)
		}
		orders, err := orderbook.FindOpenOrdersBySellerAndAsset(ctx, accountID, assetIDs)
		if err != nil {
			return nil, errors.Wrap(err, "finding orders by seller and asset")
		}
		return orders, nil
	}
	orders, err := orderbook.FindOpenOrdersBySeller(ctx, accountID)
	if err != nil {
		return nil, err
	}
	return orders, nil
}

func findAccountVotingRights(ctx context.Context, accountID string) (map[string]interface{}, error) {
	prev, limit, err := getPageData(ctx, votingRightsPageSize)
	if err != nil {
		return nil, err
	}

	rightsWithUTXOs, last, err := voting.FindRightsForAccount(ctx, accountID, prev, limit)
	if err != nil {
		return nil, errors.Wrap(err, "finding account voting rights")
	}

	rights := make([]map[string]interface{}, 0, len(rightsWithUTXOs))
	for _, r := range rightsWithUTXOs {
		var actionTypes []string
		if r.Outpoint.Hash == r.UTXO.Hash && r.Outpoint.Index == r.UTXO.Index {
			actionTypes = append(actionTypes, "votingright-authenticate", "votingright-transfer", "votingright-delegate")
		} else {
			actionTypes = append(actionTypes, "votingright-recall")
		}

		rightToken := map[string]interface{}{
			"asset_id":       r.AssetID,
			"action_types":   actionTypes,
			"transaction_id": r.UTXO.Hash,
			"index":          r.UTXO.Index,
		}
		rights = append(rights, rightToken)
	}
	return map[string]interface{}{
		"balances": rights,
		"last":     last,
	}, nil
}

// parseVotingBuildRequest parses `votingright` BuildRequest sources and
// destinations. Unlike other asset types, voting request inputs and
// outputs need data from each other in order to build the correct
// txbuilder.Reservers and txbuilder.Receivers.
//
// This function will pair the votingright sources and destinations up by
// asset ID, and use the information from both to construct the
// txbuilder.Sources and txbuilder.Destinations.
func parseVotingBuildRequest(ctx context.Context, sources []*Source, destinations []*Destination) (srcs []*txbuilder.Source, dsts []*txbuilder.Destination, err error) {
	var (
		srcsByAssetID = map[bc.AssetID]*Source{}
		dstsByAssetID = map[bc.AssetID]*Destination{}
	)
	// Pair voting rights up by asset id.
	for _, src := range sources {
		if src.AssetID == nil {
			return nil, nil, errors.WithDetail(ErrBadBuildRequest, "asset type unspecified")
		}
		if _, ok := srcsByAssetID[*src.AssetID]; ok {
			return nil, nil, errors.WithDetail(ErrBadBuildRequest, "voting right asset appears twice as source")
		}
		srcsByAssetID[*src.AssetID] = src
	}
	for _, dst := range destinations {
		if dst.AssetID == nil {
			return nil, nil, errors.WithDetail(ErrBadBuildRequest, "asset type unspecified")
		}
		if _, ok := dstsByAssetID[*dst.AssetID]; ok {
			return nil, nil, errors.WithDetail(ErrBadBuildRequest, "voting right asset appears twice as destination")
		}
		dstsByAssetID[*dst.AssetID] = dst
	}
	if len(sources) > len(destinations) {
		// Both the source and destination must be provided in the same build
		// request. This is unavoidable because:
		// - the output contract script requires knowledge of the input's chain of ownership
		// - the sigscript needs to provide the new contract parameters
		// The only exception is issuing new voting right tokens. Then there
		// will be more destinations than sources.
		return nil, nil, errors.WithDetailf(ErrBadBuildRequest,
			"voting right source and destinations must be provided in the same build request")
	}

	for assetID, dst := range dstsByAssetID {
		// Validate the destination.
		if dst.Amount != 0 {
			return nil, nil, errors.WithDetailf(ErrBadBuildRequest, "voting right destinations do not take amounts")
		}

		src, ok := srcsByAssetID[assetID]
		if !ok {
			// If there is no votingright source, then assume this is an attempt
			// to issue into a new asset into a voting right contract.
			if dst.AdminScript == nil {
				return nil, nil, errors.WithDetailf(ErrBadBuildRequest, "voting right issuance requires a voting system admin script")
			}
			holder, err := dst.buildAddress(ctx)
			if err != nil {
				return nil, nil, err
			}
			dsts = append(dsts, &txbuilder.Destination{
				AssetAmount: bc.AssetAmount{AssetID: assetID, Amount: 1},
				Metadata:    dst.Metadata,
				Receiver:    voting.RightIssuance(ctx, dst.AdminScript, holder),
			})
			continue
		}

		// Validate the source.
		if src.TxHash == nil {
			src.TxHash = src.TxHashAsID
		}
		if src.TxHash == nil || src.Index == nil {
			return nil, nil, errors.WithDetailf(ErrBadBuildRequest, "bad voting right source")
		}
		if src.Amount != 0 {
			return nil, nil, errors.WithDetailf(ErrBadBuildRequest, "voting right sources do not take amounts")
		}
		out := bc.Outpoint{Hash: *src.TxHash, Index: *src.Index}
{'fraction_non_alphanumeric': 0.11895704549056903, 'fraction_numerical': 0.0015850372483753369, 'mean_word_length': 4.123426715387739, 'pattern_counts': {'":': 12, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3463609', 'n_tokens_mistral': 4384, 'n_tokens_neox': 4006, 'n_words': 1181}