diff --git a/aggregator/aggregator.go b/aggregator/aggregator.go index 9dd0804..235ba92 100644 --- a/aggregator/aggregator.go +++ b/aggregator/aggregator.go @@ -35,7 +35,7 @@ type Aggregator struct { } // NewAggregator returns new aggregator object -func NewAggregator(db core.DB) *Aggregator { +func NewAggregator() *Aggregator { // create logger logger := common.Logger.With("module", AggregatingService) LoadedBazooka, err := core.NewPreLoadedBazooka() @@ -44,7 +44,11 @@ func NewAggregator(db core.DB) *Aggregator { } aggregator := &Aggregator{} aggregator.BaseService = *core.NewBaseService(logger, AggregatingService, aggregator) - aggregator.DB = db + DB, err := core.NewDB() + if err != nil { + panic(err) + } + aggregator.DB = DB aggregator.LoadedBazooka = LoadedBazooka return aggregator } @@ -64,7 +68,7 @@ func (a *Aggregator) OnStart() error { // OnStop stops all necessary go routines func (a *Aggregator) OnStop() { a.BaseService.OnStop() // Always call the overridden method. - + a.DB.Close() // cancel ack process a.cancelAggregating() } @@ -133,8 +137,9 @@ func (a *Aggregator) ProcessTx(txs []core.Tx) error { return err } - fromAccProof, toAccProof, PDAproof, err := a.GetTxVerificationData(tx) + fromAccProof, toAccProof, PDAproof, err := tx.GetVerificationData() if err != nil { + a.Logger.Error("Unable to create verification data", "error", err) return err } @@ -157,57 +162,5 @@ func (a *Aggregator) ProcessTx(txs []core.Tx) error { currentRoot = updatedRoot } - return nil } - -// GetTxVerificationData fetches all the data required to prove validity fo transaction -func (a *Aggregator) GetTxVerificationData(tx core.Tx) (fromMerkleProof, toMerkleProof core.AccountMerkleProof, PDAProof core.PDAMerkleProof, err error) { - fromAcc, err := a.DB.GetAccountByID(tx.From) - if err != nil { - return - } - - fromSiblings, err := a.DB.GetSiblings(fromAcc.Path) - if err != nil { - return - } - fromMerkleProof = core.NewAccountMerkleProof(fromAcc, fromSiblings) - - toAcc, err := a.DB.GetAccountByID(tx.To) - if err != nil { - return - } - var toSiblings []core.UserAccount - - mysqlTx := a.DB.Instance.Begin() - defer func() { - if r := recover(); r != nil { - mysqlTx.Rollback() - } - }() - dbCopy, _ := core.NewDB() - dbCopy.Instance = mysqlTx - - updatedFromAccountBytes, _, err := a.LoadedBazooka.ApplyTx(fromMerkleProof, tx) - if err != nil { - return - } - - fromAcc.Data = updatedFromAccountBytes - err = dbCopy.UpdateAccount(fromAcc) - if err != nil { - return - } - - // TODO add a check to ensure that DB copy of state matches the one returned by ApplyTransferTx - toSiblings, err = dbCopy.GetSiblings(toAcc.Path) - if err != nil { - return - } - - toMerkleProof = core.NewAccountMerkleProof(toAcc, toSiblings) - PDAProof = core.NewPDAProof(fromAcc.Path, fromAcc.PublicKey, fromSiblings) - mysqlTx.Rollback() - return fromMerkleProof, toMerkleProof, PDAProof, nil -} diff --git a/cmd/start.go b/cmd/start.go index 96b2145..7efc7d2 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -44,7 +44,7 @@ func StartCmd() *cobra.Command { // // create aggregator service - aggregator := agg.NewAggregator(core.DBInstance) + aggregator := agg.NewAggregator() // create the syncer service syncer := listener.NewSyncer() @@ -189,6 +189,7 @@ func LoadGenesisData(genesis config.Genesis) { newParams := core.Params{StakeAmount: genesis.StakeAmount, MaxDepth: genesis.MaxTreeDepth, MaxDepositSubTreeHeight: genesis.MaxDepositSubTreeHeight} core.DBInstance.UpdateStakeAmount(newParams.StakeAmount) core.DBInstance.UpdateMaxDepth(newParams.MaxDepth) + core.DBInstance.UpdateFinalisationTimePerBatch(40320) core.DBInstance.UpdateDepositSubTreeHeight(newParams.MaxDepositSubTreeHeight) // load sync status diff --git a/contracts/rolluputils/rolluputils.abi b/contracts/rolluputils/rolluputils.abi index 10aa387..b5249f1 100644 --- a/contracts/rolluputils/rolluputils.abi +++ b/contracts/rolluputils/rolluputils.abi @@ -392,6 +392,42 @@ "stateMutability": "pure", "type": "function" }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "txBytes", + "type": "bytes" + } + ], + "name": "DecompressTx", + "outputs": [ + { + "internalType": "uint256", + "name": "from", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "to", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sig", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, { "constant": true, "inputs": [ @@ -781,4 +817,4 @@ "stateMutability": "view", "type": "function" } - ] \ No newline at end of file + ] diff --git a/contracts/rolluputils/rolluputils.go b/contracts/rolluputils/rolluputils.go index a19103c..a41679b 100644 --- a/contracts/rolluputils/rolluputils.go +++ b/contracts/rolluputils/rolluputils.go @@ -52,7 +52,7 @@ type TypesUserAccount struct { } // RolluputilsABI is the input ABI used to generate the binding from. -const RolluputilsABI = "[{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"pubkey\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.PDALeaf\",\"name\":\"_PDA_Leaf\",\"type\":\"tuple\"}],\"name\":\"PDALeafToHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"internalType\":\"structTypes.UserAccount\",\"name\":\"original_account\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"new_balance\",\"type\":\"uint256\"}],\"name\":\"UpdateBalanceInAccount\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"internalType\":\"structTypes.UserAccount\",\"name\":\"updated_account\",\"type\":\"tuple\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"internalType\":\"structTypes.UserAccount\",\"name\":\"account\",\"type\":\"tuple\"}],\"name\":\"BalanceFromAccount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"accountBytes\",\"type\":\"bytes\"}],\"name\":\"AccountFromBytes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"internalType\":\"structTypes.UserAccount\",\"name\":\"account\",\"type\":\"tuple\"}],\"name\":\"BytesFromAccount\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"}],\"name\":\"BytesFromAccountDeconstructed\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"}],\"name\":\"getAccountHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"internalType\":\"structTypes.UserAccount\",\"name\":\"account\",\"type\":\"tuple\"}],\"name\":\"HashFromAccount\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fromIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.Transaction\",\"name\":\"_tx\",\"type\":\"tuple\"}],\"name\":\"CompressTx\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sig\",\"type\":\"bytes\"}],\"name\":\"CompressTxWithMessage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"txBytes\",\"type\":\"bytes\"}],\"name\":\"TxFromBytesDeconstructed\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"from\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"to\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"txBytes\",\"type\":\"bytes\"}],\"name\":\"TxFromBytes\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fromIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.Transaction\",\"name\":\"\",\"type\":\"tuple\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fromIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.Transaction\",\"name\":\"_tx\",\"type\":\"tuple\"}],\"name\":\"BytesFromTx\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"from\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"to\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"BytesFromTxDeconstructed\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fromIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.Transaction\",\"name\":\"_tx\",\"type\":\"tuple\"}],\"name\":\"HashFromTx\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"fromIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"getTxSignBytes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"pub\",\"type\":\"bytes\"}],\"name\":\"calculateAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"GetGenesisLeaves\",\"outputs\":[{\"internalType\":\"bytes32[2]\",\"name\":\"leaves\",\"type\":\"bytes32[2]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"GetGenesisDataBlocks\",\"outputs\":[{\"internalType\":\"bytes[2]\",\"name\":\"dataBlocks\",\"type\":\"bytes[2]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]" +const RolluputilsABI = "[{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"pubkey\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.PDALeaf\",\"name\":\"_PDA_Leaf\",\"type\":\"tuple\"}],\"name\":\"PDALeafToHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"internalType\":\"structTypes.UserAccount\",\"name\":\"original_account\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"new_balance\",\"type\":\"uint256\"}],\"name\":\"UpdateBalanceInAccount\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"internalType\":\"structTypes.UserAccount\",\"name\":\"updated_account\",\"type\":\"tuple\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"internalType\":\"structTypes.UserAccount\",\"name\":\"account\",\"type\":\"tuple\"}],\"name\":\"BalanceFromAccount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"accountBytes\",\"type\":\"bytes\"}],\"name\":\"AccountFromBytes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"internalType\":\"structTypes.UserAccount\",\"name\":\"account\",\"type\":\"tuple\"}],\"name\":\"BytesFromAccount\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"}],\"name\":\"BytesFromAccountDeconstructed\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"}],\"name\":\"getAccountHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"ID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"internalType\":\"structTypes.UserAccount\",\"name\":\"account\",\"type\":\"tuple\"}],\"name\":\"HashFromAccount\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fromIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.Transaction\",\"name\":\"_tx\",\"type\":\"tuple\"}],\"name\":\"CompressTx\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"txBytes\",\"type\":\"bytes\"}],\"name\":\"DecompressTx\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"from\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"to\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"sig\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sig\",\"type\":\"bytes\"}],\"name\":\"CompressTxWithMessage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"txBytes\",\"type\":\"bytes\"}],\"name\":\"TxFromBytesDeconstructed\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"from\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"to\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"txBytes\",\"type\":\"bytes\"}],\"name\":\"TxFromBytes\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fromIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.Transaction\",\"name\":\"\",\"type\":\"tuple\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fromIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.Transaction\",\"name\":\"_tx\",\"type\":\"tuple\"}],\"name\":\"BytesFromTx\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"from\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"to\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"BytesFromTxDeconstructed\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fromIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.Transaction\",\"name\":\"_tx\",\"type\":\"tuple\"}],\"name\":\"HashFromTx\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"fromIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"getTxSignBytes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"pub\",\"type\":\"bytes\"}],\"name\":\"calculateAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"GetGenesisLeaves\",\"outputs\":[{\"internalType\":\"bytes32[2]\",\"name\":\"leaves\",\"type\":\"bytes32[2]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"GetGenesisDataBlocks\",\"outputs\":[{\"internalType\":\"bytes[2]\",\"name\":\"dataBlocks\",\"type\":\"bytes[2]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]" // Rolluputils is an auto generated Go binding around an Ethereum contract. type Rolluputils struct { @@ -422,6 +422,50 @@ func (_Rolluputils *RolluputilsCallerSession) CompressTxWithMessage(message []by return _Rolluputils.Contract.CompressTxWithMessage(&_Rolluputils.CallOpts, message, sig) } +// DecompressTx is a free data retrieval call binding the contract method 0xeedeb9d9. +// +// Solidity: function DecompressTx(bytes txBytes) pure returns(uint256 from, uint256 to, uint256 amount, bytes sig) +func (_Rolluputils *RolluputilsCaller) DecompressTx(opts *bind.CallOpts, txBytes []byte) (struct { + From *big.Int + To *big.Int + Amount *big.Int + Sig []byte +}, error) { + ret := new(struct { + From *big.Int + To *big.Int + Amount *big.Int + Sig []byte + }) + out := ret + err := _Rolluputils.contract.Call(opts, out, "DecompressTx", txBytes) + return *ret, err +} + +// DecompressTx is a free data retrieval call binding the contract method 0xeedeb9d9. +// +// Solidity: function DecompressTx(bytes txBytes) pure returns(uint256 from, uint256 to, uint256 amount, bytes sig) +func (_Rolluputils *RolluputilsSession) DecompressTx(txBytes []byte) (struct { + From *big.Int + To *big.Int + Amount *big.Int + Sig []byte +}, error) { + return _Rolluputils.Contract.DecompressTx(&_Rolluputils.CallOpts, txBytes) +} + +// DecompressTx is a free data retrieval call binding the contract method 0xeedeb9d9. +// +// Solidity: function DecompressTx(bytes txBytes) pure returns(uint256 from, uint256 to, uint256 amount, bytes sig) +func (_Rolluputils *RolluputilsCallerSession) DecompressTx(txBytes []byte) (struct { + From *big.Int + To *big.Int + Amount *big.Int + Sig []byte +}, error) { + return _Rolluputils.Contract.DecompressTx(&_Rolluputils.CallOpts, txBytes) +} + // GetGenesisDataBlocks is a free data retrieval call binding the contract method 0x5e31c831. // // Solidity: function GetGenesisDataBlocks() view returns(bytes[2] dataBlocks) diff --git a/core/DepositTree.go b/core/DepositTree.go index 63330ed..539dd41 100644 --- a/core/DepositTree.go +++ b/core/DepositTree.go @@ -106,7 +106,6 @@ func (db *DB) FinaliseDepositsAndAddBatch(accountsRoot ByteArray, pathToDepositS func (db *DB) FinaliseDeposits(pendingAccs []UserAccount, pathToDepositSubTree uint64, maxTreeDepth uint64) error { var accounts []UserAccount - // fetch 2**DepositSubTree inactive accounts ordered by path err := db.Instance.Limit(len(pendingAccs)).Order("path").Where("status = ?", STATUS_NON_INITIALIZED).Find(&accounts).Error if err != nil { diff --git a/core/batch.go b/core/batch.go index bd8dbcb..abf055f 100644 --- a/core/batch.go +++ b/core/batch.go @@ -1,95 +1,21 @@ package core import ( - "fmt" "math/big" - - "encoding/json" ) // Batch is the batches that need to be submitted on-chain periodically type Batch struct { - Index uint64 - StateRoot ByteArray + BatchID uint64 + StateRoot string Committer string - TxRoot ByteArray + TxRoot string StakeAmount uint64 FinalisesOn big.Int SubmissionHash string - TransactionsIncluded [][]byte + TransactionsIncluded []byte `gorm:"size:1000000"` BatchType uint64 -} - -func (b *Batch) DBModel() (BatchModel, error) { - encodedTxs, err := json.Marshal(b.TransactionsIncluded) - if err != nil { - return BatchModel{}, err - } - encodedStateRoot, err := json.Marshal(b.StateRoot) - if err != nil { - return BatchModel{}, err - } - encodedTxRoot, err := json.Marshal(b.TxRoot) - if err != nil { - return BatchModel{}, err - } - finalisationBlockBytes := b.FinalisesOn.Bytes() - newBatchModel := BatchModel{ - Index: b.Index, - StateRoot: encodedStateRoot, - Committer: b.Committer, - TxRoot: encodedTxRoot, - StakeAmount: b.StakeAmount, - FinalisesOn: finalisationBlockBytes, - SubmissionHash: b.SubmissionHash, - TransactionsIncluded: encodedTxs, - } - return newBatchModel, nil -} - -// BatchModel represents the actual stuff stored in the DB -// We are encoding the whole struct because we will save some operations -// if we can just read the model for data in some cases and only decode when we need the encoded data -type BatchModel struct { - Index uint64 - StateRoot []byte - Committer string - TxRoot []byte - StakeAmount uint64 - FinalisesOn []byte - SubmissionHash string - TransactionsIncluded []byte `gorm:"size:10000"` -} - -func (b *BatchModel) Batch() (Batch, error) { - var decodedTxs [][]byte - err := json.Unmarshal(b.TransactionsIncluded, &decodedTxs) - if err != nil { - return Batch{}, err - } - var decodedStateRoot ByteArray - err = json.Unmarshal(b.StateRoot, &decodedStateRoot) - if err != nil { - return Batch{}, err - } - var decodedTxRoot ByteArray - err = json.Unmarshal(b.TxRoot, &decodedTxRoot) - if err != nil { - return Batch{}, err - } - finalisationBlockBN := big.NewInt(0) - finalisationBlockBN.SetBytes(b.FinalisesOn) - newBatch := Batch{ - Index: b.Index, - StateRoot: decodedStateRoot, - Committer: b.Committer, - TxRoot: decodedTxRoot, - StakeAmount: b.StakeAmount, - FinalisesOn: *finalisationBlockBN, - SubmissionHash: b.SubmissionHash, - TransactionsIncluded: decodedTxs, - } - return newBatch, nil + Status uint64 } func (db *DB) GetAllBatches() (batches []Batch, err error) { @@ -103,8 +29,8 @@ func (db *DB) GetAllBatches() (batches []Batch, err error) { } func (db *DB) GetLatestBatch() (batch Batch, err error) { - if err := db.Instance.First(&batch).Error; err != nil { - return batch, ErrRecordNotFound(fmt.Sprintf("unable to find latest batch")) + if err := db.Instance.Order("batch_id desc").First(&batch).Error; err != nil { + return batch, err } return batch, nil } @@ -116,9 +42,16 @@ func (db *DB) GetBatchCount() (int, error) { } func (db *DB) AddNewBatch(batch Batch) error { - batchModel, err := batch.DBModel() - if err != nil { - return err + return db.Instance.Create(batch).Error +} + +func (db *DB) GetBatchByIndex(index uint64) (batch Batch, err error) { + if err := db.Instance.Where("batch_id = ?", index).Find(&batch).Error; err != nil { + return batch, err } - return db.Instance.Create(batchModel).Error + return batch, nil +} + +func (db *DB) CommitBatch(batch Batch) error { + return db.Instance.Update(batch).Error } diff --git a/core/bazooka.go b/core/bazooka.go index 8f027df..9174fb1 100644 --- a/core/bazooka.go +++ b/core/bazooka.go @@ -244,6 +244,21 @@ func (b *Bazooka) CompressTransferTx(tx Tx) ([]byte, error) { return b.RollupUtils.CompressTxWithMessage(&opts, tx.Data, sigBytes) } +func (b *Bazooka) DecompressTransferTxs(compressedTxs [][]byte) (from, to, amount []*big.Int, sig [][]byte, err error) { + for _, compressedTx := range compressedTxs { + opts := bind.CallOpts{From: config.OperatorAddress} + decompressedTx, err := b.RollupUtils.DecompressTx(&opts, compressedTx) + if err != nil { + return from, to, amount, sig, err + } + from = append(from, decompressedTx.From) + to = append(to, decompressedTx.To) + amount = append(amount, decompressedTx.Amount) + sig = append(sig, decompressedTx.Sig) + } + return from, to, amount, sig, nil +} + func (b *Bazooka) EncodeTransferTx(from, to, token, nonce, amount, txType int64) ([]byte, error) { opts := bind.CallOpts{From: config.OperatorAddress} return b.RollupUtils.BytesFromTxDeconstructed(&opts, big.NewInt(from), big.NewInt(to), big.NewInt(token), big.NewInt(nonce), big.NewInt(txType), big.NewInt(amount)) @@ -275,20 +290,7 @@ func (b *Bazooka) GetGenesisAccounts() (genesisAccount []UserAccount, err error) if err != nil { return } - for _, account := range accounts { - ID, _, _, _, _ := b.DecodeAccount(account) - genesisAccount = append(genesisAccount, *NewUserAccount(ID.Uint64(), STATUS_ACTIVE, "", UintToString(ID.Uint64()), account)) - } - return -} -func (b *Bazooka) GetZeroValue() (genesisAccount []UserAccount, err error) { - opts := bind.CallOpts{From: config.OperatorAddress} - // get genesis accounts - accounts, err := b.RollupUtils.GetGenesisDataBlocks(&opts) - if err != nil { - return - } for _, account := range accounts { ID, _, _, _, _ := b.DecodeAccount(account) genesisAccount = append(genesisAccount, *NewUserAccount(ID.Uint64(), STATUS_ACTIVE, "", UintToString(ID.Uint64()), account)) @@ -380,7 +382,6 @@ func (b *Bazooka) SubmitBatch(updatedRoot ByteArray, txs []Tx) error { "txs", len(txs), ) - var compressedTxs [][]byte for _, tx := range txs { compressedTx, err := tx.Compress() @@ -411,16 +412,34 @@ func (b *Bazooka) SubmitBatch(updatedRoot ByteArray, txs []Tx) error { return err } + latestBatch, err := DBInstance.GetLatestBatch() + if err != nil { + return err + } + + newBatch := Batch{ + BatchID: latestBatch.BatchID + 1, + StateRoot: updatedRoot.String(), + Committer: config.OperatorAddress.String(), + Status: BATCH_BROADCASTED, + } + b.log.Info("Broadcasting a new batch", "newBatch", newBatch) + err = DBInstance.AddNewBatch(newBatch) + if err != nil { + return err + } tx, err := b.RollupContract.SubmitBatch(auth, compressedTxs, updatedRoot) if err != nil { return err } + b.log.Info("Sent a new batch!", "txHash", tx.Hash().String()) return nil } func GetTxsFromInput(input map[string]interface{}) (txs [][]byte) { - return input["_txs"].([][]byte) + data := input["_txs"].([][]byte) + return data } func (b *Bazooka) GenerateAuthObj(client *ethclient.Client, callMsg ethereum.CallMsg) (auth *bind.TransactOpts, err error) { diff --git a/core/constants.go b/core/constants.go index 094f72e..4655ee8 100644 --- a/core/constants.go +++ b/core/constants.go @@ -20,6 +20,9 @@ const ( TX_STATUS_PROCESSED = 300 TX_STATUS_REVERTED = 400 + BATCH_BROADCASTED = 100 + BATCH_COMMITTED = 200 + BATCH_TYPE = 1 TX_TRANSFER_TYPE = 1 ) diff --git a/core/params.go b/core/params.go index d5744d9..710770a 100644 --- a/core/params.go +++ b/core/params.go @@ -20,6 +20,8 @@ type Params struct { // DepositSubTreeHeight is the maximum height of the deposit subtree that the coordinator wants to merge // It is set on the contract and will be updated when that value changes MaxDepositSubTreeHeight uint64 `json:"maxDepositSubTreeHeight"` + + FinalisationTime uint64 `json:"finalisationTime"` } // Maintains sync information @@ -71,6 +73,16 @@ func (db *DB) UpdateStakeAmount(newStakeAmount uint64) error { return nil } +// UpdateFinalisationTimePerBatch updates the max depth +func (db *DB) UpdateFinalisationTimePerBatch(finalisationDuration uint64) error { + var updatedParams Params + updatedParams.MaxDepth = finalisationDuration + if err := db.Instance.Table("params").Assign(Params{FinalisationTime: finalisationDuration}).FirstOrCreate(&updatedParams).Error; err != nil { + return err + } + return nil +} + // UpdateMaxDepth updates the max depth func (db *DB) UpdateMaxDepth(newDepth uint64) error { var updatedParams Params @@ -91,6 +103,14 @@ func (db *DB) UpdateDepositSubTreeHeight(newHeight uint64) error { return nil } +func (db *DB) GetBatchFinalisationTime() (uint64, error) { + var params Params + if err := db.Instance.First(¶ms).Error; err != nil { + return 0, err + } + return params.FinalisationTime, nil +} + // GetParams gets params from the DB func (db *DB) GetParams() (params Params, err error) { if err := db.Instance.First(¶ms).Error; err != nil { diff --git a/core/tx.go b/core/tx.go index adb4e3b..478027c 100644 --- a/core/tx.go +++ b/core/tx.go @@ -69,6 +69,7 @@ func (tx *Tx) Apply(updatedFrom, updatedTo []byte) error { if err != nil { return err } + defer db.Close() // begin a transaction mysqlTx := db.Instance.Begin() @@ -185,9 +186,64 @@ func (tx *Tx) UpdateStatus(status uint64) error { return DBInstance.Instance.Model(&tx).Update("status", status).Error } +// GetVerificationData fetches all the data required to prove validity fo transaction +func (tx *Tx) GetVerificationData() (fromMerkleProof, toMerkleProof AccountMerkleProof, PDAProof PDAMerkleProof, err error) { + fromAcc, err := DBInstance.GetAccountByID(tx.From) + if err != nil { + return + } + fromSiblings, err := DBInstance.GetSiblings(fromAcc.Path) + if err != nil { + return + } + fromMerkleProof = NewAccountMerkleProof(fromAcc, fromSiblings) + toAcc, err := DBInstance.GetAccountByID(tx.To) + if err != nil { + return + } + var toSiblings []UserAccount + mysqlTx := DBInstance.Instance.Begin() + defer func() { + if r := recover(); r != nil { + mysqlTx.Rollback() + } + }() + dbCopy, _ := NewDB() + dbCopy.Instance = mysqlTx + updatedFromAccountBytes, _, err := LoadedBazooka.ApplyTx(fromMerkleProof, *tx) + if err != nil { + return + } + + fromAcc.Data = updatedFromAccountBytes + err = dbCopy.UpdateAccount(fromAcc) + if err != nil { + return + } + + // TODO add a check to ensure that DB copy of state matches the one returned by ApplyTransferTx + toSiblings, err = dbCopy.GetSiblings(toAcc.Path) + if err != nil { + return + } + + toMerkleProof = NewAccountMerkleProof(toAcc, toSiblings) + PDAProof = NewPDAProof(fromAcc.Path, fromAcc.PublicKey, fromSiblings) + mysqlTx.Rollback() + return fromMerkleProof, toMerkleProof, PDAProof, nil +} + func rlpHash(x interface{}) (h ethCmn.Hash) { hw := sha3.NewLegacyKeccak256() rlp.Encode(hw, x) hw.Sum(h[:0]) return h } + +func ConcatTxs(txs [][]byte) []byte { + var concatenatedTxs []byte + for _, tx := range txs { + concatenatedTxs = append(concatenatedTxs, tx[:]...) + } + return concatenatedTxs +} diff --git a/listener/processors.go b/listener/processors.go index 83477d5..732752d 100644 --- a/listener/processors.go +++ b/listener/processors.go @@ -9,10 +9,13 @@ import ( "github.com/BOPR/core" "github.com/ethereum/go-ethereum/accounts/abi" ethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/jinzhu/gorm" "github.com/BOPR/contracts/logger" ) +const ZEROROOT = "0x0000000000000000000000000000000000000000000000000000000000000000" + func (s *Syncer) processDepositQueued(eventName string, abiObject *abi.ABI, vLog *ethTypes.Log) { s.Logger.Info("New deposit found") @@ -122,6 +125,7 @@ func (s *Syncer) processNewBatch(eventName string, abiObject *abi.ABI, vLog *eth fmt.Println("Unable to unpack log:", err) panic(err) } + s.Logger.Info( "⬜ New event found", "event", eventName, @@ -130,50 +134,77 @@ func (s *Syncer) processNewBatch(eventName string, abiObject *abi.ABI, vLog *eth "NewStateRoot", core.ByteArray(event.UpdatedRoot).String(), "Committer", event.Committer.String(), ) + params, err := s.DBInstance.GetParams() if err != nil { return } - var txs [][]byte - var stakeAmount uint64 - if event.Index.Uint64() == 0 { - stakeAmount = 0 - } else if "0x0000000000000000000000000000000000000000000000000000000000000000" == core.ByteArray(event.Txroot).String() { - stakeAmount = params.StakeAmount - } else { + // if the batch has some txs, parse them + var txs [][]byte + if ZEROROOT != core.ByteArray(event.Txroot).String() { // pick the calldata for the batch - txHash := vLog.TxHash - txs, err = s.loadedBazooka.FetchBatchInputData(txHash) + txs, err = s.loadedBazooka.FetchBatchInputData(vLog.TxHash) if err != nil { // TODO do something with this error panic(err) } - stakeAmount = params.StakeAmount } - // TODO run the transactions through ProcessTx present on-chain - // if any tx is fraud, challenge + batch, err := s.DBInstance.GetBatchByIndex(event.Index.Uint64()) + // if we havent seen the batch, apply txs and store batch + if err != nil && gorm.IsRecordNotFoundError(err) { + s.Logger.Info("Found a new batch, applying transactions and adding new batch", "index", event.Index.Uint64) + err := s.ApplyTxsFromBatch(txs) + if err != nil { + panic(err) + } - // TODO apply transactions and match state root + newBatch := core.Batch{ + BatchID: event.Index.Uint64(), + StateRoot: core.ByteArray(event.UpdatedRoot).String(), + TxRoot: core.ByteArray(event.Txroot).String(), + TransactionsIncluded: core.ConcatTxs(txs), + Committer: event.Committer.String(), + StakeAmount: params.StakeAmount, + FinalisesOn: *big.NewInt(int64(params.FinalisationTime)), + Status: core.BATCH_COMMITTED, + } - // create a new batch - newBatch := core.Batch{ - Index: event.Index.Uint64(), - StateRoot: core.ByteArray(event.UpdatedRoot), - TxRoot: core.ByteArray(event.Txroot), - TransactionsIncluded: txs, - Committer: event.Committer.String(), - StakeAmount: stakeAmount, - FinalisesOn: *big.NewInt(100), + err = s.DBInstance.AddNewBatch(newBatch) + if err != nil { + // TODO do something with this error + panic(err) + } + return + } else if err != nil { + s.Logger.Error("Unable to fetch batch", "index", event.Index, "err", err) + return } - err = s.DBInstance.AddNewBatch(newBatch) - if err != nil { - // TODO do something with this error - panic(err) + // if batch is present but in a non committed state we parse txs and commit batch + if batch.Status != core.BATCH_COMMITTED { + s.Logger.Info("Found a non committed batch") + if batch.StateRoot != core.ByteArray(event.UpdatedRoot).String() { + // State root mismatch error + } + // batch broadcasted by us + // txs applied but batch needs to be committed + // TODO add batch type + newBatch := core.Batch{ + BatchID: event.Index.Uint64(), + StateRoot: core.ByteArray(event.UpdatedRoot).String(), + TxRoot: core.ByteArray(event.Txroot).String(), + TransactionsIncluded: core.ConcatTxs(txs), + Committer: event.Committer.String(), + StakeAmount: params.StakeAmount, + FinalisesOn: *big.NewInt(int64(params.FinalisationTime)), + Status: core.BATCH_COMMITTED, + } + s.DBInstance.CommitBatch(newBatch) } } + func (s *Syncer) processRegisteredToken(eventName string, abiObject *abi.ABI, vLog *ethTypes.Log) { s.Logger.Info("New token registered") event := new(logger.LoggerRegisteredToken) @@ -208,3 +239,62 @@ func (s *Syncer) SendDepositFinalisationTx() { err = s.loadedBazooka.FireDepositFinalisation(nodeToBeReplaced, siblings, params.MaxDepositSubTreeHeight) } + +func (s *Syncer) ApplyTxsFromBatch(txs [][]byte) error { + if len(txs) == 0 { + s.Logger.Info("No txs to apply") + return nil + } + + // Decompress all txs + from, to, amount, sig, err := s.loadedBazooka.DecompressTransferTxs(txs) + if err != nil { + return err + } + s.Logger.Debug("Fetched all data", "from", from, "to", to, "amount", amount, "sig", sig) + + var coreTxs []core.Tx + for i := range txs { + fromAccount, err := s.DBInstance.GetAccountByID(from[i].Uint64()) + if err != nil { + return err + } + _, _, nonce, token, err := s.loadedBazooka.DecodeAccount(fromAccount.Data) + if err != nil { + return err + } + s.Logger.Debug("Decoded account", "nonce", nonce, "token", token) + txData, err := s.loadedBazooka.EncodeTransferTx(from[i].Int64(), to[i].Int64(), token.Int64(), nonce.Int64(), amount[i].Int64(), core.TX_TRANSFER_TYPE) + if err != nil { + return err + } + + coreTx := core.NewTx(fromAccount.AccountID, to[i].Uint64(), core.TX_TRANSFER_TYPE, txData, hex.EncodeToString(sig[i])) + coreTxs = append(coreTxs, coreTx) + fromMP, toMP, _, err := coreTx.GetVerificationData() + if err != nil { + return err + } + + updatedFromAccData, _, err := s.loadedBazooka.ApplyTx(fromMP, coreTx) + if err != nil { + return err + } + + updatedToAccData, _, err := s.loadedBazooka.ApplyTx(toMP, coreTx) + if err != nil { + return err + } + + err = coreTx.Apply(updatedFromAccData, updatedToAccData) + if err != nil { + return err + } + // // validate updated root post application + // root, err := s.DBInstance.GetRoot() + // if err != nil { + // return err + // } + } + return nil +} diff --git a/migrations/1585168847_migration.go b/migrations/1585168847_migration.go index b121093..fbc9514 100644 --- a/migrations/1585168847_migration.go +++ b/migrations/1585168847_migration.go @@ -13,8 +13,8 @@ func init() { if !db.HasTable(&types.Tx{}) { db.CreateTable(&types.Tx{}) } - if !db.HasTable(&types.BatchModel{}) { - db.CreateTable(&types.BatchModel{}) + if !db.HasTable(&types.Batch{}) { + db.CreateTable(&types.Batch{}) } if !db.HasTable(&types.SyncStatus{}) { db.CreateTable(&types.SyncStatus{}) @@ -35,7 +35,7 @@ func init() { }, Down: func(db *gorm.DB) error { db.DropTableIfExists(&types.Tx{}) - db.DropTableIfExists(&types.BatchModel{}) + db.DropTableIfExists(&types.Batch{}) db.DropTableIfExists(&types.Params{}) db.DropTableIfExists(&types.SyncStatus{}) db.DropTableIfExists(&types.Token{}) diff --git a/simulator/main.go b/simulator/main.go index af31a9f..1c1683e 100644 --- a/simulator/main.go +++ b/simulator/main.go @@ -37,7 +37,6 @@ func NewSimulator() *Simulator { if err != nil { panic(err) } - sim.LoadedBazooka, err = core.NewPreLoadedBazooka() if err != nil { panic(err) @@ -63,7 +62,7 @@ func (s *Simulator) OnStart() error { // OnStop stops all necessary go routines func (s *Simulator) OnStop() { s.BaseService.OnStop() // Always call the overridden method. - + s.DB.Close() s.cancelSimulator() } @@ -97,7 +96,7 @@ func (s *Simulator) sendTxsToAndFro() { s.toSwap = !s.toSwap } - for i := 0; i < 7; i++ { + for i := 0; i < 3; i++ { latestFromAcc, err := s.DB.GetAccountByID(FromID) if err != nil { s.Logger.Error("unable to fetch latest account", "error", err) diff --git a/tests/account_test.go b/tests/account_test.go index 17e64e7..b424695 100644 --- a/tests/account_test.go +++ b/tests/account_test.go @@ -32,7 +32,7 @@ func TestTxProcessing(t *testing.T) { bazooka, err := core.NewPreLoadedBazooka() require.Equal(t, err, nil, "Error while creating bazooka") core.LoadedBazooka = bazooka - agg := aggregator.NewAggregator(db) + agg := aggregator.NewAggregator() genesisAccounts, err := core.LoadedBazooka.GetGenesisAccounts() require.Equal(t, err, nil, "error loading genesis accounts") zeroAccount := genesisAccounts[0]