<commit_before>// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Copyright (C) 2015-2017 The Lightning Network Developers

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	macaroon "gopkg.in/macaroon.v2"

	"github.com/btcsuite/btcutil"
	"github.com/lightningnetwork/lnd/lncfg"
	"github.com/lightningnetwork/lnd/lnrpc"
	"github.com/lightningnetwork/lnd/macaroons"
	"github.com/urfave/cli"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

const (
	defaultDataDir          = "data"
	defaultChainSubDir      = "chain"
	defaultTLSCertFilename  = "tls.cert"
	defaultMacaroonFilename = "admin.macaroon"
	defaultRPCPort          = "10009"
	defaultRPCHostPort      = "localhost:" + defaultRPCPort
)

var (
	// Commit stores the current commit hash of this build. This should be
	// set using -ldflags during compilation.
	Commit string

	defaultLndDir      = btcutil.AppDataDir("lnd", false)
	defaultTLSCertPath = filepath.Join(defaultLndDir, defaultTLSCertFilename)
)

func fatal(err error) {
	fmt.Fprintf(os.Stderr, "[lncli] %v\n", err)
	os.Exit(1)
}

func getWalletUnlockerClient(ctx *cli.Context) (lnrpc.WalletUnlockerClient, func()) {
	conn := getClientConn(ctx, true)

	cleanUp := func() {
		conn.Close()
	}

	return lnrpc.NewWalletUnlockerClient(conn), cleanUp
}

func getClient(ctx *cli.Context) (lnrpc.LightningClient, func()) {
	conn := getClientConn(ctx, false)

	cleanUp := func() {
		conn.Close()
	}

	return lnrpc.NewLightningClient(conn), cleanUp
}

func getClientConn(ctx *cli.Context, skipMacaroons bool) *grpc.ClientConn {
	// First, we'll parse the args from the command.
	tlsCertPath, macPath := parseArgs(ctx)

	// Load the specified TLS certificate and build transport credentials
	// with it.
	creds, err := credentials.NewClientTLSFromFile(tlsCertPath, "")
	if err != nil {
		fatal(err)
	}

	// Create a dial options array.
	opts := []grpc.DialOption{
		grpc.WithTransportCredentials(creds),
	}

	// Only process macaroon credentials if --no-macaroons isn't set and
	// if we're not skipping macaroon processing.
	if !ctx.GlobalBool("no-macaroons") && !skipMacaroons {
		// Load the specified macaroon file.
		macBytes, err := ioutil.ReadFile(macPath)
		if err != nil {
			fatal(err)
		}
		mac := &macaroon.Macaroon{}
		if err = mac.UnmarshalBinary(macBytes); err != nil {
			fatal(err)
		}

		macConstraints := []macaroons.Constraint{
			// We add a time-based constraint to prevent replay of the
			// macaroon. It's good for 60 seconds by default to make up for
			// any discrepancy between client and server clocks, but leaking
			// the macaroon before it becomes invalid makes it possible for
			// an attacker to reuse the macaroon. In addition, the validity
			// time of the macaroon is extended by the time the server clock
			// is behind the client clock, or shortened by the time the
			// server clock is ahead of the client clock (or invalid
			// altogether if, in the latter case, this time is more than 60
			// seconds).
			// TODO(aakselrod): add better anti-replay protection.
			macaroons.TimeoutConstraint(ctx.GlobalInt64("macaroontimeout")),

			// Lock macaroon down to a specific IP address.
			macaroons.IPLockConstraint(ctx.GlobalString("macaroonip")),

			// ... Add more constraints if needed.
		}

		// Apply constraints to the macaroon.
		constrainedMac, err := macaroons.AddConstraints(mac, macConstraints...)
		if err != nil {
			fatal(err)
		}

		// Now we append the macaroon credentials to the dial options.
		cred := macaroons.NewMacaroonCredential(constrainedMac)
		opts = append(opts, grpc.WithPerRPCCredentials(cred))
	}

	// We need to use a custom dialer so we can also connect to unix sockets
	// and not just TCP addresses.
	opts = append(
		opts, grpc.WithDialer(
			lncfg.ClientAddressDialer(defaultRPCPort),
		),
	)
	conn, err := grpc.Dial(ctx.GlobalString("rpcserver"), opts...)
	if err != nil {
		fatal(err)
	}

	return conn
}

// parseArgs parses the TLS certificate and macaroon paths from the command.
func parseArgs(ctx *cli.Context) (string, string) {
	// We'll start off by parsing the active chain and network. These are
	// needed to determine the correct path to the macaroon when not
	// specified.
	chain := strings.ToLower(ctx.GlobalString("chain"))
	switch chain {
	case "bitcoin", "litecoin":
	default:
		err := fmt.Errorf("unknown chain: %v", chain)
		fatal(err)
	}

	network := strings.ToLower(ctx.GlobalString("network"))
	switch network {
	case "mainnet", "testnet", "regtest", "simnet":
	default:
		err := fmt.Errorf("unknown network: %v", network)
		fatal(err)
	}

	var tlsCertPath, macPath string
	lndDir := cleanAndExpandPath(ctx.GlobalString("lnddir"))
	if lndDir != defaultLndDir {
		// If a custom lnd directory was set, we'll also check if custom
		// paths for the TLS cert and macaroon file were set as well. If
		// not, we'll override their paths so they can be found within
		// the custom lnd directory set. This allows us to set a custom
		// lnd directory, along with custom paths to the TLS cert and
		// macaroon file.
		tlsCertPath = cleanAndExpandPath(ctx.GlobalString("tlscertpath"))
		if tlsCertPath == defaultTLSCertPath {
			tlsCertPath = filepath.Join(lndDir, defaultTLSCertFilename)
		}

		macPath = cleanAndExpandPath(ctx.GlobalString("macaroonpath"))
		if macPath == "" {
			macPath = filepath.Join(
				lndDir, defaultDataDir, defaultChainSubDir,
				chain, network, defaultMacaroonFilename,
			)
		}
	}

	return tlsCertPath, macPath
}

func main() {
	app := cli.NewApp()
	app.Name = "lncli"
	app.Version = fmt.Sprintf("%s commit=%s", "0.4.2", Commit)
	app.Usage = "control plane for your Lightning Network Daemon (lnd)"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "rpcserver",
			Value: defaultRPCHostPort,
			Usage: "host:port of ln daemon",
		},
		cli.StringFlag{
			Name:  "lnddir",
			Value: defaultLndDir,
			Usage: "path to lnd's base directory",
		},
		cli.StringFlag{
			Name:  "tlscertpath",
			Value: defaultTLSCertPath,
			Usage: "path to TLS certificate",
		},
		cli.StringFlag{
			Name:  "chain, c",
			Usage: "the chain lnd is running on e.g. bitcoin",
			Value: "bitcoin",
		},
		cli.StringFlag{
			Name: "network, n",
			Usage: "the network lnd is running on e.g. mainnet, " +
				"testnet, etc.",
			Value: "mainnet",
		},
		cli.BoolFlag{
			Name:  "no-macaroons",
			Usage: "disable macaroon authentication",
		},
		cli.StringFlag{
			Name:  "macaroonpath",
			Usage: "path to macaroon file",
		},
		cli.Int64Flag{
			Name:  "macaroontimeout",
			Value: 60,
			Usage: "anti-replay macaroon validity time in seconds",
		},
		cli.StringFlag{
			Name:  "macaroonip",
			Usage: "if set, lock macaroon to specific IP address",
		},
	}
	app.Commands = []cli.Command{
		createCommand,
		unlockCommand,
		changePasswordCommand,
		newAddressCommand,
		sendManyCommand,
		sendCoinsCommand,
		connectCommand,
		disconnectCommand,
		openChannelCommand,
		closeChannelCommand,
		closeAllChannelsCommand,
		listPeersCommand,
		walletBalanceCommand,
		channelBalanceCommand,
		getInfoCommand,
		pendingChannelsCommand,
		sendPaymentCommand,
		payInvoiceCommand,
		sendToRouteCommand,
		addInvoiceCommand,
		lookupInvoiceCommand,
		listInvoicesCommand,
		listChannelsCommand,
		closedChannelsCommand,
		listPaymentsCommand,
		describeGraphCommand,
		getChanInfoCommand,
		getNodeInfoCommand,
		queryRoutesCommand,
		getNetworkInfoCommand,
		debugLevelCommand,
		decodePayReqCommand,
		listChainTxnsCommand,
		stopCommand,
		signMessageCommand,
		verifyMessageCommand,
		feeReportCommand,
		updateChannelPolicyCommand,
		forwardingHistoryCommand,
	}

	if err := app.Run(os.Args); err != nil {
		fatal(err)
	}
}

// cleanAndExpandPath expands environment variables and leading ~ in the
// passed path, cleans the result, and returns it.
// This function is taken from https://github.com/btcsuite/btcd
func cleanAndExpandPath(path string) string {
	if path == "" {
		return ""
	}

	// Expand initial ~ to OS specific home directory.
	if strings.HasPrefix(path, "~") {
		var homeDir string
		user, err := user.Current()
		if err == nil {
			homeDir = user.HomeDir
		} else {
			homeDir = os.Getenv("HOME")
		}

		path = strings.Replace(path, "~", homeDir, 1)
	}

	// NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
	// but the variables can still be expanded via POSIX-style $VARIABLE.
	return filepath.Clean(os.ExpandEnv(path))
}
<commit_msg>cmd/lncli: properly parse cert and macaroon paths for all config variants<commit_after>// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Copyright (C) 2015-2017 The Lightning Network Developers

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	macaroon "gopkg.in/macaroon.v2"

	"github.com/btcsuite/btcutil"
	"github.com/lightningnetwork/lnd/lncfg"
	"github.com/lightningnetwork/lnd/lnrpc"
	"github.com/lightningnetwork/lnd/macaroons"
	"github.com/urfave/cli"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

const (
	defaultDataDir          = "data"
	defaultChainSubDir      = "chain"
	defaultTLSCertFilename  = "tls.cert"
	defaultMacaroonFilename = "admin.macaroon"
	defaultRPCPort          = "10009"
	defaultRPCHostPort      = "localhost:" + defaultRPCPort
)

var (
	// Commit stores the current commit hash of this build. This should be
	// set using -ldflags during compilation.
	Commit string

	defaultLndDir      = btcutil.AppDataDir("lnd", false)
	defaultTLSCertPath = filepath.Join(defaultLndDir, defaultTLSCertFilename)
)

func fatal(err error) {
	fmt.Fprintf(os.Stderr, "[lncli] %v\n", err)
	os.Exit(1)
}

func getWalletUnlockerClient(ctx *cli.Context) (lnrpc.WalletUnlockerClient, func()) {
	conn := getClientConn(ctx, true)

	cleanUp := func() {
		conn.Close()
	}

	return lnrpc.NewWalletUnlockerClient(conn), cleanUp
}

func getClient(ctx *cli.Context) (lnrpc.LightningClient, func()) {
	conn := getClientConn(ctx, false)

	cleanUp := func() {
		conn.Close()
	}

	return lnrpc.NewLightningClient(conn), cleanUp
}

func getClientConn(ctx *cli.Context, skipMacaroons bool) *grpc.ClientConn {
	// First, we'll parse the args from the command.
	tlsCertPath, macPath := parseArgs(ctx)

	// Load the specified TLS certificate and build transport credentials
	// with it.
	creds, err := credentials.NewClientTLSFromFile(tlsCertPath, "")
	if err != nil {
		fatal(err)
	}

	// Create a dial options array.
	opts := []grpc.DialOption{
		grpc.WithTransportCredentials(creds),
	}

	// Only process macaroon credentials if --no-macaroons isn't set and
	// if we're not skipping macaroon processing.
	if !ctx.GlobalBool("no-macaroons") && !skipMacaroons {
		// Load the specified macaroon file.
		macBytes, err := ioutil.ReadFile(macPath)
		if err != nil {
			fatal(err)
		}
		mac := &macaroon.Macaroon{}
		if err = mac.UnmarshalBinary(macBytes); err != nil {
			fatal(err)
		}

		macConstraints := []macaroons.Constraint{
			// We add a time-based constraint to prevent replay of the
			// macaroon. It's good for 60 seconds by default to make up for
			// any discrepancy between client and server clocks, but leaking
			// the macaroon before it becomes invalid makes it possible for
			// an attacker to reuse the macaroon. In addition, the validity
			// time of the macaroon is extended by the time the server clock
			// is behind the client clock, or shortened by the time the
			// server clock is ahead of the client clock (or invalid
			// altogether if, in the latter case, this time is more than 60
			// seconds).
			// TODO(aakselrod): add better anti-replay protection.
			macaroons.TimeoutConstraint(ctx.GlobalInt64("macaroontimeout")),

			// Lock macaroon down to a specific IP address.
			macaroons.IPLockConstraint(ctx.GlobalString("macaroonip")),

			// ... Add more constraints if needed.
		}

		// Apply constraints to the macaroon.
		constrainedMac, err := macaroons.AddConstraints(mac, macConstraints...)
		if err != nil {
			fatal(err)
		}

		// Now we append the macaroon credentials to the dial options.
		cred := macaroons.NewMacaroonCredential(constrainedMac)
		opts = append(opts, grpc.WithPerRPCCredentials(cred))
	}

	// We need to use a custom dialer so we can also connect to unix sockets
	// and not just TCP addresses.
	opts = append(
		opts, grpc.WithDialer(
			lncfg.ClientAddressDialer(defaultRPCPort),
		),
	)
	conn, err := grpc.Dial(ctx.GlobalString("rpcserver"), opts...)
	if err != nil {
		fatal(err)
	}

	return conn
}

