Skip to content

Commit

Permalink
Merge branch 'txscript' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
xhliu committed Oct 24, 2018
2 parents 25ffd52 + ee502ed commit a18c611
Show file tree
Hide file tree
Showing 21 changed files with 267 additions and 173 deletions.
2 changes: 1 addition & 1 deletion boxd/service/chainreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
// ChainReader defines basic operations blockchain exposes
type ChainReader interface {
// interface to reader utxos
ListAllUtxos() map[types.OutPoint]*types.UtxoWrap
ListAllUtxos() (map[types.OutPoint]*types.UtxoWrap, error)
LoadUtxoByPubKeyScript([]byte) (map[types.OutPoint]*types.UtxoWrap, error)

// interface to read transactions
Expand Down
75 changes: 58 additions & 17 deletions commands/box/transaction/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ package transactioncmd
import (
"encoding/hex"
"fmt"
"path"
"strconv"

root "github.com/BOXFoundation/boxd/commands/box/root"
"github.com/BOXFoundation/boxd/rpc/client"
"github.com/BOXFoundation/boxd/util"
"github.com/BOXFoundation/boxd/wallet"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var cfgFile string
var walletDir string
var defaultWalletDir = path.Join(util.HomeDir(), ".box_keystore")

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Expand All @@ -35,31 +40,17 @@ to quickly create a Cobra application.`,
// Init adds the sub command to the root command.
func init() {
root.RootCmd.AddCommand(rootCmd)
rootCmd.PersistentFlags().StringVar(&walletDir, "wallet_dir", defaultWalletDir, "Specify directory to search keystore files")
rootCmd.AddCommand(
&cobra.Command{
Use: "listutxos",
Short: "list all utxos",
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("list utxos called")
return client.ListUtxos(viper.GetViper())
},
Run: listAllUtxoCmdFunc,
},
&cobra.Command{
Use: "sendfrom [fromaccount] [toaddress] [amount]",
Short: "Send coins from an account to an address",
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("sendfrom called")
if len(args) != 3 {
return fmt.Errorf("Invalid argument number")
}
fromPubKey, err1 := hex.DecodeString(args[0])
toPubKey, err2 := hex.DecodeString(args[1])
amount, err3 := strconv.Atoi(args[2])
if err1 != nil || err2 != nil || err3 != nil {
return fmt.Errorf("Invalid argument format")
}
return client.CreateTransaction(viper.GetViper(), fromPubKey, toPubKey, int64(amount))
},
Run: sendFromCmdFunc,
},
&cobra.Command{
Use: "sendmany [fromaccount] [toaddresslist]",
Expand All @@ -84,3 +75,53 @@ func init() {
},
)
}

func listAllUtxoCmdFunc(cmd *cobra.Command, args []string) {
fmt.Println("list utxos called")
r, err := client.ListUtxos(viper.GetViper())
if err != nil {
fmt.Println(err)
} else {
fmt.Println(util.PrettyPrint(r))
}
}

func sendFromCmdFunc(cmd *cobra.Command, args []string) {
fmt.Println("sendfrom called")
if len(args) != 3 {
fmt.Println("Invalid argument number")
return
}
fromPubKeyHash, err1 := hex.DecodeString(args[0])
toPubKeyHash, err2 := hex.DecodeString(args[1])
amount, err3 := strconv.Atoi(args[2])
if err1 != nil || err2 != nil || err3 != nil {
fmt.Println("Invalid argument format")
return
}
wltMgr, err := wallet.NewWalletManager(walletDir)
if err != nil {
fmt.Println(err)
return
}
account, exists := wltMgr.GetAccount(args[0])
if !exists {
fmt.Printf("Account %s not managed\n", args[0])
return
}
passphrase, err := wallet.ReadPassphraseStdin()
if err != nil {
fmt.Println(err)
return
}
if err := account.UnlockWithPassphrase(passphrase); err != nil {
fmt.Println("Fail to unlock account", err)
return
}
tx, err := client.CreateTransaction(viper.GetViper(), fromPubKeyHash, toPubKeyHash, account.PublicKey(), int64(amount), account)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(util.PrettyPrint(tx))
}
}
1 change: 0 additions & 1 deletion commands/box/wallet/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (

var cfgFile string
var walletDir string

var defaultWalletDir = path.Join(util.HomeDir(), ".box_keystore")

// rootCmd represents the base command when called without any subcommands
Expand Down
2 changes: 1 addition & 1 deletion consensus/dpos/dpos.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func (dpos *Dpos) mint() error {
func (dpos *Dpos) mintBlock() {
tail, _ := dpos.chain.LoadTailBlock()
block := types.NewBlock(tail)
dpos.PackTxs(block, nil)
dpos.PackTxs(block, dpos.miner)
// block.setMiner()
dpos.chain.ProcessBlock(block, true)
}
Expand Down
38 changes: 20 additions & 18 deletions core/chain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ func (chain *BlockChain) ProcessBlock(block *types.Block, broadcast bool) (bool,
logger.Error(err)
return false, false, err
}

prevHash := block.Header.PrevBlockHash
if prevHashExists := chain.blockExists(prevHash); !prevHashExists {
// Orphan block.
Expand Down Expand Up @@ -350,6 +349,11 @@ func (chain *BlockChain) tryConnectBlockToMainChain(block *types.Block, utxoSet
// str := "the coinbase for the genesis block is not spendable"
// return ErrMissingTxOut
// }
// Validate scripts here before utxoSet is updated; otherwise it may fail mistakenly
if err := validateBlockScripts(utxoSet, block); err != nil {
return err
}

transactions := block.Txs
// Perform several checks on the inputs for each transaction.
// Also accumulate the total fees.
Expand Down Expand Up @@ -400,9 +404,6 @@ func (chain *BlockChain) tryConnectBlockToMainChain(block *types.Block, utxoSet
}
}

if err := validateBlockScripts(block); err != nil {
return err
}
chain.SetTailBlock(block, utxoSet)
// Notify others such as mempool.
chain.notifyBlockConnectionUpdate(block, true)
Expand Down Expand Up @@ -526,25 +527,26 @@ func (chain *BlockChain) TailBlock() *types.Block {
return chain.tail
}

// LoadUtxoByPubKey loads utxos of a public key
func (chain *BlockChain) LoadUtxoByPubKey(pubkey []byte) (map[types.OutPoint]*types.UtxoWrap, error) {
res := make(map[types.OutPoint]*types.UtxoWrap)
// for out, entry := range chain.utxoSet.utxoMap {
// if bytes.Equal(pubkey, entry.Output.ScriptPubKey) {
// res[out] = entry
// }
// }
return res, nil
}

// ListAllUtxos list all the available utxos for testing purpose
func (chain *BlockChain) ListAllUtxos() map[types.OutPoint]*types.UtxoWrap {
return nil
func (chain *BlockChain) ListAllUtxos() (map[types.OutPoint]*types.UtxoWrap, error) {
utxoSet := NewUtxoSet()
err := utxoSet.ApplyBlock(chain.tail)
return utxoSet.utxoMap, err
}

// LoadUtxoByPubKeyScript list all the available utxos owned by a public key bytes
func (chain *BlockChain) LoadUtxoByPubKeyScript(pubkey []byte) (map[types.OutPoint]*types.UtxoWrap, error) {
return nil, nil
allUtxos, err := chain.ListAllUtxos()
if err != nil {
return nil, err
}
utxoMap := make(map[types.OutPoint]*types.UtxoWrap)
for entry, utxo := range allUtxos {
if bytes.Equal(utxo.Output.ScriptPubKey, pubkey) {
utxoMap[entry] = utxo
}
}
return utxoMap, nil
}

// GetBlockHeight returns current height of main chain
Expand Down
13 changes: 1 addition & 12 deletions core/chain/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,7 @@ func CreateCoinbaseTx(addr types.Address, blockHeight int32) (*types.Transaction
if err != nil {
return nil, err
}
if addr != nil {
pkScript, err = script.PayToPubKeyHashScript(addr.ScriptAddress())
if err != nil {
return nil, err
}
} else {
scriptBuilder := script.NewBuilder()
pkScript, err = scriptBuilder.AddOp(byte(script.OPTRUE)).Script()
if err != nil {
return nil, err
}
}
pkScript = *script.PayToPubKeyHashScript(addr.ScriptAddress())

tx := &types.Transaction{
Version: 1,
Expand Down
10 changes: 5 additions & 5 deletions core/chain/utxoset.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (u *UtxoSet) AddUtxo(tx *types.Transaction, txOutIdx uint32, blockHeight in
Output: tx.Vout[txOutIdx],
BlockHeight: blockHeight,
IsCoinBase: IsCoinBase(tx),
IsModified: false,
IsModified: true,
IsSpent: false,
}
u.utxoMap[outPoint] = &utxoWrap
Expand Down Expand Up @@ -94,6 +94,7 @@ func (u *UtxoSet) ApplyBlock(block *types.Block) error {
return err
}
}
logger.Debugf("UTXO: apply block with %d transactions", len(block.Txs))
return nil
}

Expand Down Expand Up @@ -140,7 +141,6 @@ func (u *UtxoSet) RevertBlock(block *types.Block) error {

// WriteUtxoSetToDB store utxo set to database.
func (u *UtxoSet) WriteUtxoSetToDB(db storage.Table) error {

for outpoint, utxoWrap := range u.utxoMap {
if utxoWrap == nil || !utxoWrap.IsModified {
continue
Expand All @@ -153,9 +153,9 @@ func (u *UtxoSet) WriteUtxoSetToDB(db storage.Table) error {
if err != nil {
return err
}
utxoWrap.IsModified = false
continue
}

// Serialize and store the utxo entry.
serialized, err := utxoWrap.Marshal()
if err != nil {
Expand All @@ -166,14 +166,14 @@ func (u *UtxoSet) WriteUtxoSetToDB(db storage.Table) error {
if err != nil {
return err
}
utxoWrap.IsModified = false
}
return nil
}

// LoadTxUtxos loads the unspent transaction outputs related to tx
func (u *UtxoSet) LoadTxUtxos(tx *types.Transaction, db storage.Table) error {

utxoset := NewUtxoSet()
emptySet := make(map[types.OutPoint]struct{})

prevOut := types.OutPoint{Hash: *tx.Hash}
Expand All @@ -188,7 +188,7 @@ func (u *UtxoSet) LoadTxUtxos(tx *types.Transaction, db storage.Table) error {
}

if len(emptySet) > 0 {
if err := utxoset.fetchUtxosFromOutPointSet(emptySet, db); err != nil {
if err := u.fetchUtxosFromOutPointSet(emptySet, db); err != nil {
return err
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/chain/utxoset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestUtxoSet_FindUtxo(t *testing.T) {
BlockHeight: 10000,
IsCoinBase: false,
IsSpent: false,
IsModified: false,
IsModified: true,
}

txHash, _ := tx.TxHash()
Expand Down Expand Up @@ -83,7 +83,7 @@ func TestUtxoSet_FindUtxo(t *testing.T) {
BlockHeight: int32(20000),
IsCoinBase: false,
IsSpent: false,
IsModified: false,
IsModified: true,
}

tx1Hash, _ := tx1.TxHash()
Expand Down
67 changes: 24 additions & 43 deletions core/chain/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,57 +150,43 @@ func IsTxFinalized(tx *types.Transaction, blockHeight int32, blockTime int64) bo
return true
}

func validateBlockScripts(block *types.Block) error {

numInputs := 0
for _, tx := range block.Txs {
numInputs += len(tx.Vin)
}
txValItems := make([]*script.TxValidateItem, 0, numInputs)
func validateBlockScripts(utxoSet *UtxoSet, block *types.Block) error {
// Skip coinbases.
for _, tx := range block.Txs[1:] {
for txInIdx, txIn := range tx.Vin {
txVI := &script.TxValidateItem{
TxInIndex: txInIdx,
TxIn: txIn,
Tx: tx,
}
txValItems = append(txValItems, txVI)
if err := ValidateTxScripts(utxoSet, tx); err != nil {
return err
}
}

// Validate all of the inputs.
start := time.Now()
if err := validateTxs(txValItems); err != nil {
return err
}
elapsed := time.Since(start)

logger.Debugf("block %v took %v to verify", block.BlockHash(), elapsed)
return nil
}

// ValidateTransactionScripts verify crypto signatures for each input
func ValidateTransactionScripts(tx *types.Transaction) error {
txIns := tx.Vin
txValItems := make([]*script.TxValidateItem, 0, len(txIns))
for txInIdx, txIn := range txIns {
// Skip coinbases.
if txIn.PrevOutPoint.Index == math.MaxUint32 {
continue
// ValidateTxScripts verifies unlocking script for each input to ensure it is authorized to spend the utxo
// Coinbase tx will not reach here
func ValidateTxScripts(utxoSet *UtxoSet, tx *types.Transaction) error {
txHash, _ := tx.TxHash()
for txInIdx, txIn := range tx.Vin {
// Ensure the referenced input transaction exists and is not spent.
utxo := utxoSet.FindUtxo(txIn.PrevOutPoint)
if utxo == nil {
logger.Errorf("output %v referenced from transaction %s:%d does not exist", txIn.PrevOutPoint, txHash, txInIdx)
return core.ErrMissingTxOut
}
if utxo.IsSpent {
logger.Errorf("output %v referenced from transaction %s:%d has already been spent", txIn.PrevOutPoint, txHash, txInIdx)
return core.ErrMissingTxOut
}

prevScriptPubKey := script.NewScriptFromBytes(utxo.Output.ScriptPubKey)
scriptSig := script.NewScriptFromBytes(txIn.ScriptSig)

txVI := &script.TxValidateItem{
TxInIndex: txInIdx,
TxIn: txIn,
Tx: tx,
// concatenate unlocking & locking scripts
catScript := script.NewScript().AddScript(scriptSig).AddOpCode(script.OPCODESEPARATOR).AddScript(prevScriptPubKey)
if err := catScript.Evaluate(tx, txInIdx); err != nil {
return err
}
txValItems = append(txValItems, txVI)
}

// Validate all of the inputs.
// validator := NewTxValidator(unspentUtxo, flags, sigCache, hashCache)
// return validator.Validate(txValItems)
return nil
}

Expand Down Expand Up @@ -359,8 +345,3 @@ func ValidateTransactionPreliminary(tx *types.Transaction) error {

return nil
}

func validateTxs(txValidateItems []*script.TxValidateItem) error {
// TODO: execute and verify script
return nil
}

0 comments on commit a18c611

Please sign in to comment.