From 5f54693d46153f03c5f80795d50d997ac458f148 Mon Sep 17 00:00:00 2001 From: Guanghua Guo <1536310027@qq.com> Date: Wed, 29 Nov 2017 17:41:53 +0800 Subject: [PATCH] Dev (#148) * Add rpc token authenticate function (#135) * Unify json response (#130) * Unify gas-rate json response experimentally * Unify block-height and is-mining * Reuse duplicated code * Don't ignore error check * Unify get-block-by-height and get-block-transactions-count-by-height * Unify net.go's json response (#136) * Unify net.go's json response * Add TODO * Unify block-hash (#137) * Support remote spend transaction (#139) * Support remote spend transaction * quick fix for snapshot key and a compile time error (#142) * Bvm (#138) * delete unused parameters from chain core * add unit test for snapshot tree proto * Add txfeed commands to cobra & Change accesstoken rpc to jsend format (#140) * Add txfeed comands to cobra * Change accesstoken rpc to jsend format * Change accesstoken handler from "a" to "br" * remove exp (#143) * Multi account issue (#144) * Support multi account issue asset * Update main.go * Add configure chain_id (#145) * Update README.md (#147) * Update README.md --- README.md | 45 +- blockchain/accesstokens.go | 42 +- blockchain/account/accounts.go | 14 +- blockchain/account/receivers.go | 10 +- blockchain/jsend.go | 24 + blockchain/reactor.go | 67 ++- blockchain/receivers.go | 37 +- blockchain/signers/signers.go | 95 ---- blockchain/transact.go | 4 - blockchain/txbuilder/builder.go | 9 - blockchain/txbuilder/constraint.go | 28 -- blockchain/txbuilder/finalize.go | 3 - blockchain/txbuilder/witness.go | 4 - blockchain/txbuilder/witness_test.go | 4 +- blockchain/txdb/snapshot.go | 2 +- blockchain/txdb/snapshot_test.go | 31 ++ blockchain/txfeeds.go | 44 +- cmd/bytomcli/main.go | 129 ++++-- cmd/bytomd/commands/init.go | 11 +- cmd/bytomd/commands/root.go | 5 - cmd/bytomd/main.go | 6 +- cmd/cobra/commands/accesstoken.go | 101 ++++- cmd/cobra/commands/block.go | 93 +++- cmd/cobra/commands/bytomcli.go | 6 + cmd/cobra/commands/mining.go | 27 +- cmd/cobra/commands/net.go | 80 +++- cmd/cobra/commands/transaction.go | 28 +- cmd/cobra/commands/txfeed.go | 204 +++++++++ config/genesis.go | 22 +- exp/Readme.md | 6 - exp/ivy/compiler/ast.go | 318 -------------- exp/ivy/compiler/builder.go | 210 --------- exp/ivy/compiler/builder_test.go | 51 --- exp/ivy/compiler/builtins.go | 79 ---- exp/ivy/compiler/checks.go | 211 --------- exp/ivy/compiler/checks_test.go | 77 ---- exp/ivy/compiler/cmd/ivyc/ivyc.go | 216 --------- exp/ivy/compiler/compile.go | 756 -------------------------------- exp/ivy/compiler/compile_test.go | 104 ----- exp/ivy/compiler/doc.go | 126 ------ exp/ivy/compiler/environ.go | 72 --- exp/ivy/compiler/ivytest/ivytest.go | 155 ------- exp/ivy/compiler/optimize.go | 64 --- exp/ivy/compiler/parse.go | 563 ------------------------ exp/ivy/compiler/stack.go | 116 ----- exp/ivy/compiler/types.go | 78 ---- net/http/authn/authn.go | 157 +++++++ net/http/authn/authn_test.go | 56 +++ net/http/authn/context.go | 49 +++ node/node.go | 22 +- protocol/bc/asset.go | 5 +- protocol/bc/bc.pb.go | 138 +++--- protocol/bc/bc.proto | 4 +- protocol/bc/bctest/tx.go | 3 - protocol/bc/doc.go | 22 - protocol/bc/entry.go | 12 +- protocol/bc/entry_test.go | 3 +- protocol/bc/hash.go | 3 +- protocol/bc/legacy/block.go | 12 +- protocol/bc/legacy/block_commitment.go | 9 +- protocol/bc/legacy/block_header.go | 42 +- protocol/bc/legacy/block_test.go | 7 +- protocol/bc/legacy/fuzz_test.go | 2 +- protocol/bc/legacy/map.go | 2 +- protocol/bc/legacy/map_test.go | 6 - protocol/bc/legacy/output_commitment.go | 16 +- protocol/bc/legacy/transaction.go | 73 +-- protocol/bc/legacy/transaction_test.go | 72 +-- protocol/bc/legacy/tx_test.go | 6 +- protocol/bc/legacy/txinput.go | 34 +- protocol/bc/legacy/txoutput.go | 12 +- protocol/bc/merkle_test.go | 6 +- protocol/bc/txheader.go | 6 +- protocol/patricia/patricia.go | 8 +- protocol/state/snapshot.go | 6 +- protocol/state/snapshot_test.go | 8 +- protocol/tx.go | 18 - protocol/tx_test.go | 3 - protocol/validation/validation.go | 6 - protocol/validation/validation_test.go | 30 +- protocol/validation/vmcontext.go | 2 - protocol/validation/vmcontext_test.go | 2 - protocol/vm/context.go | 2 - protocol/vm/introspection.go | 31 -- protocol/vm/introspection_test.go | 23 +- protocol/vm/ops.go | 4 - protocol/vm/vm_test.go | 5 - 87 files changed, 1262 insertions(+), 4042 deletions(-) mode change 100644 => 100755 blockchain/account/receivers.go create mode 100644 blockchain/jsend.go mode change 100644 => 100755 blockchain/receivers.go create mode 100644 cmd/cobra/commands/txfeed.go delete mode 100644 exp/Readme.md delete mode 100644 exp/ivy/compiler/ast.go delete mode 100644 exp/ivy/compiler/builder.go delete mode 100644 exp/ivy/compiler/builder_test.go delete mode 100644 exp/ivy/compiler/builtins.go delete mode 100644 exp/ivy/compiler/checks.go delete mode 100644 exp/ivy/compiler/checks_test.go delete mode 100644 exp/ivy/compiler/cmd/ivyc/ivyc.go delete mode 100644 exp/ivy/compiler/compile.go delete mode 100644 exp/ivy/compiler/compile_test.go delete mode 100644 exp/ivy/compiler/doc.go delete mode 100644 exp/ivy/compiler/environ.go delete mode 100644 exp/ivy/compiler/ivytest/ivytest.go delete mode 100644 exp/ivy/compiler/optimize.go delete mode 100644 exp/ivy/compiler/parse.go delete mode 100644 exp/ivy/compiler/stack.go delete mode 100644 exp/ivy/compiler/types.go create mode 100644 net/http/authn/authn.go create mode 100644 net/http/authn/authn_test.go create mode 100644 net/http/authn/context.go delete mode 100644 protocol/bc/doc.go diff --git a/README.md b/README.md index 253220bc..9148d67b 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,9 @@ Bytom * [Create and launch a single node](#create-and-launch-a-single-node) * [Create an account](#create-an-account) * [Create an asset](#create-an-asset) - * [Asset issuance test](#asset-issuance-test) - * [Expenditure test](#expenditure-test) + * [Issue an asset](#issue-an-asset) + * [Transfer an asset](#transfer-an-asset) + * [Transfer btm](#transfer-btm) * [Set up a wallet and manage the key](#set-up-a-wallet-and-manage-the-key) * [Multiple node](#multiple-node) * [Running Bytom in Docker](#running-bytom-in-docker) @@ -75,7 +76,7 @@ When successfully building the project, the `bytom` and `bytomcli` binary should ```bash $ cd ./cmd/bytomd -$ ./bytomd init +$ ./bytomd init --chain_id testnet ``` After that, you'll see `.bytom` generated in current directory, then launch the single node: @@ -127,9 +128,9 @@ Check out the new created asset: $ ./bytomcli list-assets ``` -#### Asset issuance test +#### Issue an asset -Since the account `alice` and the asset `gold` are ready, we need to create another account `bob`, which is also neccessary for the following Expenditure test: +Since the account alice and the asset `gold` are ready, issue `gold` to alice: ```bash $ ./bytomcli create-account bob @@ -138,38 +139,38 @@ $ ./bytomcli create-account bob Firstly, Alice issue ``, e.g., 10000, `gold`: ```bash -$ ./bytomcli sub-create-issue-tx +$ ./bytomcli sub-create-issue-tx ``` -When the transaction above is mined, query the balances: +When the transaction is on-chain, query the balances: ```bash # Alice should have 10000 gold now $ ./bytomcli list-balances ``` -#### Expenditure test - -- Alice -> Bob +#### Transfer an asset Alice pays Bob ``, e.g., 1000, `gold`: - +- Bob creates receiver program ```bash -$ ./bytomcli sub-spend-account-tx -# In our case, after Alice pays Bob 1000 gold, the amount of Alice's gold should be 9000, Bob's balances should be 1000 -$ ./bytomcli list-balances +$./bytomcli create-account-receiver bob ``` - -- Bob -> Alice - -Bob pays Alice ``, e.g., 500, `gold`: - +- off-chain transfers receiver program to alice +- Alice builds transaction and then makes this transacion on-chain ```bash -$ ./bytomcli sub-spend-account-tx -# In our case, after Bob pays Alice 500 gold, the amount of Alice's gold should be 9500, Bob's balances should be 500 -$ ./bytomcli list-balances +$./bytomcli sub-control-receiver-tx +``` +- list balance +```bash +$./bytomcli list-balances ``` +#### Transfer btm +As above, just `btm_asset_id`=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +```bash +$./bytomcli sub-control-receiver-tx +``` ### Set up a wallet and manage the key If you have started a bytom node, then you can create an account via `create-key password`, which will generate a `keystore` directory containing the keys under the project directory. diff --git a/blockchain/accesstokens.go b/blockchain/accesstokens.go index 38a9a085..47da4993 100644 --- a/blockchain/accesstokens.go +++ b/blockchain/accesstokens.go @@ -3,51 +3,49 @@ package blockchain import ( "context" "encoding/hex" - "encoding/json" "github.com/bytom/errors" ) var errCurrentToken = errors.New("token cannot delete itself") -func (a *BlockchainReactor) createAccessToken(ctx context.Context, x struct{ ID, Type string }) interface{} { - token, err := a.accessTokens.Create(ctx, x.ID, x.Type) +func (br *BlockchainReactor) createAccessToken(ctx context.Context, x struct{ ID, Type string }) interface{} { + token, err := br.accessTokens.Create(ctx, x.ID, x.Type) if err != nil { - return err.Error() + return jsendWrapper(nil, ERROR, err.Error()) } - return token + return jsendWrapper(token, SUCCESS, "") } -func (a *BlockchainReactor) listAccessTokens(ctx context.Context) interface{} { - tokens, err := a.accessTokens.List(ctx) +func (br *BlockchainReactor) listAccessTokens(ctx context.Context) interface{} { + tokens, err := br.accessTokens.List(ctx) if err != nil { - return err.Error() + return jsendWrapper(nil, ERROR, err.Error()) } - result, err := json.Marshal(tokens) - if err != nil { - return err.Error() - } - return string(result) + return jsendWrapper(tokens, SUCCESS, "") } -func (a *BlockchainReactor) deleteAccessToken(ctx context.Context, x struct{ ID, Token string }) interface{} { +func (br *BlockchainReactor) deleteAccessToken(ctx context.Context, x struct{ ID, Token string }) interface{} { //TODO Add delete permission verify. - if err := a.accessTokens.Delete(ctx, x.ID); err != nil { - return err.Error() + if err := br.accessTokens.Delete(ctx, x.ID); err != nil { + return jsendWrapper(nil, ERROR, err.Error()) } - return "success!" + return jsendWrapper("success", SUCCESS, "") } -func (a *BlockchainReactor) checkAccessToken(ctx context.Context, x struct{ ID, Secret string }) interface{} { +func (br *BlockchainReactor) checkAccessToken(ctx context.Context, x struct{ ID, Secret string }) interface{} { secret, err := hex.DecodeString(x.Secret) if err != nil { - return err.Error() + return jsendWrapper(nil, ERROR, err.Error()) } - result, err := a.accessTokens.Check(ctx, x.ID, secret) + result, err := br.accessTokens.Check(ctx, x.ID, secret) if err != nil { - return err.Error() + return jsendWrapper(nil, ERROR, err.Error()) + } + if result == true { + return jsendWrapper("success", SUCCESS, "") } - return result + return jsendWrapper("fail", SUCCESS, "") } diff --git a/blockchain/account/accounts.go b/blockchain/account/accounts.go index 0684c6fc..b5a9eb67 100755 --- a/blockchain/account/accounts.go +++ b/blockchain/account/accounts.go @@ -34,7 +34,7 @@ var ( ErrBadIdentifier = errors.New("either ID or alias must be specified, and not both") ) -func alicesKey(name string) []byte { +func aliasKey(name string) []byte { return []byte(aliasPreFix + name) } @@ -103,7 +103,7 @@ type Account struct { // Create creates a new Account. func (m *Manager) Create(ctx context.Context, xpubs []chainkd.XPub, quorum int, alias string, tags map[string]interface{}, clientToken string) (*Account, error) { - if existed := m.db.Get(alicesKey(alias)); existed != nil { + if existed := m.db.Get(aliasKey(alias)); existed != nil { return nil, fmt.Errorf("%s is an existed alias", alias) } @@ -120,7 +120,7 @@ func (m *Manager) Create(ctx context.Context, xpubs []chainkd.XPub, quorum int, accountID := accountKey(signer.ID) m.db.Set(accountID, accountJSON) - m.db.Set(alicesKey(alias), accountID) + m.db.Set(aliasKey(alias), []byte(signer.ID)) return account, nil } @@ -135,7 +135,7 @@ func (m *Manager) UpdateTags(ctx context.Context, id, alias *string, tags map[st var accountID []byte if alias != nil { - accountID = m.db.Get(alicesKey(*alias)) + accountID = m.db.Get(aliasKey(*alias)) } else { accountID = accountKey(*id) } @@ -154,10 +154,10 @@ func (m *Manager) UpdateTags(ctx context.Context, id, alias *string, tags map[st switch v { case "": delete(account.Tags, k) - m.db.Delete(alicesKey(k)) + m.db.Delete(aliasKey(k)) default: account.Tags[k] = v - m.db.Set(alicesKey(k), accountID) + m.db.Set(aliasKey(k), accountID) } } @@ -179,7 +179,7 @@ func (m *Manager) FindByAlias(ctx context.Context, alias string) (*signers.Signe return m.findByID(ctx, cachedID.(string)) } - rawID := m.db.Get(alicesKey(alias)) + rawID := m.db.Get(aliasKey(alias)) if rawID == nil { return nil, errors.New("fail to find account by alias") } diff --git a/blockchain/account/receivers.go b/blockchain/account/receivers.go old mode 100644 new mode 100755 index 144f6617..bf43c042 --- a/blockchain/account/receivers.go +++ b/blockchain/account/receivers.go @@ -14,16 +14,14 @@ const defaultReceiverExpiry = 30 * 24 * time.Hour // 30 days // 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, accID, accAlias string, expiresAt time.Time) (*txbuilder.Receiver, error) { +func (m *Manager) CreateReceiver(ctx context.Context, accInfo string, expiresAt time.Time) (*txbuilder.Receiver, error) { if expiresAt.IsZero() { expiresAt = time.Now().Add(defaultReceiverExpiry) } - if accAlias != "" { - s, err := m.FindByAlias(ctx, accAlias) - if err != nil { - return nil, err - } + accID := accInfo + + if s, err := m.FindByAlias(ctx, accInfo); err == nil { accID = s.ID } diff --git a/blockchain/jsend.go b/blockchain/jsend.go new file mode 100644 index 00000000..1e46ad04 --- /dev/null +++ b/blockchain/jsend.go @@ -0,0 +1,24 @@ +package blockchain + +import ( + "encoding/json" +) + +type jsendResponse struct { + Status string `json:"status,omitempty"` + Msg string `json:"msg,omitempty"` + Data interface{} `json:"data,omitempty"` +} + +func jsendWrapper(data interface{}, status, msg string) []byte { + response := &jsendResponse{ + Status: status, + Msg: msg, + Data: data, + } + rawResponse, err := json.Marshal(response) + if err != nil { + return DefaultRawResponse + } + return rawResponse +} diff --git a/blockchain/reactor.go b/blockchain/reactor.go index b8cbf174..4affa4cc 100755 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "reflect" + "strconv" "time" log "github.com/sirupsen/logrus" @@ -49,12 +50,14 @@ const ( ERROR = "error" ) +// Response describes the response standard. type Response struct { Status string Msg string Data []string } +// DefaultRawResponse is used as the default response when fail to get data. var DefaultRawResponse = []byte(`{"Status":"error","Msg":"Unable to get data","Data":null}`) //BlockchainReactor handles long-term catchup syncing. @@ -185,7 +188,7 @@ func (bcr *BlockchainReactor) BuildHander() { m.Handle("/peer-count", jsonHandler(bcr.peerCount)) m.Handle("/get-block-by-height", jsonHandler(bcr.getBlockByHeight)) m.Handle("/get-block-transactions-count-by-height", jsonHandler(bcr.getBlockTransactionsCountByHeight)) - m.Handle("/block-height", jsonHandler(bcr.getBlockHeight)) + m.Handle("/block-height", jsonHandler(bcr.blockHeight)) m.Handle("/is-mining", jsonHandler(bcr.isMining)) m.Handle("/gas-rate", jsonHandler(bcr.gasRate)) @@ -379,8 +382,9 @@ func (bcR *BlockchainReactor) getNetInfo() (*ctypes.ResultNetInfo, error) { return rpc.NetInfo(bcR.sw) } -func (bcR *BlockchainReactor) getBestBlockHash() *bc.Hash { - return bcR.chain.BestBlockHash() +func (bcr *BlockchainReactor) getBestBlockHash() []byte { + data := []string{bcr.chain.BestBlockHash().String()} + return resWrapper(data) } func (bcr *BlockchainReactor) getBlockHeaderByHash(strHash string) string { @@ -451,11 +455,11 @@ func (bcr *BlockchainReactor) getBlockByHash(strHash string) string { return string(ret) } -func (bcr *BlockchainReactor) getBlockByHeight(height uint64) string { +func (bcr *BlockchainReactor) getBlockByHeight(height uint64) []byte { legacyBlock, err := bcr.chain.GetBlockByHeight(height) if err != nil { log.WithField("error", err).Error("Fail to get block by hash") - return err.Error() + return DefaultRawResponse } bcBlock := legacy.MapBlock(legacyBlock) @@ -481,9 +485,10 @@ func (bcr *BlockchainReactor) getBlockByHeight(height uint64) string { ret, err := stdjson.Marshal(res) if err != nil { - return err.Error() + return DefaultRawResponse } - return string(ret) + data := []string{string(ret)} + return resWrapper(data) } func (bcr *BlockchainReactor) getBlockTransactionsCountByHash(strHash string) (int, error) { @@ -516,35 +521,53 @@ func (bcR *BlockchainReactor) BroadcastTransaction(tx *legacy.Tx) error { return nil } -func (bcr *BlockchainReactor) isNetListening() bool { - return bcr.sw.IsListening() +func (bcr *BlockchainReactor) isNetListening() []byte { + data := []string{strconv.FormatBool(bcr.sw.IsListening())} + return resWrapper(data) } -func (bcr *BlockchainReactor) peerCount() int { - return len(bcr.sw.Peers().List()) +func (bcr *BlockchainReactor) peerCount() []byte { + // TODO: use key-value instead of bare value + data := []string{strconv.FormatInt(int64(len(bcr.sw.Peers().List())), 16)} + return resWrapper(data) } -func (bcr *BlockchainReactor) isNetSyncing() bool { - return bcr.blockKeeper.IsCaughtUp() +func (bcr *BlockchainReactor) isNetSyncing() []byte { + data := []string{strconv.FormatBool(bcr.blockKeeper.IsCaughtUp())} + return resWrapper(data) } -func (bcr *BlockchainReactor) getBlockTransactionsCountByHeight(height uint64) (int, error) { +func (bcr *BlockchainReactor) getBlockTransactionsCountByHeight(height uint64) []byte { legacyBlock, err := bcr.chain.GetBlockByHeight(height) if err != nil { log.WithField("error", err).Error("Fail to get block by hash") - return -1, err + return DefaultRawResponse } - return len(legacyBlock.Transactions), nil + data := []string{strconv.FormatInt(int64(len(legacyBlock.Transactions)), 16)} + log.Infof("%v", data) + return resWrapper(data) } -func (bcr *BlockchainReactor) getBlockHeight() uint64 { - return bcr.chain.Height() +func (bcr *BlockchainReactor) blockHeight() []byte { + data := []string{strconv.FormatUint(bcr.chain.Height(), 16)} + return resWrapper(data) } -func (bcr *BlockchainReactor) isMining() bool { - return bcr.mining.IsMining() +func (bcr *BlockchainReactor) isMining() []byte { + data := []string{strconv.FormatBool(bcr.mining.IsMining())} + return resWrapper(data) } -func (bcr *BlockchainReactor) gasRate() int64 { - return validation.GasRate +func (bcr *BlockchainReactor) gasRate() []byte { + data := []string{strconv.FormatInt(validation.GasRate, 16)} + return resWrapper(data) +} + +func resWrapper(data []string) []byte { + response := Response{Status: SUCCESS, Data: data} + rawResponse, err := stdjson.Marshal(response) + if err != nil { + return DefaultRawResponse + } + return rawResponse } diff --git a/blockchain/receivers.go b/blockchain/receivers.go old mode 100644 new mode 100755 index af1dd99a..af14e29e --- a/blockchain/receivers.go +++ b/blockchain/receivers.go @@ -2,37 +2,22 @@ package blockchain import ( "context" - "sync" "time" - - "github.com/bytom/net/http/reqid" ) // POST /create-account-receiver -func (a *BlockchainReactor) createAccountReceiver(ctx context.Context, ins []struct { - AccountID string `json:"account_id"` - AccountAlias string `json:"account_alias"` - ExpiresAt time.Time `json:"expires_at"` -}) []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]) +func (a *BlockchainReactor) createAccountReceiver(ctx context.Context, ins struct { + AccountInfo string `json:"account_info"` + ExpiresAt time.Time `json:"expires_at"` +}) interface{} { + var response interface{} - receiver, err := a.accounts.CreateReceiver(subctx, ins[i].AccountID, ins[i].AccountAlias, ins[i].ExpiresAt) - if err != nil { - responses[i] = err - } else { - responses[i] = receiver - } - }(i) + receiver, err := a.accounts.CreateReceiver(nil, ins.AccountInfo, ins.ExpiresAt) + if err != nil { + response = err + } else { + response = receiver } - wg.Wait() - return responses + return response } diff --git a/blockchain/signers/signers.go b/blockchain/signers/signers.go index c158b845..6c9efe14 100755 --- a/blockchain/signers/signers.go +++ b/blockchain/signers/signers.go @@ -89,32 +89,7 @@ func Create(ctx context.Context, db dbm.DB, signerType string, xpubs []chainkd.X key := key xpubBytes = append(xpubBytes, key[:]) } - /* - - nullToken := sql.NullString{ - String: clientToken, - Valid: clientToken != "", - } - const q = ` - INSERT INTO signers (id, type, xpubs, quorum, client_token) - VALUES (next_chain_id($1::text), $2, $3, $4, $5) - ON CONFLICT (client_token) DO NOTHING - RETURNING id, key_index - ` - var ( - id string - keyIndex uint64 - ) - err := db.QueryRowContext(ctx, q, signerTypeMap[typ], typ, pq.ByteaArray(xpubBytes), quorum, nullToken). - Scan(&id, &keyIndex) - if err == sql.ErrNoRows && clientToken != "" { - return findByClientToken(ctx, db, clientToken) - } - if err != nil && err != sql.ErrNoRows { - return nil, errors.Wrap(err) - } - */ var ( id string keyIndex uint64 @@ -145,34 +120,6 @@ func New(id, typ string, xpubs [][]byte, quorum int, keyIndex uint64) (*Signer, }, nil } -/* -func findByClientToken(ctx context.Context, db pg.DB, clientToken string) (*Signer, error) { - const q = ` - SELECT id, type, xpubs, quorum, key_index - FROM signers WHERE client_token=$1 - ` - - var ( - s Signer - xpubBytes [][]byte - ) - err := db.QueryRowContext(ctx, q, clientToken). - Scan(&s.ID, &s.Type, (*pq.ByteaArray)(&xpubBytes), &s.Quorum, &s.KeyIndex) - if err != nil { - return nil, errors.Wrap(err) - } - - keys, err := ConvertKeys(xpubBytes) - if err != nil { - return nil, errors.WithDetail(errors.New("bad xpub in databse"), errors.Detail(err)) - } - - s.XPubs = keys - - return &s, 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) { @@ -215,48 +162,6 @@ func Find(ctx context.Context, db dbm.DB, typ, id string) (*Signer, error) { return &s, nil } -/* -// List returns a paginated set of Signers, limited to -// the provided type. -func List(ctx context.Context, db pg.DB, typ, prev string, limit int) ([]*Signer, string, error) { - const q = ` - SELECT id, type, xpubs, quorum, key_index - FROM signers WHERE type=$1 AND ($2='' OR $2 0 { - last = signers[len(signers)-1].ID - } - - return signers, last, nil -} -*/ - func ConvertKeys(xpubs [][]byte) ([]chainkd.XPub, error) { var xkeys []chainkd.XPub for i, xpub := range xpubs { diff --git a/blockchain/transact.go b/blockchain/transact.go index 6401e793..45aae11f 100644 --- a/blockchain/transact.go +++ b/blockchain/transact.go @@ -190,10 +190,6 @@ func (a *BlockchainReactor) waitForTxInBlock(ctx context.Context, tx *legacy.Tx, } } - if tx.MaxTime > 0 && tx.MaxTime < b.TimestampMS { - return 0, errors.Wrap(txbuilder.ErrRejected, "transaction max time exceeded") - } - // might still be in pool or might be rejected; we can't // tell definitively until its max time elapses. // Re-insert into the pool in case it was dropped. diff --git a/blockchain/txbuilder/builder.go b/blockchain/txbuilder/builder.go index d582348c..1b4a2549 100644 --- a/blockchain/txbuilder/builder.go +++ b/blockchain/txbuilder/builder.go @@ -6,7 +6,6 @@ import ( "time" "github.com/bytom/errors" - "github.com/bytom/protocol/bc" "github.com/bytom/protocol/bc/legacy" ) @@ -111,14 +110,6 @@ func (b *TemplateBuilder) Build() (*Template, *legacy.TxData, error) { tpl.Local = true } - // Update min & max times. - if !b.minTime.IsZero() && bc.Millis(b.minTime) > tx.MinTime { - tx.MinTime = bc.Millis(b.minTime) - } - if tx.MaxTime == 0 || tx.MaxTime > bc.Millis(b.maxTime) { - tx.MaxTime = bc.Millis(b.maxTime) - } - // Set transaction reference data if applicable. if len(b.referenceData) > 0 { tx.ReferenceData = b.referenceData diff --git a/blockchain/txbuilder/constraint.go b/blockchain/txbuilder/constraint.go index 467b1291..62ddf4c0 100644 --- a/blockchain/txbuilder/constraint.go +++ b/blockchain/txbuilder/constraint.go @@ -17,34 +17,6 @@ type constraint interface { code() []byte } -// timeConstraint means the tx is only valid within the given time -// bounds. Either value is allowed to be 0 meaning "ignore." -type timeConstraint struct { - minTimeMS, maxTimeMS uint64 -} - -func (t timeConstraint) code() []byte { - if t.minTimeMS == 0 && t.maxTimeMS == 0 { - return []byte{byte(vm.OP_TRUE)} - } - builder := vmutil.NewBuilder() - if t.minTimeMS > 0 { - builder.AddOp(vm.OP_MINTIME).AddInt64(int64(t.minTimeMS)).AddOp(vm.OP_GREATERTHANOREQUAL) - } - if t.maxTimeMS > 0 { - if t.minTimeMS > 0 { - // Consume the boolean left by the "mintime" clause, failing - // immediately if it's false, so that the result of the - // "maxtime" clause below is really (mintime clause && maxtime - // clause). - builder.AddOp(vm.OP_VERIFY) - } - builder.AddOp(vm.OP_MAXTIME).AddInt64(int64(t.maxTimeMS)).AddOp(vm.OP_LESSTHANOREQUAL) - } - prog, _ := builder.Build() // error is impossible - return prog -} - // outpointConstraint requires the outputID (and therefore, the outpoint) being spent to equal the // given value. type outputIDConstraint bc.Hash diff --git a/blockchain/txbuilder/finalize.go b/blockchain/txbuilder/finalize.go index 267b23ff..66b834ba 100644 --- a/blockchain/txbuilder/finalize.go +++ b/blockchain/txbuilder/finalize.go @@ -27,9 +27,6 @@ func FinalizeTx(ctx context.Context, c *protocol.Chain, tx *legacy.Tx) error { return err } - if tx.Tx.MaxTimeMs > 0 && tx.Tx.MaxTimeMs < c.TimestampMS() { - return errors.Wrap(ErrRejected, "tx expired") - } err = c.ValidateTx(tx) if errors.Root(err) == protocol.ErrBadTx { return errors.Sub(ErrRejected, err) diff --git a/blockchain/txbuilder/witness.go b/blockchain/txbuilder/witness.go index 7aae8d9b..5543e2a6 100644 --- a/blockchain/txbuilder/witness.go +++ b/blockchain/txbuilder/witness.go @@ -148,10 +148,6 @@ func buildSigProgram(tpl *Template, index uint32) []byte { return prog } constraints := make([]constraint, 0, 3+len(tpl.Transaction.Outputs)) - constraints = append(constraints, &timeConstraint{ - minTimeMS: tpl.Transaction.MinTime, - maxTimeMS: tpl.Transaction.MaxTime, - }) id := tpl.Transaction.Tx.InputIDs[index] if sp, err := tpl.Transaction.Tx.Spend(id); err == nil { constraints = append(constraints, outputIDConstraint(*sp.SpentOutputId)) diff --git a/blockchain/txbuilder/witness_test.go b/blockchain/txbuilder/witness_test.go index 296c06fc..153d1ac5 100644 --- a/blockchain/txbuilder/witness_test.go +++ b/blockchain/txbuilder/witness_test.go @@ -24,8 +24,6 @@ func TestInferConstraints(t *testing.T) { Outputs: []*legacy.TxOutput{ legacy.NewTxOutput(bc.AssetID{}, 123, []byte{10, 11, 12}, nil), }, - MinTime: 1, - MaxTime: 2, }), AllowAdditional: true, } @@ -35,7 +33,7 @@ func TestInferConstraints(t *testing.T) { if err != nil { t.Fatal(err) } - wantSrc := fmt.Sprintf("MINTIME 1 GREATERTHANOREQUAL VERIFY MAXTIME 2 LESSTHANOREQUAL VERIFY 0x%x OUTPUTID EQUAL VERIFY 0x2767f15c8af2f2c7225d5273fdd683edc714110a987d1054697c348aed4e6cc7 ENTRYDATA EQUAL VERIFY 0 0 123 0x0000000000000000000000000000000000000000000000000000000000000000 1 0x0a0b0c CHECKOUTPUT", spend.SpentOutputId.Bytes()) + wantSrc := fmt.Sprintf("0x%x OUTPUTID EQUAL VERIFY 0x2767f15c8af2f2c7225d5273fdd683edc714110a987d1054697c348aed4e6cc7 ENTRYDATA EQUAL VERIFY 0 0 123 0x0000000000000000000000000000000000000000000000000000000000000000 1 0x0a0b0c CHECKOUTPUT", spend.SpentOutputId.Bytes()) want, err := vm.Assemble(wantSrc) if err != nil { t.Fatal(err) diff --git a/blockchain/txdb/snapshot.go b/blockchain/txdb/snapshot.go index 9956cedd..45a22b51 100644 --- a/blockchain/txdb/snapshot.go +++ b/blockchain/txdb/snapshot.go @@ -13,7 +13,7 @@ import ( "github.com/bytom/protocol/state" ) -const snapshotPreFix = "MC:" +const snapshotPreFix = "SS:" func calcSnapshotKey(hash *bc.Hash) []byte { return []byte(snapshotPreFix + hash.String()) diff --git a/blockchain/txdb/snapshot_test.go b/blockchain/txdb/snapshot_test.go index 75d10987..ad03729c 100644 --- a/blockchain/txdb/snapshot_test.go +++ b/blockchain/txdb/snapshot_test.go @@ -8,8 +8,39 @@ import ( dbm "github.com/tendermint/tmlibs/db" "github.com/bytom/protocol/bc" + "github.com/bytom/protocol/state" ) +func TestProtoSnapshotTree(t *testing.T) { + testDB := dbm.NewDB("testdb", "leveldb", "temp") + defer os.RemoveAll("temp") + + hashes := []bc.Hash{} + insertSnap := state.Empty() + + hash := bc.Hash{} + for i := uint64(0); i <= uint64(10); i++ { + hash.V0 = i + hashes = append(hashes, hash) + insertSnap.Tree.Insert(hash.Bytes()) + } + + if err := saveSnapshot(testDB, insertSnap, &hash); err != nil { + t.Errorf(err.Error()) + } + + popSnap, err := getSnapshot(testDB, &hash) + if err != nil { + t.Errorf(err.Error()) + } + + for _, h := range hashes { + if !popSnap.Tree.Contains(h.Bytes()) { + t.Errorf("%s isn't in the snap tree", h.String()) + } + } +} + func TestCleanSnapshotDB(t *testing.T) { testDB := dbm.NewDB("testdb", "leveldb", "temp") defer os.RemoveAll("temp") diff --git a/blockchain/txfeeds.go b/blockchain/txfeeds.go index 9b54522b..da011614 100644 --- a/blockchain/txfeeds.go +++ b/blockchain/txfeeds.go @@ -14,17 +14,16 @@ import ( func (bcr *BlockchainReactor) createTxFeed(ctx context.Context, in struct { Alias string Filter string -}) error { +}) interface{} { if err := bcr.txFeedTracker.Create(ctx, in.Alias, in.Filter); err != nil { log.WithField("error", err).Error("Add TxFeed Failed") - return err + return jsendWrapper(nil, ERROR, err.Error()) } - return nil + return jsendWrapper("success", SUCCESS, "") } -func (bcr *BlockchainReactor) getTxFeedByAlias(ctx context.Context, filter string) ([]*txfeed.TxFeed, error) { +func (bcr *BlockchainReactor) getTxFeedByAlias(ctx context.Context, filter string) (*txfeed.TxFeed, error) { txFeed := &txfeed.TxFeed{} - txFeeds := []*txfeed.TxFeed{} jf, err := json.Marshal(filter) if err != nil { @@ -39,36 +38,43 @@ func (bcr *BlockchainReactor) getTxFeedByAlias(ctx context.Context, filter strin if err := json.Unmarshal(value, txFeed); err != nil { return nil, err } - txFeeds = append(txFeeds, txFeed) - return txFeeds, nil + return txFeed, nil } // POST /get-transaction-feed -func (bcr *BlockchainReactor) getTxFeed(ctx context.Context, in requestQuery) interface{} { - txfeeds, err := bcr.getTxFeedByAlias(ctx, in.Filter) +func (bcr *BlockchainReactor) getTxFeed(ctx context.Context, in struct { + Alias string `json:"alias,omitempty"` +}) interface{} { + txfeed, err := bcr.getTxFeedByAlias(ctx, in.Alias) if err != nil { - return err + return jsendWrapper(nil, ERROR, err.Error()) } - return txfeeds - + return jsendWrapper(txfeed, SUCCESS, "") } // POST /delete-transaction-feed func (bcr *BlockchainReactor) deleteTxFeed(ctx context.Context, in struct { Alias string `json:"alias,omitempty"` -}) error { - return bcr.txFeedTracker.Delete(ctx, in.Alias) +}) interface{} { + if err := bcr.txFeedTracker.Delete(ctx, in.Alias); err != nil { + return jsendWrapper(nil, ERROR, err.Error()) + } + return jsendWrapper("success", SUCCESS, "") } // POST /update-transaction-feed func (bcr *BlockchainReactor) updateTxFeed(ctx context.Context, in struct { Alias string Filter string -}) error { +}) interface{} { if err := bcr.txFeedTracker.Delete(ctx, in.Alias); err != nil { - return err + return jsendWrapper(nil, ERROR, err.Error()) + } + if err := bcr.txFeedTracker.Create(ctx, in.Alias, in.Filter); err != nil { + log.WithField("error", err).Error("Update TxFeed Failed") + return jsendWrapper(nil, ERROR, err.Error()) } - return bcr.txFeedTracker.Create(ctx, in.Alias, in.Filter) + return jsendWrapper("success", SUCCESS, "") } // txAfterIsBefore returns true if a is before b. It returns an error if either @@ -109,7 +115,7 @@ func (bcr *BlockchainReactor) getTxFeeds() ([]*txfeed.TxFeed, error) { func (bcr *BlockchainReactor) listTxFeeds(ctx context.Context, in requestQuery) interface{} { txfeeds, err := bcr.getTxFeeds() if err != nil { - return err + return jsendWrapper(nil, ERROR, err.Error()) } - return txfeeds + return jsendWrapper(txfeeds, SUCCESS, "") } diff --git a/cmd/bytomcli/main.go b/cmd/bytomcli/main.go index c9435edf..d85af7b6 100755 --- a/cmd/bytomcli/main.go +++ b/cmd/bytomcli/main.go @@ -89,6 +89,7 @@ var commands = map[string]*command{ "sign-transactions": {signTransactions}, "sub-create-issue-tx": {submitCreateIssueTransaction}, "sub-spend-account-tx": {submitSpendTransaction}, + "sub-control-receiver-tx": {submitReceiverTransaction}, "reset-password": {resetPassword}, "update-alias": {updateAlias}, "net-info": {netInfo}, @@ -527,29 +528,24 @@ func buildTransaction(client *rpc.Client, args []string) { func submitCreateIssueTransaction(client *rpc.Client, args []string) { if len(args) != 5 { - fatalln("error: need args: [account1 id] [account2 id] [asset id] [asset xprv] [issue amount]") + fmt.Println("error: need args: [account id] [asset id] [issue amount] [asset xprv] [account xprv]") + return } // Build Transaction. fmt.Printf("To build transaction:\n") // Now Issue actions buildReqFmt := ` {"actions": [ - { - "type":"spend_account_unspent_output", - "receiver":null, - "output_id":"%v", - "reference_data":{} - }, + {"type": "spend_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount":20000000, "account_id": "%s"}, {"type": "issue", "asset_id": "%s", "amount": %s}, - {"type": "control_account", "asset_id": "%s", "amount": %s, "account_id": "%s"}, - {"type": "control_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount": 8888888888, "account_id": "%s"}, - {"type": "control_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount": 8888888888, "account_id": "%s"} + {"type": "control_account", "asset_id": "%s", "amount": %s, "account_id": "%s"} ]}` - buildReqStr := fmt.Sprintf(buildReqFmt, config.GenerateGenesisTx().ResultIds[0], args[2], args[4], args[2], args[4], args[0], args[0], args[1]) + buildReqStr := fmt.Sprintf(buildReqFmt, args[0], args[1], args[2], args[1], args[2], args[0]) var buildReq blockchain.BuildRequest err := stdjson.Unmarshal([]byte(buildReqStr), &buildReq) if err != nil { fmt.Printf("json Unmarshal error.") + os.Exit(1) } tpl := make([]txbuilder.Template, 1) @@ -559,12 +555,29 @@ func submitCreateIssueTransaction(client *rpc.Client, args []string) { fmt.Printf("----------btm inputs:%v\n", tpl[0].Transaction.Inputs[0]) fmt.Printf("----------issue inputs:%v\n", tpl[0].Transaction.Inputs[1]) - var xprv_asset chainkd.XPrv - fmt.Printf("xprv_asset:%v\n", args[3]) - xprv_asset.UnmarshalText([]byte(args[3])) + mockWallet := make(map[chainkd.XPub]chainkd.XPrv) + var xprvAsset chainkd.XPrv + if err := xprvAsset.UnmarshalText([]byte(args[3])); err != nil { + fmt.Printf("xprv unmarshal error:%v\n", xprvAsset) + os.Exit(1) + } + mockWallet[xprvAsset.XPub()] = xprvAsset + + var xprvAccount chainkd.XPrv + if err := xprvAccount.UnmarshalText([]byte(args[4])); err != nil { + fmt.Printf("xprv unmarshal error:%v\n", xprvAccount) + os.Exit(1) + } + mockWallet[xprvAccount.XPub()] = xprvAccount + // sign-transaction - err = txbuilder.Sign(context.Background(), &tpl[0], []chainkd.XPub{xprv_asset.XPub()}, "", func(_ context.Context, _ chainkd.XPub, path [][]byte, data [32]byte, _ string) ([]byte, error) { - derived := xprv_asset.Derive(path) + err = txbuilder.Sign(context.Background(), &tpl[0], []chainkd.XPub{xprvAccount.XPub(), xprvAsset.XPub()}, "", func(_ context.Context, pub chainkd.XPub, path [][]byte, data [32]byte, _ string) ([]byte, error) { + prv, ok := mockWallet[pub] + if !ok { + fmt.Println("fail to get mockWallet pubkey") + os.Exit(1) + } + derived := prv.Derive(path) return derived.Sign(data[:]), nil }) if err != nil { @@ -581,9 +594,66 @@ func submitCreateIssueTransaction(client *rpc.Client, args []string) { fmt.Printf("submit transaction:%v\n", submitResponse) } +func submitReceiverTransaction(client *rpc.Client, args []string) { + if len(args) != 5 { + fmt.Println("error: need args: [account xprv] [account id] [asset id] [spend amount] [control_program]") + return + } + + var xprvAccount chainkd.XPrv + + err := xprvAccount.UnmarshalText([]byte(args[0])) + if err == nil { + fmt.Printf("xprv:%v\n", xprvAccount) + } else { + fmt.Printf("xprv unmarshal error:%v\n", xprvAccount) + os.Exit(1) + } + // Build Transaction-Spend_account + fmt.Printf("To build transaction:\n") + buildReqFmt := ` + {"actions": [ + {"type": "spend_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount":20000000, "account_id": "%s"}, + {"type": "spend_account", "asset_id": "%s","amount": %s,"account_id": "%s"}, + {"type": "control_receiver", "asset_id": "%s", "amount": %s, "receiver":{"control_program": "%s","expires_at":"2017-12-28T12:52:06.78309768+08:00"}} + ]}` + + buildReqStr := fmt.Sprintf(buildReqFmt, args[1], args[2], args[3], args[1], args[2], args[3], args[4]) + + var buildReq blockchain.BuildRequest + err = stdjson.Unmarshal([]byte(buildReqStr), &buildReq) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + tpl := make([]txbuilder.Template, 1) + client.Call(context.Background(), "/build-transaction", []*blockchain.BuildRequest{&buildReq}, &tpl) + fmt.Printf("tpl:%v\n", tpl) + + // sign-transaction-Spend_account + err = txbuilder.Sign(context.Background(), &tpl[0], []chainkd.XPub{xprvAccount.XPub()}, "", func(_ context.Context, _ chainkd.XPub, path [][]byte, data [32]byte, _ string) ([]byte, error) { + derived := xprvAccount.Derive(path) + return derived.Sign(data[:]), nil + }) + if err != nil { + fmt.Printf("sign-transaction error. err:%v\n", err) + os.Exit(1) + } + + fmt.Printf("sign tpl:%v\n", tpl[0]) + + // submit-transaction-Spend_account + var submitResponse interface{} + submitArg := blockchain.SubmitArg{Transactions: tpl, Wait: json.Duration{Duration: time.Duration(1000000)}, WaitUntil: "none"} + client.Call(context.Background(), "/submit-transaction", submitArg, &submitResponse) + fmt.Printf("submit transaction:%v\n", submitResponse) +} + func submitSpendTransaction(client *rpc.Client, args []string) { if len(args) != 5 { - fatalln("error: need args: [account1 id] [account2 id] [asset id] [account1 xprv] [spend amount]") + fmt.Println("error: need args: [account1 id] [account2 id] [asset id] [account1 xprv] [spend amount]") + return } var xprvAccount1 chainkd.XPrv @@ -628,8 +698,6 @@ func submitSpendTransaction(client *rpc.Client, args []string) { } fmt.Printf("sign tpl:%v\n", tpl[0]) - //fmt.Printf("sign tpl's SigningInstructions:%v\n", tpl[0].SigningInstructions[0]) - //fmt.Printf("SigningInstructions's SignatureWitnesses:%v\n", tpl[0].SigningInstructions[0].SignatureWitnesses[0]) // submit-transaction-Spend_account var submitResponse interface{} @@ -654,21 +722,23 @@ func createControlProgram(client *rpc.Client, args []string) { } func createAccountReceiver(client *rpc.Client, args []string) { - if len(args) != 0 { - fatalln("error:createAccountReceiver not use args") + if len(args) != 1 { + fmt.Println("error: need args: [account id] or [account alias]") + return } type Ins struct { - AccountID string `json:"account_id"` - AccountAlias string `json:"account_alias"` - ExpiresAt time.Time `json:"expires_at"` + AccountInfo string `json:"account_info"` + ExpiresAt time.Time `json:"expires_at"` } var ins Ins + var response interface{} + //TODO:undefined argument to ExpiresAt - ins.AccountID = "123456" - ins.AccountAlias = "zxcvbn" - responses := make([]interface{}, 50) - client.Call(context.Background(), "/create-Account-Receiver", &[]Ins{ins}, &responses) - fmt.Printf("responses:%v\n", responses) + + ins.AccountInfo = args[0] + + client.Call(context.Background(), "/create-account-receiver", &ins, &response) + fmt.Printf("responses:%v\n", response) } func createTxFeed(client *rpc.Client, args []string) { @@ -868,7 +938,6 @@ func listTransactions(client *rpc.Client, args []string) { for i, item := range response.Data { fmt.Println(i, "-----", item) } - } func listBalances(client *rpc.Client, args []string) { diff --git a/cmd/bytomd/commands/init.go b/cmd/bytomd/commands/init.go index 1f245471..381bef5e 100644 --- a/cmd/bytomd/commands/init.go +++ b/cmd/bytomd/commands/init.go @@ -9,6 +9,7 @@ import ( cmn "github.com/tendermint/tmlibs/common" "github.com/bytom/types" + cfg "github.com/bytom/config" "github.com/bytom/crypto/ed25519/chainkd" ) @@ -19,10 +20,18 @@ var initFilesCmd = &cobra.Command{ } func init() { + initFilesCmd.Flags().String("chain_id", config.ChainID, "Select [mainnet] or [testnet]") + RootCmd.AddCommand(initFilesCmd) } func initFiles(cmd *cobra.Command, args []string) { + if config.ChainID == "mainnet" { + cfg.EnsureRoot(config.RootDir, "mainnet") + } else { + cfg.EnsureRoot(config.RootDir, "testnet") + } + genFile := config.GenesisFile() if _, err := os.Stat(genFile); !os.IsNotExist(err) { log.WithField("genesis", config.GenesisFile()).Info("Already exits config file.") @@ -34,7 +43,7 @@ func initFiles(cmd *cobra.Command, args []string) { return } genDoc := types.GenesisDoc{ - ChainID: cmn.Fmt("bytom"), + ChainID: cmn.Fmt(config.ChainID), PrivateKey: hex.EncodeToString(xprv.Bytes()), } genDoc.SaveAs(genFile) diff --git a/cmd/bytomd/commands/root.go b/cmd/bytomd/commands/root.go index e8971a59..93988675 100644 --- a/cmd/bytomd/commands/root.go +++ b/cmd/bytomd/commands/root.go @@ -20,11 +20,6 @@ var RootCmd = &cobra.Command{ return err } config.SetRoot(config.RootDir) - if len(args) == 0 { - cfg.EnsureRoot(config.RootDir, "mainnet") - } else { - cfg.EnsureRoot(config.RootDir, args[0]) - } return nil }, } diff --git a/cmd/bytomd/main.go b/cmd/bytomd/main.go index e6d7dcbe..cf51c1d9 100644 --- a/cmd/bytomd/main.go +++ b/cmd/bytomd/main.go @@ -1,9 +1,11 @@ package main import ( - "github.com/bytom/cmd/bytomd/commands" - "github.com/tendermint/tmlibs/cli" "os" + + "github.com/tendermint/tmlibs/cli" + + "github.com/bytom/cmd/bytomd/commands" ) func main() { diff --git a/cmd/cobra/commands/accesstoken.go b/cmd/cobra/commands/accesstoken.go index 466c98c0..eebfa8d7 100644 --- a/cmd/cobra/commands/accesstoken.go +++ b/cmd/cobra/commands/accesstoken.go @@ -2,7 +2,9 @@ package commands import ( "context" + "encoding/base64" "encoding/json" + "fmt" "strings" "time" @@ -12,11 +14,38 @@ import ( //Token describe the access token. type Token struct { - ID string `json:"id"` + ID string `json:"id,omitempty"` Token string `json:"token,omitempty"` Type string `json:"type,omitempty"` Secret string `json:"secret,omitempty"` - Created time.Time `json:"created_at"` + Created time.Time `json:"created_at,omitempty"` +} + +type resp struct { + Status string `json:"status,omitempty"` + Msg string `json:"msg,omitempty"` + Data string `json:"data,omitempty"` +} + +type respToken struct { + Status string `json:"status,omitempty"` + Msg string `json:"msg,omitempty"` + Data []*Token `json:"data,omitempty"` +} + +func parseresp(response interface{}, pattern interface{}) error { + data, err := base64.StdEncoding.DecodeString(response.(string)) + if err != nil { + jww.ERROR.Println("response format error") + return err + } + + if err := json.Unmarshal(data, pattern); err != nil { + jww.ERROR.Println("result not json format", err) + return err + } + + return nil } var createAccessTokenCmd = &cobra.Command{ @@ -36,7 +65,21 @@ var createAccessTokenCmd = &cobra.Command{ client := mustRPCClient() client.Call(context.Background(), "/create-access-token", &token, &response) - jww.FEEDBACK.Printf("response: %v\n", response) + var rawresp resp + if err := parseresp(response, &rawresp); err != nil { + jww.ERROR.Println("parse response error") + return + } + + if rawresp.Status == "success" { + jww.FEEDBACK.Printf("%v\n", rawresp.Data) + return + } + + if rawresp.Status == "error" { + jww.ERROR.Println(rawresp.Msg) + return + } }, } @@ -50,17 +93,25 @@ var listAccessTokenCmd = &cobra.Command{ } var response interface{} - var tokens []Token client := mustRPCClient() client.Call(context.Background(), "/list-access-token", nil, &response) - if err := json.Unmarshal([]byte(response.(string)), &tokens); err != nil { - jww.ERROR.Println("result not json format") + var rawresp respToken + if err := parseresp(response, &rawresp); err != nil { + jww.ERROR.Println("parse response error") + return + } + + if rawresp.Status == "success" { + for i, v := range rawresp.Data { + fmt.Println(i, v.Token) + } return } - for i, v := range tokens { - jww.FEEDBACK.Printf("%d %v\n", i, v) + if rawresp.Status == "error" { + jww.ERROR.Println(rawresp.Msg) + return } }, } @@ -82,7 +133,22 @@ var deleteAccessTokenCmd = &cobra.Command{ client := mustRPCClient() client.Call(context.Background(), "/delete-access-token", &token, &response) - jww.FEEDBACK.Printf("response: %v\n", response) + var rawresp resp + + if err := parseresp(response, &rawresp); err != nil { + jww.ERROR.Println("parse response error") + return + } + + if rawresp.Status == "success" { + jww.FEEDBACK.Printf("%v\n", rawresp.Data) + return + } + + if rawresp.Status == "error" { + jww.ERROR.Println(rawresp.Msg) + return + } }, } @@ -103,6 +169,21 @@ var checkAccessTokenCmd = &cobra.Command{ client := mustRPCClient() client.Call(context.Background(), "/check-access-token", &token, &response) - jww.FEEDBACK.Printf("response: %v\n", response) + var rawresp resp + + if err := parseresp(response, &rawresp); err != nil { + jww.ERROR.Println("parse response error") + return + } + + if rawresp.Status == "success" { + jww.FEEDBACK.Printf("%v\n", rawresp.Data) + return + } + + if rawresp.Status == "error" { + jww.ERROR.Println(rawresp.Msg) + return + } }, } diff --git a/cmd/cobra/commands/block.go b/cmd/cobra/commands/block.go index c9cf7069..f2eb9607 100644 --- a/cmd/cobra/commands/block.go +++ b/cmd/cobra/commands/block.go @@ -2,6 +2,7 @@ package commands import ( "context" + "encoding/json" "strconv" "github.com/spf13/cobra" @@ -20,10 +21,24 @@ var blockHashCmd = &cobra.Command{ Use: "block-hash", Short: "Get the hash of most recent block", Run: func(cmd *cobra.Command, args []string) { - var response interface{} + var rawResponse []byte + var response blockchain.Response + client := mustRPCClient() - client.Call(context.Background(), "/get-best-block-hash", nil, &response) - jww.FEEDBACK.Printf("best block hash: %v\n", response) + client.Call(context.Background(), "/get-best-block-hash", nil, &rawResponse) + + if err := json.Unmarshal(rawResponse, &response); err != nil { + jww.ERROR.Println(err) + return + } + + if response.Status == blockchain.SUCCESS { + data := response.Data + hash := data[0] + jww.FEEDBACK.Printf("best block hash: %v\n", hash) + return + } + jww.ERROR.Println(response.Msg) }, } @@ -31,10 +46,28 @@ var blockHeightCmd = &cobra.Command{ Use: "block-height", Short: "Get the number of most recent block", Run: func(cmd *cobra.Command, args []string) { - var response interface{} + var rawResponse []byte + var response blockchain.Response + client := mustRPCClient() - client.Call(context.Background(), "/block-height", nil, &response) - jww.FEEDBACK.Printf("block height: %v\n", response) + client.Call(context.Background(), "/block-height", nil, &rawResponse) + + if err := json.Unmarshal(rawResponse, &response); err != nil { + jww.ERROR.Println(err) + return + } + + if response.Status == blockchain.SUCCESS { + data := response.Data + height, err := strconv.ParseInt(data[0], 16, 64) + if err != nil { + jww.ERROR.Println("Fail to parse response data") + return + } + jww.FEEDBACK.Printf("block height: %v\n", height) + return + } + jww.ERROR.Println(response.Msg) }, } @@ -94,15 +127,32 @@ var getBlockByHeightCmd = &cobra.Command{ jww.ERROR.Println("get-block-by-height args not valid\nUsage: get-block-by-height [height]") return } + ui64, err := strconv.ParseUint(args[0], 10, 64) if err != nil { jww.ERROR.Printf("Invalid height value") return } - var response interface{} + + var rawResponse []byte + var response blockchain.Response + client := mustRPCClient() - client.Call(context.Background(), "/get-block-by-height", ui64, &response) - jww.FEEDBACK.Printf("%v\n", response) + client.Call(context.Background(), "/get-block-by-height", ui64, &rawResponse) + + if err := json.Unmarshal(rawResponse, &response); err != nil { + jww.ERROR.Println(err) + return + } + + if response.Status == blockchain.SUCCESS { + data := response.Data + for idx, d := range data { + jww.FEEDBACK.Printf("%d : %v\n", idx, string(d)) + } + return + } + jww.ERROR.Println(response.Msg) }, } @@ -120,9 +170,28 @@ var getBlockTransactionsCountByHeightCmd = &cobra.Command{ jww.ERROR.Printf("Invalid height value") return } - var response interface{} + + var rawResponse []byte + var response blockchain.Response + client := mustRPCClient() - client.Call(context.Background(), "/get-block-transactions-count-by-height", ui64, &response) - jww.FEEDBACK.Printf("transactions count: %v\n", response) + client.Call(context.Background(), "/get-block-transactions-count-by-height", ui64, &rawResponse) + + if err := json.Unmarshal(rawResponse, &response); err != nil { + jww.ERROR.Println(err) + return + } + + if response.Status == blockchain.SUCCESS { + data := response.Data + cnt, err := strconv.ParseInt(data[0], 16, 64) + if err != nil { + jww.ERROR.Println("Fail to parse response data") + return + } + jww.FEEDBACK.Printf("transactions count: %v\n", cnt) + return + } + jww.ERROR.Println(response.Msg) }, } diff --git a/cmd/cobra/commands/bytomcli.go b/cmd/cobra/commands/bytomcli.go index 4f613a83..2a7c5f89 100644 --- a/cmd/cobra/commands/bytomcli.go +++ b/cmd/cobra/commands/bytomcli.go @@ -119,6 +119,12 @@ func AddCommands() { BytomcliCmd.AddCommand(deleteAccessTokenCmd) BytomcliCmd.AddCommand(checkAccessTokenCmd) + BytomcliCmd.AddCommand(createTransactionFeedCmd) + BytomcliCmd.AddCommand(listTransactionFeedsCmd) + BytomcliCmd.AddCommand(deleteTransactionFeedCmd) + BytomcliCmd.AddCommand(getTransactionFeedCmd) + BytomcliCmd.AddCommand(updateTransactionFeedCmd) + BytomcliCmd.AddCommand(versionCmd) } diff --git a/cmd/cobra/commands/mining.go b/cmd/cobra/commands/mining.go index 289d4aa2..b5bb8ea1 100644 --- a/cmd/cobra/commands/mining.go +++ b/cmd/cobra/commands/mining.go @@ -2,18 +2,39 @@ package commands import ( "context" + "encoding/json" + "strconv" "github.com/spf13/cobra" jww "github.com/spf13/jwalterweatherman" + + "github.com/bytom/blockchain" ) var isMiningCmd = &cobra.Command{ Use: "is-mining", Short: "If client is actively mining new blocks", Run: func(cmd *cobra.Command, args []string) { - var response interface{} + var rawResponse []byte + var response blockchain.Response client := mustRPCClient() - client.Call(context.Background(), "/is-mining", nil, &response) - jww.FEEDBACK.Printf("is mining: %v\n", response) + client.Call(context.Background(), "/is-mining", nil, &rawResponse) + + if err := json.Unmarshal(rawResponse, &response); err != nil { + jww.ERROR.Println(err) + return + } + + if response.Status == blockchain.SUCCESS { + data := response.Data + res, err := strconv.ParseBool(data[0]) + if err != nil { + jww.ERROR.Println("Fail to parse response data") + return + } + jww.FEEDBACK.Printf("is mining: %v\n", res) + return + } + jww.ERROR.Println(response.Msg) }, } diff --git a/cmd/cobra/commands/net.go b/cmd/cobra/commands/net.go index 4e198b28..7865b093 100644 --- a/cmd/cobra/commands/net.go +++ b/cmd/cobra/commands/net.go @@ -2,9 +2,13 @@ package commands import ( "context" + "encoding/json" + "strconv" "github.com/spf13/cobra" jww "github.com/spf13/jwalterweatherman" + + "github.com/bytom/blockchain" ) var netInfoCmd = &cobra.Command{ @@ -17,15 +21,34 @@ var netInfoCmd = &cobra.Command{ jww.FEEDBACK.Printf("net info: %v\n", response) }, } - var netListeningCmd = &cobra.Command{ Use: "net-listening", Short: "If client is actively listening for network connections", Run: func(cmd *cobra.Command, args []string) { - var response interface{} + var rawResponse []byte + var response blockchain.Response + client := mustRPCClient() - client.Call(context.Background(), "/net-listening", nil, &response) - jww.FEEDBACK.Printf("net listening: %v\n", response) + client.Call(context.Background(), "/net-listening", nil, &rawResponse) + + if err := json.Unmarshal(rawResponse, &response); err != nil { + jww.ERROR.Println(err) + return + } + + // TODO: code reuse + if response.Status == blockchain.SUCCESS { + data := response.Data + res, err := strconv.ParseBool(data[0]) + if err != nil { + jww.ERROR.Println("Fail to parse response data") + return + } + jww.FEEDBACK.Printf("net listening: %v\n", res) + return + } + jww.ERROR.Println(response.Msg) + }, } @@ -33,10 +56,29 @@ var peerCountCmd = &cobra.Command{ Use: "peer-count", Short: "Number of peers currently connected to the client", Run: func(cmd *cobra.Command, args []string) { - var response interface{} + var rawResponse []byte + var response blockchain.Response + client := mustRPCClient() - client.Call(context.Background(), "/peer-count", nil, &response) - jww.FEEDBACK.Printf("peer count: %v\n", response) + client.Call(context.Background(), "/peer-count", nil, &rawResponse) + + if err := json.Unmarshal(rawResponse, &response); err != nil { + jww.ERROR.Println(err) + return + } + + if response.Status == blockchain.SUCCESS { + data := response.Data + i, err := strconv.ParseInt(data[0], 16, 64) + if err != nil { + jww.ERROR.Println("Fail to parse response data") + return + } + jww.FEEDBACK.Printf("peer count: %v\n", i) + return + } + jww.ERROR.Println(response.Msg) + }, } @@ -44,9 +86,27 @@ var netSyncingCmd = &cobra.Command{ Use: "net-syncing", Short: "If the network is still syncing", Run: func(cmd *cobra.Command, args []string) { - var response interface{} + var rawResponse []byte + var response blockchain.Response + client := mustRPCClient() - client.Call(context.Background(), "/net-syncing", nil, &response) - jww.FEEDBACK.Printf("net syncing: %v\n", response) + client.Call(context.Background(), "/net-syncing", nil, &rawResponse) + + if err := json.Unmarshal(rawResponse, &response); err != nil { + jww.ERROR.Println(err) + return + } + + if response.Status == blockchain.SUCCESS { + data := response.Data + res, err := strconv.ParseBool(data[0]) + if err != nil { + jww.ERROR.Println("Fail to parse response data") + return + } + jww.FEEDBACK.Printf("net syncing: %v\n", res) + return + } + jww.ERROR.Println(response.Msg) }, } diff --git a/cmd/cobra/commands/transaction.go b/cmd/cobra/commands/transaction.go index e8223e81..ade332f6 100644 --- a/cmd/cobra/commands/transaction.go +++ b/cmd/cobra/commands/transaction.go @@ -2,18 +2,40 @@ package commands import ( "context" + "encoding/json" + "strconv" "github.com/spf13/cobra" jww "github.com/spf13/jwalterweatherman" + + "github.com/bytom/blockchain" ) var gasRateCmd = &cobra.Command{ Use: "gas-rate", Short: "Print the current gas rate", Run: func(cmd *cobra.Command, args []string) { - var response interface{} + var rawResponse []byte + var response blockchain.Response + client := mustRPCClient() - client.Call(context.Background(), "/gas-rate", nil, &response) - jww.FEEDBACK.Printf("gas rate: %v\n", response) + client.Call(context.Background(), "/gas-rate", nil, &rawResponse) + + if err := json.Unmarshal(rawResponse, &response); err != nil { + jww.ERROR.Println(err) + return + } + + if response.Status == blockchain.SUCCESS { + data := response.Data + i, err := strconv.ParseInt(data[0], 16, 64) + if err != nil { + jww.ERROR.Println("Fail to parse response data") + return + } + jww.FEEDBACK.Printf("gas rate: %v\n", i) + return + } + jww.ERROR.Println(response.Msg) }, } diff --git a/cmd/cobra/commands/txfeed.go b/cmd/cobra/commands/txfeed.go new file mode 100644 index 00000000..6eae8c06 --- /dev/null +++ b/cmd/cobra/commands/txfeed.go @@ -0,0 +1,204 @@ +package commands + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + jww "github.com/spf13/jwalterweatherman" +) + +type txFeed struct { + Alias string `json:"alias"` + Filter string `json:"filter,omitempty"` +} + +type respArrayTxFeed struct { + Status string `json:"status,omitempty"` + Msg string `json:"msg,omitempty"` + Data []*txFeed `json:"data,omitempty"` +} + +type respTxFeed struct { + Status string `json:"status,omitempty"` + Msg string `json:"msg,omitempty"` + Data txFeed `json:"data,omitempty"` +} + +var createTransactionFeedCmd = &cobra.Command{ + Use: "create-transaction-feed", + Short: "Create a transaction feed filter", + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 2 { + jww.ERROR.Println("create-transaction-feed needs 2 args") + return + } + + var in txFeed + in.Alias = args[0] + in.Filter = args[1] + + var response interface{} + + client := mustRPCClient() + client.Call(context.Background(), "/create-transaction-feed", &in, &response) + + var rawresp resp + + if err := parseresp(response, &rawresp); err != nil { + jww.ERROR.Println("parse response error") + return + } + + if rawresp.Status == "success" { + jww.FEEDBACK.Printf("%v\n", rawresp.Data) + return + } + + if rawresp.Status == "error" { + jww.ERROR.Println(rawresp.Msg) + return + } + }, +} + +var listTransactionFeedsCmd = &cobra.Command{ + Use: "list-transaction-feeds", + Short: "list all of transaction feeds", + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 0 { + jww.ERROR.Println("list-transaction-feeds takes no args") + return + } + + var in requestQuery + var response interface{} + + client := mustRPCClient() + client.Call(context.Background(), "/list-transaction-feeds", in, &response) + + var rawresp respArrayTxFeed + if err := parseresp(response, &rawresp); err != nil { + jww.ERROR.Println("parse response error") + return + } + + if rawresp.Status == "success" { + for i, v := range rawresp.Data { + fmt.Println(i, v.Alias, v.Filter) + } + return + } + + if rawresp.Status == "error" { + jww.ERROR.Println(rawresp.Msg) + return + } + }, +} + +var deleteTransactionFeedCmd = &cobra.Command{ + Use: "delete-transaction-feed", + Short: "Delete a transaction feed filter", + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 1 { + jww.ERROR.Println("delete-transaction-feed needs 1 args") + return + } + + var in txFeed + in.Alias = args[0] + + var response interface{} + + client := mustRPCClient() + client.Call(context.Background(), "/delete-transaction-feed", &in, &response) + + var rawresp resp + if err := parseresp(response, &rawresp); err != nil { + jww.ERROR.Println("parse response error") + return + } + + if rawresp.Status == "success" { + jww.FEEDBACK.Printf("%v\n", rawresp.Data) + return + } + + if rawresp.Status == "error" { + jww.ERROR.Println(rawresp.Msg) + return + } + }, +} + +var getTransactionFeedCmd = &cobra.Command{ + Use: "get-transaction-feed", + Short: "get a transaction feed by alias", + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 1 { + jww.ERROR.Println("get-transaction-feed needs 1 args") + return + } + + var in txFeed + in.Alias = args[0] + var response interface{} + + client := mustRPCClient() + client.Call(context.Background(), "/get-transaction-feed", &in, &response) + + var rawresp respTxFeed + + if err := parseresp(response, &rawresp); err != nil { + jww.ERROR.Println("parse response error") + return + } + + if rawresp.Status == "success" { + fmt.Println(rawresp.Data) + return + } + + if rawresp.Status == "error" { + jww.ERROR.Println(rawresp.Msg) + return + } + }, +} + +var updateTransactionFeedCmd = &cobra.Command{ + Use: "update-transaction-feed", + Short: "Update transaction feed", + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 2 { + jww.ERROR.Println("update-transaction-feed needs 2 args") + return + } + + var in txFeed + in.Alias = args[0] + in.Filter = args[1] + + var response interface{} + + client := mustRPCClient() + client.Call(context.Background(), "/update-transaction-feed", &in, &response) + + var rawresp resp + if err := parseresp(response, &rawresp); err != nil { + jww.ERROR.Println("parse response error") + return + } + + if rawresp.Status == "success" { + jww.FEEDBACK.Printf("%v\n", rawresp.Data) + return + } + + if rawresp.Status == "error" { + jww.ERROR.Println(rawresp.Msg) + return + } + }, +} diff --git a/config/genesis.go b/config/genesis.go index c6df4bf5..1fbe28ca 100644 --- a/config/genesis.go +++ b/config/genesis.go @@ -3,20 +3,20 @@ package config import ( log "github.com/sirupsen/logrus" - "github.com/bytom/protocol/bc/legacy" - "github.com/bytom/protocol/bc" "github.com/bytom/consensus" - "github.com/bytom/protocol/state" "github.com/bytom/crypto/sha3pool" + "github.com/bytom/protocol/bc" + "github.com/bytom/protocol/bc/legacy" + "github.com/bytom/protocol/state" ) // Generate genesis transaction func GenerateGenesisTx() *legacy.Tx { txData := legacy.TxData{ - Version: 1, + Version: 1, SerializedSize: 63, - Inputs: []*legacy.TxInput{}, - Outputs:[]*legacy.TxOutput{ + Inputs: []*legacy.TxInput{}, + Outputs: []*legacy.TxOutput{ &legacy.TxOutput{ AssetVersion: 1, OutputCommitment: legacy.OutputCommitment{ @@ -29,8 +29,6 @@ func GenerateGenesisTx() *legacy.Tx { }, }, }, - MinTime: 0, - MaxTime: 1511318565142, } return legacy.NewTx(txData) @@ -53,10 +51,10 @@ func GenerateGenesisBlock() *legacy.Block { sha3pool.Sum256(seed[:], make([]byte, 32)) genesisBlock := &legacy.Block{ - BlockHeader: legacy.BlockHeader{ - Version: 1, - Height: 1, - Seed: bc.NewHash(seed), + BlockHeader: legacy.BlockHeader{ + Version: 1, + Height: 1, + Seed: bc.NewHash(seed), TimestampMS: 1511318565142, BlockCommitment: legacy.BlockCommitment{ TransactionsMerkleRoot: merkleRoot, diff --git a/exp/Readme.md b/exp/Readme.md deleted file mode 100644 index c638c375..00000000 --- a/exp/Readme.md +++ /dev/null @@ -1,6 +0,0 @@ -Experimental tree -================= - -Code in this tree may be committed to `main` with minimal review, but may not be imported by production code. - -A thorough code review is required to move code out of this tree and into Chain Core proper. diff --git a/exp/ivy/compiler/ast.go b/exp/ivy/compiler/ast.go deleted file mode 100644 index 22e6e41e..00000000 --- a/exp/ivy/compiler/ast.go +++ /dev/null @@ -1,318 +0,0 @@ -package compiler - -import ( - "encoding/hex" - "fmt" - "strconv" - "strings" - - chainjson "github.com/bytom/encoding/json" -) - -// Contract is a compiled Ivy contract. -type Contract struct { - // Name is the contract name. - Name string `json:"name"` - - // Params is the list of contract parameters. - Params []*Param `json:"params,omitempty"` - - // Clauses is the list of contract clauses. - Clauses []*Clause `json:"clauses"` - - // Value is the name of the value locked by the contract. - Value string `json:"value"` - - // Body is the optimized bytecode of the contract body. This is not - // a complete program! Use instantiate to turn this (plus some - // arguments) into a program. - Body chainjson.HexBytes `json:"body_bytecode"` - - // Opcodes is the human-readable string of opcodes corresponding to - // Body. - Opcodes string `json:"body_opcodes,omitempty"` - - // Recursive tells whether this contract calls itself. (This is - // used to select between two possible instantiation options.) - Recursive bool `json:"recursive"` - - // Pre-optimized list of instruction steps, with stack snapshots. - Steps []Step `json:"-"` -} - -// Param is a contract or clause parameter. -type Param struct { - // Name is the parameter name. - Name string `json:"name"` - - // Type is the declared parameter type. - Type typeDesc `json:"declared_type"` - - // InferredType, if available, is a more-specific type than Type, - // inferred from the logic of the contract. - InferredType typeDesc `json:"inferred_type,omitempty"` -} - -// Clause is a compiled contract clause. -type Clause struct { - // Name is the clause name. - Name string `json:"name"` - - // Params is the list of clause parameters. - Params []*Param `json:"params,omitempty"` - - // Reqs is the list of requirements (from the clause's "requires" - // section). - Reqs []*ClauseReq `json:"reqs,omitempty"` - - statements []statement - - // MinTimes is the list of expressions passed to after() in this - // clause. - MinTimes []string `json:"mintimes,omitempty"` - - // MaxTimes is the list of expressions passed to before() in this - // clause. - MaxTimes []string `json:"maxtimes,omitempty"` - - // HashCalls is the list of hash functions and their arguments used - // in this clause. - HashCalls []HashCall `json:"hash_calls,omitempty"` - - // Values is the list of values unlocked or relocked in this clause. - Values []ValueInfo `json:"values"` - - // Contracts is the list of contracts called by this clause. - Contracts []string `json:"contracts,omitempty"` -} - -// HashCall describes a call to a hash function. -type HashCall struct { - // HashType is "sha3" or "sha256". - HashType string `json:"hash_type"` - - // Arg is the expression passed to the hash function. - Arg string `json:"arg"` - - // ArgType is the type of Arg. - ArgType string `json:"arg_type"` -} - -// ClauseReq describes a payment requirement of a clause (one of the -// things after the "requires" keyword). -type ClauseReq struct { - Name string `json:"name"` - - assetExpr, amountExpr expression - - // Asset is the expression describing the required asset. - Asset string `json:"asset"` - - // Amount is the expression describing the required amount. - Amount string `json:"amount"` -} - -type statement interface { - countVarRefs(map[string]int) -} - -type verifyStatement struct { - expr expression -} - -func (s verifyStatement) countVarRefs(counts map[string]int) { - s.expr.countVarRefs(counts) -} - -type lockStatement struct { - locked expression - program expression - - // Added as a decoration, used by CHECKOUTPUT - index int64 -} - -func (s lockStatement) countVarRefs(counts map[string]int) { - s.locked.countVarRefs(counts) - s.program.countVarRefs(counts) -} - -type unlockStatement struct { - expr expression -} - -func (s unlockStatement) countVarRefs(counts map[string]int) { - s.expr.countVarRefs(counts) -} - -type expression interface { - String() string - typ(*environ) typeDesc - countVarRefs(map[string]int) -} - -type binaryExpr struct { - left, right expression - op *binaryOp -} - -func (e binaryExpr) String() string { - return fmt.Sprintf("(%s %s %s)", e.left, e.op.op, e.right) -} - -func (e binaryExpr) typ(*environ) typeDesc { - return e.op.result -} - -func (e binaryExpr) countVarRefs(counts map[string]int) { - e.left.countVarRefs(counts) - e.right.countVarRefs(counts) -} - -type unaryExpr struct { - op *unaryOp - expr expression -} - -func (e unaryExpr) String() string { - return fmt.Sprintf("%s%s", e.op.op, e.expr) -} - -func (e unaryExpr) typ(*environ) typeDesc { - return e.op.result -} - -func (e unaryExpr) countVarRefs(counts map[string]int) { - e.expr.countVarRefs(counts) -} - -type callExpr struct { - fn expression - args []expression -} - -func (e callExpr) String() string { - var argStrs []string - for _, a := range e.args { - argStrs = append(argStrs, a.String()) - } - return fmt.Sprintf("%s(%s)", e.fn, strings.Join(argStrs, ", ")) -} - -func (e callExpr) typ(env *environ) typeDesc { - if b := referencedBuiltin(e.fn); b != nil { - switch b.name { - case "sha3": - if len(e.args) == 1 { - switch e.args[0].typ(env) { - case strType: - return sha3StrType - case pubkeyType: - return sha3PubkeyType - } - } - - case "sha256": - if len(e.args) == 1 { - switch e.args[0].typ(env) { - case strType: - return sha256StrType - case pubkeyType: - return sha256PubkeyType - } - } - } - - return b.result - } - if e.fn.typ(env) == predType { - return boolType - } - if e.fn.typ(env) == contractType { - return progType - } - return nilType -} - -func (e callExpr) countVarRefs(counts map[string]int) { - e.fn.countVarRefs(counts) - for _, a := range e.args { - a.countVarRefs(counts) - } -} - -type varRef string - -func (v varRef) String() string { - return string(v) -} - -func (e varRef) typ(env *environ) typeDesc { - if entry := env.lookup(string(e)); entry != nil { - return entry.t - } - return nilType -} - -func (e varRef) countVarRefs(counts map[string]int) { - counts[string(e)]++ -} - -type bytesLiteral []byte - -func (e bytesLiteral) String() string { - return "0x" + hex.EncodeToString([]byte(e)) -} - -func (bytesLiteral) typ(*environ) typeDesc { - return "String" -} - -func (bytesLiteral) countVarRefs(map[string]int) {} - -type integerLiteral int64 - -func (e integerLiteral) String() string { - return strconv.FormatInt(int64(e), 10) -} - -func (integerLiteral) typ(*environ) typeDesc { - return "Integer" -} - -func (integerLiteral) countVarRefs(map[string]int) {} - -type booleanLiteral bool - -func (e booleanLiteral) String() string { - if e { - return "true" - } - return "false" -} - -func (booleanLiteral) typ(*environ) typeDesc { - return "Boolean" -} - -func (booleanLiteral) countVarRefs(map[string]int) {} - -type listExpr []expression - -func (e listExpr) String() string { - var elts []string - for _, elt := range e { - elts = append(elts, elt.String()) - } - return fmt.Sprintf("[%s]", strings.Join(elts, ", ")) -} - -func (listExpr) typ(*environ) typeDesc { - return "List" -} - -func (e listExpr) countVarRefs(counts map[string]int) { - for _, elt := range e { - elt.countVarRefs(counts) - } -} diff --git a/exp/ivy/compiler/builder.go b/exp/ivy/compiler/builder.go deleted file mode 100644 index 180896f2..00000000 --- a/exp/ivy/compiler/builder.go +++ /dev/null @@ -1,210 +0,0 @@ -package compiler - -import ( - "fmt" - "strconv" - "strings" -) - -type builder struct { - items []*builderItem - pendingVerify *builderItem -} - -type builderItem struct { - opcodes string - stk stack -} - -func (b *builder) add(opcodes string, newstack stack) stack { - if b.pendingVerify != nil { - b.items = append(b.items, b.pendingVerify) - b.pendingVerify = nil - } - item := &builderItem{opcodes: opcodes, stk: newstack} - if opcodes == "VERIFY" { - b.pendingVerify = item - } else { - b.items = append(b.items, item) - } - return newstack -} - -func (b *builder) addRoll(stk stack, n int) stack { - b.addInt64(stk, int64(n)) - return b.add("ROLL", stk.roll(n)) -} - -func (b *builder) addDup(stk stack) stack { - return b.add("DUP", stk.dup()) -} - -func (b *builder) addInt64(stk stack, n int64) stack { - s := strconv.FormatInt(n, 10) - return b.add(s, stk.add(s)) -} - -func (b *builder) addNumEqual(stk stack, desc string) stack { - return b.add("NUMEQUAL", stk.dropN(2).add(desc)) -} - -func (b *builder) addJumpIf(stk stack, label string) stack { - return b.add(fmt.Sprintf("JUMPIF:$%s", label), stk.drop()) -} - -func (b *builder) addJumpTarget(stk stack, label string) stack { - return b.add("$"+label, stk) -} - -func (b *builder) addDrop(stk stack) stack { - return b.add("DROP", stk.drop()) -} - -func (b *builder) forgetPendingVerify() { - b.pendingVerify = nil -} - -func (b *builder) addJump(stk stack, label string) stack { - return b.add(fmt.Sprintf("JUMP:$%s", label), stk) -} - -func (b *builder) addVerify(stk stack) stack { - return b.add("VERIFY", stk.drop()) -} - -func (b *builder) addData(stk stack, data []byte) stack { - var s string - switch len(data) { - case 0: - s = "0" - case 1: - s = strconv.FormatInt(int64(data[0]), 10) - default: - s = fmt.Sprintf("0x%x", data) - } - return b.add(s, stk.add(s)) -} - -func (b *builder) addAmount(stk stack) stack { - return b.add("AMOUNT", stk.add("")) -} - -func (b *builder) addAsset(stk stack) stack { - return b.add("ASSET", stk.add("")) -} - -func (b *builder) addCheckOutput(stk stack, desc string) stack { - return b.add("CHECKOUTPUT", stk.dropN(6).add(desc)) -} - -func (b *builder) addBoolean(stk stack, val bool) stack { - if val { - return b.add("TRUE", stk.add("true")) - } - return b.add("FALSE", stk.add("false")) -} - -func (b *builder) addOps(stk stack, ops string, desc string) stack { - return b.add(ops, stk.add(desc)) -} - -func (b *builder) addToAltStack(stk stack) (stack, string) { - t := stk.top() - return b.add("TOALTSTACK", stk.drop()), t -} - -func (b *builder) addTxSigHash(stk stack) stack { - return b.add("TXSIGHASH", stk.add("")) -} - -func (b *builder) addFromAltStack(stk stack, alt string) stack { - return b.add("FROMALTSTACK", stk.add(alt)) -} - -func (b *builder) addSwap(stk stack) stack { - return b.add("SWAP", stk.swap()) -} - -func (b *builder) addCheckMultisig(stk stack, n int, desc string) stack { - return b.add("CHECKMULTISIG", stk.dropN(n).add(desc)) -} - -func (b *builder) addOver(stk stack) stack { - return b.add("OVER", stk.over()) -} - -func (b *builder) addPick(stk stack, n int) stack { - b.addInt64(stk, int64(n)) - return b.add("PICK", stk.pick(n)) -} - -func (b *builder) addCatPushdata(stk stack, desc string) stack { - return b.add("CATPUSHDATA", stk.dropN(2).add(desc)) -} - -func (b *builder) addCat(stk stack, desc string) stack { - return b.add("CAT", stk.dropN(2).add(desc)) -} - -func (b *builder) opcodes() string { - var ops []string - for _, item := range b.items { - ops = append(ops, item.opcodes) - } - return strings.Join(ops, " ") -} - -// This is for producing listings like: -// 5 | [... borrower lender deadline balanceAmount balanceAsset 5] -// ROLL | [... borrower lender deadline balanceAmount balanceAsset ] -// JUMPIF:$default | [... borrower lender deadline balanceAmount balanceAsset] -// $repay | [... borrower lender deadline balanceAmount balanceAsset] -// 0 | [... borrower lender deadline balanceAmount balanceAsset 0] -// 0 | [... borrower lender deadline balanceAmount balanceAsset 0 0] -// 3 | [... borrower lender deadline balanceAmount balanceAsset 0 0 3] -// ROLL | [... borrower lender deadline balanceAsset 0 0 balanceAmount] -// 3 | [... borrower lender deadline balanceAsset 0 0 balanceAmount 3] -// ROLL | [... borrower lender deadline 0 0 balanceAmount balanceAsset] -// 1 | [... borrower lender deadline 0 0 balanceAmount balanceAsset 1] -// 6 | [... borrower lender deadline 0 0 balanceAmount balanceAsset 1 6] -// ROLL | [... borrower deadline 0 0 balanceAmount balanceAsset 1 lender] -// CHECKOUTPUT | [... borrower deadline checkOutput(payment, lender)] -// VERIFY | [... borrower deadline] -// 1 | [... borrower deadline 1] -// 0 | [... borrower deadline 1 0] -// AMOUNT | [... borrower deadline 1 0 ] -// ASSET | [... borrower deadline 1 0 ] -// 1 | [... borrower deadline 1 0 1] -// 6 | [... borrower deadline 1 0 1 6] -// ROLL | [... deadline 1 0 1 borrower] -// CHECKOUTPUT | [... deadline checkOutput(collateral, borrower)] -// JUMP:$_end | [... borrower lender deadline balanceAmount balanceAsset] -// $default | [... borrower lender deadline balanceAmount balanceAsset] -// 2 | [... borrower lender deadline balanceAmount balanceAsset 2] -// ROLL | [... borrower lender balanceAmount balanceAsset deadline] -// MINTIME LESSTHAN | [... borrower lender balanceAmount balanceAsset after(deadline)] -// VERIFY | [... borrower lender balanceAmount balanceAsset] -// 0 | [... borrower lender balanceAmount balanceAsset 0] -// 0 | [... borrower lender balanceAmount balanceAsset 0 0] -// AMOUNT | [... borrower lender balanceAmount balanceAsset 0 0 ] -// ASSET | [... borrower lender balanceAmount balanceAsset 0 0 ] -// 1 | [... borrower lender balanceAmount balanceAsset 0 0 1] -// 7 | [... borrower lender balanceAmount balanceAsset 0 0 1 7] -// ROLL | [... borrower balanceAmount balanceAsset 0 0 1 lender] -// CHECKOUTPUT | [... borrower balanceAmount balanceAsset checkOutput(collateral, lender)] -// $_end | [... borrower lender deadline balanceAmount balanceAsset] - -type ( - Step struct { - Opcodes string `json:"opcodes"` - Stack string `json:"stack"` - } -) - -func (b *builder) steps() []Step { - var result []Step - for _, item := range b.items { - result = append(result, Step{item.opcodes, item.stk.String()}) - } - return result -} diff --git a/exp/ivy/compiler/builder_test.go b/exp/ivy/compiler/builder_test.go deleted file mode 100644 index 63ec6b1d..00000000 --- a/exp/ivy/compiler/builder_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package compiler - -// func TestBuilder(t *testing.T) { -// cases := []struct { -// name string -// f func(*builder) -// wantHex string -// }{ -// { -// "single pushdata", -// func(b *builder) { -// b.addInt64(1) -// }, -// "51", -// }, -// { -// "pushdata and verify", -// func(b *builder) { -// b.addInt64(1) -// b.addOp(vm.OP_VERIFY) -// }, -// "51", -// }, -// { -// "pushdata, verify, second pushdata", -// func(b *builder) { -// b.addInt64(1) -// b.addOp(vm.OP_VERIFY) -// b.addInt64(2) -// }, -// "516952", -// }, -// } -// for _, c := range cases { -// t.Run(c.name, func(t *testing.T) { -// b := newBuilder() -// c.f(b) -// got, err := b.build() -// if err != nil { -// t.Fatal(err) -// } -// want, err := hex.DecodeString(c.wantHex) -// if err != nil { -// t.Fatal(err) -// } -// if !bytes.Equal(got, want) { -// t.Errorf("got %x, want %x", got, want) -// } -// }) -// } -// } diff --git a/exp/ivy/compiler/builtins.go b/exp/ivy/compiler/builtins.go deleted file mode 100644 index d0b70e3d..00000000 --- a/exp/ivy/compiler/builtins.go +++ /dev/null @@ -1,79 +0,0 @@ -package compiler - -type builtin struct { - name string - opcodes string - args []typeDesc - result typeDesc -} - -var builtins = []builtin{ - {"sha3", "SHA3", []typeDesc{nilType}, hashType}, - {"sha256", "SHA256", []typeDesc{nilType}, hashType}, - {"size", "SIZE SWAP DROP", []typeDesc{nilType}, intType}, - {"abs", "ABS", []typeDesc{intType}, intType}, - {"min", "MIN", []typeDesc{intType, intType}, intType}, - {"max", "MAX", []typeDesc{intType, intType}, intType}, - {"checkTxSig", "TXSIGHASH SWAP CHECKSIG", []typeDesc{pubkeyType, sigType}, boolType}, - {"concat", "CAT", []typeDesc{nilType, nilType}, strType}, - {"concatpush", "CATPUSHDATA", []typeDesc{nilType, nilType}, strType}, - {"before", "MAXTIME GREATERTHAN", []typeDesc{timeType}, boolType}, - {"after", "MINTIME LESSTHAN", []typeDesc{timeType}, boolType}, - {"checkTxMultiSig", "", []typeDesc{listType, listType}, boolType}, // WARNING WARNING WOOP WOOP special case -} - -type binaryOp struct { - op string - precedence int - opcodes string - - left, right, result typeDesc -} - -var binaryOps = []binaryOp{ - // disjunctions disallowed (for now?) - // {"||", 1, "BOOLOR", "Boolean", "Boolean", "Boolean"}, - - // and disallow this too - // {"&&", 2, "BOOLAND", "Boolean", "Boolean", "Boolean"}, - - {">", 3, "GREATERTHAN", "Integer", "Integer", "Boolean"}, - {"<", 3, "LESSTHAN", "Integer", "Integer", "Boolean"}, - {">=", 3, "GREATERTHANOREQUAL", "Integer", "Integer", "Boolean"}, - {"<=", 3, "LESSTHANOREQUAL", "Integer", "Integer", "Boolean"}, - - {"==", 3, "EQUAL", "", "", "Boolean"}, - {"!=", 3, "EQUAL NOT", "", "", "Boolean"}, - - {"^", 4, "XOR", "", "", ""}, - {"|", 4, "OR", "", "", ""}, - - {"+", 4, "ADD", "Integer", "Integer", "Integer"}, - {"-", 4, "SUB", "Integer", "Integer", "Integer"}, - - // {"&^", 5, "INVERT AND", "", "", ""}, - {"&", 5, "AND", "", "", ""}, - - {"<<", 5, "LSHIFT", "Integer", "Integer", "Integer"}, - {">>", 5, "RSHIFT", "Integer", "Integer", "Integer"}, - - {"%", 5, "MOD", "Integer", "Integer", "Integer"}, - {"*", 5, "MUL", "Integer", "Integer", "Integer"}, - {"/", 5, "DIV", "Integer", "Integer", "Integer"}, -} - -type unaryOp struct { - op string - opcodes string - - operand, result typeDesc -} - -var unaryOps = []unaryOp{ - {"-", "NEGATE", "Integer", "Integer"}, - - // not not allowed (for now?) - // {"!", "NOT", "Boolean", "Boolean"}, - - {"~", "INVERT", "", ""}, -} diff --git a/exp/ivy/compiler/checks.go b/exp/ivy/compiler/checks.go deleted file mode 100644 index d1a1c1ab..00000000 --- a/exp/ivy/compiler/checks.go +++ /dev/null @@ -1,211 +0,0 @@ -package compiler - -import "fmt" - -func checkRecursive(contract *Contract) bool { - for _, clause := range contract.Clauses { - for _, stmt := range clause.statements { - if l, ok := stmt.(*lockStatement); ok { - if c, ok := l.program.(*callExpr); ok { - if references(c.fn, contract.Name) { - return true - } - } - } - } - } - return false -} - -func prohibitSigParams(contract *Contract) error { - for _, p := range contract.Params { - if p.Type == sigType { - return fmt.Errorf("contract parameter \"%s\" has type Signature, but contract parameters cannot have type Signature", p.Name) - } - } - return nil -} - -func prohibitValueParams(contract *Contract) error { - for _, p := range contract.Params { - if p.Type == valueType { - return fmt.Errorf("Value-typed contract parameter \"%s\" must appear in a \"locks\" clause", p.Name) - } - } - for _, c := range contract.Clauses { - for _, p := range c.Params { - if p.Type == valueType { - return fmt.Errorf("Value-typed parameter \"%s\" of clause \"%s\" must appear in a \"requires\" clause", p.Name, c.Name) - } - } - } - return nil -} - -func requireAllParamsUsedInClauses(params []*Param, clauses []*Clause) error { - for _, p := range params { - used := false - for _, c := range clauses { - err := requireAllParamsUsedInClause([]*Param{p}, c) - if err == nil { - used = true - break - } - } - if !used { - return fmt.Errorf("parameter \"%s\" is unused", p.Name) - } - } - return nil -} - -func requireAllParamsUsedInClause(params []*Param, clause *Clause) error { - for _, p := range params { - used := false - for _, stmt := range clause.statements { - switch s := stmt.(type) { - case *verifyStatement: - used = references(s.expr, p.Name) - case *lockStatement: - used = references(s.locked, p.Name) || references(s.program, p.Name) - case *unlockStatement: - used = references(s.expr, p.Name) - } - if used { - break - } - } - if !used { - for _, r := range clause.Reqs { - if references(r.amountExpr, p.Name) || references(r.assetExpr, p.Name) { - used = true - break - } - } - } - if !used { - return fmt.Errorf("parameter \"%s\" is unused in clause \"%s\"", p.Name, clause.Name) - } - } - return nil -} - -func references(expr expression, name string) bool { - switch e := expr.(type) { - case *binaryExpr: - return references(e.left, name) || references(e.right, name) - case *unaryExpr: - return references(e.expr, name) - case *callExpr: - if references(e.fn, name) { - return true - } - for _, a := range e.args { - if references(a, name) { - return true - } - } - return false - case varRef: - return string(e) == name - case listExpr: - for _, elt := range []expression(e) { - if references(elt, name) { - return true - } - } - return false - } - return false -} - -func requireAllValuesDisposedOnce(contract *Contract, clause *Clause) error { - err := valueDisposedOnce(contract.Value, clause) - if err != nil { - return err - } - for _, req := range clause.Reqs { - err = valueDisposedOnce(req.Name, clause) - if err != nil { - return err - } - } - return nil -} - -func valueDisposedOnce(name string, clause *Clause) error { - var count int - for _, s := range clause.statements { - switch stmt := s.(type) { - case *unlockStatement: - if references(stmt.expr, name) { - count++ - } - case *lockStatement: - if references(stmt.locked, name) { - count++ - } - } - } - switch count { - case 0: - return fmt.Errorf("value \"%s\" not disposed in clause \"%s\"", name, clause.Name) - case 1: - return nil - default: - return fmt.Errorf("value \"%s\" disposed multiple times in clause \"%s\"", name, clause.Name) - } -} - -func referencedBuiltin(expr expression) *builtin { - if v, ok := expr.(varRef); ok { - for _, b := range builtins { - if string(v) == b.name { - return &b - } - } - } - return nil -} - -func assignIndexes(clause *Clause) { - var nextIndex int64 - for _, s := range clause.statements { - switch stmt := s.(type) { - case *lockStatement: - stmt.index = nextIndex - nextIndex++ - - case *unlockStatement: - nextIndex++ - } - } -} - -func typeCheckClause(contract *Contract, clause *Clause, env *environ) error { - for _, s := range clause.statements { - switch stmt := s.(type) { - case *verifyStatement: - if t := stmt.expr.typ(env); t != boolType { - return fmt.Errorf("expression in verify statement in clause \"%s\" has type \"%s\", must be Boolean", clause.Name, t) - } - - case *lockStatement: - if t := stmt.locked.typ(env); t != valueType { - return fmt.Errorf("expression in lock statement in clause \"%s\" has type \"%s\", must be Value", clause.Name, t) - } - if t := stmt.program.typ(env); t != progType { - return fmt.Errorf("program in lock statement in clause \"%s\" has type \"%s\", must be Program", clause.Name, t) - } - - case *unlockStatement: - if t := stmt.expr.typ(env); t != valueType { - return fmt.Errorf("expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Value", stmt.expr, clause.Name, t) - } - if stmt.expr.String() != contract.Value { - return fmt.Errorf("expression in unlock statement of clause \"%s\" must be the contract value", clause.Name) - } - } - } - return nil -} diff --git a/exp/ivy/compiler/checks_test.go b/exp/ivy/compiler/checks_test.go deleted file mode 100644 index 79e92827..00000000 --- a/exp/ivy/compiler/checks_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package compiler - -import "testing" - -func TestRequireAllParamsUsedInClauses(t *testing.T) { - clauses := []*Clause{ - &Clause{ - statements: []statement{ - &verifyStatement{expr: varRef("foo")}, - &verifyStatement{ - expr: &binaryExpr{ - left: varRef("foo"), - right: varRef("bar"), - }, - }, - &lockStatement{ - locked: varRef("baz"), - program: varRef("foo"), - }, - }, - }, - &Clause{ - statements: []statement{ - &verifyStatement{expr: varRef("foo")}, - &verifyStatement{ - expr: &binaryExpr{ - left: varRef("foo"), - right: varRef("plugh"), - }, - }, - &lockStatement{ - locked: varRef("xyzzy"), - program: varRef("foo"), - }, - }, - }, - } - - cases := []struct { - name string - params []string - want string - }{ - { - name: "contract param used in both clauses", - params: []string{"foo"}, - }, - { - name: "contract param used in one clause", - params: []string{"bar"}, - }, - { - name: "contract param used in no clauses", - params: []string{"y2"}, - want: "parameter \"y2\" is unused", - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - var params []*Param - for _, p := range c.params { - params = append(params, &Param{Name: p}) - } - err := requireAllParamsUsedInClauses(params, clauses) - if err == nil && c.want == "" { - return - } - if err == nil { - t.Errorf("got err==nil, want %s", c.want) - return - } - if err.Error() != c.want { - t.Errorf("got %s, want %s", err, c.want) - } - }) - } -} diff --git a/exp/ivy/compiler/cmd/ivyc/ivyc.go b/exp/ivy/compiler/cmd/ivyc/ivyc.go deleted file mode 100644 index 92b2e2aa..00000000 --- a/exp/ivy/compiler/cmd/ivyc/ivyc.go +++ /dev/null @@ -1,216 +0,0 @@ -package main - -import ( - "bytes" - "flag" - "fmt" - "log" - "os" - "strings" - - "github.com/bytom/exp/ivy/compiler" -) - -func main() { - packageName := flag.String("package", "main", "Go package name for generated file") - flag.Parse() - - contracts, err := compiler.Compile(os.Stdin) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("package %s\n\n", *packageName) - - imports := map[string]bool{ - "bytes": true, - "encoding/hex": true, - "fmt": true, - "chain/exp/ivy/compiler": true, - "chain/protocol/vm": true, - } - - buf := new(bytes.Buffer) - - if len(contracts) == 1 { - fmt.Fprintf(buf, "var %s_body_bytes []byte\n\n", contracts[0].Name) - } else { - fmt.Fprintf(buf, "var (\n") - for _, contract := range contracts { - fmt.Fprintf(buf, "\t%s_body_bytes []byte\n", contract.Name) - } - fmt.Fprintf(buf, ")\n\n") - } - - fmt.Fprintf(buf, "func init() {\n") - for _, contract := range contracts { - fmt.Fprintf(buf, "\t%s_body_bytes, _ = hex.DecodeString(\"%x\")\n", contract.Name, contract.Body) - } - fmt.Fprintf(buf, "}\n\n") - - for _, contract := range contracts { - fmt.Fprintf(buf, "// contract %s(%s) locks %s\n", contract.Name, paramsStr(contract.Params), contract.Value) - fmt.Fprintf(buf, "//\n") - maxWidth := 0 - for _, step := range contract.Steps { - if len(step.Opcodes) > maxWidth { - maxWidth = len(step.Opcodes) - } - } - format := fmt.Sprintf("// %%-%d.%ds %%s\n", maxWidth, maxWidth) - for _, step := range contract.Steps { - fmt.Fprintf(buf, format, step.Opcodes, step.Stack) - } - fmt.Fprintf(buf, "\n") - - fmt.Fprintf(buf, "// PayTo%s instantiates contract %s as a program with specific arguments.\n", contract.Name, contract.Name) - goParams, newImports := asGoParams(contract.Params) - for _, imp := range newImports { - imports[imp] = true - } - fmt.Fprintf(buf, "func PayTo%s(%s) ([]byte, error) {\n", contract.Name, goParams) - fmt.Fprintf(buf, "\t_contractParams := []compiler.Param{\n") - for _, param := range contract.Params { - fmt.Fprintf(buf, "\t\t{Name: \"%s\", Type: \"%s\"},\n", param.Name, param.Type) - } - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tvar _contractArgs []compiler.ContractArg\n") - for _, param := range contract.Params { - switch param.Type { - case "Amount": - fmt.Fprintf(buf, "\t_%s := int64(%s)\n", param.Name, param.Name) - fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{I: &_%s})\n", param.Name) - case "Asset": - fmt.Fprintf(buf, "\t_%s := %s[:]\n", param.Name, param.Name) - fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{S: &_%s})\n", param.Name) - case "Boolean", "Hash", "Program", "PublicKey", "Signature", "String": - fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{S: &%s})\n", param.Name) - case "Integer": - fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{I: &%s})\n", param.Name) - case "Time": - fmt.Fprintf(buf, "\t_%s := %s.UnixNano() / time.Millisecond\n", param.Name, param.Name) - fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{I: &_%s})\n", param.Name) - } - } - fmt.Fprintf(buf, "\treturn compiler.Instantiate(_contractParams, %s_body_bytes, %v, _contractArgs)\n", contract.Name, contract.Recursive) - fmt.Fprintf(buf, "}\n\n") - - fmt.Fprintf(buf, "// ParsePayTo%s parses the arguments out of an instantiation of contract %s.\n", contract.Name, contract.Name) - fmt.Fprintf(buf, "// If the input is not an instantiation of %s, returns an error.\n", contract.Name) - fmt.Fprintf(buf, "func ParsePayTo%s(prog []byte) ([][]byte, error) {\n", contract.Name) - fmt.Fprintf(buf, "\tvar result [][]byte\n") - fmt.Fprintf(buf, "\tinsts, err := vm.ParseProgram(prog)\n") - fmt.Fprintf(buf, "\tif err != nil {\n") - fmt.Fprintf(buf, "\t\treturn nil, err\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tfor i := 0; i < %d; i++ {\n", len(contract.Params)) - fmt.Fprintf(buf, "\t\tif len(insts) == 0 {\n") - fmt.Fprintf(buf, "\t\t\treturn nil, fmt.Errorf(\"program too short\")\n") - fmt.Fprintf(buf, "\t\t}\n") - fmt.Fprintf(buf, "\t\tif !insts[0].IsPushdata() {\n") - fmt.Fprintf(buf, "\t\t\treturn nil, fmt.Errorf(\"too few arguments\")\n") - fmt.Fprintf(buf, "\t\t}\n") - fmt.Fprintf(buf, "\t\tresult = append(result, insts[0].Data)\n") - fmt.Fprintf(buf, "\t\tinsts = insts[1:]\n") - fmt.Fprintf(buf, "\t}\n") - if contract.Recursive { - // args... body DEPTH OVER 0 CHECKPREDICATE - fmt.Fprintf(buf, "\tif len(insts) == 0 {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"program too short\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tif !insts[0].IsPushdata() {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"too few arguments\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tif !bytes.Equal(%s_body_bytes, insts[0].Data) {\n", contract.Name) - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"body bytes do not match %s\")\n", contract.Name) - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tinsts = insts[1:]\n") - } // else args ... DEPTH body 0 CHECKPREDICATE - fmt.Fprintf(buf, "\tif len(insts) != 4 {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"program too short\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tif insts[0].Op != vm.OP_DEPTH {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") - fmt.Fprintf(buf, "\t}\n") - if contract.Recursive { - fmt.Fprintf(buf, "\tif insts[1].Op != vm.OP_OVER {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") - fmt.Fprintf(buf, "\t}\n") - } else { - fmt.Fprintf(buf, "\tif !insts[1].IsPushdata() {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tif !bytes.Equal(%s_body_bytes, insts[1].Data) {\n", contract.Name) - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"body bytes do not match %s\")\n", contract.Name) - fmt.Fprintf(buf, "\t}\n") - } - fmt.Fprintf(buf, "\tif !insts[2].IsPushdata() {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tv, err := vm.AsInt64(insts[2].Data)\n") - fmt.Fprintf(buf, "\tif err != nil {\n") - fmt.Fprintf(buf, "\t\treturn nil, err\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tif v != 0 {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tif insts[3].Op != vm.OP_CHECKPREDICATE {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\treturn result, nil\n") - fmt.Fprintf(buf, "}\n\n") - - // TODO(bobg): RedeemFoo_Bar functions for marshaling the args to - // the Bar clause of contract Foo. - } - - fmt.Printf("import (\n") - for imp := range imports { - fmt.Printf("\t\"%s\"\n", imp) - } - fmt.Printf(")\n\n") - - os.Stdout.Write(buf.Bytes()) -} - -func paramsStr(params []*compiler.Param) string { - var strs []string - for _, p := range params { - strs = append(strs, fmt.Sprintf("%s: %s", p.Name, p.Type)) - } - return strings.Join(strs, ", ") -} - -func asGoParams(params []*compiler.Param) (goParams string, imports []string) { - var strs []string - for _, p := range params { - var typ string - switch p.Type { - case "Amount": - typ = "uint64" - case "Asset": - typ = "bc.AssetId" - imports = append(imports, "chain/protocol/bc") - case "Boolean": - typ = "bool" - case "Hash": - typ = "[]byte" - case "Integer": - typ = "int64" - case "Program": - typ = "[]byte" - case "PublicKey": - typ = "ed25519.PublicKey" - imports = append(imports, "chain/crypto/ed25519") - case "Signature": - typ = "[]byte" - case "String": - typ = "[]byte" - case "Time": - typ = "time.Time" - imports = append(imports, "time") - } - strs = append(strs, fmt.Sprintf("%s %s", p.Name, typ)) - } - return strings.Join(strs, ", "), imports -} diff --git a/exp/ivy/compiler/compile.go b/exp/ivy/compiler/compile.go deleted file mode 100644 index f55ff722..00000000 --- a/exp/ivy/compiler/compile.go +++ /dev/null @@ -1,756 +0,0 @@ -package compiler - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - - chainjson "github.com/bytom/encoding/json" - "github.com/bytom/errors" - "github.com/bytom/protocol/vm" - "github.com/bytom/protocol/vm/vmutil" -) - -// ValueInfo describes how a blockchain value is used in a contract -// clause. -type ValueInfo struct { - // Name is the clause's name for this value. - Name string `json:"name"` - - // Program is the program expression used to the lock the value, if - // the value is locked with "lock." If it's unlocked with "unlock" - // instead, this is empty. - Program string `json:"program,omitempty"` - - // Asset is the expression describing the asset type the value must - // have, as it appears in a clause's "requires" section. If this is - // the contract value instead, this is empty. - Asset string `json:"asset,omitempty"` - - // Amount is the expression describing the amount the value must - // have, as it appears in a clause's "requires" section. If this is - // the contract value instead, this is empty. - Amount string `json:"amount,omitempty"` -} - -// ContractArg is an argument with which to instantiate a contract as -// a program. Exactly one of B, I, and S should be supplied. -type ContractArg struct { - B *bool `json:"boolean,omitempty"` - I *int64 `json:"integer,omitempty"` - S *chainjson.HexBytes `json:"string,omitempty"` -} - -// Compile parses a sequence of Ivy contracts from the supplied reader -// and produces Contract objects containing the compiled bytecode and -// other analysis. If argMap is non-nil, it maps contract names to -// lists of arguments with which to instantiate them as programs, with -// the results placed in the contract's Program field. A contract -// named in argMap but not found in the input is silently ignored. -func Compile(r io.Reader) ([]*Contract, error) { - inp, err := ioutil.ReadAll(r) - if err != nil { - return nil, errors.Wrap(err, "reading input") - } - contracts, err := parse(inp) - if err != nil { - return nil, errors.Wrap(err, "parse error") - } - - globalEnv := newEnviron(nil) - for _, k := range keywords { - globalEnv.add(k, nilType, roleKeyword) - } - for _, b := range builtins { - globalEnv.add(b.name, nilType, roleBuiltin) - } - - // All contracts must be checked for recursiveness before any are - // compiled. - for _, contract := range contracts { - contract.Recursive = checkRecursive(contract) - } - - for _, contract := range contracts { - err = globalEnv.addContract(contract) - if err != nil { - return nil, err - } - } - - for _, contract := range contracts { - err = compileContract(contract, globalEnv) - if err != nil { - return nil, errors.Wrap(err, "compiling contract") - } - for _, clause := range contract.Clauses { - for _, stmt := range clause.statements { - switch s := stmt.(type) { - case *lockStatement: - valueInfo := ValueInfo{ - Name: s.locked.String(), - Program: s.program.String(), - } - if s.locked.String() != contract.Value { - for _, r := range clause.Reqs { - if s.locked.String() == r.Name { - valueInfo.Asset = r.assetExpr.String() - valueInfo.Amount = r.amountExpr.String() - break - } - } - } - clause.Values = append(clause.Values, valueInfo) - case *unlockStatement: - valueInfo := ValueInfo{Name: contract.Value} - clause.Values = append(clause.Values, valueInfo) - } - } - } - } - - return contracts, nil -} - -func Instantiate(body []byte, params []*Param, recursive bool, args []ContractArg) ([]byte, error) { - if len(args) != len(params) { - return nil, fmt.Errorf("got %d argument(s), want %d", len(args), len(params)) - } - - // typecheck args against param types - for i, param := range params { - arg := args[i] - switch param.Type { - case amountType, intType, timeType: - if arg.I == nil { - return nil, fmt.Errorf("type mismatch in arg %d (want integer)", i) - } - case assetType, hashType, progType, pubkeyType, sigType, strType: - if arg.S == nil { - return nil, fmt.Errorf("type mismatch in arg %d (want string)", i) - } - case boolType: - if arg.B == nil { - return nil, fmt.Errorf("type mismatch in arg %d (want boolean)", i) - } - } - } - - b := vmutil.NewBuilder() - - for i := len(args) - 1; i >= 0; i-- { - a := args[i] - switch { - case a.B != nil: - var n int64 - if *a.B { - n = 1 - } - b.AddInt64(n) - case a.I != nil: - b.AddInt64(*a.I) - case a.S != nil: - b.AddData(*a.S) - } - } - - if recursive { - // ... DEPTH OVER 0 CHECKPREDICATE - b.AddData(body) - b.AddOp(vm.OP_DEPTH).AddOp(vm.OP_OVER) - } else { - // ... DEPTH 0 CHECKPREDICATE - b.AddOp(vm.OP_DEPTH) - b.AddData(body) - } - b.AddInt64(0) - b.AddOp(vm.OP_CHECKPREDICATE) - return b.Build() -} - -func compileContract(contract *Contract, globalEnv *environ) error { - var err error - - if len(contract.Clauses) == 0 { - return fmt.Errorf("empty contract") - } - env := newEnviron(globalEnv) - for _, p := range contract.Params { - err = env.add(p.Name, p.Type, roleContractParam) - if err != nil { - return err - } - } - err = env.add(contract.Value, valueType, roleContractValue) - if err != nil { - return err - } - for _, c := range contract.Clauses { - err = env.add(c.Name, nilType, roleClause) - if err != nil { - return err - } - } - - err = prohibitValueParams(contract) - if err != nil { - return err - } - err = prohibitSigParams(contract) - if err != nil { - return err - } - err = requireAllParamsUsedInClauses(contract.Params, contract.Clauses) - if err != nil { - return err - } - - var stk stack - - if len(contract.Clauses) > 1 { - stk = stk.add("") - } - - for i := len(contract.Params) - 1; i >= 0; i-- { - p := contract.Params[i] - stk = stk.add(p.Name) - } - - if contract.Recursive { - stk = stk.add(contract.Name) - } - - b := &builder{} - - if len(contract.Clauses) == 1 { - err = compileClause(b, stk, contract, env, contract.Clauses[0]) - if err != nil { - return err - } - } else { - if len(contract.Params) > 0 { - // A clause selector is at the bottom of the stack. Roll it to the - // top. - n := len(contract.Params) - if contract.Recursive { - n++ - } - stk = b.addRoll(stk, n) // stack: [ [] ] - } - - var stk2 stack - - // clauses 2..N-1 - for i := len(contract.Clauses) - 1; i >= 2; i-- { - stk = b.addDup(stk) // stack: [... ] - stk = b.addInt64(stk, int64(i)) // stack: [... ] - stk = b.addNumEqual(stk, fmt.Sprintf("( == %d)", i)) // stack: [... ] - stk = b.addJumpIf(stk, contract.Clauses[i].Name) // stack: [... ] - stk2 = stk // stack starts here for clauses 2 through N-1 - } - - // clause 1 - stk = b.addJumpIf(stk, contract.Clauses[1].Name) // consumes the clause selector - - // no jump needed for clause 0 - - for i, clause := range contract.Clauses { - if i > 1 { - // Clauses 0 and 1 have no clause selector on top of the - // stack. Clauses 2 and later do. - stk = stk2 - } - - b.addJumpTarget(stk, clause.Name) - - if i > 1 { - stk = b.addDrop(stk) - } - - err = compileClause(b, stk, contract, env, clause) - if err != nil { - return errors.Wrapf(err, "compiling clause \"%s\"", clause.Name) - } - b.forgetPendingVerify() - if i < len(contract.Clauses)-1 { - b.addJump(stk, "_end") - } - } - b.addJumpTarget(stk, "_end") - } - - opcodes := optimize(b.opcodes()) - prog, err := vm.Assemble(opcodes) - if err != nil { - return err - } - - contract.Body = prog - contract.Opcodes = opcodes - - contract.Steps = b.steps() - - return nil -} - -func compileClause(b *builder, contractStk stack, contract *Contract, env *environ, clause *Clause) error { - var err error - - // copy env to leave outerEnv unchanged - env = newEnviron(env) - for _, p := range clause.Params { - err = env.add(p.Name, p.Type, roleClauseParam) - if err != nil { - return err - } - } - for _, req := range clause.Reqs { - err = env.add(req.Name, valueType, roleClauseValue) - if err != nil { - return err - } - req.Asset = req.assetExpr.String() - req.Amount = req.amountExpr.String() - } - - assignIndexes(clause) - - var stk stack - for _, p := range clause.Params { - // NOTE: the order of clause params is not reversed, unlike - // contract params (and also unlike the arguments to Ivy - // function-calls). - stk = stk.add(p.Name) - } - stk = stk.addFromStack(contractStk) - - // a count of the number of times each variable is referenced - counts := make(map[string]int) - for _, req := range clause.Reqs { - req.assetExpr.countVarRefs(counts) - req.amountExpr.countVarRefs(counts) - } - for _, s := range clause.statements { - s.countVarRefs(counts) - } - - for _, s := range clause.statements { - switch stmt := s.(type) { - case *verifyStatement: - stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr) - if err != nil { - return errors.Wrapf(err, "in verify statement in clause \"%s\"", clause.Name) - } - stk = b.addVerify(stk) - - // special-case reporting of certain function calls - if c, ok := stmt.expr.(*callExpr); ok && len(c.args) == 1 { - if b := referencedBuiltin(c.fn); b != nil { - switch b.name { - case "before": - clause.MaxTimes = append(clause.MaxTimes, c.args[0].String()) - case "after": - clause.MinTimes = append(clause.MinTimes, c.args[0].String()) - } - } - } - - case *lockStatement: - // index - stk = b.addInt64(stk, stmt.index) - - // refdatahash - stk = b.addData(stk, nil) - - // TODO: permit more complex expressions for locked, - // like "lock x+y with foo" (?) - - if stmt.locked.String() == contract.Value { - stk = b.addAmount(stk) - stk = b.addAsset(stk) - } else { - var req *ClauseReq - for _, r := range clause.Reqs { - if stmt.locked.String() == r.Name { - req = r - break - } - } - if req == nil { - return fmt.Errorf("unknown value \"%s\" in lock statement in clause \"%s\"", stmt.locked, clause.Name) - } - - // amount - stk, err = compileExpr(b, stk, contract, clause, env, counts, req.amountExpr) - if err != nil { - return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name) - } - - // asset - stk, err = compileExpr(b, stk, contract, clause, env, counts, req.assetExpr) - if err != nil { - return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name) - } - } - - // version - stk = b.addInt64(stk, 1) - - // prog - stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.program) - if err != nil { - return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name) - } - - stk = b.addCheckOutput(stk, fmt.Sprintf("checkOutput(%s, %s)", stmt.locked, stmt.program)) - stk = b.addVerify(stk) - - case *unlockStatement: - if len(clause.statements) == 1 { - // This is the only statement in the clause, make sure TRUE is - // on the stack. - stk = b.addBoolean(stk, true) - } - } - } - - err = requireAllValuesDisposedOnce(contract, clause) - if err != nil { - return err - } - err = typeCheckClause(contract, clause, env) - if err != nil { - return err - } - err = requireAllParamsUsedInClause(clause.Params, clause) - if err != nil { - return err - } - - return nil -} - -func compileExpr(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, error) { - var err error - - switch e := expr.(type) { - case *binaryExpr: - // Do typechecking after compiling subexpressions (because other - // compilation errors are more interesting than type mismatch - // errors). - - stk, err = compileExpr(b, stk, contract, clause, env, counts, e.left) - if err != nil { - return stk, errors.Wrapf(err, "in left operand of \"%s\" expression", e.op.op) - } - stk, err = compileExpr(b, stk, contract, clause, env, counts, e.right) - if err != nil { - return stk, errors.Wrapf(err, "in right operand of \"%s\" expression", e.op.op) - } - - lType := e.left.typ(env) - if e.op.left != "" && lType != e.op.left { - return stk, fmt.Errorf("in \"%s\", left operand has type \"%s\", must be \"%s\"", e, lType, e.op.left) - } - - rType := e.right.typ(env) - if e.op.right != "" && rType != e.op.right { - return stk, fmt.Errorf("in \"%s\", right operand has type \"%s\", must be \"%s\"", e, rType, e.op.right) - } - - switch e.op.op { - case "==", "!=": - if lType != rType { - // Maybe one is Hash and the other is (more-specific-Hash subtype). - // TODO(bobg): generalize this mechanism - if lType == hashType && isHashSubtype(rType) { - propagateType(contract, clause, env, rType, e.left) - } else if rType == hashType && isHashSubtype(lType) { - propagateType(contract, clause, env, lType, e.right) - } else { - return stk, fmt.Errorf("type mismatch in \"%s\": left operand has type \"%s\", right operand has type \"%s\"", e, lType, rType) - } - } - if lType == "Boolean" { - return stk, fmt.Errorf("in \"%s\": using \"%s\" on Boolean values not allowed", e, e.op.op) - } - } - - stk = b.addOps(stk.dropN(2), e.op.opcodes, e.String()) - - case *unaryExpr: - // Do typechecking after compiling subexpression (because other - // compilation errors are more interesting than type mismatch - // errors). - - var err error - stk, err = compileExpr(b, stk, contract, clause, env, counts, e.expr) - if err != nil { - return stk, errors.Wrapf(err, "in \"%s\" expression", e.op.op) - } - - if e.op.operand != "" && e.expr.typ(env) != e.op.operand { - return stk, fmt.Errorf("in \"%s\", operand has type \"%s\", must be \"%s\"", e, e.expr.typ(env), e.op.operand) - } - b.addOps(stk.drop(), e.op.opcodes, e.String()) - - case *callExpr: - bi := referencedBuiltin(e.fn) - if bi == nil { - if v, ok := e.fn.(varRef); ok { - if entry := env.lookup(string(v)); entry != nil && entry.t == contractType { - clause.Contracts = append(clause.Contracts, entry.c.Name) - - partialName := fmt.Sprintf("%s(...)", v) - stk = b.addData(stk, nil) - - if len(e.args) != len(entry.c.Params) { - return stk, fmt.Errorf("contract \"%s\" expects %d argument(s), got %d", entry.c.Name, len(entry.c.Params), len(e.args)) - } - - for i := len(e.args) - 1; i >= 0; i-- { - arg := e.args[i] - if entry.c.Params[i].Type != "" && arg.typ(env) != entry.c.Params[i].Type { - return stk, fmt.Errorf("argument %d to contract \"%s\" has type \"%s\", must be \"%s\"", i, entry.c.Name, arg.typ(env), entry.c.Params[i].Type) - } - stk, err = compileExpr(b, stk, contract, clause, env, counts, arg) - if err != nil { - return stk, err - } - stk = b.addCatPushdata(stk, partialName) - } - - switch { - case entry.c == contract: - // Recursive call - cannot use entry.c.Body - // ... DEPTH OVER 0 CHECKPREDICATE - stk, err = compileRef(b, stk, counts, varRef(contract.Name)) - if err != nil { - return stk, errors.Wrap(err, "compiling contract call") - } - stk = b.addCatPushdata(stk, partialName) - stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)}) - stk = b.addCat(stk, partialName) - - case entry.c.Recursive: - // Non-recursive call to a (different) recursive contract - // ... DEPTH OVER 0 CHECKPREDICATE - if len(entry.c.Body) == 0 { - // TODO(bobg): sort input contracts topologically to permit forward calling - return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name) - } - stk = b.addData(stk, entry.c.Body) - stk = b.addCatPushdata(stk, partialName) - stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)}) - stk = b.addCat(stk, partialName) - - default: - // Non-recursive call to non-recursive contract - // ... DEPTH 0 CHECKPREDICATE - stk = b.addData(stk, []byte{byte(vm.OP_DEPTH)}) - stk = b.addCat(stk, partialName) - if len(entry.c.Body) == 0 { - // TODO(bobg): sort input contracts topologically to permit forward calling - return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name) - } - stk = b.addData(stk, entry.c.Body) - stk = b.addCatPushdata(stk, partialName) - } - stk = b.addData(stk, vm.Int64Bytes(0)) - stk = b.addCatPushdata(stk, partialName) - stk = b.addData(stk, []byte{byte(vm.OP_CHECKPREDICATE)}) - stk = b.addCat(stk, e.String()) - - return stk, nil - } - } - return stk, fmt.Errorf("unknown function \"%s\"", e.fn) - } - - if len(e.args) != len(bi.args) { - return stk, fmt.Errorf("wrong number of args for \"%s\": have %d, want %d", bi.name, len(e.args), len(bi.args)) - } - - // WARNING WARNING WOOP WOOP - // special-case hack - // WARNING WARNING WOOP WOOP - if bi.name == "checkTxMultiSig" { - if _, ok := e.args[0].(listExpr); !ok { - return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 0", e.args[0]) - } - if _, ok := e.args[1].(listExpr); !ok { - return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 1", e.args[1]) - } - - var k1, k2 int - - stk, k1, err = compileArg(b, stk, contract, clause, env, counts, e.args[1]) - if err != nil { - return stk, err - } - - // stack: [... sigM ... sig1 M] - - var altEntry string - stk, altEntry = b.addToAltStack(stk) // stack: [... sigM ... sig1] - stk = b.addTxSigHash(stk) // stack: [... sigM ... sig1 txsighash] - - stk, k2, err = compileArg(b, stk, contract, clause, env, counts, e.args[0]) - if err != nil { - return stk, err - } - - // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N] - - stk = b.addFromAltStack(stk, altEntry) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N M] - stk = b.addSwap(stk) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 M N] - stk = b.addCheckMultisig(stk, k1+k2, e.String()) - - return stk, nil - } - - var k int - - for i := len(e.args) - 1; i >= 0; i-- { - a := e.args[i] - var k2 int - var err error - stk, k2, err = compileArg(b, stk, contract, clause, env, counts, a) - if err != nil { - return stk, errors.Wrapf(err, "compiling argument %d in call expression", i) - } - k += k2 - } - - // Do typechecking after compiling subexpressions (because other - // compilation errors are more interesting than type mismatch - // errors). - for i, actual := range e.args { - if bi.args[i] != "" && actual.typ(env) != bi.args[i] { - return stk, fmt.Errorf("argument %d to \"%s\" has type \"%s\", must be \"%s\"", i, bi.name, actual.typ(env), bi.args[i]) - } - } - - stk = b.addOps(stk.dropN(k), bi.opcodes, e.String()) - - // special-case reporting - switch bi.name { - case "sha3", "sha256": - clause.HashCalls = append(clause.HashCalls, HashCall{bi.name, e.args[0].String(), string(e.args[0].typ(env))}) - } - - case varRef: - return compileRef(b, stk, counts, e) - - case integerLiteral: - stk = b.addInt64(stk, int64(e)) - - case bytesLiteral: - stk = b.addData(stk, []byte(e)) - - case booleanLiteral: - stk = b.addBoolean(stk, bool(e)) - - case listExpr: - // Lists are excluded here because they disobey the invariant of - // this function: namely, that it increases the stack size by - // exactly one. (A list pushes its items and its length on the - // stack.) But they're OK as function-call arguments because the - // function (presumably) consumes all the stack items added. - return stk, fmt.Errorf("encountered list outside of function-call context") - } - return stk, nil -} - -func compileArg(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, int, error) { - var n int - if list, ok := expr.(listExpr); ok { - for i := 0; i < len(list); i++ { - elt := list[len(list)-i-1] - var err error - stk, err = compileExpr(b, stk, contract, clause, env, counts, elt) - if err != nil { - return stk, 0, err - } - n++ - } - stk = b.addInt64(stk, int64(len(list))) - n++ - return stk, n, nil - } - var err error - stk, err = compileExpr(b, stk, contract, clause, env, counts, expr) - return stk, 1, err -} - -func compileRef(b *builder, stk stack, counts map[string]int, ref varRef) (stack, error) { - depth := stk.find(string(ref)) - if depth < 0 { - return stk, fmt.Errorf("undefined reference: \"%s\"", ref) - } - - var isFinal bool - if count, ok := counts[string(ref)]; ok && count > 0 { - count-- - counts[string(ref)] = count - isFinal = count == 0 - } - - switch depth { - case 0: - if !isFinal { - stk = b.addDup(stk) - } - case 1: - if isFinal { - stk = b.addSwap(stk) - } else { - stk = b.addOver(stk) - } - default: - if isFinal { - stk = b.addRoll(stk, depth) - } else { - stk = b.addPick(stk, depth) - } - } - return stk, nil -} - -func (a *ContractArg) UnmarshalJSON(b []byte) error { - var m map[string]json.RawMessage - err := json.Unmarshal(b, &m) - if err != nil { - return err - } - if r, ok := m["boolean"]; ok { - var bval bool - err = json.Unmarshal(r, &bval) - if err != nil { - return err - } - a.B = &bval - return nil - } - if r, ok := m["integer"]; ok { - var ival int64 - err = json.Unmarshal(r, &ival) - if err != nil { - return err - } - a.I = &ival - return nil - } - r, ok := m["string"] - if !ok { - return fmt.Errorf("contract arg must define one of boolean, integer, string") - } - var sval chainjson.HexBytes - err = json.Unmarshal(r, &sval) - if err != nil { - return err - } - a.S = &sval - return nil -} diff --git a/exp/ivy/compiler/compile_test.go b/exp/ivy/compiler/compile_test.go deleted file mode 100644 index 745c431f..00000000 --- a/exp/ivy/compiler/compile_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package compiler - -import ( - "encoding/hex" - "encoding/json" - "strings" - "testing" - - "github.com/bytom/exp/ivy/compiler/ivytest" -) - -func TestCompile(t *testing.T) { - cases := []struct { - name string - contract string - wantJSON string - }{ - { - "TrivialLock", - ivytest.TrivialLock, - `[{"name":"TrivialLock","clauses":[{"name":"trivialUnlock","values":[{"name":"locked"}]}],"value":"locked","body_bytecode":"51","body_opcodes":"TRUE","recursive":false}]`, - }, - { - "LockWithPublicKey", - ivytest.LockWithPublicKey, - `[{"name":"LockWithPublicKey","params":[{"name":"publicKey","declared_type":"PublicKey"}],"clauses":[{"name":"unlockWithSig","params":[{"name":"sig","declared_type":"Signature"}],"values":[{"name":"locked"}]}],"value":"locked","body_bytecode":"ae7cac","body_opcodes":"TXSIGHASH SWAP CHECKSIG","recursive":false}]`, - }, - { - "LockWithPublicKeyHash", - ivytest.LockWithPKHash, - `[{"name":"LockWithPublicKeyHash","params":[{"name":"pubKeyHash","declared_type":"Hash","inferred_type":"Sha3(PublicKey)"}],"clauses":[{"name":"spend","params":[{"name":"pubKey","declared_type":"PublicKey"},{"name":"sig","declared_type":"Signature"}],"hash_calls":[{"hash_type":"sha3","arg":"pubKey","arg_type":"PublicKey"}],"values":[{"name":"value"}]}],"value":"value","body_bytecode":"5279aa887cae7cac","body_opcodes":"2 PICK SHA3 EQUALVERIFY SWAP TXSIGHASH SWAP CHECKSIG","recursive":false}]`, - }, - { - "LockWith2of3Keys", - ivytest.LockWith2of3Keys, - `[{"name":"LockWith3Keys","params":[{"name":"pubkey1","declared_type":"PublicKey"},{"name":"pubkey2","declared_type":"PublicKey"},{"name":"pubkey3","declared_type":"PublicKey"}],"clauses":[{"name":"unlockWith2Sigs","params":[{"name":"sig1","declared_type":"Signature"},{"name":"sig2","declared_type":"Signature"}],"values":[{"name":"locked"}]}],"value":"locked","body_bytecode":"537a547a526bae71557a536c7cad","body_opcodes":"3 ROLL 4 ROLL 2 TOALTSTACK TXSIGHASH 2ROT 5 ROLL 3 FROMALTSTACK SWAP CHECKMULTISIG","recursive":false}]`, - }, - { - "LockToOutput", - ivytest.LockToOutput, - `[{"name":"LockToOutput","params":[{"name":"address","declared_type":"Program"}],"clauses":[{"name":"relock","values":[{"name":"locked","program":"address"}]}],"value":"locked","body_bytecode":"0000c3c251557ac1","body_opcodes":"0 0 AMOUNT ASSET 1 5 ROLL CHECKOUTPUT","recursive":false}]`, - }, - { - "TradeOffer", - ivytest.TradeOffer, - `[{"name":"TradeOffer","params":[{"name":"requestedAsset","declared_type":"Asset"},{"name":"requestedAmount","declared_type":"Amount"},{"name":"sellerProgram","declared_type":"Program"},{"name":"sellerKey","declared_type":"PublicKey"}],"clauses":[{"name":"trade","reqs":[{"name":"payment","asset":"requestedAsset","amount":"requestedAmount"}],"values":[{"name":"payment","program":"sellerProgram","asset":"requestedAsset","amount":"requestedAmount"},{"name":"offered"}]},{"name":"cancel","params":[{"name":"sellerSig","declared_type":"Signature"}],"values":[{"name":"offered","program":"sellerProgram"}]}],"value":"offered","body_bytecode":"547a641300000000007251557ac16323000000547a547aae7cac690000c3c251577ac1","body_opcodes":"4 ROLL JUMPIF:$cancel $trade 0 0 2SWAP 1 5 ROLL CHECKOUTPUT JUMP:$_end $cancel 4 ROLL 4 ROLL TXSIGHASH SWAP CHECKSIG VERIFY 0 0 AMOUNT ASSET 1 7 ROLL CHECKOUTPUT $_end","recursive":false}]`, - }, - { - "EscrowedTransfer", - ivytest.EscrowedTransfer, - `[{"name":"EscrowedTransfer","params":[{"name":"agent","declared_type":"PublicKey"},{"name":"sender","declared_type":"Program"},{"name":"recipient","declared_type":"Program"}],"clauses":[{"name":"approve","params":[{"name":"sig","declared_type":"Signature"}],"values":[{"name":"value","program":"recipient"}]},{"name":"reject","params":[{"name":"sig","declared_type":"Signature"}],"values":[{"name":"value","program":"sender"}]}],"value":"value","body_bytecode":"537a641b000000537a7cae7cac690000c3c251567ac1632a000000537a7cae7cac690000c3c251557ac1","body_opcodes":"3 ROLL JUMPIF:$reject $approve 3 ROLL SWAP TXSIGHASH SWAP CHECKSIG VERIFY 0 0 AMOUNT ASSET 1 6 ROLL CHECKOUTPUT JUMP:$_end $reject 3 ROLL SWAP TXSIGHASH SWAP CHECKSIG VERIFY 0 0 AMOUNT ASSET 1 5 ROLL CHECKOUTPUT $_end","recursive":false}]`, - }, - { - "CollateralizedLoan", - ivytest.CollateralizedLoan, - `[{"name":"CollateralizedLoan","params":[{"name":"balanceAsset","declared_type":"Asset"},{"name":"balanceAmount","declared_type":"Amount"},{"name":"deadline","declared_type":"Time"},{"name":"lender","declared_type":"Program"},{"name":"borrower","declared_type":"Program"}],"clauses":[{"name":"repay","reqs":[{"name":"payment","asset":"balanceAsset","amount":"balanceAmount"}],"values":[{"name":"payment","program":"lender","asset":"balanceAsset","amount":"balanceAmount"},{"name":"collateral","program":"borrower"}]},{"name":"default","mintimes":["deadline"],"values":[{"name":"collateral","program":"lender"}]}],"value":"collateral","body_bytecode":"557a641c00000000007251567ac1695100c3c251567ac163280000007bc59f690000c3c251577ac1","body_opcodes":"5 ROLL JUMPIF:$default $repay 0 0 2SWAP 1 6 ROLL CHECKOUTPUT VERIFY 1 0 AMOUNT ASSET 1 6 ROLL CHECKOUTPUT JUMP:$_end $default ROT MINTIME LESSTHAN VERIFY 0 0 AMOUNT ASSET 1 7 ROLL CHECKOUTPUT $_end","recursive":false}]`, - }, - { - "RevealPreimage", - ivytest.RevealPreimage, - `[{"name":"RevealPreimage","params":[{"name":"hash","declared_type":"Hash","inferred_type":"Sha3(String)"}],"clauses":[{"name":"reveal","params":[{"name":"string","declared_type":"String"}],"hash_calls":[{"hash_type":"sha3","arg":"string","arg_type":"String"}],"values":[{"name":"value"}]}],"value":"value","body_bytecode":"7caa87","body_opcodes":"SWAP SHA3 EQUAL","recursive":false}]`, - }, - { - "CallOptionWithSettlement", - ivytest.CallOptionWithSettlement, - `[{"name":"CallOptionWithSettlement","params":[{"name":"strikePrice","declared_type":"Amount"},{"name":"strikeCurrency","declared_type":"Asset"},{"name":"sellerProgram","declared_type":"Program"},{"name":"sellerKey","declared_type":"PublicKey"},{"name":"buyerKey","declared_type":"PublicKey"},{"name":"deadline","declared_type":"Time"}],"clauses":[{"name":"exercise","params":[{"name":"buyerSig","declared_type":"Signature"}],"reqs":[{"name":"payment","asset":"strikeCurrency","amount":"strikePrice"}],"maxtimes":["deadline"],"values":[{"name":"payment","program":"sellerProgram","asset":"strikeCurrency","amount":"strikePrice"},{"name":"underlying"}]},{"name":"expire","mintimes":["deadline"],"values":[{"name":"underlying","program":"sellerProgram"}]},{"name":"settle","params":[{"name":"sellerSig","declared_type":"Signature"},{"name":"buyerSig","declared_type":"Signature"}],"values":[{"name":"underlying"}]}],"value":"underlying","body_bytecode":"567a76529c64390000006427000000557ac6a06971ae7cac6900007b537a51557ac16349000000557ac59f690000c3c251577ac1634900000075577a547aae7cac69557a547aae7cac","body_opcodes":"6 ROLL DUP 2 NUMEQUAL JUMPIF:$settle JUMPIF:$expire $exercise 5 ROLL MAXTIME GREATERTHAN VERIFY 2ROT TXSIGHASH SWAP CHECKSIG VERIFY 0 0 ROT 3 ROLL 1 5 ROLL CHECKOUTPUT JUMP:$_end $expire 5 ROLL MINTIME LESSTHAN VERIFY 0 0 AMOUNT ASSET 1 7 ROLL CHECKOUTPUT JUMP:$_end $settle DROP 7 ROLL 4 ROLL TXSIGHASH SWAP CHECKSIG VERIFY 5 ROLL 4 ROLL TXSIGHASH SWAP CHECKSIG $_end","recursive":false}]`, - }, - { - "PriceChanger", - ivytest.PriceChanger, - `[{"name":"PriceChanger","params":[{"name":"askAmount","declared_type":"Amount"},{"name":"askAsset","declared_type":"Asset"},{"name":"sellerKey","declared_type":"PublicKey"},{"name":"sellerProg","declared_type":"Program"}],"clauses":[{"name":"changePrice","params":[{"name":"newAmount","declared_type":"Amount"},{"name":"newAsset","declared_type":"Asset"},{"name":"sig","declared_type":"Signature"}],"values":[{"name":"offered","program":"PriceChanger(newAmount, newAsset, sellerKey, sellerProg)"}],"contracts":["PriceChanger"]},{"name":"redeem","reqs":[{"name":"payment","asset":"askAsset","amount":"askAmount"}],"values":[{"name":"payment","program":"sellerProg","asset":"askAsset","amount":"askAmount"},{"name":"offered"}]}],"value":"offered","body_bytecode":"557a6433000000557a5479ae7cac690000c3c251005a7a89597a89597a89597a89567a890274787e008901c07ec1633d0000000000537a547a51577ac1","body_opcodes":"5 ROLL JUMPIF:$redeem $changePrice 5 ROLL 4 PICK TXSIGHASH SWAP CHECKSIG VERIFY 0 0 AMOUNT ASSET 1 0 10 ROLL CATPUSHDATA 9 ROLL CATPUSHDATA 9 ROLL CATPUSHDATA 9 ROLL CATPUSHDATA 6 ROLL CATPUSHDATA 0x7478 CAT 0 CATPUSHDATA 192 CAT CHECKOUTPUT JUMP:$_end $redeem 0 0 3 ROLL 4 ROLL 1 7 ROLL CHECKOUTPUT $_end","recursive":true}]`, - }, - { - "OneTwo", - ivytest.OneTwo, - `[{"name":"Two","params":[{"name":"b","declared_type":"Program"},{"name":"c","declared_type":"Program"},{"name":"expirationTime","declared_type":"Time"}],"clauses":[{"name":"redeem","maxtimes":["expirationTime"],"values":[{"name":"value","program":"b"}]},{"name":"default","mintimes":["expirationTime"],"values":[{"name":"value","program":"c"}]}],"value":"value","body_bytecode":"537a64180000007bc6a0690000c3c251557ac163240000007bc59f690000c3c251567ac1","body_opcodes":"3 ROLL JUMPIF:$default $redeem ROT MAXTIME GREATERTHAN VERIFY 0 0 AMOUNT ASSET 1 5 ROLL CHECKOUTPUT JUMP:$_end $default ROT MINTIME LESSTHAN VERIFY 0 0 AMOUNT ASSET 1 6 ROLL CHECKOUTPUT $_end","recursive":false},{"name":"One","params":[{"name":"a","declared_type":"Program"},{"name":"b","declared_type":"Program"},{"name":"c","declared_type":"Program"},{"name":"switchTime","declared_type":"Time"},{"name":"expirationTime","declared_type":"Time"}],"clauses":[{"name":"redeem","maxtimes":["switchTime"],"values":[{"name":"value","program":"a"}]},{"name":"switch","mintimes":["switchTime"],"values":[{"name":"value","program":"Two(b, c, expirationTime)"}],"contracts":["Two"]}],"value":"value","body_bytecode":"557a6419000000537ac6a0690000c3c251557ac1635c000000537ac59f690000c3c25100597a89587a89577a8901747e24537a64180000007bc6a0690000c3c251557ac163240000007bc59f690000c3c251567ac189008901c07ec1","body_opcodes":"5 ROLL JUMPIF:$switch $redeem 3 ROLL MAXTIME GREATERTHAN VERIFY 0 0 AMOUNT ASSET 1 5 ROLL CHECKOUTPUT JUMP:$_end $switch 3 ROLL MINTIME LESSTHAN VERIFY 0 0 AMOUNT ASSET 1 0 9 ROLL CATPUSHDATA 8 ROLL CATPUSHDATA 7 ROLL CATPUSHDATA 116 CAT 0x537a64180000007bc6a0690000c3c251557ac163240000007bc59f690000c3c251567ac1 CATPUSHDATA 0 CATPUSHDATA 192 CAT CHECKOUTPUT $_end","recursive":false}]`, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - r := strings.NewReader(c.contract) - got, err := Compile(r) - if err != nil { - t.Fatal(err) - } - gotJSON, _ := json.Marshal(got) - if string(gotJSON) != c.wantJSON { - t.Errorf("\ngot %s\nwant %s", string(gotJSON), c.wantJSON) - } else { - for _, contract := range got { - t.Log(contract.Opcodes) - } - } - }) - } -} - -func mustDecodeHex(h string) []byte { - bits, err := hex.DecodeString(h) - if err != nil { - panic(err) - } - return bits -} diff --git a/exp/ivy/compiler/doc.go b/exp/ivy/compiler/doc.go deleted file mode 100644 index 5a1736a6..00000000 --- a/exp/ivy/compiler/doc.go +++ /dev/null @@ -1,126 +0,0 @@ -/* -Package ivy provides a compiler for Chain's Ivy contract language. - -A contract is a means to lock some payment in the output of a -transaction. It contains a number of clauses, each describing a way to -unlock, or redeem, the payment in a subsequent transaction. By -executing the statements in a clause, using contract arguments -supplied by the payer and clause arguments supplied by the redeemer, -nodes in a Chain network can determine whether a proposed spend is -valid. - -The language definition is in flux, but here's what's implemented as -of late May 2017. - - program = contract* - - contract = "contract" identifier "(" [params] ")" "locks" identifier "{" clause+ "}" - - The identifier after "locks" is a name for the value locked by - the contract. It must be unlocked or re-locked (with "unlock" - or "lock") in every clause. - - clause = "clause" identifier "(" [params] ")" ["requires" requirements] "{" statement+ "}" - - The requirements are blockchain values that must be present in - the spending transaction in order to spend the value locked by - the earlier transaction. Each such value must be re-locked - (with "lock") in its clause. - - statement = verify | unlock | lock - - verify = "verify" expr - - Verifies that boolean expression expr produces a true result. - - unlock = "unlock" expr - - Expr must evaluate to the contract value. This unlocks that - value for any use. - - lock = "lock" expr "with" expr - - The first expr must be a blockchain value (i.e., one named - with "locks" or "requires"). The second expr must be a - program. This unlocks expr and re-locks it with the new - program. - - requirements = requirement | requirements "," requirement - - requirement = identifier ":" expr "of" expr - - The first expr must be an amount, the second must be an - asset. This denotes that the named value must have the given - quantity and asset type. - - params = param | params "," param - - param = idlist ":" identifier - - The identifiers in idlist are individual parameter names. The - identifier after the colon is their type. Available types are: - - Amount; Asset; Boolean; Hash; Integer; Program; PublicKey; - Signature; String; Time - - idlist = identifier | idlist "," identifier - - expr = unary_expr | binary_expr | call_expr | identifier | "(" expr ")" | literal - - unary_expr = unary_op expr - - binary_expr = expr binary_op expr - - call_expr = expr "(" [args] ")" - - If expr is the name of an Ivy contract, then calling it (with - the appropriate arguments) produces a program suitable for use - in "lock" statements. - - Otherwise, expr should be one of these builtin functions: - - sha3(x) - SHA3-256 hash of x. - sha256(x) - SHA-256 hash of x. - size(x) - Size in bytes of x. - abs(x) - Absolute value of x. - min(x, y) - The lesser of x and y. - max(x, y) - The greater of x and y. - checkTxSig(pubkey, signature) - Whether signature matches both the spending - transaction and pubkey. - concat(x, y) - The concatenation of x and y. - concatpush(x, y) - The concatenation of x with the bytecode sequence - needed to push y on the ChainVM stack. - before(x) - Whether the spending transaction is happening before - time x. - after(x) - Whether the spending transaction is happening after - time x. - checkTxMultiSig([pubkey1, pubkey2, ...], [sig1, sig2, ...]) - Like checkTxSig, but for M-of-N signature checks. - Every sig must match both the spending transaction and - one of the pubkeys. There may be more pubkeys than - sigs, but they are only checked left-to-right so must - be supplied in the same order as the sigs. The square - brackets here are literal and must appear as shown. - - unary_op = "-" | "~" - - binary_op = ">" | "<" | ">=" | "<=" | "==" | "!=" | "^" | "|" | - "+" | "-" | "&" | "<<" | ">>" | "%" | "*" | "/" - - args = expr | args "," expr - - literal = int_literal | str_literal | hex_literal - -*/ -package compiler diff --git a/exp/ivy/compiler/environ.go b/exp/ivy/compiler/environ.go deleted file mode 100644 index 07c4645e..00000000 --- a/exp/ivy/compiler/environ.go +++ /dev/null @@ -1,72 +0,0 @@ -package compiler - -import "fmt" - -// name-binding environment -type environ struct { - entries map[string]*envEntry - parent *environ -} - -type envEntry struct { - t typeDesc - r role - c *Contract // if t == contractType -} - -type role int - -const ( - roleKeyword role = 1 + iota - roleBuiltin - roleContract - roleContractParam - roleContractValue - roleClause - roleClauseParam - roleClauseValue -) - -var roleDesc = map[role]string{ - roleKeyword: "keyword", - roleBuiltin: "built-in function", - roleContract: "contract", - roleContractParam: "contract parameter", - roleContractValue: "contract value", - roleClause: "clause", - roleClauseParam: "clause parameter", - roleClauseValue: "clause value", -} - -func newEnviron(parent *environ) *environ { - return &environ{ - entries: make(map[string]*envEntry), - parent: parent, - } -} - -func (e *environ) add(name string, t typeDesc, r role) error { - if entry := e.lookup(name); entry != nil { - return fmt.Errorf("%s \"%s\" conflicts with %s", roleDesc[r], name, roleDesc[entry.r]) - } - e.entries[name] = &envEntry{t: t, r: r} - return nil -} - -func (e *environ) addContract(contract *Contract) error { - if entry := e.lookup(contract.Name); entry != nil { - return fmt.Errorf("%s \"%s\" conflicts with %s", roleDesc[roleContract], contract.Name, roleDesc[entry.r]) - } - e.entries[contract.Name] = &envEntry{t: contractType, r: roleContract, c: contract} - return nil -} - -func (e environ) lookup(name string) *envEntry { - if res, ok := e.entries[name]; ok { - return res - } - if e.parent != nil { - return e.parent.lookup(name) - } - return nil -} diff --git a/exp/ivy/compiler/ivytest/ivytest.go b/exp/ivy/compiler/ivytest/ivytest.go deleted file mode 100644 index 55888a14..00000000 --- a/exp/ivy/compiler/ivytest/ivytest.go +++ /dev/null @@ -1,155 +0,0 @@ -package ivytest - -const TrivialLock = ` -contract TrivialLock() locks locked { - clause trivialUnlock() { - unlock locked - } -} -` - -const LockWithPublicKey = ` -contract LockWithPublicKey(publicKey: PublicKey) locks locked { - clause unlockWithSig(sig: Signature) { - verify checkTxSig(publicKey, sig) - unlock locked - } -} -` - -const LockWithPKHash = ` -contract LockWithPublicKeyHash(pubKeyHash: Hash) locks value { - clause spend(pubKey: PublicKey, sig: Signature) { - verify sha3(pubKey) == pubKeyHash - verify checkTxSig(pubKey, sig) - unlock value - } -} -` - -const LockWith2of3Keys = ` -contract LockWith3Keys(pubkey1, pubkey2, pubkey3: PublicKey) locks locked { - clause unlockWith2Sigs(sig1, sig2: Signature) { - verify checkTxMultiSig([pubkey1, pubkey2, pubkey3], [sig1, sig2]) - unlock locked - } -} -` - -const LockToOutput = ` -contract LockToOutput(address: Program) locks locked { - clause relock() { - lock locked with address - } -} -` - -const TradeOffer = ` -contract TradeOffer(requestedAsset: Asset, requestedAmount: Amount, sellerProgram: Program, sellerKey: PublicKey) locks offered { - clause trade() requires payment: requestedAmount of requestedAsset { - lock payment with sellerProgram - unlock offered - } - clause cancel(sellerSig: Signature) { - verify checkTxSig(sellerKey, sellerSig) - lock offered with sellerProgram - } -} -` - -const EscrowedTransfer = ` -contract EscrowedTransfer(agent: PublicKey, sender: Program, recipient: Program) locks value { - clause approve(sig: Signature) { - verify checkTxSig(agent, sig) - lock value with recipient - } - clause reject(sig: Signature) { - verify checkTxSig(agent, sig) - lock value with sender - } -} -` - -const CollateralizedLoan = ` -contract CollateralizedLoan(balanceAsset: Asset, balanceAmount: Amount, deadline: Time, lender: Program, borrower: Program) locks collateral { - clause repay() requires payment: balanceAmount of balanceAsset { - lock payment with lender - lock collateral with borrower - } - clause default() { - verify after(deadline) - lock collateral with lender - } -} -` - -const RevealPreimage = ` -contract RevealPreimage(hash: Hash) locks value { - clause reveal(string: String) { - verify sha3(string) == hash - unlock value - } -} -` - -const PriceChanger = ` -contract PriceChanger(askAmount: Amount, askAsset: Asset, sellerKey: PublicKey, sellerProg: Program) locks offered { - clause changePrice(newAmount: Amount, newAsset: Asset, sig: Signature) { - verify checkTxSig(sellerKey, sig) - lock offered with PriceChanger(newAmount, newAsset, sellerKey, sellerProg) - } - clause redeem() requires payment: askAmount of askAsset { - lock payment with sellerProg - unlock offered - } -} -` - -const CallOptionWithSettlement = ` -contract CallOptionWithSettlement(strikePrice: Amount, - strikeCurrency: Asset, - sellerProgram: Program, - sellerKey: PublicKey, - buyerKey: PublicKey, - deadline: Time) locks underlying { - clause exercise(buyerSig: Signature) - requires payment: strikePrice of strikeCurrency { - verify before(deadline) - verify checkTxSig(buyerKey, buyerSig) - lock payment with sellerProgram - unlock underlying - } - clause expire() { - verify after(deadline) - lock underlying with sellerProgram - } - clause settle(sellerSig: Signature, buyerSig: Signature) { - verify checkTxSig(sellerKey, sellerSig) - verify checkTxSig(buyerKey, buyerSig) - unlock underlying - } -} -` - -const OneTwo = ` -contract Two(b, c: Program, expirationTime: Time) locks value { - clause redeem() { - verify before(expirationTime) - lock value with b - } - clause default() { - verify after(expirationTime) - lock value with c - } -} -contract One(a, b, c: Program, switchTime, expirationTime: Time) locks value { - clause redeem() { - verify before(switchTime) - lock value with a - } - clause switch() { - verify after(switchTime) - lock value with Two(b, c, expirationTime) - } -} -` diff --git a/exp/ivy/compiler/optimize.go b/exp/ivy/compiler/optimize.go deleted file mode 100644 index f05cac4b..00000000 --- a/exp/ivy/compiler/optimize.go +++ /dev/null @@ -1,64 +0,0 @@ -package compiler - -import "strings" - -var optimizations = []struct { - before, after string -}{ - {"0 ROLL", ""}, - {"0 PICK", "DUP"}, - {"1 ROLL", "SWAP"}, - {"1 PICK", "OVER"}, - {"2 ROLL", "ROT"}, - {"TRUE VERIFY", ""}, - {"SWAP SWAP", ""}, - {"OVER OVER", "2DUP"}, - {"SWAP OVER", "TUCK"}, - {"DROP DROP", "2DROP"}, - {"SWAP DROP", "NIP"}, - {"5 ROLL 5 ROLL", "2ROT"}, - {"3 PICK 3 PICK", "2OVER"}, - {"3 ROLL 3 ROLL", "2SWAP"}, - {"2 PICK 2 PICK 2 PICK", "3DUP"}, - {"1 ADD", "1ADD"}, - {"1 SUB", "1SUB"}, - {"EQUAL VERIFY", "EQUALVERIFY"}, - {"SWAP TXSIGHASH ROT", "TXSIGHASH SWAP"}, - {"SWAP EQUAL", "EQUAL"}, - {"SWAP EQUALVERIFY", "EQUALVERIFY"}, - {"SWAP ADD", "ADD"}, - {"SWAP BOOLAND", "BOOLAND"}, - {"SWAP BOOLOR", "BOOLOR"}, - {"SWAP MIN", "MIN"}, - {"SWAP MAX", "MAX"}, - {"DUP 2 PICK EQUAL", "2DUP EQUAL"}, - {"DUP 2 PICK EQUALVERIFY", "2DUP EQUALVERIFY"}, - {"DUP 2 PICK ADD", "2DUP ADD"}, - {"DUP 2 PICK BOOLAND", "2DUP BOOLAND"}, - {"DUP 2 PICK BOOLOR", "2DUP BOOLOR"}, - {"DUP 2 PICK MIN", "2DUP MIN"}, - {"DUP 2 PICK MAX", "2DUP MAX"}, -} - -func optimize(opcodes string) string { - opcodes = " " + opcodes + " " - looping := true - for looping { - looping = false - for _, o := range optimizations { - before := " " + o.before + " " - var after string - if o.after == "" { - after = " " - } else { - after = " " + o.after + " " - } - newOpcodes := strings.Replace(opcodes, before, after, -1) - if newOpcodes != opcodes { - looping = true - opcodes = newOpcodes - } - } - } - return strings.TrimSpace(opcodes) -} diff --git a/exp/ivy/compiler/parse.go b/exp/ivy/compiler/parse.go deleted file mode 100644 index 61a47654..00000000 --- a/exp/ivy/compiler/parse.go +++ /dev/null @@ -1,563 +0,0 @@ -package compiler - -import ( - "bytes" - "encoding/hex" - "fmt" - "strconv" - "unicode" -) - -// We have some function naming conventions. -// -// For terminals: -// scanX takes buf and position, returns new position (and maybe a value) -// peekX takes *parser, returns bool or string -// consumeX takes *parser and maybe a required literal, maybe returns value -// also updates the parser position -// -// For nonterminals: -// parseX takes *parser, returns AST node, updates parser position - -type parser struct { - buf []byte - pos int -} - -func (p *parser) errorf(format string, args ...interface{}) { - panic(parserErr{buf: p.buf, offset: p.pos, format: format, args: args}) -} - -// parse is the main entry point to the parser -func parse(buf []byte) (contracts []*Contract, err error) { - defer func() { - if val := recover(); val != nil { - if e, ok := val.(parserErr); ok { - err = e - } else { - panic(val) - } - } - }() - p := &parser{buf: buf} - contracts = parseContracts(p) - return -} - -// parse functions - -func parseContracts(p *parser) []*Contract { - var result []*Contract - for peekKeyword(p) == "contract" { - contract := parseContract(p) - result = append(result, contract) - } - return result -} - -// contract name(p1, p2: t1, p3: t2) locks value { ... } -func parseContract(p *parser) *Contract { - consumeKeyword(p, "contract") - name := consumeIdentifier(p) - params := parseParams(p) - consumeKeyword(p, "locks") - value := consumeIdentifier(p) - consumeTok(p, "{") - clauses := parseClauses(p) - consumeTok(p, "}") - return &Contract{Name: name, Params: params, Clauses: clauses, Value: value} -} - -// (p1, p2: t1, p3: t2) -func parseParams(p *parser) []*Param { - var params []*Param - consumeTok(p, "(") - first := true - for !peekTok(p, ")") { - if first { - first = false - } else { - consumeTok(p, ",") - } - pt := parseParamsType(p) - params = append(params, pt...) - } - consumeTok(p, ")") - return params -} - -func parseClauses(p *parser) []*Clause { - var clauses []*Clause - for !peekTok(p, "}") { - c := parseClause(p) - clauses = append(clauses, c) - } - return clauses -} - -func parseParamsType(p *parser) []*Param { - firstName := consumeIdentifier(p) - params := []*Param{&Param{Name: firstName}} - for peekTok(p, ",") { - consumeTok(p, ",") - name := consumeIdentifier(p) - params = append(params, &Param{Name: name}) - } - consumeTok(p, ":") - typ := consumeIdentifier(p) - for _, parm := range params { - if tdesc, ok := types[typ]; ok { - parm.Type = tdesc - } else { - p.errorf("unknown type %s", typ) - } - } - return params -} - -func parseClause(p *parser) *Clause { - var c Clause - consumeKeyword(p, "clause") - c.Name = consumeIdentifier(p) - c.Params = parseParams(p) - if peekKeyword(p) == "requires" { - consumeKeyword(p, "requires") - c.Reqs = parseClauseRequirements(p) - } - consumeTok(p, "{") - c.statements = parseStatements(p) - consumeTok(p, "}") - return &c -} - -func parseClauseRequirements(p *parser) []*ClauseReq { - var result []*ClauseReq - first := true - for { - switch { - case first: - first = false - case peekTok(p, ","): - consumeTok(p, ",") - default: - return result - } - var req ClauseReq - req.Name = consumeIdentifier(p) - consumeTok(p, ":") - req.amountExpr = parseExpr(p) - consumeKeyword(p, "of") - req.assetExpr = parseExpr(p) - result = append(result, &req) - } -} - -func parseStatements(p *parser) []statement { - var statements []statement - for !peekTok(p, "}") { - s := parseStatement(p) - statements = append(statements, s) - } - return statements -} - -func parseStatement(p *parser) statement { - switch peekKeyword(p) { - case "verify": - return parseVerifyStmt(p) - case "lock": - return parseLockStmt(p) - case "unlock": - return parseUnlockStmt(p) - } - panic(parseErr(p.buf, p.pos, "unknown keyword \"%s\"", peekKeyword(p))) -} - -func parseVerifyStmt(p *parser) *verifyStatement { - consumeKeyword(p, "verify") - expr := parseExpr(p) - return &verifyStatement{expr: expr} -} - -func parseLockStmt(p *parser) *lockStatement { - consumeKeyword(p, "lock") - locked := parseExpr(p) - consumeKeyword(p, "with") - program := parseExpr(p) - return &lockStatement{locked: locked, program: program} -} - -func parseUnlockStmt(p *parser) *unlockStatement { - consumeKeyword(p, "unlock") - expr := parseExpr(p) - return &unlockStatement{expr} -} - -func parseExpr(p *parser) expression { - // Uses the precedence-climbing algorithm - // - expr := parseUnaryExpr(p) - expr2, pos := parseExprCont(p, expr, 0) - if pos < 0 { - p.errorf("expected expression") - } - p.pos = pos - return expr2 -} - -func parseUnaryExpr(p *parser) expression { - op, pos := scanUnaryOp(p.buf, p.pos) - if pos < 0 { - return parseExpr2(p) - } - p.pos = pos - expr := parseUnaryExpr(p) - return &unaryExpr{op: op, expr: expr} -} - -func parseExprCont(p *parser, lhs expression, minPrecedence int) (expression, int) { - for { - op, pos := scanBinaryOp(p.buf, p.pos) - if pos < 0 || op.precedence < minPrecedence { - break - } - p.pos = pos - - rhs := parseUnaryExpr(p) - - for { - op2, pos2 := scanBinaryOp(p.buf, p.pos) - if pos2 < 0 || op2.precedence <= op.precedence { - break - } - rhs, p.pos = parseExprCont(p, rhs, op2.precedence) - if p.pos < 0 { - return nil, -1 // or is this an error? - } - } - lhs = &binaryExpr{left: lhs, right: rhs, op: op} - } - return lhs, p.pos -} - -func parseExpr2(p *parser) expression { - if expr, pos := scanLiteralExpr(p.buf, p.pos); pos >= 0 { - p.pos = pos - return expr - } - return parseExpr3(p) -} - -func parseExpr3(p *parser) expression { - e := parseExpr4(p) - if peekTok(p, "(") { - args := parseArgs(p) - return &callExpr{fn: e, args: args} - } - return e -} - -func parseExpr4(p *parser) expression { - if peekTok(p, "(") { - consumeTok(p, "(") - e := parseExpr(p) - consumeTok(p, ")") - return e - } - if peekTok(p, "[") { - var elts []expression - consumeTok(p, "[") - first := true - for !peekTok(p, "]") { - if first { - first = false - } else { - consumeTok(p, ",") - } - e := parseExpr(p) - elts = append(elts, e) - } - consumeTok(p, "]") - return listExpr(elts) - } - name := consumeIdentifier(p) - return varRef(name) -} - -func parseArgs(p *parser) []expression { - var exprs []expression - consumeTok(p, "(") - first := true - for !peekTok(p, ")") { - if first { - first = false - } else { - consumeTok(p, ",") - } - e := parseExpr(p) - exprs = append(exprs, e) - } - consumeTok(p, ")") - return exprs -} - -// peek functions - -func peekKeyword(p *parser) string { - name, _ := scanIdentifier(p.buf, p.pos) - return name -} - -func peekTok(p *parser, token string) bool { - pos := scanTok(p.buf, p.pos, token) - return pos >= 0 -} - -// consume functions - -var keywords = []string{ - "contract", "clause", "verify", "output", "return", - "locks", "requires", "of", "lock", "with", "unlock", -} - -func consumeKeyword(p *parser, keyword string) { - pos := scanKeyword(p.buf, p.pos, keyword) - if pos < 0 { - p.errorf("expected keyword %s", keyword) - } - p.pos = pos -} - -func consumeIdentifier(p *parser) string { - name, pos := scanIdentifier(p.buf, p.pos) - if pos < 0 { - p.errorf("expected identifier") - } - p.pos = pos - return name -} - -func consumeTok(p *parser, token string) { - pos := scanTok(p.buf, p.pos, token) - if pos < 0 { - p.errorf("expected %s token", token) - } - p.pos = pos -} - -// scan functions - -func scanUnaryOp(buf []byte, offset int) (*unaryOp, int) { - // Maximum munch. Make sure "-3" scans as ("-3"), not ("-", "3"). - if _, pos := scanIntLiteral(buf, offset); pos >= 0 { - return nil, -1 - } - for _, op := range unaryOps { - newOffset := scanTok(buf, offset, op.op) - if newOffset >= 0 { - return &op, newOffset - } - } - return nil, -1 -} - -func scanBinaryOp(buf []byte, offset int) (*binaryOp, int) { - offset = skipWsAndComments(buf, offset) - var ( - found *binaryOp - newOffset = -1 - ) - for i, op := range binaryOps { - offset2 := scanTok(buf, offset, op.op) - if offset2 >= 0 { - if found == nil || len(op.op) > len(found.op) { - found = &binaryOps[i] - newOffset = offset2 - } - } - } - return found, newOffset -} - -// TODO(bobg): boolean literals? -func scanLiteralExpr(buf []byte, offset int) (expression, int) { - offset = skipWsAndComments(buf, offset) - intliteral, newOffset := scanIntLiteral(buf, offset) - if newOffset >= 0 { - return intliteral, newOffset - } - strliteral, newOffset := scanStrLiteral(buf, offset) - if newOffset >= 0 { - return strliteral, newOffset - } - bytesliteral, newOffset := scanBytesLiteral(buf, offset) // 0x6c249a... - if newOffset >= 0 { - return bytesliteral, newOffset - } - return nil, -1 -} - -func scanIdentifier(buf []byte, offset int) (string, int) { - offset = skipWsAndComments(buf, offset) - i := offset - for ; i < len(buf) && isIDChar(buf[i], i == offset); i++ { - } - if i == offset { - return "", -1 - } - return string(buf[offset:i]), i -} - -func scanTok(buf []byte, offset int, s string) int { - offset = skipWsAndComments(buf, offset) - prefix := []byte(s) - if bytes.HasPrefix(buf[offset:], prefix) { - return offset + len(prefix) - } - return -1 -} - -func scanKeyword(buf []byte, offset int, keyword string) int { - id, newOffset := scanIdentifier(buf, offset) - if newOffset < 0 { - return -1 - } - if id != keyword { - return -1 - } - return newOffset -} - -func scanIntLiteral(buf []byte, offset int) (integerLiteral, int) { - offset = skipWsAndComments(buf, offset) - start := offset - if offset < len(buf) && buf[offset] == '-' { - offset++ - } - i := offset - for ; i < len(buf) && unicode.IsDigit(rune(buf[i])); i++ { - } - if i > offset { - n, err := strconv.ParseInt(string(buf[start:i]), 10, 64) - if err != nil { - return 0, -1 - } - return integerLiteral(n), i - } - return 0, -1 -} - -func scanStrLiteral(buf []byte, offset int) (bytesLiteral, int) { - offset = skipWsAndComments(buf, offset) - if offset >= len(buf) || buf[offset] != '\'' { - return bytesLiteral{}, -1 - } - for i := offset + 1; i < len(buf); i++ { - if buf[i] == '\'' { - return bytesLiteral(buf[offset : i+1]), i + 1 - } - if buf[i] == '\\' { - i++ - } - } - panic(parseErr(buf, offset, "unterminated string literal")) -} - -func scanBytesLiteral(buf []byte, offset int) (bytesLiteral, int) { - offset = skipWsAndComments(buf, offset) - if offset+4 >= len(buf) { - return nil, -1 - } - if buf[offset] != '0' || (buf[offset+1] != 'x' && buf[offset+1] != 'X') { - return nil, -1 - } - if !isHexDigit(buf[offset+2]) || !isHexDigit(buf[offset+3]) { - return nil, -1 - } - i := offset + 4 - for ; i < len(buf); i += 2 { - if i == len(buf)-1 { - panic(parseErr(buf, offset, "odd number of digits in hex literal")) - } - if !isHexDigit(buf[i]) { - break - } - if !isHexDigit(buf[i+1]) { - panic(parseErr(buf, offset, "odd number of digits in hex literal")) - } - } - decoded := make([]byte, hex.DecodedLen(i-(offset+2))) - _, err := hex.Decode(decoded, buf[offset+2:i]) - if err != nil { - return bytesLiteral{}, -1 - } - return bytesLiteral(decoded), i -} - -func skipWsAndComments(buf []byte, offset int) int { - var inComment bool - for ; offset < len(buf); offset++ { - c := buf[offset] - if inComment { - if c == '\n' { - inComment = false - } - } else { - if c == '/' && offset < len(buf)-1 && buf[offset+1] == '/' { - inComment = true - offset++ // skip two chars instead of one - } else if !unicode.IsSpace(rune(c)) { - break - } - } - } - return offset -} - -func isHexDigit(b byte) bool { - return (b >= '0' && b <= '9') || (b >= 'a' && b <= 'f') || (b >= 'A' && b <= 'F') -} - -func isIDChar(c byte, initial bool) bool { - if c >= 'a' && c <= 'z' { - return true - } - if c >= 'A' && c <= 'Z' { - return true - } - if c == '_' { - return true - } - if initial { - return false - } - return unicode.IsDigit(rune(c)) -} - -type parserErr struct { - buf []byte - offset int - format string - args []interface{} -} - -func parseErr(buf []byte, offset int, format string, args ...interface{}) error { - return parserErr{buf: buf, offset: offset, format: format, args: args} -} - -func (p parserErr) Error() string { - // Lines start at 1, columns start at 0, like nature intended. - line := 1 - col := 0 - for i := 0; i < p.offset; i++ { - if p.buf[i] == '\n' { - line++ - col = 0 - } else { - col++ - } - } - args := []interface{}{line, col} - args = append(args, p.args...) - return fmt.Sprintf("line %d, col %d: "+p.format, args...) -} diff --git a/exp/ivy/compiler/stack.go b/exp/ivy/compiler/stack.go deleted file mode 100644 index 1459f02a..00000000 --- a/exp/ivy/compiler/stack.go +++ /dev/null @@ -1,116 +0,0 @@ -package compiler - -type ( - stack struct { - *stackEntry - } - stackEntry struct { - str string - prev *stackEntry - } -) - -func (stk stack) isEmpty() bool { - return stk.stackEntry == nil -} - -func (stk stack) top() string { - if stk.isEmpty() { - return "" - } - return stk.str -} - -func (stk stack) add(str string) stack { - e := &stackEntry{ - str: str, - prev: stk.stackEntry, - } - return stack{e} -} - -func (stk stack) addFromStack(other stack) stack { - if other.isEmpty() { - return stk - } - res := stk.addFromStack(other.drop()) - return res.add(other.top()) -} - -func (stk stack) drop() stack { - if !stk.isEmpty() { - stk = stack{stk.prev} - } - return stk -} - -func (stk stack) dropN(n int) stack { - for n > 0 { - stk = stk.drop() - n-- - } - return stk -} - -func (stk stack) find(str string) int { - if stk.isEmpty() { - return -1 - } - if stk.str == str { - return 0 - } - res := stk.drop().find(str) - if res < 0 { - return res - } - return res + 1 -} - -func (stk stack) roll(n int) stack { - var x func(stack, int) (stack, string) - x = func(stk stack, n int) (stack, string) { - if n == 0 { - return stk.drop(), stk.top() - } - stk2, entry := x(stk.drop(), n-1) - return stk2.add(stk.top()), entry - } - stk, entry := x(stk, n) - return stk.add(entry) -} - -func (stk stack) swap() stack { - a := stk.top() - stk = stk.drop() - b := stk.top() - stk = stk.drop() - return stk.add(a).add(b) -} - -func (stk stack) dup() stack { - return stk.add(stk.top()) -} - -func (stk stack) over() stack { - t := stk.drop().top() - return stk.add(t) -} - -func (stk stack) pick(n int) stack { - t := stk.dropN(n).top() - return stk.add(t) -} - -func (stk stack) String() string { - if stk.stackEntry == nil { - return "[]" - } - var x func(stk stack) string - x = func(stk stack) string { - if stk.stackEntry == nil { - return "" - } - return x(stk.drop()) + " " + stk.stackEntry.str - } - return "[..." + x(stk) + "]" -} diff --git a/exp/ivy/compiler/types.go b/exp/ivy/compiler/types.go deleted file mode 100644 index 4fb4bfff..00000000 --- a/exp/ivy/compiler/types.go +++ /dev/null @@ -1,78 +0,0 @@ -package compiler - -type typeDesc string - -var ( - amountType = typeDesc("Amount") - assetType = typeDesc("Asset") - boolType = typeDesc("Boolean") - contractType = typeDesc("Contract") - hashType = typeDesc("Hash") - intType = typeDesc("Integer") - listType = typeDesc("List") - nilType = typeDesc("") - predType = typeDesc("Predicate") - progType = typeDesc("Program") - pubkeyType = typeDesc("PublicKey") - sigType = typeDesc("Signature") - strType = typeDesc("String") - timeType = typeDesc("Time") - valueType = typeDesc("Value") - - sha3StrType = typeDesc("Sha3(String)") - sha3PubkeyType = typeDesc("Sha3(PublicKey)") - sha256StrType = typeDesc("Sha256(String)") - sha256PubkeyType = typeDesc("Sha256(PublicKey)") -) - -var types = map[string]typeDesc{ - string(amountType): amountType, - string(assetType): assetType, - string(boolType): boolType, - string(hashType): hashType, - string(intType): intType, - string(listType): listType, - string(nilType): nilType, - string(predType): predType, - string(progType): progType, - string(pubkeyType): pubkeyType, - string(sigType): sigType, - string(strType): strType, - string(timeType): timeType, - string(valueType): valueType, - - string(sha3StrType): sha3StrType, - string(sha3PubkeyType): sha3PubkeyType, - string(sha256StrType): sha256StrType, - string(sha256PubkeyType): sha256PubkeyType, -} - -func isHashSubtype(t typeDesc) bool { - switch t { - case sha3StrType, sha3PubkeyType, sha256StrType, sha256PubkeyType: - return true - } - return false -} - -func propagateType(contract *Contract, clause *Clause, env *environ, t typeDesc, e expression) { - v, ok := e.(varRef) - if !ok { - return - } - if entry := env.lookup(string(v)); entry != nil { - entry.t = t - for _, p := range contract.Params { - if p.Name == string(v) { - p.InferredType = t - return - } - } - for _, p := range clause.Params { - if p.Name == string(v) { - p.InferredType = t - return - } - } - } -} diff --git a/net/http/authn/authn.go b/net/http/authn/authn.go new file mode 100644 index 00000000..2bda7759 --- /dev/null +++ b/net/http/authn/authn.go @@ -0,0 +1,157 @@ +package authn + +import ( + "context" + "crypto/x509" + "encoding/hex" + "net" + "net/http" + "strings" + "sync" + "time" + + "github.com/bytom/blockchain/accesstoken" + "github.com/bytom/errors" +) + +const tokenExpiry = time.Minute * 5 + +var loopbackOn = true + +var ( + //ErrInvalidToken is returned when authenticate is called with invalide token. + ErrInvalidToken = errors.New("invalid token") + //ErrNoToken is returned when authenticate is called with no token. + ErrNoToken = errors.New("no token") +) + +//API describe the token authenticate. +type API struct { + tokens *accesstoken.CredentialStore + crosscoreRPCPrefix string + rootCAs *x509.CertPool + + tokenMu sync.Mutex // protects the following + tokenMap map[string]tokenResult +} + +type tokenResult struct { + valid bool + lastLookup time.Time +} + +//NewAPI create a token authenticate object. +func NewAPI(tokens *accesstoken.CredentialStore) *API { + return &API{ + tokens: tokens, + tokenMap: make(map[string]tokenResult), + } +} + +// Authenticate returns the request, with added tokens and/or localhost +// flags in the context, as appropriate. +func (a *API) Authenticate(req *http.Request) (*http.Request, error) { + ctx := req.Context() + + token, err := a.tokenAuthn(req) + if err == nil && token != "" { + // if this request was successfully authenticated with a token, pass the token along + ctx = newContextWithToken(ctx, token) + } + local := a.localhostAuthn(req) + if local { + ctx = newContextWithLocalhost(ctx) + } + // Temporary workaround. Dashboard is always ok. + // See loopbackOn comment above. + if strings.HasPrefix(req.URL.Path, "/dashboard/") || req.URL.Path == "/dashboard" { + return req.WithContext(ctx), nil + } + if loopbackOn && local { + return req.WithContext(ctx), nil + } + + return req.WithContext(ctx), err +} + +// checks the request for a valid client cert list. +// If found, it is added to the request's context. +// Note that an *invalid* client cert is treated the +// same as no client cert -- it is omitted from the +// returned context, but the connection may proceed. +func certAuthn(req *http.Request, rootCAs *x509.CertPool) context.Context { + if req.TLS != nil && len(req.TLS.PeerCertificates) > 0 { + certs := req.TLS.PeerCertificates + + // Same logic as serverHandshakeState.processCertsFromClient + // in $GOROOT/src/crypto/tls/handshake_server.go. + opts := x509.VerifyOptions{ + Roots: rootCAs, + CurrentTime: time.Now(), + Intermediates: x509.NewCertPool(), + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + } + for _, cert := range certs[1:] { + opts.Intermediates.AddCert(cert) + } + + if _, err := certs[0].Verify(opts); err != nil { + // crypto/tls treats this as an error: + // errors.New("tls: failed to verify client's certificate: " + err.Error()) + // For us, it is ok; we want to treat it the same as if there + // were no client cert presented. + return req.Context() + } + + return context.WithValue(req.Context(), x509CertsKey, certs) + } + return req.Context() +} + +// returns true if this request is coming from a loopback address +func (a *API) localhostAuthn(req *http.Request) bool { + h, _, err := net.SplitHostPort(req.RemoteAddr) + if err != nil { + return false + } + if !net.ParseIP(h).IsLoopback() { + return false + } + return true +} + +func (a *API) tokenAuthn(req *http.Request) (string, error) { + user, pw, ok := req.BasicAuth() + if !ok { + return "", ErrNoToken + } + return user, a.cachedTokenAuthnCheck(req.Context(), user, pw) +} + +func (a *API) tokenAuthnCheck(ctx context.Context, user, pw string) (bool, error) { + pwBytes, err := hex.DecodeString(pw) + if err != nil { + return false, nil + } + return a.tokens.Check(ctx, user, pwBytes) +} + +func (a *API) cachedTokenAuthnCheck(ctx context.Context, user, pw string) error { + a.tokenMu.Lock() + res, ok := a.tokenMap[user+pw] + a.tokenMu.Unlock() + if !ok || time.Now().After(res.lastLookup.Add(tokenExpiry)) { + valid, err := a.tokenAuthnCheck(ctx, user, pw) + if err != nil { + return errors.Wrap(err) + } + res = tokenResult{valid: valid, lastLookup: time.Now()} + a.tokenMu.Lock() + a.tokenMap[user+pw] = res + a.tokenMu.Unlock() + } + if !res.valid { + return ErrInvalidToken + } + return nil +} diff --git a/net/http/authn/authn_test.go b/net/http/authn/authn_test.go new file mode 100644 index 00000000..e0e7d723 --- /dev/null +++ b/net/http/authn/authn_test.go @@ -0,0 +1,56 @@ +package authn + +import ( + "context" + "net/http" + "os" + "strings" + "testing" + + dbm "github.com/tendermint/tmlibs/db" + + "github.com/bytom/blockchain/accesstoken" + "github.com/bytom/errors" +) + +func TestAuthenticate(t *testing.T) { + ctx := context.Background() + + var token *string + tokenDB := dbm.NewDB("testdb", "leveldb", "temp") + defer os.RemoveAll("temp") + accessTokens := accesstoken.NewStore(tokenDB) + token, err := accessTokens.Create(ctx, "alice", "test") + if err != nil { + t.Errorf("create token error") + } + + cases := []struct { + id, tok string + want error + }{ + {"alice", *token, nil}, + {"alice", "alice:abcsdsdfassdfsefsfsfesfesfefsefa", ErrInvalidToken}, + } + + api := NewAPI(accessTokens) + + for _, c := range cases { + var username, password string + toks := strings.SplitN(c.tok, ":", 2) + if len(toks) > 0 { + username = toks[0] + } + if len(toks) > 1 { + password = toks[1] + } + + req, _ := http.NewRequest("GET", "/", nil) + req.SetBasicAuth(username, password) + + _, err := api.Authenticate(req) + if errors.Root(err) != c.want { + t.Errorf("Authenticate(%s) error = %s want %s", c.id, err, c.want) + } + } +} diff --git a/net/http/authn/context.go b/net/http/authn/context.go new file mode 100644 index 00000000..e2b1369f --- /dev/null +++ b/net/http/authn/context.go @@ -0,0 +1,49 @@ +package authn + +import ( + "context" + "crypto/x509" +) + +type key int + +const ( + tokenKey key = iota + localhostKey + x509CertsKey +) + +// X509Certs returns the cert stored in the context, if it exists. +func X509Certs(ctx context.Context) []*x509.Certificate { + c, _ := ctx.Value(x509CertsKey).([]*x509.Certificate) + return c +} + +// newContextWithToken sets the token in a new context and returns the context. +func newContextWithToken(ctx context.Context, token string) context.Context { + return context.WithValue(ctx, tokenKey, token) +} + +// Token returns the token stored in the context, if there is one. +func Token(ctx context.Context) string { + t, ok := ctx.Value(tokenKey).(string) + if !ok { + return "" + } + return t +} + +// newContextWithLocalhost sets the localhost flag to `true` in a new context +// and returns that context. +func newContextWithLocalhost(ctx context.Context) context.Context { + return context.WithValue(ctx, localhostKey, true) +} + +// Localhost returns true if the localhost flag has been set. +func Localhost(ctx context.Context) bool { + l, ok := ctx.Value(localhostKey).(bool) + if ok && l { + return true + } + return false +} diff --git a/node/node.go b/node/node.go index de4333d0..f5546fa6 100755 --- a/node/node.go +++ b/node/node.go @@ -28,6 +28,7 @@ import ( cfg "github.com/bytom/config" "github.com/bytom/env" "github.com/bytom/errors" + "github.com/bytom/net/http/authn" "github.com/bytom/p2p" "github.com/bytom/protocol" "github.com/bytom/types" @@ -86,7 +87,23 @@ func (wh *waitHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { wh.h.ServeHTTP(w, req) } -func rpcInit(h *bc.BlockchainReactor, config *cfg.Config) { +func AuthHandler(handler http.Handler, accessTokens *accesstoken.CredentialStore) http.Handler { + + authenticator := authn.NewAPI(accessTokens) + + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + // TODO(tessr): check that this path exists; return early if this path isn't legit + req, err := authenticator.Authenticate(req) + if err != nil { + log.WithField("error", errors.Wrap(err, "Serve")).Error("Authenticate fail") + + return + } + handler.ServeHTTP(rw, req) + }) +} + +func rpcInit(h *bc.BlockchainReactor, config *cfg.Config, accessTokens *accesstoken.CredentialStore) { // The waitHandler accepts incoming requests, but blocks until its underlying // handler is set, when the second phase is complete. var coreHandler waitHandler @@ -95,6 +112,7 @@ func rpcInit(h *bc.BlockchainReactor, config *cfg.Config) { mux.Handle("/", &coreHandler) var handler http.Handler = mux + handler = AuthHandler(handler, accessTokens) handler = RedirectHandler(handler) secureheader.DefaultConfig.PermitClearLoopback = true @@ -215,7 +233,7 @@ func NewNode(config *cfg.Config) *Node { sw.AddReactor("BLOCKCHAIN", bcReactor) - rpcInit(bcReactor, config) + rpcInit(bcReactor, config, accessTokens) // Optionally, start the pex reactor var addrBook *p2p.AddrBook if config.P2P.PexReactor { diff --git a/protocol/bc/asset.go b/protocol/bc/asset.go index c330738f..4791a490 100644 --- a/protocol/bc/asset.go +++ b/protocol/bc/asset.go @@ -47,10 +47,9 @@ func ComputeAssetID(prog []byte, initialBlockID *Hash, vmVersion uint64, data *H return def.ComputeAssetID() } -func (a *AssetAmount) ReadFrom(r *blockchain.Reader) error { +func (a *AssetAmount) ReadFrom(r *blockchain.Reader) (err error) { var assetID AssetID - _, err := assetID.ReadFrom(r) - if err != nil { + if _, err = assetID.ReadFrom(r); err != nil { return err } a.AssetId = &assetID diff --git a/protocol/bc/bc.pb.go b/protocol/bc/bc.pb.go index 9162ddd1..705a872e 100644 --- a/protocol/bc/bc.pb.go +++ b/protocol/bc/bc.pb.go @@ -364,9 +364,7 @@ type TxHeader struct { SerializedSize uint64 `protobuf:"varint,2,opt,name=serialized_size,json=serializedSize" json:"serialized_size,omitempty"` ResultIds []*Hash `protobuf:"bytes,3,rep,name=result_ids,json=resultIds" json:"result_ids,omitempty"` Data *Hash `protobuf:"bytes,4,opt,name=data" json:"data,omitempty"` - MinTimeMs uint64 `protobuf:"varint,5,opt,name=min_time_ms,json=minTimeMs" json:"min_time_ms,omitempty"` - MaxTimeMs uint64 `protobuf:"varint,6,opt,name=max_time_ms,json=maxTimeMs" json:"max_time_ms,omitempty"` - ExtHash *Hash `protobuf:"bytes,7,opt,name=ext_hash,json=extHash" json:"ext_hash,omitempty"` + ExtHash *Hash `protobuf:"bytes,5,opt,name=ext_hash,json=extHash" json:"ext_hash,omitempty"` } func (m *TxHeader) Reset() { *m = TxHeader{} } @@ -402,20 +400,6 @@ func (m *TxHeader) GetData() *Hash { return nil } -func (m *TxHeader) GetMinTimeMs() uint64 { - if m != nil { - return m.MinTimeMs - } - return 0 -} - -func (m *TxHeader) GetMaxTimeMs() uint64 { - if m != nil { - return m.MaxTimeMs - } - return 0 -} - func (m *TxHeader) GetExtHash() *Hash { if m != nil { return m.ExtHash @@ -781,65 +765,63 @@ func init() { func init() { proto.RegisterFile("bc.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 956 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4b, 0x6f, 0xdb, 0x46, - 0x10, 0x06, 0x45, 0x4a, 0xa4, 0x46, 0xa9, 0x25, 0xaf, 0x8d, 0x80, 0x08, 0xd2, 0x22, 0x65, 0xe1, - 0x3a, 0x41, 0x01, 0xc3, 0x91, 0xd3, 0xa2, 0x87, 0x5e, 0xdc, 0xba, 0x6d, 0x74, 0x50, 0x1f, 0x74, - 0x90, 0x2b, 0xb1, 0x22, 0x37, 0xd6, 0xa2, 0xe2, 0xae, 0xca, 0x5d, 0xaa, 0x82, 0x7f, 0x46, 0xaf, - 0xfd, 0x17, 0x3d, 0xf6, 0x9c, 0x7f, 0xd3, 0x5b, 0x6f, 0xbd, 0x15, 0x1c, 0x2e, 0xa9, 0x87, 0xa5, - 0x44, 0x42, 0xd2, 0x1b, 0xe7, 0xc1, 0x79, 0x7c, 0x33, 0xdf, 0xee, 0x82, 0x37, 0x8a, 0xcf, 0xa6, - 0x99, 0xd4, 0x92, 0x34, 0x46, 0x71, 0xf0, 0x1d, 0x38, 0xcf, 0xa9, 0x1a, 0x93, 0x03, 0x68, 0xcc, - 0xce, 0x7d, 0xeb, 0x91, 0xf5, 0xb8, 0x15, 0x36, 0x66, 0xe7, 0x28, 0x3f, 0xf5, 0x1b, 0x46, 0x7e, - 0x8a, 0x72, 0xdf, 0xb7, 0x8d, 0xdc, 0x47, 0xf9, 0xc2, 0x77, 0x8c, 0x7c, 0x11, 0x7c, 0x05, 0xee, - 0x4f, 0x99, 0xbc, 0xc9, 0x68, 0x4a, 0x3e, 0x04, 0x98, 0xa5, 0xd1, 0x8c, 0x65, 0x8a, 0x4b, 0x81, - 0x21, 0x9d, 0xb0, 0x3d, 0x4b, 0x5f, 0x96, 0x0a, 0x42, 0xc0, 0x89, 0x65, 0xc2, 0x30, 0xf6, 0xbd, - 0x10, 0xbf, 0x83, 0x01, 0xb8, 0x97, 0x4a, 0x31, 0x3d, 0xb8, 0x7a, 0xe7, 0x42, 0x86, 0xd0, 0xc1, - 0x50, 0x97, 0xa9, 0xcc, 0x85, 0x26, 0x9f, 0x82, 0x47, 0x0b, 0x31, 0xe2, 0x09, 0x06, 0xed, 0xf4, - 0x3b, 0x67, 0xa3, 0xf8, 0xcc, 0x64, 0x0b, 0x5d, 0x34, 0x0e, 0x12, 0x72, 0x1f, 0x5a, 0x14, 0xff, - 0xc0, 0x54, 0x4e, 0x68, 0xa4, 0xe0, 0x0f, 0x0b, 0xba, 0xe8, 0x7c, 0xc5, 0x5e, 0x71, 0xc1, 0x75, - 0xd1, 0x41, 0x1f, 0x7a, 0xf8, 0x49, 0x27, 0xd1, 0x68, 0x22, 0xe3, 0x5f, 0x16, 0xb1, 0xbd, 0x22, - 0x76, 0x81, 0x67, 0x78, 0x60, 0x3c, 0xbe, 0x2e, 0x1c, 0x06, 0x09, 0xf9, 0x02, 0x7a, 0x5c, 0xa9, - 0x9c, 0x8a, 0x98, 0x45, 0xd3, 0x12, 0x28, 0xcc, 0x64, 0xea, 0x31, 0xd8, 0x85, 0xdd, 0xca, 0xa9, - 0x02, 0xf3, 0x21, 0x38, 0x09, 0xd5, 0x14, 0x1b, 0x5e, 0x8e, 0x8f, 0xda, 0x60, 0x02, 0x9d, 0x97, - 0x74, 0x92, 0xb3, 0x6b, 0x99, 0x67, 0x31, 0x23, 0x0f, 0xc0, 0xce, 0xd8, 0xab, 0x3b, 0xb5, 0x14, - 0x4a, 0x72, 0x02, 0xcd, 0x59, 0xe1, 0x6a, 0xb2, 0x76, 0x6b, 0x14, 0x4a, 0xa0, 0xc2, 0xd2, 0x4a, - 0x1e, 0x80, 0x37, 0x95, 0x0a, 0xfb, 0xc4, 0x9c, 0x4e, 0x58, 0xcb, 0xc1, 0xaf, 0xd0, 0xc3, 0x6c, - 0x57, 0x4c, 0x69, 0x2e, 0x28, 0x62, 0xf1, 0x3f, 0xa7, 0xfc, 0xbb, 0x01, 0x1d, 0x84, 0xf0, 0x39, - 0xa3, 0x09, 0xcb, 0x88, 0x0f, 0xee, 0xea, 0x62, 0x55, 0x22, 0x39, 0x85, 0xae, 0x62, 0x19, 0xa7, - 0x13, 0x7e, 0xcb, 0x92, 0x48, 0xf1, 0x5b, 0x66, 0x26, 0x79, 0xb0, 0x50, 0x5f, 0xf3, 0x5b, 0x56, - 0x4c, 0x7a, 0xcc, 0xf8, 0xcd, 0x58, 0x9b, 0x64, 0x46, 0x22, 0xcf, 0xe0, 0x70, 0x9a, 0xb1, 0x19, - 0x97, 0xb9, 0x5a, 0x8c, 0xd5, 0x59, 0xeb, 0xab, 0x5b, 0xb9, 0x54, 0x73, 0x7d, 0x08, 0x8e, 0x62, - 0x2c, 0xf1, 0x9b, 0xeb, 0xf3, 0x29, 0xb4, 0xe4, 0x63, 0xb8, 0xa7, 0x79, 0xca, 0x94, 0xa6, 0xe9, - 0x34, 0x4a, 0x95, 0xdf, 0xc2, 0x8c, 0x9d, 0x5a, 0x37, 0x54, 0xe4, 0x73, 0x38, 0xd4, 0x19, 0x15, - 0x8a, 0xc6, 0x45, 0xc3, 0x2a, 0xca, 0xa4, 0xd4, 0xbe, 0xbb, 0x16, 0xad, 0xb7, 0xec, 0x12, 0x4a, - 0xa9, 0xc9, 0x13, 0xe8, 0xe0, 0xea, 0x9a, 0x1f, 0xbc, 0xb5, 0x1f, 0xa0, 0x34, 0xa2, 0xeb, 0x31, - 0x34, 0x85, 0x14, 0x31, 0xf3, 0xdb, 0x98, 0xbd, 0x14, 0x0a, 0x1a, 0x8e, 0xb8, 0x56, 0x3e, 0xa0, - 0x12, 0xbf, 0x83, 0x7f, 0x2d, 0xf0, 0x5e, 0xcc, 0xdf, 0x1f, 0xd4, 0xa7, 0x00, 0x19, 0x53, 0xf9, - 0xa4, 0x60, 0x9f, 0xf2, 0xed, 0x47, 0xf6, 0x4a, 0x8d, 0xed, 0xd2, 0x36, 0x48, 0x54, 0xbd, 0xe5, - 0xce, 0xa6, 0x2d, 0x27, 0x1f, 0x41, 0x27, 0xe5, 0x22, 0x2a, 0x50, 0x2b, 0x40, 0x6c, 0x96, 0x27, - 0x4a, 0xca, 0xc5, 0x0b, 0x9e, 0xb2, 0xa1, 0x42, 0x3b, 0x9d, 0xd7, 0xf6, 0x96, 0xb1, 0xd3, 0xb9, - 0xb1, 0x7f, 0x02, 0x1e, 0x9b, 0xeb, 0x68, 0x4c, 0xd5, 0xf8, 0x0e, 0xb2, 0x2e, 0x9b, 0xeb, 0xe2, - 0x23, 0xf8, 0xc7, 0x02, 0x7b, 0x98, 0xcf, 0xc9, 0x13, 0x70, 0x15, 0xb2, 0x49, 0xf9, 0x16, 0x16, - 0x8c, 0x6b, 0xbb, 0xc4, 0xb2, 0xb0, 0xb2, 0x93, 0x13, 0x70, 0xdf, 0x40, 0xe5, 0xca, 0xb6, 0x92, - 0xde, 0xde, 0x92, 0x9e, 0x7c, 0x0f, 0xc7, 0xbf, 0x71, 0x2d, 0x98, 0x52, 0x51, 0xb2, 0xa0, 0x97, - 0xf2, 0x1d, 0xac, 0xe1, 0xb8, 0xae, 0x61, 0x89, 0x7b, 0xe1, 0x91, 0xf9, 0x63, 0x49, 0xa7, 0xc8, - 0x67, 0x70, 0x58, 0x05, 0xa2, 0xd9, 0x4d, 0x9e, 0x32, 0xa1, 0x0b, 0xc8, 0xec, 0xc7, 0xf7, 0xc2, - 0x9e, 0x31, 0x5c, 0x56, 0xfa, 0xe0, 0x2f, 0x0b, 0x9a, 0x3f, 0xe0, 0x3a, 0x2c, 0xf5, 0x62, 0xed, - 0xd8, 0x4b, 0x63, 0x5b, 0x2f, 0x1b, 0x4b, 0xb0, 0x37, 0x97, 0x40, 0xbe, 0x84, 0xa3, 0xda, 0x59, - 0xc4, 0x63, 0x99, 0xb1, 0x64, 0x13, 0xf1, 0xaa, 0x88, 0x97, 0xc6, 0x67, 0x90, 0x04, 0x3f, 0x83, - 0xf7, 0x8d, 0xe4, 0x62, 0x44, 0x15, 0x23, 0xdf, 0x2e, 0xa2, 0x2c, 0xc1, 0x67, 0x5a, 0xd9, 0x8c, - 0x1e, 0xb9, 0x8b, 0x5e, 0xf0, 0xda, 0x82, 0xd6, 0x8f, 0xb9, 0x9e, 0xe6, 0x9a, 0x9c, 0x42, 0xab, - 0x9c, 0xb3, 0x09, 0x72, 0x67, 0x0d, 0x8c, 0x99, 0x3c, 0x83, 0x6e, 0x2c, 0x85, 0xce, 0xe4, 0xe4, - 0x4d, 0x07, 0xfb, 0x81, 0xf1, 0xd9, 0xe9, 0x5c, 0x5f, 0x81, 0xd9, 0xd9, 0x06, 0xb3, 0x0f, 0xae, - 0xcc, 0x12, 0x2e, 0xe8, 0xc4, 0x50, 0xa2, 0x12, 0x83, 0xdf, 0x2d, 0x80, 0x90, 0x69, 0x9e, 0xb1, - 0x02, 0xe3, 0xdd, 0x5b, 0xa9, 0x8a, 0x6a, 0xbc, 0xb5, 0x28, 0x7b, 0x87, 0xa2, 0x9c, 0xd5, 0xa2, - 0xfe, 0xb4, 0xc1, 0x1b, 0x98, 0xdb, 0x8d, 0x9c, 0x40, 0xbb, 0x9c, 0xf6, 0xa6, 0xbb, 0xd3, 0x2b, - 0x4d, 0x83, 0x64, 0xd7, 0x1b, 0xe4, 0x3d, 0x80, 0xb9, 0x65, 0x81, 0x9a, 0xfb, 0x2d, 0x10, 0x19, - 0x82, 0x5f, 0x6f, 0x33, 0x3e, 0x3b, 0x92, 0xfa, 0xd9, 0x80, 0xe7, 0x52, 0xa7, 0x7f, 0x54, 0xf7, - 0xb0, 0x78, 0x51, 0x84, 0xf7, 0xab, 0xed, 0x5e, 0x7b, 0x69, 0x6c, 0x64, 0x92, 0xbb, 0x1f, 0x93, - 0xbc, 0xb7, 0x32, 0x69, 0x79, 0x68, 0xed, 0xd5, 0xa1, 0xbd, 0x6e, 0x40, 0xf3, 0x7a, 0xca, 0x44, - 0x42, 0xce, 0xa1, 0xab, 0xa6, 0x4c, 0xe8, 0x48, 0x22, 0x3f, 0x36, 0xcd, 0xed, 0x03, 0x74, 0x28, - 0xf9, 0x53, 0x5e, 0x8d, 0xef, 0xba, 0x4d, 0x5b, 0xa6, 0xe2, 0xec, 0x39, 0x95, 0x7d, 0xce, 0xc4, - 0x6d, 0x30, 0xb6, 0xf6, 0x82, 0xd1, 0x5d, 0x81, 0x71, 0xd4, 0xc2, 0x07, 0xf7, 0xc5, 0x7f, 0x01, - 0x00, 0x00, 0xff, 0xff, 0x01, 0x5d, 0xef, 0x9a, 0x7c, 0x0b, 0x00, 0x00, + // 928 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xdd, 0x6e, 0xe3, 0x44, + 0x14, 0x96, 0x63, 0x27, 0x76, 0x4e, 0x4a, 0x93, 0x4e, 0xab, 0x95, 0xb5, 0x5a, 0xa4, 0x62, 0x54, + 0xba, 0x2b, 0xa4, 0xaa, 0x9b, 0x2e, 0x88, 0x0b, 0x6e, 0x0a, 0x05, 0x36, 0x17, 0xe5, 0xc7, 0x45, + 0x7b, 0x6b, 0x4d, 0xec, 0xd9, 0x66, 0x44, 0xe2, 0x09, 0x33, 0xe3, 0x50, 0xf5, 0x31, 0xb8, 0xe5, + 0x2d, 0xb8, 0x83, 0xeb, 0x7d, 0x1b, 0xee, 0x78, 0x02, 0xe4, 0xe3, 0xb1, 0xf3, 0xe7, 0xec, 0x26, + 0xda, 0x72, 0xe7, 0xf3, 0xe3, 0xf3, 0xf3, 0x9d, 0xf3, 0xcd, 0x0c, 0x78, 0xc3, 0xf8, 0x6c, 0x2a, + 0x85, 0x16, 0xa4, 0x31, 0x8c, 0x83, 0x6f, 0xc1, 0x79, 0x49, 0xd5, 0x88, 0xec, 0x43, 0x63, 0x76, + 0xee, 0x5b, 0xc7, 0xd6, 0xd3, 0x56, 0xd8, 0x98, 0x9d, 0xa3, 0xfc, 0xdc, 0x6f, 0x18, 0xf9, 0x39, + 0xca, 0x7d, 0xdf, 0x36, 0x72, 0x1f, 0xe5, 0x0b, 0xdf, 0x31, 0xf2, 0x45, 0xf0, 0x25, 0xb8, 0x3f, + 0x4a, 0x71, 0x2b, 0xe9, 0x84, 0x7c, 0x08, 0x30, 0x9b, 0x44, 0x33, 0x26, 0x15, 0x17, 0x29, 0x86, + 0x74, 0xc2, 0xf6, 0x6c, 0xf2, 0xaa, 0x50, 0x10, 0x02, 0x4e, 0x2c, 0x12, 0x86, 0xb1, 0xf7, 0x42, + 0xfc, 0x0e, 0x06, 0xe0, 0x5e, 0x2a, 0xc5, 0xf4, 0xe0, 0xea, 0xbd, 0x0b, 0xb9, 0x86, 0x0e, 0x86, + 0xba, 0x9c, 0x88, 0x2c, 0xd5, 0xe4, 0x13, 0xf0, 0x68, 0x2e, 0x46, 0x3c, 0xc1, 0xa0, 0x9d, 0x7e, + 0xe7, 0x6c, 0x18, 0x9f, 0x99, 0x6c, 0xa1, 0x8b, 0xc6, 0x41, 0x42, 0x1e, 0x41, 0x8b, 0xe2, 0x1f, + 0x98, 0xca, 0x09, 0x8d, 0x14, 0xfc, 0x61, 0x41, 0x17, 0x9d, 0xaf, 0xd8, 0x6b, 0x9e, 0x72, 0x9d, + 0x77, 0xd0, 0x87, 0x1e, 0x7e, 0xd2, 0x71, 0x34, 0x1c, 0x8b, 0xf8, 0x97, 0x79, 0x6c, 0x2f, 0x8f, + 0x9d, 0xe3, 0x19, 0xee, 0x1b, 0x8f, 0xaf, 0x72, 0x87, 0x41, 0x42, 0x3e, 0x87, 0x1e, 0x57, 0x2a, + 0xa3, 0x69, 0xcc, 0xa2, 0x69, 0x01, 0x14, 0x66, 0x32, 0xf5, 0x18, 0xec, 0xc2, 0x6e, 0xe9, 0x54, + 0x82, 0xf9, 0x04, 0x9c, 0x84, 0x6a, 0x8a, 0x0d, 0x2f, 0xc6, 0x47, 0x6d, 0x30, 0x86, 0xce, 0x2b, + 0x3a, 0xce, 0xd8, 0x8d, 0xc8, 0x64, 0xcc, 0xc8, 0x63, 0xb0, 0x25, 0x7b, 0xbd, 0x56, 0x4b, 0xae, + 0x24, 0x27, 0xd0, 0x9c, 0xe5, 0xae, 0x26, 0x6b, 0xb7, 0x42, 0xa1, 0x00, 0x2a, 0x2c, 0xac, 0xe4, + 0x31, 0x78, 0x53, 0xa1, 0xb0, 0x4f, 0xcc, 0xe9, 0x84, 0x95, 0x1c, 0xfc, 0x0a, 0x3d, 0xcc, 0x76, + 0xc5, 0x94, 0xe6, 0x29, 0x45, 0x2c, 0xfe, 0xe7, 0x94, 0xff, 0x34, 0xa0, 0x83, 0x10, 0xbe, 0x64, + 0x34, 0x61, 0x92, 0xf8, 0xe0, 0x2e, 0x2f, 0x56, 0x29, 0x92, 0x53, 0xe8, 0x2a, 0x26, 0x39, 0x1d, + 0xf3, 0x7b, 0x96, 0x44, 0x8a, 0xdf, 0x33, 0x33, 0xc9, 0xfd, 0xb9, 0xfa, 0x86, 0xdf, 0xb3, 0x7c, + 0xd2, 0x23, 0xc6, 0x6f, 0x47, 0xda, 0x24, 0x33, 0x12, 0x79, 0x01, 0x07, 0x53, 0xc9, 0x66, 0x5c, + 0x64, 0x6a, 0x3e, 0x56, 0x67, 0xa5, 0xaf, 0x6e, 0xe9, 0x52, 0xce, 0xf5, 0x09, 0x38, 0x8a, 0xb1, + 0xc4, 0x6f, 0xae, 0xce, 0x27, 0xd7, 0x92, 0x8f, 0x60, 0x4f, 0xf3, 0x09, 0x53, 0x9a, 0x4e, 0xa6, + 0xd1, 0x44, 0xf9, 0x2d, 0xcc, 0xd8, 0xa9, 0x74, 0xd7, 0x8a, 0x7c, 0x06, 0x07, 0x5a, 0xd2, 0x54, + 0xd1, 0x38, 0x6f, 0x58, 0x45, 0x52, 0x08, 0xed, 0xbb, 0x2b, 0xd1, 0x7a, 0x8b, 0x2e, 0xa1, 0x10, + 0x9a, 0x3c, 0x83, 0x0e, 0xae, 0xae, 0xf9, 0xc1, 0x5b, 0xf9, 0x01, 0x0a, 0x23, 0xba, 0x1e, 0x41, + 0x33, 0x15, 0x69, 0xcc, 0xfc, 0x36, 0x66, 0x2f, 0x84, 0x9c, 0x86, 0x43, 0xae, 0x95, 0x0f, 0xa8, + 0xc4, 0xef, 0xe0, 0x2f, 0x0b, 0xbc, 0x9f, 0xef, 0x1e, 0x0e, 0xea, 0x53, 0x00, 0xc9, 0x54, 0x36, + 0xce, 0xd9, 0xa7, 0x7c, 0xfb, 0xd8, 0x5e, 0xaa, 0xb1, 0x5d, 0xd8, 0x06, 0x89, 0xaa, 0xb6, 0xdc, + 0xa9, 0xdb, 0x72, 0xf2, 0x31, 0x78, 0xec, 0x4e, 0x47, 0x23, 0xaa, 0x46, 0x6b, 0x38, 0xbb, 0xec, + 0x4e, 0xe7, 0x1f, 0xc1, 0xbf, 0x16, 0xd8, 0xd7, 0xd9, 0x1d, 0x79, 0x06, 0xae, 0x42, 0x36, 0x28, + 0xdf, 0xc2, 0x84, 0xb8, 0x76, 0x0b, 0x2c, 0x09, 0x4b, 0x3b, 0x39, 0x01, 0xf7, 0x2d, 0x54, 0x2c, + 0x6d, 0x4b, 0xe9, 0xed, 0x0d, 0xe9, 0xc9, 0x77, 0x70, 0xf4, 0x1b, 0xd7, 0x29, 0x53, 0x2a, 0x4a, + 0xe6, 0xf4, 0x50, 0xbe, 0x83, 0x35, 0x1c, 0x55, 0x35, 0x2c, 0x70, 0x27, 0x3c, 0x34, 0x7f, 0x2c, + 0xe8, 0x14, 0xf9, 0x14, 0x0e, 0xca, 0x40, 0x54, 0xde, 0x66, 0x13, 0x96, 0x6a, 0xe5, 0x37, 0x8f, + 0xed, 0xa7, 0x7b, 0x61, 0xcf, 0x18, 0x2e, 0x4b, 0x7d, 0xf0, 0xb7, 0x05, 0xcd, 0xef, 0x71, 0x9c, + 0x0b, 0xbd, 0x58, 0x5b, 0xf6, 0xd2, 0xd8, 0xd4, 0x4b, 0x6d, 0x09, 0x76, 0x7d, 0x09, 0xe4, 0x0b, + 0x38, 0xac, 0x9c, 0xd3, 0x78, 0x24, 0x24, 0x4b, 0xea, 0x88, 0x53, 0x46, 0xbc, 0x34, 0x3e, 0x83, + 0x24, 0xf8, 0x09, 0xbc, 0xaf, 0x05, 0x4f, 0x87, 0x54, 0x31, 0xf2, 0xcd, 0x3c, 0xca, 0x02, 0x7c, + 0xa6, 0x95, 0x7a, 0xf4, 0xc8, 0x3a, 0x7a, 0xc1, 0x1b, 0x0b, 0x5a, 0x3f, 0x64, 0x7a, 0x9a, 0x69, + 0x72, 0x0a, 0xad, 0x62, 0xce, 0x26, 0xc8, 0xda, 0x1a, 0x18, 0x33, 0x79, 0x01, 0xdd, 0x58, 0xa4, + 0x5a, 0x8a, 0xf1, 0xdb, 0x0e, 0xe6, 0x7d, 0xe3, 0xb3, 0xd5, 0xb9, 0xbc, 0x04, 0xb3, 0xb3, 0x09, + 0x66, 0x1f, 0x5c, 0x21, 0x13, 0x9e, 0xd2, 0x31, 0x6e, 0xb5, 0x13, 0x96, 0x62, 0xf0, 0xbb, 0x05, + 0x10, 0x32, 0xcd, 0x25, 0xcb, 0x31, 0xde, 0xbe, 0x95, 0xb2, 0xa8, 0xc6, 0x3b, 0x8b, 0xb2, 0xb7, + 0x28, 0xca, 0x59, 0x2e, 0xea, 0x4f, 0x1b, 0xbc, 0x81, 0xb9, 0x9d, 0xc8, 0x09, 0xb4, 0x8b, 0x69, + 0xd7, 0xdd, 0x7d, 0x5e, 0x61, 0x1a, 0x24, 0xdb, 0xde, 0x00, 0x0f, 0x00, 0xe6, 0x86, 0x05, 0x6a, + 0xee, 0xb6, 0x40, 0xe4, 0x1a, 0xfc, 0x6a, 0x9b, 0xf1, 0xd9, 0x90, 0x54, 0xd7, 0x3e, 0x1e, 0xde, + 0x9d, 0xfe, 0x61, 0xd5, 0xc3, 0xfc, 0x45, 0x10, 0x3e, 0x2a, 0xb7, 0x7b, 0xe5, 0xa5, 0x50, 0xcb, + 0x24, 0x77, 0x37, 0x26, 0x79, 0xef, 0x64, 0xd2, 0xe2, 0xd0, 0xda, 0xcb, 0x43, 0x7b, 0xd3, 0x80, + 0xe6, 0xcd, 0x94, 0xa5, 0x09, 0x39, 0x87, 0xae, 0x9a, 0xb2, 0x54, 0x47, 0x02, 0xf9, 0x51, 0x37, + 0xb7, 0x0f, 0xd0, 0xa1, 0xe0, 0x4f, 0x71, 0xb5, 0xbd, 0xef, 0x36, 0x6d, 0x98, 0x8a, 0xb3, 0xe3, + 0x54, 0x76, 0x39, 0x13, 0x37, 0xc1, 0xd8, 0xda, 0x09, 0x46, 0x77, 0x09, 0xc6, 0x61, 0x0b, 0x1f, + 0xcc, 0x17, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x0f, 0x45, 0xae, 0x3c, 0x0b, 0x00, 0x00, } diff --git a/protocol/bc/bc.proto b/protocol/bc/bc.proto index b4225514..9261066d 100644 --- a/protocol/bc/bc.proto +++ b/protocol/bc/bc.proto @@ -67,9 +67,7 @@ message TxHeader { uint64 serialized_size = 2; repeated Hash result_ids = 3; Hash data = 4; - uint64 min_time_ms. = 5; - uint64 max_time_ms. = 6; - Hash ext_hash = 7; + Hash ext_hash = 5; } message Mux { diff --git a/protocol/bc/bctest/tx.go b/protocol/bc/bctest/tx.go index c85148ec..948d8367 100644 --- a/protocol/bc/bctest/tx.go +++ b/protocol/bc/bctest/tx.go @@ -5,7 +5,6 @@ package bctest import ( "crypto/rand" "testing" - "time" "golang.org/x/crypto/sha3" @@ -51,8 +50,6 @@ func NewIssuanceTx(tb testing.TB, initial bc.Hash, opts ...func(*legacy.Tx)) *le tx := legacy.NewTx(legacy.TxData{ Version: 1, - MinTime: bc.Millis(time.Now().Add(-5 * time.Minute)), - MaxTime: bc.Millis(time.Now().Add(5 * time.Minute)), Inputs: []*legacy.TxInput{txin}, Outputs: []*legacy.TxOutput{ legacy.NewTxOutput(txin.AssetID(), 100, []byte{0xbe, 0xef}, nil), diff --git a/protocol/bc/doc.go b/protocol/bc/doc.go deleted file mode 100644 index cd478098..00000000 --- a/protocol/bc/doc.go +++ /dev/null @@ -1,22 +0,0 @@ -/* -Package bc provides the fundamental blockchain data structures used in -the Chain Protocol. - -This package is in transition from a set of "old" data structures -(TxData, TxInput, TxOutput, etc.) to a new data model based on -"entries," each with a specific type (such as spend, issuance, output, -etc.), and each with its own distinct hash. The hash of a designated -"header" entry serves as the hash of the entire transaction. The -rationale for this change is that it is considerably more extensible, -and it allows future scripting tools to traverse and access -transaction data by making all components hash-addressable. - -Hashing and validation (of the old types) are redefined to mean -"convert to the new data structures and hash/validate that." - -Soon the old structures will be retired entirely. - -These changes will be made in a compatible way; in particular, block -and transaction hashes will not change. -*/ -package bc diff --git a/protocol/bc/entry.go b/protocol/bc/entry.go index 3b736859..525ac0f8 100644 --- a/protocol/bc/entry.go +++ b/protocol/bc/entry.go @@ -72,8 +72,7 @@ var byte32zero [32]byte // hash-serialization formats are not specified. It MUST NOT produce // errors in other cases. func mustWriteForHash(w io.Writer, c interface{}) { - err := writeForHash(w, c) - if err != nil { + if err := writeForHash(w, c); err != nil { panic(err) } } @@ -129,8 +128,7 @@ func writeForHash(w io.Writer, c interface{}) error { return writeForHash(w, elem.Interface()) case reflect.Slice: l := v.Len() - _, err := blockchain.WriteVarint31(w, uint64(l)) - if err != nil { + if _, err := blockchain.WriteVarint31(w, uint64(l)); err != nil { return errors.Wrapf(err, "writing slice (len %d) for hash", l) } for i := 0; i < l; i++ { @@ -138,8 +136,7 @@ func writeForHash(w io.Writer, c interface{}) error { if !c.CanInterface() { return errInvalidValue } - err := writeForHash(w, c.Interface()) - if err != nil { + if err := writeForHash(w, c.Interface()); err != nil { return errors.Wrapf(err, "writing slice element %d for hash", i) } } @@ -152,8 +149,7 @@ func writeForHash(w io.Writer, c interface{}) error { if !c.CanInterface() { return errInvalidValue } - err := writeForHash(w, c.Interface()) - if err != nil { + if err := writeForHash(w, c.Interface()); err != nil { t := v.Type() f := t.Field(i) return errors.Wrapf(err, "writing struct field %d (%s.%s) for hash", i, t.Name(), f.Name) diff --git a/protocol/bc/entry_test.go b/protocol/bc/entry_test.go index e97f0948..053e6de6 100644 --- a/protocol/bc/entry_test.go +++ b/protocol/bc/entry_test.go @@ -3,7 +3,6 @@ package bc import ( "reflect" "testing" - "time" ) func BenchmarkEntryID(b *testing.B) { @@ -11,7 +10,7 @@ func BenchmarkEntryID(b *testing.B) { entries := []Entry{ NewIssuance(nil, &AssetAmount{}, &Hash{}, 0), - NewTxHeader(1, 1, nil, &Hash{}, uint64(time.Now().Unix()), uint64(time.Now().Unix())), + NewTxHeader(1, 1, nil, &Hash{}), m, NewNonce(&Program{Code: []byte{1}, VmVersion: 1}), NewOutput(&ValueSource{}, &Program{Code: []byte{1}, VmVersion: 1}, &Hash{}, 0), diff --git a/protocol/bc/hash.go b/protocol/bc/hash.go index c2df967c..ab3f8b08 100644 --- a/protocol/bc/hash.go +++ b/protocol/bc/hash.go @@ -64,8 +64,7 @@ func (h *Hash) UnmarshalJSON(b []byte) error { return nil } var s string - err := json.Unmarshal(b, &s) - if err != nil { + if err := json.Unmarshal(b, &s); err != nil { return err } return h.UnmarshalText([]byte(s)) diff --git a/protocol/bc/legacy/block.go b/protocol/bc/legacy/block.go index ebce09c6..62776102 100644 --- a/protocol/bc/legacy/block.go +++ b/protocol/bc/legacy/block.go @@ -12,13 +12,12 @@ import ( "github.com/bytom/errors" ) +// serflag variables, start with 1 const ( - SerBlockWitness = 1 - SerBlockTransactions = 2 - - SerBlockSigHash = 0 - SerBlockHeader = SerBlockWitness - SerBlockFull = SerBlockWitness | SerBlockTransactions + _ = iota + SerBlockHeader + SerBlockTransactions + SerBlockFull ) // Block describes a complete block, including its header @@ -117,6 +116,7 @@ func (b *Block) readFrom(r *blockchain.Reader) error { return nil } +// WriteTo will write block to input io.Writer func (b *Block) WriteTo(w io.Writer) (int64, error) { ew := errors.NewWriter(w) b.writeTo(ew, SerBlockFull) diff --git a/protocol/bc/legacy/block_commitment.go b/protocol/bc/legacy/block_commitment.go index 7ad0bcec..e7c23304 100644 --- a/protocol/bc/legacy/block_commitment.go +++ b/protocol/bc/legacy/block_commitment.go @@ -7,6 +7,7 @@ import ( "github.com/bytom/protocol/bc" ) +// BlockCommitment store the TransactionsMerkleRoot && AssetsMerkleRoot type BlockCommitment struct { // TransactionsMerkleRoot is the root hash of the Merkle binary hash // tree formed by the hashes of all transactions included in the @@ -20,14 +21,10 @@ type BlockCommitment struct { } func (bc *BlockCommitment) readFrom(r *blockchain.Reader) error { - _, err := bc.TransactionsMerkleRoot.ReadFrom(r) - if err != nil { - return err - } - _, err = bc.AssetsMerkleRoot.ReadFrom(r) - if err != nil { + if _, err := bc.TransactionsMerkleRoot.ReadFrom(r); err != nil { return err } + _, err := bc.AssetsMerkleRoot.ReadFrom(r) return err } diff --git a/protocol/bc/legacy/block_header.go b/protocol/bc/legacy/block_header.go index c0d5722b..f69995ad 100644 --- a/protocol/bc/legacy/block_header.go +++ b/protocol/bc/legacy/block_header.go @@ -67,8 +67,7 @@ func (bh *BlockHeader) Hash() bc.Hash { func (bh *BlockHeader) MarshalText() ([]byte, error) { buf := bufpool.Get() defer bufpool.Put(buf) - _, err := bh.WriteTo(buf) - if err != nil { + if _, err := bh.WriteTo(buf); err != nil { return nil, err } @@ -80,63 +79,48 @@ func (bh *BlockHeader) MarshalText() ([]byte, error) { // UnmarshalText fulfills the encoding.TextUnmarshaler interface. func (bh *BlockHeader) UnmarshalText(text []byte) error { decoded := make([]byte, hex.DecodedLen(len(text))) - _, err := hex.Decode(decoded, text) - if err != nil { + if _, err := hex.Decode(decoded, text); err != nil { return err } - _, err = bh.readFrom(blockchain.NewReader(decoded)) + _, err := bh.readFrom(blockchain.NewReader(decoded)) return err } -func (bh *BlockHeader) readFrom(r *blockchain.Reader) (uint8, error) { +func (bh *BlockHeader) readFrom(r *blockchain.Reader) (serflag uint8, err error) { var serflags [1]byte io.ReadFull(r, serflags[:]) - switch serflags[0] { - case SerBlockSigHash, SerBlockHeader, SerBlockFull: + serflag = serflags[0] + switch serflag { + case SerBlockHeader, SerBlockFull: default: return 0, fmt.Errorf("unsupported serialization flags 0x%x", serflags) } - var err error - - bh.Version, err = blockchain.ReadVarint63(r) - if err != nil { + if bh.Version, err = blockchain.ReadVarint63(r); err != nil { return 0, err } - - bh.Height, err = blockchain.ReadVarint63(r) - if err != nil { + if bh.Height, err = blockchain.ReadVarint63(r); err != nil { return 0, err } - if _, err = bh.PreviousBlockHash.ReadFrom(r); err != nil { return 0, err } - if _, err = bh.Seed.ReadFrom(r); err != nil { return 0, err } - - bh.TimestampMS, err = blockchain.ReadVarint63(r) - if err != nil { + if bh.TimestampMS, err = blockchain.ReadVarint63(r); err != nil { return 0, err } - if _, err = blockchain.ReadExtensibleString(r, bh.BlockCommitment.readFrom); err != nil { return 0, err } - - bh.Nonce, err = blockchain.ReadVarint63(r) - if err != nil { + if bh.Nonce, err = blockchain.ReadVarint63(r); err != nil { return 0, err } - - bh.Bits, err = blockchain.ReadVarint63(r) - if err != nil { + if bh.Bits, err = blockchain.ReadVarint63(r); err != nil { return 0, err } - - return serflags[0], nil + return } // WriteTo writes the block header to the input io.Writer diff --git a/protocol/bc/legacy/block_test.go b/protocol/bc/legacy/block_test.go index 85d91ccd..b971e47a 100644 --- a/protocol/bc/legacy/block_test.go +++ b/protocol/bc/legacy/block_test.go @@ -23,7 +23,7 @@ func TestMarshalBlock(t *testing.T) { Transactions: []*Tx{ NewTx(TxData{ Version: 1, - SerializedSize: uint64(46), + SerializedSize: uint64(43), Outputs: []*TxOutput{ NewTxOutput(bc.AssetID{}, 1, nil, nil), }, @@ -52,9 +52,6 @@ func TestMarshalBlock(t *testing.T) { "01" + // num transactions "07" + // tx 0, serialization flags "01" + // tx 0, tx version - "02" + // tx 0, common fields extensible length string - "00" + // tx 0, common fields mintime - "00" + // tx 0, common fields maxtime "00" + // tx 0, common witness extensible string length "00" + // tx 0, inputs count "01" + // tx 0, outputs count @@ -165,7 +162,7 @@ func TestSmallBlock(t *testing.T) { "00" + // nonce "00" + // bits "01" + // num transactions - "070102000000000000") // transaction + "070100000000") // transaction want, _ := hex.DecodeString(wantHex) if !bytes.Equal(got, want) { t.Errorf("small block bytes = %x want %x", got, want) diff --git a/protocol/bc/legacy/fuzz_test.go b/protocol/bc/legacy/fuzz_test.go index 4a056686..cc524091 100644 --- a/protocol/bc/legacy/fuzz_test.go +++ b/protocol/bc/legacy/fuzz_test.go @@ -3,7 +3,7 @@ package legacy import "testing" func TestFuzzUnknownAssetVersion(t *testing.T) { - const rawTx = `07010700f785c1f1b72b0001f1b72b0001012b00089def834ab929327f3f479177e2d8c293f2f7fc4f251db8547896c0eeafb984261a73767178584c246400b50150935a092ffad7ec9fbac4f4486db6c3b8cd5b9f51cf697248584dde286a722000012b766baa20627e83fdad13dd98436fa7cbdd1412d50ef65528edb7e2ed8f2675b2a0b209235151ad696c00c0030040b984261ad6e71876ec4c2464012b766baa209d44ee5b6ebf6c408772ead7713f1a66b9de7655ff452513487be1fb10de7d985151ad696c00c02a7b2274657374223a225175657279546573742e7465737442616c616e636551756572792e74657374227d` + const rawTx = `07010001f1b72b0001012b00089def834ab929327f3f479177e2d8c293f2f7fc4f251db8547896c0eeafb984261a73767178584c246400b50150935a092ffad7ec9fbac4f4486db6c3b8cd5b9f51cf697248584dde286a722000012b766baa20627e83fdad13dd98436fa7cbdd1412d50ef65528edb7e2ed8f2675b2a0b209235151ad696c00c0030040b984261ad6e71876ec4c2464012b766baa209d44ee5b6ebf6c408772ead7713f1a66b9de7655ff452513487be1fb10de7d985151ad696c00c02a7b2274657374223a225175657279546573742e7465737442616c616e636551756572792e74657374227d` var want Tx err := want.UnmarshalText([]byte(rawTx)) diff --git a/protocol/bc/legacy/map.go b/protocol/bc/legacy/map.go index 325387aa..0114d5c3 100644 --- a/protocol/bc/legacy/map.go +++ b/protocol/bc/legacy/map.go @@ -225,7 +225,7 @@ func mapTx(tx *TxData) (headerID bc.Hash, hdr *bc.TxHeader, entryMap map[bc.Hash } refdatahash := hashData(tx.ReferenceData) - h := bc.NewTxHeader(tx.Version, tx.SerializedSize, resultIDs, &refdatahash, tx.MinTime, tx.MaxTime) + h := bc.NewTxHeader(tx.Version, tx.SerializedSize, resultIDs, &refdatahash) headerID = addEntry(h) return headerID, h, entryMap diff --git a/protocol/bc/legacy/map_test.go b/protocol/bc/legacy/map_test.go index da342cc2..732d58ce 100644 --- a/protocol/bc/legacy/map_test.go +++ b/protocol/bc/legacy/map_test.go @@ -24,12 +24,6 @@ func TestMapTx(t *testing.T) { if header.SerializedSize != oldTx.SerializedSize { t.Errorf("header.SerializedSize is %d, expected %d", header.SerializedSize, oldTx.SerializedSize) } - if header.MinTimeMs != oldTx.MinTime { - t.Errorf("header.MinTimeMs is %d, expected %d", header.MinTimeMs, oldTx.MinTime) - } - if header.MaxTimeMs != oldTx.MaxTime { - t.Errorf("header.MaxTimeMs is %d, expected %d", header.MaxTimeMs, oldTx.MaxTime) - } if len(header.ResultIds) != len(oldOuts) { t.Errorf("header.ResultIds contains %d item(s), expected %d", len(header.ResultIds), len(oldOuts)) } diff --git a/protocol/bc/legacy/output_commitment.go b/protocol/bc/legacy/output_commitment.go index c3707f0e..7ba0a3c6 100644 --- a/protocol/bc/legacy/output_commitment.go +++ b/protocol/bc/legacy/output_commitment.go @@ -27,22 +27,18 @@ func (oc *OutputCommitment) writeExtensibleString(w io.Writer, suffix []byte, as func (oc *OutputCommitment) writeContents(w io.Writer, suffix []byte, assetVersion uint64) (err error) { if assetVersion == 1 { - _, err = oc.AssetAmount.WriteTo(w) - if err != nil { + if _, err = oc.AssetAmount.WriteTo(w); err != nil { return errors.Wrap(err, "writing asset amount") } - _, err = blockchain.WriteVarint63(w, oc.VMVersion) - if err != nil { + if _, err = blockchain.WriteVarint63(w, oc.VMVersion); err != nil { return errors.Wrap(err, "writing vm version") } - _, err = blockchain.WriteVarstr31(w, oc.ControlProgram) - if err != nil { + if _, err = blockchain.WriteVarstr31(w, oc.ControlProgram); err != nil { return errors.Wrap(err, "writing control program") } } if len(suffix) > 0 { - _, err = w.Write(suffix) - if err != nil { + if _, err = w.Write(suffix); err != nil { return errors.Wrap(err, "writing suffix") } } @@ -52,8 +48,7 @@ func (oc *OutputCommitment) writeContents(w io.Writer, suffix []byte, assetVersi func (oc *OutputCommitment) readFrom(r *blockchain.Reader, assetVersion uint64) (suffix []byte, err error) { return blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error { if assetVersion == 1 { - err := oc.AssetAmount.ReadFrom(r) - if err != nil { + if err := oc.AssetAmount.ReadFrom(r); err != nil { return errors.Wrap(err, "reading asset+amount") } oc.VMVersion, err = blockchain.ReadVarint63(r) @@ -70,6 +65,7 @@ func (oc *OutputCommitment) readFrom(r *blockchain.Reader, assetVersion uint64) }) } +// Hash convert suffix && assetVersion to bc.Hash func (oc *OutputCommitment) Hash(suffix []byte, assetVersion uint64) (outputhash bc.Hash) { h := sha3pool.Get256() defer sha3pool.Put256(h) diff --git a/protocol/bc/legacy/transaction.go b/protocol/bc/legacy/transaction.go index f14a24b1..25ec6bfd 100644 --- a/protocol/bc/legacy/transaction.go +++ b/protocol/bc/legacy/transaction.go @@ -85,10 +85,6 @@ type TxData struct { Inputs []*TxInput Outputs []*TxOutput - // Common fields - MinTime uint64 - MaxTime uint64 - // The unconsumed suffix of the common fields extensible string CommonFieldsSuffix []byte @@ -115,14 +111,12 @@ func (tx *TxData) IsCoinbase() bool { func (tx *TxData) UnmarshalText(p []byte) error { b := make([]byte, hex.DecodedLen(len(p))) - _, err := hex.Decode(b, p) - if err != nil { + if _, err := hex.Decode(b, p); err != nil { return err } r := blockchain.NewReader(b) - err = tx.readFrom(r) - if err != nil { + if err := tx.readFrom(r); err != nil { return err } if trailing := r.Len(); trailing > 0 { @@ -131,13 +125,12 @@ func (tx *TxData) UnmarshalText(p []byte) error { return nil } -func (tx *TxData) readFrom(r *blockchain.Reader) error { +func (tx *TxData) readFrom(r *blockchain.Reader) (err error) { var serflags [1]byte - _, err := io.ReadFull(r, serflags[:]) - if err != nil { + if _, err = io.ReadFull(r, serflags[:]); err != nil { return errors.Wrap(err, "reading serialization flags") } - if err == nil && serflags[0] != serRequired { + if serflags[0] != serRequired { return fmt.Errorf("unsupported serflags %#x", serflags[0]) } @@ -148,19 +141,6 @@ func (tx *TxData) readFrom(r *blockchain.Reader) error { tx.SerializedSize = uint64(r.Len()) - // Common fields - tx.CommonFieldsSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error { - tx.MinTime, err = blockchain.ReadVarint63(r) - if err != nil { - return errors.Wrap(err, "reading transaction mintime") - } - tx.MaxTime, err = blockchain.ReadVarint63(r) - return errors.Wrap(err, "reading transaction maxtime") - }) - if err != nil { - return errors.Wrap(err, "reading transaction common fields") - } - // Common witness tx.CommonWitnessSuffix, err = blockchain.ReadExtensibleString(r, tx.readCommonWitness) if err != nil { @@ -173,8 +153,7 @@ func (tx *TxData) readFrom(r *blockchain.Reader) error { } for ; n > 0; n-- { ti := new(TxInput) - err = ti.readFrom(r) - if err != nil { + if err = ti.readFrom(r); err != nil { return errors.Wrapf(err, "reading input %d", len(tx.Inputs)) } tx.Inputs = append(tx.Inputs, ti) @@ -186,8 +165,7 @@ func (tx *TxData) readFrom(r *blockchain.Reader) error { } for ; n > 0; n-- { to := new(TxOutput) - err = to.readFrom(r, tx.Version) - if err != nil { + if err = to.readFrom(r, tx.Version); err != nil { return errors.Wrapf(err, "reading output %d", len(tx.Outputs)) } tx.Outputs = append(tx.Outputs, to) @@ -213,61 +191,40 @@ func (tx *TxData) MarshalText() ([]byte, error) { // WriteTo writes tx to w. func (tx *TxData) WriteTo(w io.Writer) (int64, error) { ew := errors.NewWriter(w) - err := tx.writeTo(ew, serRequired) - if err != nil { + if err := tx.writeTo(ew, serRequired); err != nil { return ew.Written(), ew.Err() } return ew.Written(), ew.Err() } func (tx *TxData) writeTo(w io.Writer, serflags byte) error { - _, err := w.Write([]byte{serflags}) - if err != nil { + if _, err := w.Write([]byte{serflags}); err != nil { return errors.Wrap(err, "writing serialization flags") } - _, err = blockchain.WriteVarint63(w, tx.Version) - if err != nil { + if _, err := blockchain.WriteVarint63(w, tx.Version); err != nil { return errors.Wrap(err, "writing transaction version") } - // common fields - _, err = blockchain.WriteExtensibleString(w, tx.CommonFieldsSuffix, func(w io.Writer) error { - _, err := blockchain.WriteVarint63(w, tx.MinTime) - if err != nil { - return errors.Wrap(err, "writing transaction min time") - } - _, err = blockchain.WriteVarint63(w, tx.MaxTime) - return errors.Wrap(err, "writing transaction max time") - }) - if err != nil { - return errors.Wrap(err, "writing common fields") - } - // common witness - _, err = blockchain.WriteExtensibleString(w, tx.CommonWitnessSuffix, tx.writeCommonWitness) - if err != nil { + if _, err := blockchain.WriteExtensibleString(w, tx.CommonWitnessSuffix, tx.writeCommonWitness); err != nil { return errors.Wrap(err, "writing common witness") } - _, err = blockchain.WriteVarint31(w, uint64(len(tx.Inputs))) - if err != nil { + if _, err := blockchain.WriteVarint31(w, uint64(len(tx.Inputs))); err != nil { return errors.Wrap(err, "writing tx input count") } for i, ti := range tx.Inputs { - err = ti.writeTo(w, serflags) - if err != nil { + if err := ti.writeTo(w, serflags); err != nil { return errors.Wrapf(err, "writing tx input %d", i) } } - _, err = blockchain.WriteVarint31(w, uint64(len(tx.Outputs))) - if err != nil { + if _, err := blockchain.WriteVarint31(w, uint64(len(tx.Outputs))); err != nil { return errors.Wrap(err, "writing tx output count") } for i, to := range tx.Outputs { - err = to.writeTo(w, serflags) - if err != nil { + if err := to.writeTo(w, serflags); err != nil { return errors.Wrapf(err, "writing tx output %d", i) } } diff --git a/protocol/bc/legacy/transaction_test.go b/protocol/bc/legacy/transaction_test.go index 94b8c181..9f0aa530 100644 --- a/protocol/bc/legacy/transaction_test.go +++ b/protocol/bc/legacy/transaction_test.go @@ -16,7 +16,7 @@ import ( ) func TestTransactionTrailingGarbage(t *testing.T) { - const validTxHex = `07010700d0929893b92b00000101270eac870dfde1e0feaa4fac6693dee38da2afe7f5cc83ce2b024f04a2400fd6e20a0104deadbeef027b7d0000` + const validTxHex = `070100000101270eac870dfde1e0feaa4fac6693dee38da2afe7f5cc83ce2b024f04a2400fd6e20a0104deadbeef027b7d0000` var validTx Tx err := validTx.UnmarshalText([]byte(validTxHex)) @@ -47,43 +47,33 @@ func TestTransaction(t *testing.T) { { tx: NewTx(TxData{ Version: 1, - SerializedSize: uint64(7), + SerializedSize: uint64(4), Inputs: nil, Outputs: nil, - MinTime: 0, - MaxTime: 0, ReferenceData: nil, }), hex: ("07" + // serflags "01" + // transaction version - "02" + // common fields extensible string length - "00" + // common fields, mintime - "00" + // common fields, maxtime "00" + // common witness extensible string length "00" + // inputs count "00" + // outputs count "00"), // reference data - hash: mustDecodeHash("7ae6eef6b02fe61d35cc185405aec5f690ccb0ac291ecd6214445a1dff8fc9fd"), + hash: mustDecodeHash("3629cd98d6707aab6055e125ea16be6a4e8e8c60aa195126ab7ee0cc246ab430"), }, { tx: NewTx(TxData{ Version: 1, - SerializedSize: uint64(159), + SerializedSize: uint64(156), Inputs: []*TxInput{ NewIssuanceInput([]byte{10, 9, 8}, 1000000000000, []byte("input"), initialBlockHash, issuanceScript, [][]byte{[]byte{1, 2, 3}}, nil), }, Outputs: []*TxOutput{ NewTxOutput(bc.AssetID{}, 1000000000000, []byte{1}, []byte("output")), }, - MinTime: 0, - MaxTime: 0, ReferenceData: []byte("issuance"), }), hex: ("07" + // serflags "01" + // transaction version - "02" + // common fields extensible string length - "00" + // common fields, mintime - "00" + // common fields, maxtime "00" + // common witness extensible string length "01" + // inputs count "01" + // input 0, asset version @@ -113,12 +103,12 @@ func TestTransaction(t *testing.T) { "066f7574707574" + // output 0, reference data "00" + // output 0, output witness "0869737375616e6365"), // reference data - hash: mustDecodeHash("515774561625cfe07629e49d4cf938d641aeb62af58e1b3ae2c582fee41dc628"), + hash: mustDecodeHash("191646bc995127b369a1459a58759368d8f1b95adfda87c9c858165a49f67659"), }, { tx: NewTx(TxData{ Version: 1, - SerializedSize: uint64(235), + SerializedSize: uint64(224), Inputs: []*TxInput{ NewSpendInput(nil, mustDecodeHash("dd385f6fe25d91d8c1bd0fa58951ad56b0c5229dcc01f61d9f9e8b9eb92d3292"), bc.AssetID{}, 1000000000000, 1, []byte{1}, bc.Hash{}, []byte("input")), }, @@ -126,16 +116,10 @@ func TestTransaction(t *testing.T) { NewTxOutput(assetID, 600000000000, []byte{1}, nil), NewTxOutput(assetID, 400000000000, []byte{2}, nil), }, - MinTime: 1492590000, - MaxTime: 1492590591, ReferenceData: []byte("distribution"), }), hex: ("07" + // serflags "01" + // transaction version - "0a" + // common fields extensible string length - - "b0bbdcc705" + // common fields, mintime - "ffbfdcc705" + // common fields, maxtime "00" + // common witness extensible string length "01" + // inputs count "01" + // input 0, asset version @@ -170,47 +154,8 @@ func TestTransaction(t *testing.T) { "00" + // output 1, reference data "00" + // output 1, output witness "0c646973747269627574696f6e"), // reference data - hash: mustDecodeHash("c328ad4278045b4c50e8af7e7d0df198e7d9436d2b5de35df1339f13a1192331"), + hash: mustDecodeHash("52611aef626501b815b0d80312fba172a5055b06458bbe32b980fd5e41ba8f46"), }, - - //07 - //01 - //0a - //b0bbdcc705 - //ffbfdcc705 - //00 - //01 - //01 - //4b - //01 - //dd385f6fe25d91d8c1bd0fa58951ad56b0c5229dcc01f61d9f9e8b9eb92d3292 - //29 - //0000000000000000000000000000000000000000000000000000000000000000 - //80a094a58d1d - //01 - //0101 - //05696e707574 - //01 - //00 - //02 - //01 - //29 - //a9b2b6c5394888ab5396f583ae484b8459486b14268e2bef1b637440335eb6c1 - //80e0a596bb11 - //01 - //0101 - //00 - //00 - //01 - //29 - //a9b2b6c5394888ab5396f583ae484b8459486b14268e2bef1b637440335eb6c1 - //80c0ee8ed20b - //01 - //0102 - //00 - //00 - //0c646973747269627574696f6e - } for i, test := range cases { got := serialize(t, test.tx) @@ -321,9 +266,6 @@ func TestIsCoinbase(t *testing.T) { func TestInvalidIssuance(t *testing.T) { hex := ("07" + // serflags "01" + // transaction version - "02" + // common fields extensible string length - "00" + // common fields, mintime - "00" + // common fields, maxtime "00" + // common witness extensible string length "01" + // inputs count "01" + // input 0, asset version diff --git a/protocol/bc/legacy/tx_test.go b/protocol/bc/legacy/tx_test.go index 981929e9..430e2d74 100644 --- a/protocol/bc/legacy/tx_test.go +++ b/protocol/bc/legacy/tx_test.go @@ -15,11 +15,11 @@ func TestTxHashes(t *testing.T) { }{ { txdata: &TxData{}, - hash: mustDecodeHash("e367a95b0f1dafdedd86f633456c81ef6bd4f2623f0890d56417f73a18a67297"), + hash: mustDecodeHash("02439cf4a8d801d10e84f5b3818226e38dac889dc626b7a1b5888b49510b38fe"), }, { txdata: sampleTx(), - hash: mustDecodeHash("9fad4f5024412d99d17508ef3cc66f81f1e09914a71b2641683acca87081c098"), // todo: verify this value, + hash: mustDecodeHash("360eab1b2563e85d9a3f290f3f2c0d99c622c89088f8c2e2003000fbee62cca0"), // todo: verify this value, }, } @@ -62,8 +62,6 @@ func sampleTx() *TxData { NewTxOutput(assetID, 600000000000, []byte{1}, nil), NewTxOutput(assetID, 400000000000, []byte{2}, nil), }, - MinTime: 1492590000, - MaxTime: 1492590591, ReferenceData: []byte("distribution"), } } diff --git a/protocol/bc/legacy/txinput.go b/protocol/bc/legacy/txinput.go index fbab4d67..9520d4ad 100644 --- a/protocol/bc/legacy/txinput.go +++ b/protocol/bc/legacy/txinput.go @@ -106,8 +106,7 @@ func (t *TxInput) readFrom(r *blockchain.Reader) (err error) { return nil } var icType [1]byte - _, err = io.ReadFull(r, icType[:]) - if err != nil { + if _, err = io.ReadFull(r, icType[:]); err != nil { return errors.Wrap(err, "reading input commitment type") } switch icType[0] { @@ -118,8 +117,7 @@ func (t *TxInput) readFrom(r *blockchain.Reader) (err error) { if err != nil { return err } - _, err = assetID.ReadFrom(r) - if err != nil { + if _, err = assetID.ReadFrom(r); err != nil { return err } ii.Amount, err = blockchain.ReadVarint63(r) @@ -156,8 +154,7 @@ func (t *TxInput) readFrom(r *blockchain.Reader) (err error) { if ii != nil { // read IssuanceInput witness - _, err = ii.InitialBlock.ReadFrom(r) - if err != nil { + if _, err = ii.InitialBlock.ReadFrom(r); err != nil { return err } @@ -203,12 +200,11 @@ func (t *TxInput) readFrom(r *blockchain.Reader) (err error) { } func (t *TxInput) writeTo(w io.Writer, serflags uint8) error { - _, err := blockchain.WriteVarint63(w, t.AssetVersion) - if err != nil { + if _, err := blockchain.WriteVarint63(w, t.AssetVersion); err != nil { return errors.Wrap(err, "writing asset version") } - _, err = blockchain.WriteExtensibleString(w, t.CommitmentSuffix, func(w io.Writer) error { + _, err := blockchain.WriteExtensibleString(w, t.CommitmentSuffix, func(w io.Writer) error { return t.WriteInputCommitment(w, serflags) }) @@ -216,14 +212,12 @@ func (t *TxInput) writeTo(w io.Writer, serflags uint8) error { return errors.Wrap(err, "writing input commitment") } - _, err = blockchain.WriteVarstr31(w, t.ReferenceData) - if err != nil { + if _, err = blockchain.WriteVarstr31(w, t.ReferenceData); err != nil { return errors.Wrap(err, "writing reference data") } if serflags&SerWitness != 0 { - _, err = blockchain.WriteExtensibleString(w, t.WitnessSuffix, t.writeInputWitness) - if err != nil { + if _, err = blockchain.WriteExtensibleString(w, t.WitnessSuffix, t.writeInputWitness); err != nil { return errors.Wrap(err, "writing input witness") } } @@ -231,31 +225,27 @@ func (t *TxInput) writeTo(w io.Writer, serflags uint8) error { return nil } -func (t *TxInput) WriteInputCommitment(w io.Writer, serflags uint8) error { +func (t *TxInput) WriteInputCommitment(w io.Writer, serflags uint8) (err error) { if t.AssetVersion != 1 { return nil } switch inp := t.TypedInput.(type) { case *IssuanceInput: - _, err := w.Write([]byte{0}) // issuance type - if err != nil { + if _, err = w.Write([]byte{0}); err != nil { return err } - _, err = blockchain.WriteVarstr31(w, inp.Nonce) - if err != nil { + if _, err = blockchain.WriteVarstr31(w, inp.Nonce); err != nil { return err } assetID := t.AssetID() - _, err = assetID.WriteTo(w) - if err != nil { + if _, err = assetID.WriteTo(w); err != nil { return err } _, err = blockchain.WriteVarint63(w, inp.Amount) return err case *SpendInput: - _, err := w.Write([]byte{1}) // spend type - if err != nil { + if _, err = w.Write([]byte{1}); err != nil { return err } if serflags&SerPrevout != 0 { diff --git a/protocol/bc/legacy/txoutput.go b/protocol/bc/legacy/txoutput.go index 4be9078c..81d8ae1a 100644 --- a/protocol/bc/legacy/txoutput.go +++ b/protocol/bc/legacy/txoutput.go @@ -60,24 +60,20 @@ func (to *TxOutput) readFrom(r *blockchain.Reader, txVersion uint64) (err error) } func (to *TxOutput) writeTo(w io.Writer, serflags byte) error { - _, err := blockchain.WriteVarint63(w, to.AssetVersion) - if err != nil { + if _, err := blockchain.WriteVarint63(w, to.AssetVersion); err != nil { return errors.Wrap(err, "writing asset version") } - err = to.WriteCommitment(w) - if err != nil { + if err := to.WriteCommitment(w); err != nil { return errors.Wrap(err, "writing output commitment") } - err = writeRefData(w, to.ReferenceData, serflags) - if err != nil { + if err := writeRefData(w, to.ReferenceData, serflags); err != nil { return errors.Wrap(err, "writing reference data") } // write witness (empty in v1) - _, err = blockchain.WriteVarstr31(w, nil) - if err != nil { + if _, err := blockchain.WriteVarstr31(w, nil); err != nil { return errors.Wrap(err, "writing witness") } return nil diff --git a/protocol/bc/merkle_test.go b/protocol/bc/merkle_test.go index 664f4550..c14ffcd4 100644 --- a/protocol/bc/merkle_test.go +++ b/protocol/bc/merkle_test.go @@ -20,7 +20,7 @@ func TestMerkleRoot(t *testing.T) { []byte("00000"), }, }, - want: mustDecodeHash("77eae4222f60bfd74c07994d700161d0b831ed723037952b9c7ee98ed8766977"), + want: mustDecodeHash("c1b9e0b7f761e15b590834a01fe7a10e602d9ee6a4c674392a2b106242f29e8b"), }, { witnesses: [][][]byte{ [][]byte{ @@ -32,7 +32,7 @@ func TestMerkleRoot(t *testing.T) { []byte("111111"), }, }, - want: mustDecodeHash("526737fcca853f5ad352081c5a7341aca4ee05b09a002c8600e26a06df02aa3b"), + want: mustDecodeHash("1719bd4ff9fdf4de4b8e403a9302c30162e61bec1e688c21173c7c2aec4792bb"), }, { witnesses: [][][]byte{ [][]byte{ @@ -45,7 +45,7 @@ func TestMerkleRoot(t *testing.T) { []byte("222222"), }, }, - want: mustDecodeHash("526737fcca853f5ad352081c5a7341aca4ee05b09a002c8600e26a06df02aa3b"), + want: mustDecodeHash("1719bd4ff9fdf4de4b8e403a9302c30162e61bec1e688c21173c7c2aec4792bb"), }} for _, c := range cases { diff --git a/protocol/bc/txheader.go b/protocol/bc/txheader.go index d893922e..723ac0b0 100644 --- a/protocol/bc/txheader.go +++ b/protocol/bc/txheader.go @@ -12,19 +12,15 @@ func (h *TxHeader) writeForHash(w io.Writer) { mustWriteForHash(w, h.Version) mustWriteForHash(w, h.ResultIds) mustWriteForHash(w, h.Data) - mustWriteForHash(w, h.MinTimeMs) - mustWriteForHash(w, h.MaxTimeMs) mustWriteForHash(w, h.ExtHash) } // NewTxHeader creates an new TxHeader. -func NewTxHeader(version, serializedSize uint64, resultIDs []*Hash, data *Hash, minTimeMS, maxTimeMS uint64) *TxHeader { +func NewTxHeader(version, serializedSize uint64, resultIDs []*Hash, data *Hash) *TxHeader { return &TxHeader{ Version: version, SerializedSize: serializedSize, ResultIds: resultIDs, Data: data, - MinTimeMs: minTimeMS, - MaxTimeMs: maxTimeMS, } } diff --git a/protocol/patricia/patricia.go b/protocol/patricia/patricia.go index 2e4c4342..1e0cba6c 100644 --- a/protocol/patricia/patricia.go +++ b/protocol/patricia/patricia.go @@ -56,12 +56,11 @@ func walk(n *node, walkFn WalkFunc) error { return walkFn(n.Key()) } - err := walk(n.children[0], walkFn) - if err != nil { + if err := walk(n.children[0], walkFn); err != nil { return err } - err = walk(n.children[1], walkFn) + err := walk(n.children[1], walkFn) return err } @@ -104,7 +103,7 @@ func lookup(n *node, key []uint8) *node { // 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) error { +func (t *Tree) Insert(item []byte) (err error) { key := bitKey(item) var hash bc.Hash @@ -119,7 +118,6 @@ func (t *Tree) Insert(item []byte) error { return nil } - var err error t.root, err = insert(t.root, key, &hash) return err } diff --git a/protocol/state/snapshot.go b/protocol/state/snapshot.go index b97d13fb..53caedcf 100644 --- a/protocol/state/snapshot.go +++ b/protocol/state/snapshot.go @@ -59,10 +59,8 @@ func Empty() *Snapshot { // ApplyBlock updates s in place. func (s *Snapshot) ApplyBlock(block *bc.Block) error { - s.PruneNonces(block.TimestampMs) for i, tx := range block.Transactions { - err := s.ApplyTx(tx) - if err != nil { + if err := s.ApplyTx(tx); err != nil { return errors.Wrapf(err, "applying block transaction %d", i) } } @@ -78,7 +76,7 @@ func (s *Snapshot) ApplyTx(tx *bc.Tx) error { return fmt.Errorf("conflicting nonce %x", n.Bytes()) } - s.Nonces[n] = tx.TxHeader.MaxTimeMs + s.Nonces[n] = 0 } // Remove spent outputs. Each output must be present. diff --git a/protocol/state/snapshot_test.go b/protocol/state/snapshot_test.go index b90c3d5c..e4b5f0e7 100644 --- a/protocol/state/snapshot_test.go +++ b/protocol/state/snapshot_test.go @@ -79,9 +79,7 @@ func TestCopySnapshot(t *testing.T) { func TestApplyBlock(t *testing.T) { // Setup a snapshot with a nonce with a known expiry. maxTime := bc.Millis(time.Now().Add(5 * time.Minute)) - issuance := bctest.NewIssuanceTx(t, bc.EmptyStringHash, func(tx *legacy.Tx) { - tx.MaxTime = maxTime - }) + issuance := bctest.NewIssuanceTx(t, bc.EmptyStringHash, func(tx *legacy.Tx) {}) snap := Empty() err := snap.ApplyTx(legacy.MapTx(&issuance.TxData)) if err != nil { @@ -101,7 +99,7 @@ func TestApplyBlock(t *testing.T) { if err != nil { t.Fatal(err) } - if n := len(snap.Nonces); n != 0 { - t.Errorf("got %d nonces, want 0", n) + if n := len(snap.Nonces); n != 1 { + t.Errorf("got %d nonces, want 1", n) } } diff --git a/protocol/tx.go b/protocol/tx.go index 588b1c03..2756134a 100644 --- a/protocol/tx.go +++ b/protocol/tx.go @@ -2,7 +2,6 @@ package protocol import ( "github.com/bytom/errors" - "github.com/bytom/protocol/bc" "github.com/bytom/protocol/bc/legacy" "github.com/bytom/protocol/validation" ) @@ -15,9 +14,6 @@ var ErrBadTx = errors.New("invalid transaction") // performing full validation. func (c *Chain) ValidateTx(tx *legacy.Tx) error { newTx := tx.Tx - if err := c.checkIssuanceWindow(newTx); err != nil { - return err - } if ok := c.txPool.HaveTransaction(&newTx.ID); ok { return c.txPool.GetErrCache(&newTx.ID) } @@ -37,17 +33,3 @@ func (c *Chain) ValidateTx(tx *legacy.Tx) error { c.txPool.AddTransaction(tx, block.BlockHeader.Height, fee) return errors.Sub(ErrBadTx, err) } - -func (c *Chain) checkIssuanceWindow(tx *bc.Tx) error { - if c.MaxIssuanceWindow == 0 { - return nil - } - for _, entryID := range tx.InputIDs { - if _, err := tx.Issuance(entryID); err == nil { - if tx.MinTimeMs+bc.DurationMillis(c.MaxIssuanceWindow) < tx.MaxTimeMs { - return errors.WithDetailf(ErrBadTx, "issuance input's time window is larger than the network maximum (%s)", c.MaxIssuanceWindow) - } - } - } - return nil -} diff --git a/protocol/tx_test.go b/protocol/tx_test.go index 803598da..e857a36f 100644 --- a/protocol/tx_test.go +++ b/protocol/tx_test.go @@ -3,7 +3,6 @@ package protocol import ( "fmt" "testing" - "time" "golang.org/x/crypto/sha3" @@ -92,8 +91,6 @@ func issue(t testing.TB, asset *testAsset, dest *testDest, amount uint64) (*lega Outputs: []*legacy.TxOutput{ legacy.NewTxOutput(asset.AssetID, amount, destCP, nil), }, - MinTime: bc.Millis(time.Now()), - MaxTime: bc.Millis(time.Now().Add(time.Hour)), }) asset.sign(t, tx, 0) diff --git a/protocol/validation/validation.go b/protocol/validation/validation.go index e0c854bc..fe60ffdb 100644 --- a/protocol/validation/validation.go +++ b/protocol/validation/validation.go @@ -530,12 +530,6 @@ func ValidateBlock(b, prev *bc.Block, seedCaches *seed.SeedCaches) error { if b.Version == 1 && tx.Version != 1 { return errors.WithDetailf(errTxVersion, "block version %d, transaction version %d", b.Version, tx.Version) } - if tx.MaxTimeMs > 0 && b.TimestampMs > tx.MaxTimeMs { - return errors.WithDetailf(errUntimelyTransaction, "block timestamp %d, transaction time range %d-%d", b.TimestampMs, tx.MinTimeMs, tx.MaxTimeMs) - } - if tx.MinTimeMs > 0 && b.TimestampMs > 0 && b.TimestampMs < tx.MinTimeMs { - return errors.WithDetailf(errUntimelyTransaction, "block timestamp %d, transaction time range %d-%d", b.TimestampMs, tx.MinTimeMs, tx.MaxTimeMs) - } txBTMValue, err := ValidateTx(tx, b) if err != nil { diff --git a/protocol/validation/validation_test.go b/protocol/validation/validation_test.go index 64197782..d62bc827 100644 --- a/protocol/validation/validation_test.go +++ b/protocol/validation/validation_test.go @@ -4,7 +4,6 @@ import ( "fmt" "math" "testing" - "time" "github.com/bytom/consensus" "github.com/bytom/crypto/sha3pool" @@ -547,17 +546,16 @@ func TestBlockHeaderValid(t *testing.T) { // affect the transaction that's built. The components of the // transaction are the fields of txFixture. type txFixture struct { - initialBlockID bc.Hash - issuanceProg bc.Program - issuanceArgs [][]byte - assetDef []byte - assetID bc.AssetID - txVersion uint64 - txInputs []*legacy.TxInput - txOutputs []*legacy.TxOutput - txMinTime, txMaxTime uint64 - txRefData []byte - tx *legacy.TxData + initialBlockID bc.Hash + issuanceProg bc.Program + issuanceArgs [][]byte + assetDef []byte + assetID bc.AssetID + txVersion uint64 + txInputs []*legacy.TxInput + txOutputs []*legacy.TxOutput + txRefData []byte + tx *legacy.TxData } // Produces a sample transaction in a txFixture object (see above). A @@ -646,12 +644,6 @@ func sample(tb testing.TB, in *txFixture) *txFixture { legacy.NewTxOutput(result.assetID, 45, cp2, []byte{12}), } } - if result.txMinTime == 0 { - result.txMinTime = bc.Millis(time.Now().Add(-time.Minute)) - } - if result.txMaxTime == 0 { - result.txMaxTime = bc.Millis(time.Now().Add(time.Minute)) - } if len(result.txRefData) == 0 { result.txRefData = []byte{13} } @@ -660,8 +652,6 @@ func sample(tb testing.TB, in *txFixture) *txFixture { Version: result.txVersion, Inputs: result.txInputs, Outputs: result.txOutputs, - MinTime: result.txMinTime, - MaxTime: result.txMaxTime, ReferenceData: result.txRefData, } diff --git a/protocol/validation/vmcontext.go b/protocol/validation/vmcontext.go index 580d0616..fc14d157 100644 --- a/protocol/validation/vmcontext.go +++ b/protocol/validation/vmcontext.go @@ -101,8 +101,6 @@ func NewTxVMContext(vs *validationState, entry bc.Entry, prog *bc.Program, args NumResults: &numResults, AssetID: assetID, Amount: amount, - MinTimeMS: &tx.MinTimeMs, - MaxTimeMS: &tx.MaxTimeMs, EntryData: entryData, TxData: &txData, DestPos: destPos, diff --git a/protocol/validation/vmcontext_test.go b/protocol/validation/vmcontext_test.go index 2b34604a..b31471bc 100644 --- a/protocol/validation/vmcontext_test.go +++ b/protocol/validation/vmcontext_test.go @@ -25,8 +25,6 @@ func TestCheckOutput(t *testing.T) { legacy.NewTxOutput(bc.NewAssetID([32]byte{2}), 7, []byte("controlprog"), nil), legacy.NewTxOutput(bc.NewAssetID([32]byte{2}), 7, []byte("controlprog"), []byte("outref")), }, - MinTime: 0, - MaxTime: 20, }) txCtx := &entryContext{ diff --git a/protocol/vm/context.go b/protocol/vm/context.go index 5574a117..12ddfae4 100644 --- a/protocol/vm/context.go +++ b/protocol/vm/context.go @@ -27,8 +27,6 @@ type Context struct { NumResults *uint64 AssetID *[]byte Amount *uint64 - MinTimeMS *uint64 - MaxTimeMS *uint64 EntryData *[]byte TxData *[]byte DestPos *uint64 diff --git a/protocol/vm/introspection.go b/protocol/vm/introspection.go index e694b1ea..5712756e 100644 --- a/protocol/vm/introspection.go +++ b/protocol/vm/introspection.go @@ -1,7 +1,5 @@ package vm -import "math" - func opCheckOutput(vm *virtualMachine) error { err := vm.applyCost(16) if err != nil { @@ -86,35 +84,6 @@ func opProgram(vm *virtualMachine) error { return vm.push(vm.context.Code, true) } -func opMinTime(vm *virtualMachine) error { - err := vm.applyCost(1) - if err != nil { - return err - } - - if vm.context.MinTimeMS == nil { - return ErrContext - } - return vm.pushInt64(int64(*vm.context.MinTimeMS), true) -} - -func opMaxTime(vm *virtualMachine) error { - err := vm.applyCost(1) - if err != nil { - return err - } - - if vm.context.MaxTimeMS == nil { - return ErrContext - } - maxTimeMS := *vm.context.MaxTimeMS - if maxTimeMS == 0 || maxTimeMS > math.MaxInt64 { - maxTimeMS = uint64(math.MaxInt64) - } - - return vm.pushInt64(int64(maxTimeMS), true) -} - func opEntryData(vm *virtualMachine) error { err := vm.applyCost(1) if err != nil { diff --git a/protocol/vm/introspection_test.go b/protocol/vm/introspection_test.go index 283ff331..86493f97 100644 --- a/protocol/vm/introspection_test.go +++ b/protocol/vm/introspection_test.go @@ -268,26 +268,6 @@ func TestIntrospectionOps(t *testing.T) { dataStack: [][]byte{[]byte("issueprog")}, }, }, { - op: OP_MINTIME, - startVM: &virtualMachine{ - context: &Context{MinTimeMS: new(uint64)}, - }, - wantVM: &virtualMachine{ - runLimit: 49991, - deferredCost: 8, - dataStack: [][]byte{[]byte{}}, - }, - }, { - op: OP_MAXTIME, - startVM: &virtualMachine{ - context: &Context{MaxTimeMS: uint64ptr(20)}, - }, - wantVM: &virtualMachine{ - runLimit: 49990, - deferredCost: 9, - dataStack: [][]byte{{20}}, - }, - }, { op: OP_TXDATA, startVM: &virtualMachine{ context: &Context{TxData: &txData}, @@ -331,8 +311,7 @@ func TestIntrospectionOps(t *testing.T) { txops := []Op{ OP_CHECKOUTPUT, OP_ASSET, OP_AMOUNT, OP_PROGRAM, - OP_MINTIME, OP_MAXTIME, OP_TXDATA, OP_ENTRYDATA, - OP_INDEX, OP_OUTPUTID, + OP_TXDATA, OP_ENTRYDATA, OP_INDEX, OP_OUTPUTID, } for _, op := range txops { diff --git a/protocol/vm/ops.go b/protocol/vm/ops.go index 2fe0f313..0e6ed6d3 100644 --- a/protocol/vm/ops.go +++ b/protocol/vm/ops.go @@ -204,8 +204,6 @@ const ( OP_ASSET Op = 0xc2 OP_AMOUNT Op = 0xc3 OP_PROGRAM Op = 0xc4 - OP_MINTIME Op = 0xc5 - OP_MAXTIME Op = 0xc6 OP_TXDATA Op = 0xc7 OP_ENTRYDATA Op = 0xc8 OP_INDEX Op = 0xc9 @@ -314,8 +312,6 @@ var ( OP_ASSET: {OP_ASSET, "ASSET", opAsset}, OP_AMOUNT: {OP_AMOUNT, "AMOUNT", opAmount}, OP_PROGRAM: {OP_PROGRAM, "PROGRAM", opProgram}, - OP_MINTIME: {OP_MINTIME, "MINTIME", opMinTime}, - OP_MAXTIME: {OP_MAXTIME, "MAXTIME", opMaxTime}, OP_TXDATA: {OP_TXDATA, "TXDATA", opTxData}, OP_ENTRYDATA: {OP_ENTRYDATA, "ENTRYDATA", opEntryData}, OP_INDEX: {OP_INDEX, "INDEX", opIndex}, diff --git a/protocol/vm/vm_test.go b/protocol/vm/vm_test.go index f9ac2c18..942a2360 100644 --- a/protocol/vm/vm_test.go +++ b/protocol/vm/vm_test.go @@ -406,11 +406,6 @@ func TestVerifyTxInputQuickCheck(t *testing.T) { VMVersion: 1, Code: program, Arguments: witnesses, - - // Leaving this out reduces coverage. - // TODO(kr): figure out why and convert that - // to a normal unit test. - MaxTimeMS: new(uint64), } Verify(vctx, 10000) -- 2.11.0