// parseArgs parses the TLS certificate and macaroon paths from the command.
func parseArgs(ctx *cli.Context) (string, string) {
	// We'll start off by parsing the active chain and network. These are
	// needed to determine the correct path to the macaroon when not
	// specified.
	chain := strings.ToLower(ctx.GlobalString("chain"))
	switch chain {
	case "bitcoin", "litecoin":
	default:
		err := fmt.Errorf("unknown chain: %v", chain)
		fatal(err)
	}

	network := strings.ToLower(ctx.GlobalString("network"))
	switch network {
	case "mainnet", "testnet", "regtest", "simnet":
	default:
		err := fmt.Errorf("unknown network: %v", network)
		fatal(err)
	}

	// We'll now fetch the lnddir so we can make a decision  on how to
	// properly read the macaroons (if needed) and also the cert. This will
	// either be the default, or will have been overwritten by the end
	// user.
	lndDir := cleanAndExpandPath(ctx.GlobalString("lnddir"))

	// If the macaroon path as been manually provided, then we'll only
	// target the specified file.
	var macPath string
	if ctx.GlobalString("macaroonpath") != "" {
		macPath = cleanAndExpandPath(ctx.GlobalString("macaroonpath"))
	} else {
		// Otherwise, we'll go into the path:
		// lnddir/data/chain/<chain>/<network> in order to fetch the
		// macaroon that we need.
		macPath = filepath.Join(
			lndDir, defaultDataDir, defaultChainSubDir, chain,
			network, defaultMacaroonFilename,
		)
	}

	tlsCertPath := cleanAndExpandPath(ctx.GlobalString("tlscertpath"))

	// If a custom lnd directory was set, we'll also check if custom paths
	// for the TLS cert and macaroon file were set as well. If not, we'll
	// override their paths so they can be found within the custom lnd
	// directory set. This allows us to set a custom lnd directory, along
	// with custom paths to the TLS cert and macaroon file.
	if lndDir != defaultLndDir {
		tlsCertPath = filepath.Join(lndDir, defaultTLSCertFilename)
	}

	return tlsCertPath, macPath
}

func main() {
	app := cli.NewApp()
	app.Name = "lncli"
	app.Version = fmt.Sprintf("%s commit=%s", "0.4.2", Commit)
	app.Usage = "control plane for your Lightning Network Daemon (lnd)"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "rpcserver",
			Value: defaultRPCHostPort,
			Usage: "host:port of ln daemon",
		},
		cli.StringFlag{
			Name:  "lnddir",
			Value: defaultLndDir,
			Usage: "path to lnd's base directory",
		},
		cli.StringFlag{
			Name:  "tlscertpath",
			Value: defaultTLSCertPath,
			Usage: "path to TLS certificate",
		},
		cli.StringFlag{
			Name:  "chain, c",
			Usage: "the chain lnd is running on e.g. bitcoin",
			Value: "bitcoin",
		},
		cli.StringFlag{
			Name: "network, n",
			Usage: "the network lnd is running on e.g. mainnet, " +
				"testnet, etc.",
			Value: "mainnet",
		},
		cli.BoolFlag{
			Name:  "no-macaroons",
			Usage: "disable macaroon authentication",
		},
		cli.StringFlag{
			Name:  "macaroonpath",
			Usage: "path to macaroon file",
		},
		cli.Int64Flag{
			Name:  "macaroontimeout",
			Value: 60,
			Usage: "anti-replay macaroon validity time in seconds",
		},
		cli.StringFlag{
			Name:  "macaroonip",
			Usage: "if set, lock macaroon to specific IP address",
		},
	}
	app.Commands = []cli.Command{
		createCommand,
		unlockCommand,
		changePasswordCommand,
		newAddressCommand,
		sendManyCommand,
		sendCoinsCommand,
		connectCommand,
		disconnectCommand,
		openChannelCommand,
		closeChannelCommand,
		closeAllChannelsCommand,
		listPeersCommand,
		walletBalanceCommand,
		channelBalanceCommand,
		getInfoCommand,
		pendingChannelsCommand,
		sendPaymentCommand,
		payInvoiceCommand,
		sendToRouteCommand,
		addInvoiceCommand,
		lookupInvoiceCommand,
		listInvoicesCommand,
		listChannelsCommand,
		closedChannelsCommand,
		listPaymentsCommand,
		describeGraphCommand,
		getChanInfoCommand,
		getNodeInfoCommand,
		queryRoutesCommand,
		getNetworkInfoCommand,
		debugLevelCommand,
		decodePayReqCommand,
		listChainTxnsCommand,
		stopCommand,
		signMessageCommand,
		verifyMessageCommand,
		feeReportCommand,
		updateChannelPolicyCommand,
		forwardingHistoryCommand,
	}

	if err := app.Run(os.Args); err != nil {
		fatal(err)
	}
}

// cleanAndExpandPath expands environment variables and leading ~ in the
// passed path, cleans the result, and returns it.
// This function is taken from https://github.com/btcsuite/btcd
func cleanAndExpandPath(path string) string {
	if path == "" {
		return ""
	}

	// Expand initial ~ to OS specific home directory.
	if strings.HasPrefix(path, "~") {
		var homeDir string
		user, err := user.Current()
		if err == nil {
			homeDir = user.HomeDir
		} else {
			homeDir = os.Getenv("HOME")
		}

		path = strings.Replace(path, "~", homeDir, 1)
	}

	// NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
	// but the variables can still be expanded via POSIX-style $VARIABLE.
	return filepath.Clean(os.ExpandEnv(path))
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime_test

import (
	"runtime"
	"testing"
)

func TestGcSys(t *testing.T) {
	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
	memstats := new(runtime.MemStats)
	runtime.GC()
	runtime.ReadMemStats(memstats)
	sys := memstats.Sys

	runtime.MemProfileRate = 0 // disable profiler

	itercount := 1000000
	if testing.Short() {
		itercount = 100000
	}
	for i := 0; i < itercount; i++ {
		workthegc()
	}

	// Should only be using a few MB.
	// We allocated 100 MB or (if not short) 1 GB.
	runtime.ReadMemStats(memstats)
	if sys > memstats.Sys {
		sys = 0
	} else {
		sys = memstats.Sys - sys
	}
	t.Logf("used %d extra bytes", sys)
	if sys > 16<<20 {
		t.Fatalf("using too much memory: %d bytes", sys)
	}
}

func workthegc() []byte {
	return make([]byte, 1029)
}

func TestGcDeepNesting(t *testing.T) {
	type T [2][2][2][2][2][2][2][2][2][2]*int
	a := new(T)

	// Prevent the compiler from applying escape analysis.
	// This makes sure new(T) is allocated on heap, not on the stack.
	t.Logf("%p", a)

	a[0][0][0][0][0][0][0][0][0][0] = new(int)
	*a[0][0][0][0][0][0][0][0][0][0] = 13
	runtime.GC()
	if *a[0][0][0][0][0][0][0][0][0][0] != 13 {
		t.Fail()
	}
}
<commit_msg>runtime: better error from TestGcSys when gc is disabled<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime_test

import (
	"os"
	"runtime"
	"testing"
)

func TestGcSys(t *testing.T) {
	if os.Getenv("GOGC") == "off" {
		t.Fatalf("GOGC=off in environment; test cannot pass")
	}
	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
	memstats := new(runtime.MemStats)
	runtime.GC()
	runtime.ReadMemStats(memstats)
	sys := memstats.Sys

	runtime.MemProfileRate = 0 // disable profiler

	itercount := 1000000
	if testing.Short() {
		itercount = 100000
	}
	for i := 0; i < itercount; i++ {
		workthegc()
	}

	// Should only be using a few MB.
	// We allocated 100 MB or (if not short) 1 GB.
	runtime.ReadMemStats(memstats)
	if sys > memstats.Sys {
		sys = 0
	} else {
		sys = memstats.Sys - sys
	}
	t.Logf("used %d extra bytes", sys)
	if sys > 16<<20 {
		t.Fatalf("using too much memory: %d bytes", sys)
	}
}

func workthegc() []byte {
	return make([]byte, 1029)
}

func TestGcDeepNesting(t *testing.T) {
	type T [2][2][2][2][2][2][2][2][2][2]*int
	a := new(T)

	// Prevent the compiler from applying escape analysis.
	// This makes sure new(T) is allocated on heap, not on the stack.
	t.Logf("%p", a)

	a[0][0][0][0][0][0][0][0][0][0] = new(int)
	*a[0][0][0][0][0][0][0][0][0][0] = 13
	runtime.GC()
	if *a[0][0][0][0][0][0][0][0][0][0] != 13 {
		t.Fail()
	}
}
<|endoftext|>


<commit_before>// +build linux darwin freebsd

package mount

import (
	"sync"
	"sync/atomic"
	"time"

	"bazil.org/fuse"
	fusefs "bazil.org/fuse/fs"
	"github.com/ncw/rclone/fs"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
)

// File represents a file
type File struct {
	size    int64        // size of file - read and written with atomic int64 - must be 64 bit aligned
	d       *Dir         // parent directory - read only
	mu      sync.RWMutex // protects the following
	o       fs.Object    // NB o may be nil if file is being written
	writers int          // number of writers for this file
}

// newFile creates a new File
func newFile(d *Dir, o fs.Object) *File {
	return &File{
		d: d,
		o: o,
	}
}

// rename should be called to update f.o and f.d after a rename
func (f *File) rename(d *Dir, o fs.Object) {
	f.mu.Lock()
	f.o = o
	f.d = d
	f.mu.Unlock()
}

// addWriters increments or decrements the writers
func (f *File) addWriters(n int) {
	f.mu.Lock()
	f.writers += n
	f.mu.Unlock()
}

// Check interface satisfied
var _ fusefs.Node = (*File)(nil)

// Attr fills out the attributes for the file
func (f *File) Attr(ctx context.Context, a *fuse.Attr) error {
	f.mu.Lock()
	defer f.mu.Unlock()
	a.Gid = gid
	a.Uid = uid
	a.Mode = filePerms
	// if o is nil it isn't valid yet, so return the size so far
	if f.o == nil {
		a.Size = uint64(atomic.LoadInt64(&f.size))
	} else {
		a.Size = uint64(f.o.Size())
		if !noModTime {
			modTime := f.o.ModTime()
			a.Atime = modTime
			a.Mtime = modTime
			a.Ctime = modTime
			a.Crtime = modTime
		}
	}
	a.Blocks = (a.Size + 511) / 512
	fs.Debugf(f.o, "File.Attr %+v", a)
	return nil
}

// Update the size while writing
func (f *File) written(n int64) {
	atomic.AddInt64(&f.size, n)
}

// Update the object when written
func (f *File) setObject(o fs.Object) {
	f.mu.Lock()
	defer f.mu.Unlock()
	f.o = o
	f.d.addObject(o, f)
}

// Wait for f.o to become non nil for a short time returning it or an
// error
//
// Call without the mutex held
func (f *File) waitForValidObject() (o fs.Object, err error) {
	for i := 0; i < 50; i++ {
		f.mu.Lock()
		o = f.o
		writers := f.writers
		f.mu.Unlock()
		if o != nil {
			return o, nil
		}
		if writers == 0 {
			return nil, errors.New("can't open file - writer failed")
		}
		time.Sleep(100 * time.Millisecond)
	}
	return nil, fuse.ENOENT
}

// Check interface satisfied
var _ fusefs.NodeOpener = (*File)(nil)

// Open the file for read or write
func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fusefs.Handle, error) {
	// if o is nil it isn't valid yet
	o, err := f.waitForValidObject()
	if err != nil {
		return nil, err
	}

	fs.Debugf(o, "File.Open")

	switch {
	case req.Flags.IsReadOnly():
		if noSeek {
			resp.Flags |= fuse.OpenNonSeekable
		}
		fh, err := newReadFileHandle(o)
		if err != nil {
			fs.Debugf(o, "File.Open failed to open for read: %v", err)
			return nil, err
		}
		return fh, nil
	case req.Flags.IsWriteOnly() || (req.Flags.IsReadWrite() && (req.Flags&fuse.OpenTruncate) != 0):
		resp.Flags |= fuse.OpenNonSeekable
		src := newCreateInfo(f.d.f, o.Remote())
		fh, err := newWriteFileHandle(f.d, f, src)
		if err != nil {
			fs.Debugf(o, "File.Open failed to open for write: %v", err)
			return nil, err
		}
		return fh, nil
	case req.Flags.IsReadWrite():
		return nil, errors.New("can't open read and write")
	}

	/*
	   // File was opened in append-only mode, all writes will go to end
	   // of file. OS X does not provide this information.
	   OpenAppend    OpenFlags = syscall.O_APPEND
	   OpenCreate    OpenFlags = syscall.O_CREAT
	   OpenDirectory OpenFlags = syscall.O_DIRECTORY
	   OpenExclusive OpenFlags = syscall.O_EXCL
	   OpenNonblock  OpenFlags = syscall.O_NONBLOCK
	   OpenSync      OpenFlags = syscall.O_SYNC
	   OpenTruncate  OpenFlags = syscall.O_TRUNC
	*/
	return nil, errors.New("can't figure out how to open")
}

// Check interface satisfied
var _ fusefs.NodeFsyncer = (*File)(nil)

// Fsync the file
//
// Note that we don't do anything except return OK
func (f *File) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
	return nil
}
<commit_msg>mount: fix logging for unimplemented file open modes #1195<commit_after>// +build linux darwin freebsd

package mount

import (
	"sync"
	"sync/atomic"
	"time"

	"bazil.org/fuse"
	fusefs "bazil.org/fuse/fs"
	"github.com/ncw/rclone/fs"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
)

// File represents a file
type File struct {
	size    int64        // size of file - read and written with atomic int64 - must be 64 bit aligned
	d       *Dir         // parent directory - read only
	mu      sync.RWMutex // protects the following
	o       fs.Object    // NB o may be nil if file is being written
	writers int          // number of writers for this file
}

// newFile creates a new File
func newFile(d *Dir, o fs.Object) *File {
	return &File{
		d: d,
		o: o,
	}
}

// rename should be called to update f.o and f.d after a rename
func (f *File) rename(d *Dir, o fs.Object) {
	f.mu.Lock()
	f.o = o
	f.d = d
	f.mu.Unlock()
}

