Compare commits

..

8 Commits

Author SHA1 Message Date
Mayukha Vadari
1b857cee98 one more update 2021-12-01 15:16:03 -05:00
Mayukha Vadari
3793012acd update rbc 2021-12-01 14:06:32 -05:00
Mayukha Vadari
515407bdaf update keypairs 2021-12-01 13:54:58 -05:00
Mayukha Vadari
23a15e416c update beta version 2021-12-01 13:11:35 -05:00
Mayukha Vadari
8ff9ebe276 edit address-codec and keypairs versions 2021-12-01 13:11:22 -05:00
Elliot Lee
dcf3473f1c bump version to 1.9.8-storm.1 2021-12-01 09:51:33 -08:00
Elliot Lee
ac99b3e25c bump ripple-binary-codec to 1.1.4-beta.1 2021-12-01 09:50:05 -08:00
natenichols
d6da4f820e publish a branch for data team's legacy process 2021-12-01 10:46:48 -06:00
29 changed files with 804 additions and 1720 deletions

View File

@@ -55,27 +55,3 @@ jobs:
env:
HOST: localhost
PORT: ${{ job.services.rippled.ports['6006'] }}
browser:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x] # This just needs to be compatible w/ puppeteer
services:
rippled:
image: natenichols/rippled-standalone:latest
ports:
- 6006:6006
options:
--health-cmd="wget localhost:6006 || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: yarn install
- run: yarn test:browser

3
.gitignore vendored
View File

@@ -68,6 +68,3 @@ scripts/cache
# nyc (istanbul)
.nyc_output
# browser tests
test-compiled-for-web

View File

