// Account is structure of Bytom account
type Account struct {
*signers.Signer
+ ID string `json:"id"`
Alias string `json:"alias"`
Tags map[string]interface{} `json:"tags"`
}
return nil, ErrDuplicateAlias
}
- signer, err := signers.Create(ctx, m.db, "account", xpubs, quorum, accessToken)
+ id, signer, err := signers.Create(ctx, m.db, "account", xpubs, quorum, accessToken)
if err != nil {
return nil, errors.Wrap(err)
}
- account := &Account{Signer: signer, Alias: alias, Tags: tags}
+ account := &Account{Signer: signer, ID: id, Alias: alias, Tags: tags}
rawAccount, err := json.Marshal(account)
if err != nil {
return nil, ErrMarshalAccount
}
storeBatch := m.db.NewBatch()
- accountID := Key(signer.ID)
+ accountID := Key(id)
storeBatch.Set(accountID, rawAccount)
- storeBatch.Set(aliasKey(alias), []byte(signer.ID))
+ storeBatch.Set(aliasKey(alias), []byte(id))
storeBatch.Write()
return account, nil
}
// FindByAlias retrieves an account's Signer record by its alias
-func (m *Manager) FindByAlias(ctx context.Context, alias string) (*signers.Signer, error) {
+func (m *Manager) FindByAlias(ctx context.Context, alias string) (*Account, error) {
m.cacheMu.Lock()
cachedID, ok := m.aliasCache.Get(alias)
m.cacheMu.Unlock()
}
// findByID returns an account's Signer record by its ID.
-func (m *Manager) findByID(ctx context.Context, id string) (*signers.Signer, error) {
+func (m *Manager) findByID(ctx context.Context, id string) (*Account, error) {
m.cacheMu.Lock()
- cachedSigner, ok := m.cache.Get(id)
+ cachedAccount, ok := m.cache.Get(id)
m.cacheMu.Unlock()
if ok {
- return cachedSigner.(*signers.Signer), nil
+ return cachedAccount.(*Account), nil
}
rawAccount := m.db.Get(Key(id))
return nil, ErrFindAccount
}
- var account Account
- if err := json.Unmarshal(rawAccount, &account); err != nil {
+ account := &Account{}
+ if err := json.Unmarshal(rawAccount, account); err != nil {
return nil, err
}
m.cacheMu.Lock()
- m.cache.Add(id, account.Signer)
+ m.cache.Add(id, account)
m.cacheMu.Unlock()
- return account.Signer, nil
+ return account, nil
}
// GetAliasByID return the account alias by given ID
return cp, nil
}
-func (m *Manager) createP2PKH(ctx context.Context, account *signers.Signer, change bool, expiresAt time.Time) (*CtrlProgram, error) {
+func (m *Manager) createP2PKH(ctx context.Context, account *Account, change bool, expiresAt time.Time) (*CtrlProgram, error) {
idx, err := m.nextIndex(ctx)
if err != nil {
return nil, err
}
- path := signers.Path(account, signers.AccountKeySpace, idx)
+ path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
derivedPK := derivedXPubs[0].PublicKey()
pubHash := crypto.Ripemd160(derivedPK)
}, nil
}
-func (m *Manager) createP2SH(ctx context.Context, account *signers.Signer, change bool, expiresAt time.Time) (*CtrlProgram, error) {
+func (m *Manager) createP2SH(ctx context.Context, account *Account, change bool, expiresAt time.Time) (*CtrlProgram, error) {
idx, err := m.nextIndex(ctx)
if err != nil {
return nil, err
}
- path := signers.Path(account, signers.AccountKeySpace, idx)
+ path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
derivedPKs := chainkd.XPubKeys(derivedXPubs)
signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, account.Quorum)
return nil, err
}
- path := signers.Path(account, signers.AccountKeySpace, idx)
+ path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
derivedPKs := chainkd.XPubKeys(derivedXPubs)
control, err := vmutil.P2SPMultiSigProgram(derivedPKs, account.Quorum)
// GetCoinbaseControlProgram will return a coinbase script
func (m *Manager) GetCoinbaseControlProgram(height uint64) ([]byte, error) {
- signerIter := m.db.IteratorPrefix([]byte(accountPrefix))
- if !signerIter.Next() {
+ accountIter := m.db.IteratorPrefix([]byte(accountPrefix))
+ defer accountIter.Release()
+ if !accountIter.Next() {
log.Warningf("GetCoinbaseControlProgram: can't find any account in db")
return vmutil.CoinbaseProgram(nil, 0, height)
}
- rawSigner := signerIter.Value()
- signerIter.Release()
+ rawAccount := accountIter.Value()
- signer := &signers.Signer{}
- if err := json.Unmarshal(rawSigner, signer); err != nil {
- log.Errorf("GetCoinbaseControlProgram: fail to unmarshal signer %v", err)
+ account := &Account{}
+ if err := json.Unmarshal(rawAccount, account); err != nil {
+ log.Errorf("GetCoinbaseControlProgram: fail to unmarshal account %v", err)
return vmutil.CoinbaseProgram(nil, 0, height)
}
log.Errorf("GetCoinbaseControlProgram: fail to get nextIndex %v", err)
return vmutil.CoinbaseProgram(nil, 0, height)
}
- path := signers.Path(signer, signers.AccountKeySpace, idx)
- derivedXPubs := chainkd.DeriveXPubs(signer.XPubs, path)
+ path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
+ derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
derivedPKs := chainkd.XPubKeys(derivedXPubs)
- script, err := vmutil.CoinbaseProgram(derivedPKs, signer.Quorum, height)
+ script, err := vmutil.CoinbaseProgram(derivedPKs, account.Quorum, height)
if err != nil {
return script, err
}
err = m.insertAccountControlProgram(ctx, &CtrlProgram{
- AccountID: signer.ID,
+ AccountID: account.ID,
KeyIndex: idx,
ControlProgram: script,
Change: false,
b.OnRollback(canceler(ctx, a.accounts, res.ID))
for _, r := range res.UTXOs {
- txInput, sigInst, err := UtxoToInputs(acct, r, a.ReferenceData)
+ txInput, sigInst, err := UtxoToInputs(acct.Signer, r, a.ReferenceData)
if err != nil {
return errors.Wrap(err, "creating inputs")
}
}
b.OnRollback(canceler(ctx, a.accounts, res.ID))
- var acct *signers.Signer
- if res.Source.AccountID == "" {
- //TODO coinbase
- acct = &signers.Signer{}
- } else {
- acct, err = a.accounts.findByID(ctx, res.Source.AccountID)
- if err != nil {
- return err
- }
+ account, err := a.accounts.findByID(ctx, res.Source.AccountID)
+ if err != nil {
+ return err
}
- txInput, sigInst, err := UtxoToInputs(acct, res.UTXOs[0], a.ReferenceData)
+ txInput, sigInst, err := UtxoToInputs(account.Signer, res.UTXOs[0], a.ReferenceData)
if err != nil {
return err
}
}
// UtxoToInputs convert an utxo to the txinput
-func UtxoToInputs(account *signers.Signer, u *UTXO, refData []byte) (*legacy.TxInput, *txbuilder.SigningInstruction, error) {
+func UtxoToInputs(signer *signers.Signer, u *UTXO, refData []byte) (*legacy.TxInput, *txbuilder.SigningInstruction, error) {
txInput := legacy.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram, u.RefDataHash, refData)
- path := signers.Path(account, signers.AccountKeySpace, u.ControlProgramIndex)
+ path := signers.Path(signer, signers.AccountKeySpace, u.ControlProgramIndex)
sigInst := &txbuilder.SigningInstruction{}
if u.Address == "" {
- sigInst.AddWitnessKeys(account.XPubs, path, account.Quorum)
+ sigInst.AddWitnessKeys(signer.XPubs, path, signer.Quorum)
return txInput, sigInst, nil
}
switch address.(type) {
case *common.AddressWitnessPubKeyHash:
- sigInst.AddRawWitnessKeys(account.XPubs, path, account.Quorum)
- derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
+ sigInst.AddRawWitnessKeys(signer.XPubs, path, signer.Quorum)
+ derivedXPubs := chainkd.DeriveXPubs(signer.XPubs, path)
derivedPK := derivedXPubs[0].PublicKey()
sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
case *common.AddressWitnessScriptHash:
- sigInst.AddWitnessKeys(account.XPubs, path, account.Quorum)
- path := signers.Path(account, signers.AccountKeySpace, u.ControlProgramIndex)
- derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
+ sigInst.AddWitnessKeys(signer.XPubs, path, signer.Quorum)
+ path := signers.Path(signer, signers.AccountKeySpace, u.ControlProgramIndex)
+ derivedXPubs := chainkd.DeriveXPubs(signer.XPubs, path)
derivedPKs := chainkd.XPubKeys(derivedXPubs)
- script, err := vmutil.P2SPMultiSigProgram(derivedPKs, account.Quorum)
+ script, err := vmutil.P2SPMultiSigProgram(derivedPKs, signer.Quorum)
if err != nil {
return nil, nil, err
}
}
return &txbuilder.Receiver{
- Address: program.Address,
- ExpiresAt: expiresAt,
+ ControlProgram: program.ControlProgram,
+ Address: program.Address,
+ ExpiresAt: expiresAt,
}, nil
}
import (
"context"
"encoding/json"
- "fmt"
"sync"
"github.com/golang/groupcache/lru"
"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"
//Asset describe asset on bytom chain
type Asset struct {
- AssetID bc.AssetID
- Alias *string
- VMVersion uint64
- IssuanceProgram []byte
- InitialBlockHash bc.Hash
*signers.Signer
- Tags map[string]interface{}
- RawDefinitionByte []byte
- DefinitionMap map[string]interface{}
+ AssetID bc.AssetID `json:"id"`
+ Alias *string `json:"alias"`
+ VMVersion uint64 `json:"vm_version"`
+ IssuanceProgram chainjson.HexBytes `json:"issue_program"`
+ InitialBlockHash bc.Hash `json:"init_blockhash"`
+ Tags map[string]interface{} `json:"tags"`
+ RawDefinitionByte []byte `json:"raw_definition_byte"`
+ DefinitionMap map[string]interface{} `json:"definition"`
}
//RawDefinition return asset in the raw format
return nil, ErrDuplicateAlias
}
- assetSigner, err := signers.Create(ctx, reg.db, "asset", xpubs, quorum, clientToken)
+ _, assetSigner, err := signers.Create(ctx, reg.db, "asset", xpubs, quorum, clientToken)
if err != nil {
return nil, err
}
return *asset.Alias
}
-type annotatedAsset struct {
- AssetID string `json:"id"`
- Alias string `json:"alias"`
- VMVersion uint64 `json:"vm_version"`
- IssuanceProgram string `json:"issue_program"`
- InitialBlockHash string `json:"init_blockhash"`
- XPubs []chainkd.XPub `json:"xpubs"`
- Quorum int `json:"quorum"`
- KeyIndex uint64 `json:"key_index"`
- Definition *json.RawMessage `json:"definition"`
- Tags *json.RawMessage `json:"tags"`
-}
-
// ListAssets returns the accounts in the db
-func (reg *Registry) ListAssets(id string) ([]annotatedAsset, error) {
- asset := Asset{}
- tmpAsset := annotatedAsset{}
- assets := make([]annotatedAsset, 0)
- jsonTags := json.RawMessage(`{}`)
- jsonDefinition := json.RawMessage(`{}`)
-
+func (reg *Registry) ListAssets(id string) ([]*Asset, error) {
+ assets := []*Asset{}
assetIter := reg.db.IteratorPrefix([]byte(assetPrefix + id))
defer assetIter.Release()
for assetIter.Next() {
- if err := json.Unmarshal(assetIter.Value(), &asset); err != nil {
+ asset := &Asset{}
+ if err := json.Unmarshal(assetIter.Value(), asset); err != nil {
return nil, err
}
-
- tmpAsset.AssetID = asset.AssetID.String()
- tmpAsset.Alias = *asset.Alias
- tmpAsset.VMVersion = asset.VMVersion
- tmpAsset.InitialBlockHash = asset.InitialBlockHash.String()
- tmpAsset.IssuanceProgram = fmt.Sprintf("%x", asset.IssuanceProgram)
- tmpAsset.XPubs = asset.XPubs
- tmpAsset.Quorum = asset.Quorum
- tmpAsset.KeyIndex = asset.KeyIndex
-
- // a.RawDefinition is the asset definition as it appears on the
- // blockchain, so it's untrusted and may not be valid json.
- if isValidJSON(asset.RawDefinition()) {
- jsonDefinition = json.RawMessage(asset.RawDefinition())
- }
- tmpAsset.Definition = &jsonDefinition
- if asset.Tags != nil {
- t, err := json.Marshal(asset.Tags)
- if err != nil {
- return nil, err
- }
- jsonTags = t
- }
- tmpAsset.Tags = &jsonTags
-
- assets = append(assets, tmpAsset)
+ assets = append(assets, asset)
}
-
return assets, nil
}
// which is composed of a set of keys as well as
// the amount of signatures needed for quorum.
type Signer struct {
- ID string `json:"id"`
Type string `json:"type"`
XPubs []chainkd.XPub `json:"xpubs"`
Quorum int `json:"quorum"`
}
// Create creates and stores a Signer in the database
-func Create(ctx context.Context, db dbm.DB, signerType string, xpubs []chainkd.XPub, quorum int, clientToken string) (*Signer, error) {
+func Create(ctx context.Context, db dbm.DB, signerType string, xpubs []chainkd.XPub, quorum int, clientToken string) (string, *Signer, error) {
if len(xpubs) == 0 {
- return nil, errors.Wrap(ErrNoXPubs)
+ return "", nil, errors.Wrap(ErrNoXPubs)
}
sort.Sort(sortKeys(xpubs)) // this transforms the input slice
for i := 1; i < len(xpubs); i++ {
if bytes.Equal(xpubs[i][:], xpubs[i-1][:]) {
- return nil, errors.WithDetailf(ErrDupeXPub, "duplicated key=%x", xpubs[i])
+ return "", nil, errors.WithDetailf(ErrDupeXPub, "duplicated key=%x", xpubs[i])
}
}
if quorum == 0 || quorum > len(xpubs) {
- return nil, errors.Wrap(ErrBadQuorum)
+ return "", nil, errors.Wrap(ErrBadQuorum)
}
var xpubBytes [][]byte
xpubBytes = append(xpubBytes, key[:])
}
- var (
- id string
- keyIndex uint64
- )
-
- id, keyIndex = IdGenerate()
-
- return &Signer{
- ID: id,
+ id, keyIndex := IdGenerate()
+ return id, &Signer{
Type: signerType,
XPubs: xpubs,
Quorum: quorum,
}, nil
}
-func New(id, typ string, xpubs [][]byte, quorum int, keyIndex uint64) (*Signer, error) {
- keys, err := ConvertKeys(xpubs)
- if err != nil {
- return nil, errors.WithDetail(errors.New("bad xpub in databse"), errors.Detail(err))
- }
- return &Signer{
- ID: id,
- Type: typ,
- XPubs: keys,
- Quorum: quorum,
- KeyIndex: keyIndex,
- }, nil
-}
-
// Find retrieves a Signer from the database
// using the type and id.
func Find(ctx context.Context, db dbm.DB, typ, id string) (*Signer, error) {
var buildControlAddressReqFmtByAlias = `
{"actions": [
{"type": "spend_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount":%s, "account_alias": "%s"},
- {"type": "spend_account", "asset_id": "%s","amount": %s, "account_alias": "%s"},
- {"type": "control_address", "asset_id": "%s", "amount": %s,"address": "%s"}
+ {"type": "spend_account", "asset_alias": "%s","amount": %s, "account_alias": "%s"},
+ {"type": "control_address", "asset_alias": "%s", "amount": %s,"address": "%s"}
]}`
var buildTransactionCmd = &cobra.Command{
t.Fatal(err)
}
- controlProg, err := accountManager.CreateAddress(nil, testAccount.Signer.ID, false, time.Now())
+ controlProg, err := accountManager.CreateAddress(nil, testAccount.ID, false, time.Now())
if err != nil {
t.Fatal(err)
}
t.Fatal(err)
}
- controlProg, err := accountManager.CreateAddress(nil, testAccount.Signer.ID, false, time.Now())
+ controlProg, err := accountManager.CreateAddress(nil, testAccount.ID, false, time.Now())
if err != nil {
t.Fatal(err)
}