// addWriters increments or decrements the writers
func (f *File) addWriters(n int) {
	f.mu.Lock()
	f.writers += n
	f.mu.Unlock()
}

// Check interface satisfied
var _ fusefs.Node = (*File)(nil)

// Attr fills out the attributes for the file
func (f *File) Attr(ctx context.Context, a *fuse.Attr) error {
	f.mu.Lock()
	defer f.mu.Unlock()
	a.Gid = gid
	a.Uid = uid
	a.Mode = filePerms
	// if o is nil it isn't valid yet, so return the size so far
	if f.o == nil {
		a.Size = uint64(atomic.LoadInt64(&f.size))
	} else {
		a.Size = uint64(f.o.Size())
		if !noModTime {
			modTime := f.o.ModTime()
			a.Atime = modTime
			a.Mtime = modTime
			a.Ctime = modTime
			a.Crtime = modTime
		}
	}
	a.Blocks = (a.Size + 511) / 512
	fs.Debugf(f.o, "File.Attr %+v", a)
	return nil
}

// Update the size while writing
func (f *File) written(n int64) {
	atomic.AddInt64(&f.size, n)
}

// Update the object when written
func (f *File) setObject(o fs.Object) {
	f.mu.Lock()
	defer f.mu.Unlock()
	f.o = o
	f.d.addObject(o, f)
}

// Wait for f.o to become non nil for a short time returning it or an
// error
//
// Call without the mutex held
func (f *File) waitForValidObject() (o fs.Object, err error) {
	for i := 0; i < 50; i++ {
		f.mu.Lock()
		o = f.o
		writers := f.writers
		f.mu.Unlock()
		if o != nil {
			return o, nil
		}
		if writers == 0 {
			return nil, errors.New("can't open file - writer failed")
		}
		time.Sleep(100 * time.Millisecond)
	}
	return nil, fuse.ENOENT
}

// Check interface satisfied
var _ fusefs.NodeOpener = (*File)(nil)

// Open the file for read or write
func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fh fusefs.Handle, err error) {
	// if o is nil it isn't valid yet
	o, err := f.waitForValidObject()
	if err != nil {
		return nil, err
	}
	fs.Debugf(o, "File.Open %v", req.Flags)

	switch {
	case req.Flags.IsReadOnly():
		if noSeek {
			resp.Flags |= fuse.OpenNonSeekable
		}
		fh, err = newReadFileHandle(o)
		err = errors.Wrap(err, "open for read")
	case req.Flags.IsWriteOnly() || (req.Flags.IsReadWrite() && (req.Flags&fuse.OpenTruncate) != 0):
		resp.Flags |= fuse.OpenNonSeekable
		src := newCreateInfo(f.d.f, o.Remote())
		fh, err = newWriteFileHandle(f.d, f, src)
		err = errors.Wrap(err, "open for write")
	case req.Flags.IsReadWrite():
		err = errors.New("can't open for read and write simultaneously")
	default:
		err = errors.Errorf("can't figure out how to open with flags %v", req.Flags)
	}

	/*
	   // File was opened in append-only mode, all writes will go to end
	   // of file. OS X does not provide this information.
	   OpenAppend    OpenFlags = syscall.O_APPEND
	   OpenCreate    OpenFlags = syscall.O_CREAT
	   OpenDirectory OpenFlags = syscall.O_DIRECTORY
	   OpenExclusive OpenFlags = syscall.O_EXCL
	   OpenNonblock  OpenFlags = syscall.O_NONBLOCK
	   OpenSync      OpenFlags = syscall.O_SYNC
	   OpenTruncate  OpenFlags = syscall.O_TRUNC
	*/

	if err != nil {
		fs.Errorf(o, "File.Open failed: %v", err)
		return nil, err
	}
	return fh, nil
}

// Check interface satisfied
var _ fusefs.NodeFsyncer = (*File)(nil)

// Fsync the file
//
// Note that we don't do anything except return OK
func (f *File) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testing

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"os"
	"strings"
	"time"
)

var matchExamples = flag.String("test.example", "", "regular expression to select examples to run")

type InternalExample struct {
	Name   string
	F      func()
	Output string
}

func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool) {
	if *match != "" {
		return // Don't run examples if testing is restricted: we're debugging.
	}
	ok = true

	var eg InternalExample

	stdout, stderr := os.Stdout, os.Stderr

	for _, eg = range examples {
		matched, err := matchString(*matchExamples, eg.Name)
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.example: %s\n", err)
			os.Exit(1)
		}
		if !matched {
			continue
		}
		if *chatty {
			fmt.Printf("=== RUN: %s\n", eg.Name)
		}

		// capture stdout and stderr
		r, w, err := os.Pipe()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		os.Stdout, os.Stderr = w, w
		outC := make(chan string)
		go func() {
			buf := new(bytes.Buffer)
			_, err := io.Copy(buf, r)
			if err != nil {
				fmt.Fprintf(stderr, "testing: copying pipe: %v\n", err)
				os.Exit(1)
			}
			outC <- buf.String()
		}()

		// run example
		t0 := time.Now()
		eg.F()
		dt := time.Now().Sub(t0)

		// close pipe, restore stdout/stderr, get output
		w.Close()
		os.Stdout, os.Stderr = stdout, stderr
		out := <-outC

		// report any errors
		tstr := fmt.Sprintf("(%.2f seconds)", dt.Seconds())
		if g, e := strings.TrimSpace(out), strings.TrimSpace(eg.Output); g != e {
			fmt.Printf("--- FAIL: %s %s\ngot:\n%s\nwant:\n%s\n",
				eg.Name, tstr, g, e)
			ok = false
		} else if *chatty {
			fmt.Printf("--- PASS: %s %s\n", eg.Name, tstr)
		}
	}

	return
}
<commit_msg>cmd/go: run examples even if -run is set if -example is also set Allows one to disable everything but the example being debugged. This time for sure.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testing

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"os"
	"strings"
	"time"
)

var matchExamples = flag.String("test.example", "", "regular expression to select examples to run")

type InternalExample struct {
	Name   string
	F      func()
	Output string
}

func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool) {
	if *match != "" && *matchExamples == "" {
		return // Don't run examples if testing is restricted: we're debugging.
	}
	ok = true

	var eg InternalExample

	stdout, stderr := os.Stdout, os.Stderr

	for _, eg = range examples {
		matched, err := matchString(*matchExamples, eg.Name)
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.example: %s\n", err)
			os.Exit(1)
		}
		if !matched {
			continue
		}
		if *chatty {
			fmt.Printf("=== RUN: %s\n", eg.Name)
		}

		// capture stdout and stderr
		r, w, err := os.Pipe()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		os.Stdout, os.Stderr = w, w
		outC := make(chan string)
		go func() {
			buf := new(bytes.Buffer)
			_, err := io.Copy(buf, r)
			if err != nil {
				fmt.Fprintf(stderr, "testing: copying pipe: %v\n", err)
				os.Exit(1)
			}
			outC <- buf.String()
		}()

		// run example
		t0 := time.Now()
		eg.F()
		dt := time.Now().Sub(t0)

		// close pipe, restore stdout/stderr, get output
		w.Close()
		os.Stdout, os.Stderr = stdout, stderr
		out := <-outC

		// report any errors
		tstr := fmt.Sprintf("(%.2f seconds)", dt.Seconds())
		if g, e := strings.TrimSpace(out), strings.TrimSpace(eg.Output); g != e {
			fmt.Printf("--- FAIL: %s %s\ngot:\n%s\nwant:\n%s\n",
				eg.Name, tstr, g, e)
			ok = false
		} else if *chatty {
			fmt.Printf("--- PASS: %s %s\n", eg.Name, tstr)
		}
	}

	return
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testing

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"
	"time"
)

type InternalExample struct {
	Name   string
	F      func()
	Output string
}

func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool) {
	ok = true

	var eg InternalExample

	for _, eg = range examples {
		matched, err := matchString(*match, eg.Name)
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.run: %s\n", err)
			os.Exit(1)
		}
		if !matched {
			continue
		}
		if !runExample(eg) {
			ok = false
		}
	}

	return
}

func runExample(eg InternalExample) (ok bool) {
	if *chatty {
		fmt.Printf("=== RUN: %s\n", eg.Name)
	}

	// Capture stdout.
	stdout := os.Stdout
	r, w, err := os.Pipe()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	os.Stdout = w
	outC := make(chan string)
	go func() {
		buf := new(bytes.Buffer)
		_, err := io.Copy(buf, r)
		r.Close()
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: copying pipe: %v\n", err)
			os.Exit(1)
		}
		outC <- buf.String()
	}()

	start := time.Now()

	// Clean up in a deferred call so we can recover if the example panics.
	defer func() {
		d := time.Now().Sub(start)

		// Close pipe, restore stdout, get output.
		w.Close()
		os.Stdout = stdout
		out := <-outC

		var fail string
		err := recover()
		if g, e := strings.TrimSpace(out), strings.TrimSpace(eg.Output); g != e && err == nil {
			fail = fmt.Sprintf("got:\n%s\nwant:\n%s\n", g, e)
		}
		if fail != "" || err != nil {
			fmt.Printf("--- FAIL: %s (%v)\n%s", eg.Name, d, fail)
		} else if *chatty {
			fmt.Printf("--- PASS: %s (%v)\n", eg.Name, d)
		}
		if err != nil {
			panic(err)
		}
	}()

	// Run example.
	eg.F()
	return
}
<commit_msg>testing: allow examples to pass (fix build)<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testing

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"
	"time"
)

type InternalExample struct {
	Name   string
	F      func()
	Output string
}

func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool) {
	ok = true

	var eg InternalExample

	for _, eg = range examples {
		matched, err := matchString(*match, eg.Name)
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.run: %s\n", err)
			os.Exit(1)
		}
		if !matched {
			continue
		}
		if !runExample(eg) {
			ok = false
		}
	}

	return
}

func runExample(eg InternalExample) (ok bool) {
	if *chatty {
		fmt.Printf("=== RUN: %s\n", eg.Name)
	}

	// Capture stdout.
	stdout := os.Stdout
	r, w, err := os.Pipe()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	os.Stdout = w
	outC := make(chan string)
	go func() {
		buf := new(bytes.Buffer)
		_, err := io.Copy(buf, r)
		r.Close()
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: copying pipe: %v\n", err)
			os.Exit(1)
		}
		outC <- buf.String()
	}()

	start := time.Now()
	ok = true

	// Clean up in a deferred call so we can recover if the example panics.
	defer func() {
		d := time.Now().Sub(start)

		// Close pipe, restore stdout, get output.
		w.Close()
		os.Stdout = stdout
		out := <-outC

		var fail string
		err := recover()
		if g, e := strings.TrimSpace(out), strings.TrimSpace(eg.Output); g != e && err == nil {
			fail = fmt.Sprintf("got:\n%s\nwant:\n%s\n", g, e)
		}
		if fail != "" || err != nil {
			fmt.Printf("--- FAIL: %s (%v)\n%s", eg.Name, d, fail)
			ok = false
		} else if *chatty {
			fmt.Printf("--- PASS: %s (%v)\n", eg.Name, d)
		}
		if err != nil {
			panic(err)
		}
	}()

	// Run example.
	eg.F()
	return
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/jessevdk/go-flags"
	"github.com/qmsk/e2/client"
	"github.com/qmsk/e2/discovery"
	"github.com/qmsk/e2/tally"
	"github.com/qmsk/e2/web"
	"log"
	"os"
	"os/signal"
)

type module interface {
	start(tally *tally.Tally) error
	stop() error
}

var Options = struct {
	DiscoveryOptions discovery.Options `group:"E2 Discovery"`
	ClientOptions    client.Options    `group:"E2 XML"`
	TallyOptions     tally.Options     `group:"Tally"`
	WebOptions       web.Options       `group:"Web API"`

	modules map[string]module
}{
	modules: make(map[string]module),
}

func registerModule(name string, module module) {
	if _, err := parser.AddGroup(name, name, module); err != nil {
		panic(err)
	}

	Options.modules[name] = module
}

var parser = flags.NewParser(&Options, flags.Default)

func start(tally *tally.Tally) {
	for moduleName, module := range Options.modules {
		if err := module.start(tally); err != nil {
			log.Fatalf("%v main: %v", moduleName, err)
		}
	}
}

func stop() {
	for moduleName, module := range Options.modules {
		if err := module.stop(); err != nil {
			log.Printf("Stop %v: %v", moduleName, err)
		}
	}
}

func main() {
	if _, err := parser.Parse(); err != nil {
		log.Fatalf("%v\n", err)
	}

	tally, err := Options.TallyOptions.Tally(Options.ClientOptions, Options.DiscoveryOptions)
	if err != nil {
		log.Fatalf("Tally: %v\n", err)
	}

	// setup modules
	start(tally)
	defer stop()

	// stopping
	stopChan := make(chan os.Signal)

	signal.Notify(stopChan, os.Interrupt)

	go func() {
		<-stopChan

		// second SIGINT kills
		signal.Stop(stopChan)

		tally.Stop()

	}()

	// Web
	webAPI := tally.WebAPI()
	webEvents := tally.WebEvents()

	go Options.WebOptions.Server(
		web.RoutePrefix("/api/", webAPI),
		web.RoutePrefix("/events", webEvents),
		Options.WebOptions.RouteStatic("/static/"),
		Options.WebOptions.RouteFile("/", "tally.html"),
	)

	// run
	if err := tally.Run(); err != nil {
		log.Fatalf("Tally.Run: %v\n", err)
	} else {
		log.Printf("Exit")
	}
}
<commit_msg>tally: always in readonly mode<commit_after>package main

import (
	"github.com/jessevdk/go-flags"
	"github.com/qmsk/e2/client"
	"github.com/qmsk/e2/discovery"
	"github.com/qmsk/e2/tally"
	"github.com/qmsk/e2/web"
	"log"
	"os"
	"os/signal"
)