@@ -2,15 +2,6 @@
Subscribe to [the **ripple-lib-announce** mailing list](https://groups.google.com/forum/#!forum/ripple-lib-announce) for release announcements. We recommend that ripple-lib users stay up-to-date with the latest stable release.
## 1.10.0 (2021-08-12)
* Add address generation from Devnet/Testnet faucets (#1497)
* Fix bug with `getBalances()` ledgerVersion (#1505)
* Include lodash in webpack build (#1500)
* Documentation Updates:
* Export and document AccountSetFlags (#1525)
* Add links to example keypair derivation (#1523)
## 1.9.8 (2021-07-30)
* Export offline methods to top level of package (#1479)

View File

@@ -93,7 +93,6 @@
- [isValidSecret](#isvalidsecret)
- [deriveKeypair](#derivekeypair)
- [deriveAddress](#deriveaddress)
- [generateFaucetWallet](#generatefaucetwallet)
- [signPaymentChannelClaim](#signpaymentchannelclaim)
- [verifyPaymentChannelClaim](#verifypaymentchannelclaim)
- [computeLedgerHash](#computeledgerhash)
@@ -102,7 +101,6 @@
- [iso8601ToRippleTime](#iso8601torippletime)
- [rippleTimeToISO8601](#rippletimetoiso8601)
- [txFlags](#txflags)
- [accountSetFlags](#accountsetflags)
- [schemaValidator](#schemavalidator)
- [schemaValidate](#schemavalidate)
- [API Events](#api-events)
@@ -5661,8 +5659,8 @@ Name | Type | Description
---- | ---- | -----------
txJSON | string | Transaction represented as a JSON string in rippled format.
keypair | object | *Optional* The private and public key of the account that is initiating the transaction. (This field cannot be used with secret).
*keypair.* privateKey | privateKey | The uppercase hexadecimal representation of the secp256k1 or Ed25519 private key. Ed25519 keys are prefixed with 0xED. You can read about how keys are derived [here](https://xrpl.org/cryptographic-keys.html).
*keypair.* publicKey | publicKey | The uppercase hexadecimal representation of the secp256k1 or Ed25519 public key. Ed25519 keys are prefixed with 0xED. You can read about how keys are derived [here](https://xrpl.org/cryptographic-keys.html).
*keypair.* privateKey | privateKey | The uppercase hexadecimal representation of the secp256k1 or Ed25519 private key.
*keypair.* publicKey | publicKey | The uppercase hexadecimal representation of the secp256k1 or Ed25519 public key.
options | object | *Optional* Options that control the type of signature to create.
*options.* signAs | [address](#address) | *Optional* The account that the signature should count for in multisigning.
secret | secret string | *Optional* The secret of the account that is initiating the transaction. (This field cannot be used with keypair).
@@ -5697,19 +5695,6 @@ return api.sign(txJSON, secret); // or: api.sign(txJSON, keypair);
```
### Example Keypairs
To learn how keypairs are derived read [here](https://xrpl.org/cryptographic-keys.html#generating-keys).
```javascript
// secp25519 (33 bytes)
const privateKey = "002512BBDFDBB77510883B7DCCBEF270B86DEAC8B64AC762873D75A1BEE6298665"
const publicKey = "0390A196799EE412284A5D80BF78C3E84CBB80E1437A0AECD9ADF94D7FEAAFA284"
// ed25519 (Note the 0xED prefixes a 32 byte value for a total of 33 bytes)
const privateKey = "ED0B6CBAC838DFE7F47EA1BD0DF00EC282FDF45510C92161072CCFB84035390C4D"
const publicKey = "ED1A7C082846CFF58FF9A892BA4BA2593151CCF1DBA59F37714CC9ED39824AF85F"
```
### Example (multi-signing)
```javascript
@@ -5761,7 +5746,7 @@ const multiSignPaymentTransaction = {
};
const multiSignPaymentInstruction = {
signersCount: 2
signersCount: 2
};
const api = new RippleAPI({
@@ -6092,38 +6077,6 @@ This method returns a string corresponding to the address derived from the publi
var address = api.deriveAddress(public_key);
```
## generateFaucetWallet
`generateFaucetWallet(onTestnet = true)`
Calls the Testnet or Devnet faucet API in order to generate a new, random wallet with some amount of test XRP. This is for testing purposes only.
### Example
**Request**
Create a new wallet on the Testnet:
```javascript
const wallet = await api.generateFaucetWallet()
```
**Response**
```json
{
"account": {
"xAddress": "T7i2Q8yGcMcCQa2n6d9EvSEptT4CE6ap7Q1r1fmjstkLfsK",
"secret": "ssKCsaRqWh669atvv83bdYRaiHomY",
"classicAddress": "r9SYfmVxrb7iuCVfNhW2gqqzapfE2r6juG",
"address": "r9SYfmVxrb7iuCVfNhW2gqqzapfE2r6juG"
},
"amount": 1000,
"balance": 1000
}
```
## signPaymentChannelClaim
`signPaymentChannelClaim(channel: string, amount: string, privateKey: string): string`
@@ -6444,22 +6397,6 @@ The remaining transaction types do not have any flags at this time.
* PaymentChannelCreate
* PaymentChannelFund
## accountSetFlags
To modify account flags, you can use an AccountSet transaction and its `SetFlag` or `ClearFlag` fields.
The flags are called [AccountSet flags (asf*)](https://xrpl.org/accountset.html#accountset-flags):
`RippleAPI.accountSetFlags.requireDestinationTag`: Require a destination tag to send transactions to this account.
`RippleAPI.accountSetFlags.requireAuthorization`: Require authorization for users to hold balances issued by this address. Can only be enabled if the address has no trust lines connected to it.
`RippleAPI.accountSetFlags.disallowIncomingXRP`: XRP should not be sent to this account. (Enforced by client applications, not by rippled)
`RippleAPI.accountSetFlags.disableMasterKey`: Disallow use of the master key pair. Can only be enabled if the account has configured another way to sign transactions.
`RippleAPI.accountSetFlags.enableTransactionIDTracking`: Track the ID of this account's most recent transaction.
`RippleAPI.accountSetFlags.noFreeze`: Permanently give up the ability to freeze individual trust lines or disable Global Freeze. This flag can never be disabled after being enabled.
`RippleAPI.accountSetFlags.globalFreeze`: Freeze all assets issued by this account.
`RippleAPI.accountSetFlags.defaultRipple`: Enable [rippling](https://xrpl.org/rippling.html) on this account's trust lines by default.
`RippleAPI.accountSetFlags.depositAuth`:Enable Deposit Authorization on this account.
## schemaValidator
Unlike the rest of the ripple-lib API, schemaValidator is a static object on RippleAPI. It provides utility methods that do not use a server.

View File

@@ -1,15 +0,0 @@
## accountSetFlags
To modify account flags, you can use an AccountSet transaction and its `SetFlag` or `ClearFlag` fields.
The flags are called [AccountSet flags (asf*)](https://xrpl.org/accountset.html#accountset-flags):
`RippleAPI.accountSetFlags.requireDestinationTag`: Require a destination tag to send transactions to this account.
`RippleAPI.accountSetFlags.requireAuthorization`: Require authorization for users to hold balances issued by this address. Can only be enabled if the address has no trust lines connected to it.
`RippleAPI.accountSetFlags.disallowIncomingXRP`: XRP should not be sent to this account. (Enforced by client applications, not by rippled)
`RippleAPI.accountSetFlags.disableMasterKey`: Disallow use of the master key pair. Can only be enabled if the account has configured another way to sign transactions.
`RippleAPI.accountSetFlags.enableTransactionIDTracking`: Track the ID of this account's most recent transaction.
`RippleAPI.accountSetFlags.noFreeze`: Permanently give up the ability to freeze individual trust lines or disable Global Freeze. This flag can never be disabled after being enabled.
`RippleAPI.accountSetFlags.globalFreeze`: Freeze all assets issued by this account.
`RippleAPI.accountSetFlags.defaultRipple`: Enable [rippling](https://xrpl.org/rippling.html) on this account's trust lines by default.
`RippleAPI.accountSetFlags.depositAuth`:Enable Deposit Authorization on this account.

View File

@@ -1,19 +0,0 @@
## generateFaucetWallet
`generateFaucetWallet(onTestnet = true)`
Calls the Testnet or Devnet faucet API in order to generate a new, random wallet with some amount of test XRP. This is for testing purposes only.
### Example
**Request**
Create a new wallet on the Testnet:
```javascript
const wallet = await api.generateFaucetWallet()
```
**Response**
<%- renderFixture('responses/generate-faucet-wallet.json') %>

View File

@@ -59,7 +59,6 @@
<%- include('isValidSecret.md.ejs') %>
<%- include('deriveKeypair.md.ejs') %>
<%- include('deriveAddress.md.ejs') %>
<%- include('generateFaucetWallet.md.ejs') %>
<%- include('signPaymentChannelClaim.md.ejs') %>
<%- include('verifyPaymentChannelClaim.md.ejs') %>
<%- include('computeLedgerHash.md.ejs') %>
@@ -67,6 +66,5 @@
<%- include('iso8601ToRippleTime.md.ejs') %>
<%- include('rippleTimeToISO8601.md.ejs') %>
<%- include('txFlags.md.ejs') %>
<%- include('accountSetFlags.md.ejs') %>
<%- include('schemaValidator.md.ejs') %>
<%- include('events.md.ejs') %>

View File

@@ -35,19 +35,6 @@ return api.sign(txJSON, secret); // or: api.sign(txJSON, keypair);
<%- renderFixture("responses/sign.json") %>
### Example Keypairs
To learn how keypairs are derived read [here](https://xrpl.org/cryptographic-keys.html#generating-keys).
```javascript
// secp25519 (33 bytes)
const privateKey = "002512BBDFDBB77510883B7DCCBEF270B86DEAC8B64AC762873D75A1BEE6298665"
const publicKey = "0390A196799EE412284A5D80BF78C3E84CBB80E1437A0AECD9ADF94D7FEAAFA284"
// ed25519 (Note the 0xED prefixes a 32 byte value for a total of 33 bytes)
const privateKey = "ED0B6CBAC838DFE7F47EA1BD0DF00EC282FDF45510C92161072CCFB84035390C4D"
const publicKey = "ED1A7C082846CFF58FF9A892BA4BA2593151CCF1DBA59F37714CC9ED39824AF85F"
```
### Example (multi-signing)
```javascript

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "1.10.0",
"version": "1.9.8-storm.4",
"license": "ISC",
"description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser",
"files": [
@@ -26,9 +26,9 @@
"https-proxy-agent": "^5.0.0",
"jsonschema": "1.2.2",
"lodash": "^4.17.4",
"ripple-address-codec": "^4.1.1",
"ripple-binary-codec": "^1.1.3",
"ripple-keypairs": "^1.0.3",
"ripple-address-codec": "4.1.3",
"ripple-binary-codec": "1.1.4-beta.2",
"ripple-keypairs": "1.0.4-storm.2",
"ripple-lib-transactionparser": "0.8.2",
"ws": "^7.2.0"
},
@@ -36,7 +36,7 @@
"elliptic": "^6.5.4"
},
"devDependencies": {
"@types/mocha": "^9.0.0",
"@types/mocha": "^8.2.1",
"@types/node": "^16.4.3",
"@typescript-eslint/eslint-plugin": "^2.3.3",
"@typescript-eslint/parser": "^2.27.0",
@@ -48,17 +48,12 @@
"ejs": "^3.0.1",
"eslint": "^6.5.1",
"eventemitter2": "^6.0.0",
"https-browserify": "^1.0.0",
"json-schema-to-markdown-table": "^0.4.0",
"mocha": "^9",
"nyc": "^15",
"path-browserify": "1.0.1",
"prettier": "^2.0.5",
"process": "^0.11.10",
"puppeteer": "5.4.1",
"stream-browserify": "^3.0.0",
"stream-http": "3.1.1",
"ts-loader": "^8.0.11",
"ts-node": "^10.1.0",
"typescript": "^3.9.9",
"url": "^0.11.0",
@@ -79,7 +74,6 @@
"prepublish": "yarn clean && yarn build",
"test": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha --config=test/.mocharc.json --exit",
"test:integration": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha ./test/integration/*.ts",
"test:browser": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha ./test/browser/*.ts",
"test:watch": "TS_NODE_PROJECT=src/tsconfig.json mocha --config=test/.mocharc.json --watch --reporter dot",
"format": "prettier --write '{src,test}/**/*.ts'",
"lint": "eslint 'src/**/*.ts' 'test/*-test.{ts,js}'",

View File

@@ -1,7 +1,6 @@
import {EventEmitter} from 'events'
import {
Connection,
constants,
errors,
validate,
xrpToDrops,
@@ -9,7 +8,7 @@ import {
rippleTimeToISO8601,
iso8601ToRippleTime,
txFlags,
ensureClassicAddress,
ensureClassicAddress
} from './common'
import {
connect,
@@ -48,7 +47,7 @@ import prepareTicketCreate from './transaction/ticket'
import sign from './transaction/sign'
import combine from './transaction/combine'
import submit from './transaction/submit'
import {generateAddress, generateXAddress} from './offline/utils'
import { generateAddress, generateXAddress } from './offline/utils'
import {deriveKeypair, deriveAddress, deriveXAddress} from './offline/derive'
import computeLedgerHash from './offline/ledgerhash'
import signPaymentChannelClaim from './offline/sign-payment-channel-claim'
@@ -86,22 +85,7 @@ import {getServerInfo, getFee} from './common/serverinfo'
import {clamp, renameCounterpartyToIssuer} from './ledger/utils'
import {TransactionJSON, Instructions, Prepare} from './transaction/types'
import {ConnectionUserOptions} from './common/connection'
import {
classicAddressToXAddress,
xAddressToClassicAddress,
isValidXAddress,
isValidClassicAddress,
encodeSeed,
decodeSeed,
encodeAccountID,
decodeAccountID,
encodeNodePublic,
decodeNodePublic,
encodeAccountPublic,
decodeAccountPublic,
encodeXAddress,
decodeXAddress
} from 'ripple-address-codec'
import {classicAddressToXAddress, xAddressToClassicAddress, isValidXAddress, isValidClassicAddress, encodeSeed, decodeSeed, encodeAccountID, decodeAccountID, encodeNodePublic, decodeNodePublic, encodeAccountPublic, decodeAccountPublic, encodeXAddress, decodeXAddress} from 'ripple-address-codec'
import {
computeBinaryTransactionHash,
computeTransactionHash,
@@ -116,8 +100,6 @@ import {
computePaymentChannelHash
} from './common/hashes'
import generateFaucetWallet from './wallet/wallet-generation'
export interface APIOptions extends ConnectionUserOptions {
server?: string
feeCushion?: number
@@ -417,9 +399,6 @@ class RippleAPI extends EventEmitter {
computeLedgerHash = computeLedgerHash // @deprecated Invoke from top-level package instead
signPaymentChannelClaim = signPaymentChannelClaim // @deprecated Invoke from top-level package instead
verifyPaymentChannelClaim = verifyPaymentChannelClaim // @deprecated Invoke from top-level package instead
generateFaucetWallet = generateFaucetWallet
errors = errors
static deriveXAddress = deriveXAddress
@@ -430,20 +409,20 @@ class RippleAPI extends EventEmitter {
/**
* Static methods to expose ripple-address-codec methods
*/
static classicAddressToXAddress = classicAddressToXAddress
static xAddressToClassicAddress = xAddressToClassicAddress
static isValidXAddress = isValidXAddress
static isValidClassicAddress = isValidClassicAddress
static encodeSeed = encodeSeed
static decodeSeed = decodeSeed
static encodeAccountID = encodeAccountID
static decodeAccountID = decodeAccountID
static encodeNodePublic = encodeNodePublic
static decodeNodePublic = decodeNodePublic
static encodeAccountPublic = encodeAccountPublic
static decodeAccountPublic = decodeAccountPublic
static encodeXAddress = encodeXAddress
static decodeXAddress = decodeXAddress
static classicAddressToXAddress = classicAddressToXAddress
static xAddressToClassicAddress = xAddressToClassicAddress
static isValidXAddress = isValidXAddress
static isValidClassicAddress = isValidClassicAddress
static encodeSeed = encodeSeed
static decodeSeed = decodeSeed
static encodeAccountID = encodeAccountID
static decodeAccountID = decodeAccountID
static encodeNodePublic = encodeNodePublic
static decodeNodePublic = decodeNodePublic
static encodeAccountPublic = encodeAccountPublic
static decodeAccountPublic = decodeAccountPublic
static encodeXAddress = encodeXAddress
static decodeXAddress = decodeXAddress
/**
* Static methods that replace functionality from the now-deprecated ripple-hashes library
@@ -455,8 +434,7 @@ class RippleAPI extends EventEmitter {
// @deprecated Invoke from top-level package instead
static computeTransactionHash = computeTransactionHash // (txJSON: any): string
// @deprecated Invoke from top-level package instead
static computeBinaryTransactionSigningHash =
computeBinaryTransactionSigningHash // (txBlobHex: string): string
static computeBinaryTransactionSigningHash = computeBinaryTransactionSigningHash // (txBlobHex: string): string
// Compute the hash of an account, given the account's classic address (starting with `r`).
// @deprecated Invoke from top-level package instead
static computeAccountLedgerObjectID = computeAccountLedgerObjectID // (address: string): string
@@ -488,13 +466,14 @@ class RippleAPI extends EventEmitter {
rippleTimeToISO8601 = rippleTimeToISO8601 // @deprecated Invoke from top-level package instead
iso8601ToRippleTime = iso8601ToRippleTime // @deprecated Invoke from top-level package instead
txFlags = txFlags
static accountSetFlags = constants.AccountSetFlags
isValidAddress = schemaValidator.isValidAddress
isValidSecret = schemaValidator.isValidSecret
}
export {RippleAPI}
export {
RippleAPI
}
export type {
AccountObjectsRequest,

View File

@@ -81,7 +81,7 @@ function createWebSocket(url: string, config: ConnectionOptions): WebSocket {
passphrase: config.passphrase,
cert: config.certificate
},
(value) => value == null
value => value == null
)
const proxyOptions = Object.assign({}, parsedProxyURL, proxyOverrides)
let HttpsProxyAgent
@@ -103,7 +103,7 @@ function createWebSocket(url: string, config: ConnectionOptions): WebSocket {
passphrase: config.passphrase,
cert: config.certificate
},
(value) => value == null
value => value == null
)
const websocketOptions = Object.assign({}, options, optionsOverrides)
const websocket = new WebSocket(url, null, websocketOptions)
@@ -642,13 +642,4 @@ export class Connection extends EventEmitter {
return responsePromise
}
/**
* Get the Websocket connection URL
*
* @returns The Websocket connection URL
*/
getUrl(): string {
return this._url
}
}

View File

@@ -70,7 +70,7 @@ export interface Settings {
defaultRipple?: boolean
}
const AccountSetFlags = {
const AccountFlagIndices = {
requireDestinationTag: txFlagIndices.AccountSet.asfRequireDest,
requireAuthorization: txFlagIndices.AccountSet.asfRequireAuth,
depositAuth: txFlagIndices.AccountSet.asfDepositAuth,
@@ -96,4 +96,4 @@ const AccountFields = {
TickSize: {name: 'tickSize', defaults: 0}
}
export {AccountFields, AccountSetFlags, AccountFlags}
export {AccountFields, AccountFlagIndices, AccountFlags}

View File

@@ -54,8 +54,6 @@ class ResponseFormatError extends ConnectionError {}
class ValidationError extends RippleError {}
class XRPLFaucetError extends RippleError {}
class NotFoundError extends RippleError {
constructor(message = 'Not found') {
super(message)
@@ -92,6 +90,5 @@ export {
NotFoundError,
PendingLedgerVersionError,
MissingLedgerHistoryError,
LedgerVersionError,
XRPLFaucetError
LedgerVersionError
}

View File

@@ -17,11 +17,11 @@
"properties": {
"privateKey": {
"type": "privateKey",
"description": "The uppercase hexadecimal representation of the secp256k1 or Ed25519 private key. Ed25519 keys are prefixed with 0xED. You can read about how keys are derived [here](https://xrpl.org/cryptographic-keys.html)."
"description": "The uppercase hexadecimal representation of the secp256k1 or Ed25519 private key."
},
"publicKey": {
"type": "publicKey",
"description": "The uppercase hexadecimal representation of the secp256k1 or Ed25519 public key. Ed25519 keys are prefixed with 0xED. You can read about how keys are derived [here](https://xrpl.org/cryptographic-keys.html)."
"description": "The uppercase hexadecimal representation of the secp256k1 or Ed25519 public key."
}
},
"description": "The private and public key of the account that is initiating the transaction. (This field cannot be used with secret).",

View File

@@ -31,7 +31,7 @@ async function getTrustlines(
// 2. Make Request
const responses = await this._requestAll('account_lines', {
account: address,
ledger_index: options.ledgerVersion ?? await this.getLedgerVersion(),
ledger_index: await this.getLedgerVersion(),
limit: options.limit,
peer: options.counterparty
})

View File

@@ -1,66 +1,43 @@
import * as _ from 'lodash'
import binary from 'ripple-binary-codec'
import * as utils from './utils'
import BigNumber from 'bignumber.js'
import {ValidationError} from '../common/errors'
import {decodeAccountID} from 'ripple-address-codec'
import {validate} from '../common'
import {computeBinaryTransactionHash} from '../common/hashes'
import {JsonObject} from 'ripple-binary-codec/dist/types/serialized-type'
/**
* The transactions should all be equal except for the 'Signers' field.
*/
function validateTransactionEquivalence(transactions: Array<JsonObject>) {
const exampleTransaction = JSON.stringify({...transactions[0], Signers: null})
if (transactions.slice(1).some(tx => JSON.stringify({...tx, Signers: null}) !== exampleTransaction)) {
throw new ValidationError('txJSON is not the same for all signedTransactions')
}
}
function addressToBigNumber(address) {
const hex = Buffer.from(decodeAccountID(address)).toString('hex')
return new BigNumber(hex, 16)
}
/**
* If presented in binary form, the Signers array must be sorted based on
* the numeric value of the signer addresses, with the lowest value first.
* (If submitted as JSON, the submit_multisigned method handles this automatically.)
* https://xrpl.org/multi-signing.html
*/
function compareSigners(a, b) {
return addressToBigNumber(a.Signer.Account).comparedTo(
addressToBigNumber(b.Signer.Account)
)
}
function getTransactionWithAllSigners(transactions: Array<JsonObject>): JsonObject {
// Signers must be sorted - see compareSigners for more details
const sortedSigners = _.flatMap(transactions, tx => tx.Signers)
.filter(signer => signer)
.sort(compareSigners)
return {...transactions[0], Signers: sortedSigners}
}
/**
*
* @param signedTransactions A collection of the same transaction signed by different signers. The only difference
* between the elements of signedTransactions should be the Signers field.
* @returns An object with the combined transaction (now having a sorted list of all signers) which is encoded, along
* with a transaction id based on the combined transaction.
*/
function combine(signedTransactions: Array<string>): object {
validate.combine({signedTransactions})
const transactions: JsonObject[] = signedTransactions.map(binary.decode);
validateTransactionEquivalence(transactions)
const signedTransaction = binary.encode(getTransactionWithAllSigners(transactions))
return {
signedTransaction: signedTransaction,
id: computeBinaryTransactionHash(signedTransaction)
// TODO: signedTransactions is an array of strings in the documentation, but
// tests and this code handle it as an array of objects. Fix!
const txs: any[] = signedTransactions.map(binary.decode)
const tx = _.omit(txs[0], 'Signers')
if (!txs.every((_tx) => _.isEqual(tx, _.omit(_tx, 'Signers')))) {
throw new utils.common.errors.ValidationError(
'txJSON is not the same for all signedTransactions'
)
}
const unsortedSigners = txs.reduce(
(accumulator, _tx) => accumulator.concat(_tx.Signers || []),
[]
)
const signers = unsortedSigners.sort(compareSigners)
const signedTx = Object.assign({}, tx, {Signers: signers})
const signedTransaction = binary.encode(signedTx)
const id = computeBinaryTransactionHash(signedTransaction)
return {signedTransaction, id}
}
export default combine

View File

@@ -2,7 +2,7 @@ import * as assert from 'assert'
import BigNumber from 'bignumber.js'
import * as utils from './utils'
const validate = utils.common.validate
const AccountSetFlags = utils.common.constants.AccountSetFlags
const AccountFlagIndices = utils.common.constants.AccountFlagIndices
const AccountFields = utils.common.constants.AccountFields
import {
Instructions,
@@ -17,14 +17,14 @@ function setTransactionFlags(
txJSON: TransactionJSON,
values: FormattedSettings
) {
const keys = Object.keys(values).filter((key) => AccountSetFlags[key] != null)
const keys = Object.keys(values).filter((key) => AccountFlagIndices[key] != null)
assert.ok(
keys.length <= 1,
'ERROR: can only set one setting per transaction'
)
const flagName = keys[0]
const value = values[flagName]
const index = AccountSetFlags[flagName]
const index = AccountFlagIndices[flagName]
if (index != null) {
if (value) {
txJSON.SetFlag = index

View File

@@ -1,222 +0,0 @@
import https = require('https')
import {RippleAPI} from '..'
import {errors} from '../common'
import {GeneratedAddress} from '../offline/generate-address'
import {isValidAddress} from '../common/schema-validator'
import {RippledError} from '../common/errors'
export interface FaucetWallet {
account: GeneratedAddress
amount: number
balance: number
}
export enum FaucetNetwork {
Testnet = 'faucet.altnet.rippletest.net',
Devnet = 'faucet.devnet.rippletest.net'
}
const INTERVAL_SECONDS = 1 // Interval to check an account balance
const MAX_ATTEMPTS = 20 // Maximum attempts to retrieve a balance
/**
* Generates a random wallet with some amount of XRP (usually 1000 XRP).
*
* @param address - An existing XRPL address to fund, if undefined, a new wallet will be created.
* @returns - A Wallet on the Testnet or Devnet that contains some amount of XRP.
*/
async function generateFaucetWallet(
this: RippleAPI,
address?: string
): Promise<FaucetWallet | void> {
if(!this.isConnected())
throw new RippledError("RippleAPI not connected, cannot call faucet")
// Initialize some variables
let body: Uint8Array
let startingBalance = 0
let faucetUrl = getFaucetUrl(this)
// If the user provides an existing wallet to fund
if (address && isValidAddress(address)) {
// Create the POST request body
body = new TextEncoder().encode(
JSON.stringify({
destination: address
})
)
// Retrieve the existing account balance
const addressToFundBalance = await getAddressXrpBalance(this, address)
// Check the address balance is not undefined and is a number
if (addressToFundBalance && !isNaN(+addressToFundBalance)) {
startingBalance = +addressToFundBalance
} else {
startingBalance = 0
}
}
// Options to pass to https.request
const options = {
hostname: faucetUrl,
port: 443,
path: '/accounts',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': body ? body.length : 0
}
}
return new Promise((resolve, reject) => {
const request = https.request(options, (response) => {
const chunks = []
response.on('data', (d) => {
chunks.push(d)
})
response.on('end', async () => {
const body = Buffer.concat(chunks).toString()
// "application/json; charset=utf-8"
if (response.headers['content-type'].startsWith('application/json')) {
const wallet: FaucetWallet = JSON.parse(body)
const classicAddress = wallet.account.classicAddress
if (classicAddress) {
try {
// Check at regular interval if the address is enabled on the XRPL and funded
const isFunded = await hasAddressBalanceIncreased(
this,
classicAddress,
startingBalance
)
if (isFunded) {
resolve(wallet)
} else {
reject(
new errors.XRPLFaucetError(
`Unable to fund address with faucet after waiting ${
INTERVAL_SECONDS * MAX_ATTEMPTS
} seconds`
)
)
}
} catch (err) {
reject(new errors.XRPLFaucetError(err))
}
} else {
reject(
new errors.XRPLFaucetError(
`The faucet account classic address is undefined`
)
)
}
} else {
reject({
statusCode: response.statusCode,
contentType: response.headers['content-type'],
body
})
}
})
})
// POST the body
request.write(body ? body : '')
request.on('error', (error) => {
reject(error)
})
request.end()
})
}
/**
* Retrieves an XRPL address XRP balance
*
* @param api - RippleAPI
* @param address - XRPL address.
* @returns
*/
async function getAddressXrpBalance(
api: RippleAPI,
address: string
): Promise<string> {
// Get all the account balances
try {
const balances = await api.getBalances(address)
// Retrieve the XRP balance
const xrpBalance = balances.filter(
(balance) => balance.currency.toUpperCase() === 'XRP'
)
return xrpBalance[0].value
} catch (err) {
return `Unable to retrieve ${address} balance. Error: ${err}`
}
}
/**
* Check at regular interval if the address is enabled on the XRPL and funded
*
* @param api - RippleAPI
* @param address - the account address to check
* @param originalBalance - the initial balance before the funding
* @returns A Promise boolean
*/
async function hasAddressBalanceIncreased(
api: RippleAPI,
address: string,
originalBalance: number
): Promise<boolean> {
return new Promise((resolve, reject) => {
let attempts = MAX_ATTEMPTS
const interval = setInterval(async () => {
if (attempts < 0) {
clearInterval(interval)
resolve(false)
} else {
attempts--
}
try {
const newBalance = +(await getAddressXrpBalance(api, address))
if (newBalance > originalBalance) {
clearInterval(interval)
resolve(true)
}
} catch (err) {
clearInterval(interval)
reject(
new errors.XRPLFaucetError(
`Unable to check if the address ${address} balance has increased. Error: ${err}`
)
)
}
}, INTERVAL_SECONDS * 1000)
})
}
/**
* Get the faucet URL based on the RippleAPI connection
* @param api - RippleAPI
* @returns A {@link FaucetNetwork}
*/
export function getFaucetUrl(api: RippleAPI) {
const connectionUrl = api.connection.getUrl()
// 'altnet' for Ripple Testnet server and 'testnet' for XRPL Labs Testnet server
if (connectionUrl.includes('altnet') || connectionUrl.includes('testnet')) {
return FaucetNetwork.Testnet
}
if (connectionUrl.includes('devnet')) {
return FaucetNetwork.Devnet
}
return undefined
}
export default generateFaucetWallet

View File

@@ -37,13 +37,4 @@ export default <TestSuite>{
'getTrustlines'
)
},
'getTrustlines - ledger version option': async (api, address) => {
const result = await api.getTrustlines(addresses.FOURTH_ACCOUNT, {ledgerVersion: 5})
assertResultMatch(
result,
RESPONSE_FIXTURES.moreThan400Items,
'getTrustlines'
)
},
}

View File

@@ -1,30 +0,0 @@
import assert from 'assert'
import puppeteer from 'puppeteer'
describe("Browser Tests", () => {
it("Integration Tests", async () => {
const browser = await puppeteer.launch({"headless": true});
try {
const page = await browser.newPage().catch();
await page.goto(`file:///${__dirname}/../localintegrationrunner.html`);
await page.waitForFunction('document.querySelector("body").innerText.includes("submit multisigned transaction")');
const fails = await page.evaluate(() => {
return document.querySelector('.failures').textContent
})
const passes = await page.evaluate(() => {
return document.querySelector('.passes').textContent
})
assert.equal(fails, "failures: 0")
assert.notEqual(passes, "passes: 0")
} catch (err) {
console.log(err)
assert(false)
} finally {
await browser.close();
}
}).timeout(40000)
})

View File

@@ -1,10 +0,0 @@
{
"account": {
"xAddress": "T7i2Q8yGcMcCQa2n6d9EvSEptT4CE6ap7Q1r1fmjstkLfsK",
"secret": "ssKCsaRqWh669atvv83bdYRaiHomY",
"classicAddress": "r9SYfmVxrb7iuCVfNhW2gqqzapfE2r6juG",
"address": "r9SYfmVxrb7iuCVfNhW2gqqzapfE2r6juG"
},
"amount": 1000,
"balance": 1000
}

View File

@@ -218,6 +218,5 @@ module.exports = {
single: require('./combine')
},
submit: require('./submit'),
ledgerEvent: require('./ledger-event'),
generateFaucetWallet: require('./generate-faucet-wallet.json')
ledgerEvent: require('./ledger-event')
};

View File

@@ -12,12 +12,10 @@ import {isValidSecret} from 'ripple-api/common/utils'
const TIMEOUT = 20000
const INTERVAL = 1000 // how long to wait between checks for validated ledger
const HOST = process.env.HOST ?? "0.0.0.0"
const HOST = process.env.HOST ?? "127.0.0.1"
const PORT = process.env.PORT ?? "6006"
const serverUrl = `ws://${HOST}:${PORT}`
console.log(serverUrl)
function acceptLedger(api) {
return api.connection.request({command: 'ledger_accept'})
}

View File

@@ -3,6 +3,7 @@
<meta charset="utf-8">
<!-- encoding must be set for mocha's special characters to render properly -->
<link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
<script src="./vendor/lodash.min.js"></script>
</head>
<body>
<div id="deb"></div>
@@ -17,7 +18,7 @@
mocha.ui('bdd')
</script>
<script src="../test-compiled-for-web/integration-test.js"></script>
<script src="../test-compiled-for-web/integration/integration-test.js"></script>
<script>
mocha.run()

View File

@@ -669,11 +669,7 @@ export function createMockRippled(port) {
} else if (request.account === addresses.THIRD_ACCOUNT) {
conn.send(accountLinesResponse.manyItems(request))
} else if (request.account === addresses.FOURTH_ACCOUNT) {
if (request.ledger_index === 5) {
conn.send(accountLinesResponse.manyItems(request))
} else {
conn.send(accountLinesResponse.ripplingDisabled(request))
}
conn.send(accountLinesResponse.ripplingDisabled(request))
} else if (request.account === addresses.NOTFOUND) {
conn.send(createResponse(request, fixtures.account_info.notfound))
} else {

View File

@@ -1,3 +1,6 @@
{
"extends": "../tsconfig-base.json",
"compilerOptions": {
"noEmit": true,
}
}

View File

@@ -1,35 +0,0 @@
import assert from 'assert-diff'
import setupAPI from './setup-api'
import {getFaucetUrl, FaucetNetwork} from '../src/wallet/wallet-generation'
describe('Get Faucet URL', function () {
beforeEach(setupAPI.setup)
afterEach(setupAPI.teardown)
it('returns the Devnet URL', function () {
const expectedFaucet = FaucetNetwork.Devnet
this.api.connection._url = FaucetNetwork.Devnet
assert.strictEqual(getFaucetUrl(this.api), expectedFaucet)
})
it('returns the Testnet URL', function () {
const expectedFaucet = FaucetNetwork.Testnet
this.api.connection._url = FaucetNetwork.Testnet
assert.strictEqual(getFaucetUrl(this.api), expectedFaucet)
})
it('returns the Testnet URL with the XRPL Labs server', function () {
const expectedFaucet = FaucetNetwork.Testnet
this.api.connection._url = 'wss://testnet.xrpl-labs.com'
assert.strictEqual(getFaucetUrl(this.api), expectedFaucet)
})
it('returns undefined if not a Testnet or Devnet server URL', function () {
// Info: setupAPI.setup creates a connection to 'localhost'
assert.strictEqual(getFaucetUrl(this.api), undefined)
})
})

View File

@@ -1,7 +1,6 @@
'use strict';
const path = require('path');
const webpack = require('webpack');
const assert = require('assert');
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
function getDefaultConfiguration() {
@@ -9,6 +8,9 @@ function getDefaultConfiguration() {
cache: true,
performance: { hints: false },
stats: 'errors-only',
externals: [{
'lodash': '_'
}],
entry: './dist/npm/index.js',
output: {
library: 'ripple',
@@ -32,85 +34,20 @@ function getDefaultConfiguration() {
"assert": require.resolve("assert/"),
"url": require.resolve("url/"),
"stream": require.resolve("stream-browserify"),
"crypto": require.resolve("crypto-browserify"),
"https": require.resolve("https-browserify"),
"http": require.resolve('stream-http')
"crypto": require.resolve("crypto-browserify")
}
},
};
}
function webpackForTest(testFileName) {
const match = testFileName.match(/\/?([^\/]*)-test.ts$/);
if (!match) {
assert(false, 'wrong filename:' + testFileName);
}
const test = {
cache: true,
externals: [{
'lodash': '_',
'ripple-api': 'ripple',
'net': 'null'
}],
entry: testFileName,
output: {
library: match[1].replace(/-/g, '_'),
path: path.join(__dirname, './test-compiled-for-web/'),
filename: match[1] + '-test.js'
},
plugins: [
new webpack.ProvidePlugin({ process: 'process/browser' }),
new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] })
],
module: {
rules: [{
test: /jayson/,
use: 'null',
}, {
test: /\.ts$/,
use: [{
loader: 'ts-loader',
options: {
compilerOptions: {
composite: false,
declaration: false,
declarationMap: false
}
},
}],
}]
},
node: {
global: true,
__filename: false,
__dirname: true,
},
resolve: {
extensions: [ '.ts', '.js', '.json' ],
fallback: {
"buffer": require.resolve("buffer/"),
"assert": require.resolve("assert/"),
"url": require.resolve("url/"),
"stream": require.resolve("stream-browserify"),
"crypto": require.resolve("crypto-browserify"),
"path": require.resolve("path-browserify"),
"http": require.resolve("stream-http"),
"fs": false
}
}
};
return Object.assign({}, getDefaultConfiguration(), test);
}
module.exports = [
(env, argv) => {
function(env, argv) {
const config = getDefaultConfiguration();
config.mode = 'development';
config.output.filename = `ripple-latest.js`;
return config;
},
(env, argv) => {
function(env, argv) {
const config = getDefaultConfiguration();
config.mode = 'production';
config.output.filename = `ripple-latest-min.js`;
@@ -119,5 +56,4 @@ module.exports = [
}
return config;
},
(env, argv) => webpackForTest('./test/integration/integration-test.ts'),
];

1797
yarn.lock

File diff suppressed because it is too large Load Diff