diff --git a/Gopkg.lock b/Gopkg.lock index 0c65789c68d8b397a022d323da2413f848968e9b..99eff5eeb676cebb4462eedf0560de196c54c395 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -281,13 +281,7 @@ name = "github.com/tendermint/iavl" packages = ["."] revision = "594cc0c062a7174475f0ab654384038d77067917" - version = "v0.2.0" - -[[projects]] - name = "github.com/tendermint/merkleeyes" - packages = ["iavl"] - revision = "102aaf5a8ffda1846413fb22805a94def2045b9f" - version = "v0.2.4" + version = "v0.5.0" [[projects]] name = "github.com/tendermint/tendermint" @@ -462,8 +456,8 @@ [[projects]] name = "gopkg.in/go-playground/validator.v9" packages = ["."] - revision = "3620d3c0694119b61c72071ff5a05a976e97b05a" - version = "v9.9.4" + revision = "8ce234ff024d85b3848e485decba806385d6e276" + version = "v9.13.0" [[projects]] name = "gopkg.in/tylerb/graceful.v1" @@ -480,6 +474,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "305882cc35ee7345aa49517218d941e2892c5d418426973b9f9f73d9b8149330" + inputs-digest = "5db3ac43679cafd5f68104e93a248b62266d313aef2d8ba3bd7f3986a5a99834" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index b2922635322c2d74b742e007fa1a81b5f3b08aad..b1f9779a1f7fc121303e95d73ea11a438bde1664 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -1,14 +1,8 @@ -required = ["github.com/tendermint/iavl"] - [prune] go-tests = true unused-packages = true non-go = true -[[constraint]] - name = "github.com/tendermint/iavl" - version = "~0.2.0" - [[constraint]] name = "github.com/BurntSushi/toml" version = "0.3.0" @@ -73,6 +67,10 @@ required = ["github.com/tendermint/iavl"] name = "github.com/tendermint/go-wire" version = "~0.7.2" +[[constraint]] + name = "github.com/tendermint/iavl" + version = "~0.5.0" + [[constraint]] name = "github.com/tendermint/merkleeyes" version = "~0.2.4" diff --git a/Makefile b/Makefile index 7bda2257aea480aaa7195aa83c55f8628e6f9f5d..ba7048e3fe3d39db21a656fc2287e6eee95389e5 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ test: check .PHONY: test_integration test_integration: - @go get -u github.com/monax/bosmarmot/keys/cmd/monax-keys + @go get github.com/monax/bosmarmot/keys/cmd/monax-keys @go test ./keys/integration -tags integration @go test ./rpc/tm/integration -tags integration diff --git a/account/state_cache.go b/account/state_cache.go index 0ddbb3cfe82353293837f999fc6a2bbd458625d7..28b6d0c58affd8a2f3383847459985b0ebd2a241 100644 --- a/account/state_cache.go +++ b/account/state_cache.go @@ -95,6 +95,7 @@ func (cache *stateCache) RemoveAccount(address Address) error { return nil } +// Iterates over all accounts first in cache and then in backend until consumer returns true for 'stop' func (cache *stateCache) IterateAccounts(consumer func(Account) (stop bool)) (stopped bool, err error) { // Try cache first for early exit cache.RLock() @@ -147,7 +148,9 @@ func (cache *stateCache) SetStorage(address Address, key binary.Word256, value b return nil } -func (cache *stateCache) IterateStorage(address Address, consumer func(key, value binary.Word256) (stop bool)) (stopped bool, err error) { +// Iterates over all storage items first in cache and then in backend until consumer returns true for 'stop' +func (cache *stateCache) IterateStorage(address Address, + consumer func(key, value binary.Word256) (stop bool)) (stopped bool, err error) { accInfo, err := cache.get(address) if err != nil { return false, err diff --git a/account/state_cache_test.go b/account/state_cache_test.go index cc40d7e94834e28c401dec199f0a11974d9bcd9b..6ecc2cdd108b2e947b567943afdcf4ccdf40565d 100644 --- a/account/state_cache_test.go +++ b/account/state_cache_test.go @@ -8,6 +8,7 @@ import ( "github.com/hyperledger/burrow/binary" ) +// TODO: write tests as part of feature branch type testStateReader struct { Accounts map[Address]Account Storage map[Address]map[binary.Word256]binary.Word256 diff --git a/client/websocket_client.go b/client/websocket_client.go index e6b257707e47c517c893f0f0ec5212e59295b46d..41ba931c3f689a2ba2a7af65631ae047d9b56f68 100644 --- a/client/websocket_client.go +++ b/client/websocket_client.go @@ -164,10 +164,10 @@ func (burrowNodeWebsocketClient *burrowNodeWebsocketClient) WaitForConfirmation( return } - if !bytes.Equal(txs.TxHash(chainId, eventDataTx.Tx), txs.TxHash(chainId, tx)) { + if !bytes.Equal(eventDataTx.Tx.Hash(chainId), tx.Hash(chainId)) { logging.TraceMsg(burrowNodeWebsocketClient.logger, "Received different event", // TODO: consider re-implementing TxID again, or other more clear debug - "received transaction event", txs.TxHash(chainId, eventDataTx.Tx)) + "received transaction event", eventDataTx.Tx.Hash(chainId)) continue } diff --git a/consensus/tendermint/abci/app.go b/consensus/tendermint/abci/app.go index dd388df64fc9e3707d59b117b6ad105907d1f5c7..ee549575566b2420d0f53214d94f97d6a3ce63c5 100644 --- a/consensus/tendermint/abci/app.go +++ b/consensus/tendermint/abci/app.go @@ -74,6 +74,7 @@ func (app *abciApp) CheckTx(txBytes []byte) abci_types.ResponseCheckTx { tx, err := app.txDecoder.DecodeTx(txBytes) if err != nil { logging.TraceMsg(app.logger, "CheckTx decoding error", + "tag", "CheckTx", structure.ErrorKey, err) return abci_types.ResponseCheckTx{ Code: codes.EncodingErrorCode, @@ -87,6 +88,7 @@ func (app *abciApp) CheckTx(txBytes []byte) abci_types.ResponseCheckTx { if err != nil { logging.TraceMsg(app.logger, "CheckTx execution error", structure.ErrorKey, err, + "tag", "CheckTx", "tx_hash", receipt.TxHash, "creates_contract", receipt.CreatesContract) return abci_types.ResponseCheckTx{ @@ -97,6 +99,7 @@ func (app *abciApp) CheckTx(txBytes []byte) abci_types.ResponseCheckTx { receiptBytes := wire.BinaryBytes(receipt) logging.TraceMsg(app.logger, "CheckTx success", + "tag", "CheckTx", "tx_hash", receipt.TxHash, "creates_contract", receipt.CreatesContract) return abci_types.ResponseCheckTx{ @@ -122,6 +125,7 @@ func (app *abciApp) DeliverTx(txBytes []byte) abci_types.ResponseDeliverTx { tx, err := app.txDecoder.DecodeTx(txBytes) if err != nil { logging.TraceMsg(app.logger, "DeliverTx decoding error", + "tag", "DeliverTx", structure.ErrorKey, err) return abci_types.ResponseDeliverTx{ Code: codes.EncodingErrorCode, @@ -134,6 +138,7 @@ func (app *abciApp) DeliverTx(txBytes []byte) abci_types.ResponseDeliverTx { if err != nil { logging.TraceMsg(app.logger, "DeliverTx execution error", structure.ErrorKey, err, + "tag", "DeliverTx", "tx_hash", receipt.TxHash, "creates_contract", receipt.CreatesContract) return abci_types.ResponseDeliverTx{ @@ -143,6 +148,7 @@ func (app *abciApp) DeliverTx(txBytes []byte) abci_types.ResponseDeliverTx { } logging.TraceMsg(app.logger, "DeliverTx success", + "tag", "DeliverTx", "tx_hash", receipt.TxHash, "creates_contract", receipt.CreatesContract) receiptBytes := wire.BinaryBytes(receipt) @@ -163,14 +169,22 @@ func (app *abciApp) Commit() abci_types.ResponseCommit { defer app.mtx.Unlock() tip := app.blockchain.Tip() logging.InfoMsg(app.logger, "Committing block", + "tag", "Commit", structure.ScopeKey, "Commit()", - "block_height", tip.LastBlockHeight(), + "block_height", app.block.Header.Height, "block_hash", app.block.Hash, "block_time", app.block.Header.Time, "num_txs", app.block.Header.NumTxs, "last_block_time", tip.LastBlockTime(), "last_block_hash", tip.LastBlockHash()) + err := app.checker.Reset() + if err != nil { + return abci_types.ResponseCommit{ + Code: codes.CommitErrorCode, + Log: fmt.Sprintf("Could not reset check cache during commit: %s", err), + } + } appHash, err := app.committer.Commit() if err != nil { return abci_types.ResponseCommit{ diff --git a/core/kernel.go b/core/kernel.go index 9d52342b5bc0dd0c15dd5003890c9859d14ec5f1..f00cd7a9b17d0d03f690ddcd2c135a6c33b63795 100644 --- a/core/kernel.go +++ b/core/kernel.go @@ -23,8 +23,6 @@ import ( "syscall" "time" - "bytes" - bcm "github.com/hyperledger/burrow/blockchain" "github.com/hyperledger/burrow/consensus/tendermint" "github.com/hyperledger/burrow/consensus/tendermint/query" @@ -64,22 +62,24 @@ func NewKernel(ctx context.Context, privValidator tm_types.PrivValidator, genesi tmConf *tm_config.Config, rpcConfig *rpc.RPCConfig, logger logging_types.InfoTraceLogger) (*Kernel, error) { logger = logging.WithScope(logger, "NewKernel") - + var err error stateDB := dbm.NewDB("burrow_state", dbm.GoLevelDBBackendStr, tmConf.DBDir()) - state, err := execution.LoadOrMakeGenesisState(stateDB, genesisDoc, logger) - if err != nil { - return nil, fmt.Errorf("error making or loading genesis state: %v", err) - } blockchain, err := bcm.LoadOrNewBlockchain(stateDB, genesisDoc, logger) if err != nil { return nil, fmt.Errorf("error creating or loading blockchain state: %v", err) } + var state *execution.State // These should be in sync unless we are at the genesis block - if blockchain.LastBlockHeight() > 0 && !bytes.Equal(blockchain.AppHashAfterLastBlock(), state.LastSavedHash()) { - return nil, fmt.Errorf("blockchain app hash: 0x%X does not match execution state hash: 0x%X", - blockchain.AppHashAfterLastBlock(), state.LastSavedHash()) + if blockchain.LastBlockHeight() > 0 { + state, err = execution.LoadState(stateDB, blockchain.AppHashAfterLastBlock()) + if err != nil { + return nil, fmt.Errorf("could not load persisted execution state at hash 0x%X: %v", + blockchain.AppHashAfterLastBlock(), err) + } + } else { + state, err = execution.MakeGenesisState(stateDB, genesisDoc) } tmGenesisDoc := tendermint.DeriveGenesisDoc(genesisDoc) diff --git a/core/kernel_test.go b/core/kernel_test.go index dd14a02a1733805b4ded2910d7906e464a3fb4ab..427461075c7118fd5dde4c3364168fa4abe599f0 100644 --- a/core/kernel_test.go +++ b/core/kernel_test.go @@ -46,9 +46,11 @@ func TestBootShutdownResume(t *testing.T) { i := int64(1) // asserts we get a consecutive run of blocks - blockChecker := func(block *tm_types.EventDataNewBlock) { + blockChecker := func(block *tm_types.EventDataNewBlock) bool { assert.Equal(t, i, block.Block.Height) i++ + // stop every third block + return i%3 != 0 } // First run assert.NoError(t, bootWaitBlocksShutdown(privValidator, genesisDoc, tmConf, logger, blockChecker)) @@ -61,7 +63,7 @@ func TestBootShutdownResume(t *testing.T) { func bootWaitBlocksShutdown(privValidator tm_types.PrivValidator, genesisDoc *genesis.GenesisDoc, tmConf *tm_config.Config, logger logging_types.InfoTraceLogger, - blockChecker func(block *tm_types.EventDataNewBlock)) error { + blockChecker func(block *tm_types.EventDataNewBlock) (cont bool)) error { kern, err := NewKernel(context.Background(), privValidator, genesisDoc, tmConf, rpc.DefaultRPCConfig(), logger) if err != nil { @@ -75,15 +77,18 @@ func bootWaitBlocksShutdown(privValidator tm_types.PrivValidator, genesisDoc *ge ch := make(chan *tm_types.EventDataNewBlock) tendermint.SubscribeNewBlock(context.Background(), kern.Emitter, "TestBootShutdownResume", ch) - for i := 0; i < 2; i++ { + cont := true + for cont { select { case <-time.After(2 * time.Second): if err != nil { return fmt.Errorf("timed out waiting for block") } case ednb := <-ch: - if blockChecker != nil { - blockChecker(ednb) + if blockChecker == nil { + cont = false + } else { + cont = blockChecker(ednb) } } } diff --git a/execution/evm/accounts.go b/execution/evm/accounts.go index 19735838ae9c771a58bc4c4ff6d1d2cfe2d5808a..618557aaa9aabca3d6f4982ce81ad793f5838521 100644 --- a/execution/evm/accounts.go +++ b/execution/evm/accounts.go @@ -2,14 +2,22 @@ package evm import ( acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/logging" + logging_types "github.com/hyperledger/burrow/logging/types" ptypes "github.com/hyperledger/burrow/permission/types" ) // Create a new account from a parent 'creator' account. The creator account will have its // sequence number incremented -func DeriveNewAccount(creator acm.MutableAccount, permissions ptypes.AccountPermissions) acm.MutableAccount { +func DeriveNewAccount(creator acm.MutableAccount, permissions ptypes.AccountPermissions, + logger logging_types.InfoTraceLogger) acm.MutableAccount { // Generate an address sequence := creator.Sequence() + logging.TraceMsg(logger, "Incrementing sequence number in DeriveNewAccount()", + "tag", "sequence", + "account", creator.Address(), + "old_sequence", sequence, + "new_sequence", sequence+1) creator.IncSequence() addr := acm.NewContractAddress(creator.Address(), sequence) diff --git a/execution/evm/vm.go b/execution/evm/vm.go index fe1f51cbfde5783612babd698940888874609e0a..98fd2943281ce862ed1bdcda0e7fc60eb811dd19 100644 --- a/execution/evm/vm.go +++ b/execution/evm/vm.go @@ -72,7 +72,7 @@ type VM struct { memoryProvider func() Memory params Params origin acm.Address - txid []byte + txHash []byte callDepth int publisher event.Publisher logger logging_types.InfoTraceLogger @@ -86,7 +86,7 @@ func NewVM(state acm.StateWriter, memoryProvider func() Memory, params Params, o params: params, origin: origin, callDepth: 0, - txid: txid, + txHash: txid, logger: logging.WithScope(logger, "NewVM"), } } @@ -117,7 +117,7 @@ func (vm *VM) fireCallEvent(exception *string, output *[]byte, callerAddress, ca events.PublishAccountCall(vm.publisher, calleeAddress, &events.EventDataCall{ &events.CallData{Caller: callerAddress, Callee: calleeAddress, Data: input, Value: value, Gas: *gas}, vm.origin, - vm.txid, + vm.txHash, *output, *exception, }) @@ -746,7 +746,8 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value } // TODO charge for gas to create account _ the code length * GasCreateByte - newAccount := DeriveNewAccount(callee, permission.GlobalAccountPermissions(vm.state)) + newAccount := DeriveNewAccount(callee, permission.GlobalAccountPermissions(vm.state), + vm.logger.With("tx_hash", vm.txHash)) vm.state.UpdateAccount(newAccount) // Run the input to get the contract code. diff --git a/execution/execution.go b/execution/execution.go index abbb51ecc3db70391538ca9d28dba7abe64c8a46..fbe69f4f4fdddacd6e9d2bc68bcfe03d08b91098 100644 --- a/execution/execution.go +++ b/execution/execution.go @@ -32,6 +32,9 @@ import ( "github.com/hyperledger/burrow/txs" ) +// TODO +const GasLimit = uint64(1000000) + type BatchExecutor interface { acm.StateIterable acm.Updater @@ -132,11 +135,16 @@ func (exe *executor) IterateStorage(address acm.Address, consumer func(key, valu return exe.stateCache.IterateStorage(address, consumer) } -func (exe *executor) Commit() ([]byte, error) { +func (exe *executor) Commit() (hash []byte, err error) { exe.Lock() defer exe.Unlock() + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("recovered from panic in executor.Commit(): %v", r) + } + }() // flush the caches - err := exe.stateCache.Flush(exe.state) + err = exe.stateCache.Flush(exe.state) if err != nil { return nil, err } @@ -151,13 +159,12 @@ func (exe *executor) Commit() ([]byte, error) { } // flush events to listeners (XXX: note issue with blocking) exe.eventCache.Flush() - return exe.state.LastSavedHash(), nil + return exe.state.Hash(), nil } func (exe *executor) Reset() error { - exe.stateCache = acm.NewStateCache(exe.state) - exe.nameRegCache = NewNameRegCache(exe.state) - exe.eventCache = event.NewEventCache(exe.publisher) + exe.stateCache.Reset(exe.state) + exe.nameRegCache.Reset(exe.state) return nil } @@ -169,9 +176,11 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { err = fmt.Errorf("recovered from panic in executor.Execute(%s): %v", tx.String(), r) } }() - txHash := txs.TxHash(exe.chainID, tx) + + txHash := tx.Hash(exe.chainID) logger := logging.WithScope(exe.logger, "executor.Execute(tx txs.Tx)").With( "run_call", exe.runCall, + "tx", tx.String(), "tx_hash", txHash) logging.TraceMsg(logger, "Executing transaction", "tx", tx.String()) // TODO: do something with fees @@ -213,7 +222,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { fees += fee // Good! Adjust accounts - err = adjustByInputs(accounts, tx.Inputs) + err = adjustByInputs(accounts, tx.Inputs, logger) if err != nil { return err } @@ -308,7 +317,8 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { // Good! value := tx.Input.Amount - tx.Fee - logging.TraceMsg(logger, "Incrementing sequence number", + logging.TraceMsg(logger, "Incrementing sequence number for CallTx", + "tag", "sequence", "account", inAcc.Address(), "old_sequence", inAcc.Sequence(), "new_sequence", inAcc.Sequence()+1) @@ -363,7 +373,12 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { // get or create callee if createContract { // We already checked for permission - callee = evm.DeriveNewAccount(caller, permission.GlobalAccountPermissions(exe.state)) + callee = evm.DeriveNewAccount(caller, permission.GlobalAccountPermissions(exe.state), + logger.With( + "tx", tx.String(), + "tx_hash", txHash, + "run_call", exe.runCall, + )) code = tx.Data logging.TraceMsg(logger, "Creating new contract", "contract_address", callee.Address(), @@ -384,7 +399,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { txCache.UpdateAccount(caller) txCache.UpdateAccount(callee) vmach := evm.NewVM(txCache, evm.DefaultDynamicMemoryProvider, params, caller.Address(), - txs.TxHash(exe.chainID, tx), logger) + tx.Hash(exe.chainID), logger) vmach.SetPublisher(exe.eventCache) // NOTE: Call() transfers the value from caller to callee iff call succeeds. ret, err = vmach.Call(caller, callee, code, tx.Data, value, &gas) @@ -418,7 +433,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { if err != nil { exception = err.Error() } - txHash := txs.TxHash(exe.chainID, tx) + txHash := tx.Hash(exe.chainID) events.PublishAccountInput(exe.eventCache, tx.Input.Address, txHash, tx, ret, exception) if tx.Address != nil { events.PublishAccountOutput(exe.eventCache, *tx.Address, txHash, tx, ret, exception) @@ -435,8 +450,8 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { } if createContract { // This is done by DeriveNewAccount when runCall == true - logging.TraceMsg(logger, "Incrementing sequence number since creates contract", + "tag", "sequence", "account", inAcc.Address(), "old_sequence", inAcc.Sequence(), "new_sequence", inAcc.Sequence()+1) @@ -588,6 +603,11 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { // TODO: something with the value sent? // Good! + logging.TraceMsg(logger, "Incrementing sequence number for NameTx", + "tag", "sequence", + "account", inAcc.Address(), + "old_sequence", inAcc.Sequence(), + "new_sequence", inAcc.Sequence()+1) inAcc.IncSequence() inAcc, err = inAcc.SubtractFromBalance(value) if err != nil { @@ -598,7 +618,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { // TODO: maybe we want to take funds on error and allow txs in that don't do anythingi? if exe.eventCache != nil { - txHash := txs.TxHash(exe.chainID, tx) + txHash := tx.Hash(exe.chainID) events.PublishAccountInput(exe.eventCache, tx.Input.Address, txHash, tx, nil, "") events.PublishNameReg(exe.eventCache, txHash, tx) } @@ -838,6 +858,11 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { } // Good! + logging.TraceMsg(logger, "Incrementing sequence number for PermissionsTx", + "tag", "sequence", + "account", inAcc.Address(), + "old_sequence", inAcc.Sequence(), + "new_sequence", inAcc.Sequence()+1) inAcc.IncSequence() inAcc, err = inAcc.SubtractFromBalance(value) if err != nil { @@ -849,7 +874,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { } if exe.eventCache != nil { - txHash := txs.TxHash(exe.chainID, tx) + txHash := tx.Hash(exe.chainID) events.PublishAccountInput(exe.eventCache, tx.Input.Address, txHash, tx, nil, "") events.PublishPermissions(exe.eventCache, permission.PermFlagToString(permFlag), txHash, tx) } @@ -1148,7 +1173,7 @@ func validateOutputs(outs []*txs.TxOutput) (uint64, error) { return total, nil } -func adjustByInputs(accs map[acm.Address]acm.MutableAccount, ins []*txs.TxInput) error { +func adjustByInputs(accs map[acm.Address]acm.MutableAccount, ins []*txs.TxInput, logger logging_types.InfoTraceLogger) error { for _, in := range ins { acc := accs[in.Address] if acc == nil { @@ -1163,6 +1188,11 @@ func adjustByInputs(accs map[acm.Address]acm.MutableAccount, ins []*txs.TxInput) if err != nil { return err } + logging.TraceMsg(logger, "Incrementing sequence number for SendTx (adjustByInputs)", + "tag", "sequence", + "account", acc.Address(), + "old_sequence", acc.Sequence(), + "new_sequence", acc.Sequence()+1) acc.IncSequence() } return nil diff --git a/execution/execution_test.go b/execution/execution_test.go index da8c62104bbbf7c9cc0f46decb57cef772eeaba6..47e913ce9e727a98a0a835a18a6491be7f23c13a 100644 --- a/execution/execution_test.go +++ b/execution/execution_test.go @@ -31,6 +31,7 @@ import ( . "github.com/hyperledger/burrow/execution/evm/asm" "github.com/hyperledger/burrow/execution/evm/asm/bc" evm_events "github.com/hyperledger/burrow/execution/evm/events" + "github.com/hyperledger/burrow/execution/evm/sha3" "github.com/hyperledger/burrow/genesis" "github.com/hyperledger/burrow/logging/loggers" "github.com/hyperledger/burrow/permission" @@ -38,10 +39,11 @@ import ( "github.com/hyperledger/burrow/txs" "github.com/stretchr/testify/require" dbm "github.com/tendermint/tmlibs/db" + "github.com/tmthrgd/go-hex" ) var ( - dbBackend = "memdb" + dbBackend = dbm.MemDBBackendStr dbDir = "" permissionsContract = evm.SNativeContracts()["Permissions"] ) @@ -110,6 +112,10 @@ x - roles: has, add, rm // keys var users = makeUsers(10) var logger = loggers.NewNoopInfoTraceLogger() +var deterministicGenesis = genesis.NewDeterministicGenesis(34059836243380576) +var testGenesisDoc, testPrivAccounts, _ = deterministicGenesis. + GenesisDoc(3, true, 1000, 1, true, 1000) +var testChainID = testGenesisDoc.ChainID() func makeUsers(n int) []acm.PrivateAccount { users := make([]acm.PrivateAccount, n) @@ -166,6 +172,7 @@ func newBaseGenDoc(globalPerm, accountPerm ptypes.AccountPermissions) genesis.Ge func TestSendFails(t *testing.T) { stateDB := dbm.NewDB("state", dbBackend, dbDir) + defer stateDB.Close() genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions) genDoc.Accounts[1].Permissions.Base.Set(permission.Send, true) genDoc.Accounts[2].Permissions.Base.Set(permission.Call, true) @@ -235,6 +242,7 @@ func TestSendFails(t *testing.T) { func TestName(t *testing.T) { stateDB := dbm.NewDB("state", dbBackend, dbDir) + defer stateDB.Close() genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions) genDoc.Accounts[0].Permissions.Base.Set(permission.Send, true) genDoc.Accounts[1].Permissions.Base.Set(permission.Name, true) @@ -270,6 +278,7 @@ func TestName(t *testing.T) { func TestCallFails(t *testing.T) { stateDB := dbm.NewDB("state", dbBackend, dbDir) + defer stateDB.Close() genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions) genDoc.Accounts[1].Permissions.Base.Set(permission.Send, true) genDoc.Accounts[2].Permissions.Base.Set(permission.Call, true) @@ -342,6 +351,7 @@ func TestCallFails(t *testing.T) { func TestSendPermission(t *testing.T) { stateDB := dbm.NewDB("state", dbBackend, dbDir) + defer stateDB.Close() genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions) genDoc.Accounts[0].Permissions.Base.Set(permission.Send, true) // give the 0 account permission st, err := MakeGenesisState(stateDB, &genDoc) @@ -379,6 +389,7 @@ func TestSendPermission(t *testing.T) { func TestCallPermission(t *testing.T) { stateDB := dbm.NewDB("state", dbBackend, dbDir) + defer stateDB.Close() genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions) genDoc.Accounts[0].Permissions.Base.Set(permission.Call, true) // give the 0 account permission st, err := MakeGenesisState(stateDB, &genDoc) @@ -502,6 +513,7 @@ func TestCallPermission(t *testing.T) { func TestCreatePermission(t *testing.T) { stateDB := dbm.NewDB("state", dbBackend, dbDir) + defer stateDB.Close() genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions) genDoc.Accounts[0].Permissions.Base.Set(permission.CreateContract, true) // give the 0 account permission genDoc.Accounts[0].Permissions.Base.Set(permission.Call, true) // give the 0 account permission @@ -617,133 +629,9 @@ func TestCreatePermission(t *testing.T) { } } -/* TODO -func TestBondPermission(t *testing.T) { - stateDB := dbm.NewDB("state",dbBackend,dbDir) - genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse) - st, err := MakeGenesisState(stateDB, &genDoc) - batchCommitter := makeExecutor(st) - var bondAcc *acm.Account - - //------------------------------ - // one bonder without permission should fail - tx, _ := txs.NewBondTx(users[1].PublicKey()) - if err := tx.AddInput(batchCommitter.blockCache, users[1].PublicKey(), 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(users[1].Address(), 5) - tx.SignInput(testChainID, 0, users[1]) - tx.SignBond(testChainID, users[1]) - if err := ExecTx(batchCommitter.blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - //------------------------------ - // one bonder with permission should pass - bondAcc = batchCommitter.blockCache.GetAccount(users[1].Address()) - bondAcc.Permissions.Base.Set(permission.Bond, true) - batchCommitter.blockCache.UpdateAccount(bondAcc) - if err := ExecTx(batchCommitter.blockCache, tx, true, nil); err != nil { - t.Fatal("Unexpected error", err) - } - - // reset state (we can only bond with an account once ..) - genDoc = newBaseGenDoc(PermsAllFalse, PermsAllFalse) - st = MakeGenesisState(stateDB, &genDoc) - batchCommitter.blockCache = NewBlockCache(st) - bondAcc = batchCommitter.blockCache.GetAccount(users[1].Address()) - bondAcc.Permissions.Base.Set(permission.Bond, true) - batchCommitter.blockCache.UpdateAccount(bondAcc) - //------------------------------ - // one bonder with permission and an input without send should fail - tx, _ = txs.NewBondTx(users[1].PublicKey()) - if err := tx.AddInput(batchCommitter.blockCache, users[2].PublicKey(), 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(users[1].Address(), 5) - tx.SignInput(testChainID, 0, users[2]) - tx.SignBond(testChainID, users[1]) - if err := ExecTx(batchCommitter.blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } else { - fmt.Println(err) - } - - // reset state (we can only bond with an account once ..) - genDoc = newBaseGenDoc(PermsAllFalse, PermsAllFalse) - st = MakeGenesisState(stateDB, &genDoc) - batchCommitter.blockCache = NewBlockCache(st) - bondAcc = batchCommitter.blockCache.GetAccount(users[1].Address()) - bondAcc.Permissions.Base.Set(permission.Bond, true) - batchCommitter.blockCache.UpdateAccount(bondAcc) - //------------------------------ - // one bonder with permission and an input with send should pass - sendAcc := batchCommitter.blockCache.GetAccount(users[2].Address()) - sendAcc.Permissions.Base.Set(permission.Send, true) - batchCommitter.blockCache.UpdateAccount(sendAcc) - tx, _ = txs.NewBondTx(users[1].PublicKey()) - if err := tx.AddInput(batchCommitter.blockCache, users[2].PublicKey(), 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(users[1].Address(), 5) - tx.SignInput(testChainID, 0, users[2]) - tx.SignBond(testChainID, users[1]) - if err := ExecTx(batchCommitter.blockCache, tx, true, nil); err != nil { - t.Fatal("Unexpected error", err) - } - - // reset state (we can only bond with an account once ..) - genDoc = newBaseGenDoc(PermsAllFalse, PermsAllFalse) - st = MakeGenesisState(stateDB, &genDoc) - batchCommitter.blockCache = NewBlockCache(st) - bondAcc = batchCommitter.blockCache.GetAccount(users[1].Address()) - bondAcc.Permissions.Base.Set(permission.Bond, true) - batchCommitter.blockCache.UpdateAccount(bondAcc) - //------------------------------ - // one bonder with permission and an input with bond should pass - sendAcc.Permissions.Base.Set(permission.Bond, true) - batchCommitter.blockCache.UpdateAccount(sendAcc) - tx, _ = txs.NewBondTx(users[1].PublicKey()) - if err := tx.AddInput(batchCommitter.blockCache, users[2].PublicKey(), 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(users[1].Address(), 5) - tx.SignInput(testChainID, 0, users[2]) - tx.SignBond(testChainID, users[1]) - if err := ExecTx(batchCommitter.blockCache, tx, true, nil); err != nil { - t.Fatal("Unexpected error", err) - } - - // reset state (we can only bond with an account once ..) - genDoc = newBaseGenDoc(PermsAllFalse, PermsAllFalse) - st = MakeGenesisState(stateDB, &genDoc) - batchCommitter.blockCache = NewBlockCache(st) - bondAcc = batchCommitter.blockCache.GetAccount(users[1].Address()) - bondAcc.Permissions.Base.Set(permission.Bond, true) - batchCommitter.blockCache.UpdateAccount(bondAcc) - //------------------------------ - // one bonder with permission and an input from that bonder and an input without send or bond should fail - tx, _ = txs.NewBondTx(users[1].PublicKey()) - if err := tx.AddInput(batchCommitter.blockCache, users[1].PublicKey(), 5); err != nil { - t.Fatal(err) - } - if err := tx.AddInput(batchCommitter.blockCache, users[2].PublicKey(), 5); err != nil { - t.Fatal(err) - } - tx.AddOutput(users[1].Address(), 5) - tx.SignInput(testChainID, 0, users[1]) - tx.SignInput(testChainID, 1, users[2]) - tx.SignBond(testChainID, users[1]) - if err := ExecTx(batchCommitter.blockCache, tx, true, nil); err == nil { - t.Fatal("Expected error") - } -} -*/ - func TestCreateAccountPermission(t *testing.T) { stateDB := dbm.NewDB("state", dbBackend, dbDir) + defer stateDB.Close() genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions) genDoc.Accounts[0].Permissions.Base.Set(permission.Send, true) // give the 0 account permission genDoc.Accounts[1].Permissions.Base.Set(permission.Send, true) // give the 0 account permission @@ -892,6 +780,7 @@ func init() { func TestSNativeCALL(t *testing.T) { stateDB := dbm.NewDB("state", dbBackend, dbDir) + defer stateDB.Close() genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions) genDoc.Accounts[0].Permissions.Base.Set(permission.Call, true) // give the 0 account permission genDoc.Accounts[3].Permissions.Base.Set(permission.Bond, true) // some arbitrary permission to play with @@ -1028,6 +917,7 @@ func TestSNativeCALL(t *testing.T) { func TestSNativeTx(t *testing.T) { stateDB := dbm.NewDB("state", dbBackend, dbDir) + defer stateDB.Close() genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions) genDoc.Accounts[0].Permissions.Base.Set(permission.Call, true) // give the 0 account permission genDoc.Accounts[3].Permissions.Base.Set(permission.Bond, true) // some arbitrary permission to play with @@ -1097,6 +987,749 @@ func TestSNativeTx(t *testing.T) { } } +func TestTxSequence(t *testing.T) { + state, privAccounts := makeGenesisState(3, true, 1000, 1, true, 1000) + acc0 := getAccount(state, privAccounts[0].Address()) + acc0PubKey := privAccounts[0].PublicKey() + acc1 := getAccount(state, privAccounts[1].Address()) + + // Test a variety of sequence numbers for the tx. + // The tx should only pass when i == 1. + for i := uint64(0); i < 3; i++ { + sequence := acc0.Sequence() + i + tx := txs.NewSendTx() + tx.AddInputWithSequence(acc0PubKey, 1, sequence) + tx.AddOutput(acc1.Address(), 1) + tx.Inputs[0].Signature = acm.ChainSign(privAccounts[0], testChainID, tx) + stateCopy := state.Copy(dbm.NewMemDB()) + err := execTxWithState(stateCopy, tx) + if i == 1 { + // Sequence is good. + if err != nil { + t.Errorf("Expected good sequence to pass: %v", err) + } + // Check acc.Sequence(). + newAcc0 := getAccount(stateCopy, acc0.Address()) + if newAcc0.Sequence() != sequence { + t.Errorf("Expected account sequence to change to %v, got %v", + sequence, newAcc0.Sequence()) + } + } else { + // Sequence is bad. + if err == nil { + t.Errorf("Expected bad sequence to fail") + } + // Check acc.Sequence(). (shouldn't have changed) + newAcc0 := getAccount(stateCopy, acc0.Address()) + if newAcc0.Sequence() != acc0.Sequence() { + t.Errorf("Expected account sequence to not change from %v, got %v", + acc0.Sequence(), newAcc0.Sequence()) + } + } + } +} + +func TestNameTxs(t *testing.T) { + state, err := MakeGenesisState(dbm.NewMemDB(), testGenesisDoc) + require.NoError(t, err) + state.Save() + + txs.MinNameRegistrationPeriod = 5 + blockchain := bcm.NewBlockchain(nil, testGenesisDoc) + startingBlock := blockchain.LastBlockHeight() + + // try some bad names. these should all fail + names := []string{"", "\n", "123#$%", "\x00", string([]byte{20, 40, 60, 80}), + "baffledbythespectacleinallofthisyouseeehesaidwithouteyessurprised", "no spaces please"} + data := "something about all this just doesn't feel right." + fee := uint64(1000) + numDesiredBlocks := uint64(5) + for _, name := range names { + amt := fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier* + txs.NameBaseCost(name, data) + tx, _ := txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee) + tx.Sign(testChainID, testPrivAccounts[0]) + + if err := execTxWithState(state, tx); err == nil { + t.Fatalf("Expected invalid name error from %s", name) + } + } + + // try some bad data. these should all fail + name := "hold_it_chum" + datas := []string{"cold&warm", "!@#$%^&*()", "<<<>>>>", "because why would you ever need a ~ or a & or even a % in a json file? make your case and we'll talk"} + for _, data := range datas { + amt := fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier* + txs.NameBaseCost(name, data) + tx, _ := txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee) + tx.Sign(testChainID, testPrivAccounts[0]) + + if err := execTxWithState(state, tx); err == nil { + t.Fatalf("Expected invalid data error from %s", data) + } + } + + validateEntry := func(t *testing.T, entry *NameRegEntry, name, data string, addr acm.Address, expires uint64) { + + if entry == nil { + t.Fatalf("Could not find name %s", name) + } + if entry.Owner != addr { + t.Fatalf("Wrong owner. Got %s expected %s", entry.Owner, addr) + } + if data != entry.Data { + t.Fatalf("Wrong data. Got %s expected %s", entry.Data, data) + } + if name != entry.Name { + t.Fatalf("Wrong name. Got %s expected %s", entry.Name, name) + } + if expires != entry.Expires { + t.Fatalf("Wrong expiry. Got %d, expected %d", entry.Expires, expires) + } + } + + // try a good one, check data, owner, expiry + name = "@looking_good/karaoke_bar.broadband" + data = "on this side of neptune there are 1234567890 people: first is OMNIVORE+-3. Or is it. Ok this is pretty restrictive. No exclamations :(. Faces tho :')" + amt := fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*txs.NameBaseCost(name, data) + tx, _ := txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee) + tx.Sign(testChainID, testPrivAccounts[0]) + if err := execTxWithState(state, tx); err != nil { + t.Fatal(err) + } + entry, err := state.GetNameRegEntry(name) + require.NoError(t, err) + validateEntry(t, entry, name, data, testPrivAccounts[0].Address(), startingBlock+numDesiredBlocks) + + // fail to update it as non-owner, in same block + tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee) + tx.Sign(testChainID, testPrivAccounts[1]) + if err := execTxWithState(state, tx); err == nil { + t.Fatal("Expected error") + } + + // update it as owner, just to increase expiry, in same block + // NOTE: we have to resend the data or it will clear it (is this what we want?) + tx, _ = txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee) + tx.Sign(testChainID, testPrivAccounts[0]) + if err := execTxWithStateNewBlock(state, blockchain, tx); err != nil { + t.Fatal(err) + } + entry, err = state.GetNameRegEntry(name) + require.NoError(t, err) + validateEntry(t, entry, name, data, testPrivAccounts[0].Address(), startingBlock+numDesiredBlocks*2) + + // update it as owner, just to increase expiry, in next block + tx, _ = txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee) + tx.Sign(testChainID, testPrivAccounts[0]) + if err := execTxWithStateNewBlock(state, blockchain, tx); err != nil { + t.Fatal(err) + } + entry, err = state.GetNameRegEntry(name) + require.NoError(t, err) + validateEntry(t, entry, name, data, testPrivAccounts[0].Address(), startingBlock+numDesiredBlocks*3) + + // fail to update it as non-owner + // Fast forward + for blockchain.Tip().LastBlockHeight() < entry.Expires-1 { + commitNewBlock(state, blockchain) + } + tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee) + tx.Sign(testChainID, testPrivAccounts[1]) + if err := execTxWithStateAndBlockchain(state, blockchain, tx); err == nil { + t.Fatal("Expected error") + } + commitNewBlock(state, blockchain) + + // once expires, non-owner succeeds + tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee) + tx.Sign(testChainID, testPrivAccounts[1]) + if err := execTxWithStateAndBlockchain(state, blockchain, tx); err != nil { + t.Fatal(err) + } + entry, err = state.GetNameRegEntry(name) + require.NoError(t, err) + validateEntry(t, entry, name, data, testPrivAccounts[1].Address(), blockchain.LastBlockHeight()+numDesiredBlocks) + + // update it as new owner, with new data (longer), but keep the expiry! + data = "In the beginning there was no thing, not even the beginning. It hadn't been here, no there, nor for that matter anywhere, not especially because it had not to even exist, let alone to not. Nothing especially odd about that." + oldCredit := amt - fee + numDesiredBlocks = 10 + amt = fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*txs.NameBaseCost(name, data) - oldCredit + tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee) + tx.Sign(testChainID, testPrivAccounts[1]) + if err := execTxWithStateAndBlockchain(state, blockchain, tx); err != nil { + t.Fatal(err) + } + entry, err = state.GetNameRegEntry(name) + require.NoError(t, err) + validateEntry(t, entry, name, data, testPrivAccounts[1].Address(), blockchain.LastBlockHeight()+numDesiredBlocks) + + // test removal + amt = fee + data = "" + tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee) + tx.Sign(testChainID, testPrivAccounts[1]) + if err := execTxWithStateNewBlock(state, blockchain, tx); err != nil { + t.Fatal(err) + } + entry, err = state.GetNameRegEntry(name) + require.NoError(t, err) + if entry != nil { + t.Fatal("Expected removed entry to be nil") + } + + // create entry by key0, + // test removal by key1 after expiry + name = "looking_good/karaoke_bar" + data = "some data" + amt = fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*txs.NameBaseCost(name, data) + tx, _ = txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee) + tx.Sign(testChainID, testPrivAccounts[0]) + if err := execTxWithStateAndBlockchain(state, blockchain, tx); err != nil { + t.Fatal(err) + } + entry, err = state.GetNameRegEntry(name) + require.NoError(t, err) + validateEntry(t, entry, name, data, testPrivAccounts[0].Address(), blockchain.LastBlockHeight()+numDesiredBlocks) + // Fast forward + for blockchain.Tip().LastBlockHeight() < entry.Expires { + commitNewBlock(state, blockchain) + } + + amt = fee + data = "" + tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee) + tx.Sign(testChainID, testPrivAccounts[1]) + if err := execTxWithStateNewBlock(state, blockchain, tx); err != nil { + t.Fatal(err) + } + entry, err = state.GetNameRegEntry(name) + require.NoError(t, err) + if entry != nil { + t.Fatal("Expected removed entry to be nil") + } +} + +// Test creating a contract from futher down the call stack +/* +contract Factory { + address a; + function create() returns (address){ + a = new PreFactory(); + return a; + } +} + +contract PreFactory{ + address a; + function create(Factory c) returns (address) { + a = c.create(); + return a; + } +} +*/ + +// run-time byte code for each of the above +var preFactoryCode, _ = hex.DecodeString("60606040526000357C0100000000000000000000000000000000000000000000000000000000900480639ED933181461003957610037565B005B61004F600480803590602001909190505061007B565B604051808273FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16815260200191505060405180910390F35B60008173FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1663EFC81A8C604051817C01000000000000000000000000000000000000000000000000000000000281526004018090506020604051808303816000876161DA5A03F1156100025750505060405180519060200150600060006101000A81548173FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02191690830217905550600060009054906101000A900473FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16905061013C565B91905056") +var factoryCode, _ = hex.DecodeString("60606040526000357C010000000000000000000000000000000000000000000000000000000090048063EFC81A8C146037576035565B005B60426004805050606E565B604051808273FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16815260200191505060405180910390F35B6000604051610153806100E0833901809050604051809103906000F0600060006101000A81548173FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02191690830217905550600060009054906101000A900473FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16905060DD565B90566060604052610141806100126000396000F360606040526000357C0100000000000000000000000000000000000000000000000000000000900480639ED933181461003957610037565B005B61004F600480803590602001909190505061007B565B604051808273FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16815260200191505060405180910390F35B60008173FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1663EFC81A8C604051817C01000000000000000000000000000000000000000000000000000000000281526004018090506020604051808303816000876161DA5A03F1156100025750505060405180519060200150600060006101000A81548173FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02191690830217905550600060009054906101000A900473FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16905061013C565B91905056") +var createData, _ = hex.DecodeString("9ed93318") + +func TestCreates(t *testing.T) { + //evm.SetDebug(true) + state, privAccounts := makeGenesisState(3, true, 1000, 1, true, 1000) + + //val0 := state.GetValidatorInfo(privValidators[0].Address()) + acc0 := getAccount(state, privAccounts[0].Address()) + acc0PubKey := privAccounts[0].PublicKey() + acc1 := getAccount(state, privAccounts[1].Address()) + acc2 := getAccount(state, privAccounts[2].Address()) + + newAcc1 := getAccount(state, acc1.Address()) + newAcc1.SetCode(preFactoryCode) + newAcc2 := getAccount(state, acc2.Address()) + newAcc2.SetCode(factoryCode) + + state.UpdateAccount(newAcc1) + state.UpdateAccount(newAcc2) + + createData = append(createData, acc2.Address().Word256().Bytes()...) + + // call the pre-factory, triggering the factory to run a create + tx := &txs.CallTx{ + Input: &txs.TxInput{ + Address: acc0.Address(), + Amount: 1, + Sequence: acc0.Sequence() + 1, + PublicKey: acc0PubKey, + }, + Address: addressPtr(acc1), + GasLimit: 10000, + Data: createData, + } + + tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) + err := execTxWithState(state, tx) + if err != nil { + t.Errorf("Got error in executing call transaction, %v", err) + } + + acc1 = getAccount(state, acc1.Address()) + firstCreatedAddress, err := state.GetStorage(acc1.Address(), LeftPadWord256(nil)) + require.NoError(t, err) + + acc0 = getAccount(state, acc0.Address()) + // call the pre-factory, triggering the factory to run a create + tx = &txs.CallTx{ + Input: &txs.TxInput{ + Address: acc0.Address(), + Amount: 1, + Sequence: acc0.Sequence() + 1, + PublicKey: acc0PubKey, + }, + Address: addressPtr(acc1), + GasLimit: 100000, + Data: createData, + } + + tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) + err = execTxWithState(state, tx) + if err != nil { + t.Errorf("Got error in executing call transaction, %v", err) + } + + acc1 = getAccount(state, acc1.Address()) + secondCreatedAddress, err := state.GetStorage(acc1.Address(), LeftPadWord256(nil)) + require.NoError(t, err) + + if firstCreatedAddress == secondCreatedAddress { + t.Errorf("Multiple contracts created with the same address!") + } +} + +/* +contract Caller { + function send(address x){ + x.send(msg.value); + } +} +*/ +var callerCode, _ = hex.DecodeString("60606040526000357c0100000000000000000000000000000000000000000000000000000000900480633e58c58c146037576035565b005b604b6004808035906020019091905050604d565b005b8073ffffffffffffffffffffffffffffffffffffffff16600034604051809050600060405180830381858888f19350505050505b5056") +var sendData, _ = hex.DecodeString("3e58c58c") + +func TestContractSend(t *testing.T) { + state, privAccounts := makeGenesisState(3, true, 1000, 1, true, 1000) + + //val0 := state.GetValidatorInfo(privValidators[0].Address()) + acc0 := getAccount(state, privAccounts[0].Address()) + acc0PubKey := privAccounts[0].PublicKey() + acc1 := getAccount(state, privAccounts[1].Address()) + acc2 := getAccount(state, privAccounts[2].Address()) + + newAcc1 := getAccount(state, acc1.Address()) + newAcc1.SetCode(callerCode) + state.UpdateAccount(newAcc1) + + sendData = append(sendData, acc2.Address().Word256().Bytes()...) + sendAmt := uint64(10) + acc2Balance := acc2.Balance() + + // call the contract, triggering the send + tx := &txs.CallTx{ + Input: &txs.TxInput{ + Address: acc0.Address(), + Amount: sendAmt, + Sequence: acc0.Sequence() + 1, + PublicKey: acc0PubKey, + }, + Address: addressPtr(acc1), + GasLimit: 1000, + Data: sendData, + } + + tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) + err := execTxWithState(state, tx) + if err != nil { + t.Errorf("Got error in executing call transaction, %v", err) + } + + acc2 = getAccount(state, acc2.Address()) + if acc2.Balance() != sendAmt+acc2Balance { + t.Errorf("Value transfer from contract failed! Got %d, expected %d", acc2.Balance(), sendAmt+acc2Balance) + } +} + +func TestMerklePanic(t *testing.T) { + state, privAccounts := makeGenesisState(3, true, 1000, 1, true, + 1000) + + //val0 := state.GetValidatorInfo(privValidators[0].Address()) + acc0 := getAccount(state, privAccounts[0].Address()) + acc0PubKey := privAccounts[0].PublicKey() + acc1 := getAccount(state, privAccounts[1].Address()) + + state.Save() + // SendTx. + { + stateSendTx := state.Copy(dbm.NewMemDB()) + tx := &txs.SendTx{ + Inputs: []*txs.TxInput{ + { + Address: acc0.Address(), + Amount: 1, + Sequence: acc0.Sequence() + 1, + PublicKey: acc0PubKey, + }, + }, + Outputs: []*txs.TxOutput{ + { + Address: acc1.Address(), + Amount: 1, + }, + }, + } + + tx.Inputs[0].Signature = acm.ChainSign(privAccounts[0], testChainID, tx) + err := execTxWithState(stateSendTx, tx) + if err != nil { + t.Errorf("Got error in executing send transaction, %v", err) + } + // uncomment for panic fun! + //stateSendTx.Save() + } + + // CallTx. Just runs through it and checks the transfer. See vm, rpc tests for more + { + stateCallTx := state.Copy(dbm.NewMemDB()) + newAcc1 := getAccount(stateCallTx, acc1.Address()) + newAcc1.SetCode([]byte{0x60}) + stateCallTx.UpdateAccount(newAcc1) + tx := &txs.CallTx{ + Input: &txs.TxInput{ + Address: acc0.Address(), + Amount: 1, + Sequence: acc0.Sequence() + 1, + PublicKey: acc0PubKey, + }, + Address: addressPtr(acc1), + GasLimit: 10, + } + + tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) + err := execTxWithState(stateCallTx, tx) + if err != nil { + t.Errorf("Got error in executing call transaction, %v", err) + } + } + state.Save() + trygetacc0 := getAccount(state, privAccounts[0].Address()) + fmt.Println(trygetacc0.Address()) +} + +// TODO: test overflows. +// TODO: test for unbonding validators. +func TestTxs(t *testing.T) { + state, privAccounts := makeGenesisState(3, true, 1000, 1, true, 1000) + + //val0 := state.GetValidatorInfo(privValidators[0].Address()) + acc0 := getAccount(state, privAccounts[0].Address()) + acc0PubKey := privAccounts[0].PublicKey() + acc1 := getAccount(state, privAccounts[1].Address()) + + // SendTx. + { + stateSendTx := state.Copy(dbm.NewMemDB()) + tx := &txs.SendTx{ + Inputs: []*txs.TxInput{ + { + Address: acc0.Address(), + Amount: 1, + Sequence: acc0.Sequence() + 1, + PublicKey: acc0PubKey, + }, + }, + Outputs: []*txs.TxOutput{ + { + Address: acc1.Address(), + Amount: 1, + }, + }, + } + + tx.Inputs[0].Signature = acm.ChainSign(privAccounts[0], testChainID, tx) + err := execTxWithState(stateSendTx, tx) + if err != nil { + t.Errorf("Got error in executing send transaction, %v", err) + } + newAcc0 := getAccount(stateSendTx, acc0.Address()) + if acc0.Balance()-1 != newAcc0.Balance() { + t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v", + acc0.Balance()-1, newAcc0.Balance()) + } + newAcc1 := getAccount(stateSendTx, acc1.Address()) + if acc1.Balance()+1 != newAcc1.Balance() { + t.Errorf("Unexpected newAcc1 balance. Expected %v, got %v", + acc1.Balance()+1, newAcc1.Balance()) + } + } + + // CallTx. Just runs through it and checks the transfer. See vm, rpc tests for more + { + stateCallTx := state.Copy(dbm.NewMemDB()) + newAcc1 := getAccount(stateCallTx, acc1.Address()) + newAcc1.SetCode([]byte{0x60}) + stateCallTx.UpdateAccount(newAcc1) + tx := &txs.CallTx{ + Input: &txs.TxInput{ + Address: acc0.Address(), + Amount: 1, + Sequence: acc0.Sequence() + 1, + PublicKey: acc0PubKey, + }, + Address: addressPtr(acc1), + GasLimit: 10, + } + + tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) + err := execTxWithState(stateCallTx, tx) + if err != nil { + t.Errorf("Got error in executing call transaction, %v", err) + } + newAcc0 := getAccount(stateCallTx, acc0.Address()) + if acc0.Balance()-1 != newAcc0.Balance() { + t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v", + acc0.Balance()-1, newAcc0.Balance()) + } + newAcc1 = getAccount(stateCallTx, acc1.Address()) + if acc1.Balance()+1 != newAcc1.Balance() { + t.Errorf("Unexpected newAcc1 balance. Expected %v, got %v", + acc1.Balance()+1, newAcc1.Balance()) + } + } + trygetacc0 := getAccount(state, privAccounts[0].Address()) + fmt.Println(trygetacc0.Address()) + + // NameTx. + { + entryName := "satoshi" + entryData := ` +A purely peer-to-peer version of electronic cash would allow online +payments to be sent directly from one party to another without going through a +financial institution. Digital signatures provide part of the solution, but the main +benefits are lost if a trusted third party is still required to prevent double-spending. +We propose a solution to the double-spending problem using a peer-to-peer network. +The network timestamps transactions by hashing them into an ongoing chain of +hash-based proof-of-work, forming a record that cannot be changed without redoing +the proof-of-work. The longest chain not only serves as proof of the sequence of +events witnessed, but proof that it came from the largest pool of CPU power. As +long as a majority of CPU power is controlled by nodes that are not cooperating to +attack the network, they'll generate the longest chain and outpace attackers. The +network itself requires minimal structure. Messages are broadcast on a best effort +basis, and nodes can leave and rejoin the network at will, accepting the longest +proof-of-work chain as proof of what happened while they were gone ` + entryAmount := uint64(10000) + + stateNameTx := state + tx := &txs.NameTx{ + Input: &txs.TxInput{ + Address: acc0.Address(), + Amount: entryAmount, + Sequence: acc0.Sequence() + 1, + PublicKey: acc0PubKey, + }, + Name: entryName, + Data: entryData, + } + + tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) + + err := execTxWithState(stateNameTx, tx) + if err != nil { + t.Errorf("Got error in executing call transaction, %v", err) + } + newAcc0 := getAccount(stateNameTx, acc0.Address()) + if acc0.Balance()-entryAmount != newAcc0.Balance() { + t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v", + acc0.Balance()-entryAmount, newAcc0.Balance()) + } + entry, err := stateNameTx.GetNameRegEntry(entryName) + require.NoError(t, err) + if entry == nil { + t.Errorf("Expected an entry but got nil") + } + if entry.Data != entryData { + t.Errorf("Wrong data stored") + } + + // test a bad string + tx.Data = string([]byte{0, 1, 2, 3, 127, 128, 129, 200, 251}) + tx.Input.Sequence += 1 + tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) + err = execTxWithState(stateNameTx, tx) + if _, ok := err.(txs.ErrTxInvalidString); !ok { + t.Errorf("Expected invalid string error. Got: %s", err.Error()) + } + } + + // BondTx. TODO + /* + { + state := state.Copy() + tx := &txs.BondTx{ + PublicKey: acc0PubKey.(acm.PublicKeyEd25519), + Inputs: []*txs.TxInput{ + &txs.TxInput{ + Address: acc0.Address(), + Amount: 1, + Sequence: acc0.Sequence() + 1, + PublicKey: acc0PubKey, + }, + }, + UnbondTo: []*txs.TxOutput{ + &txs.TxOutput{ + Address: acc0.Address(), + Amount: 1, + }, + }, + } + tx.Signature = privAccounts[0] acm.ChainSign(testChainID, tx).(crypto.SignatureEd25519) + tx.Inputs[0].Signature = privAccounts[0] acm.ChainSign(testChainID, tx) + err := execTxWithState(state, tx) + if err != nil { + t.Errorf("Got error in executing bond transaction, %v", err) + } + newAcc0 := getAccount(state, acc0.Address()) + if newAcc0.Balance() != acc0.Balance()-1 { + t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v", + acc0.Balance()-1, newAcc0.Balance()) + } + _, acc0Val := state.BondedValidators.GetByAddress(acc0.Address()) + if acc0Val == nil { + t.Errorf("acc0Val not present") + } + if acc0Val.BondHeight != blockchain.LastBlockHeight()+1 { + t.Errorf("Unexpected bond height. Expected %v, got %v", + blockchain.LastBlockHeight(), acc0Val.BondHeight) + } + if acc0Val.VotingPower != 1 { + t.Errorf("Unexpected voting power. Expected %v, got %v", + acc0Val.VotingPower, acc0.Balance()) + } + if acc0Val.Accum != 0 { + t.Errorf("Unexpected accum. Expected 0, got %v", + acc0Val.Accum) + } + } */ + + // TODO UnbondTx. + +} + +func TestSelfDestruct(t *testing.T) { + + state, privAccounts := makeGenesisState(3, true, 1000, 1, true, 1000) + + acc0 := getAccount(state, privAccounts[0].Address()) + acc0PubKey := privAccounts[0].PublicKey() + acc1 := getAccount(state, privAccounts[1].Address()) + acc2 := getAccount(state, privAccounts[2].Address()) + sendingAmount, refundedBalance, oldBalance := uint64(1), acc1.Balance(), acc2.Balance() + + newAcc1 := getAccount(state, acc1.Address()) + + // store 0x1 at 0x1, push an address, then self-destruct:) + contractCode := []byte{0x60, 0x01, 0x60, 0x01, 0x55, 0x73} + contractCode = append(contractCode, acc2.Address().Bytes()...) + contractCode = append(contractCode, 0xff) + newAcc1.SetCode(contractCode) + state.UpdateAccount(newAcc1) + + // send call tx with no data, cause self-destruct + tx := txs.NewCallTxWithSequence(acc0PubKey, addressPtr(acc1), nil, sendingAmount, 1000, 0, acc0.Sequence()+1) + tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) + + // we use cache instead of execTxWithState so we can run the tx twice + exe := NewBatchCommitter(state, testChainID, bcm.NewBlockchain(nil, testGenesisDoc), event.NewNoOpPublisher(), logger) + if err := exe.Execute(tx); err != nil { + t.Errorf("Got error in executing call transaction, %v", err) + } + + // if we do it again, we won't get an error, but the self-destruct + // shouldn't happen twice and the caller should lose fee + tx.Input.Sequence += 1 + tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) + if err := exe.Execute(tx); err != nil { + t.Errorf("Got error in executing call transaction, %v", err) + } + + // commit the block + exe.Commit() + + // acc2 should receive the sent funds and the contracts balance + newAcc2 := getAccount(state, acc2.Address()) + newBalance := sendingAmount + refundedBalance + oldBalance + if newAcc2.Balance() != newBalance { + t.Errorf("Unexpected newAcc2 balance. Expected %v, got %v", + newAcc2.Balance(), newBalance) + } + newAcc1 = getAccount(state, acc1.Address()) + if newAcc1 != nil { + t.Errorf("Expected account to be removed") + } +} + +func execTxWithStateAndBlockchain(state *State, tip bcm.Tip, tx txs.Tx) error { + exe := newExecutor(true, state, testChainID, tip, event.NewNoOpPublisher(), logger) + if err := exe.Execute(tx); err != nil { + return err + } else { + exe.Commit() + return nil + } +} + +func execTxWithState(state *State, tx txs.Tx) error { + return execTxWithStateAndBlockchain(state, bcm.NewBlockchain(nil, testGenesisDoc), tx) +} + +func commitNewBlock(state *State, blockchain bcm.MutableBlockchain) { + blockchain.CommitBlock(blockchain.LastBlockTime().Add(time.Second), sha3.Sha3(blockchain.LastBlockHash()), + state.Hash()) +} + +func execTxWithStateNewBlock(state *State, blockchain bcm.MutableBlockchain, tx txs.Tx) error { + if err := execTxWithStateAndBlockchain(state, blockchain, tx); err != nil { + return err + } + commitNewBlock(state, blockchain) + return nil +} + +func makeGenesisState(numAccounts int, randBalance bool, minBalance uint64, numValidators int, randBonded bool, + minBonded int64) (*State, []acm.PrivateAccount) { + testGenesisDoc, privAccounts, _ := deterministicGenesis.GenesisDoc(numAccounts, randBalance, minBalance, + numValidators, randBonded, minBonded) + s0, err := MakeGenesisState(dbm.NewMemDB(), testGenesisDoc) + if err != nil { + panic(fmt.Errorf("could not make genesis state: %v", err)) + } + s0.Save() + return s0, privAccounts +} + +func getAccount(state acm.Getter, address acm.Address) acm.MutableAccount { + acc, _ := acm.GetMutableAccount(state, address) + return acc +} + +func addressPtr(account acm.Account) *acm.Address { + if account == nil { + return nil + } + accountAddresss := account.Address() + return &accountAddresss +} + //------------------------------------------------------------------------------------- // helpers diff --git a/execution/namereg_cache.go b/execution/namereg_cache.go index f08224741b0aa4a1f1b1d9a765f90e5b5ad5c0d0..f941b9f9ba370057153acd781f3a8cd6ce2496c9 100644 --- a/execution/namereg_cache.go +++ b/execution/namereg_cache.go @@ -124,6 +124,8 @@ func (cache *NameRegCache) Sync(state NameRegWriter) error { // Resets the cache to empty initialising the backing map to the same size as the previous iteration. func (cache *NameRegCache) Reset(backend NameRegGetter) { + cache.Lock() + defer cache.Unlock() cache.backend = backend cache.names = make(map[string]*nameInfo) } diff --git a/execution/namereg_cache_test.go b/execution/namereg_cache_test.go index 371bf21e9dbdf24b823e574e11e53be8b744285d..7671133b8d934c25bb9a8c531faeadd8a84d729a 100644 --- a/execution/namereg_cache_test.go +++ b/execution/namereg_cache_test.go @@ -18,6 +18,7 @@ import ( "testing" ) +// TODO: tests func TestNewNameRegCache(t *testing.T) { } diff --git a/execution/state.go b/execution/state.go index c9917f6c989eb7a3b673cf04e5bdfff41410b666..7385267b3be6395b0b17dd6decddb9a4b0d4b01a 100644 --- a/execution/state.go +++ b/execution/state.go @@ -16,7 +16,6 @@ package execution import ( "bytes" - "encoding/json" "fmt" "io" "sync" @@ -25,71 +24,52 @@ import ( acm "github.com/hyperledger/burrow/account" "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/genesis" - "github.com/hyperledger/burrow/logging" logging_types "github.com/hyperledger/burrow/logging/types" "github.com/hyperledger/burrow/permission" ptypes "github.com/hyperledger/burrow/permission" "github.com/hyperledger/burrow/txs" "github.com/tendermint/go-wire" - "github.com/tendermint/merkleeyes/iavl" + "github.com/tendermint/iavl" dbm "github.com/tendermint/tmlibs/db" - "github.com/tendermint/tmlibs/merkle" ) -var ( - stateKey = []byte("ExecutionState") - defaultAccountsCacheCapacity = 1000 // TODO adjust -) +const ( + defaultCacheCapacity = 1024 -// TODO -const GasLimit = uint64(1000000) + // Version by state hash + versionPrefix = "v/" -//----------------------------------------------------------------------------- + // Prefix of keys in state tree + accountsPrefix = "a/" + storagePrefix = "s/" + nameRegPrefix = "n/" +) -// NOTE: not goroutine-safe. -type State struct { - sync.RWMutex - db dbm.DB - accounts merkle.Tree // Shouldn't be accessed directly. - nameReg merkle.Tree // Shouldn't be accessed directly. - lastSavedHash []byte - logger logging_types.InfoTraceLogger -} +var ( + accountsStart, accountsEnd []byte = prefixKeyRange(accountsPrefix) + storageStart, storageEnd []byte = prefixKeyRange(storagePrefix) + nameRegStart, nameRegEnd []byte = prefixKeyRange(nameRegPrefix) +) // Implements account and blockchain state var _ acm.Updater = &State{} var _ acm.StateIterable = &State{} var _ acm.StateWriter = &State{} -type PersistedState struct { - AccountsRootHash []byte - NameRegHash []byte +type State struct { + sync.RWMutex + db dbm.DB + version uint64 + // TODO: + tree *iavl.VersionedTree + logger logging_types.InfoTraceLogger } -func newState(db dbm.DB) *State { +func NewState(db dbm.DB) *State { return &State{ - db: db, - accounts: iavl.NewIAVLTree(defaultAccountsCacheCapacity, db), - nameReg: iavl.NewIAVLTree(0, db), - } -} - -func LoadOrMakeGenesisState(db dbm.DB, genesisDoc *genesis.GenesisDoc, - logger logging_types.InfoTraceLogger) (*State, error) { - - logger = logging.WithScope(logger, "LoadOrMakeGenesisState") - logging.InfoMsg(logger, "Trying to load execution state from database", - "database_key", stateKey) - state, err := LoadState(db) - if err != nil { - return nil, fmt.Errorf("error loading genesis state from database: %v", err) + db: db, + tree: iavl.NewVersionedTree(defaultCacheCapacity, db), } - if state != nil { - return state, nil - } - - logging.InfoMsg(logger, "No existing execution state found in database, making genesis state") - return MakeGenesisState(db, genesisDoc) } // Make genesis state from GenesisDoc and save to DB @@ -98,7 +78,7 @@ func MakeGenesisState(db dbm.DB, genesisDoc *genesis.GenesisDoc) (*State, error) return nil, fmt.Errorf("the genesis file has no validators") } - state := newState(db) + state := NewState(db) if genesisDoc.GenesisTime.IsZero() { // NOTE: [ben] change GenesisTime to requirement on v0.17 @@ -117,11 +97,10 @@ func MakeGenesisState(db dbm.DB, genesisDoc *genesis.GenesisDoc) (*State, error) Balance: genAcc.Amount, Permissions: perm, } - encodedAcc, err := acc.Encode() + err := state.UpdateAccount(acc.Account()) if err != nil { return nil, err } - state.accounts.Set(acc.Address.Bytes(), encodedAcc) } // global permissions are saved as the 0 address @@ -137,11 +116,10 @@ func MakeGenesisState(db dbm.DB, genesisDoc *genesis.GenesisDoc) (*State, error) Balance: 1337, Permissions: globalPerms, } - encodedPermsAcc, err := permsAcc.Encode() + err := state.UpdateAccount(permsAcc.Account()) if err != nil { return nil, err } - state.accounts.Set(permsAcc.Address.Bytes(), encodedPermsAcc) // IAVLTrees must be persisted before copy operations. err = state.Save() @@ -153,21 +131,21 @@ func MakeGenesisState(db dbm.DB, genesisDoc *genesis.GenesisDoc) (*State, error) } // Tries to load the execution state from DB, returns nil with no error if no state found -func LoadState(db dbm.DB) (*State, error) { - state := newState(db) - buf := db.Get(stateKey) - if len(buf) == 0 { - return nil, nil +func LoadState(db dbm.DB, hash []byte) (*State, error) { + versionBytes := db.Get(prefixedKey(versionPrefix, hash)) + if versionBytes == nil { + return nil, fmt.Errorf("could not retrieve version corresponding to state hash '%X' in database", hash) } - persistedState, err := Decode(buf) + state := NewState(db) + state.version = binary.GetUint64BE(versionBytes) + err := state.tree.Load() if err != nil { - return nil, err + return nil, fmt.Errorf("could not load versioned state tree") } - state.accounts.Load(persistedState.AccountsRootHash) - state.nameReg.Load(persistedState.NameRegHash) - err = state.Save() - if err != nil { - return nil, err + + if state.tree.LatestVersion() != state.version { + return nil, fmt.Errorf("state tree version %v expected for state hash %X but latest state tree version "+ + "loaded is %v", state.version, hash, state.tree.LatestVersion()) } return state, nil } @@ -175,74 +153,29 @@ func LoadState(db dbm.DB) (*State, error) { func (s *State) Save() error { s.Lock() defer s.Unlock() - s.accounts.Save() - s.nameReg.Save() - encodedState, err := s.Encode() + s.version++ + hash, err := s.tree.SaveVersion(s.version) if err != nil { return err } - s.db.SetSync(stateKey, encodedState) - s.lastSavedHash = s.hash() + versionBytes := make([]byte, 8) + binary.PutUint64BE(versionBytes, s.version) + s.db.SetSync(prefixedKey(versionPrefix, hash), versionBytes) return nil } -func (s *State) LastSavedHash() []byte { - return s.lastSavedHash -} - -func (s *State) Encode() ([]byte, error) { - persistedState := &PersistedState{ - AccountsRootHash: s.accounts.Hash(), - NameRegHash: s.nameReg.Hash(), - } - encodedState, err := json.Marshal(persistedState) - if err != nil { - return nil, err - } - return encodedState, nil -} - -func Decode(encodedState []byte) (*PersistedState, error) { - persistedState := new(PersistedState) - err := json.Unmarshal(encodedState, persistedState) - if err != nil { - return nil, err - } - return persistedState, nil -} - -// CONTRACT: -// Copy() is a cheap way to take a snapshot, -// as if State were copied by value. -// TODO [Silas]: Kill this with fire it is totally broken - there is no safe way to copy IAVLTree while sharing database -func (s *State) copy() *State { - return &State{ - db: s.db, - accounts: s.accounts.Copy(), - nameReg: s.nameReg.Copy(), - } -} - // Computes the state hash, also computed on save where it is returned func (s *State) Hash() []byte { s.RLock() defer s.RUnlock() - return s.hash() -} - -// As Hash without lock -func (s *State) hash() []byte { - return merkle.SimpleHashFromMap(map[string]interface{}{ - "Accounts": s.accounts, - "NameRegistry": s.nameReg, - }) + return s.tree.Hash() } // Returns nil if account does not exist with given address. func (s *State) GetAccount(address acm.Address) (acm.Account, error) { s.RLock() defer s.RUnlock() - _, accBytes, _ := s.accounts.Get(address.Bytes()) + _, accBytes := s.tree.Get(prefixedKey(accountsPrefix, address.Bytes())) if accBytes == nil { return nil, nil } @@ -250,39 +183,31 @@ func (s *State) GetAccount(address acm.Address) (acm.Account, error) { } func (s *State) UpdateAccount(account acm.Account) error { - // TODO: interop with StateCache by performing an update on the StorageRoot here if storage is dirty - // we need `dirtyStorage map[acm.Address]bool` - //if dirtyStorage[account] == true { - // s.accountStorage(account.Address()) - // := acm.AsMutableAccount(account).SetStorageRoot() - //} s.Lock() defer s.Unlock() - encodedAccount, err := account.Encode() + // TODO: find a way to implement something equivalent to this so we can set the account StorageRoot + //storageRoot := s.tree.SubTreeHash(prefixedKey(storagePrefix, account.Address().Bytes())) + // Alternatively just abandon and + accountWithStorageRoot := acm.AsMutableAccount(account).SetStorageRoot(nil) + encodedAccount, err := accountWithStorageRoot.Encode() if err != nil { return err } - s.accounts.Set(account.Address().Bytes(), encodedAccount) + s.tree.Set(prefixedKey(accountsPrefix, account.Address().Bytes()), encodedAccount) return nil } func (s *State) RemoveAccount(address acm.Address) error { s.Lock() defer s.Unlock() - s.accounts.Remove(address.Bytes()) + s.tree.Remove(prefixedKey(accountsPrefix, address.Bytes())) return nil } -// This does not give a true independent copy since the underlying database is shared and any save calls all copies -// to become invalid and using them may cause panics -func (s *State) GetAccounts() merkle.Tree { - return s.accounts.Copy() -} - func (s *State) IterateAccounts(consumer func(acm.Account) (stop bool)) (stopped bool, err error) { s.RLock() defer s.RUnlock() - stopped = s.accounts.Iterate(func(key, value []byte) bool { + stopped = s.tree.IterateRange(accountsStart, accountsEnd, true, func(key, value []byte) bool { var account acm.Account account, err = acm.Decode(value) if err != nil { @@ -293,62 +218,26 @@ func (s *State) IterateAccounts(consumer func(acm.Account) (stop bool)) (stopped return } -//------------------------------------- -// State.storage - -func (s *State) accountStorage(address acm.Address) (merkle.Tree, error) { - account, err := s.GetAccount(address) - if err != nil { - return nil, err - } - if account == nil { - return nil, fmt.Errorf("could not find account %s to access its storage", address) - } - return s.LoadStorage(account.StorageRoot()), nil -} - -func (s *State) LoadStorage(hash []byte) merkle.Tree { - s.RLock() - defer s.RUnlock() - storage := iavl.NewIAVLTree(1024, s.db) - storage.Load(hash) - return storage -} - func (s *State) GetStorage(address acm.Address, key binary.Word256) (binary.Word256, error) { s.RLock() defer s.RUnlock() - storageTree, err := s.accountStorage(address) - if err != nil { - return binary.Zero256, err - } - _, value, _ := storageTree.Get(key.Bytes()) + _, value := s.tree.Get(prefixedKey(storagePrefix, address.Bytes(), key.Bytes())) return binary.LeftPadWord256(value), nil } func (s *State) SetStorage(address acm.Address, key, value binary.Word256) error { - // TODO: not sure this actually works - loading at old hash s.Lock() defer s.Unlock() - storageTree, err := s.accountStorage(address) - if err != nil { - return err - } - if storageTree != nil { - storageTree.Set(key.Bytes(), value.Bytes()) - } + s.tree.Set(prefixedKey(storagePrefix, address.Bytes(), key.Bytes()), value.Bytes()) return nil } func (s *State) IterateStorage(address acm.Address, consumer func(key, value binary.Word256) (stop bool)) (stopped bool, err error) { + s.RLock() + defer s.RUnlock() - var storageTree merkle.Tree - storageTree, err = s.accountStorage(address) - if err != nil { - return - } - stopped = storageTree.Iterate(func(key []byte, value []byte) (stop bool) { + stopped = s.tree.IterateRange(storageStart, storageEnd, true, func(key []byte, value []byte) (stop bool) { // Note: no left padding should occur unless there is a bug and non-words have been writte to this storage tree if len(key) != binary.Word256Length { err = fmt.Errorf("key '%X' stored for account %s is not a %v-byte word", @@ -372,7 +261,7 @@ func (s *State) IterateStorage(address acm.Address, var _ NameRegIterable = &State{} func (s *State) GetNameRegEntry(name string) (*NameRegEntry, error) { - _, valueBytes, _ := s.nameReg.Get([]byte(name)) + _, valueBytes := s.tree.Get(prefixedKey(nameRegPrefix, []byte(name))) if valueBytes == nil { return nil, nil } @@ -381,18 +270,11 @@ func (s *State) GetNameRegEntry(name string) (*NameRegEntry, error) { } func (s *State) IterateNameRegEntries(consumer func(*NameRegEntry) (stop bool)) (stopped bool, err error) { - return s.nameReg.Iterate(func(key []byte, value []byte) (stop bool) { + return s.tree.IterateRange(nameRegStart, nameRegEnd, true, func(key []byte, value []byte) (stop bool) { return consumer(DecodeNameRegEntry(value)) }), nil } -func DecodeNameRegEntry(entryBytes []byte) *NameRegEntry { - var n int - var err error - value := NameRegDecode(bytes.NewBuffer(entryBytes), &n, &err) - return value.(*NameRegEntry) -} - func (s *State) UpdateNameRegEntry(entry *NameRegEntry) error { w := new(bytes.Buffer) var n int @@ -401,15 +283,32 @@ func (s *State) UpdateNameRegEntry(entry *NameRegEntry) error { if err != nil { return err } - s.nameReg.Set([]byte(entry.Name), w.Bytes()) + s.tree.Set(prefixedKey(nameRegPrefix, []byte(entry.Name)), w.Bytes()) return nil } func (s *State) RemoveNameRegEntry(name string) error { - s.nameReg.Remove([]byte(name)) + s.tree.Remove(prefixedKey(nameRegPrefix, []byte(name))) return nil } +// Creates a copy of the database to the supplied db +func (s *State) Copy(db dbm.DB) *State { + state := NewState(db) + s.tree.Iterate(func(key []byte, value []byte) bool { + state.tree.Set(key, value) + return false + }) + return state +} + +func DecodeNameRegEntry(entryBytes []byte) *NameRegEntry { + var n int + var err error + value := NameRegDecode(bytes.NewBuffer(entryBytes), &n, &err) + return value.(*NameRegEntry) +} + func NameRegEncode(o interface{}, w io.Writer, n *int, err *error) { wire.WriteBinary(o.(*NameRegEntry), w, n, err) } @@ -417,3 +316,27 @@ func NameRegEncode(o interface{}, w io.Writer, n *int, err *error) { func NameRegDecode(r io.Reader, n *int, err *error) interface{} { return wire.ReadBinary(&NameRegEntry{}, r, txs.MaxDataLength, n, err) } + +func prefixedKey(prefix string, suffices ...[]byte) []byte { + key := []byte(prefix) + for _, suffix := range suffices { + key = append(key, suffix...) + } + return key +} + +// Returns the start key equal to the bytes of prefix and the end key which lexicographically above any key beginning +// with prefix +func prefixKeyRange(prefix string) (start, end []byte) { + start = []byte(prefix) + for i := len(start) - 1; i >= 0; i-- { + c := start[i] + if c < 0xff { + end = make([]byte, i+1) + copy(end, start) + end[i]++ + return + } + } + return +} diff --git a/execution/state_test.go b/execution/state_test.go index e1844a998665190c41681964705672c334c5cd75..fb310574d557ffdb4b96abc0b07631678859c176 100644 --- a/execution/state_test.go +++ b/execution/state_test.go @@ -15,993 +15,18 @@ package execution import ( - "bytes" - "encoding/hex" "testing" - "fmt" - - "github.com/hyperledger/burrow/execution/evm/sha3" - "github.com/stretchr/testify/require" - - "time" - acm "github.com/hyperledger/burrow/account" - "github.com/hyperledger/burrow/binary" - bcm "github.com/hyperledger/burrow/blockchain" - "github.com/hyperledger/burrow/event" - "github.com/hyperledger/burrow/genesis" - "github.com/hyperledger/burrow/txs" - "github.com/stretchr/testify/assert" - dbm "github.com/tendermint/tmlibs/db" + "github.com/stretchr/testify/require" + "github.com/tendermint/tmlibs/db" ) -var deterministicGenesis = genesis.NewDeterministicGenesis(34059836243380576) -var testGenesisDoc, testPrivAccounts, _ = deterministicGenesis. - GenesisDoc(3, true, 1000, 1, true, 1000) -var testChainID = testGenesisDoc.ChainID() - -func execTxWithStateAndBlockchain(state *State, tip bcm.Tip, tx txs.Tx) error { - exe := newExecutor(true, state, testChainID, tip, event.NewNoOpPublisher(), logger) - if err := exe.Execute(tx); err != nil { - return err - } else { - exe.stateCache.Flush(exe.state) - return nil - } -} - -func execTxWithState(state *State, tx txs.Tx) error { - return execTxWithStateAndBlockchain(state, bcm.NewBlockchain(nil, testGenesisDoc), tx) -} - -func commitNewBlock(state *State, blockchain bcm.MutableBlockchain) { - blockchain.CommitBlock(blockchain.LastBlockTime().Add(time.Second), sha3.Sha3(blockchain.LastBlockHash()), - state.Hash()) -} - -func execTxWithStateNewBlock(state *State, blockchain bcm.MutableBlockchain, tx txs.Tx) error { - if err := execTxWithStateAndBlockchain(state, blockchain, tx); err != nil { - return err - } - commitNewBlock(state, blockchain) - return nil -} - -func makeGenesisState(numAccounts int, randBalance bool, minBalance uint64, numValidators int, randBonded bool, - minBonded int64) (*State, []acm.PrivateAccount) { - testGenesisDoc, privAccounts, _ := deterministicGenesis.GenesisDoc(numAccounts, randBalance, minBalance, - numValidators, randBonded, minBonded) - s0, err := MakeGenesisState(dbm.NewMemDB(), testGenesisDoc) - if err != nil { - panic(fmt.Errorf("could not make genesis state: %v", err)) - } - s0.Save() - return s0, privAccounts -} - -func getAccount(state acm.Getter, address acm.Address) acm.MutableAccount { - acc, _ := acm.GetMutableAccount(state, address) - return acc -} - -func addressPtr(account acm.Account) *acm.Address { - if account == nil { - return nil - } - accountAddresss := account.Address() - return &accountAddresss -} - -// Tests - -func TestCopyState(t *testing.T) { - // Generate a random state - s0, privAccounts := makeGenesisState(10, true, 1000, 5, true, 1000) - s0Hash := s0.Hash() - if len(s0Hash) == 0 { - t.Error("Expected state hash") - } - - // Check hash of copy - s0Copy := s0.copy() - assert.Equal(t, s0Hash, s0Copy.Hash(), "Expected state copy hash to be the same") - assert.Equal(t, s0Copy.copy().Hash(), s0Copy.Hash(), "Expected COPY COPY COPY the same") - - // Mutate the original; hash should change. - acc0Address := privAccounts[0].Address() - acc := getAccount(s0, acc0Address) - acc.AddToBalance(1) - - // The account balance shouldn't have changed yet. - if getAccount(s0, acc0Address).Balance() == acc.Balance() { - t.Error("Account balance changed unexpectedly") - } - - // Setting, however, should change the balance. - s0.UpdateAccount(acc) - if getAccount(s0, acc0Address).Balance() != acc.Balance() { - t.Error("Account balance wasn't set") - } - - // Now that the state changed, the hash should change too. - if bytes.Equal(s0Hash, s0.Hash()) { - t.Error("Expected state hash to have changed") - } - - // The s0Copy shouldn't have changed though. - if !bytes.Equal(s0Hash, s0Copy.Hash()) { - t.Error("Expected state copy hash to have not changed") - } -} - -/* -func makeBlock(t *testing.T, state *State, validation *tmtypes.Commit, txs []txs.Tx) *tmtypes.Block { - if validation == nil { - validation = &tmtypes.Commit{} - } - block := &tmtypes.Block{ - Header: &tmtypes.Header{ - testChainID: testChainID, - Height: blockchain.LastBlockHeight() + 1, - Time: state.lastBlockTime.Add(time.Minute), - NumTxs: len(txs), - lastBlockAppHash: state.lastBlockAppHash, - LastBlockParts: state.LastBlockParts, - AppHash: nil, - }, - LastCommit: validation, - Data: &tmtypes.Data{ - Txs: txs, - }, - } - block.FillHeader() - - // Fill in block StateHash - err := state.ComputeBlockStateHash(block) - if err != nil { - t.Error("Error appending initial block:", err) - } - if len(block.Header.StateHash) == 0 { - t.Error("Expected StateHash but got nothing.") - } - - return block -} - -func TestGenesisSaveLoad(t *testing.T) { - - // Generate a state, save & load it. - s0, _, _ := makeGenesisState(10, true, 1000, 5, true, 1000) - - // Make complete block and blockParts - block := makeBlock(t, s0, nil, nil) - blockParts := block.MakePartSet() - - // Now append the block to s0. - err := ExecBlock(s0, block, blockParts.Header()) - if err != nil { - t.Error("Error appending initial block:", err) - } - - // Save s0 - s0.Save() - - // Sanity check s0 - //s0.db.(*dbm.MemDB).Print() - if s0.BondedValidators.TotalVotingPower() == 0 { - t.Error("s0 BondedValidators TotalVotingPower should not be 0") - } - if s0.lastBlockHeight != 1 { - t.Error("s0 lastBlockHeight should be 1, got", s0.lastBlockHeight) - } - - // Load s1 - s1 := LoadState(s0.db) - - // Compare height & blockHash - if s0.lastBlockHeight != s1.lastBlockHeight { - t.Error("lastBlockHeight mismatch") - } - if !bytes.Equal(s0.lastBlockAppHash, s1.lastBlockAppHash) { - t.Error("lastBlockAppHash mismatch") - } - - // Compare state merkle trees - if s0.BondedValidators.Size() != s1.BondedValidators.Size() { - t.Error("BondedValidators Size mismatch") - } - if s0.BondedValidators.TotalVotingPower() != s1.BondedValidators.TotalVotingPower() { - t.Error("BondedValidators TotalVotingPower mismatch") - } - if !bytes.Equal(s0.BondedValidators.Hash(), s1.BondedValidators.Hash()) { - t.Error("BondedValidators hash mismatch") - } - if s0.UnbondingValidators.Size() != s1.UnbondingValidators.Size() { - t.Error("UnbondingValidators Size mismatch") - } - if s0.UnbondingValidators.TotalVotingPower() != s1.UnbondingValidators.TotalVotingPower() { - t.Error("UnbondingValidators TotalVotingPower mismatch") - } - if !bytes.Equal(s0.UnbondingValidators.Hash(), s1.UnbondingValidators.Hash()) { - t.Error("UnbondingValidators hash mismatch") - } - if !bytes.Equal(s0.accounts.Hash(), s1.accounts.Hash()) { - t.Error("Accounts mismatch") - } - if !bytes.Equal(s0.validatorInfos.Hash(), s1.validatorInfos.Hash()) { - t.Error("Accounts mismatch") - } -} -*/ - -func TestTxSequence(t *testing.T) { - - state, privAccounts := makeGenesisState(3, true, 1000, 1, true, 1000) - acc0 := getAccount(state, privAccounts[0].Address()) - acc0PubKey := privAccounts[0].PublicKey() - acc1 := getAccount(state, privAccounts[1].Address()) - - // Test a variety of sequence numbers for the tx. - // The tx should only pass when i == 1. - for i := uint64(0); i < 3; i++ { - sequence := acc0.Sequence() + i - tx := txs.NewSendTx() - tx.AddInputWithSequence(acc0PubKey, 1, sequence) - tx.AddOutput(acc1.Address(), 1) - tx.Inputs[0].Signature = acm.ChainSign(privAccounts[0], testChainID, tx) - stateCopy := state.copy() - err := execTxWithState(stateCopy, tx) - if i == 1 { - // Sequence is good. - if err != nil { - t.Errorf("Expected good sequence to pass: %v", err) - } - // Check acc.Sequence(). - newAcc0 := getAccount(stateCopy, acc0.Address()) - if newAcc0.Sequence() != sequence { - t.Errorf("Expected account sequence to change to %v, got %v", - sequence, newAcc0.Sequence()) - } - } else { - // Sequence is bad. - if err == nil { - t.Errorf("Expected bad sequence to fail") - } - // Check acc.Sequence(). (shouldn't have changed) - newAcc0 := getAccount(stateCopy, acc0.Address()) - if newAcc0.Sequence() != acc0.Sequence() { - t.Errorf("Expected account sequence to not change from %v, got %v", - acc0.Sequence(), newAcc0.Sequence()) - } - } - } -} - -func TestNameTxs(t *testing.T) { - state, err := MakeGenesisState(dbm.NewMemDB(), testGenesisDoc) - require.NoError(t, err) - state.Save() - - txs.MinNameRegistrationPeriod = 5 - blockchain := bcm.NewBlockchain(nil, testGenesisDoc) - startingBlock := blockchain.LastBlockHeight() - - // try some bad names. these should all fail - names := []string{"", "\n", "123#$%", "\x00", string([]byte{20, 40, 60, 80}), - "baffledbythespectacleinallofthisyouseeehesaidwithouteyessurprised", "no spaces please"} - data := "something about all this just doesn't feel right." - fee := uint64(1000) - numDesiredBlocks := uint64(5) - for _, name := range names { - amt := fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier* - txs.NameBaseCost(name, data) - tx, _ := txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee) - tx.Sign(testChainID, testPrivAccounts[0]) - - if err := execTxWithState(state, tx); err == nil { - t.Fatalf("Expected invalid name error from %s", name) - } - } - - // try some bad data. these should all fail - name := "hold_it_chum" - datas := []string{"cold&warm", "!@#$%^&*()", "<<<>>>>", "because why would you ever need a ~ or a & or even a % in a json file? make your case and we'll talk"} - for _, data := range datas { - amt := fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier* - txs.NameBaseCost(name, data) - tx, _ := txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee) - tx.Sign(testChainID, testPrivAccounts[0]) - - if err := execTxWithState(state, tx); err == nil { - t.Fatalf("Expected invalid data error from %s", data) - } - } - - validateEntry := func(t *testing.T, entry *NameRegEntry, name, data string, addr acm.Address, expires uint64) { - - if entry == nil { - t.Fatalf("Could not find name %s", name) - } - if entry.Owner != addr { - t.Fatalf("Wrong owner. Got %s expected %s", entry.Owner, addr) - } - if data != entry.Data { - t.Fatalf("Wrong data. Got %s expected %s", entry.Data, data) - } - if name != entry.Name { - t.Fatalf("Wrong name. Got %s expected %s", entry.Name, name) - } - if expires != entry.Expires { - t.Fatalf("Wrong expiry. Got %d, expected %d", entry.Expires, expires) - } - } - - // try a good one, check data, owner, expiry - name = "@looking_good/karaoke_bar.broadband" - data = "on this side of neptune there are 1234567890 people: first is OMNIVORE+-3. Or is it. Ok this is pretty restrictive. No exclamations :(. Faces tho :')" - amt := fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*txs.NameBaseCost(name, data) - tx, _ := txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee) - tx.Sign(testChainID, testPrivAccounts[0]) - if err := execTxWithState(state, tx); err != nil { - t.Fatal(err) - } - entry, err := state.GetNameRegEntry(name) - require.NoError(t, err) - validateEntry(t, entry, name, data, testPrivAccounts[0].Address(), startingBlock+numDesiredBlocks) - - // fail to update it as non-owner, in same block - tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee) - tx.Sign(testChainID, testPrivAccounts[1]) - if err := execTxWithState(state, tx); err == nil { - t.Fatal("Expected error") - } - - // update it as owner, just to increase expiry, in same block - // NOTE: we have to resend the data or it will clear it (is this what we want?) - tx, _ = txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee) - tx.Sign(testChainID, testPrivAccounts[0]) - if err := execTxWithStateNewBlock(state, blockchain, tx); err != nil { - t.Fatal(err) - } - entry, err = state.GetNameRegEntry(name) - require.NoError(t, err) - validateEntry(t, entry, name, data, testPrivAccounts[0].Address(), startingBlock+numDesiredBlocks*2) - - // update it as owner, just to increase expiry, in next block - tx, _ = txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee) - tx.Sign(testChainID, testPrivAccounts[0]) - if err := execTxWithStateNewBlock(state, blockchain, tx); err != nil { - t.Fatal(err) - } - entry, err = state.GetNameRegEntry(name) - require.NoError(t, err) - validateEntry(t, entry, name, data, testPrivAccounts[0].Address(), startingBlock+numDesiredBlocks*3) - - // fail to update it as non-owner - // Fast forward - for blockchain.Tip().LastBlockHeight() < entry.Expires-1 { - commitNewBlock(state, blockchain) - } - tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee) - tx.Sign(testChainID, testPrivAccounts[1]) - if err := execTxWithStateAndBlockchain(state, blockchain, tx); err == nil { - t.Fatal("Expected error") - } - commitNewBlock(state, blockchain) - - // once expires, non-owner succeeds - tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee) - tx.Sign(testChainID, testPrivAccounts[1]) - if err := execTxWithStateAndBlockchain(state, blockchain, tx); err != nil { - t.Fatal(err) - } - entry, err = state.GetNameRegEntry(name) - require.NoError(t, err) - validateEntry(t, entry, name, data, testPrivAccounts[1].Address(), blockchain.LastBlockHeight()+numDesiredBlocks) - - // update it as new owner, with new data (longer), but keep the expiry! - data = "In the beginning there was no thing, not even the beginning. It hadn't been here, no there, nor for that matter anywhere, not especially because it had not to even exist, let alone to not. Nothing especially odd about that." - oldCredit := amt - fee - numDesiredBlocks = 10 - amt = fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*txs.NameBaseCost(name, data) - oldCredit - tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee) - tx.Sign(testChainID, testPrivAccounts[1]) - if err := execTxWithStateAndBlockchain(state, blockchain, tx); err != nil { - t.Fatal(err) - } - entry, err = state.GetNameRegEntry(name) - require.NoError(t, err) - validateEntry(t, entry, name, data, testPrivAccounts[1].Address(), blockchain.LastBlockHeight()+numDesiredBlocks) - - // test removal - amt = fee - data = "" - tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee) - tx.Sign(testChainID, testPrivAccounts[1]) - if err := execTxWithStateNewBlock(state, blockchain, tx); err != nil { - t.Fatal(err) - } - entry, err = state.GetNameRegEntry(name) - require.NoError(t, err) - if entry != nil { - t.Fatal("Expected removed entry to be nil") - } - - // create entry by key0, - // test removal by key1 after expiry - name = "looking_good/karaoke_bar" - data = "some data" - amt = fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*txs.NameBaseCost(name, data) - tx, _ = txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee) - tx.Sign(testChainID, testPrivAccounts[0]) - if err := execTxWithStateAndBlockchain(state, blockchain, tx); err != nil { - t.Fatal(err) - } - entry, err = state.GetNameRegEntry(name) - require.NoError(t, err) - validateEntry(t, entry, name, data, testPrivAccounts[0].Address(), blockchain.LastBlockHeight()+numDesiredBlocks) - // Fast forward - for blockchain.Tip().LastBlockHeight() < entry.Expires { - commitNewBlock(state, blockchain) - } - - amt = fee - data = "" - tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee) - tx.Sign(testChainID, testPrivAccounts[1]) - if err := execTxWithStateNewBlock(state, blockchain, tx); err != nil { - t.Fatal(err) - } - entry, err = state.GetNameRegEntry(name) +func TestState_UpdateAccount(t *testing.T) { + state := NewState(db.NewMemDB()) + account := acm.NewConcreteAccountFromSecret("Foo").MutableAccount() + account.SetStorageRoot([]byte{2, 3, 4}) + state.UpdateAccount(account) + err := state.Save() require.NoError(t, err) - if entry != nil { - t.Fatal("Expected removed entry to be nil") - } -} - -// Test creating a contract from futher down the call stack -/* -contract Factory { - address a; - function create() returns (address){ - a = new PreFactory(); - return a; - } -} - -contract PreFactory{ - address a; - function create(Factory c) returns (address) { - a = c.create(); - return a; - } -} -*/ - -// run-time byte code for each of the above -var preFactoryCode, _ = hex.DecodeString("60606040526000357C0100000000000000000000000000000000000000000000000000000000900480639ED933181461003957610037565B005B61004F600480803590602001909190505061007B565B604051808273FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16815260200191505060405180910390F35B60008173FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1663EFC81A8C604051817C01000000000000000000000000000000000000000000000000000000000281526004018090506020604051808303816000876161DA5A03F1156100025750505060405180519060200150600060006101000A81548173FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02191690830217905550600060009054906101000A900473FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16905061013C565B91905056") -var factoryCode, _ = hex.DecodeString("60606040526000357C010000000000000000000000000000000000000000000000000000000090048063EFC81A8C146037576035565B005B60426004805050606E565B604051808273FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16815260200191505060405180910390F35B6000604051610153806100E0833901809050604051809103906000F0600060006101000A81548173FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02191690830217905550600060009054906101000A900473FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16905060DD565B90566060604052610141806100126000396000F360606040526000357C0100000000000000000000000000000000000000000000000000000000900480639ED933181461003957610037565B005B61004F600480803590602001909190505061007B565B604051808273FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16815260200191505060405180910390F35B60008173FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1663EFC81A8C604051817C01000000000000000000000000000000000000000000000000000000000281526004018090506020604051808303816000876161DA5A03F1156100025750505060405180519060200150600060006101000A81548173FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02191690830217905550600060009054906101000A900473FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16905061013C565B91905056") -var createData, _ = hex.DecodeString("9ed93318") - -func TestCreates(t *testing.T) { - //evm.SetDebug(true) - state, privAccounts := makeGenesisState(3, true, 1000, 1, true, 1000) - - //val0 := state.GetValidatorInfo(privValidators[0].Address()) - acc0 := getAccount(state, privAccounts[0].Address()) - acc0PubKey := privAccounts[0].PublicKey() - acc1 := getAccount(state, privAccounts[1].Address()) - acc2 := getAccount(state, privAccounts[2].Address()) - - state = state.copy() - newAcc1 := getAccount(state, acc1.Address()) - newAcc1.SetCode(preFactoryCode) - newAcc2 := getAccount(state, acc2.Address()) - newAcc2.SetCode(factoryCode) - - state.UpdateAccount(newAcc1) - state.UpdateAccount(newAcc2) - - createData = append(createData, acc2.Address().Word256().Bytes()...) - - // call the pre-factory, triggering the factory to run a create - tx := &txs.CallTx{ - Input: &txs.TxInput{ - Address: acc0.Address(), - Amount: 1, - Sequence: acc0.Sequence() + 1, - PublicKey: acc0PubKey, - }, - Address: addressPtr(acc1), - GasLimit: 10000, - Data: createData, - } - - tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) - err := execTxWithState(state, tx) - if err != nil { - t.Errorf("Got error in executing call transaction, %v", err) - } - - acc1 = getAccount(state, acc1.Address()) - storage := state.LoadStorage(acc1.StorageRoot()) - _, firstCreatedAddress, _ := storage.Get(binary.LeftPadBytes([]byte{0}, 32)) - - acc0 = getAccount(state, acc0.Address()) - // call the pre-factory, triggering the factory to run a create - tx = &txs.CallTx{ - Input: &txs.TxInput{ - Address: acc0.Address(), - Amount: 1, - Sequence: acc0.Sequence() + 1, - PublicKey: acc0PubKey, - }, - Address: addressPtr(acc1), - GasLimit: 100000, - Data: createData, - } - - tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) - err = execTxWithState(state, tx) - if err != nil { - t.Errorf("Got error in executing call transaction, %v", err) - } - - acc1 = getAccount(state, acc1.Address()) - storage = state.LoadStorage(acc1.StorageRoot()) - _, secondCreatedAddress, _ := storage.Get(binary.LeftPadBytes([]byte{0}, 32)) - - if bytes.Equal(firstCreatedAddress, secondCreatedAddress) { - t.Errorf("Multiple contracts created with the same address!") - } -} - -/* -contract Caller { - function send(address x){ - x.send(msg.value); - } -} -*/ -var callerCode, _ = hex.DecodeString("60606040526000357c0100000000000000000000000000000000000000000000000000000000900480633e58c58c146037576035565b005b604b6004808035906020019091905050604d565b005b8073ffffffffffffffffffffffffffffffffffffffff16600034604051809050600060405180830381858888f19350505050505b5056") -var sendData, _ = hex.DecodeString("3e58c58c") - -func TestContractSend(t *testing.T) { - state, privAccounts := makeGenesisState(3, true, 1000, 1, true, 1000) - - //val0 := state.GetValidatorInfo(privValidators[0].Address()) - acc0 := getAccount(state, privAccounts[0].Address()) - acc0PubKey := privAccounts[0].PublicKey() - acc1 := getAccount(state, privAccounts[1].Address()) - acc2 := getAccount(state, privAccounts[2].Address()) - - state = state.copy() - newAcc1 := getAccount(state, acc1.Address()) - newAcc1.SetCode(callerCode) - state.UpdateAccount(newAcc1) - - sendData = append(sendData, acc2.Address().Word256().Bytes()...) - sendAmt := uint64(10) - acc2Balance := acc2.Balance() - - // call the contract, triggering the send - tx := &txs.CallTx{ - Input: &txs.TxInput{ - Address: acc0.Address(), - Amount: sendAmt, - Sequence: acc0.Sequence() + 1, - PublicKey: acc0PubKey, - }, - Address: addressPtr(acc1), - GasLimit: 1000, - Data: sendData, - } - - tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) - err := execTxWithState(state, tx) - if err != nil { - t.Errorf("Got error in executing call transaction, %v", err) - } - - acc2 = getAccount(state, acc2.Address()) - if acc2.Balance() != sendAmt+acc2Balance { - t.Errorf("Value transfer from contract failed! Got %d, expected %d", acc2.Balance(), sendAmt+acc2Balance) - } -} -func TestMerklePanic(t *testing.T) { - state, privAccounts := makeGenesisState(3, true, 1000, 1, true, - 1000) - - //val0 := state.GetValidatorInfo(privValidators[0].Address()) - acc0 := getAccount(state, privAccounts[0].Address()) - acc0PubKey := privAccounts[0].PublicKey() - acc1 := getAccount(state, privAccounts[1].Address()) - - state.Save() - // SendTx. - { - stateSendTx := state.copy() - tx := &txs.SendTx{ - Inputs: []*txs.TxInput{ - { - Address: acc0.Address(), - Amount: 1, - Sequence: acc0.Sequence() + 1, - PublicKey: acc0PubKey, - }, - }, - Outputs: []*txs.TxOutput{ - { - Address: acc1.Address(), - Amount: 1, - }, - }, - } - - tx.Inputs[0].Signature = acm.ChainSign(privAccounts[0], testChainID, tx) - err := execTxWithState(stateSendTx, tx) - if err != nil { - t.Errorf("Got error in executing send transaction, %v", err) - } - // uncomment for panic fun! - //stateSendTx.Save() - } - - // CallTx. Just runs through it and checks the transfer. See vm, rpc tests for more - { - stateCallTx := state.copy() - newAcc1 := getAccount(stateCallTx, acc1.Address()) - newAcc1.SetCode([]byte{0x60}) - stateCallTx.UpdateAccount(newAcc1) - tx := &txs.CallTx{ - Input: &txs.TxInput{ - Address: acc0.Address(), - Amount: 1, - Sequence: acc0.Sequence() + 1, - PublicKey: acc0PubKey, - }, - Address: addressPtr(acc1), - GasLimit: 10, - } - - tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) - err := execTxWithState(stateCallTx, tx) - if err != nil { - t.Errorf("Got error in executing call transaction, %v", err) - } - } - state.Save() - trygetacc0 := getAccount(state, privAccounts[0].Address()) - fmt.Println(trygetacc0.Address()) -} - -// TODO: test overflows. -// TODO: test for unbonding validators. -func TestTxs(t *testing.T) { - state, privAccounts := makeGenesisState(3, true, 1000, 1, true, 1000) - - //val0 := state.GetValidatorInfo(privValidators[0].Address()) - acc0 := getAccount(state, privAccounts[0].Address()) - acc0PubKey := privAccounts[0].PublicKey() - acc1 := getAccount(state, privAccounts[1].Address()) - - // SendTx. - { - stateSendTx := state.copy() - tx := &txs.SendTx{ - Inputs: []*txs.TxInput{ - { - Address: acc0.Address(), - Amount: 1, - Sequence: acc0.Sequence() + 1, - PublicKey: acc0PubKey, - }, - }, - Outputs: []*txs.TxOutput{ - { - Address: acc1.Address(), - Amount: 1, - }, - }, - } - - tx.Inputs[0].Signature = acm.ChainSign(privAccounts[0], testChainID, tx) - err := execTxWithState(stateSendTx, tx) - if err != nil { - t.Errorf("Got error in executing send transaction, %v", err) - } - newAcc0 := getAccount(stateSendTx, acc0.Address()) - if acc0.Balance()-1 != newAcc0.Balance() { - t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v", - acc0.Balance()-1, newAcc0.Balance()) - } - newAcc1 := getAccount(stateSendTx, acc1.Address()) - if acc1.Balance()+1 != newAcc1.Balance() { - t.Errorf("Unexpected newAcc1 balance. Expected %v, got %v", - acc1.Balance()+1, newAcc1.Balance()) - } - } - - // CallTx. Just runs through it and checks the transfer. See vm, rpc tests for more - { - stateCallTx := state.copy() - newAcc1 := getAccount(stateCallTx, acc1.Address()) - newAcc1.SetCode([]byte{0x60}) - stateCallTx.UpdateAccount(newAcc1) - tx := &txs.CallTx{ - Input: &txs.TxInput{ - Address: acc0.Address(), - Amount: 1, - Sequence: acc0.Sequence() + 1, - PublicKey: acc0PubKey, - }, - Address: addressPtr(acc1), - GasLimit: 10, - } - - tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) - err := execTxWithState(stateCallTx, tx) - if err != nil { - t.Errorf("Got error in executing call transaction, %v", err) - } - newAcc0 := getAccount(stateCallTx, acc0.Address()) - if acc0.Balance()-1 != newAcc0.Balance() { - t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v", - acc0.Balance()-1, newAcc0.Balance()) - } - newAcc1 = getAccount(stateCallTx, acc1.Address()) - if acc1.Balance()+1 != newAcc1.Balance() { - t.Errorf("Unexpected newAcc1 balance. Expected %v, got %v", - acc1.Balance()+1, newAcc1.Balance()) - } - } - trygetacc0 := getAccount(state, privAccounts[0].Address()) - fmt.Println(trygetacc0.Address()) - - // NameTx. - { - entryName := "satoshi" - entryData := ` -A purely peer-to-peer version of electronic cash would allow online -payments to be sent directly from one party to another without going through a -financial institution. Digital signatures provide part of the solution, but the main -benefits are lost if a trusted third party is still required to prevent double-spending. -We propose a solution to the double-spending problem using a peer-to-peer network. -The network timestamps transactions by hashing them into an ongoing chain of -hash-based proof-of-work, forming a record that cannot be changed without redoing -the proof-of-work. The longest chain not only serves as proof of the sequence of -events witnessed, but proof that it came from the largest pool of CPU power. As -long as a majority of CPU power is controlled by nodes that are not cooperating to -attack the network, they'll generate the longest chain and outpace attackers. The -network itself requires minimal structure. Messages are broadcast on a best effort -basis, and nodes can leave and rejoin the network at will, accepting the longest -proof-of-work chain as proof of what happened while they were gone ` - entryAmount := uint64(10000) - - stateNameTx := state.copy() - tx := &txs.NameTx{ - Input: &txs.TxInput{ - Address: acc0.Address(), - Amount: entryAmount, - Sequence: acc0.Sequence() + 1, - PublicKey: acc0PubKey, - }, - Name: entryName, - Data: entryData, - } - - tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) - - err := execTxWithState(stateNameTx, tx) - if err != nil { - t.Errorf("Got error in executing call transaction, %v", err) - } - newAcc0 := getAccount(stateNameTx, acc0.Address()) - if acc0.Balance()-entryAmount != newAcc0.Balance() { - t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v", - acc0.Balance()-entryAmount, newAcc0.Balance()) - } - entry, err := stateNameTx.GetNameRegEntry(entryName) - require.NoError(t, err) - if entry == nil { - t.Errorf("Expected an entry but got nil") - } - if entry.Data != entryData { - t.Errorf("Wrong data stored") - } - - // test a bad string - tx.Data = string([]byte{0, 1, 2, 3, 127, 128, 129, 200, 251}) - tx.Input.Sequence += 1 - tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) - err = execTxWithState(stateNameTx, tx) - if _, ok := err.(txs.ErrTxInvalidString); !ok { - t.Errorf("Expected invalid string error. Got: %s", err.Error()) - } - } - - // BondTx. TODO - /* - { - state := state.Copy() - tx := &txs.BondTx{ - PublicKey: acc0PubKey.(acm.PublicKeyEd25519), - Inputs: []*txs.TxInput{ - &txs.TxInput{ - Address: acc0.Address(), - Amount: 1, - Sequence: acc0.Sequence() + 1, - PublicKey: acc0PubKey, - }, - }, - UnbondTo: []*txs.TxOutput{ - &txs.TxOutput{ - Address: acc0.Address(), - Amount: 1, - }, - }, - } - tx.Signature = privAccounts[0] acm.ChainSign(testChainID, tx).(crypto.SignatureEd25519) - tx.Inputs[0].Signature = privAccounts[0] acm.ChainSign(testChainID, tx) - err := execTxWithState(state, tx) - if err != nil { - t.Errorf("Got error in executing bond transaction, %v", err) - } - newAcc0 := getAccount(state, acc0.Address()) - if newAcc0.Balance() != acc0.Balance()-1 { - t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v", - acc0.Balance()-1, newAcc0.Balance()) - } - _, acc0Val := state.BondedValidators.GetByAddress(acc0.Address()) - if acc0Val == nil { - t.Errorf("acc0Val not present") - } - if acc0Val.BondHeight != blockchain.LastBlockHeight()+1 { - t.Errorf("Unexpected bond height. Expected %v, got %v", - blockchain.LastBlockHeight(), acc0Val.BondHeight) - } - if acc0Val.VotingPower != 1 { - t.Errorf("Unexpected voting power. Expected %v, got %v", - acc0Val.VotingPower, acc0.Balance()) - } - if acc0Val.Accum != 0 { - t.Errorf("Unexpected accum. Expected 0, got %v", - acc0Val.Accum) - } - } */ - - // TODO UnbondTx. - -} - -func TestSelfDestruct(t *testing.T) { - - state, privAccounts := makeGenesisState(3, true, 1000, 1, true, 1000) - - acc0 := getAccount(state, privAccounts[0].Address()) - acc0PubKey := privAccounts[0].PublicKey() - acc1 := getAccount(state, privAccounts[1].Address()) - acc2 := getAccount(state, privAccounts[2].Address()) - sendingAmount, refundedBalance, oldBalance := uint64(1), acc1.Balance(), acc2.Balance() - - newAcc1 := getAccount(state, acc1.Address()) - - // store 0x1 at 0x1, push an address, then self-destruct:) - contractCode := []byte{0x60, 0x01, 0x60, 0x01, 0x55, 0x73} - contractCode = append(contractCode, acc2.Address().Bytes()...) - contractCode = append(contractCode, 0xff) - newAcc1.SetCode(contractCode) - state.UpdateAccount(newAcc1) - - // send call tx with no data, cause self-destruct - tx := txs.NewCallTxWithSequence(acc0PubKey, addressPtr(acc1), nil, sendingAmount, 1000, 0, acc0.Sequence()+1) - tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) - - // we use cache instead of execTxWithState so we can run the tx twice - exe := NewBatchCommitter(state, testChainID, bcm.NewBlockchain(nil, testGenesisDoc), event.NewNoOpPublisher(), logger) - if err := exe.Execute(tx); err != nil { - t.Errorf("Got error in executing call transaction, %v", err) - } - - // if we do it again, we won't get an error, but the self-destruct - // shouldn't happen twice and the caller should lose fee - tx.Input.Sequence += 1 - tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx) - if err := exe.Execute(tx); err != nil { - t.Errorf("Got error in executing call transaction, %v", err) - } - - // commit the block - exe.Commit() - - // acc2 should receive the sent funds and the contracts balance - newAcc2 := getAccount(state, acc2.Address()) - newBalance := sendingAmount + refundedBalance + oldBalance - if newAcc2.Balance() != newBalance { - t.Errorf("Unexpected newAcc2 balance. Expected %v, got %v", - newAcc2.Balance(), newBalance) - } - newAcc1 = getAccount(state, acc1.Address()) - if newAcc1 != nil { - t.Errorf("Expected account to be removed") - } - -} - -/* TODO -func TestAddValidator(t *testing.T) { - - // Generate a state, save & load it. - s0, privAccounts, privValidators := makeGenesisState(10, false, 1000, 1, false, 1000) - - // The first privAccount will become a validator - acc0 := privAccounts[0] - bondTx := &txs.BondTx{ - PublicKey: acc0.PublicKey.(account.PubKeyEd25519), - Inputs: []*txs.TxInput{ - &txs.TxInput{ - Address: acc0.Address(), - Amount: 1000, - Sequence: 1, - PublicKey: acc0.PublicKey, - }, - }, - UnbondTo: []*txs.TxOutput{ - &txs.TxOutput{ - Address: acc0.Address(), - Amount: 1000, - }, - }, - } - bondTx.Signature = acc0 acm.ChainSign(testChainID, bondTx).(account.SignatureEd25519) - bondTx.Inputs[0].Signature = acc0 acm.ChainSign(testChainID, bondTx) - - // Make complete block and blockParts - block0 := makeBlock(t, s0, nil, []txs.Tx{bondTx}) - block0Parts := block0.MakePartSet() - - // Sanity check - if s0.BondedValidators.Size() != 1 { - t.Error("Expected there to be 1 validators before bondTx") - } - - // Now append the block to s0. - err := ExecBlock(s0, block0, block0Parts.Header()) - if err != nil { - t.Error("Error appending initial block:", err) - } - - // Must save before further modification - s0.Save() - - // Test new validator set - if s0.BondedValidators.Size() != 2 { - t.Error("Expected there to be 2 validators after bondTx") - } - - // The validation for the next block should only require 1 signature - // (the new validator wasn't active for block0) - precommit0 := &txs.Vote{ - Height: 1, - Round: 0, - Type: txs.VoteTypePrecommit, - BlockHash: block0.Hash(), - BlockPartsHeader: block0Parts.Header(), - } - privValidators[0].SignVote(testChainID, precommit0) - - block1 := makeBlock(t, s0, - &txs.Validation{ - Precommits: []*txs.Vote{ - precommit0, - }, - }, nil, - ) - block1Parts := block1.MakePartSet() - err = ExecBlock(s0, block1, block1Parts.Header()) - if err != nil { - t.Error("Error appending secondary block:", err) - } } -*/ diff --git a/execution/transactor.go b/execution/transactor.go index eda8d5f33c5885716b200922e75934fecdf656b7..f54cd02f60a3f56029e994ab5f699032cae206dc 100644 --- a/execution/transactor.go +++ b/execution/transactor.go @@ -140,6 +140,9 @@ func (trans *transactor) BroadcastTxAsync(tx txs.Tx, callback func(res *abci_typ // Broadcast a transaction. func (trans *transactor) BroadcastTx(tx txs.Tx) (*txs.Receipt, error) { + trans.logger.Trace("method", "BroadcastTx", + "tx_hash", tx.Hash(trans.blockchain.ChainID()), + "tx", tx.String()) responseCh := make(chan *abci_types.Response, 1) err := trans.BroadcastTxAsync(tx, func(res *abci_types.Response) { responseCh <- res diff --git a/logging/loggers/info_trace_logger.go b/logging/loggers/info_trace_logger.go index 7e3923e0c7a09c242e8e6e9857402c6a6e726ce4..df51014787d13d90075847869a52f0a01b7d9955 100644 --- a/logging/loggers/info_trace_logger.go +++ b/logging/loggers/info_trace_logger.go @@ -97,6 +97,7 @@ func (l *infoTraceLogger) Log(keyvals ...interface{}) error { // Wrap the output loggers with a a set of standard transforms, a non-blocking // ChannelLogger and an outer context func wrapOutputLogger(outputLogger kitlog.Logger) (kitlog.Logger, channels.Channel) { - return NonBlockingLogger(VectorValuedLogger(SortLogger(BurrowFormatLogger(outputLogger), - structure.ChannelKey, structure.MessageKey, structure.TimeKey, structure.ComponentKey))) + //return outputLogger, channels.NewDeadChannel() + return VectorValuedLogger(SortLogger(BurrowFormatLogger(outputLogger), + structure.ChannelKey, structure.MessageKey, structure.TimeKey, structure.ComponentKey)), channels.NewDeadChannel() } diff --git a/rpc/service.go b/rpc/service.go index d84c7b70b71458ecad30bbf662c97afa378ef886..c91e7645ce90ec09fad5236c4267f5b977a14a04 100644 --- a/rpc/service.go +++ b/rpc/service.go @@ -243,7 +243,9 @@ func (s *service) GetAccount(address acm.Address) (*ResultGetAccount, error) { if err != nil { return nil, err } - s.logger.Trace("method", "GetAccount", "sequence", acc.Sequence()) + s.logger.Trace("method", "GetAccount", + "address", address, + "sequence", acc.Sequence()) return &ResultGetAccount{Account: acm.AsConcreteAccount(acc)}, nil } diff --git a/rpc/tm/integration/client_test.go b/rpc/tm/integration/client_test.go index bf62e6c7be72403145be18d2667ffafea5a69af9..a827524f9de0ac13e438dd2f8d76ad002fda442f 100644 --- a/rpc/tm/integration/client_test.go +++ b/rpc/tm/integration/client_test.go @@ -55,7 +55,7 @@ func TestBroadcastTx(t *testing.T) { amt := hashString(clientName) % 1000 toAddr := privateAccounts[1].Address() tx := makeDefaultSendTxSigned(t, client, toAddr, amt) - receipt, err := broadcastTxAndWaitForBlock(t, client, wsc, tx) + receipt, err := broadcastTxAndWait(t, client, wsc, tx) require.NoError(t, err) assert.False(t, receipt.CreatesContract, "This tx should not create a contract") assert.NotEmpty(t, receipt.TxHash, "Failed to compute tx hash") @@ -102,7 +102,7 @@ func TestGetStorage(t *testing.T) { code := []byte{0x60, 0x5, 0x60, 0x1, 0x55} // Call with nil address will create a contract tx := makeDefaultCallTx(t, client, nil, code, amt, gasLim, fee) - receipt, err := broadcastTxAndWaitForBlock(t, client, wsc, tx) + receipt, err := broadcastTxAndWait(t, client, wsc, tx) assert.NoError(t, err) assert.Equal(t, true, receipt.CreatesContract, "This transaction should"+ " create a contract") @@ -156,7 +156,7 @@ func TestCallContract(t *testing.T) { amt, gasLim, fee := uint64(6969), uint64(1000), uint64(1000) code, _, _ := simpleContract() tx := makeDefaultCallTx(t, client, nil, code, amt, gasLim, fee) - receipt, err := broadcastTxAndWaitForBlock(t, client, wsc, tx) + receipt, err := broadcastTxAndWait(t, client, wsc, tx) assert.NoError(t, err) if err != nil { t.Fatalf("Problem broadcasting transaction: %v", err) @@ -197,7 +197,7 @@ func TestNameReg(t *testing.T) { // verify the name by both using the event and by checking get_name subscribeAndWaitForNext(t, wsc, exe_events.EventStringNameReg(name), func() { - broadcastTxAndWaitForBlock(t, client, wsc, tx) + broadcastTxAndWait(t, client, wsc, tx) }, func(eventID string, resultEvent *rpc.ResultEvent) (bool, error) { @@ -224,7 +224,7 @@ func TestNameReg(t *testing.T) { amt = fee + numDesiredBlocks*txs.NameByteCostMultiplier* txs.NameBlockCostMultiplier*txs.NameBaseCost(name, updatedData) tx = makeDefaultNameTx(t, client, name, updatedData, amt, fee) - broadcastTxAndWaitForBlock(t, client, wsc, tx) + broadcastTxAndWait(t, client, wsc, tx) entry = getNameRegEntry(t, client, name) assert.Equal(t, updatedData, entry.Data) @@ -234,7 +234,7 @@ func TestNameReg(t *testing.T) { getSequence(t, client, privateAccounts[1].Address())+1) tx.Sign(genesisDoc.ChainID(), privateAccounts[1]) - _, err := broadcastTxAndWaitForBlock(t, client, wsc, tx) + _, err := broadcastTxAndWait(t, client, wsc, tx) assert.Error(t, err, "Expected error when updating someone else's unexpired"+ " name registry entry") if err != nil { @@ -250,7 +250,7 @@ func TestNameReg(t *testing.T) { tx = txs.NewNameTxWithSequence(privateAccounts[1].PublicKey(), name, data2, amt, fee, getSequence(t, client, privateAccounts[1].Address())+1) tx.Sign(genesisDoc.ChainID(), privateAccounts[1]) - _, err = broadcastTxAndWaitForBlock(t, client, wsc, tx) + _, err = broadcastTxAndWait(t, client, wsc, tx) assert.NoError(t, err, "Should be able to update a previously expired name"+ " registry entry as a different address") entry = getNameRegEntry(t, client, name) diff --git a/rpc/tm/integration/shared.go b/rpc/tm/integration/shared.go index 55b31ebc05c97b201a4d3b4ff3f6e7e7f1a3e56c..d7ad492993dc992d02412d5b61ba299fcd514d73 100644 --- a/rpc/tm/integration/shared.go +++ b/rpc/tm/integration/shared.go @@ -56,7 +56,7 @@ const ( ) // Enable logger output during tests -var debugLogging = false +var debugLogging = true // global variables for use across all tests var ( @@ -89,13 +89,16 @@ func TestWrapper(runner func() int) int { SetTransform(config.FilterTransform(config.IncludeWhenAnyMatches, //"","", "method", "GetAccount", - "message", "execution error", - "message", "Incrementing sequence number", + "method", "BroadcastTx", + "tag", "sequence", + "tag", "Commit", + "tag", "CheckTx", + "tag", "DeliverTx", )). - AddSinks(config.Sink().SetTransform(config.FilterTransform(config.ExcludeWhenAnyMatches, "run_call", "false")). - AddSinks(config.Sink().SetTransform(config.PruneTransform("log_channel", "trace", "scope", "returns", "run_id", "args")). - AddSinks(config.Sink().SetTransform(config.SortTransform("tx_hash", "time", "message", "method")). - SetOutput(config.StdoutOutput())))), + //AddSinks(config.Sink().SetTransform(config.FilterTransform(config.ExcludeWhenAnyMatches, "run_call", "false")). + AddSinks(config.Sink().SetTransform(config.PruneTransform("log_channel", "trace", "scope", "returns", "run_id", "args")). + AddSinks(config.Sink().SetTransform(config.SortTransform("tx_hash", "time", "message", "method")). + SetOutput(config.StdoutOutput()))), }) if err != nil { panic(err) diff --git a/rpc/tm/integration/websocket_client_test.go b/rpc/tm/integration/websocket_client_test.go index 8f7c974fa1154ed5970104a562ad96589b3a9d27..bc6da482db85d6cb4b5b264bca60ac37ae28f30d 100644 --- a/rpc/tm/integration/websocket_client_test.go +++ b/rpc/tm/integration/websocket_client_test.go @@ -28,7 +28,6 @@ import ( evm_events "github.com/hyperledger/burrow/execution/evm/events" "github.com/hyperledger/burrow/rpc" tm_client "github.com/hyperledger/burrow/rpc/tm/client" - "github.com/hyperledger/burrow/txs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" tm_types "github.com/tendermint/tendermint/types" @@ -150,13 +149,13 @@ func TestWSDoubleFire(t *testing.T) { // create a contract, wait for the event, and send it a msg, validate the return func TestWSCallWait(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } wsc := newWSClient() defer stopWSClient(wsc) // Mini soak test for i := 0; i < 20; i++ { - if testing.Short() { - t.Skip("skipping test in short mode.") - } amt, gasLim, fee := uint64(10000), uint64(1000), uint64(1000) code, returnCode, returnVal := simpleContract() var contractAddr acm.Address @@ -197,7 +196,7 @@ func TestWSCallNoWait(t *testing.T) { code, _, returnVal := simpleContract() tx := makeDefaultCallTx(t, jsonRpcClient, nil, code, amt, gasLim, fee) - receipt, err := broadcastTxAndWaitForBlock(t, jsonRpcClient, wsc, tx) + receipt, err := broadcastTxAndWait(t, jsonRpcClient, wsc, tx) require.NoError(t, err) contractAddr := receipt.ContractAddress @@ -223,11 +222,11 @@ func TestWSCallCall(t *testing.T) { defer stopWSClient(wsc) amt, gasLim, fee := uint64(10000), uint64(1000), uint64(1000) code, _, returnVal := simpleContract() - txid := new([]byte) + TxHash := new([]byte) // deploy the two contracts tx := makeDefaultCallTx(t, jsonRpcClient, nil, code, amt, gasLim, fee) - receipt, err := broadcastTxAndWaitForBlock(t, jsonRpcClient, wsc, tx) + receipt, err := broadcastTxAndWait(t, jsonRpcClient, wsc, tx) require.NoError(t, err) contractAddr1 := receipt.ContractAddress @@ -256,10 +255,10 @@ func TestWSCallCall(t *testing.T) { func() { tx := makeDefaultCallTx(t, jsonRpcClient, &contractAddr2, nil, amt, gasLim, fee) broadcastTx(t, jsonRpcClient, tx) - *txid = txs.TxHash(genesisDoc.ChainID(), tx) + *TxHash = tx.Hash(genesisDoc.ChainID()) }, // Event checker - unmarshalValidateCall(privateAccounts[0].Address(), returnVal, txid)) + unmarshalValidateCall(privateAccounts[0].Address(), returnVal, TxHash)) } func TestSubscribe(t *testing.T) { diff --git a/rpc/tm/integration/websocket_helpers.go b/rpc/tm/integration/websocket_helpers.go index a69cf9a675daea5ff4d2ef69a58f77f9c6d13095..ec1c7ff2cfc22e50aa1b6c4840697fe0e72a316c 100644 --- a/rpc/tm/integration/websocket_helpers.go +++ b/rpc/tm/integration/websocket_helpers.go @@ -25,6 +25,7 @@ import ( "time" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/execution/events" "github.com/hyperledger/burrow/rpc" tm_client "github.com/hyperledger/burrow/rpc/tm/client" "github.com/hyperledger/burrow/txs" @@ -94,14 +95,23 @@ func unsubscribe(t *testing.T, wsc *rpcclient.WSClient, subscriptionId string) { } // broadcast transaction and wait for new block -func broadcastTxAndWaitForBlock(t *testing.T, client tm_client.RPCClient, wsc *rpcclient.WSClient, +func broadcastTxAndWait(t *testing.T, client tm_client.RPCClient, wsc *rpcclient.WSClient, tx txs.Tx) (*txs.Receipt, error) { + inputs := tx.GetInputs() + if len(inputs) == 0 { + t.Fatalf("cannot broadcastAndWait fot Tx with no inputs") + } + address := inputs[0].Address + var rec *txs.Receipt var err error - runThenWaitForBlock(t, wsc, nextBlockPredicateFn(), + + subscribeAndWaitForNext(t, wsc, events.EventStringAccountInput(address), func() { rec, err = tm_client.BroadcastTx(client, tx) + }, func(eventID string, resultEvent *rpc.ResultEvent) (bool, error) { + return true, nil }) return rec, err } @@ -159,7 +169,6 @@ func subscribeAndWaitForNext(t *testing.T, wsc *rpcclient.WSClient, event string // waitForEvent will fail the test. func waitForEvent(t *testing.T, wsc *rpcclient.WSClient, eventID string, runner func(), checker resultEventChecker) waitForEventResult { - // go routine to wait for websocket msg eventsCh := make(chan *rpc.ResultEvent) shutdownEventsCh := make(chan bool, 1) diff --git a/txs/tx.go b/txs/tx.go index 7838e86646d61803a7a2b0ab11df6b028c79305a..1133e948718f5bcc0188c1e6127fa12f90f2ce61 100644 --- a/txs/tx.go +++ b/txs/tx.go @@ -102,6 +102,8 @@ type ( Tx interface { WriteSignBytes(chainID string, w io.Writer, n *int, err *error) String() string + GetInputs() []TxInput + Hash(chainID string) []byte } Wrapper struct { @@ -116,9 +118,17 @@ type ( DecodeTx(txBytes []byte) (Tx, error) } - SendTx struct { - Inputs []*TxInput - Outputs []*TxOutput + TxInput struct { + Address acm.Address + Amount uint64 + Sequence uint64 + Signature acm.Signature + PublicKey acm.PublicKey + } + + TxOutput struct { + Address acm.Address + Amount uint64 } // BroadcastTx or Transact @@ -128,11 +138,20 @@ type ( ContractAddress acm.Address } + //------------------- + // Transaction Types + SendTx struct { + Inputs []*TxInput + Outputs []*TxOutput + txHashMemoizer + } + NameTx struct { Input *TxInput Name string Data string Fee uint64 + txHashMemoizer } CallTx struct { @@ -142,19 +161,36 @@ type ( GasLimit uint64 Fee uint64 Data []byte + txHashMemoizer } - TxInput struct { + PermissionsTx struct { + Input *TxInput + PermArgs ptypes.PermArgs + txHashMemoizer + } + + // Out of service + BondTx struct { + PubKey acm.PublicKey + Signature acm.Signature + Inputs []*TxInput + UnbondTo []*TxOutput + txHashMemoizer + } + + UnbondTx struct { Address acm.Address - Amount uint64 - Sequence uint64 + Height int Signature acm.Signature - PublicKey acm.PublicKey + txHashMemoizer } - TxOutput struct { - Address acm.Address - Amount uint64 + RebondTx struct { + Address acm.Address + Height int + Signature acm.Signature + txHashMemoizer } ) @@ -249,10 +285,18 @@ func (tx *SendTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error wire.WriteTo([]byte(`]}]}`), w, n, err) } +func (tx *SendTx) GetInputs() []TxInput { + return copyInputs(tx.Inputs) +} + func (tx *SendTx) String() string { return fmt.Sprintf("SendTx{%v -> %v}", tx.Inputs, tx.Outputs) } +func (tx *SendTx) Hash(chainID string) []byte { + return tx.txHashMemoizer.hash(chainID, tx) +} + //----------------------------------------------------------------------------- func (tx *CallTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { @@ -263,10 +307,18 @@ func (tx *CallTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error wire.WriteTo([]byte(`}]}`), w, n, err) } +func (tx *CallTx) GetInputs() []TxInput { + return []TxInput{*tx.Input} +} + func (tx *CallTx) String() string { return fmt.Sprintf("CallTx{%v -> %s: %X}", tx.Input, tx.Address, tx.Data) } +func (tx *CallTx) Hash(chainID string) []byte { + return tx.txHashMemoizer.hash(chainID, tx) +} + //----------------------------------------------------------------------------- func (tx *NameTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { @@ -278,6 +330,10 @@ func (tx *NameTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error wire.WriteTo([]byte(`}]}`), w, n, err) } +func (tx *NameTx) GetInputs() []TxInput { + return []TxInput{*tx.Input} +} + func (tx *NameTx) ValidateStrings() error { if len(tx.Name) == 0 { return ErrTxInvalidString{"Name must not be empty"} @@ -304,15 +360,12 @@ func (tx *NameTx) String() string { return fmt.Sprintf("NameTx{%v -> %s: %s}", tx.Input, tx.Name, tx.Data) } -//----------------------------------------------------------------------------- - -type BondTx struct { - PubKey acm.PublicKey - Signature acm.Signature - Inputs []*TxInput - UnbondTo []*TxOutput +func (tx *NameTx) Hash(chainID string) []byte { + return tx.txHashMemoizer.hash(chainID, tx) } +//----------------------------------------------------------------------------- + func (tx *BondTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { wire.WriteTo([]byte(fmt.Sprintf(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err) wire.WriteTo([]byte(fmt.Sprintf(`,"tx":[%v,{"inputs":[`, TxTypeBond)), w, n, err) @@ -334,51 +387,58 @@ func (tx *BondTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error wire.WriteTo([]byte(`]}]}`), w, n, err) } +func (tx *BondTx) GetInputs() []TxInput { + return copyInputs(tx.Inputs) +} + func (tx *BondTx) String() string { return fmt.Sprintf("BondTx{%v: %v -> %v}", tx.PubKey, tx.Inputs, tx.UnbondTo) } -//----------------------------------------------------------------------------- - -type UnbondTx struct { - Address acm.Address - Height int - Signature acm.Signature +func (tx *BondTx) Hash(chainID string) []byte { + return tx.txHashMemoizer.hash(chainID, tx) } +//----------------------------------------------------------------------------- + func (tx *UnbondTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { wire.WriteTo([]byte(fmt.Sprintf(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err) wire.WriteTo([]byte(fmt.Sprintf(`,"tx":[%v,{"address":"%s","height":%v}]}`, TxTypeUnbond, tx.Address, tx.Height)), w, n, err) } +func (tx *UnbondTx) GetInputs() []TxInput { + return nil +} + func (tx *UnbondTx) String() string { return fmt.Sprintf("UnbondTx{%s,%v,%v}", tx.Address, tx.Height, tx.Signature) } -//----------------------------------------------------------------------------- - -type RebondTx struct { - Address acm.Address - Height int - Signature acm.Signature +func (tx *UnbondTx) Hash(chainID string) []byte { + return tx.txHashMemoizer.hash(chainID, tx) } +//----------------------------------------------------------------------------- + func (tx *RebondTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { wire.WriteTo([]byte(fmt.Sprintf(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err) wire.WriteTo([]byte(fmt.Sprintf(`,"tx":[%v,{"address":"%s","height":%v}]}`, TxTypeRebond, tx.Address, tx.Height)), w, n, err) } +func (tx *RebondTx) GetInputs() []TxInput { + return nil +} + func (tx *RebondTx) String() string { return fmt.Sprintf("RebondTx{%s,%v,%v}", tx.Address, tx.Height, tx.Signature) } -//----------------------------------------------------------------------------- - -type PermissionsTx struct { - Input *TxInput - PermArgs ptypes.PermArgs +func (tx *RebondTx) Hash(chainID string) []byte { + return tx.txHashMemoizer.hash(chainID, tx) } +//----------------------------------------------------------------------------- + func (tx *PermissionsTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { wire.WriteTo([]byte(fmt.Sprintf(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err) wire.WriteTo([]byte(fmt.Sprintf(`,"tx":[%v,{"args":"`, TxTypePermissions)), w, n, err) @@ -388,12 +448,34 @@ func (tx *PermissionsTx) WriteSignBytes(chainID string, w io.Writer, n *int, err wire.WriteTo([]byte(`}]}`), w, n, err) } +func (tx *PermissionsTx) GetInputs() []TxInput { + return []TxInput{*tx.Input} +} + func (tx *PermissionsTx) String() string { return fmt.Sprintf("PermissionsTx{%v -> %v}", tx.Input, tx.PermArgs) } +func (tx *PermissionsTx) Hash(chainID string) []byte { + return tx.txHashMemoizer.hash(chainID, tx) +} + //----------------------------------------------------------------------------- +// Avoid re-hashing the same in-memory Tx +type txHashMemoizer struct { + txHashBytes []byte + chainID string +} + +func (thm *txHashMemoizer) hash(chainID string, tx Tx) []byte { + if thm.txHashBytes == nil || thm.chainID != chainID { + thm.chainID = chainID + thm.txHashBytes = TxHash(chainID, tx) + } + return thm.txHashBytes +} + func TxHash(chainID string, tx Tx) []byte { signBytes := acm.SignBytes(chainID, tx) hasher := ripemd160.New() @@ -402,11 +484,9 @@ func TxHash(chainID string, tx Tx) []byte { return hasher.Sum(nil) } -//----------------------------------------------------------------------------- - func GenerateReceipt(chainId string, tx Tx) Receipt { receipt := Receipt{ - TxHash: TxHash(chainId, tx), + TxHash: tx.Hash(chainId), } if callTx, ok := tx.(*CallTx); ok { receipt.CreatesContract = callTx.Address == nil @@ -421,6 +501,14 @@ func GenerateReceipt(chainId string, tx Tx) Receipt { //-------------------------------------------------------------------------------- +func copyInputs(inputs []*TxInput) []TxInput { + inputsCopy := make([]TxInput, len(inputs)) + for i, input := range inputs { + inputsCopy[i] = *input + } + return inputsCopy +} + // Contract: This function is deterministic and completely reversible. func jsonEscape(str string) string { // TODO: escape without panic diff --git a/txs/tx_test.go b/txs/tx_test.go index 7ec8f44004742ab8bf7c63bd2a278d44877c88f0..3a10bc394cd5b5f1ffb5382a8b8ccb53c9fe1b27 100644 --- a/txs/tx_test.go +++ b/txs/tx_test.go @@ -237,34 +237,13 @@ func testTxMarshalJSON(t *testing.T, tx Tx) { assert.Equal(t, string(bs), string(bsOut)) } -/* -func TestDupeoutTxSignable(t *testing.T) { - privAcc := acm.GeneratePrivateAccount() - partSetHeader := types.PartSetHeader{Total: 10, Hash: makeAddress("partsethash")} - voteA := &types.Vote{ - Height: 10, - Round: 2, - Type: types.VoteTypePrevote, - BlockHash: makeAddress("myblockhash"), - BlockPartsHeader: partSetHeader, - } - sig := privAcc acm.ChainSign(chainID, voteA) - voteA.Signature = sig.(crypto.SignatureEd25519) - voteB := voteA.Copy() - voteB.BlockHash = makeAddress("myotherblockhash") - sig = privAcc acm.ChainSign(chainID, voteB) - voteB.Signature = sig.(crypto.SignatureEd25519) - - dupeoutTx := &DupeoutTx{ - Address: makeAddress("address1"), - VoteA: *voteA, - VoteB: *voteB, - } - signBytes := acm.SignBytes(chainID, dupeoutTx) - signStr := string(signBytes) - expected := fmt.Sprintf(`{"chain_id":"%s","tx":[20,{"address":"%s","vote_a":%v,"vote_b":%v}]}`, - chainID, *voteA, *voteB) - if signStr != expected { - t.Errorf("Got unexpected sign string for DupeoutTx") +func TestTxHashMemoizer(t *testing.T) { + tx := &CallTx{ + Input: &TxInput{ + Sequence: 4, + }, } -}*/ + hsh := tx.Hash("foo") + assert.Equal(t, hsh, tx.txHashMemoizer.txHashBytes) + assert.Equal(t, "foo", tx.txHashMemoizer.chainID) +} diff --git a/vendor/github.com/tendermint/merkleeyes/LICENSE b/vendor/github.com/tendermint/merkleeyes/LICENSE deleted file mode 100644 index be6f5f001adbcb70aa9f65bac429f38256c1a28a..0000000000000000000000000000000000000000 --- a/vendor/github.com/tendermint/merkleeyes/LICENSE +++ /dev/null @@ -1,193 +0,0 @@ -Tendermint MerkleEyes -Copyright (C) 2015 Tendermint - - - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/tendermint/merkleeyes/iavl/iavl_node.go b/vendor/github.com/tendermint/merkleeyes/iavl/iavl_node.go deleted file mode 100644 index 8d77be68439df42ed1dc2c2fddd22be7b9b0762a..0000000000000000000000000000000000000000 --- a/vendor/github.com/tendermint/merkleeyes/iavl/iavl_node.go +++ /dev/null @@ -1,526 +0,0 @@ -package iavl - -import ( - "bytes" - "io" - - "golang.org/x/crypto/ripemd160" - - . "github.com/tendermint/tmlibs/common" - "github.com/tendermint/go-wire" -) - -// Node - -type IAVLNode struct { - key []byte - value []byte - height int8 - size int - hash []byte - leftHash []byte - leftNode *IAVLNode - rightHash []byte - rightNode *IAVLNode - persisted bool -} - -func NewIAVLNode(key []byte, value []byte) *IAVLNode { - return &IAVLNode{ - key: key, - value: value, - height: 0, - size: 1, - } -} - -// NOTE: The hash is not saved or set. The caller should set the hash afterwards. -// (Presumably the caller already has the hash) -func MakeIAVLNode(buf []byte, t *IAVLTree) (node *IAVLNode, err error) { - node = &IAVLNode{} - - // node header - node.height = int8(buf[0]) - buf = buf[1:] - var n int - node.size, n, err = wire.GetVarint(buf) - if err != nil { - return nil, err - } - buf = buf[n:] - node.key, n, err = wire.GetByteSlice(buf) - if err != nil { - return nil, err - } - buf = buf[n:] - if node.height == 0 { - // value - node.value, n, err = wire.GetByteSlice(buf) - if err != nil { - return nil, err - } - buf = buf[n:] - } else { - // children - leftHash, n, err := wire.GetByteSlice(buf) - if err != nil { - return nil, err - } - buf = buf[n:] - rightHash, n, err := wire.GetByteSlice(buf) - if err != nil { - return nil, err - } - buf = buf[n:] - node.leftHash = leftHash - node.rightHash = rightHash - } - return node, nil -} - -func (node *IAVLNode) _copy() *IAVLNode { - if node.height == 0 { - PanicSanity("Why are you copying a value node?") - } - return &IAVLNode{ - key: node.key, - height: node.height, - size: node.size, - hash: nil, // Going to be mutated anyways. - leftHash: node.leftHash, - leftNode: node.leftNode, - rightHash: node.rightHash, - rightNode: node.rightNode, - persisted: false, // Going to be mutated, so it can't already be persisted. - } -} - -func (node *IAVLNode) has(t *IAVLTree, key []byte) (has bool) { - if bytes.Compare(node.key, key) == 0 { - return true - } - if node.height == 0 { - return false - } else { - if bytes.Compare(key, node.key) < 0 { - return node.getLeftNode(t).has(t, key) - } else { - return node.getRightNode(t).has(t, key) - } - } -} - -func (node *IAVLNode) get(t *IAVLTree, key []byte) (index int, value []byte, exists bool) { - if node.height == 0 { - cmp := bytes.Compare(node.key, key) - if cmp == 0 { - return 0, node.value, true - } else if cmp == -1 { - return 1, nil, false - } else { - return 0, nil, false - } - } else { - if bytes.Compare(key, node.key) < 0 { - return node.getLeftNode(t).get(t, key) - } else { - rightNode := node.getRightNode(t) - index, value, exists = rightNode.get(t, key) - index += node.size - rightNode.size - return index, value, exists - } - } -} - -func (node *IAVLNode) getByIndex(t *IAVLTree, index int) (key []byte, value []byte) { - if node.height == 0 { - if index == 0 { - return node.key, node.value - } else { - PanicSanity("getByIndex asked for invalid index") - return nil, nil - } - } else { - // TODO: could improve this by storing the - // sizes as well as left/right hash. - leftNode := node.getLeftNode(t) - if index < leftNode.size { - return leftNode.getByIndex(t, index) - } else { - return node.getRightNode(t).getByIndex(t, index-leftNode.size) - } - } -} - -// NOTE: sets hashes recursively -func (node *IAVLNode) hashWithCount(t *IAVLTree) ([]byte, int) { - if node.hash != nil { - return node.hash, 0 - } - - hasher := ripemd160.New() - buf := new(bytes.Buffer) - _, hashCount, err := node.writeHashBytes(t, buf) - if err != nil { - PanicCrisis(err) - } - hasher.Write(buf.Bytes()) - node.hash = hasher.Sum(nil) - - return node.hash, hashCount + 1 -} - -// NOTE: sets hashes recursively -func (node *IAVLNode) writeHashBytes(t *IAVLTree, w io.Writer) (n int, hashCount int, err error) { - // height & size - wire.WriteInt8(node.height, w, &n, &err) - wire.WriteVarint(node.size, w, &n, &err) - // key is not written for inner nodes, unlike writePersistBytes - - if node.height == 0 { - // key & value - wire.WriteByteSlice(node.key, w, &n, &err) - wire.WriteByteSlice(node.value, w, &n, &err) - } else { - // left - if node.leftNode != nil { - leftHash, leftCount := node.leftNode.hashWithCount(t) - node.leftHash = leftHash - hashCount += leftCount - } - if node.leftHash == nil { - PanicSanity("node.leftHash was nil in writeHashBytes") - } - wire.WriteByteSlice(node.leftHash, w, &n, &err) - // right - if node.rightNode != nil { - rightHash, rightCount := node.rightNode.hashWithCount(t) - node.rightHash = rightHash - hashCount += rightCount - } - if node.rightHash == nil { - PanicSanity("node.rightHash was nil in writeHashBytes") - } - wire.WriteByteSlice(node.rightHash, w, &n, &err) - } - return -} - -// NOTE: clears leftNode/rigthNode recursively -// NOTE: sets hashes recursively -func (node *IAVLNode) save(t *IAVLTree) { - if node.hash == nil { - node.hash, _ = node.hashWithCount(t) - } - if node.persisted { - return - } - - // save children - if node.leftNode != nil { - node.leftNode.save(t) - node.leftNode = nil - } - if node.rightNode != nil { - node.rightNode.save(t) - node.rightNode = nil - } - - // save node - t.ndb.SaveNode(t, node) - return -} - -// NOTE: sets hashes recursively -func (node *IAVLNode) writePersistBytes(t *IAVLTree, w io.Writer) (n int, err error) { - // node header - wire.WriteInt8(node.height, w, &n, &err) - wire.WriteVarint(node.size, w, &n, &err) - // key (unlike writeHashBytes, key is written for inner nodes) - wire.WriteByteSlice(node.key, w, &n, &err) - - if node.height == 0 { - // value - wire.WriteByteSlice(node.value, w, &n, &err) - } else { - // left - if node.leftHash == nil { - PanicSanity("node.leftHash was nil in writePersistBytes") - } - wire.WriteByteSlice(node.leftHash, w, &n, &err) - // right - if node.rightHash == nil { - PanicSanity("node.rightHash was nil in writePersistBytes") - } - wire.WriteByteSlice(node.rightHash, w, &n, &err) - } - return -} - -func (node *IAVLNode) set(t *IAVLTree, key []byte, value []byte) (newSelf *IAVLNode, updated bool) { - if node.height == 0 { - cmp := bytes.Compare(key, node.key) - if cmp < 0 { - return &IAVLNode{ - key: node.key, - height: 1, - size: 2, - leftNode: NewIAVLNode(key, value), - rightNode: node, - }, false - } else if cmp == 0 { - removeOrphan(t, node) - return NewIAVLNode(key, value), true - } else { - return &IAVLNode{ - key: key, - height: 1, - size: 2, - leftNode: node, - rightNode: NewIAVLNode(key, value), - }, false - } - } else { - removeOrphan(t, node) - node = node._copy() - if bytes.Compare(key, node.key) < 0 { - node.leftNode, updated = node.getLeftNode(t).set(t, key, value) - node.leftHash = nil // leftHash is yet unknown - } else { - node.rightNode, updated = node.getRightNode(t).set(t, key, value) - node.rightHash = nil // rightHash is yet unknown - } - if updated { - return node, updated - } else { - node.calcHeightAndSize(t) - return node.balance(t), updated - } - } -} - -// newHash/newNode: The new hash or node to replace node after remove. -// newKey: new leftmost leaf key for tree after successfully removing 'key' if changed. -// value: removed value. -func (node *IAVLNode) remove(t *IAVLTree, key []byte) ( - newHash []byte, newNode *IAVLNode, newKey []byte, value []byte, removed bool) { - if node.height == 0 { - if bytes.Compare(key, node.key) == 0 { - removeOrphan(t, node) - return nil, nil, nil, node.value, true - } else { - return node.hash, node, nil, nil, false - } - } else { - if bytes.Compare(key, node.key) < 0 { - var newLeftHash []byte - var newLeftNode *IAVLNode - newLeftHash, newLeftNode, newKey, value, removed = node.getLeftNode(t).remove(t, key) - if !removed { - return node.hash, node, nil, value, false - } else if newLeftHash == nil && newLeftNode == nil { // left node held value, was removed - return node.rightHash, node.rightNode, node.key, value, true - } - removeOrphan(t, node) - node = node._copy() - node.leftHash, node.leftNode = newLeftHash, newLeftNode - node.calcHeightAndSize(t) - node = node.balance(t) - return node.hash, node, newKey, value, true - } else { - var newRightHash []byte - var newRightNode *IAVLNode - newRightHash, newRightNode, newKey, value, removed = node.getRightNode(t).remove(t, key) - if !removed { - return node.hash, node, nil, value, false - } else if newRightHash == nil && newRightNode == nil { // right node held value, was removed - return node.leftHash, node.leftNode, nil, value, true - } - removeOrphan(t, node) - node = node._copy() - node.rightHash, node.rightNode = newRightHash, newRightNode - if newKey != nil { - node.key = newKey - } - node.calcHeightAndSize(t) - node = node.balance(t) - return node.hash, node, nil, value, true - } - } -} - -func (node *IAVLNode) getLeftNode(t *IAVLTree) *IAVLNode { - if node.leftNode != nil { - return node.leftNode - } else { - return t.ndb.GetNode(t, node.leftHash) - } -} - -func (node *IAVLNode) getRightNode(t *IAVLTree) *IAVLNode { - if node.rightNode != nil { - return node.rightNode - } else { - return t.ndb.GetNode(t, node.rightHash) - } -} - -// NOTE: overwrites node -// TODO: optimize balance & rotate -func (node *IAVLNode) rotateRight(t *IAVLTree) *IAVLNode { - node = node._copy() - l := node.getLeftNode(t) - removeOrphan(t, l) - _l := l._copy() - - _lrHash, _lrCached := _l.rightHash, _l.rightNode - _l.rightHash, _l.rightNode = node.hash, node - node.leftHash, node.leftNode = _lrHash, _lrCached - - node.calcHeightAndSize(t) - _l.calcHeightAndSize(t) - - return _l -} - -// NOTE: overwrites node -// TODO: optimize balance & rotate -func (node *IAVLNode) rotateLeft(t *IAVLTree) *IAVLNode { - node = node._copy() - r := node.getRightNode(t) - removeOrphan(t, r) - _r := r._copy() - - _rlHash, _rlCached := _r.leftHash, _r.leftNode - _r.leftHash, _r.leftNode = node.hash, node - node.rightHash, node.rightNode = _rlHash, _rlCached - - node.calcHeightAndSize(t) - _r.calcHeightAndSize(t) - - return _r -} - -// NOTE: mutates height and size -func (node *IAVLNode) calcHeightAndSize(t *IAVLTree) { - node.height = maxInt8(node.getLeftNode(t).height, node.getRightNode(t).height) + 1 - node.size = node.getLeftNode(t).size + node.getRightNode(t).size -} - -func (node *IAVLNode) calcBalance(t *IAVLTree) int { - return int(node.getLeftNode(t).height) - int(node.getRightNode(t).height) -} - -// NOTE: assumes that node can be modified -// TODO: optimize balance & rotate -func (node *IAVLNode) balance(t *IAVLTree) (newSelf *IAVLNode) { - if node.persisted { - panic("Unexpected balance() call on persisted node") - } - balance := node.calcBalance(t) - if balance > 1 { - if node.getLeftNode(t).calcBalance(t) >= 0 { - // Left Left Case - return node.rotateRight(t) - } else { - // Left Right Case - // node = node._copy() - left := node.getLeftNode(t) - removeOrphan(t, left) - node.leftHash, node.leftNode = nil, left.rotateLeft(t) - //node.calcHeightAndSize() - return node.rotateRight(t) - } - } - if balance < -1 { - if node.getRightNode(t).calcBalance(t) <= 0 { - // Right Right Case - return node.rotateLeft(t) - } else { - // Right Left Case - // node = node._copy() - right := node.getRightNode(t) - removeOrphan(t, right) - node.rightHash, node.rightNode = nil, right.rotateRight(t) - //node.calcHeightAndSize() - return node.rotateLeft(t) - } - } - // Nothing changed - return node -} - -// traverse is a wrapper over traverseInRange when we want the whole tree -func (node *IAVLNode) traverse(t *IAVLTree, ascending bool, cb func(*IAVLNode) bool) bool { - return node.traverseInRange(t, nil, nil, ascending, cb) -} - -func (node *IAVLNode) traverseInRange(t *IAVLTree, start, end []byte, ascending bool, cb func(*IAVLNode) bool) bool { - afterStart := (start == nil || bytes.Compare(start, node.key) <= 0) - beforeEnd := (end == nil || bytes.Compare(node.key, end) <= 0) - - stop := false - if afterStart && beforeEnd { - // IterateRange ignores this if not leaf - stop = cb(node) - } - if stop { - return stop - } - - if node.height > 0 { - if ascending { - // check lower nodes, then higher - if afterStart { - stop = node.getLeftNode(t).traverseInRange(t, start, end, ascending, cb) - } - if stop { - return stop - } - if beforeEnd { - stop = node.getRightNode(t).traverseInRange(t, start, end, ascending, cb) - } - } else { - // check the higher nodes first - if beforeEnd { - stop = node.getRightNode(t).traverseInRange(t, start, end, ascending, cb) - } - if stop { - return stop - } - if afterStart { - stop = node.getLeftNode(t).traverseInRange(t, start, end, ascending, cb) - } - } - } - - return stop -} - -// Only used in testing... -func (node *IAVLNode) lmd(t *IAVLTree) *IAVLNode { - if node.height == 0 { - return node - } - return node.getLeftNode(t).lmd(t) -} - -// Only used in testing... -func (node *IAVLNode) rmd(t *IAVLTree) *IAVLNode { - if node.height == 0 { - return node - } - return node.getRightNode(t).rmd(t) -} - -//---------------------------------------- - -func removeOrphan(t *IAVLTree, node *IAVLNode) { - if !node.persisted { - return - } - if t.ndb == nil { - return - } - t.ndb.RemoveNode(t, node) -} diff --git a/vendor/github.com/tendermint/merkleeyes/iavl/iavl_proof.go b/vendor/github.com/tendermint/merkleeyes/iavl/iavl_proof.go deleted file mode 100644 index 95fbf3b7780abed7d7bc9a08ffb6b27e7e78ed46..0000000000000000000000000000000000000000 --- a/vendor/github.com/tendermint/merkleeyes/iavl/iavl_proof.go +++ /dev/null @@ -1,153 +0,0 @@ -package iavl - -import ( - "bytes" - - "golang.org/x/crypto/ripemd160" - - . "github.com/tendermint/tmlibs/common" - "github.com/tendermint/go-wire" -) - -const proofLimit = 1 << 16 // 64 KB - -type IAVLProof struct { - LeafHash []byte - InnerNodes []IAVLProofInnerNode - RootHash []byte -} - -func (proof *IAVLProof) Verify(key []byte, value []byte, root []byte) bool { - if !bytes.Equal(proof.RootHash, root) { - return false - } - leafNode := IAVLProofLeafNode{KeyBytes: key, ValueBytes: value} - leafHash := leafNode.Hash() - if !bytes.Equal(leafHash, proof.LeafHash) { - return false - } - hash := leafHash - for _, branch := range proof.InnerNodes { - hash = branch.Hash(hash) - } - return bytes.Equal(proof.RootHash, hash) -} - -// Please leave this here! I use it in light-client to fulfill an interface -func (proof *IAVLProof) Root() []byte { - return proof.RootHash -} - -// ReadProof will deserialize a IAVLProof from bytes -func ReadProof(data []byte) (*IAVLProof, error) { - // TODO: make go-wire never panic - n, err := int(0), error(nil) - proof := wire.ReadBinary(&IAVLProof{}, bytes.NewBuffer(data), proofLimit, &n, &err).(*IAVLProof) - return proof, err -} - -type IAVLProofInnerNode struct { - Height int8 - Size int - Left []byte - Right []byte -} - -func (branch IAVLProofInnerNode) Hash(childHash []byte) []byte { - hasher := ripemd160.New() - buf := new(bytes.Buffer) - n, err := int(0), error(nil) - wire.WriteInt8(branch.Height, buf, &n, &err) - wire.WriteVarint(branch.Size, buf, &n, &err) - if len(branch.Left) == 0 { - wire.WriteByteSlice(childHash, buf, &n, &err) - wire.WriteByteSlice(branch.Right, buf, &n, &err) - } else { - wire.WriteByteSlice(branch.Left, buf, &n, &err) - wire.WriteByteSlice(childHash, buf, &n, &err) - } - if err != nil { - PanicCrisis(Fmt("Failed to hash IAVLProofInnerNode: %v", err)) - } - // fmt.Printf("InnerNode hash bytes: %X\n", buf.Bytes()) - hasher.Write(buf.Bytes()) - return hasher.Sum(nil) -} - -type IAVLProofLeafNode struct { - KeyBytes []byte - ValueBytes []byte -} - -func (leaf IAVLProofLeafNode) Hash() []byte { - hasher := ripemd160.New() - buf := new(bytes.Buffer) - n, err := int(0), error(nil) - wire.WriteInt8(0, buf, &n, &err) - wire.WriteVarint(1, buf, &n, &err) - wire.WriteByteSlice(leaf.KeyBytes, buf, &n, &err) - wire.WriteByteSlice(leaf.ValueBytes, buf, &n, &err) - if err != nil { - PanicCrisis(Fmt("Failed to hash IAVLProofLeafNode: %v", err)) - } - // fmt.Printf("LeafNode hash bytes: %X\n", buf.Bytes()) - hasher.Write(buf.Bytes()) - return hasher.Sum(nil) -} - -func (node *IAVLNode) constructProof(t *IAVLTree, key []byte, valuePtr *[]byte, proof *IAVLProof) (exists bool) { - if node.height == 0 { - if bytes.Compare(node.key, key) == 0 { - *valuePtr = node.value - proof.LeafHash = node.hash - return true - } else { - return false - } - } else { - if bytes.Compare(key, node.key) < 0 { - exists := node.getLeftNode(t).constructProof(t, key, valuePtr, proof) - if !exists { - return false - } - branch := IAVLProofInnerNode{ - Height: node.height, - Size: node.size, - Left: nil, - Right: node.getRightNode(t).hash, - } - proof.InnerNodes = append(proof.InnerNodes, branch) - return true - } else { - exists := node.getRightNode(t).constructProof(t, key, valuePtr, proof) - if !exists { - return false - } - branch := IAVLProofInnerNode{ - Height: node.height, - Size: node.size, - Left: node.getLeftNode(t).hash, - Right: nil, - } - proof.InnerNodes = append(proof.InnerNodes, branch) - return true - } - } -} - -// Returns nil, nil if key is not in tree. -func (t *IAVLTree) ConstructProof(key []byte) (value []byte, proof *IAVLProof) { - if t.root == nil { - return nil, nil - } - t.root.hashWithCount(t) // Ensure that all hashes are calculated. - proof = &IAVLProof{ - RootHash: t.root.hash, - } - exists := t.root.constructProof(t, key, &value, proof) - if exists { - return value, proof - } else { - return nil, nil - } -} diff --git a/vendor/github.com/tendermint/merkleeyes/iavl/iavl_tree.go b/vendor/github.com/tendermint/merkleeyes/iavl/iavl_tree.go deleted file mode 100644 index b644612d0051801f596eafd2a829516d4f4cf4ca..0000000000000000000000000000000000000000 --- a/vendor/github.com/tendermint/merkleeyes/iavl/iavl_tree.go +++ /dev/null @@ -1,327 +0,0 @@ -package iavl - -import ( - "bytes" - "container/list" - "sync" - - wire "github.com/tendermint/go-wire" - . "github.com/tendermint/tmlibs/common" - dbm "github.com/tendermint/tmlibs/db" - "github.com/tendermint/tmlibs/merkle" -) - -/* -Immutable AVL Tree (wraps the Node root) -This tree is not goroutine safe. -*/ -type IAVLTree struct { - root *IAVLNode - ndb *nodeDB -} - -// NewIAVLTree creates both im-memory and persistent instances -func NewIAVLTree(cacheSize int, db dbm.DB) *IAVLTree { - if db == nil { - // In-memory IAVLTree - return &IAVLTree{} - } else { - // Persistent IAVLTree - ndb := newNodeDB(cacheSize, db) - return &IAVLTree{ - ndb: ndb, - } - } -} - -// The returned tree and the original tree are goroutine independent. -// That is, they can each run in their own goroutine. -// However, upon Save(), any other trees that share a db will become -// outdated, as some nodes will become orphaned. -// Note that Save() clears leftNode and rightNode. Otherwise, -// two copies would not be goroutine independent. -func (t *IAVLTree) Copy() merkle.Tree { - if t.root == nil { - return &IAVLTree{ - root: nil, - ndb: t.ndb, - } - } - if t.ndb != nil && !t.root.persisted { - // Saving a tree finalizes all the nodes. - // It sets all the hashes recursively, - // clears all the leftNode/rightNode values recursively, - // and all the .persisted flags get set. - PanicSanity("It is unsafe to Copy() an unpersisted tree.") - } else if t.ndb == nil && t.root.hash == nil { - // An in-memory IAVLTree is finalized when the hashes are - // calculated. - t.root.hashWithCount(t) - } - return &IAVLTree{ - root: t.root, - ndb: t.ndb, - } -} - -func (t *IAVLTree) Size() int { - if t.root == nil { - return 0 - } - return t.root.size -} - -func (t *IAVLTree) Height() int8 { - if t.root == nil { - return 0 - } - return t.root.height -} - -func (t *IAVLTree) Has(key []byte) bool { - if t.root == nil { - return false - } - return t.root.has(t, key) -} - -func (t *IAVLTree) Proof(key []byte) (value []byte, proofBytes []byte, exists bool) { - value, proof := t.ConstructProof(key) - if proof == nil { - return nil, nil, false - } - proofBytes = wire.BinaryBytes(proof) - return value, proofBytes, true -} - -func (t *IAVLTree) Set(key []byte, value []byte) (updated bool) { - if t.root == nil { - t.root = NewIAVLNode(key, value) - return false - } - t.root, updated = t.root.set(t, key, value) - return updated -} - -// BatchSet adds a Set to the current batch, will get handled atomically -func (t *IAVLTree) BatchSet(key []byte, value []byte) { - t.ndb.batch.Set(key, value) -} - -func (t *IAVLTree) Hash() []byte { - if t.root == nil { - return nil - } - hash, _ := t.root.hashWithCount(t) - return hash -} - -func (t *IAVLTree) HashWithCount() ([]byte, int) { - if t.root == nil { - return nil, 0 - } - return t.root.hashWithCount(t) -} - -func (t *IAVLTree) Save() []byte { - if t.root == nil { - return nil - } - if t.ndb != nil { - t.root.save(t) - t.ndb.Commit() - } - return t.root.hash -} - -// Sets the root node by reading from db. -// If the hash is empty, then sets root to nil. -func (t *IAVLTree) Load(hash []byte) { - if len(hash) == 0 { - t.root = nil - } else { - t.root = t.ndb.GetNode(t, hash) - } -} - -func (t *IAVLTree) Get(key []byte) (index int, value []byte, exists bool) { - if t.root == nil { - return 0, nil, false - } - return t.root.get(t, key) -} - -func (t *IAVLTree) GetByIndex(index int) (key []byte, value []byte) { - if t.root == nil { - return nil, nil - } - return t.root.getByIndex(t, index) -} - -func (t *IAVLTree) Remove(key []byte) (value []byte, removed bool) { - if t.root == nil { - return nil, false - } - newRootHash, newRoot, _, value, removed := t.root.remove(t, key) - if !removed { - return nil, false - } - if newRoot == nil && newRootHash != nil { - t.root = t.ndb.GetNode(t, newRootHash) - } else { - t.root = newRoot - } - return value, true -} - -func (t *IAVLTree) Iterate(fn func(key []byte, value []byte) bool) (stopped bool) { - if t.root == nil { - return false - } - return t.root.traverse(t, true, func(node *IAVLNode) bool { - if node.height == 0 { - return fn(node.key, node.value) - } else { - return false - } - }) -} - -// IterateRange makes a callback for all nodes with key between start and end inclusive -// If either are nil, then it is open on that side (nil, nil is the same as Iterate) -func (t *IAVLTree) IterateRange(start, end []byte, ascending bool, fn func(key []byte, value []byte) bool) (stopped bool) { - if t.root == nil { - return false - } - return t.root.traverseInRange(t, start, end, ascending, func(node *IAVLNode) bool { - if node.height == 0 { - return fn(node.key, node.value) - } else { - return false - } - }) -} - -//----------------------------------------------------------------------------- - -type nodeDB struct { - mtx sync.Mutex - cache map[string]*list.Element - cacheSize int - cacheQueue *list.List - db dbm.DB - batch dbm.Batch - orphans map[string]struct{} - orphansPrev map[string]struct{} -} - -func newNodeDB(cacheSize int, db dbm.DB) *nodeDB { - ndb := &nodeDB{ - cache: make(map[string]*list.Element), - cacheSize: cacheSize, - cacheQueue: list.New(), - db: db, - batch: db.NewBatch(), - orphans: make(map[string]struct{}), - orphansPrev: make(map[string]struct{}), - } - return ndb -} - -func (ndb *nodeDB) GetNode(t *IAVLTree, hash []byte) *IAVLNode { - ndb.mtx.Lock() - defer ndb.mtx.Unlock() - // Check the cache. - elem, ok := ndb.cache[string(hash)] - if ok { - // Already exists. Move to back of cacheQueue. - ndb.cacheQueue.MoveToBack(elem) - return elem.Value.(*IAVLNode) - } else { - // Doesn't exist, load. - buf := ndb.db.Get(hash) - if len(buf) == 0 { - // ndb.db.Print() - PanicSanity(Fmt("Value missing for key %X", hash)) - } - node, err := MakeIAVLNode(buf, t) - if err != nil { - PanicCrisis(Fmt("Error reading IAVLNode. bytes: %X error: %v", buf, err)) - } - node.hash = hash - node.persisted = true - ndb.cacheNode(node) - return node - } -} - -func (ndb *nodeDB) SaveNode(t *IAVLTree, node *IAVLNode) { - ndb.mtx.Lock() - defer ndb.mtx.Unlock() - if node.hash == nil { - PanicSanity("Expected to find node.hash, but none found.") - } - if node.persisted { - PanicSanity("Shouldn't be calling save on an already persisted node.") - } - /*if _, ok := ndb.cache[string(node.hash)]; ok { - panic("Shouldn't be calling save on an already cached node.") - }*/ - // Save node bytes to db - buf := bytes.NewBuffer(nil) - _, err := node.writePersistBytes(t, buf) - if err != nil { - PanicCrisis(err) - } - ndb.batch.Set(node.hash, buf.Bytes()) - node.persisted = true - ndb.cacheNode(node) - // Re-creating the orphan, - // Do not garbage collect. - delete(ndb.orphans, string(node.hash)) - delete(ndb.orphansPrev, string(node.hash)) -} - -func (ndb *nodeDB) RemoveNode(t *IAVLTree, node *IAVLNode) { - ndb.mtx.Lock() - defer ndb.mtx.Unlock() - if node.hash == nil { - PanicSanity("Expected to find node.hash, but none found.") - } - if !node.persisted { - PanicSanity("Shouldn't be calling remove on a non-persisted node.") - } - elem, ok := ndb.cache[string(node.hash)] - if ok { - ndb.cacheQueue.Remove(elem) - delete(ndb.cache, string(node.hash)) - } - ndb.orphans[string(node.hash)] = struct{}{} -} - -func (ndb *nodeDB) cacheNode(node *IAVLNode) { - // Create entry in cache and append to cacheQueue. - elem := ndb.cacheQueue.PushBack(node) - ndb.cache[string(node.hash)] = elem - // Maybe expire an item. - if ndb.cacheQueue.Len() > ndb.cacheSize { - hash := ndb.cacheQueue.Remove(ndb.cacheQueue.Front()).(*IAVLNode).hash - delete(ndb.cache, string(hash)) - } -} - -func (ndb *nodeDB) Commit() { - ndb.mtx.Lock() - defer ndb.mtx.Unlock() - // Delete orphans from previous block - for orphanHashStr, _ := range ndb.orphansPrev { - ndb.batch.Delete([]byte(orphanHashStr)) - } - // Write saves & orphan deletes - ndb.batch.Write() - ndb.db.SetSync(nil, nil) - ndb.batch = ndb.db.NewBatch() - // Shift orphans - ndb.orphansPrev = ndb.orphans - ndb.orphans = make(map[string]struct{}) -} diff --git a/vendor/github.com/tendermint/merkleeyes/iavl/iavl_tree_dump.go b/vendor/github.com/tendermint/merkleeyes/iavl/iavl_tree_dump.go deleted file mode 100644 index e38a0c4d9a81fdd02d0c8237520d06a35c7a2c32..0000000000000000000000000000000000000000 --- a/vendor/github.com/tendermint/merkleeyes/iavl/iavl_tree_dump.go +++ /dev/null @@ -1,144 +0,0 @@ -package iavl - -import ( - "bytes" - "fmt" - //"strings" - - wire "github.com/tendermint/go-wire" -) - -type Formatter func(in []byte) (out string) - -type KeyValueMapping struct { - Key Formatter - Value Formatter -} - -// Flip back and forth between ascii and hex. -func mixedDisplay(value []byte) string { - - var buffer bytes.Buffer - var last []byte - - ascii := true - for i := 0; i < len(value); i++ { - if value[i] < 32 || value[i] > 126 { - if ascii && len(last) > 0 { - // only if there are 6 or more chars - if len(last) > 5 { - buffer.WriteString(fmt.Sprintf("%s", last)) - last = nil - } - ascii = false - } - } - last = append(last, value[i]) - } - if ascii { - buffer.WriteString(fmt.Sprintf("%s", last)) - } else { - buffer.WriteString(fmt.Sprintf("%X", last)) - } - return buffer.String() -} - -// This is merkleeyes state, that it is writing to a specific key -type state struct { - Hash []byte - Height uint64 -} - -// Try to interpet as merkleeyes state -func stateMapping(value []byte) string { - var s state - err := wire.ReadBinaryBytes(value, &s) - if err != nil || s.Height > 500 { - return mixedDisplay(value) - } - return fmt.Sprintf("Height:%d, [%X]", s.Height, s.Hash) -} - -// This is basecoin accounts, that it is writing to a specific key -type account struct { - PubKey []byte - Sequence int - Balance []coin -} - -type wrapper struct { - bytes []byte -} - -type coin struct { - Denom string - Amount int64 -} - -// Perhaps this is an IAVL tree node? -func nodeMapping(node *IAVLNode) string { - - formattedKey := mixedDisplay(node.key) - - var formattedValue string - var acc account - - err := wire.ReadBinaryBytes(node.value, &acc) - if err != nil { - formattedValue = mixedDisplay(node.value) - } else { - formattedValue = fmt.Sprintf("%v", acc) - } - - if node.height == 0 { - return fmt.Sprintf(" LeafNode[height: %d, size %d, key: %s, value: %s]", - node.height, node.size, formattedKey, formattedValue) - } else { - return fmt.Sprintf("InnerNode[height: %d, size %d, key: %s, leftHash: %X, rightHash: %X]", - node.height, node.size, formattedKey, node.leftHash, node.rightHash) - } -} - -// Try everything and see what sticks... -func overallMapping(value []byte) (str string) { - // underneath make node, wire can throw a panic - defer func() { - if recover() != nil { - str = fmt.Sprintf("%X", value) - return - } - }() - - // test to see if this is a node - node, err := MakeIAVLNode(value, nil) - - if err == nil && node.height < 100 && node.key != nil { - return nodeMapping(node) - } - - // Unknown value type - return stateMapping(value) -} - -// Dump everything from the database -func (t *IAVLTree) Dump(verbose bool, mapping *KeyValueMapping) { - if verbose && t.root == nil { - fmt.Printf("No root loaded into memory\n") - } - - if mapping == nil { - mapping = &KeyValueMapping{Key: mixedDisplay, Value: overallMapping} - } - - if verbose { - stats := t.ndb.db.Stats() - for key, value := range stats { - fmt.Printf("%s:\n\t%s\n", key, value) - } - } - - iter := t.ndb.db.Iterator() - for iter.Next() { - fmt.Printf("%s: %s\n", mapping.Key(iter.Key()), mapping.Value(iter.Value())) - } -} diff --git a/vendor/github.com/tendermint/merkleeyes/iavl/util.go b/vendor/github.com/tendermint/merkleeyes/iavl/util.go deleted file mode 100644 index add1c3c2368471dca20835a5e507c78b3bdb0e1c..0000000000000000000000000000000000000000 --- a/vendor/github.com/tendermint/merkleeyes/iavl/util.go +++ /dev/null @@ -1,43 +0,0 @@ -package iavl - -import ( - "fmt" -) - -// Prints the in-memory children recursively. -func PrintIAVLNode(node *IAVLNode) { - fmt.Println("==== NODE") - if node != nil { - printIAVLNode(node, 0) - } - fmt.Println("==== END") -} - -func printIAVLNode(node *IAVLNode, indent int) { - indentPrefix := "" - for i := 0; i < indent; i++ { - indentPrefix += " " - } - - if node.rightNode != nil { - printIAVLNode(node.rightNode, indent+1) - } else if node.rightHash != nil { - fmt.Printf("%s %X\n", indentPrefix, node.rightHash) - } - - fmt.Printf("%s%v:%v\n", indentPrefix, node.key, node.height) - - if node.leftNode != nil { - printIAVLNode(node.leftNode, indent+1) - } else if node.leftHash != nil { - fmt.Printf("%s %X\n", indentPrefix, node.leftHash) - } - -} - -func maxInt8(a, b int8) int8 { - if a > b { - return a - } - return b -} diff --git a/vendor/github.com/tendermint/merkleeyes/iavl/version.go b/vendor/github.com/tendermint/merkleeyes/iavl/version.go deleted file mode 100644 index 0f5a943e1807ac78b9af47732576473f71488648..0000000000000000000000000000000000000000 --- a/vendor/github.com/tendermint/merkleeyes/iavl/version.go +++ /dev/null @@ -1,3 +0,0 @@ -package iavl - -const Version = "0.4.0" // benchmarking, update proof diff --git a/vendor/gopkg.in/go-playground/validator.v9/baked_in.go b/vendor/gopkg.in/go-playground/validator.v9/baked_in.go index 66540946831ab8c67232afa27a9ab5abf931bd2b..231b78ee85af2f7dadbdf505863c354be858bac7 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/baked_in.go +++ b/vendor/gopkg.in/go-playground/validator.v9/baked_in.go @@ -6,7 +6,9 @@ import ( "net" "net/url" "reflect" + "strconv" "strings" + "sync" "time" "unicode/utf8" ) @@ -55,88 +57,130 @@ var ( // you can add, remove or even replace items to suite your needs, // or even disregard and use your own map if so desired. bakedInValidators = map[string]Func{ - "required": hasValue, - "isdefault": isDefault, - "len": hasLengthOf, - "min": hasMinOf, - "max": hasMaxOf, - "eq": isEq, - "ne": isNe, - "lt": isLt, - "lte": isLte, - "gt": isGt, - "gte": isGte, - "eqfield": isEqField, - "eqcsfield": isEqCrossStructField, - "necsfield": isNeCrossStructField, - "gtcsfield": isGtCrossStructField, - "gtecsfield": isGteCrossStructField, - "ltcsfield": isLtCrossStructField, - "ltecsfield": isLteCrossStructField, - "nefield": isNeField, - "gtefield": isGteField, - "gtfield": isGtField, - "ltefield": isLteField, - "ltfield": isLtField, - "alpha": isAlpha, - "alphanum": isAlphanum, - "alphaunicode": isAlphaUnicode, - "alphanumunicode": isAlphanumUnicode, - "numeric": isNumeric, - "number": isNumber, - "hexadecimal": isHexadecimal, - "hexcolor": isHEXColor, - "rgb": isRGB, - "rgba": isRGBA, - "hsl": isHSL, - "hsla": isHSLA, - "email": isEmail, - "url": isURL, - "uri": isURI, - "base64": isBase64, - "contains": contains, - "containsany": containsAny, - "containsrune": containsRune, - "excludes": excludes, - "excludesall": excludesAll, - "excludesrune": excludesRune, - "isbn": isISBN, - "isbn10": isISBN10, - "isbn13": isISBN13, - "uuid": isUUID, - "uuid3": isUUID3, - "uuid4": isUUID4, - "uuid5": isUUID5, - "ascii": isASCII, - "printascii": isPrintableASCII, - "multibyte": hasMultiByteCharacter, - "datauri": isDataURI, - "latitude": isLatitude, - "longitude": isLongitude, - "ssn": isSSN, - "ipv4": isIPv4, - "ipv6": isIPv6, - "ip": isIP, - "cidrv4": isCIDRv4, - "cidrv6": isCIDRv6, - "cidr": isCIDR, - "tcp4_addr": isTCP4AddrResolvable, - "tcp6_addr": isTCP6AddrResolvable, - "tcp_addr": isTCPAddrResolvable, - "udp4_addr": isUDP4AddrResolvable, - "udp6_addr": isUDP6AddrResolvable, - "udp_addr": isUDPAddrResolvable, - "ip4_addr": isIP4AddrResolvable, - "ip6_addr": isIP6AddrResolvable, - "ip_addr": isIPAddrResolvable, - "unix_addr": isUnixAddrResolvable, - "mac": isMAC, - "hostname": isHostname, - "fqdn": isFQDN, - "unique": isUnique, + "required": hasValue, + "isdefault": isDefault, + "len": hasLengthOf, + "min": hasMinOf, + "max": hasMaxOf, + "eq": isEq, + "ne": isNe, + "lt": isLt, + "lte": isLte, + "gt": isGt, + "gte": isGte, + "eqfield": isEqField, + "eqcsfield": isEqCrossStructField, + "necsfield": isNeCrossStructField, + "gtcsfield": isGtCrossStructField, + "gtecsfield": isGteCrossStructField, + "ltcsfield": isLtCrossStructField, + "ltecsfield": isLteCrossStructField, + "nefield": isNeField, + "gtefield": isGteField, + "gtfield": isGtField, + "ltefield": isLteField, + "ltfield": isLtField, + "alpha": isAlpha, + "alphanum": isAlphanum, + "alphaunicode": isAlphaUnicode, + "alphanumunicode": isAlphanumUnicode, + "numeric": isNumeric, + "number": isNumber, + "hexadecimal": isHexadecimal, + "hexcolor": isHEXColor, + "rgb": isRGB, + "rgba": isRGBA, + "hsl": isHSL, + "hsla": isHSLA, + "email": isEmail, + "url": isURL, + "uri": isURI, + "base64": isBase64, + "contains": contains, + "containsany": containsAny, + "containsrune": containsRune, + "excludes": excludes, + "excludesall": excludesAll, + "excludesrune": excludesRune, + "isbn": isISBN, + "isbn10": isISBN10, + "isbn13": isISBN13, + "uuid": isUUID, + "uuid3": isUUID3, + "uuid4": isUUID4, + "uuid5": isUUID5, + "ascii": isASCII, + "printascii": isPrintableASCII, + "multibyte": hasMultiByteCharacter, + "datauri": isDataURI, + "latitude": isLatitude, + "longitude": isLongitude, + "ssn": isSSN, + "ipv4": isIPv4, + "ipv6": isIPv6, + "ip": isIP, + "cidrv4": isCIDRv4, + "cidrv6": isCIDRv6, + "cidr": isCIDR, + "tcp4_addr": isTCP4AddrResolvable, + "tcp6_addr": isTCP6AddrResolvable, + "tcp_addr": isTCPAddrResolvable, + "udp4_addr": isUDP4AddrResolvable, + "udp6_addr": isUDP6AddrResolvable, + "udp_addr": isUDPAddrResolvable, + "ip4_addr": isIP4AddrResolvable, + "ip6_addr": isIP6AddrResolvable, + "ip_addr": isIPAddrResolvable, + "unix_addr": isUnixAddrResolvable, + "mac": isMAC, + "hostname": isHostnameRFC952, // RFC 952 + "hostname_rfc1123": isHostnameRFC1123, // RFC 1123 + "fqdn": isFQDN, + "unique": isUnique, + "oneof": isOneOf, } ) +var oneofValsCache = map[string][]string{} +var oneofValsCacheRWLock = sync.RWMutex{} + +func parseOneOfParam2(s string) []string { + oneofValsCacheRWLock.RLock() + vals, ok := oneofValsCache[s] + oneofValsCacheRWLock.RUnlock() + if !ok { + oneofValsCacheRWLock.Lock() + vals = strings.Fields(s) + oneofValsCache[s] = vals + oneofValsCacheRWLock.Unlock() + } + return vals +} + +func isOneOf(fl FieldLevel) bool { + vals := parseOneOfParam2(fl.Param()) + + field := fl.Field() + + var v string + switch field.Kind() { + case reflect.String: + v = field.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v = strconv.FormatInt(field.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v = strconv.FormatUint(field.Uint(), 10) + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } + for i := 0; i < len(vals); i++ { + if vals[i] == v { + return true + } + } + return false +} + // isUnique is the validation function for validating if each array|slice element is unique func isUnique(fl FieldLevel) bool { @@ -1511,8 +1555,12 @@ func isIP6Addr(fl FieldLevel) bool { return ip != nil && ip.To4() == nil } -func isHostname(fl FieldLevel) bool { - return hostnameRegex.MatchString(fl.Field().String()) +func isHostnameRFC952(fl FieldLevel) bool { + return hostnameRegexRFC952.MatchString(fl.Field().String()) +} + +func isHostnameRFC1123(fl FieldLevel) bool { + return hostnameRegexRFC1123.MatchString(fl.Field().String()) } func isFQDN(fl FieldLevel) bool { @@ -1526,6 +1574,6 @@ func isFQDN(fl FieldLevel) bool { val = val[0 : len(val)-1] } - return (strings.IndexAny(val, ".") > -1) && - hostnameRegex.MatchString(val) + return strings.ContainsAny(val, ".") && + hostnameRegexRFC952.MatchString(val) } diff --git a/vendor/gopkg.in/go-playground/validator.v9/cache.go b/vendor/gopkg.in/go-playground/validator.v9/cache.go index 3906d2517fcef915616f2ed89986696d7efaf6ec..c7fb0fb1bb773aafe431440777511d037385074f 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/cache.go +++ b/vendor/gopkg.in/go-playground/validator.v9/cache.go @@ -91,14 +91,14 @@ type cTag struct { aliasTag string actualAliasTag string param string - typeof tagType keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation next *cTag + fn FuncCtx + typeof tagType hasTag bool hasAlias bool hasParam bool // true if parameter used eg. eq= where the equal sign has been set isBlockEnd bool // indicates the current tag represents the last validation in the block - fn FuncCtx } func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct { diff --git a/vendor/gopkg.in/go-playground/validator.v9/doc.go b/vendor/gopkg.in/go-playground/validator.v9/doc.go index f0a748d41cfa2faea3ff734bbbaa898ee95f3188..f7efe234bef2783863beca9ec451fe8d9af4d67c 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/doc.go +++ b/vendor/gopkg.in/go-playground/validator.v9/doc.go @@ -295,6 +295,16 @@ validates the number of items. Usage: ne=10 +One Of + +For strings, ints, and uints, oneof will ensure that the value +is one of the values in the parameter. The parameter should be +a list of values separated by whitespace. Values may be +strings or numbers. + + Usage: oneof=red green + oneof=5 7 9 + Greater Than For numbers, this will ensure that the value is greater than the @@ -832,12 +842,18 @@ Note: See Go's ParseMAC for accepted formats and types: http://golang.org/src/net/mac.go?s=866:918#L29 -Hostname +Hostname RFC 952 -This validates that a string value is a valid Hostname +This validates that a string value is a valid Hostname according to RFC 952 https://tools.ietf.org/html/rfc952 Usage: hostname +Hostname RFC 1123 + +This validates that a string value is a valid Hostname according to RFC 1123 https://tools.ietf.org/html/rfc1123 + + Usage: hostname_rfc1123 or if you want to continue to use 'hostname' in your tags, create an alias. + Full Qualified Domain Name (FQDN) This validates that a string value contains a valid FQDN. diff --git a/vendor/gopkg.in/go-playground/validator.v9/regexes.go b/vendor/gopkg.in/go-playground/validator.v9/regexes.go index ec78cebc5cfbc7f5df98443de8835fdb0c2bf3b2..78f3ea0aa1b7939834b8e14934201c4101b933fc 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/regexes.go +++ b/vendor/gopkg.in/go-playground/validator.v9/regexes.go @@ -30,7 +30,8 @@ const ( latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" sSNRegexString = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` - hostnameRegexString = `^[a-zA-Z][a-zA-Z0-9\-\.]+[a-z-Az0-9]$` + hostnameRegexStringRFC952 = `^[a-zA-Z][a-zA-Z0-9\-\.]+[a-z-Az0-9]$` // https://tools.ietf.org/html/rfc952 + hostnameRegexStringRFC1123 = `^[a-zA-Z0-9][a-zA-Z0-9\-\.]+[a-z-Az0-9]$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123 ) var ( @@ -61,5 +62,6 @@ var ( latitudeRegex = regexp.MustCompile(latitudeRegexString) longitudeRegex = regexp.MustCompile(longitudeRegexString) sSNRegex = regexp.MustCompile(sSNRegexString) - hostnameRegex = regexp.MustCompile(hostnameRegexString) + hostnameRegexRFC952 = regexp.MustCompile(hostnameRegexStringRFC952) + hostnameRegexRFC1123 = regexp.MustCompile(hostnameRegexStringRFC1123) ) diff --git a/vendor/gopkg.in/go-playground/validator.v9/util.go b/vendor/gopkg.in/go-playground/validator.v9/util.go index a01d4b16ed8862b6cd3f9ae594672e64870e75ba..16a5517c97b1ed78248c35d21ac9f5f49c403c36 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/util.go +++ b/vendor/gopkg.in/go-playground/validator.v9/util.go @@ -80,7 +80,7 @@ BEGIN: typ := current.Type() fld := namespace - ns := namespace + var ns string if typ != timeType { diff --git a/vendor/gopkg.in/go-playground/validator.v9/validator.go b/vendor/gopkg.in/go-playground/validator.v9/validator.go index 5fbb1660db435fd08d12b60f580346478245a4da..483e0a2bea8f688b51d077e8aa1c3ba85491b24b 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/validator.go +++ b/vendor/gopkg.in/go-playground/validator.v9/validator.go @@ -14,24 +14,19 @@ type validate struct { ns []byte actualNs []byte errs ValidationErrors + includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise + ffn FilterFunc + slflParent reflect.Value // StructLevel & FieldLevel + slCurrent reflect.Value // StructLevel & FieldLevel + flField reflect.Value // StructLevel & FieldLevel + cf *cField // StructLevel & FieldLevel + ct *cTag // StructLevel & FieldLevel + misc []byte // misc reusable + str1 string // misc reusable + str2 string // misc reusable + fldIsPointer bool // StructLevel & FieldLevel isPartial bool hasExcludes bool - includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise - - ffn FilterFunc - - // StructLevel & FieldLevel fields - slflParent reflect.Value - slCurrent reflect.Value - flField reflect.Value - fldIsPointer bool - cf *cField - ct *cTag - - // misc reusable values - misc []byte - str1 string - str2 string } // parent and current will be the same the first run of validateStruct @@ -127,7 +122,6 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr } if kind == reflect.Invalid { - v.errs = append(v.errs, &fieldError{ v: v.v, diff --git a/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go b/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go index f4b49a84c6a8ebc226a9d99994c3f4c6d5ab7446..e84b452dba7779277989e07c1dd9ef477756f173 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go +++ b/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go @@ -370,39 +370,37 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields . typ := val.Type() name := typ.Name() - if fields != nil { - for _, k := range fields { + for _, k := range fields { - flds := strings.Split(k, namespaceSeparator) - if len(flds) > 0 { + flds := strings.Split(k, namespaceSeparator) + if len(flds) > 0 { - vd.misc = append(vd.misc[0:0], name...) - vd.misc = append(vd.misc, '.') - - for _, s := range flds { + vd.misc = append(vd.misc[0:0], name...) + vd.misc = append(vd.misc, '.') - idx := strings.Index(s, leftBracket) + for _, s := range flds { - if idx != -1 { - for idx != -1 { - vd.misc = append(vd.misc, s[:idx]...) - vd.includeExclude[string(vd.misc)] = struct{}{} + idx := strings.Index(s, leftBracket) - idx2 := strings.Index(s, rightBracket) - idx2++ - vd.misc = append(vd.misc, s[idx:idx2]...) - vd.includeExclude[string(vd.misc)] = struct{}{} - s = s[idx2:] - idx = strings.Index(s, leftBracket) - } - } else { + if idx != -1 { + for idx != -1 { + vd.misc = append(vd.misc, s[:idx]...) + vd.includeExclude[string(vd.misc)] = struct{}{} - vd.misc = append(vd.misc, s...) + idx2 := strings.Index(s, rightBracket) + idx2++ + vd.misc = append(vd.misc, s[idx:idx2]...) vd.includeExclude[string(vd.misc)] = struct{}{} + s = s[idx2:] + idx = strings.Index(s, leftBracket) } + } else { - vd.misc = append(vd.misc, '.') + vd.misc = append(vd.misc, s...) + vd.includeExclude[string(vd.misc)] = struct{}{} } + + vd.misc = append(vd.misc, '.') } } }