type module interface {
	start(tally *tally.Tally) error
	stop() error
}

var Options = struct {
	DiscoveryOptions discovery.Options `group:"E2 Discovery"`
	ClientOptions    client.Options    `group:"E2 XML"`
	TallyOptions     tally.Options     `group:"Tally"`
	WebOptions       web.Options       `group:"Web API"`

	modules map[string]module
}{
	ClientOptions: client.Options{
		ReadOnly: true,
	},
	modules: make(map[string]module),
}

func registerModule(name string, module module) {
	if _, err := parser.AddGroup(name, name, module); err != nil {
		panic(err)
	}

	Options.modules[name] = module
}

var parser = flags.NewParser(&Options, flags.Default)

func start(tally *tally.Tally) {
	for moduleName, module := range Options.modules {
		if err := module.start(tally); err != nil {
			log.Fatalf("%v main: %v", moduleName, err)
		}
	}
}

func stop() {
	for moduleName, module := range Options.modules {
		if err := module.stop(); err != nil {
			log.Printf("Stop %v: %v", moduleName, err)
		}
	}
}

func main() {
	if _, err := parser.Parse(); err != nil {
		log.Fatalf("%v\n", err)
	}

	tally, err := Options.TallyOptions.Tally(Options.ClientOptions, Options.DiscoveryOptions)
	if err != nil {
		log.Fatalf("Tally: %v\n", err)
	}

	// setup modules
	start(tally)
	defer stop()

	// stopping
	stopChan := make(chan os.Signal)

	signal.Notify(stopChan, os.Interrupt)

	go func() {
		<-stopChan

		// second SIGINT kills
		signal.Stop(stopChan)

		tally.Stop()

	}()

	// Web
	webAPI := tally.WebAPI()
	webEvents := tally.WebEvents()

	go Options.WebOptions.Server(
		web.RoutePrefix("/api/", webAPI),
		web.RoutePrefix("/events", webEvents),
		Options.WebOptions.RouteStatic("/static/"),
		Options.WebOptions.RouteFile("/", "tally.html"),
	)

	// run
	if err := tally.Run(); err != nil {
		log.Fatalf("Tally.Run: %v\n", err)
	} else {
		log.Printf("Exit")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/Shopify/themekit/cmd"
	"github.com/Shopify/themekit/kit"
)

func main() {
	if err := cmd.ThemeCmd.Execute(); err != nil {
		kit.LogFatal(err)
	}
}
<commit_msg>Adding profiling functionality<commit_after>package main

import (
	"os"
	"runtime"
	"runtime/pprof"

	"github.com/Shopify/themekit/cmd"
	"github.com/Shopify/themekit/kit"
)

const (
	cpuProfileVar = "THEMEKIT_CPUPROFILE"
	memProfileVar = "THEMEKIT_MEMPROFILE"
)

func main() {
	if CPUProfile := os.Getenv(cpuProfileVar); CPUProfile != "" {
		f, err := os.Create(CPUProfile)
		if err != nil {
			kit.LogFatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if err := cmd.ThemeCmd.Execute(); err != nil {
		kit.LogFatal(err)
	}

	if memProfile := os.Getenv(memProfileVar); memProfile != "" {
		f, err := os.Create(memProfile)
		if err != nil {
			kit.LogFatal("could not create memory profile: ", err)
		}
		runtime.GC() // get up-to-date statistics
		if err := pprof.WriteHeapProfile(f); err != nil {
			kit.LogFatal("could not write memory profile: ", err)
		}
		f.Close()
	}
}
<|endoftext|>


<commit_before>package coal

import (
	"reflect"
	"testing"

	"github.com/stretchr/testify/assert"
	"gopkg.in/mgo.v2/bson"
)

type malformedBase1 struct {
	Base
}

type malformedBase2 struct {
	Base `json:"-"`
}

type malformedBase3 struct {
	Base `json:"-" bson:",inline"`
}

type malformedBase4 struct {
	Foo  string `json:"foo"`
	Base `json:"-" bson:",inline" coal:"foo:foos" valid:"required"`
}

type malformedToOne struct {
	Base `json:"-" bson:",inline" coal:"foo:foos"`
	Foo  bson.ObjectId `coal:"foo:foo:foo"`
}

type malformedToMany struct {
	Base `json:"-" bson:",inline" coal:"foo:foos"`
	Foo  []bson.ObjectId `coal:"foo:foo:foo"`
}

type malformedHasOne struct {
	Base `json:"-" bson:",inline" coal:"foo:foos"`
	Foo  HasOne
}

type malformedHasMany struct {
	Base `json:"-" bson:",inline" coal:"foo:foos"`
	Foo  HasMany
}

type unexpectedTag struct {
	Base `json:"-" bson:",inline" coal:"foo:foos"`
	Foo  string `coal:"foo"`
}

func TestNewMeta(t *testing.T) {
	assert.Panics(t, func() {
		NewMeta(&malformedBase1{})
	})

	assert.Panics(t, func() {
		NewMeta(&malformedBase2{})
	})

	assert.Panics(t, func() {
		NewMeta(&malformedBase3{})
	})

	assert.Panics(t, func() {
		NewMeta(&malformedBase4{})
	})

	assert.Panics(t, func() {
		NewMeta(&malformedToOne{})
	})

	assert.Panics(t, func() {
		NewMeta(&malformedToMany{})
	})

	assert.Panics(t, func() {
		NewMeta(&malformedHasOne{})
	})

	assert.Panics(t, func() {
		NewMeta(&malformedHasMany{})
	})

	assert.Panics(t, func() {
		NewMeta(&unexpectedTag{})
	})

	assert.PanicsWithValue(t, `coal: duplicate JSON key "text"`, func() {
		type m struct {
			Base  `json:"-" bson:",inline" valid:"required" coal:"ms"`
			Text1 string `json:"text"`
			Text2 string `json:"text"`
		}

		NewMeta(&m{})
	})

	assert.PanicsWithValue(t, `coal: duplicate BSON field "text"`, func() {
		type m struct {
			Base  `json:"-" bson:",inline" valid:"required" coal:"ms"`
			Text1 string `bson:"text"`
			Text2 string `bson:"text"`
		}

		NewMeta(&m{})
	})

	assert.PanicsWithValue(t, `coal: duplicate relationship "parent"`, func() {
		type m struct {
			Base    `json:"-" bson:",inline" valid:"required" coal:"ms"`
			Parent1 bson.ObjectId `valid:"object-id" coal:"parent:parents"`
			Parent2 bson.ObjectId `valid:"object-id" coal:"parent:parents"`
		}

		NewMeta(&m{})
	})
}

func TestMeta(t *testing.T) {
	post := Init(&postModel{}).Meta()

	assert.Equal(t, &Meta{
		Name:       "coal.postModel",
		Collection: "posts",
		PluralName: "posts",
		Fields: map[string]*Field{
			"Title": {
				Name:      "Title",
				Type:      reflect.TypeOf(""),
				Kind:      reflect.String,
				JSONKey:   "title",
				BSONField: "title",
				index:     1,
			},
			"Published": {
				Name:      "Published",
				Type:      reflect.TypeOf(true),
				Kind:      reflect.Bool,
				JSONKey:   "published",
				BSONField: "published",
				index:     2,
			},
			"TextBody": {
				Name:      "TextBody",
				Type:      reflect.TypeOf(""),
				Kind:      reflect.String,
				JSONKey:   "text-body",
				BSONField: "text_body",
				index:     3,
			},
			"Comments": {
				Name:       "Comments",
				Type:       hasManyType,
				Kind:       reflect.Struct,
				HasMany:    true,
				RelName:    "comments",
				RelType:    "comments",
				RelInverse: "post",
				index:      4,
			},
			"Selections": {
				Name:       "Selections",
				Type:       hasManyType,
				Kind:       reflect.Struct,
				HasMany:    true,
				RelName:    "selections",
				RelType:    "selections",
				RelInverse: "posts",
				index:      5,
			},
			"Note": {
				Name:       "Note",
				Type:       hasOneType,
				Kind:       reflect.Struct,
				HasOne:     true,
				RelName:    "note",
				RelType:    "notes",
				RelInverse: "post",
				index:      6,
			},
		},
		OrderedFields: []*Field{
			post.Fields["Title"],
			post.Fields["Published"],
			post.Fields["TextBody"],
			post.Fields["Comments"],
			post.Fields["Selections"],
			post.Fields["Note"],
		},
		DatabaseFields: map[string]*Field{
			"title":     post.Fields["Title"],
			"published": post.Fields["Published"],
			"text_body": post.Fields["TextBody"],
		},
		Attributes: map[string]*Field{
			"title":     post.Fields["Title"],
			"published": post.Fields["Published"],
			"text-body": post.Fields["TextBody"],
		},
		Relationships: map[string]*Field{
			"comments":   post.Fields["Comments"],
			"selections": post.Fields["Selections"],
			"note":       post.Fields["Note"],
		},
		model: post.model,
	}, post)

	comment := Init(&commentModel{}).Meta()
	assert.Equal(t, &Meta{
		Name:       "coal.commentModel",
		Collection: "comments",
		PluralName: "comments",
		Fields: map[string]*Field{
			"Message": {
				Name:      "Message",
				Type:      reflect.TypeOf(""),
				Kind:      reflect.String,
				JSONKey:   "message",
				BSONField: "message",
				index:     1,
			},
			"Parent": {
				Name:      "Parent",
				Type:      optionalToOneType,
				Kind:      reflect.String,
				JSONKey:   "",
				BSONField: "parent",
				Optional:  true,
				ToOne:     true,
				RelName:   "parent",
				RelType:   "comments",
				index:     2,
			},
			"Post": {
				Name:      "Post",
				Type:      toOneType,
				Kind:      reflect.String,
				JSONKey:   "",
				BSONField: "post_id",
				ToOne:     true,
				RelName:   "post",
				RelType:   "posts",
				index:     3,
			},
		},
		OrderedFields: []*Field{
			comment.Fields["Message"],
			comment.Fields["Parent"],
			comment.Fields["Post"],
		},
		DatabaseFields: map[string]*Field{
			"message": comment.Fields["Message"],
			"parent":  comment.Fields["Parent"],
			"post_id": comment.Fields["Post"],
		},
		Attributes: map[string]*Field{
			"message": comment.Fields["Message"],
		},
		Relationships: map[string]*Field{
			"parent": comment.Fields["Parent"],
			"post":   comment.Fields["Post"],
		},
		model: comment.model,
	}, comment)

	selection := Init(&selectionModel{}).Meta()
	assert.Equal(t, &Meta{
		Name:       "coal.selectionModel",
		Collection: "selections",
		PluralName: "selections",
		Fields: map[string]*Field{
			"Name": {
				Name:      "Name",
				Type:      reflect.TypeOf(""),
				Kind:      reflect.String,
				JSONKey:   "name",
				BSONField: "name",
				index:     1,
			},
			"Posts": {
				Name:      "Posts",
				Type:      toManyType,
				Kind:      reflect.Slice,
				BSONField: "post_ids",
				ToMany:    true,
				RelName:   "posts",
				RelType:   "posts",
				index:     2,
			},
		},
		OrderedFields: []*Field{
			selection.Fields["Name"],
			selection.Fields["Posts"],
		},
		DatabaseFields: map[string]*Field{
			"name":     selection.Fields["Name"],
			"post_ids": selection.Fields["Posts"],
		},
		Attributes: map[string]*Field{
			"name": selection.Fields["Name"],
		},
		Relationships: map[string]*Field{
			"posts": selection.Fields["Posts"],
		},
		model: selection.model,
	}, selection)
}

func TestMetaMake(t *testing.T) {
	post := Init(&postModel{}).Meta().Make()

	assert.Equal(t, "<*coal.postModel Value>", reflect.ValueOf(post).String())
}

func TestMetaMakeSlice(t *testing.T) {
	posts := Init(&postModel{}).Meta().MakeSlice()

	assert.Equal(t, "<*[]*coal.postModel Value>", reflect.ValueOf(posts).String())
}

func BenchmarkNewMeta(b *testing.B) {
	for i := 0; i < b.N; i++ {
		NewMeta(&postModel{})
	}
}
<commit_msg>inlined types<commit_after>package coal

import (
	"reflect"
	"testing"

	"github.com/stretchr/testify/assert"
	"gopkg.in/mgo.v2/bson"
)

func TestNewMeta(t *testing.T) {
	assert.Panics(t, func() {
		type m struct {
			Base
		}

		NewMeta(&m{})
	})

	assert.Panics(t, func() {
		type m struct {
			Base `json:"-"`
		}

		NewMeta(&m{})
	})

	assert.Panics(t, func() {
		type m struct {
			Base `json:"-" bson:",inline"`
		}

		NewMeta(&m{})
	})

	assert.Panics(t, func() {
		type m struct {
			Foo  string `json:"foo"`
			Base `json:"-" bson:",inline" coal:"foo:foos" valid:"required"`
		}

		NewMeta(&m{})
	})

	assert.Panics(t, func() {
		type m struct {
			Base `json:"-" bson:",inline" coal:"foo:foos"`
			Foo  bson.ObjectId `coal:"foo:foo:foo"`
		}

		NewMeta(&m{})
	})

	assert.Panics(t, func() {
		type m struct {
			Base `json:"-" bson:",inline" coal:"foo:foos"`
			Foo  []bson.ObjectId `coal:"foo:foo:foo"`
		}

		NewMeta(&m{})
	})

	assert.Panics(t, func() {
		type m struct {
			Base `json:"-" bson:",inline" coal:"foo:foos"`
			Foo  HasOne
		}

		NewMeta(&m{})
	})

	assert.Panics(t, func() {
		type m struct {
			Base `json:"-" bson:",inline" coal:"foo:foos"`
			Foo  HasMany
		}

		NewMeta(&m{})
	})

	assert.Panics(t, func() {
		type m struct {
			Base `json:"-" bson:",inline" coal:"foo:foos"`
			Foo  string `coal:"foo"`
		}

		NewMeta(&m{})
	})

	//assert.PanicsWithValue(t, `coal: duplicate JSON key "text"`, func() {
	//	type m struct {
	//		Base  `json:"-" bson:",inline" valid:"required" coal:"ms"`
	//		Text1 string `json:"text"`
	//		Text2 string `json:"text"`
	//	}
	//
	//	NewMeta(&m{})
	//})

	assert.PanicsWithValue(t, `coal: duplicate BSON field "text"`, func() {
		type m struct {
			Base  `json:"-" bson:",inline" valid:"required" coal:"ms"`
			Text1 string `bson:"text"`
			Text2 string `bson:"text"`
		}

		NewMeta(&m{})
	})

	assert.PanicsWithValue(t, `coal: duplicate relationship "parent"`, func() {
		type m struct {
			Base    `json:"-" bson:",inline" valid:"required" coal:"ms"`
			Parent1 bson.ObjectId `valid:"object-id" coal:"parent:parents"`
			Parent2 bson.ObjectId `valid:"object-id" coal:"parent:parents"`
		}

		NewMeta(&m{})
	})
}

func TestMeta(t *testing.T) {
	post := Init(&postModel{}).Meta()

	assert.Equal(t, &Meta{
		Name:       "coal.postModel",
		Collection: "posts",
		PluralName: "posts",
		Fields: map[string]*Field{
			"Title": {
				Name:      "Title",
				Type:      reflect.TypeOf(""),
				Kind:      reflect.String,
				JSONKey:   "title",
				BSONField: "title",
				index:     1,
			},
			"Published": {
				Name:      "Published",
				Type:      reflect.TypeOf(true),
				Kind:      reflect.Bool,
				JSONKey:   "published",
				BSONField: "published",
				index:     2,
			},
			"TextBody": {
				Name:      "TextBody",
				Type:      reflect.TypeOf(""),
				Kind:      reflect.String,
				JSONKey:   "text-body",
				BSONField: "text_body",
				index:     3,
			},
			"Comments": {
				Name:       "Comments",
				Type:       hasManyType,
				Kind:       reflect.Struct,
				HasMany:    true,
				RelName:    "comments",
				RelType:    "comments",
				RelInverse: "post",
				index:      4,
			},
			"Selections": {
				Name:       "Selections",
				Type:       hasManyType,
				Kind:       reflect.Struct,
				HasMany:    true,
				RelName:    "selections",
				RelType:    "selections",
				RelInverse: "posts",
				index:      5,
			},
			"Note": {
				Name:       "Note",
				Type:       hasOneType,
				Kind:       reflect.Struct,
				HasOne:     true,
				RelName:    "note",
				RelType:    "notes",
				RelInverse: "post",
				index:      6,
			},
		},
		OrderedFields: []*Field{
			post.Fields["Title"],
			post.Fields["Published"],
			post.Fields["TextBody"],
			post.Fields["Comments"],
			post.Fields["Selections"],
			post.Fields["Note"],
		},
		DatabaseFields: map[string]*Field{
			"title":     post.Fields["Title"],
			"published": post.Fields["Published"],
			"text_body": post.Fields["TextBody"],
		},
		Attributes: map[string]*Field{
			"title":     post.Fields["Title"],
			"published": post.Fields["Published"],
			"text-body": post.Fields["TextBody"],
		},
		Relationships: map[string]*Field{
			"comments":   post.Fields["Comments"],
			"selections": post.Fields["Selections"],
			"note":       post.Fields["Note"],
		},
		model: post.model,
	}, post)

	comment := Init(&commentModel{}).Meta()
	assert.Equal(t, &Meta{
		Name:       "coal.commentModel",
		Collection: "comments",
		PluralName: "comments",
		Fields: map[string]*Field{
			"Message": {
				Name:      "Message",
				Type:      reflect.TypeOf(""),
				Kind:      reflect.String,
				JSONKey:   "message",
				BSONField: "message",
				index:     1,
			},
			"Parent": {
				Name:      "Parent",
				Type:      optionalToOneType,
				Kind:      reflect.String,
				JSONKey:   "",
				BSONField: "parent",
				Optional:  true,
				ToOne:     true,
				RelName:   "parent",
				RelType:   "comments",
				index:     2,
			},
			"Post": {
				Name:      "Post",
				Type:      toOneType,
				Kind:      reflect.String,
				JSONKey:   "",
				BSONField: "post_id",
				ToOne:     true,
				RelName:   "post",
				RelType:   "posts",
				index:     3,
			},
		},
		OrderedFields: []*Field{
			comment.Fields["Message"],
			comment.Fields["Parent"],
			comment.Fields["Post"],
		},
		DatabaseFields: map[string]*Field{
			"message": comment.Fields["Message"],
			"parent":  comment.Fields["Parent"],
			"post_id": comment.Fields["Post"],
		},
		Attributes: map[string]*Field{
			"message": comment.Fields["Message"],
		},
		Relationships: map[string]*Field{
			"parent": comment.Fields["Parent"],
			"post":   comment.Fields["Post"],
		},
		model: comment.model,
	}, comment)

	selection := Init(&selectionModel{}).Meta()
	assert.Equal(t, &Meta{
		Name:       "coal.selectionModel",
		Collection: "selections",
		PluralName: "selections",
		Fields: map[string]*Field{
			"Name": {
				Name:      "Name",
				Type:      reflect.TypeOf(""),
				Kind:      reflect.String,
				JSONKey:   "name",
				BSONField: "name",
				index:     1,
			},
			"Posts": {
				Name:      "Posts",
				Type:      toManyType,
				Kind:      reflect.Slice,
				BSONField: "post_ids",
				ToMany:    true,
				RelName:   "posts",
				RelType:   "posts",
				index:     2,
			},
		},
		OrderedFields: []*Field{
			selection.Fields["Name"],
			selection.Fields["Posts"],
		},
		DatabaseFields: map[string]*Field{
			"name":     selection.Fields["Name"],
			"post_ids": selection.Fields["Posts"],
		},
		Attributes: map[string]*Field{
			"name": selection.Fields["Name"],
		},
		Relationships: map[string]*Field{
			"posts": selection.Fields["Posts"],
		},
		model: selection.model,
	}, selection)
}

func TestMetaMake(t *testing.T) {
	post := Init(&postModel{}).Meta().Make()

	assert.Equal(t, "<*coal.postModel Value>", reflect.ValueOf(post).String())
}

func TestMetaMakeSlice(t *testing.T) {
	posts := Init(&postModel{}).Meta().MakeSlice()

	assert.Equal(t, "<*[]*coal.postModel Value>", reflect.ValueOf(posts).String())
}

func BenchmarkNewMeta(b *testing.B) {
	for i := 0; i < b.N; i++ {
		NewMeta(&postModel{})
	}
}
<|endoftext|>


<commit_before>package asix

import (
	"encoding/xml"
)

type Ledger struct {
	XMLName xml.Name `xml:"Ledger,omitempty"`

	ProductionDate RequiredDate `xml:"productionDate,attr,omitempty"`
	Invoices       Invoices     `xml:"Invoice,omitempty"`
	Credits        Credits      `xml:"Credit,omitempty"`
}

type Invoices []Invoice

type Credits []Credit

func NewLedger() *Ledger {
	return &Ledger{}
}

type Invoice struct {
	// Client Number in Asitis Finance, ask your contact at the Finance company.
	ClientNo NonEmptyCode20 `xml:"ClientNo,omitempty"`

	// (not used at this time)
	// Action             interface{}    `xml:"Action,omitempty"`

	// Customers Legal No.
	// 1234561234
	CustLegalNo Code20 `xml:"CustLegalNo"`

	// Customers No.
	// C00002
	CustNo NonEmptyCode20 `xml:"CustNo,omitempty"`

	// Customers Name
	// Albert Nobel
	Name Text50 `xml:"Name,omitempty"`

	// Customers Name, second line.
	// C/O Name
	Name2 Text50 `xml:"Name2,omitempty"`

	// Customers postal Adress.
	// Dynamic road 2
	Adress Text30 `xml:"Adress"`

	// Customers postal Adress, second line.
	Adress2 Text30 `xml:"Adress2,omitempty"`

	// Customers Postal code.
	// 54130
	PostCode Text20 `xml:"PostCode"`

	// Customers postal city.
	// Skövde
	City Text30 `xml:"City"`

	// Customers Country code.
	// SE
	CountryCode CountryCode `xml:"CountryCode"` // description: Customers Country code.

	// Invoice No.
	// F002110
	InvoiceNo NonEmptyCode20 `xml:"InvoiceNo,omitempty"`

	// Invoice Date
	// 2010-02-14
	InvoiceDate RequiredDate `xml:"InvoiceDate,omitempty"`

	// Due Date for the Invoice
	// 2010-03-14
	InvoiceDueDate RequiredDate `xml:"InvoiceDueDate,omitempty"` // description: Due Date for the Invoice

	// Total Sum of Invoice. Always positive.
	// 34521
	Amount PositiveAmount `xml:"Amount,omitempty"` // description:

	// Total VAT Amount. Always positive.
	// 8630.25
	VATAmount PositiveAmount `xml:"VATAmount"` // description:

	// Currency of Invoice
	// SEK
	Currency CurrencyType `xml:"Currency,omitempty"` // description:

	// Date when Order was made.
	// 2010-02-10
	OrderDate RequiredDate `xml:"OrderDate,omitempty"` // description:

	// Date then Order was delivered.
	// 2010-02-14
	DeliveryDate RequiredDate `xml:"DeliveryDate,omitempty"` // description:

	// Payment reference number or code.
	// 0021108
	PaymentRefNo Code30 `xml:"PaymentRefNo,omitempty"` // description:

	// Order Number.
	// 002110
	OrderNo Code20 `xml:"OrderNo"` // description:

	// Package / tracking number for delivery.
	PackageNo Text30 `xml:"PackageNo,omitempty"` // description:

	// Partial Payment Code, when payment is partial.
	PartialPaymentCode Code10 `xml:"PartialPaymentCode,omitempty"` // description:

	// Identifier for debt. collection processing.
	// INV02
	PostProcessingCode Code10 `xml:"PostProcessingCode,omitempty"` // description:

	// Indicates if the invoice shall be posted against an account or not
	InvoiceAccount InvoiceAccount `xml:"InvoiceAccount,omitempty"` // description:

	// "Your Reference" on the document
	// John Doe
	YourRef Text30 `xml:"YourRef,omitempty"` // description:

	// The Customers VAT Reg. No
	// SE554433221101
	CustVATNo Text30 `xml:"CustVATNo,omitempty"` // description:

	// The Customers Email
	// john@some.where
	Email Text80 `xml:"Email,omitempty"` // description:

	// See Lines
	Lines Lines `xml:"Line,omitempty"` // description:

	// Optional Fields

	// "Our Reference" on the document
	// Salesperson Xy
	OurRef Text30 `xml:"OurRef,omitempty"` // description:

	// The Customers Phone Number
	// 016 13 75 20
	PhoneNo Text30 `xml:"PhoneNo,omitempty"` // description:

	// How to deliver the document
	// 1
	DeliveryOption int `xml:"DeliveryOption,omitempty"` // description:

	// Global Location Number
	// 3322115544621
	GLN Code20 `xml:"GLN,omitempty"` // description:

	// Shipment Name on document
	ShipName Text50 `xml:"ShipName,omitempty"` // description:

	// Shipment Name, second line.
	// C/O Name
	ShipName2 Text50 `xml:"ShipName2,omitempty"` // description:

	// Shipment postal Adress.
	// Dynamic road 2
	ShipAdress Text30 `xml:"ShipAdress,omitempty"` // description:

	// Shipment postal Adress, second line.
	ShipAdress2 Text30 `xml:"ShipAdress2,omitempty"` // description:

	// Shipment Postal code.
	// 54130
	ShipPostCode Text20 `xml:"ShipPostCode,omitempty"` // description:

	// Shipment postal city.
	// Skövde
	ShipCity Text30 `xml:"ShipCity,omitempty"` // description:

	// Shipment Country code.
	// SE
	ShipCountryCode CountryCode `xml:"ShipCountryCode,omitempty"` // description:

	// A languagecode in the receiving system, or a decimal value from this list (1033 for English, 1053 for Swedish)
	// SV
	// 1053
	Language Code10 `xml:"Language,omitempty"` // description:

	// 0 = No payment request
	// 1 = Autogiro
	// 0
	PaymentRequestType int `xml:"PaymentRequestType,omitempty"` // description:

	// Id to be used with the selected payment request type.  For Autogiro this is 'betalarnr'
	// 438294
	PaymentRequestId Text30 `xml:"PaymentRequestId,omitempty"` // description:

	// Source System ID. Indicates that the client no. specified will be converted to the internal client no. used in Asitis Finance.  The Source System ID and mapping has to be entered in the application.
	// EXTERNAL
	SourceSystem Code20 `xml:"SourceSystem,omitempty"` // description:

	// Only for invoice
	Attachments Attachments `xml:"Attachment,omitempty"` // description:
}

type Credit struct {
	// Client Number in Asitis Finance, ask your contact at the Finance company.
	// 0050, C001
	ClientNo NonEmptyCode20 `xml:"ClientNo,omitempty"`

	// Customers Legal No.
	// 1234561234
	CustLegalNo Code20 `xml:"CustLegalNo"`

	// Customers No.
	// C00002
	CustNo NonEmptyCode20 `xml:"CustNo,omitempty"`

	// Customers Name
	// Albert Nobel
	Name Text50 `xml:"Name,omitempty"`

	// Customers Name, second line.
	// C/O Name
	Name2 Text50 `xml:"Name2,omitempty"`

	// Customers postal Adress.
	// Dynamic road 2
	Adress Text30 `xml:"Adress,omitempty"`

	// Customers postal Adress, second line.
	Adress2 Text30 `xml:"Adress2,omitempty"`

	// Customers Postal code.
	// 54130
	PostCode Text20 `xml:"PostCode,omitempty"`

	// Customers postal city.
	// Skövde
	City Text30 `xml:"City,omitempty"`

	// Customers Country code.
	// SE
	CountryCode CountryCode `xml:"CountryCode,omitempty"`

	// Credit Note No.
	// F002110
	CreditNo NonEmptyCode20 `xml:"CreditNo,omitempty"`

	// Credit Note Date
	// 2010-02-14
	CreditDate RequiredDate `xml:"CreditDate,omitempty"`

	// Due Date for the Credit
	// 2010-03-14
	CreditDueDate RequiredDate `xml:"CreditDueDate,omitempty"`

	// Ref type
	// 0
	CreditRefType CreditRefType `xml:"CreditRefType,omitempty"`

	// Ref Identifier
	// 12354
	CreditRefNo Code30 `xml:"CreditRefNo,omitempty"`

	// Total Sum of Invoice. Always positive.
	// 34521
	Amount PositiveAmount `xml:"Amount,omitempty"`

	// Total VAT Amount. Always positive.
	// 8630.25
	VATAmount PositiveAmount `xml:"VATAmount"`

	// Currency of Invoice
	// SEK
	Currency CurrencyType `xml:"Currency,omitempty"`

	// Date when Order was made.
	// 2010-02-10
	OrderDate RequiredDate `xml:"OrderDate,omitempty"`

	// Date then Order was delivered.
	// 2010-02-14
	DeliveryDate RequiredDate `xml:"RequiredDate,omitempty"`

	// Payment reference number or code.
	PaymentRefNo Code30 `xml:"PaymentRefNo,omitempty"`

	// Order Number.
	// 002110
	OrderNo Code20 `xml:"OrderNo"`

	// Package / tracking number for delivery.
	PackageNo Text30 `xml:"PackageNo,omitempty"`

	// See Lines
	Lines Lines `xml:"Line,omitempty"`
}

type InvoiceAccount struct {
	Type              int  `xml:"type,attr"`
	InterestStartDate Date `xml:"InterestStartDate,omitempty"`
	PaymentStartDate  Date `xml:"PaymentStartDate,omitempty"`
}

func (i InvoiceAccount) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	if i.Type == 0 {
		return nil
	}
	return e.EncodeElement(i, start)
}

type Lines []Line

// If the ‘DiscountPct’ is used, then it should be together with ‘UnitPrice’ in all formulas. (i.e. a positive
// ‘DiscountPct’ reduces the value of the ‘UnitPrice’ that is used in the calculation. It does not reduce the
// actual ‘UnitPrice’-value)
// Any numeric field can be negative, as long as the formulas are still valid.
// The sum of all ‘LineAmountInclVAT’ must always be positive.
type Line struct {
	// Item No, Article No.
	// 001220
	ItemNo Text20 `xml:"ItemNo"`

	// Description of Item.
	// Black Pencil
	Description Text80 `xml:"Description,omitempty"`

	// Description of Item, Second line.
	// With red line.
	Description2 Text80 `xml:"Description2,omitempty"`

	// Measurement Unit
	// M, kg, Hour
	UnitOfMeasure Text10 `xml:"UnitOfMeasure,omitempty"`

	// Quantity
	// 10
	Quantity Decimal `xml:"Quantity,omitempty"`

	// The price for one item. Excluding VAT.
	// 2.9805
	UnitPrice UnitAmount `xml:"UnitPrice,omitempty"`

	// VAT Percent for item
	// 25
	VATPct Percentage `xml:"VATPct,omitempty"`

	// VAT Amount for all Items.  7.45 Quantity * UnitPrice * (VATPct/100)
	// 7.45
	VATAmount Amount `xml:"VATAmount,omitempty"`

	// Discount in percent.
	// 0
	DiscountPct Percentage `xml:"DiscountPct,omitempty"`

	// Line Amount, Quantity * UnitPrice
	// 29.8
	LineAmountExclVAT Amount `xml:"LineAmountExclVAT,omitempty"`

	// LineAmountExclVAT + VATAmount
	// 37.25
	LineAmountInclVAT Amount `xml:"LineAmountInclVAT,omitempty"`
}

type Attachments []Attachment

type Attachment struct {
	// Namespace for attachment
	// Company A or Client A
	Realm Text50 `xml:"Realm,omitempty"`

	// File Name
	// InvoieNo_1_Attachment1.pdf
	Name Text150 `xml:"Name,omitempty"`

	// Number of pages of attachment
	// 5
	Pages int `xml:"Pages,omitempty"`
}
<commit_msg>Make type Credit mirror Invoice<commit_after>package asix

import (
	"encoding/xml"
)

type Ledger struct {
	XMLName xml.Name `xml:"Ledger,omitempty"`

	ProductionDate RequiredDate `xml:"productionDate,attr,omitempty"`
	Invoices       Invoices     `xml:"Invoice,omitempty"`
	Credits        Credits      `xml:"Credit,omitempty"`
}

type Invoices []Invoice

type Credits []Credit

func NewLedger() *Ledger {
	return &Ledger{}
}

type Invoice struct {
	// Client Number in Asitis Finance, ask your contact at the Finance company.
	ClientNo NonEmptyCode20 `xml:"ClientNo,omitempty"`

	// (not used at this time)
	// Action             interface{}    `xml:"Action,omitempty"`

	// Customers Legal No.
	// 1234561234
	CustLegalNo Code20 `xml:"CustLegalNo"`

	// Customers No.
	// C00002
	CustNo NonEmptyCode20 `xml:"CustNo,omitempty"`

	// Customers Name
	// Albert Nobel
	Name Text50 `xml:"Name,omitempty"`

	// Customers Name, second line.
	// C/O Name
	Name2 Text50 `xml:"Name2,omitempty"`

	// Customers postal Adress.
	// Dynamic road 2
	Adress Text30 `xml:"Adress"`

	// Customers postal Adress, second line.
	Adress2 Text30 `xml:"Adress2,omitempty"`

	// Customers Postal code.
	// 54130
	PostCode Text20 `xml:"PostCode"`

	// Customers postal city.
	// Skövde
	City Text30 `xml:"City"`

	// Customers Country code.
	// SE
	CountryCode CountryCode `xml:"CountryCode"` // description: Customers Country code.

	// Invoice No.
	// F002110
	InvoiceNo NonEmptyCode20 `xml:"InvoiceNo,omitempty"`

	// Invoice Date
	// 2010-02-14
	InvoiceDate RequiredDate `xml:"InvoiceDate,omitempty"`

	// Due Date for the Invoice
	// 2010-03-14
	InvoiceDueDate RequiredDate `xml:"InvoiceDueDate,omitempty"` // description: Due Date for the Invoice

	// Total Sum of Invoice. Always positive.
	// 34521
	Amount PositiveAmount `xml:"Amount,omitempty"` // description:

	// Total VAT Amount. Always positive.
	// 8630.25
	VATAmount PositiveAmount `xml:"VATAmount"` // description:

	// Currency of Invoice
	// SEK
	Currency CurrencyType `xml:"Currency,omitempty"` // description:

	// Date when Order was made.
	// 2010-02-10
	OrderDate RequiredDate `xml:"OrderDate,omitempty"` // description:

	// Date then Order was delivered.
	// 2010-02-14
	DeliveryDate RequiredDate `xml:"DeliveryDate,omitempty"` // description:

	// Payment reference number or code.
	// 0021108
	PaymentRefNo Code30 `xml:"PaymentRefNo,omitempty"` // description:

	// Order Number.
	// 002110
	OrderNo Code20 `xml:"OrderNo"` // description:

	// Package / tracking number for delivery.
	PackageNo Text30 `xml:"PackageNo,omitempty"` // description:

	// Partial Payment Code, when payment is partial.
	PartialPaymentCode Code10 `xml:"PartialPaymentCode,omitempty"` // description:

	// Identifier for debt. collection processing.
	// INV02
	PostProcessingCode Code10 `xml:"PostProcessingCode,omitempty"` // description:

	// Indicates if the invoice shall be posted against an account or not
	InvoiceAccount InvoiceAccount `xml:"InvoiceAccount,omitempty"` // description:

	// "Your Reference" on the document
	// John Doe
	YourRef Text30 `xml:"YourRef,omitempty"` // description:

	// The Customers VAT Reg. No
	// SE554433221101
	CustVATNo Text30 `xml:"CustVATNo,omitempty"` // description:

	// The Customers Email
	// john@some.where
	Email Text80 `xml:"Email,omitempty"` // description:

	// See Lines
	Lines Lines `xml:"Line,omitempty"` // description:

	// Optional Fields

	// "Our Reference" on the document
	// Salesperson Xy
	OurRef Text30 `xml:"OurRef,omitempty"` // description:

	// The Customers Phone Number
	// 016 13 75 20
	PhoneNo Text30 `xml:"PhoneNo,omitempty"` // description:

	// How to deliver the document
	// 1
	DeliveryOption int `xml:"DeliveryOption,omitempty"` // description:

	// Global Location Number
	// 3322115544621
	GLN Code20 `xml:"GLN,omitempty"` // description:

	// Shipment Name on document
	ShipName Text50 `xml:"ShipName,omitempty"` // description:

	// Shipment Name, second line.
	// C/O Name
	ShipName2 Text50 `xml:"ShipName2,omitempty"` // description:

	// Shipment postal Adress.
	// Dynamic road 2
	ShipAdress Text30 `xml:"ShipAdress,omitempty"` // description:

	// Shipment postal Adress, second line.
	ShipAdress2 Text30 `xml:"ShipAdress2,omitempty"` // description:

	// Shipment Postal code.
	// 54130
	ShipPostCode Text20 `xml:"ShipPostCode,omitempty"` // description:

	// Shipment postal city.
	// Skövde
	ShipCity Text30 `xml:"ShipCity,omitempty"` // description:

	// Shipment Country code.
	// SE
	ShipCountryCode CountryCode `xml:"ShipCountryCode,omitempty"` // description:

	// A languagecode in the receiving system, or a decimal value from this list (1033 for English, 1053 for Swedish)
	// SV
	// 1053
	Language Code10 `xml:"Language,omitempty"` // description:

	// 0 = No payment request
	// 1 = Autogiro
	// 0
	PaymentRequestType int `xml:"PaymentRequestType,omitempty"` // description:

	// Id to be used with the selected payment request type.  For Autogiro this is 'betalarnr'
	// 438294
	PaymentRequestId Text30 `xml:"PaymentRequestId,omitempty"` // description:

	// Source System ID. Indicates that the client no. specified will be converted to the internal client no. used in Asitis Finance.  The Source System ID and mapping has to be entered in the application.
	// EXTERNAL
	SourceSystem Code20 `xml:"SourceSystem,omitempty"` // description:

	// Only for invoice
	Attachments Attachments `xml:"Attachment,omitempty"` // description:
}

type Credit struct {
	// Client Number in Asitis Finance, ask your contact at the Finance company.
	// 0050, C001
	ClientNo NonEmptyCode20 `xml:"ClientNo,omitempty"`

	// Customers Legal No.
	// 1234561234
	CustLegalNo Code20 `xml:"CustLegalNo"`

	// Customers No.
	// C00002
	CustNo NonEmptyCode20 `xml:"CustNo,omitempty"`

	// Customers Name
	// Albert Nobel
	Name Text50 `xml:"Name,omitempty"`

	// Customers Name, second line.
	// C/O Name
	Name2 Text50 `xml:"Name2,omitempty"`

	// Customers postal Adress.
	// Dynamic road 2
	Adress Text30 `xml:"Adress"`

	// Customers postal Adress, second line.
	Adress2 Text30 `xml:"Adress2,omitempty"`

	// Customers Postal code.
	// 54130
	PostCode Text20 `xml:"PostCode"`

	// Customers postal city.
	// Skövde
	City Text30 `xml:"City"`

	// Customers Country code.
	// SE
	CountryCode CountryCode `xml:"CountryCode"`

	// Credit Note No.
	// F002110
	CreditNo NonEmptyCode20 `xml:"CreditNo,omitempty"`

	// Credit Note Date
	// 2010-02-14
	CreditDate RequiredDate `xml:"CreditDate,omitempty"`

	// Due Date for the Credit
	// 2010-03-14
	CreditDueDate RequiredDate `xml:"CreditDueDate,omitempty"`

	// Ref type
	// 0
	CreditRefType CreditRefType `xml:"CreditRefType,omitempty"`

	// Ref Identifier
	// 12354
	CreditRefNo Code30 `xml:"CreditRefNo,omitempty"`

	// Total Sum of Invoice. Always positive.
	// 34521
	Amount PositiveAmount `xml:"Amount,omitempty"`

	// Total VAT Amount. Always positive.
	// 8630.25
	VATAmount PositiveAmount `xml:"VATAmount"`

	// Currency of Invoice
	// SEK
	Currency CurrencyType `xml:"Currency,omitempty"`

	// Date when Order was made.
	// 2010-02-10
	OrderDate RequiredDate `xml:"OrderDate,omitempty"`

	// Date then Order was delivered.
	// 2010-02-14
	DeliveryDate RequiredDate `xml:"DeliverDate,omitempty"`

	// Payment reference number or code.
	PaymentRefNo Code30 `xml:"PaymentRefNo,omitempty"`

	// Order Number.
	// 002110
	OrderNo Code20 `xml:"OrderNo"`

	// Package / tracking number for delivery.
	PackageNo Text30 `xml:"PackageNo,omitempty"`

	// See Lines
	Lines Lines `xml:"Line,omitempty"`
}

type InvoiceAccount struct {
	Type              int  `xml:"type,attr"`
	InterestStartDate Date `xml:"InterestStartDate,omitempty"`
	PaymentStartDate  Date `xml:"PaymentStartDate,omitempty"`
}

func (i InvoiceAccount) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	if i.Type == 0 {
		return nil
	}
	return e.EncodeElement(i, start)
}

type Lines []Line

// If the ‘DiscountPct’ is used, then it should be together with ‘UnitPrice’ in all formulas. (i.e. a positive
// ‘DiscountPct’ reduces the value of the ‘UnitPrice’ that is used in the calculation. It does not reduce the
// actual ‘UnitPrice’-value)
// Any numeric field can be negative, as long as the formulas are still valid.
// The sum of all ‘LineAmountInclVAT’ must always be positive.
type Line struct {
	// Item No, Article No.
	// 001220
	ItemNo Text20 `xml:"ItemNo"`

	// Description of Item.
	// Black Pencil
	Description Text80 `xml:"Description,omitempty"`

	// Description of Item, Second line.
	// With red line.
	Description2 Text80 `xml:"Description2,omitempty"`

	// Measurement Unit
	// M, kg, Hour
	UnitOfMeasure Text10 `xml:"UnitOfMeasure,omitempty"`

	// Quantity
	// 10
	Quantity Decimal `xml:"Quantity,omitempty"`

	// The price for one item. Excluding VAT.
	// 2.9805
	UnitPrice UnitAmount `xml:"UnitPrice,omitempty"`

	// VAT Percent for item
	// 25
	VATPct Percentage `xml:"VATPct,omitempty"`

	// VAT Amount for all Items.  7.45 Quantity * UnitPrice * (VATPct/100)
	// 7.45
	VATAmount Amount `xml:"VATAmount,omitempty"`

	// Discount in percent.
	// 0
	DiscountPct Percentage `xml:"DiscountPct,omitempty"`

	// Line Amount, Quantity * UnitPrice
	// 29.8
	LineAmountExclVAT Amount `xml:"LineAmountExclVAT,omitempty"`

	// LineAmountExclVAT + VATAmount
	// 37.25
	LineAmountInclVAT Amount `xml:"LineAmountInclVAT,omitempty"`
}

type Attachments []Attachment

type Attachment struct {
	// Namespace for attachment
	// Company A or Client A
	Realm Text50 `xml:"Realm,omitempty"`

	// File Name
	// InvoieNo_1_Attachment1.pdf
	Name Text150 `xml:"Name,omitempty"`

	// Number of pages of attachment
	// 5
	Pages int `xml:"Pages,omitempty"`
}
<|endoftext|>


<commit_before>package asset

import (
	"context"
	"encoding/json"
	"strings"
	"sync"

	"github.com/golang/groupcache/lru"
	dbm "github.com/tendermint/tmlibs/db"
	"golang.org/x/crypto/sha3"

	"github.com/bytom/blockchain/signers"
	"github.com/bytom/common"
	"github.com/bytom/consensus"
	"github.com/bytom/crypto/ed25519"
	"github.com/bytom/crypto/ed25519/chainkd"
	chainjson "github.com/bytom/encoding/json"
	"github.com/bytom/errors"
	"github.com/bytom/protocol"
	"github.com/bytom/protocol/bc"
	"github.com/bytom/protocol/vm/vmutil"
)

// DefaultNativeAsset native BTM asset
var DefaultNativeAsset *Asset

const (
	maxAssetCache = 1000
)

var (
	assetIndexKey  = []byte("AssetIndex")
	assetPrefix    = []byte("Asset:")
	aliasPrefix    = []byte("AssetAlias:")
	extAssetPrefix = []byte("EXA:")
)

func initNativeAsset() {
	signer := &signers.Signer{Type: "internal"}
	alias := consensus.BTMAlias

	definitionBytes, _ := serializeAssetDef(consensus.BTMDefinitionMap)
	DefaultNativeAsset = &Asset{
		Signer:            signer,
		AssetID:           *consensus.BTMAssetID,
		Alias:             &alias,
		VMVersion:         1,
		DefinitionMap:     consensus.BTMDefinitionMap,
		RawDefinitionByte: definitionBytes,
	}
}

// AliasKey store asset alias prefix
func aliasKey(name string) []byte {
	return append(aliasPrefix, []byte(name)...)
}

// Key store asset prefix
func Key(id *bc.AssetID) []byte {
	return append(assetPrefix, id.Bytes()...)
}

// ExtAssetKey return store external assets key
func ExtAssetKey(id *bc.AssetID) []byte {
	return append(extAssetPrefix, id.Bytes()...)
}

// pre-define errors for supporting bytom errorFormatter
var (
	ErrDuplicateAlias = errors.New("duplicate asset alias")
	ErrDuplicateAsset = errors.New("duplicate asset id")
	ErrSerializing    = errors.New("serializing asset definition")
	ErrMarshalAsset   = errors.New("failed marshal asset")
	ErrFindAsset      = errors.New("fail to find asset")
	ErrInternalAsset  = errors.New("btm has been defined as the internal asset")
	ErrNullAlias      = errors.New("null asset alias")
)

//NewRegistry create new registry
func NewRegistry(db dbm.DB, chain *protocol.Chain) *Registry {
	initNativeAsset()
	return &Registry{
		db:         db,
		chain:      chain,
		cache:      lru.New(maxAssetCache),
		aliasCache: lru.New(maxAssetCache),
	}
}

// Registry tracks and stores all known assets on a blockchain.
type Registry struct {
	db    dbm.DB
	chain *protocol.Chain

	cacheMu    sync.Mutex
	cache      *lru.Cache
	aliasCache *lru.Cache

	assetIndexMu sync.Mutex
	assetMu      sync.Mutex
}

//Asset describe asset on bytom chain
type Asset struct {
	*signers.Signer
	AssetID           bc.AssetID             `json:"id"`
	Alias             *string                `json:"alias"`
	VMVersion         uint64                 `json:"vm_version"`
	IssuanceProgram   chainjson.HexBytes     `json:"issue_program"`
	RawDefinitionByte chainjson.HexBytes     `json:"raw_definition_byte"`
	DefinitionMap     map[string]interface{} `json:"definition"`
}

func (reg *Registry) getNextAssetIndex() uint64 {
	reg.assetIndexMu.Lock()
	defer reg.assetIndexMu.Unlock()

	nextIndex := uint64(1)
	if rawIndex := reg.db.Get(assetIndexKey); rawIndex != nil {
		nextIndex = common.BytesToUnit64(rawIndex) + 1
	}

	reg.db.Set(assetIndexKey, common.Unit64ToBytes(nextIndex))
	return nextIndex
}

// Define defines a new Asset.
func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[string]interface{}, alias string) (*Asset, error) {
	if len(xpubs) == 0 {
		return nil, errors.Wrap(signers.ErrNoXPubs)
	}

	alias = strings.ToUpper(strings.TrimSpace(alias))
	if alias == "" {
		return nil, errors.Wrap(ErrNullAlias)
	}

	if alias == consensus.BTMAlias {
		return nil, ErrInternalAsset
	}

	nextAssetIndex := reg.getNextAssetIndex()
	assetSigner, err := signers.Create("asset", xpubs, quorum, nextAssetIndex)
	if err != nil {
		return nil, err
	}

	rawDefinition, err := serializeAssetDef(definition)
	if err != nil {
		return nil, ErrSerializing
	}

	path := signers.Path(assetSigner, signers.AssetKeySpace)
	derivedXPubs := chainkd.DeriveXPubs(assetSigner.XPubs, path)
	derivedPKs := chainkd.XPubKeys(derivedXPubs)
	issuanceProgram, vmver, err := multisigIssuanceProgram(derivedPKs, assetSigner.Quorum)
	if err != nil {
		return nil, err
	}

	defHash := bc.NewHash(sha3.Sum256(rawDefinition))
	a := &Asset{
		DefinitionMap:     definition,
		RawDefinitionByte: rawDefinition,
		VMVersion:         vmver,
		IssuanceProgram:   issuanceProgram,
		AssetID:           bc.ComputeAssetID(issuanceProgram, vmver, &defHash),
		Signer:            assetSigner,
		Alias:             &alias,
	}
	return a, reg.SaveAsset(a, alias)
}

