return account.Alias
}
-// CreateAddress generate an address for the select account
func (m *Manager) CreateAddress(ctx context.Context, accountID string, change bool) (cp *CtrlProgram, err error) {
account, err := m.findByID(ctx, accountID)
if err != nil {
return nil, err
}
+ return m.createAddress(ctx, account, change)
+}
- expiresAt := time.Now().Add(defaultReceiverExpiry)
+// CreateAddress generate an address for the select account
+func (m *Manager) createAddress(ctx context.Context, account *Account, change bool) (cp *CtrlProgram, err error) {
if len(account.XPubs) == 1 {
- cp, err = m.createP2PKH(ctx, account, change, expiresAt)
+ cp, err = m.createP2PKH(ctx, account, change)
} else {
- cp, err = m.createP2SH(ctx, account, change, expiresAt)
+ cp, err = m.createP2SH(ctx, account, change)
}
if err != nil {
return nil, err
return cp, nil
}
-func (m *Manager) createP2PKH(ctx context.Context, account *Account, change bool, expiresAt time.Time) (*CtrlProgram, error) {
+func (m *Manager) createP2PKH(ctx context.Context, account *Account, change bool) (*CtrlProgram, error) {
idx := m.nextIndex()
path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
KeyIndex: idx,
ControlProgram: control,
Change: change,
- ExpiresAt: expiresAt,
}, nil
}
-func (m *Manager) createP2SH(ctx context.Context, account *Account, change bool, expiresAt time.Time) (*CtrlProgram, error) {
+func (m *Manager) createP2SH(ctx context.Context, account *Account, change bool) (*CtrlProgram, error) {
idx := m.nextIndex()
path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
KeyIndex: idx,
ControlProgram: control,
Change: change,
- ExpiresAt: expiresAt,
}, nil
}
-func (m *Manager) createControlProgram(ctx context.Context, accountID string, change bool, expiresAt time.Time) (*CtrlProgram, error) {
- account, err := m.findByID(ctx, accountID)
- if err != nil {
- return nil, err
- }
-
- idx := m.nextIndex()
- 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)
- if err != nil {
- return nil, err
- }
-
- return &CtrlProgram{
- AccountID: account.ID,
- KeyIndex: idx,
- ControlProgram: control,
- Change: change,
- ExpiresAt: expiresAt,
- }, nil
-}
-
-// CreateControlProgram creates a control program
-// that is tied to the Account and stores it in the database.
-func (m *Manager) CreateControlProgram(ctx context.Context, accountID string, change bool, expiresAt time.Time) ([]byte, error) {
- cp, err := m.createControlProgram(ctx, accountID, change, expiresAt)
- if err != nil {
- return nil, err
- }
-
- if err = m.insertAccountControlProgram(ctx, cp); err != nil {
- return nil, err
- }
- return cp.ControlProgram, nil
-}
-
//CtrlProgram is structure of account control program
type CtrlProgram struct {
AccountID string
KeyIndex uint64
ControlProgram []byte
Change bool
- ExpiresAt time.Time
}
func (m *Manager) insertAccountControlProgram(ctx context.Context, progs ...*CtrlProgram) error {
}
// GetCoinbaseControlProgram will return a coinbase script
-func (m *Manager) GetCoinbaseControlProgram(height uint64) ([]byte, error) {
+func (m *Manager) GetCoinbaseControlProgram() ([]byte, error) {
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)
+ return vmutil.DefaultCoinbaseProgram()
}
rawAccount := accountIter.Value()
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)
- }
-
- ctx := context.Background()
- idx := m.nextIndex()
- path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
- derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
- derivedPKs := chainkd.XPubKeys(derivedXPubs)
-
- script, err := vmutil.CoinbaseProgram(derivedPKs, account.Quorum, height)
- if err != nil {
- return script, err
+ return nil, err
}
- err = m.insertAccountControlProgram(ctx, &CtrlProgram{
- AccountID: account.ID,
- KeyIndex: idx,
- ControlProgram: script,
- Change: false,
- })
+ program, err := m.createAddress(nil, account, false)
if err != nil {
- log.Errorf("GetCoinbaseControlProgram: fail to insertAccountControlProgram %v", err)
+ return nil, err
}
- return script, nil
+ return program.ControlProgram, nil
}
func (m *Manager) nextIndex() uint64 {
}
if res.Change > 0 {
- acp, err := a.accounts.createControlProgram(ctx, a.AccountID, true, b.MaxTime())
+ acp, err := a.accounts.CreateAddress(ctx, a.AccountID, true)
if err != nil {
return errors.Wrap(err, "creating control program")
}
}
// Produce a control program, but don't insert it into the database yet.
- acp, err := a.accounts.createControlProgram(ctx, a.AccountID, false, b.MaxTime())
+ acp, err := a.accounts.CreateAddress(ctx, a.AccountID, false)
if err != nil {
return err
}
"time"
"github.com/bytom/blockchain/txbuilder"
- "github.com/bytom/errors"
)
const defaultReceiverExpiry = 30 * 24 * time.Hour // 30 days
-// CreateReceiver creates a new account receiver for an account
-// with the provided expiry. If a zero time is provided for the
-// expiry, a default expiry of 30 days from the current time is
-// used.
-func (m *Manager) CreateReceiver(ctx context.Context, accountInfo string) (*txbuilder.Receiver, error) {
- expiresAt := time.Now().Add(defaultReceiverExpiry)
-
- accountID := accountInfo
-
- if s, err := m.FindByAlias(ctx, accountInfo); err == nil {
- accountID = s.ID
- }
-
- cp, err := m.CreateControlProgram(ctx, accountID, false, expiresAt)
- if err != nil {
- return nil, errors.Wrap(err)
- }
-
- receiver := &txbuilder.Receiver{
- ControlProgram: cp,
- ExpiresAt: expiresAt,
- }
-
- return receiver, nil
-}
-
// CreateAddressReceiver creates a new address receiver for an account
func (m *Manager) CreateAddressReceiver(ctx context.Context, accountInfo string) (*txbuilder.Receiver, error) {
accountID := accountInfo
return &txbuilder.Receiver{
ControlProgram: program.ControlProgram,
Address: program.Address,
- ExpiresAt: program.ExpiresAt,
}, nil
}
"github.com/bytom/testutil"
)
-func TestCreateReceiver(t *testing.T) {
- m := mockAccountManager(t)
- ctx := context.Background()
-
- account, err := m.Create([]chainkd.XPub{testutil.TestXPub}, 1, "test-alias", nil)
- if err != nil {
- testutil.FatalErr(t, err)
- }
-
- _, err = m.CreateReceiver(ctx, account.ID)
- if err != nil {
- testutil.FatalErr(t, err)
- }
-
- _, err = m.CreateReceiver(ctx, account.Alias)
- if err != nil {
- testutil.FatalErr(t, err)
- }
-}
-
func TestCreateAddressReceiver(t *testing.T) {
m := mockAccountManager(t)
ctx := context.Background()
+++ /dev/null
-package blockchain
-
-import (
- "context"
- stdjson "encoding/json"
- "sync"
- "time"
-
- "github.com/bytom/encoding/json"
- "github.com/bytom/errors"
- "github.com/bytom/net/http/httpjson"
- "github.com/bytom/net/http/reqid"
-)
-
-// POST /create-control-program
-func (bcr *BlockchainReactor) createControlProgram(ctx context.Context, ins []struct {
- Type string
- Params stdjson.RawMessage
-}) interface{} {
- responses := make([]interface{}, len(ins))
- var wg sync.WaitGroup
- wg.Add(len(responses))
-
- for i := 0; i < len(responses); i++ {
- go func(i int) {
- subctx := reqid.NewSubContext(ctx, reqid.New())
- defer wg.Done()
- defer batchRecover(subctx, &responses[i])
-
- var (
- prog interface{}
- err error
- )
- switch ins[i].Type {
- case "account":
- prog, err = bcr.createAccountControlProgram(subctx, ins[i].Params)
- default:
- err = errors.WithDetailf(httpjson.ErrBadRequest, "unknown control program type %q", ins[i].Type)
- }
- if err != nil {
- responses[i] = err
- } else {
- responses[i] = prog
- }
- }(i)
- }
-
- wg.Wait()
- return responses
-}
-
-func (bcr *BlockchainReactor) createAccountControlProgram(ctx context.Context, input []byte) (interface{}, error) {
- var parsed struct {
- AccountAlias string `json:"account_alias"`
- AccountID string `json:"account_id"`
- }
- err := stdjson.Unmarshal(input, &parsed)
- if err != nil {
- return nil, errors.WithDetailf(httpjson.ErrBadRequest, "bad parameters for account control program")
- }
-
- accountID := parsed.AccountID
- if accountID == "" {
- acc, err := bcr.accounts.FindByAlias(ctx, parsed.AccountAlias)
- if err != nil {
- return nil, err
- }
- accountID = acc.ID
- }
-
- controlProgram, err := bcr.accounts.CreateControlProgram(ctx, accountID, false, time.Time{})
- if err != nil {
- return nil, err
- }
-
- ret := map[string]interface{}{
- "control_program": json.HexBytes(controlProgram),
- }
- return ret, nil
-}
"context"
)
-// POST /create-account-receiver
-func (bcr *BlockchainReactor) createAccountReceiver(ctx context.Context, ins struct {
- AccountInfo string `json:"account_info"`
-}) Response {
- receiver, err := bcr.accounts.CreateReceiver(nil, ins.AccountInfo)
- if err != nil {
- return NewErrorResponse(err)
- }
-
- return NewSuccessResponse(*receiver)
-}
-
func (bcr *BlockchainReactor) createAccountAddress(ctx context.Context, ins struct {
- AccountInfo string `json:"account_info"`
+ AccountInfo string `json:"account_info"`
}) Response {
receiver, err := bcr.accounts.CreateAddressReceiver(ctx, ins.AccountInfo)
if err != nil {
if bcr.accounts != nil && bcr.assets != nil {
m.Handle("/create-account", jsonHandler(bcr.createAccount))
m.Handle("/update-account-tags", jsonHandler(bcr.updateAccountTags))
- m.Handle("/create-account-receiver", jsonHandler(bcr.createAccountReceiver))
m.Handle("/create-account-address", jsonHandler(bcr.createAccountAddress))
m.Handle("/list-accounts", jsonHandler(bcr.listAccounts))
m.Handle("/delete-account", jsonHandler(bcr.deleteAccount))
m.Handle("/submit-transaction", jsonHandler(bcr.submit))
m.Handle("/sign-submit-transaction", jsonHandler(bcr.signSubmit))
- m.Handle("/create-control-program", jsonHandler(bcr.createControlProgram))
m.Handle("/create-transaction-feed", jsonHandler(bcr.createTxFeed))
m.Handle("/get-transaction-feed", jsonHandler(bcr.getTxFeed))
m.Handle("/update-transaction-feed", jsonHandler(bcr.updateTxFeed))
if len(a.Receiver.ControlProgram) == 0 {
missing = append(missing, "receiver.control_program")
}
- if a.Receiver.ExpiresAt.IsZero() {
- missing = append(missing, "receiver.expires_at")
- }
}
if a.AssetId.IsZero() {
missing = append(missing, "asset_id")
return MissingFieldsError(missing...)
}
- b.RestrictMaxTime(a.Receiver.ExpiresAt)
out := legacy.NewTxOutput(*a.AssetId, a.Amount, a.Receiver.ControlProgram, a.ReferenceData)
return b.AddOutput(out)
}
import (
"context"
- "time"
chainjson "github.com/bytom/encoding/json"
"github.com/bytom/protocol/bc"
type Receiver struct {
ControlProgram chainjson.HexBytes `json:"control_program,omitempty"`
Address string `json:"address,omitempty"`
- ExpiresAt time.Time `json:"expires_at"`
}
},
}
-var createAccountReceiverCmd = &cobra.Command{
- Use: "create-account-receiver <accountID | alias>",
- Short: "Create an account receiver control program",
- Args: cobra.ExactArgs(1),
- Run: func(cmd *cobra.Command, args []string) {
- var ins = struct {
- AccountInfo string `json:"account_info"`
- ExpiresAt time.Time `json:"expires_at,omitempty"`
- }{AccountInfo: args[0]}
-
- data, exitCode := util.ClientCall("/create-account-receiver", &ins)
- if exitCode != util.Success {
- os.Exit(exitCode)
- }
-
- printJSON(data)
- },
-}
-
var createAccountAddressCmd = &cobra.Command{
Use: "create-account-address <accountID | alias>",
Short: "Create an account address",
BytomcliCmd.AddCommand(deleteAccountCmd)
BytomcliCmd.AddCommand(listAccountsCmd)
BytomcliCmd.AddCommand(updateAccountTagsCmd)
- BytomcliCmd.AddCommand(createAccountReceiverCmd)
BytomcliCmd.AddCommand(createAccountAddressCmd)
BytomcliCmd.AddCommand(createAssetCmd)
BytomcliCmd.AddCommand(versionCmd)
}
-
-
// is nil, the coinbase transaction will instead be redeemable by anyone.
func createCoinbaseTx(accountManager *account.Manager, amount uint64, blockHeight uint64) (tx *legacy.Tx, err error) {
amount += consensus.BlockSubsidy(blockHeight)
- unlockHeight := blockHeight + consensus.CoinbasePendingBlockNumber
var script []byte
if accountManager == nil {
- script, err = vmutil.CoinbaseProgram(nil, 0, unlockHeight)
+ script, err = vmutil.DefaultCoinbaseProgram()
} else {
- script, err = accountManager.GetCoinbaseControlProgram(unlockHeight)
+ script, err = accountManager.GetCoinbaseControlProgram()
}
if err != nil {
return
+++ /dev/null
-// Package patricia computes the Merkle Patricia Tree Hash of a
-// set of bit strings, as described in the Chain Protocol spec.
-// See https://chain.com/docs/protocol/specifications/data#merkle-patricia-tree.
-// Because a patricia tree (a radix tree with a radix of 2)
-// provides efficient incremental updates, so does the Merkle
-// Patricia Tree Hash computation, making this structure suitable
-// for the blockchain full-state commitment.
-//
-// Type Tree represents a set, where the elements are bit strings.
-// The set must be prefix-free -- no item can be a prefix of
-// any other -- enforced by Insert.
-// The length of each bit string must also be a multiple of eight,
-// because the interface uses []byte to represent an item.
-//
-// The nodes in the tree form an immutable persistent data
-// structure. It is okay to copy a Tree struct,
-// which contains the root of the tree, to obtain a new tree
-// with the same contents. The time to make such a copy is
-// independent of the size of the tree.
-package patricia
-
-import (
- "bytes"
-
- "github.com/bytom/crypto/sha3pool"
- "github.com/bytom/errors"
- "github.com/bytom/protocol/bc"
-)
-
-var (
- leafPrefix = []byte{0x00}
- interiorPrefix = []byte{0x01}
-)
-
-// Tree implements a patricia tree.
-type Tree struct {
- root *node
-}
-
-// WalkFunc is the type of the function called for each item
-// visited by Walk. If an error is returned, processing stops.
-type WalkFunc func(item []byte) error
-
-// Walk walks t calling walkFn for each item.
-// If an error is returned by walkFn at any point,
-// processing is stopped and the error is returned.
-func Walk(t *Tree, walkFn WalkFunc) error {
- if t.root == nil {
- return nil
- }
- return walk(t.root, walkFn)
-}
-
-func walk(n *node, walkFn WalkFunc) error {
- if n.isLeaf {
- return walkFn(n.Key())
- }
-
- if err := walk(n.children[0], walkFn); err != nil {
- return err
- }
-
- err := walk(n.children[1], walkFn)
- return err
-}
-
-// Contains returns whether t contains item.
-func (t *Tree) Contains(item []byte) bool {
- if t.root == nil {
- return false
- }
-
- key := bitKey(item)
- n := lookup(t.root, key)
-
- var hash bc.Hash
- h := sha3pool.Get256()
- h.Write(leafPrefix)
- h.Write(item)
- hash.ReadFrom(h)
- sha3pool.Put256(h)
- return n != nil && n.Hash() == hash
-}
-
-func lookup(n *node, key []uint8) *node {
- if bytes.Equal(n.key, key) {
- if !n.isLeaf {
- return nil
- }
- return n
- }
- if !bytes.HasPrefix(key, n.key) {
- return nil
- }
-
- bit := key[len(n.key)]
- return lookup(n.children[bit], key)
-}
-
-// Insert inserts item into t.
-//
-// It is an error for item to be a prefix of an element
-// in t or to contain an element in t as a prefix.
-// If item itself is already in t, Insert does nothing
-// (and this is not an error).
-func (t *Tree) Insert(item []byte) (err error) {
- key := bitKey(item)
-
- var hash bc.Hash
- h := sha3pool.Get256()
- h.Write(leafPrefix)
- h.Write(item)
- hash.ReadFrom(h)
- sha3pool.Put256(h)
-
- if t.root == nil {
- t.root = &node{key: key, hash: &hash, isLeaf: true}
- return nil
- }
-
- t.root, err = insert(t.root, key, &hash)
- return err
-}
-
-func insert(n *node, key []uint8, hash *bc.Hash) (*node, error) {
- if bytes.Equal(n.key, key) {
- if !n.isLeaf {
- return n, errors.Wrap(errors.New("key provided is a prefix to other keys"))
- }
-
- n = &node{
- isLeaf: true,
- key: n.key,
- hash: hash,
- }
- return n, nil
- }
-
- if bytes.HasPrefix(key, n.key) {
- if n.isLeaf {
- return n, errors.Wrap(errors.New("key provided is a prefix to other keys"))
- }
- bit := key[len(n.key)]
-
- child := n.children[bit]
- child, err := insert(child, key, hash)
- if err != nil {
- return n, err
- }
- newNode := new(node)
- *newNode = *n
- newNode.children[bit] = child // mutation is ok because newNode hasn't escaped yet
- newNode.hash = nil
- return newNode, nil
- }
-
- common := commonPrefixLen(n.key, key)
- newNode := &node{
- key: key[:common],
- }
- newNode.children[key[common]] = &node{
- key: key,
- hash: hash,
- isLeaf: true,
- }
- newNode.children[1-key[common]] = n
- return newNode, nil
-}
-
-// Delete removes item from t, if present.
-func (t *Tree) Delete(item []byte) {
- key := bitKey(item)
-
- if t.root != nil {
- t.root = delete(t.root, key)
- }
-}
-
-func delete(n *node, key []uint8) *node {
- if bytes.Equal(key, n.key) {
- if !n.isLeaf {
- return n
- }
- return nil
- }
-
- if !bytes.HasPrefix(key, n.key) {
- return n
- }
-
- bit := key[len(n.key)]
- newChild := delete(n.children[bit], key)
-
- if newChild == nil {
- return n.children[1-bit]
- }
-
- newNode := new(node)
- *newNode = *n
- newNode.key = newChild.key[:len(n.key)] // only use slices of leaf node keys
- newNode.children[bit] = newChild
- newNode.hash = nil
-
- return newNode
-}
-
-// RootHash returns the Merkle root of the tree.
-func (t *Tree) RootHash() bc.Hash {
- root := t.root
- if root == nil {
- return bc.Hash{}
- }
- return root.Hash()
-}
-
-// bitKey takes a byte array and returns a key that can
-// be used inside insert and delete operations.
-func bitKey(byteKey []byte) []uint8 {
- key := make([]uint8, 0, len(byteKey)*8)
- for _, b := range byteKey {
- for i := uint(0); i < 8; i++ {
- key = append(key, (b>>(7-i))&1)
- }
- }
- return key
-}
-
-// byteKey is the inverse of bitKey.
-func byteKey(bitKey []uint8) (key []byte) {
- key = make([]byte, len(bitKey)/8)
- for i := uint(0); i < uint(len(key)); i++ {
- var b byte
- for j := uint(0); j < 8; j++ {
- bit := bitKey[i*8+j]
- b |= bit << (7 - j)
- }
- key[i] = b
- }
- return key
-}
-
-func commonPrefixLen(a, b []uint8) int {
- var common int
- for i := 0; i < len(a) && i < len(b); i++ {
- if a[i] != b[i] {
- break
- }
- common++
- }
- return common
-}
-
-// node is a leaf or branch node in a tree
-type node struct {
- key []uint8
- hash *bc.Hash
- isLeaf bool
- children [2]*node
-}
-
-// Key returns the key for the current node as bytes, as it
-// was provided to Insert.
-func (n *node) Key() []byte { return byteKey(n.key) }
-
-// Hash will return the hash for this node.
-func (n *node) Hash() bc.Hash {
- n.calcHash()
- return *n.hash
-}
-
-func (n *node) calcHash() {
- if n.hash != nil {
- return
- }
-
- h := sha3pool.Get256()
- h.Write(interiorPrefix)
- for _, c := range n.children {
- c.calcHash()
- c.hash.WriteTo(h)
- }
-
- var hash bc.Hash
- hash.ReadFrom(h)
- n.hash = &hash
- sha3pool.Put256(h)
-}
+++ /dev/null
-package patricia
-
-import (
- "fmt"
- "log"
- "math/rand"
- "strconv"
- "strings"
- "testing"
- "testing/quick"
-
- "golang.org/x/crypto/sha3"
-
- "github.com/bytom/protocol/bc"
- "github.com/bytom/testutil"
-)
-
-func BenchmarkInserts(b *testing.B) {
- const nodes = 10000
- for i := 0; i < b.N; i++ {
- r := rand.New(rand.NewSource(12345))
- tr := new(Tree)
- for j := 0; j < nodes; j++ {
- var h [32]byte
- _, err := r.Read(h[:])
- if err != nil {
- b.Fatal(err)
- }
-
- err = tr.Insert(h[:])
- if err != nil {
- b.Fatal(err)
- }
- }
- }
-}
-
-func BenchmarkInsertsRootHash(b *testing.B) {
- const nodes = 10000
- for i := 0; i < b.N; i++ {
- r := rand.New(rand.NewSource(12345))
- tr := new(Tree)
- for j := 0; j < nodes; j++ {
- var h [32]byte
- _, err := r.Read(h[:])
- if err != nil {
- b.Fatal(err)
- }
-
- err = tr.Insert(h[:])
- if err != nil {
- b.Fatal(err)
- }
- }
- tr.RootHash()
- }
-}
-
-func TestRootHashBug(t *testing.T) {
- tr := new(Tree)
-
- err := tr.Insert([]byte{0x94})
- if err != nil {
- t.Fatal(err)
- }
- err = tr.Insert([]byte{0x36})
- if err != nil {
- t.Fatal(err)
- }
- before := tr.RootHash()
- err = tr.Insert([]byte{0xba})
- if err != nil {
- t.Fatal(err)
- }
- if tr.RootHash() == before {
- t.Errorf("before and after root hash is %s", before.String())
- }
-}
-
-func TestLeafVsInternalNodes(t *testing.T) {
- tr0 := new(Tree)
-
- err := tr0.Insert([]byte{0x01})
- if err != nil {
- t.Fatal(err)
- }
- err = tr0.Insert([]byte{0x02})
- if err != nil {
- t.Fatal(err)
- }
- err = tr0.Insert([]byte{0x03})
- if err != nil {
- t.Fatal(err)
- }
- err = tr0.Insert([]byte{0x04})
- if err != nil {
- t.Fatal(err)
- }
-
- // Force calculation of all the hashes.
- tr0.RootHash()
- t.Logf("first child = %s, %t", tr0.root.children[0].hash, tr0.root.children[0].isLeaf)
- t.Logf("second child = %s, %t", tr0.root.children[1].hash, tr0.root.children[1].isLeaf)
-
- // Create a second tree using an internal node from tr1.
- tr1 := new(Tree)
- err = tr1.Insert(tr0.root.children[0].hash.Bytes()) // internal node of tr0
- if err != nil {
- t.Fatal(err)
- }
- err = tr1.Insert(tr0.root.children[1].hash.Bytes()) // sibling leaf node of above node ^
- if err != nil {
- t.Fatal(err)
- }
-
- if tr1.RootHash() == tr0.RootHash() {
- t.Errorf("tr0 and tr1 have matching root hashes: %x", tr1.RootHash().Bytes())
- }
-}
-
-func TestRootHashInsertQuickCheck(t *testing.T) {
- tr := new(Tree)
-
- f := func(b [32]byte) bool {
- before := tr.RootHash()
- err := tr.Insert(b[:])
- if err != nil {
- return false
- }
- return before != tr.RootHash()
- }
- if err := quick.Check(f, nil); err != nil {
- t.Error(err)
- }
-}
-
-func TestLookup(t *testing.T) {
- tr := &Tree{
- root: &node{key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- }
- got := lookup(tr.root, bitKey(bits("11111111")))
- if !testutil.DeepEqual(got, tr.root) {
- t.Log("lookup on 1-node tree")
- t.Fatalf("got:\n%swant:\n%s", prettyNode(got, 0), prettyNode(tr.root, 0))
- }
-
- tr = &Tree{
- root: &node{key: bools("11111110"), hash: hashPtr(hashForLeaf(bits("11111110"))), isLeaf: true},
- }
- got = lookup(tr.root, bitKey(bits("11111111")))
- if got != nil {
- t.Log("lookup nonexistent key on 1-node tree")
- t.Fatalf("got:\n%swant nil", prettyNode(got, 0))
- }
-
- tr = &Tree{
- root: &node{
- key: bools("1111"),
- hash: hashPtr(hashForNonLeaf(hashForLeaf(bits("11110000")), hashForLeaf(bits("11111111")))),
- children: [2]*node{
- {key: bools("11110000"), hash: hashPtr(hashForLeaf(bits("11110000"))), isLeaf: true},
- {key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- },
- },
- }
- got = lookup(tr.root, bitKey(bits("11110000")))
- if !testutil.DeepEqual(got, tr.root.children[0]) {
- t.Log("lookup root's first child")
- t.Fatalf("got:\n%swant:\n%s", prettyNode(got, 0), prettyNode(tr.root.children[0], 0))
- }
-
- tr = &Tree{
- root: &node{
- key: bools("1111"),
- hash: hashPtr(hashForNonLeaf(
- hashForLeaf(bits("11110000")),
- hashForNonLeaf(hashForLeaf(bits("11111100")), hashForLeaf(bits("11111111"))),
- )),
- children: [2]*node{
- {key: bools("11110000"), hash: hashPtr(hashForLeaf(bits("11110000"))), isLeaf: true},
- {
- key: bools("111111"),
- hash: hashPtr(hashForNonLeaf(hashForLeaf(bits("11111100")), hashForLeaf(bits("11111111")))),
- children: [2]*node{
- {key: bools("11111100"), hash: hashPtr(hashForLeaf(bits("11111100"))), isLeaf: true},
- {key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- },
- },
- },
- },
- }
- got = lookup(tr.root, bitKey(bits("11111100")))
- if !testutil.DeepEqual(got, tr.root.children[1].children[0]) {
- t.Fatalf("got:\n%swant:\n%s", prettyNode(got, 0), prettyNode(tr.root.children[1].children[0], 0))
- }
-}
-
-func TestContains(t *testing.T) {
- tr := new(Tree)
- tr.Insert(bits("00000011"))
- tr.Insert(bits("00000010"))
-
- if v := bits("00000011"); !tr.Contains(v) {
- t.Errorf("expected tree to contain %x, but did not", v)
- }
- if v := bits("00000000"); tr.Contains(v) {
- t.Errorf("expected tree to not contain %x, but did", v)
- }
- if v := bits("00000010"); !tr.Contains(v) {
- t.Errorf("expected tree to contain %x, but did not", v)
- }
-}
-
-func TestInsert(t *testing.T) {
- tr := new(Tree)
-
- tr.Insert(bits("11111111"))
- tr.RootHash()
- want := &Tree{
- root: &node{key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- }
- if !testutil.DeepEqual(tr.root, want.root) {
- log.Printf("want hash? %x", hashForLeaf(bits("11111111")).Bytes())
- t.Log("insert into empty tree")
- t.Fatalf("got:\n%swant:\n%s", pretty(tr), pretty(want))
- }
-
- tr.Insert(bits("11111111"))
- tr.RootHash()
- want = &Tree{
- root: &node{key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- }
- if !testutil.DeepEqual(tr.root, want.root) {
- t.Log("inserting the same key does not modify the tree")
- t.Fatalf("got:\n%swant:\n%s", pretty(tr), pretty(want))
- }
-
- tr.Insert(bits("11110000"))
- tr.RootHash()
- want = &Tree{
- root: &node{
- key: bools("1111"),
- hash: hashPtr(hashForNonLeaf(hashForLeaf(bits("11110000")), hashForLeaf(bits("11111111")))),
- children: [2]*node{
- {key: bools("11110000"), hash: hashPtr(hashForLeaf(bits("11110000"))), isLeaf: true},
- {key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- },
- },
- }
- if !testutil.DeepEqual(tr.root, want.root) {
- t.Log("different key creates a fork")
- t.Fatalf("got:\n%swant:\n%s", pretty(tr), pretty(want))
- }
-
- tr.Insert(bits("11111100"))
- tr.RootHash()
- want = &Tree{
- root: &node{
- key: bools("1111"),
- hash: hashPtr(hashForNonLeaf(
- hashForLeaf(bits("11110000")),
- hashForNonLeaf(hashForLeaf(bits("11111100")), hashForLeaf(bits("11111111"))),
- )),
- children: [2]*node{
- {key: bools("11110000"), hash: hashPtr(hashForLeaf(bits("11110000"))), isLeaf: true},
- {
- key: bools("111111"),
- hash: hashPtr(hashForNonLeaf(hashForLeaf(bits("11111100")), hashForLeaf(bits("11111111")))),
- children: [2]*node{
- {key: bools("11111100"), hash: hashPtr(hashForLeaf(bits("11111100"))), isLeaf: true},
- {key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- },
- },
- },
- },
- }
- if !testutil.DeepEqual(tr.root, want.root) {
- t.Fatalf("got:\n%swant:\n%s", pretty(tr), pretty(want))
- }
-
- tr.Insert(bits("11111110"))
- tr.RootHash()
- want = &Tree{
- root: &node{
- key: bools("1111"),
- hash: hashPtr(hashForNonLeaf(
- hashForLeaf(bits("11110000")),
- hashForNonLeaf(
- hashForLeaf(bits("11111100")),
- hashForNonLeaf(hashForLeaf(bits("11111110")), hashForLeaf(bits("11111111"))),
- ),
- )),
- children: [2]*node{
- {key: bools("11110000"), hash: hashPtr(hashForLeaf(bits("11110000"))), isLeaf: true},
- {
- key: bools("111111"),
- hash: hashPtr(hashForNonLeaf(
- hashForLeaf(bits("11111100")),
- hashForNonLeaf(hashForLeaf(bits("11111110")), hashForLeaf(bits("11111111"))))),
- children: [2]*node{
- {key: bools("11111100"), hash: hashPtr(hashForLeaf(bits("11111100"))), isLeaf: true},
- {
- key: bools("1111111"),
- hash: hashPtr(hashForNonLeaf(hashForLeaf(bits("11111110")), hashForLeaf(bits("11111111")))),
- children: [2]*node{
- {key: bools("11111110"), hash: hashPtr(hashForLeaf(bits("11111110"))), isLeaf: true},
- {key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- },
- },
- },
- },
- },
- },
- }
- if !testutil.DeepEqual(tr.root, want.root) {
- t.Log("a fork is created for each level of similar key")
- t.Fatalf("got:\n%swant:\n%s", pretty(tr), pretty(want))
- }
-
- tr.Insert(bits("11111011"))
- tr.RootHash()
- want = &Tree{
- root: &node{
- key: bools("1111"),
- hash: hashPtr(hashForNonLeaf(
- hashForLeaf(bits("11110000")),
- hashForNonLeaf(
- hashForLeaf(bits("11111011")),
- hashForNonLeaf(
- hashForLeaf(bits("11111100")),
- hashForNonLeaf(hashForLeaf(bits("11111110")), hashForLeaf(bits("11111111"))),
- ),
- ),
- )),
- children: [2]*node{
- {key: bools("11110000"), hash: hashPtr(hashForLeaf(bits("11110000"))), isLeaf: true},
- {
- key: bools("11111"),
- hash: hashPtr(hashForNonLeaf(
- hashForLeaf(bits("11111011")),
- hashForNonLeaf(
- hashForLeaf(bits("11111100")),
- hashForNonLeaf(hashForLeaf(bits("11111110")), hashForLeaf(bits("11111111"))),
- ))),
- children: [2]*node{
- {key: bools("11111011"), hash: hashPtr(hashForLeaf(bits("11111011"))), isLeaf: true},
- {
- key: bools("111111"),
- hash: hashPtr(hashForNonLeaf(
- hashForLeaf(bits("11111100")),
- hashForNonLeaf(hashForLeaf(bits("11111110")), hashForLeaf(bits("11111111"))),
- )),
- children: [2]*node{
- {key: bools("11111100"), hash: hashPtr(hashForLeaf(bits("11111100"))), isLeaf: true},
- {
- key: bools("1111111"),
- hash: hashPtr(hashForNonLeaf(hashForLeaf(bits("11111110")), hashForLeaf(bits("11111111")))),
- children: [2]*node{
- {key: bools("11111110"), hash: hashPtr(hashForLeaf(bits("11111110"))), isLeaf: true},
- {key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- },
- },
- },
- },
- },
- },
- },
- },
- }
- if !testutil.DeepEqual(tr.root, want.root) {
- t.Log("compressed branch node is split")
- t.Fatalf("got:\n%swant:\n%s", pretty(tr), pretty(want))
- }
-}
-
-func TestDelete(t *testing.T) {
- tr := new(Tree)
- tr.root = &node{
- key: bools("1111"),
- hash: hashPtr(hashForNonLeaf(
- hashForLeaf(bits("11110000")),
- hashForNonLeaf(
- hashForLeaf(bits("11111100")),
- hashForNonLeaf(hashForLeaf(bits("11111110")), hashForLeaf(bits("11111111"))),
- ),
- )),
- children: [2]*node{
- {key: bools("11110000"), hash: hashPtr(hashForLeaf(bits("11110000"))), isLeaf: true},
- {
- key: bools("111111"),
- hash: hashPtr(hashForNonLeaf(
- hashForLeaf(bits("11111100")),
- hashForNonLeaf(hashForLeaf(bits("11111110")), hashForLeaf(bits("11111111"))),
- )),
- children: [2]*node{
- {key: bools("11111100"), hash: hashPtr(hashForLeaf(bits("11111100"))), isLeaf: true},
- {
- key: bools("1111111"),
- hash: hashPtr(hashForNonLeaf(hashForLeaf(bits("11111110")), hashForLeaf(bits("11111111")))),
- children: [2]*node{
- {key: bools("11111110"), hash: hashPtr(hashForLeaf(bits("11111110"))), isLeaf: true},
- {key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- },
- },
- },
- },
- },
- }
-
- tr.Delete(bits("11111110"))
- tr.RootHash()
- want := &Tree{
- root: &node{
- key: bools("1111"),
- hash: hashPtr(hashForNonLeaf(
- hashForLeaf(bits("11110000")),
- hashForNonLeaf(hashForLeaf(bits("11111100")), hashForLeaf(bits("11111111"))),
- )),
- children: [2]*node{
- {key: bools("11110000"), hash: hashPtr(hashForLeaf(bits("11110000"))), isLeaf: true},
- {
- key: bools("111111"),
- hash: hashPtr(hashForNonLeaf(hashForLeaf(bits("11111100")), hashForLeaf(bits("11111111")))),
- children: [2]*node{
- {key: bools("11111100"), hash: hashPtr(hashForLeaf(bits("11111100"))), isLeaf: true},
- {key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- },
- },
- },
- },
- }
- if !testutil.DeepEqual(tr.root, want.root) {
- t.Fatalf("got:\n%swant:\n%s", pretty(tr), pretty(want))
- }
-
- tr.Delete(bits("11111100"))
- tr.RootHash()
- want = &Tree{
- root: &node{
- key: bools("1111"),
- hash: hashPtr(hashForNonLeaf(hashForLeaf(bits("11110000")), hashForLeaf(bits("11111111")))),
- children: [2]*node{
- {key: bools("11110000"), hash: hashPtr(hashForLeaf(bits("11110000"))), isLeaf: true},
- {key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- },
- },
- }
- if !testutil.DeepEqual(tr.root, want.root) {
- t.Fatalf("got:\n%swant:\n%s", pretty(tr), pretty(want))
- }
-
- tr.Delete(bits("11110011")) // nonexistent value
- tr.RootHash()
- if !testutil.DeepEqual(tr.root, want.root) {
- t.Fatalf("got:\n%swant:\n%s", pretty(tr), pretty(want))
- }
-
- tr.Delete(bits("11110000"))
- tr.RootHash()
- want = &Tree{
- root: &node{key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- }
- if !testutil.DeepEqual(tr.root, want.root) {
- t.Fatalf("got:\n%swant:\n%s", pretty(tr), pretty(want))
- }
-
- tr.Delete(bits("11111111"))
- tr.RootHash()
- want = &Tree{}
- if !testutil.DeepEqual(tr.root, want.root) {
- t.Fatalf("got:\n%swant:\n%s", pretty(tr), pretty(want))
- }
-}
-
-func TestDeletePrefix(t *testing.T) {
- root := &node{
- key: bools("111111"),
- hash: hashPtr(hashForNonLeaf(hashForLeaf(bits("11111110")), hashForLeaf(bits("11111111")))),
- children: [2]*node{
- {key: bools("11111110"), hash: hashPtr(hashForLeaf(bits("11111110"))), isLeaf: true},
- {key: bools("11111111"), hash: hashPtr(hashForLeaf(bits("11111111"))), isLeaf: true},
- },
- }
-
- got := delete(root, bools("111111"))
- got.calcHash()
- if !testutil.DeepEqual(got, root) {
- t.Fatalf("got:\n%swant:\n%s", prettyNode(got, 0), prettyNode(root, 0))
- }
-}
-
-func TestBoolKey(t *testing.T) {
- cases := []struct {
- b []byte
- w []uint8
- }{{
- b: nil,
- w: []uint8{},
- }, {
- b: []byte{0x8f},
- w: []uint8{1, 0, 0, 0, 1, 1, 1, 1},
- }, {
- b: []byte{0x81},
- w: []uint8{1, 0, 0, 0, 0, 0, 0, 1},
- }}
-
- for _, c := range cases {
- g := bitKey(c.b)
-
- if !testutil.DeepEqual(g, c.w) {
- t.Errorf("Key(0x%x) = %v want %v", c.b, g, c.w)
- }
- }
-}
-
-func TestByteKey(t *testing.T) {
- cases := []struct {
- b []uint8
- w []byte
- }{{
- b: []uint8{},
- w: []byte{},
- }, {
- b: []uint8{1, 0, 0, 0, 1, 1, 1, 1},
- w: []byte{0x8f},
- }, {
- b: []uint8{1, 0, 0, 0, 0, 0, 0, 1},
- w: []byte{0x81},
- }, {
- b: []uint8{1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1},
- w: []byte{0x81, 0x8f},
- }}
-
- for _, c := range cases {
- g := byteKey(c.b)
-
- if !testutil.DeepEqual(g, c.w) {
- t.Errorf("byteKey(%#v) = %x want %x", c.b, g, c.w)
- }
- }
-}
-
-func pretty(t *Tree) string {
- if t.root == nil {
- return ""
- }
- return prettyNode(t.root, 0)
-}
-
-func prettyNode(n *node, depth int) string {
- prettyStr := strings.Repeat(" ", depth)
- if n == nil {
- prettyStr += "nil\n"
- return prettyStr
- }
- var b int
- if len(n.key) > 31*8 {
- b = 31 * 8
- }
- prettyStr += fmt.Sprintf("key=%+v", n.key[b:])
- if n.hash != nil {
- prettyStr += fmt.Sprintf(" hash=%+v", n.hash)
- }
- prettyStr += "\n"
-
- for _, c := range n.children {
- if c != nil {
- prettyStr += prettyNode(c, depth+1)
- }
- }
-
- return prettyStr
-}
-
-func bits(lit string) []byte {
- var b [31]byte
- n, _ := strconv.ParseUint(lit, 2, 8)
- return append(b[:], byte(n))
-}
-
-func bools(lit string) []uint8 {
- b := bitKey(bits(lit))
- return append(b[:31*8], b[32*8-len(lit):]...)
-}
-
-func hashForLeaf(item []byte) bc.Hash {
- return bc.NewHash(sha3.Sum256(append([]byte{0x00}, item...)))
-}
-
-func hashForNonLeaf(a, b bc.Hash) bc.Hash {
- d := []byte{0x01}
- d = append(d, a.Bytes()...)
- d = append(d, b.Bytes()...)
- return bc.NewHash(sha3.Sum256(d))
-}
-
-func hashPtr(h bc.Hash) *bc.Hash {
- return &h
-}
"errors"
"github.com/bytom/blockchain/txdb/storage"
+ "github.com/bytom/consensus"
"github.com/bytom/protocol/bc"
)
if entry.Spent {
return errors.New("utxo has been spent")
}
+ if entry.IsCoinBase && entry.BlockHeight+consensus.CoinbasePendingBlockNumber > block.Height {
+ return errors.New("coinbase utxo is not ready for use")
+ }
entry.SpendOutput()
}
},
err: false,
},
+ {
+ block: &bc.Block{
+ BlockHeader: &bc.BlockHeader{
+ Height: 7,
+ },
+ Transactions: []*bc.Tx{
+ &bc.Tx{
+ TxHeader: &bc.TxHeader{
+ ResultIds: []*bc.Hash{},
+ },
+ SpentOutputIDs: []bc.Hash{
+ bc.Hash{V0: 0},
+ },
+ },
+ },
+ },
+ inputView: &UtxoViewpoint{
+ Entries: map[bc.Hash]*storage.UtxoEntry{
+ bc.Hash{V0: 0}: storage.NewUtxoEntry(true, 0, false),
+ },
+ },
+ fetchView: &UtxoViewpoint{
+ Entries: map[bc.Hash]*storage.UtxoEntry{
+ bc.Hash{V0: 0}: storage.NewUtxoEntry(true, 0, true),
+ },
+ },
+ err: false,
+ },
+ {
+ block: &bc.Block{
+ BlockHeader: &bc.BlockHeader{
+ Height: 0,
+ },
+ Transactions: []*bc.Tx{
+ &bc.Tx{
+ TxHeader: &bc.TxHeader{
+ ResultIds: []*bc.Hash{},
+ },
+ SpentOutputIDs: []bc.Hash{
+ bc.Hash{V0: 0},
+ },
+ },
+ },
+ },
+ inputView: &UtxoViewpoint{
+ Entries: map[bc.Hash]*storage.UtxoEntry{
+ bc.Hash{V0: 0}: storage.NewUtxoEntry(true, 0, false),
+ },
+ },
+ fetchView: &UtxoViewpoint{
+ Entries: map[bc.Hash]*storage.UtxoEntry{
+ bc.Hash{V0: 0}: storage.NewUtxoEntry(true, 0, true),
+ },
+ },
+ err: true,
+ },
}
for i, c := range cases {
return nil
}
-// CoinbaseProgram generates the script for contorl coinbase output
-func CoinbaseProgram(pubkeys []ed25519.PublicKey, nrequired int, height uint64) ([]byte, error) {
+// DefaultCoinbaseProgram generates the script for contorl coinbase output
+func DefaultCoinbaseProgram() ([]byte, error) {
builder := NewBuilder()
- builder.AddOp(vm.OP_BLOCKHEIGHT)
- builder.AddInt64(int64(height))
- builder.AddOp(vm.OP_GREATERTHAN)
- builder.AddOp(vm.OP_VERIFY)
-
- if nrequired == 0 {
- return builder.Build()
- }
- if err := builder.addP2SPMultiSig(pubkeys, nrequired); err != nil {
- return nil, err
- }
+ builder.AddOp(vm.OP_TRUE)
return builder.Build()
}