// SaveAsset store asset
func (reg *Registry) SaveAsset(a *Asset, alias string) error {
	reg.assetMu.Lock()
	defer reg.assetMu.Unlock()

	aliasKey := aliasKey(alias)
	if existed := reg.db.Get(aliasKey); existed != nil {
		return ErrDuplicateAlias
	}

	assetKey := Key(&a.AssetID)
	if existAsset := reg.db.Get(assetKey); existAsset != nil {
		return ErrDuplicateAsset
	}

	rawAsset, err := json.Marshal(a)
	if err != nil {
		return ErrMarshalAsset
	}

	storeBatch := reg.db.NewBatch()
	storeBatch.Set(aliasKey, []byte(a.AssetID.String()))
	storeBatch.Set(assetKey, rawAsset)
	storeBatch.Write()
	return nil
}

// FindByID retrieves an Asset record along with its signer, given an assetID.
func (reg *Registry) FindByID(ctx context.Context, id *bc.AssetID) (*Asset, error) {
	reg.cacheMu.Lock()
	cached, ok := reg.cache.Get(id.String())
	reg.cacheMu.Unlock()
	if ok {
		return cached.(*Asset), nil
	}

	bytes := reg.db.Get(Key(id))
	if bytes == nil {
		return nil, ErrFindAsset
	}

	asset := &Asset{}
	if err := json.Unmarshal(bytes, asset); err != nil {
		return nil, err
	}

	reg.cacheMu.Lock()
	reg.cache.Add(id.String(), asset)
	reg.cacheMu.Unlock()
	return asset, nil
}

// FindByAlias retrieves an Asset record along with its signer,
// given an asset alias.
func (reg *Registry) FindByAlias(alias string) (*Asset, error) {
	reg.cacheMu.Lock()
	cachedID, ok := reg.aliasCache.Get(alias)
	reg.cacheMu.Unlock()
	if ok {
		return reg.FindByID(nil, cachedID.(*bc.AssetID))
	}

	rawID := reg.db.Get(aliasKey(alias))
	if rawID == nil {
		return nil, errors.Wrapf(ErrFindAsset, "no such asset, alias: %s", alias)
	}

	assetID := &bc.AssetID{}
	if err := assetID.UnmarshalText(rawID); err != nil {
		return nil, err
	}

	reg.cacheMu.Lock()
	reg.aliasCache.Add(alias, assetID)
	reg.cacheMu.Unlock()
	return reg.FindByID(nil, assetID)
}

//GetAliasByID return asset alias string by AssetID string
func (reg *Registry) GetAliasByID(id string) string {
	//btm
	if id == consensus.BTMAssetID.String() {
		return consensus.BTMAlias
	}

	assetID := &bc.AssetID{}
	if err := assetID.UnmarshalText([]byte(id)); err != nil {
		return ""
	}

	asset, err := reg.FindByID(nil, assetID)
	if err != nil {
		return ""
	}

	return *asset.Alias
}

// GetAsset get asset by assetID
func (reg *Registry) GetAsset(id string) (*Asset, error) {
	var assetID bc.AssetID
	if err := assetID.UnmarshalText([]byte(id)); err != nil {
		return nil, err
	}

	if assetID.String() == DefaultNativeAsset.AssetID.String() {
		return DefaultNativeAsset, nil
	}

	asset := &Asset{}
	if interAsset := reg.db.Get(Key(&assetID)); interAsset != nil {
		if err := json.Unmarshal(interAsset, asset); err != nil {
			return nil, err
		}
		return asset, nil
	}

	if extAsset := reg.db.Get(ExtAssetKey(&assetID)); extAsset != nil {
		if err := json.Unmarshal(extAsset, asset); err != nil {
			return nil, err
		}
		return asset, nil
	}

	return nil, errors.WithDetailf(ErrFindAsset, "no such asset, assetID: %s", id)
}

// ListAssets returns the accounts in the db
func (reg *Registry) ListAssets(id string) ([]*Asset, error) {
	assets := []*Asset{DefaultNativeAsset}

	assetIDStr := strings.TrimSpace(id)
	if strings.Compare(assetIDStr, DefaultNativeAsset.AssetID.String()) == 0 {
		return assets, nil
	}

	if strings.Compare(assetIDStr, "") != 0 {
		assetID := &bc.AssetID{}
		if err := assetID.UnmarshalText([]byte(assetIDStr)); err != nil {
			return nil, err
		}

		asset := &Asset{}
		interAsset := reg.db.Get(Key(assetID))
		if interAsset != nil {
			if err := json.Unmarshal(interAsset, asset); err != nil {
				return nil, err
			}
			return []*Asset{asset}, nil
		}

		return []*Asset{}, nil
	}

	assetIter := reg.db.IteratorPrefix(assetPrefix)
	defer assetIter.Release()

	for assetIter.Next() {
		asset := &Asset{}
		if err := json.Unmarshal(assetIter.Value(), asset); err != nil {
			return nil, err
		}
		assets = append(assets, asset)
	}

	return assets, nil
}

// serializeAssetDef produces a canonical byte representation of an asset
// definition. Currently, this is implemented using pretty-printed JSON.
// As is the standard for Go's map[string] serialization, object keys will
// appear in lexicographic order. Although this is mostly meant for machine
// consumption, the JSON is pretty-printed for easy reading.
func serializeAssetDef(def map[string]interface{}) ([]byte, error) {
	if def == nil {
		def = make(map[string]interface{}, 0)
	}
	return json.MarshalIndent(def, "", "  ")
}

func multisigIssuanceProgram(pubkeys []ed25519.PublicKey, nrequired int) (program []byte, vmversion uint64, err error) {
	issuanceProg, err := vmutil.P2SPMultiSigProgram(pubkeys, nrequired)
	if err != nil {
		return nil, 0, err
	}
	builder := vmutil.NewBuilder()
	builder.AddRawBytes(issuanceProg)
	prog, err := builder.Build()
	return prog, 1, err
}

//UpdateAssetAlias updates asset alias
func (reg *Registry) UpdateAssetAlias(id, newAlias string) error {
	oldAlias := reg.GetAliasByID(id)
	newAlias = strings.ToUpper(strings.TrimSpace(newAlias))

	if oldAlias == consensus.BTMAlias || newAlias == consensus.BTMAlias {
		return ErrInternalAsset
	}

	if oldAlias == "" || newAlias == "" {
		return ErrNullAlias
	}

	reg.assetMu.Lock()
	defer reg.assetMu.Unlock()

	if _, err := reg.FindByAlias(newAlias); err == nil {
		return ErrDuplicateAlias
	}

	findAsset, err := reg.FindByAlias(oldAlias)
	if err != nil {
		return err
	}

	storeBatch := reg.db.NewBatch()
	findAsset.Alias = &newAlias
	assetID := &findAsset.AssetID
	rawAsset, err := json.Marshal(findAsset)
	if err != nil {
		return err
	}

	storeBatch.Set(Key(assetID), rawAsset)
	storeBatch.Set(aliasKey(newAlias), []byte(assetID.String()))
	storeBatch.Delete(aliasKey(oldAlias))
	storeBatch.Write()

	reg.cacheMu.Lock()
	reg.aliasCache.Add(newAlias, assetID)
	reg.aliasCache.Remove(oldAlias)
	reg.cacheMu.Unlock()

	return nil
}
<commit_msg>optimise<commit_after>package asset

import (
	"context"
	"encoding/json"
	"strings"
	"sync"

	"github.com/golang/groupcache/lru"
	dbm "github.com/tendermint/tmlibs/db"
	"golang.org/x/crypto/sha3"

	"github.com/bytom/blockchain/signers"
	"github.com/bytom/common"
	"github.com/bytom/consensus"
	"github.com/bytom/crypto/ed25519"
	"github.com/bytom/crypto/ed25519/chainkd"
	chainjson "github.com/bytom/encoding/json"
	"github.com/bytom/errors"
	"github.com/bytom/protocol"
	"github.com/bytom/protocol/bc"
	"github.com/bytom/protocol/vm/vmutil"
)

// DefaultNativeAsset native BTM asset
var DefaultNativeAsset *Asset

const (
	maxAssetCache = 1000
)

var (
	assetIndexKey  = []byte("AssetIndex")
	assetPrefix    = []byte("Asset:")
	aliasPrefix    = []byte("AssetAlias:")
	extAssetPrefix = []byte("EXA:")
)

func initNativeAsset() {
	signer := &signers.Signer{Type: "internal"}
	alias := consensus.BTMAlias

	definitionBytes, _ := serializeAssetDef(consensus.BTMDefinitionMap)
	DefaultNativeAsset = &Asset{
		Signer:            signer,
		AssetID:           *consensus.BTMAssetID,
		Alias:             &alias,
		VMVersion:         1,
		DefinitionMap:     consensus.BTMDefinitionMap,
		RawDefinitionByte: definitionBytes,
	}
}

// AliasKey store asset alias prefix
func aliasKey(name string) []byte {
	return append(aliasPrefix, []byte(name)...)
}

// Key store asset prefix
func Key(id *bc.AssetID) []byte {
	return append(assetPrefix, id.Bytes()...)
}

// ExtAssetKey return store external assets key
func ExtAssetKey(id *bc.AssetID) []byte {
	return append(extAssetPrefix, id.Bytes()...)
}

// pre-define errors for supporting bytom errorFormatter
var (
	ErrDuplicateAlias = errors.New("duplicate asset alias")
	ErrDuplicateAsset = errors.New("duplicate asset id")
	ErrSerializing    = errors.New("serializing asset definition")
	ErrMarshalAsset   = errors.New("failed marshal asset")
	ErrFindAsset      = errors.New("fail to find asset")
	ErrInternalAsset  = errors.New("btm has been defined as the internal asset")
	ErrNullAlias      = errors.New("null asset alias")
)

//NewRegistry create new registry
func NewRegistry(db dbm.DB, chain *protocol.Chain) *Registry {
	initNativeAsset()
	return &Registry{
		db:         db,
		chain:      chain,
		cache:      lru.New(maxAssetCache),
		aliasCache: lru.New(maxAssetCache),
	}
}

// Registry tracks and stores all known assets on a blockchain.
type Registry struct {
	db    dbm.DB
	chain *protocol.Chain

	cacheMu    sync.Mutex
	cache      *lru.Cache
	aliasCache *lru.Cache

	assetIndexMu sync.Mutex
	assetMu      sync.Mutex
}

//Asset describe asset on bytom chain
type Asset struct {
	*signers.Signer
	AssetID           bc.AssetID             `json:"id"`
	Alias             *string                `json:"alias"`
	VMVersion         uint64                 `json:"vm_version"`
	IssuanceProgram   chainjson.HexBytes     `json:"issue_program"`
	RawDefinitionByte chainjson.HexBytes     `json:"raw_definition_byte"`
	DefinitionMap     map[string]interface{} `json:"definition"`
}

func (reg *Registry) getNextAssetIndex() uint64 {
	reg.assetIndexMu.Lock()
	defer reg.assetIndexMu.Unlock()

	nextIndex := uint64(1)
	if rawIndex := reg.db.Get(assetIndexKey); rawIndex != nil {
		nextIndex = common.BytesToUnit64(rawIndex) + 1
	}

	reg.db.Set(assetIndexKey, common.Unit64ToBytes(nextIndex))
	return nextIndex
}

// Define defines a new Asset.
func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[string]interface{}, alias string) (*Asset, error) {
	if len(xpubs) == 0 {
		return nil, errors.Wrap(signers.ErrNoXPubs)
	}

	alias = strings.ToUpper(strings.TrimSpace(alias))
	if alias == "" {
		return nil, errors.Wrap(ErrNullAlias)
	}

	if alias == consensus.BTMAlias {
		return nil, ErrInternalAsset
	}

	nextAssetIndex := reg.getNextAssetIndex()
	assetSigner, err := signers.Create("asset", xpubs, quorum, nextAssetIndex)
	if err != nil {
		return nil, err
	}

	rawDefinition, err := serializeAssetDef(definition)
	if err != nil {
		return nil, ErrSerializing
	}

	path := signers.Path(assetSigner, signers.AssetKeySpace)
	derivedXPubs := chainkd.DeriveXPubs(assetSigner.XPubs, path)
	derivedPKs := chainkd.XPubKeys(derivedXPubs)
	issuanceProgram, vmver, err := multisigIssuanceProgram(derivedPKs, assetSigner.Quorum)
	if err != nil {
		return nil, err
	}

	defHash := bc.NewHash(sha3.Sum256(rawDefinition))
	a := &Asset{
		DefinitionMap:     definition,
		RawDefinitionByte: rawDefinition,
		VMVersion:         vmver,
		IssuanceProgram:   issuanceProgram,
		AssetID:           bc.ComputeAssetID(issuanceProgram, vmver, &defHash),
		Signer:            assetSigner,
		Alias:             &alias,
	}
	return a, reg.SaveAsset(a, alias)
}

// SaveAsset store asset
func (reg *Registry) SaveAsset(a *Asset, alias string) error {
	reg.assetMu.Lock()
	defer reg.assetMu.Unlock()

	aliasKey := aliasKey(alias)
	if existed := reg.db.Get(aliasKey); existed != nil {
		return ErrDuplicateAlias
	}

	assetKey := Key(&a.AssetID)
	if existAsset := reg.db.Get(assetKey); existAsset != nil {
		return ErrDuplicateAsset
	}

	rawAsset, err := json.Marshal(a)
	if err != nil {
		return ErrMarshalAsset
	}

	storeBatch := reg.db.NewBatch()
	storeBatch.Set(aliasKey, []byte(a.AssetID.String()))
	storeBatch.Set(assetKey, rawAsset)
	storeBatch.Write()
	return nil
}

// FindByID retrieves an Asset record along with its signer, given an assetID.
func (reg *Registry) FindByID(ctx context.Context, id *bc.AssetID) (*Asset, error) {
	reg.cacheMu.Lock()
	cached, ok := reg.cache.Get(id.String())
	reg.cacheMu.Unlock()
	if ok {
		return cached.(*Asset), nil
	}

	bytes := reg.db.Get(Key(id))
	if bytes == nil {
		return nil, ErrFindAsset
	}

	asset := &Asset{}
	if err := json.Unmarshal(bytes, asset); err != nil {
		return nil, err
	}

	reg.cacheMu.Lock()
	reg.cache.Add(id.String(), asset)
	reg.cacheMu.Unlock()
	return asset, nil
}

// FindByAlias retrieves an Asset record along with its signer,
// given an asset alias.
func (reg *Registry) FindByAlias(alias string) (*Asset, error) {
	reg.cacheMu.Lock()
	cachedID, ok := reg.aliasCache.Get(alias)
	reg.cacheMu.Unlock()
	if ok {
		return reg.FindByID(nil, cachedID.(*bc.AssetID))
	}

	rawID := reg.db.Get(aliasKey(alias))
	if rawID == nil {
		return nil, errors.Wrapf(ErrFindAsset, "no such asset, alias: %s", alias)
	}

	assetID := &bc.AssetID{}
	if err := assetID.UnmarshalText(rawID); err != nil {
		return nil, err
	}

	reg.cacheMu.Lock()
	reg.aliasCache.Add(alias, assetID)
	reg.cacheMu.Unlock()
	return reg.FindByID(nil, assetID)
}

//GetAliasByID return asset alias string by AssetID string
func (reg *Registry) GetAliasByID(id string) string {
	//btm
	if id == consensus.BTMAssetID.String() {
		return consensus.BTMAlias
	}

	assetID := &bc.AssetID{}
	if err := assetID.UnmarshalText([]byte(id)); err != nil {
		return ""
	}

	asset, err := reg.FindByID(nil, assetID)
	if err != nil {
		return ""
	}

	return *asset.Alias
}

// GetAsset get asset by assetID
func (reg *Registry) GetAsset(id string) (*Asset, error) {
	var assetID bc.AssetID
	if err := assetID.UnmarshalText([]byte(id)); err != nil {
		return nil, err
	}

	if assetID.String() == DefaultNativeAsset.AssetID.String() {
		return DefaultNativeAsset, nil
	}

	asset := &Asset{}
	if interAsset := reg.db.Get(Key(&assetID)); interAsset != nil {
		if err := json.Unmarshal(interAsset, asset); err != nil {
			return nil, err
		}
		return asset, nil
	}

	if extAsset := reg.db.Get(ExtAssetKey(&assetID)); extAsset != nil {
		if err := json.Unmarshal(extAsset, asset); err != nil {
			return nil, err
		}
		return asset, nil
	}

	return nil, errors.WithDetailf(ErrFindAsset, "no such asset, assetID: %s", id)
}

// ListAssets returns the accounts in the db
func (reg *Registry) ListAssets(id string) ([]*Asset, error) {
	assets := []*Asset{DefaultNativeAsset}

	assetIDStr := strings.TrimSpace(id)
	if assetIDStr == DefaultNativeAsset.AssetID.String() {
		return assets, nil
	}

	if assetIDStr == "" {
		assetID := &bc.AssetID{}
		if err := assetID.UnmarshalText([]byte(assetIDStr)); err != nil {
			return nil, err
		}

		asset := &Asset{}
		interAsset := reg.db.Get(Key(assetID))
		if interAsset != nil {
			if err := json.Unmarshal(interAsset, asset); err != nil {
				return nil, err
			}
			return []*Asset{asset}, nil
		}

		return []*Asset{}, nil
	}

	assetIter := reg.db.IteratorPrefix(assetPrefix)
	defer assetIter.Release()

	for assetIter.Next() {
		asset := &Asset{}
		if err := json.Unmarshal(assetIter.Value(), asset); err != nil {
			return nil, err
		}
		assets = append(assets, asset)
	}

	return assets, nil
}

// serializeAssetDef produces a canonical byte representation of an asset
// definition. Currently, this is implemented using pretty-printed JSON.
// As is the standard for Go's map[string] serialization, object keys will
// appear in lexicographic order. Although this is mostly meant for machine
// consumption, the JSON is pretty-printed for easy reading.
func serializeAssetDef(def map[string]interface{}) ([]byte, error) {
	if def == nil {
		def = make(map[string]interface{}, 0)
	}
	return json.MarshalIndent(def, "", "  ")
}

func multisigIssuanceProgram(pubkeys []ed25519.PublicKey, nrequired int) (program []byte, vmversion uint64, err error) {
	issuanceProg, err := vmutil.P2SPMultiSigProgram(pubkeys, nrequired)
	if err != nil {
		return nil, 0, err
	}
	builder := vmutil.NewBuilder()
	builder.AddRawBytes(issuanceProg)
	prog, err := builder.Build()
	return prog, 1, err
}

//UpdateAssetAlias updates asset alias
func (reg *Registry) UpdateAssetAlias(id, newAlias string) error {
	oldAlias := reg.GetAliasByID(id)
	newAlias = strings.ToUpper(strings.TrimSpace(newAlias))

	if oldAlias == consensus.BTMAlias || newAlias == consensus.BTMAlias {
		return ErrInternalAsset
	}

	if oldAlias == "" || newAlias == "" {
		return ErrNullAlias
	}

	reg.assetMu.Lock()
	defer reg.assetMu.Unlock()

	if _, err := reg.FindByAlias(newAlias); err == nil {
		return ErrDuplicateAlias
	}

	findAsset, err := reg.FindByAlias(oldAlias)
	if err != nil {
		return err
	}

	storeBatch := reg.db.NewBatch()
	findAsset.Alias = &newAlias
	assetID := &findAsset.AssetID
	rawAsset, err := json.Marshal(findAsset)
	if err != nil {
		return err
	}

	storeBatch.Set(Key(assetID), rawAsset)
	storeBatch.Set(aliasKey(newAlias), []byte(assetID.String()))
	storeBatch.Delete(aliasKey(oldAlias))
	storeBatch.Write()

	reg.cacheMu.Lock()
	reg.aliasCache.Add(newAlias, assetID)
	reg.aliasCache.Remove(oldAlias)
	reg.cacheMu.Unlock()

	return nil
}
<|endoftext|>